From 7735d8fd2e337427b47d8787040a603406a8a783 Mon Sep 17 00:00:00 2001 From: Intege-rs Date: Tue, 7 Apr 2026 22:44:31 -0400 Subject: [PATCH] 2026.04.02-51731a32a --- .../hypixel/hytale/assetstore/AssetPack.java | 9 +- .../hypixel/hytale/assetstore/AssetStore.java | 4 + .../adventure/farming/FarmingPlugin.java | 8 - .../adventure/farming/FarmingSystems.java | 38 +- .../adventure/farming/FarmingUtil.java | 226 +- .../component/CoopResidentComponent.java | 7 +- .../farming/config/FarmingCoopAsset.java | 7 +- .../LightLevelGrowthModifierAsset.java | 4 +- .../stages/BlockStateFarmingStageData.java | 37 +- .../stages/BlockTypeFarmingStageData.java | 27 +- .../config/stages/PrefabFarmingStageData.java | 31 +- .../ChangeFarmingStageInteraction.java | 11 +- .../FertilizeSoilInteraction.java | 18 +- .../interactions/HarvestCropInteraction.java | 2 +- .../UseCaptureCrateInteraction.java | 50 +- .../interactions/UseCoopInteraction.java | 27 +- .../UseWateringCanInteraction.java | 113 +- .../adventure/farming/states/CoopBlock.java | 33 +- .../farming/states/FarmingBlockState.java | 116 - .../memories/MemoriesGameplayConfig.java | 29 + .../adventure/memories/MemoriesPlugin.java | 61 +- .../memories/memories/npc/NPCMemory.java | 20 +- .../adventure/memories/page/MemoriesPage.java | 16 +- .../temple/TempleRespawnPlayersSystem.java | 4 +- .../assets/BountyObjectiveTaskAsset.java | 2 +- .../assets/KillObjectiveTaskAsset.java | 2 +- .../KillSpawnBeaconObjectiveTaskAsset.java | 7 +- .../KillSpawnMarkerObjectiveTaskAsset.java | 2 +- .../npcobjectives/npc/ActionCompleteTask.java | 5 +- .../npc/ActionStartObjective.java | 5 +- .../task/BountyObjectiveTask.java | 15 +- .../task/KillSpawnBeaconObjectiveTask.java | 16 +- .../task/KillSpawnMarkerObjectiveTask.java | 6 +- .../npcshop/npc/ActionOpenBarterShop.java | 5 +- .../adventure/npcshop/npc/ActionOpenShop.java | 5 +- .../adventure/objectives/Objective.java | 2 +- .../adventure/objectives/ObjectivePlugin.java | 95 +- ...hestState.java => TreasureChestBlock.java} | 46 +- .../ClearObjectiveItemsCompletion.java | 19 +- .../completion/GiveItemsCompletion.java | 49 +- .../markerarea/ObjectiveLocationAreaBox.java | 2 +- .../ObjectiveLocationAreaRadius.java | 2 +- .../ObjectiveLocationMarkerArea.java | 2 +- .../config/task/CountObjectiveTaskAsset.java | 2 +- .../config/task/CraftObjectiveTaskAsset.java | 2 +- .../config/task/GatherObjectiveTaskAsset.java | 2 +- .../config/task/ObjectiveTaskAsset.java | 5 +- .../task/TreasureMapObjectiveTaskAsset.java | 2 +- .../task/UseBlockObjectiveTaskAsset.java | 2 +- .../task/UseEntityObjectiveTaskAsset.java | 2 +- .../taskcondition/SoloInventoryCondition.java | 34 +- .../CheckTagWorldHeightRadiusProvider.java | 2 +- .../LocationRadiusProvider.java | 4 +- .../LookBlocksBelowProvider.java | 4 +- .../WorldLocationProvider.java | 2 +- .../CanBreakRespawnPointInteraction.java | 2 +- .../DestroyTreasureConditionInteraction.java | 78 + .../OpenTreasureContainerInteraction.java | 146 + .../markers/ObjectiveTaskMarker.java | 2 +- .../ObjectiveLocationMarkerSystems.java | 12 +- .../ReachLocationMarkerSystems.java | 6 +- .../ObjectiveInventoryChangeSystem.java | 111 + .../objectives/task/GatherObjectiveTask.java | 51 +- .../objectives/task/InventoryChangeAware.java | 12 + .../objectives/task/ObjectiveTask.java | 2 +- .../objectives/task/ReachLocationTask.java | 10 +- .../task/TreasureMapObjectiveTask.java | 53 +- .../SpawnTreasureChestTransactionRecord.java | 19 +- .../adventure/shop/GiveItemInteraction.java | 9 +- .../adventure/shop/barter/BarterPage.java | 15 +- .../builtin/adventure/stash/StashPlugin.java | 60 +- .../teleporter/component/Teleporter.java | 12 +- .../server/TeleporterInteraction.java | 13 +- .../interaction/server/UsedTeleporter.java | 4 +- .../page/TeleporterSettingsPage.java | 18 +- .../system/ClearUsedTeleporterSystem.java | 4 +- .../CreateWarpWhenTeleporterPlacedSystem.java | 6 +- .../systems/AmbientEmitterSystems.java | 2 +- .../AssetEditorGamePacketHandler.java | 59 +- .../asseteditor/AssetEditorPacketHandler.java | 21 +- .../asseteditor/AssetEditorPlugin.java | 102 +- .../AssetSpecificFunctionality.java | 48 +- .../hytale/builtin/asseteditor/Messages.java | 2 + .../beds/interactions/BedInteraction.java | 14 +- .../OverrideNearbyRespawnPointPage.java | 7 +- .../beds/respawn/RespawnPointPage.java | 8 +- .../SelectOverrideRespawnPointPage.java | 6 +- .../beds/respawn/SetNameRespawnPointPage.java | 2 +- .../sleep/systems/player/EnterBedSystem.java | 2 +- .../blockphysics/BlockPhysicsUtil.java | 15 +- .../blockphysics/PrefabBufferValidator.java | 16 +- .../blockspawner/BlockSpawnerEntry.java | 7 - .../blockspawner/BlockSpawnerPlugin.java | 33 +- .../command/BlockSpawnerGetCommand.java | 2 +- .../command/BlockSpawnerSetCommand.java | 2 +- .../builtin/blocktick/BlockTickPlugin.java | 53 +- .../system/ChunkBlockTickSystem.java | 41 +- .../BuilderToolsPacketHandler.java | 419 +-- .../buildertools/BuilderToolsPlugin.java | 1751 ++++++----- .../buildertools/BuilderToolsSystems.java | 12 + .../buildertools/BuilderToolsUserData.java | 21 +- .../builtin/buildertools/EditOperation.java | 68 +- .../PrototypePlayerBuilderToolSettings.java | 28 +- .../commands/ClearBlocksCommand.java | 28 +- .../commands/ClearEntitiesCommand.java | 16 +- .../commands/ContractSelectionCommand.java | 8 +- .../buildertools/commands/CopyCommand.java | 48 +- .../buildertools/commands/CutCommand.java | 8 +- .../commands/DeselectCommand.java | 2 +- .../commands/EditLineCommand.java | 10 +- .../buildertools/commands/ExpandCommand.java | 8 +- .../commands/ExtendFaceCommand.java | 6 +- .../buildertools/commands/FillCommand.java | 2 +- .../buildertools/commands/FlipCommand.java | 2 +- .../buildertools/commands/HollowCommand.java | 2 +- .../buildertools/commands/LayerCommand.java | 26 +- .../buildertools/commands/MoveCommand.java | 6 +- .../buildertools/commands/PasteCommand.java | 4 +- .../buildertools/commands/Pos1Command.java | 8 +- .../buildertools/commands/Pos2Command.java | 8 +- .../buildertools/commands/PrefabCommand.java | 60 +- .../commands/RepairFillersCommand.java | 2 +- .../buildertools/commands/ReplaceCommand.java | 235 +- .../buildertools/commands/RotateCommand.java | 4 +- .../commands/SelectChunkCommand.java | 10 +- .../commands/SelectChunkSectionCommand.java | 12 +- .../buildertools/commands/SetCommand.java | 2 +- .../buildertools/commands/ShiftCommand.java | 8 +- .../buildertools/commands/StackCommand.java | 4 +- .../commands/SubmergeCommand.java | 2 +- .../buildertools/commands/TintCommand.java | 2 +- .../commands/UpdateSelectionCommand.java | 2 +- .../buildertools/commands/WallsCommand.java | 2 +- .../imageimport/ImageImportPage.java | 37 +- .../interactions/PickupItemInteraction.java | 2 +- .../buildertools/objimport/ObjImportPage.java | 46 +- .../prefabeditor/PrefabDirtySystems.java | 2 +- .../prefabeditor/PrefabEditSession.java | 12 +- .../PrefabEditSessionManager.java | 144 +- .../prefabeditor/PrefabEditingMetadata.java | 23 +- .../PrefabEditorCreationSettings.java | 27 +- .../PrefabSelectionInteraction.java | 24 +- .../PrefabSetAnchorInteraction.java | 93 - .../commands/PrefabEditBackCommand.java | 63 + .../commands/PrefabEditCommand.java | 1 + .../commands/PrefabEditInfoCommand.java | 8 +- .../PrefabEditKillEntitiesCommand.java | 15 +- .../commands/PrefabEditLoadCommand.java | 24 +- .../commands/PrefabEditSaveAsCommand.java | 104 +- .../commands/PrefabEditSaveCommand.java | 216 +- .../commands/PrefabEditSaveUICommand.java | 4 + .../commands/PrefabEditSelectCommand.java | 13 +- .../commands/PrefabEditUpdateBoxCommand.java | 2 +- .../prefabeditor/saving/PrefabSaver.java | 25 +- .../ui/PrefabEditorExitConfirmPage.java | 8 +- .../ui/PrefabEditorLoadOptionsPage.java | 95 + .../ui/PrefabEditorLoadSettingsPage.java | 603 ++-- .../ui/PrefabEditorSaveSettingsPage.java | 620 ++-- .../prefabeditor/ui/PrefabTeleportPage.java | 8 +- .../prefablist/AssetPrefabFileProvider.java | 5 + .../buildertools/prefablist/PrefabPage.java | 2 +- .../prefablist/PrefabSavePage.java | 162 +- .../scriptedbrushes/BrushConfig.java | 43 +- .../BrushConfigChunkAccessor.java | 2 +- .../BrushConfigCommandExecutor.java | 17 +- .../scriptedbrushes/BrushConfigEditStore.java | 70 +- .../scriptedbrushes/ScriptedBrushAsset.java | 38 + .../commands/BrushConfigClearCommand.java | 1 + .../commands/BrushConfigCommand.java | 5 + .../commands/BrushConfigLoadCommand.java | 18 +- .../sequential/ClearRotationOperation.java | 31 + .../operations/sequential/ErodeOperation.java | 2 +- .../operations/sequential/LayerOperation.java | 4 +- .../LoadIntFromToolArgOperation.java | 2 +- .../sequential/PastePrefabOperation.java | 49 +- .../sequential/RunCommandOperation.java | 10 +- .../flowcontrol/JumpIfBlockTypeOperation.java | 2 +- .../loops/CircleOffsetAndLoopOperation.java | 18 +- .../loops/CircleOffsetFromArgOperation.java | 18 +- .../sequential/offsets/OffsetOperation.java | 8 +- .../offsets/RandomOffsetOperation.java | 2 +- .../transforms/RotateOperation.java | 78 + .../operations/system/BrushOperation.java | 4 + .../scriptedbrushes/ui/ScriptedBrushPage.java | 125 +- .../snapshot/BlockSelectionSnapshot.java | 6 +- .../snapshot/ClipboardBoundsSnapshot.java | 8 +- .../snapshot/ClipboardContentsSnapshot.java | 8 +- .../snapshot/ClipboardSnapshot.java | 12 +- .../snapshot/EntityAddSnapshot.java | 4 +- .../snapshot/EntityRemoveSnapshot.java | 4 +- .../buildertools/snapshot/EntitySnapshot.java | 10 +- .../snapshot/EntityTransformSnapshot.java | 12 +- .../snapshot/SelectionSnapshot.java | 4 +- .../tooloperations/FloodOperation.java | 5 +- .../tooloperations/LaserPointerOperation.java | 23 +- .../tooloperations/LayersOperation.java | 29 +- .../tooloperations/NoiseOperation.java | 6 +- .../tooloperations/OperationFactory.java | 7 +- .../tooloperations/PaintOperation.java | 15 +- .../tooloperations/RevolveOperation.java | 480 +++ .../tooloperations/ScatterOperation.java | 5 +- .../tooloperations/SculptOperation.java | 10 +- .../tooloperations/SmoothOperation.java | 2 +- .../tooloperations/TintOperation.java | 145 +- .../tooloperations/ToolOperation.java | 574 +++- .../tooloperations/transform/Composite.java | 2 +- .../tooloperations/transform/Mirror.java | 10 +- .../tooloperations/transform/Rotate.java | 14 +- .../tooloperations/transform/Transform.java | 2 +- .../tooloperations/transform/Translate.java | 4 +- .../builtin/buildertools/utils/Material.java | 52 +- .../buildertools/utils/PasteToolUtil.java | 13 +- .../utils/RecursivePrefabLoader.java | 7 +- .../commandmacro/MacroCommandBase.java | 2 +- .../builtin/crafting/CraftingPlugin.java | 87 +- .../crafting/component/BenchBlock.java | 108 + .../crafting/component/CraftingManager.java | 346 ++- .../component/ProcessingBenchBlock.java | 896 ++++++ .../interaction/OpenBenchPageInteraction.java | 62 +- .../OpenProcessingBenchInteraction.java | 116 +- .../builtin/crafting/state/BenchState.java | 143 - .../crafting/state/ProcessingBenchState.java | 795 ----- .../builtin/crafting/system/BenchSystems.java | 490 +++ .../system/PlayerCraftingSystems.java | 9 - .../builtin/crafting/window/BenchWindow.java | 40 +- .../crafting/window/CraftingWindow.java | 20 +- .../window/DiagramCraftingWindow.java | 59 +- .../crafting/window/FieldCraftingWindow.java | 2 +- .../window/ProcessingBenchWindow.java | 94 +- .../crafting/window/SimpleCraftingWindow.java | 28 +- .../window/StructuralCraftingWindow.java | 50 +- .../creativehub/CreativeHubPlugin.java | 4 +- .../interactions/HubPortalInteraction.java | 5 +- .../builtin/deployables/DeployablesUtils.java | 10 +- .../component/DeployableComponent.java | 2 +- .../component/DeployableOwnerComponent.java | 3 +- .../DeployableProjectileComponent.java | 11 +- .../DeployableProjectileShooterComponent.java | 8 +- .../config/DeployableAoeConfig.java | 9 +- .../deployables/config/DeployableSpawner.java | 7 +- .../config/DeployableTrapConfig.java | 7 +- .../config/DeployableTrapSpawnerConfig.java | 8 +- .../config/DeployableTurretConfig.java | 78 +- ...awnDeployableAtHitLocationInteraction.java | 11 +- ...SpawnDeployableFromRaycastInteraction.java | 21 +- .../deployables/system/DeployablesSystem.java | 8 +- .../builtin/fluid/DisabledFluidResource.java | 49 + .../hytale/builtin/fluid/FluidCommand.java | 13 +- .../hytale/builtin/fluid/FluidPlugin.java | 39 +- .../hytale/builtin/fluid/FluidSystems.java | 89 +- .../entity => }/EntityPlacementData.java | 6 +- .../{newsystem => }/GridUtils.java | 135 +- .../builtin/hytalegenerator/PropField.java | 33 - .../builtin/hytalegenerator/PropRuntime.java | 24 + .../builtin/hytalegenerator/VectorUtil.java | 147 +- .../NViewport.java => Viewport.java} | 7 +- .../{datastructures => }/WeightedMap.java | 2 +- .../hytalegenerator/assets/AssetManager.java | 144 +- .../hytalegenerator/assets/ValidatorUtil.java | 12 +- .../AssignmentsAsset.java | 25 +- .../ConstantAssignmentsAsset.java | 14 +- .../FieldFunctionAssignmentsAsset.java | 10 +- .../ImportedAssignmentsAsset.java | 8 +- .../SandwichAssignmentsAsset.java | 10 +- .../WeightedAssignmentsAsset.java | 14 +- .../assets/biomes/BiomeAsset.java | 18 +- .../assets/bounds/DecimalBounds3dAsset.java | 7 +- .../assets/bounds/IntegerBounds3dAsset.java | 7 +- .../assets/curves/ClampCurveAsset.java | 2 +- .../assets/curves/DistanceSCurveAsset.java | 2 +- .../curves/SmoothCeilingCurveAsset.java | 2 +- .../assets/curves/SmoothClampCurveAsset.java | 2 +- .../assets/curves/SmoothFloorCurveAsset.java | 2 +- .../assets/curves/SmoothMaxCurveAsset.java | 2 +- .../assets/curves/SmoothMinCurveAsset.java | 2 +- .../curves/legacy/NodeFunctionYOutAsset.java | 33 +- .../assets/curves/legacy/PointYOutAsset.java | 2 +- .../curves/manual/ManualCurveAsset.java | 4 +- .../assets/curves/manual/PointInOutAsset.java | 2 +- .../assets/density/AngleDensityAsset.java | 5 +- .../assets/density/AxisDensityAsset.java | 24 +- .../density/CellNoise2DDensityAsset.java | 6 +- .../density/CellNoise3DDensityAsset.java | 6 +- .../assets/density/CuboidDensityAsset.java | 26 +- .../assets/density/CylinderDensityAsset.java | 5 +- .../assets/density/DensityAsset.java | 6 +- .../assets/density/EllipsoidDensityAsset.java | 26 +- .../assets/density/GradientDensityAsset.java | 26 +- .../assets/density/PlaneDensityAsset.java | 24 +- .../density/PositionsTwistDensityAsset.java | 5 +- .../assets/density/RotatorDensityAsset.java | 5 +- .../assets/density/ShellDensityAsset.java | 5 +- .../density/SimplexNoise2dDensityAsset.java | 4 +- .../density/SimplexNoise3DDensityAsset.java | 4 +- .../density/VectorWarpDensityAsset.java | 5 +- .../assets/density/YSampledDensityAsset.java | 5 +- .../DistanceFunctionAsset.java | 2 +- .../EuclideanDistanceFunctionAsset.java | 2 +- .../ManhattanDistanceFunctionAsset.java | 2 +- .../returntypes/CellValueReturnTypeAsset.java | 4 +- .../returntypes/CurveReturnTypeAsset.java | 4 +- .../returntypes/DensityReturnTypeAsset.java | 4 +- .../Distance2AddReturnTypeAsset.java | 4 +- .../Distance2DivReturnTypeAsset.java | 4 +- .../Distance2MulReturnTypeAsset.java | 4 +- .../returntypes/Distance2ReturnTypeAsset.java | 4 +- .../Distance2SubReturnTypeAsset.java | 4 +- .../returntypes/DistanceReturnTypeAsset.java | 4 +- .../returntypes/ImportedReturnTypeAsset.java | 11 +- .../returntypes/ReturnTypeAsset.java | 4 +- .../EnvironmentProviderAsset.java | 4 +- .../MaterialProviderAsset.java | 4 +- .../WeightedMaterialProviderAsset.java | 2 +- .../WeightedThicknessLayerAsset.java | 2 +- .../noisegenerators/CellNoiseAsset.java | 39 +- .../assets/noisegenerators/NoiseAsset.java | 4 +- .../noisegenerators/SimplexNoiseAsset.java | 4 +- .../assets/patterns/AndPatternAsset.java | 3 +- .../assets/patterns/BlockSetPatternAsset.java | 3 +- .../assets/patterns/CeilingPatternAsset.java | 3 +- .../assets/patterns/ConstantPatternAsset.java | 21 +- .../assets/patterns/CuboidPatternAsset.java | 10 +- .../assets/patterns/DensityPatternAsset.java | 3 +- .../assets/patterns/FloorPatternAsset.java | 3 +- .../assets/patterns/GapPatternAsset.java | 73 - .../assets/patterns/ImportedPatternAsset.java | 7 +- .../assets/patterns/MaterialPatternAsset.java | 3 +- .../assets/patterns/NotPatternAsset.java | 3 +- .../assets/patterns/OffsetPatternAsset.java | 10 +- .../assets/patterns/OrPatternAsset.java | 3 +- .../assets/patterns/PatternAsset.java | 4 +- .../assets/patterns/RotatorPatternAsset.java | 38 + .../assets/patterns/SurfacePatternAsset.java | 3 +- .../assets/patterns/WallPatternAsset.java | 3 +- .../MeshPointGeneratorAsset.java | 6 +- .../NoPointGeneratorAsset.java | 17 +- .../pointgenerators/PointGeneratorAsset.java | 4 +- .../AnchorPositionProviderAsset.java | 9 +- .../BaseHeightPositionProviderAsset.java | 8 +- .../BoundPositionProviderAsset.java | 3 +- .../CachedPositionProviderAsset.java | 5 +- .../ClustersPositionProviderAsset.java | 64 + .../EmptyPositionProviderAsset.java | 14 + ...nctionOccurrencePositionProviderAsset.java | 3 +- .../FieldFunctionPositionProviderAsset.java | 3 +- .../FrameworkPositionProviderAsset.java | 5 +- .../ImportedPositionProviderAsset.java | 5 +- .../Jitter2dPositionProviderAsset.java | 55 + .../Jitter3dPositionProviderAsset.java | 55 + .../ListPositionProviderAsset.java | 23 +- .../Mesh2DPositionProviderAsset.java | 5 +- .../Mesh3DPositionProviderAsset.java | 7 +- .../OffsetPositionProviderAsset.java | 39 +- .../PositionProviderAsset.java | 11 +- .../ScalerPositionProviderAsset.java | 64 + ...SimpleHorizontalPositionProviderAsset.java | 3 +- .../SquareGrid2dPositionProviderAsset.java | 21 + .../SquareGrid3dPositionProviderAsset.java | 21 + ...TriangularGrid2dPositionProviderAsset.java | 21 + .../UnionPositionProviderAsset.java | 3 +- .../AssignedPropDistributionAsset.java | 47 + .../ConstantPropDistributionAsset.java | 40 + .../ImportedPropDistributionAsset.java | 36 + .../NoPropDistributionAsset.java | 18 + .../PositionsPropDistributionAsset.java | 43 + .../PropDistributionAsset.java | 104 + .../UnionPropDistributionAsset.java | 43 + .../PropRuntimeAsset.java | 42 +- .../assets/props/BoxPropAsset.java | 16 +- .../assets/props/ClusterPropAsset.java | 42 +- .../assets/props/ColumnPropAsset.java | 13 +- .../assets/props/CuboidPropAsset.java | 48 + .../assets/props/DensityPropAsset.java | 81 +- .../props/DensitySelectorPropAsset.java | 105 + .../assets/props/EmptyPropAsset.java | 21 + .../assets/props/ImportedPropAsset.java | 7 +- .../assets/props/LocatorPropAsset.java | 58 + .../assets/props/ManualPropAsset.java | 75 + .../assets/props/MaskPropAsset.java | 42 + .../assets/props/NoPropAsset.java | 59 - .../assets/props/OffsetPropAsset.java | 10 +- .../assets/props/OrienterPropAsset.java | 86 + .../assets/props/PondFillerPropAsset.java | 56 +- .../assets/props/PropAsset.java | 12 +- .../assets/props/QueuePropAsset.java | 13 +- .../assets/props/RandomRotatorPropAsset.java | 75 + .../assets/props/StaticRotatorPropAsset.java | 43 + .../assets/props/UnionPropAsset.java | 9 +- .../assets/props/WeightedPropAsset.java | 7 +- .../props/prefabprop/PrefabFileVisitor.java | 23 +- .../assets/props/prefabprop/PrefabLoader.java | 31 +- .../props/prefabprop/PrefabPropAsset.java | 138 +- .../directionality/DirectionalityAsset.java | 6 +- .../ImportedDirectionalityAsset.java | 2 +- .../PatternDirectionalityAsset.java | 23 +- .../RandomDirectionalityAsset.java | 4 +- .../StaticDirectionalityAsset.java | 23 +- .../assets/scanners/AreaScannerAsset.java | 7 +- .../scanners/ColumnLinearScannerAsset.java | 5 +- .../scanners/ColumnRandomScannerAsset.java | 7 +- .../assets/scanners/DirectScannerAsset.java | 21 + .../assets/scanners/ImportedScannerAsset.java | 7 +- .../assets/scanners/LinearScannerAsset.java | 46 + .../assets/scanners/OriginScannerAsset.java | 20 - .../assets/scanners/QueueScannerAsset.java | 43 + .../assets/scanners/RadialScannerAsset.java | 36 + .../assets/scanners/RandomScannerAsset.java | 49 + .../assets/scanners/ScannerAsset.java | 2 +- .../assets/terrains/DensityTerrainAsset.java | 4 +- .../assets/terrains/TerrainAsset.java | 4 +- .../tintproviders/TintProviderAsset.java | 4 +- .../CacheVectorProviderAsset.java | 2 +- .../ConstantVectorProviderAsset.java | 7 +- .../DensityGradientVectorProviderAsset.java | 2 +- .../ExportedVectorProviderAsset.java | 2 +- .../ImportedVectorProviderAsset.java | 2 +- .../vectorproviders/VectorProviderAsset.java | 4 +- .../worldstructures/WorldStructureAsset.java | 4 +- .../Assignments.java | 20 +- .../ConstantAssignments.java | 15 +- .../FieldFunctionAssignments.java | 46 +- .../SandwichAssignments.java | 34 +- .../WeightedAssignments.java | 38 +- .../builtin/hytalegenerator/biome/Biome.java | 2 - .../hytalegenerator/biome/PropsSource.java | 9 +- .../hytalegenerator/biome/SimpleBiome.java | 38 +- .../hytalegenerator/bounds/Bounds3d.java | 152 +- .../hytalegenerator/bounds/Bounds3i.java | 161 +- .../hytalegenerator/bounds/SpaceSize.java | 88 - .../cartas/SimpleNoiseCarta.java | 6 +- .../commands/ViewportCommand.java | 22 +- .../stagedconveyor/ContextDependency.java | 192 -- .../datastructures/CollectionFactory.java | 22 - .../datastructures/TieredList.java | 201 -- .../bicoordinatecache/BiCoordinateCache.java | 15 - .../HashedBiCoordinateCache.java | 64 - .../WrappedBiCoordinateCache.java | 104 - .../WrappedBiCoordinateDoubleCache.java | 100 - .../compression/Compressor.java | 90 - .../voxelspace/ArrayVoxelSpace.java | 330 -- .../voxelspace/BooleanVoxelSpace.java | 382 --- .../datastructures/voxelspace/NullSpace.java | 143 - .../voxelspace/VoxelConsumer.java | 6 - .../voxelspace/VoxelCoordinate.java | 33 - .../datastructures/voxelspace/VoxelSpace.java | 64 - .../voxelspace/VoxelSpaceUtil.java | 97 - .../voxelspace/WindowVoxelSpace.java | 210 -- .../hytalegenerator/density/Density.java | 30 +- .../density/nodes/AmplitudeDensity.java | 2 +- .../density/nodes/AnchorDensity.java | 6 +- .../density/nodes/AngleDensity.java | 4 +- .../density/nodes/AxisDensity.java | 4 +- .../density/nodes/CacheDensity.java | 4 +- .../density/nodes/ClampDensity.java | 2 +- .../density/nodes/CylinderDensity.java | 2 +- .../density/nodes/DistanceDensity.java | 2 +- .../nodes/FastGradientWarpDensity.java | 8 +- .../density/nodes/GradientDensity.java | 14 +- .../density/nodes/GradientWarpDensity.java | 16 +- .../density/nodes/MultiCacheDensity.java | 4 +- .../density/nodes/MultiMixDensity.java | 2 +- .../density/nodes/Noise2dDensity.java | 2 +- .../density/nodes/Noise3dDensity.java | 2 +- .../density/nodes/NormalizerDensity.java | 2 +- .../density/nodes/PlaneDensity.java | 6 +- .../PositionsHorizontalPinchDensity.java | 41 +- .../density/nodes/PositionsPinchDensity.java | 31 +- .../density/nodes/PositionsTwistDensity.java | 35 +- .../density/nodes/RotatorDensity.java | 10 +- .../density/nodes/ScaleDensity.java | 6 +- .../density/nodes/SelectorDensity.java | 4 +- .../density/nodes/ShellDensity.java | 7 +- .../density/nodes/SliderDensity.java | 4 +- .../density/nodes/SmoothCeilingDensity.java | 2 +- .../density/nodes/SmoothClampDensity.java | 2 +- .../density/nodes/SmoothFloorDensity.java | 2 +- .../density/nodes/SmoothMaxDensity.java | 2 +- .../density/nodes/SmoothMinDensity.java | 2 +- .../density/nodes/TerrainDensity.java | 3 +- .../density/nodes/VectorWarpDensity.java | 8 +- .../density/nodes/XOverrideDensity.java | 4 +- .../density/nodes/YOverrideDensity.java | 4 +- .../density/nodes/YSampledDensity.java | 22 +- .../density/nodes/ZOverrideDensity.java | 4 +- .../nodes/positions/PositionsDensity.java | 30 +- .../distancefunctions/DistanceFunction.java | 2 +- .../EuclideanDistanceFunction.java | 2 +- .../ManhattanDistanceFunction.java | 2 +- .../returntypes/CellValueReturnType.java | 2 +- .../returntypes/CurveReturnType.java | 2 +- .../returntypes/DensityReturnType.java | 6 +- .../returntypes/Distance2AddReturnType.java | 2 +- .../returntypes/Distance2DivReturnType.java | 2 +- .../returntypes/Distance2MulReturnType.java | 2 +- .../returntypes/Distance2ReturnType.java | 2 +- .../returntypes/Distance2SubReturnType.java | 2 +- .../returntypes/DistanceReturnType.java | 2 +- .../positions/returntypes/ReturnType.java | 2 +- .../TerrainDensityProvider.java | 4 +- .../bufferbundle/BufferBundle.java} | 140 +- .../engine/bufferbundle/buffers/Buffer.java | 6 + .../buffers/CountedPixelBuffer.java} | 47 +- .../bufferbundle/buffers/EntityBuffer.java} | 10 +- .../bufferbundle/buffers/PixelBuffer.java} | 6 +- .../buffers/SimplePixelBuffer.java} | 43 +- .../bufferbundle/buffers/VoxelBuffer.java} | 75 +- .../buffers/type/BufferType.java} | 14 +- .../buffers/type/ParametrizedBufferType.java} | 14 +- .../chunkgenerator/ChunkGenerator.java | 5 +- .../chunkgenerator/ChunkRequest.java | 2 +- .../chunkgenerator/FallbackGenerator.java | 8 +- .../chunkgenerator/StagedChunkGenerator.java} | 244 +- .../containers/FloatContainer3d.java | 8 +- .../engine/entityfunnel/EntityFunnel.java | 25 + .../entityfunnel/RotationEntityFunnel.java | 60 + .../performanceinstruments/MemInstrument.java | 2 +- .../TimeInstrument.java | 14 +- .../stages/BiomeDistanceStage.java} | 107 +- .../engine/stages/BiomeStage.java | 68 + .../stages/EnvironmentStage.java} | 58 +- .../engine/stages/PropStage.java | 277 ++ .../hytalegenerator/engine/stages/Stage.java | 34 + .../stages/TerrainStage.java} | 149 +- .../stages/TintStage.java} | 57 +- .../engine/views/EntityBufferView.java | 90 + .../engine/views/PixelBufferView.java | 88 + .../engine/views/VoxelBufferView.java | 134 + .../EnvironmentProvider.java | 2 +- .../framework/cartas/ImageCarta.java | 144 - .../framework/cartas/LayeredCarta.java | 60 - .../framework/cartas/SingleElementCarta.java | 32 - .../functions/BiDouble2DoubleFunction.java | 6 - .../functions/QuadDoubleFunction.java | 6 - .../functions/QuintoDoubleFunction.java | 6 - .../interfaces/functions/TriCarta.java | 13 - .../functions/TriDoubleFunction.java | 6 - .../interfaces/functions/TriFunction.java | 6 - .../framework/math/BitConverter.java | 152 - .../framework/math/CoPrimeGenerator.java | 60 - .../framework/math/Combiner.java | 127 - .../framework/math/MultipliedIteration.java | 35 - .../framework/math/Probability.java | 10 - .../framework/math/RegionGrid.java | 27 - .../framework/math/SeedGenerator.java | 82 - .../framework/math/Splitter.java | 136 - .../framework/math/Stepinizer.java | 93 - .../framework/shaders/MaskShader.java | 79 - .../framework/shaders/RelationalShader.java | 46 - .../framework/shaders/Shader.java | 9 - .../framework/shaders/SimpleShader.java | 41 - .../framework/shaders/WeighedShader.java | 54 - .../iterators/BackwardIntIterator.java | 51 - .../iterators/ForwardIntIterator.java | 51 - .../iterators/IntIterators.java | 11 - .../hytalegenerator/material/Material.java | 14 +- .../material/MaterialCache.java | 14 + .../materialproviders/MaterialProvider.java | 21 +- .../WeightedMaterialProvider.java | 21 +- .../spaceanddepth/layers/NoiseThickness.java | 2 +- .../layers/RangedThicknessLayer.java | 10 +- .../layers/WeightedThicknessLayer.java | 12 +- .../{framework => }/math/Calculator.java | 10 +- .../math/InterpolatedCurve.java | 2 +- .../{framework => }/math/Interpolation.java | 2 +- .../{framework => }/math/NodeFunction.java | 2 +- .../{framework => }/math/Normalizer.java | 2 +- .../bufferbundle/buffers/NBuffer.java | 6 - .../newsystem/stages/NBiomeStage.java | 67 - .../newsystem/stages/NPropStage.java | 246 -- .../newsystem/stages/NStage.java | 34 - .../newsystem/stages/NTestPropStage.java | 105 - .../newsystem/stages/NTestTerrainStage.java | 80 - .../newsystem/views/EntityContainer.java | 10 - .../newsystem/views/NEntityBufferView.java | 106 - .../newsystem/views/NPixelBufferView.java | 179 -- .../newsystem/views/NVoxelBufferView.java | 210 -- .../{fields => }/noise/CellNoiseField.java | 3 +- .../{fields => noise}/FastNoiseLite.java | 6 +- .../{fields => }/noise/NoiseField.java | 2 +- .../{fields => }/noise/Simplex.java | 2 +- .../{fields => }/noise/SimplexNoiseField.java | 2 +- .../pointprovider}/JitterPointField.java | 28 +- .../pointprovider}/PointField.java | 10 +- .../pointprovider}/PointProvider.java | 10 +- .../hytalegenerator/patterns/AndPattern.java | 20 +- .../patterns/ConstantPattern.java | 25 + .../patterns/CuboidPattern.java | 22 +- .../patterns/FieldFunctionPattern.java | 33 +- .../hytalegenerator/patterns/GapPattern.java | 238 -- .../patterns/MaterialPattern.java | 17 +- .../patterns/MaterialSetPattern.java | 18 +- .../hytalegenerator/patterns/NotPattern.java | 11 +- .../patterns/OffsetPattern.java | 17 +- .../hytalegenerator/patterns/OrPattern.java | 20 +- .../hytalegenerator/patterns/Pattern.java | 49 +- .../patterns/RotatorPattern.java | 41 + .../patterns/SurfacePattern.java | 40 +- .../hytalegenerator/patterns/WallPattern.java | 42 +- .../builtin/hytalegenerator/pipe/Control.java | 9 + .../builtin/hytalegenerator/pipe/Pipe.java | 26 + .../hytalegenerator/plugin/Handle.java | 11 +- .../plugin/HandleProvider.java | 2 +- .../plugin/HytaleGenerator.java | 127 +- .../AnchorPositionProvider.java | 70 +- .../BaseHeightPositionProvider.java | 45 +- .../BoundPositionProvider.java | 12 +- .../ClustersPositionProvider.java | 88 + .../EmptyPositionProvider.java | 11 + ...eldFunctionOccurrencePositionProvider.java | 59 +- .../FieldFunctionPositionProvider.java | 60 +- .../Jitter2dPositionProvider.java | 71 + .../Jitter3dPositionProvider.java | 76 + .../ListPositionProvider.java | 45 +- .../Mesh2DPositionProvider.java | 26 - .../Mesh3DPositionProvider.java | 18 - .../OffsetPositionProvider.java | 63 +- .../positionproviders/PositionProvider.java | 52 +- .../ScalerPositionProvider.java | 56 + .../SimpleHorizontalPositionProvider.java | 36 +- .../SquareGrid2dPositionProvider.java | 47 + .../SquareGrid3dPositionProvider.java | 50 + .../TriangularGrid2dPositionProvider.java | 63 + .../UnionPositionProvider.java | 4 +- .../cached/CacheThreadMemory.java | 2 +- .../cached/CachedPositionProvider.java | 41 +- .../deprecated/Mesh2DPositionProvider.java | 29 + .../deprecated/Mesh3DPositionProvider.java | 21 + .../AssignedPropDistribution.java | 61 + .../ConstantPropDistribution.java | 61 + .../propdistributions/NoPropDistribution.java | 20 + .../PositionsPropDistribution.java | 55 + .../propdistributions/PropDistribution.java | 40 + .../UnionPropDistribution.java | 31 + .../hytalegenerator/props/BoxProp.java | 109 - .../hytalegenerator/props/ClusterProp.java | 135 - .../hytalegenerator/props/CuboidProp.java | 64 + .../hytalegenerator/props/DensityProp.java | 252 +- .../props/DensitySelectorProp.java | 66 + .../hytalegenerator/props/EmptyProp.java | 25 + .../hytalegenerator/props/LocatorProp.java | 100 + .../hytalegenerator/props/ManualProp.java | 71 + .../hytalegenerator/props/MaskProp.java | 43 + .../hytalegenerator/props/OffsetProp.java | 42 +- .../hytalegenerator/props/OrienterProp.java | 176 ++ .../hytalegenerator/props/PondFillerProp.java | 206 ++ .../hytalegenerator/props/PrefabProp.java | 252 ++ .../builtin/hytalegenerator/props/Prop.java | 117 +- .../hytalegenerator/props/QueueProp.java | 83 +- .../props/StaticRotatorProp.java | 63 + .../hytalegenerator/props/UnionProp.java | 79 +- .../hytalegenerator/props/WeightedProp.java | 59 +- .../props/deprecated/BoxProp.java | 102 + .../props/deprecated/ClusterProp.java | 153 + .../props/{ => deprecated}/ColumnProp.java | 67 +- .../props/deprecated/DensityProp.java | 210 ++ .../PositionListScanResult.java | 4 +- .../{ => deprecated}/PositionScanResult.java | 8 +- .../props/{ => deprecated}/ScanResult.java | 2 +- .../directionality/Directionality.java | 19 +- .../directionality/OrthogonalDirection.java | 2 +- .../directionality/PatternDirectionality.java | 26 +- .../directionality/RandomDirectionality.java | 19 +- .../directionality/RotatedPosition.java | 12 +- .../RotatedPositionsScanResult.java | 4 +- .../directionality/StaticDirectionality.java | 11 +- .../filler/FillerPropScanResult.java | 16 +- .../filler/PondFillerProp.java | 395 ++- .../prefab/MoldingDirection.java | 2 +- .../prefab/PrefabMoldingConfiguration.java | 2 +- .../{ => deprecated}/prefab/PrefabProp.java | 221 +- .../prefab/PrefabPropUtil.java} | 13 +- .../builtin/hytalegenerator/rng/Rng.java | 43 + .../builtin/hytalegenerator/rng/RngField.java | 30 + .../{seed => rng}/SeedBox.java | 6 +- .../hytalegenerator/scanners/AreaScanner.java | 92 - .../scanners/ColumnLinearScanner.java | 79 - .../scanners/ColumnRandomScanner.java | 154 - .../scanners/DirectScanner.java | 36 + .../scanners/EmptyScanner.java | 26 + .../scanners/LinearScanner.java | 122 + .../scanners/OriginScanner.java | 36 - .../scanners/QueueScanner.java | 41 + .../scanners/RadialScanner.java | 112 + .../scanners/RandomScanner.java | 112 + .../hytalegenerator/scanners/Scanner.java | 61 +- .../scanners/deprecated/AreaScanner.java | 93 + .../deprecated/ColumnLinearScanner.java | 86 + .../deprecated/ColumnRandomScanner.java | 154 + .../tintproviders/TintProvider.java | 4 +- .../vectorproviders/CacheVectorProvider.java | 8 +- .../ConstantVectorProvider.java | 6 +- .../DensityGradientVectorProvider.java | 12 +- .../vectorproviders/VectorProvider.java | 4 +- .../voxelspace/ArrayVoxelSpace.java | 74 + .../voxelspace/MaskVoxelSpace.java | 68 + .../hytalegenerator/voxelspace/NullSpace.java | 55 + .../voxelspace/RotationVoxelSpace.java | 99 + .../voxelspace/VoxelSpace.java | 23 + .../voxelspace/VoxelSpaceUtil.java | 47 + .../voxelspace/WindowVoxelSpace.java | 81 + .../WorkerIndexer.java | 11 +- .../functions => worldstructure}/BiCarta.java | 5 +- .../worldstructure/WorldStructure.java | 1 - .../builtin/instances/InstancesPlugin.java | 73 +- .../blocks/ConfigurableInstanceBlock.java | 17 +- .../command/InstanceMigrateCommand.java | 41 +- .../command/InstanceSpawnCommand.java | 21 +- .../TeleportConfigInstanceInteraction.java | 21 +- .../TeleportInstanceInteraction.java | 26 +- .../page/ConfigureInstanceBlockPage.java | 14 +- .../instances/page/InstanceListPage.java | 5 +- .../builtin/model/pages/ChangeModelPage.java | 16 +- .../hytale/builtin/mounts/BlockMountAPI.java | 15 +- .../builtin/mounts/BlockMountComponent.java | 21 +- .../hytale/builtin/mounts/MountPlugin.java | 2 + .../hytale/builtin/mounts/MountSystems.java | 20 +- .../builtin/mounts/MountedByComponent.java | 4 +- .../builtin/mounts/MountedComponent.java | 10 +- .../builtin/mounts/NPCMountSystems.java | 17 + .../mounts/interactions/MountInteraction.java | 11 +- .../interactions/SeatingInteraction.java | 17 +- .../SpawnMinecartInteraction.java | 81 +- .../builtin/mounts/npc/ActionMount.java | 5 +- .../CombatActionEvaluatorSystems.java | 2 +- .../corecomponents/ActionCombatAbility.java | 12 +- .../corecomponents/CombatTargetCollector.java | 4 +- .../SensorCombatActionEvaluator.java | 4 +- .../evaluator/CombatActionEvaluator.java | 10 +- .../combatactions/AbilityCombatAction.java | 10 +- .../BasicAttackTargetCombatAction.java | 8 +- .../memory/TargetMemory.java | 6 +- .../builtin/npceditor/NPCEditorPlugin.java | 2 +- .../parkour/ParkourCheckpointSystems.java | 26 +- .../hytale/builtin/parkour/ParkourPlugin.java | 5 +- .../commands/CheckpointAddCommand.java | 6 +- .../builtin/path/PathSpatialSystem.java | 2 +- .../builtin/path/PrefabPathCollection.java | 9 +- .../builtin/path/PrefabPathSystems.java | 38 +- .../hytale/builtin/path/WorldPathData.java | 2 +- .../path/commands/PrefabPathHelper.java | 12 +- .../path/commands/PrefabPathNodesCommand.java | 6 +- .../commands/WorldPathBuilderCommand.java | 4 +- .../path/entities/PatrolPathMarkerEntity.java | 6 +- .../hytale/builtin/path/path/IPrefabPath.java | 5 +- .../hytale/builtin/path/path/PatrolPath.java | 10 +- .../builtin/path/path/TransientPath.java | 16 +- .../path/path/TransientPathDefinition.java | 6 +- .../portals/commands/player/LeaveCommand.java | 5 +- .../portals/components/PortalDevice.java | 12 + .../interactions/EnterPortalInteraction.java | 89 +- .../interactions/ReturnPortalInteraction.java | 37 +- .../curse/DeleteCursedItemsOnSpawnSystem.java | 10 +- .../systems/curse/DiedInPortalSystem.java | 18 +- .../VoidInvasionPortalsSpawnSystem.java | 50 +- .../systems/voidevent/VoidSpawnerSystems.java | 4 +- .../portals/ui/PortalDeviceActivePage.java | 71 +- .../portals/ui/PortalDevicePageSupplier.java | 13 +- .../portals/ui/PortalDeviceSummonPage.java | 187 +- .../builtin/portals/ui/PortalSpawnFinder.java | 14 +- .../builtin/portals/utils/CursedItems.java | 5 - .../utils/posqueries/PositionPredicate.java | 2 +- .../utils/posqueries/SpatialQuery.java | 2 +- .../utils/posqueries/SpatialQueryDebug.java | 2 +- .../posqueries/generators/SearchBelow.java | 4 +- .../posqueries/generators/SearchCircular.java | 4 +- .../posqueries/generators/SearchCone.java | 4 +- .../posqueries/predicates/FitsAPortal.java | 7 +- .../predicates/NotNearAnyInHashGrid.java | 2 +- .../posqueries/predicates/NotNearPoint.java | 4 +- .../posqueries/predicates/NotNearPointXZ.java | 6 +- .../predicates/generic/FilterQuery.java | 2 +- .../predicates/generic/FlatMapQuery.java | 2 +- .../portals/utils/spatial/OctTree.java | 118 - .../utils/spatial/SpatialHashGrid.java | 42 +- .../procedures/SpreadToProcedure.java | 20 +- .../builtin/teleport/TeleportPlugin.java | 43 +- .../commands/teleport/SpawnCommand.java | 22 +- .../commands/teleport/SpawnSetCommand.java | 27 +- .../commands/teleport/TeleportAllCommand.java | 40 +- .../teleport/TeleportHomeCommand.java | 8 +- .../commands/teleport/TeleportTopCommand.java | 17 +- .../teleport/TeleportWorldCommand.java | 56 +- .../variant/TeleportOtherToPlayerCommand.java | 14 +- .../TeleportPlayerToCoordinatesCommand.java | 33 +- .../variant/TeleportToCoordinatesCommand.java | 35 +- .../variant/TeleportToPlayerCommand.java | 12 +- .../teleport/commands/warp/WarpCommand.java | 8 +- .../commands/warp/WarpSetCommand.java | 8 +- .../teleport/components/TeleportHistory.java | 104 +- .../weather/components/WeatherTracker.java | 14 +- .../weather/systems/WeatherSystem.java | 13 +- .../builtin/worldgen/WorldGenPlugin.java | 59 + .../worldgen/modifier/EventHandler.java | 108 + .../builtin/worldgen/modifier/Target.java | 37 + .../worldgen/modifier/WorldGenModifier.java | 91 + .../worldgen/modifier/content/Codecs.java | 43 + .../worldgen/modifier/content/Content.java | 35 + .../modifier/content/FileContent.java | 38 + .../content/cave/CaveTypeContent.java | 129 + .../content/cave/CaveTypeGenerator.java | 15 + .../modifier/content/cave/ore/OreCluster.java | 96 + .../modifier/content/common/BlockEntry.java | 50 + .../modifier/content/common/BlockMask.java | 191 ++ .../modifier/content/common/HeightMask.java | 79 + .../modifier/content/common/NoiseMask.java | 80 + .../modifier/content/common/PointGrid.java | 73 + .../content/cover/BiomeCoverContent.java | 53 + .../content/cover/CaveCoverContent.java | 56 + .../modifier/content/cover/CoverContent.java | 67 + .../content/fluid/BiomeFluidContent.java | 77 + .../layer/BiomeDynamicLayerContent.java | 88 + .../layer/BiomeStaticLayerContent.java | 93 + .../modifier/content/layer/LayerContent.java | 29 + .../content/layer/NoiseBlockEntry.java | 63 + .../content/prefab/BiomePrefabContent.java | 80 + .../content/prefab/CavePrefabContent.java | 72 + .../content/prefab/PrefabContent.java | 107 + .../content/tint/BiomeTintContent.java | 85 + .../worldgen/modifier/event/ModifyEvent.java | 54 + .../worldgen/modifier/event/ModifyEvents.java | 150 + .../builtin/worldgen/modifier/op/AddOp.java | 39 + .../builtin/worldgen/modifier/op/LogOp.java | 33 + .../builtin/worldgen/modifier/op/Op.java | 18 + .../worldgen/modifier/op/RemoveOp.java | 126 + .../hytale/codec/builder/BuilderField.java | 4 + .../codec/function/BsonFunctionCodec.java | 2 +- .../hytale/codec/validation/Validator.java | 5 + .../common/collection/BucketItemPool.java | 23 +- src/com/hypixel/hytale/common/plugin/Mod.java | 211 ++ .../common/plugin/ModLoadOrderException.java | 24 + .../hypixel/hytale/common/semver/Semver.java | 15 +- .../hytale/common/util/StringUtil.java | 194 -- .../hypixel/hytale/component/Archetype.java | 46 +- .../hytale/component/ArchetypeChunk.java | 168 +- .../hytale/component/CommandBuffer.java | 17 + .../hytale/component/ComponentAccessor.java | 5 + .../hytale/component/ComponentRegistry.java | 77 + .../hytale/component/ComponentType.java | 4 +- src/com/hypixel/hytale/component/Holder.java | 96 +- src/com/hypixel/hytale/component/Ref.java | 36 +- src/com/hypixel/hytale/component/Store.java | 346 ++- .../event/EntityHolderEventType.java | 17 + .../hytale/component/spatial/KDTree.java | 10 +- .../hytale/component/spatial/SpatialData.java | 6 +- .../component/spatial/SpatialResource.java | 10 +- .../component/spatial/SpatialStructure.java | 2 +- .../component/spatial/SpatialSystem.java | 2 +- .../system/EntityHolderEventSystem.java | 22 + src/com/hypixel/hytale/event/EventBus.java | 4 +- .../function/consumer/BiIntConsumer.java | 6 + .../consumer/TriIntObjectConsumer.java | 6 + .../hypixel/hytale/logger/HytaleLogger.java | 2 + .../hytale/logger/util/GithubMessageUtil.java | 8 +- src/com/hypixel/hytale/math/Axis.java | 45 +- src/com/hypixel/hytale/math/Mat4f.java | 106 - src/com/hypixel/hytale/math/Quatf.java | 36 - src/com/hypixel/hytale/math/Vec2f.java | 33 - src/com/hypixel/hytale/math/Vec3f.java | 38 - src/com/hypixel/hytale/math/Vec4f.java | 36 - .../hytale/math/block/BlockConeUtil.java | 124 +- .../hytale/math/block/BlockCubeUtil.java | 58 +- .../hytale/math/block/BlockCylinderUtil.java | 101 +- .../hytale/math/block/BlockDiamondUtil.java | 66 +- .../hytale/math/block/BlockDomeUtil.java | 73 +- .../math/block/BlockInvertedDomeUtil.java | 73 +- .../hytale/math/block/BlockPyramidUtil.java | 122 +- .../hytale/math/block/BlockSphereUtil.java | 154 +- .../hytale/math/block/BlockTorusUtil.java | 50 +- .../hypixel/hytale/math/block/BlockUtil.java | 7 +- .../hytale/math/codec/Vector2dArrayCodec.java | 6 +- .../hytale/math/codec/Vector3dArrayCodec.java | 8 +- .../hytale/math/codec/Vector3iArrayCodec.java | 8 +- .../math/data/Int3ObjectOpenHashMap.java | 234 ++ .../math/hitdetection/HitDetectionBuffer.java | 8 +- .../hitdetection/HitDetectionExecutor.java | 55 +- .../math/hitdetection/MatrixProvider.java | 2 +- .../math/hitdetection/Vector4dBufferList.java | 4 +- .../projection/FrustumProjectionProvider.java | 10 +- .../OrthogonalProjectionProvider.java | 10 +- .../view/DirectionViewProvider.java | 20 +- .../hytale/math/iterator/BlockIterator.java | 4 +- .../math/iterator/BoxBlockIterator.java | 2 +- .../hytale/math/iterator/CircleIterator.java | 13 +- .../hytale/math/iterator/LineIterator.java | 2 +- .../hypixel/hytale/math/matrix/Matrix4d.java | 542 ---- .../hytale/math/matrix/Matrix4dUtil.java | 29 + .../hytale/math/random/RandomExtra.java | 2 +- src/com/hypixel/hytale/math/shape/Box.java | 143 +- src/com/hypixel/hytale/math/shape/Box2D.java | 59 +- .../hypixel/hytale/math/shape/Cylinder.java | 4 +- .../hypixel/hytale/math/shape/Ellipsoid.java | 4 +- .../hytale/math/shape/OriginShape.java | 10 +- src/com/hypixel/hytale/math/shape/Quad2d.java | 8 +- src/com/hypixel/hytale/math/shape/Quad4d.java | 42 +- .../hypixel/hytale/math/shape/Rectangle.java | 2 +- src/com/hypixel/hytale/math/shape/Shape.java | 16 +- .../hypixel/hytale/math/shape/Shape2D.java | 4 +- .../hypixel/hytale/math/shape/Triangle2d.java | 4 +- .../hypixel/hytale/math/shape/Triangle4d.java | 35 +- .../hypixel/hytale/math/shape/ViewUtil.java | 2 +- .../hypixel/hytale/math/util/ChunkUtil.java | 14 + .../hypixel/hytale/math/util/MathUtil.java | 46 +- .../hytale/math/util/NearestBlockUtil.java | 12 +- .../hypixel/hytale/math/vector/Location.java | 38 +- .../hytale/math/vector/Rotation3f.java | 349 +++ .../hytale/math/vector/Rotation3fc.java | 30 + .../hypixel/hytale/math/vector/Transform.java | 69 +- .../hypixel/hytale/math/vector/Vector2d.java | 336 -- .../hytale/math/vector/Vector2dUtil.java | 40 + .../hytale/math/vector/Vector2fUtil.java | 27 + .../hypixel/hytale/math/vector/Vector2i.java | 273 -- .../hytale/math/vector/Vector2iUtil.java | 37 + .../hypixel/hytale/math/vector/Vector2l.java | 269 -- .../hytale/math/vector/Vector3LUtil.java | 28 + .../hypixel/hytale/math/vector/Vector3d.java | 559 ---- .../hytale/math/vector/Vector3dUtil.java | 89 + .../hypixel/hytale/math/vector/Vector3f.java | 616 ---- .../hytale/math/vector/Vector3fUtil.java | 30 + .../hypixel/hytale/math/vector/Vector3i.java | 378 --- .../hytale/math/vector/Vector3iUtil.java | 100 + .../hypixel/hytale/math/vector/Vector3l.java | 374 --- .../hypixel/hytale/math/vector/Vector4d.java | 105 - .../hytale/math/vector/Vector4dUtil.java | 20 + .../hytale/math/vector/VectorBoxUtil.java | 7 +- .../hytale/math/vector/VectorSphereUtil.java | 7 +- .../vector/relative/RelativeVector2d.java | 70 - .../vector/relative/RelativeVector2i.java | 70 - .../vector/relative/RelativeVector2l.java | 70 - .../vector/relative/RelativeVector3d.java | 70 - .../vector/relative/RelativeVector3i.java | 70 - .../vector/relative/RelativeVector3l.java | 70 - .../plugin/early/TransformingClassLoader.java | 1 - .../json/BlendNoisePropertyJsonLoader.java | 4 +- .../hytale/procedurallib/json/JsonLoader.java | 2 + .../hytale/procedurallib/logic/MeshNoise.java | 2 +- .../logic/cell/evaluator/BranchEvaluator.java | 2 +- .../hytale/protocol/AOECircleSelector.java | 36 +- .../hytale/protocol/AOECylinderSelector.java | 38 +- .../hytale/protocol/AbilityEffects.java | 70 +- .../protocol/ActiveAnimationsUpdate.java | 92 +- .../hypixel/hytale/protocol/AmbienceFX.java | 311 +- .../hytale/protocol/AmbienceFXAmbientBed.java | 83 +- .../protocol/AmbienceFXBlockSoundSet.java | 26 +- .../hytale/protocol/AmbienceFXConditions.java | 1275 ++++++-- .../hytale/protocol/AmbienceFXMusic.java | 82 +- .../protocol/AmbienceFXPhysicalMaterial.java | 103 + .../hytale/protocol/AmbienceFXSound.java | 91 +- .../protocol/AmbienceFXSoundEffect.java | 15 +- .../protocol/AmbienceFXSoundPlay3D.java | 3 +- .../hypixel/hytale/protocol/AngledDamage.java | 27 +- .../hytale/protocol/AngledWielding.java | 15 +- .../hypixel/hytale/protocol/Animation.java | 129 +- .../hypixel/hytale/protocol/AnimationSet.java | 137 +- .../hytale/protocol/ApplicationEffects.java | 361 ++- .../hypixel/hytale/protocol/AppliedForce.java | 55 +- .../protocol/ApplyEffectInteraction.java | 368 ++- .../protocol/ApplyForceInteraction.java | 536 ++-- src/com/hypixel/hytale/protocol/Asset.java | 36 +- .../hytale/protocol/AssetIconProperties.java | 49 +- .../hytale/protocol/AudioCategory.java | 46 +- .../hypixel/hytale/protocol/AudioUpdate.java | 12 +- src/com/hypixel/hytale/protocol/Bench.java | 58 +- .../hytale/protocol/BenchRequirement.java | 180 +- .../hytale/protocol/BenchTierLevel.java | 27 +- .../protocol/BenchUpgradeRequirement.java | 60 +- .../hytale/protocol/BlockBreaking.java | 162 +- .../hytale/protocol/BlockBreakingDecal.java | 80 +- .../protocol/BlockConditionInteraction.java | 514 ++-- .../hytale/protocol/BlockFaceSupport.java | 121 +- .../hypixel/hytale/protocol/BlockFlags.java | 13 +- .../hytale/protocol/BlockGathering.java | 92 +- .../hypixel/hytale/protocol/BlockGroup.java | 80 +- .../hytale/protocol/BlockIdMatcher.java | 106 +- .../hypixel/hytale/protocol/BlockMatcher.java | 46 +- .../hypixel/hytale/protocol/BlockMount.java | 82 +- .../protocol/BlockMovementSettings.java | 33 +- .../hytale/protocol/BlockParticleSet.java | 181 +- .../protocol/BlockPlacementSettings.java | 37 +- .../hytale/protocol/BlockPosition.java | 15 +- .../hytale/protocol/BlockRotation.java | 32 +- .../protocol/BlockSelectorToolData.java | 11 +- src/com/hypixel/hytale/protocol/BlockSet.java | 117 +- .../hytale/protocol/BlockSoundSet.java | 122 +- .../hytale/protocol/BlockTextures.java | 314 +- .../hypixel/hytale/protocol/BlockType.java | 2706 ++++++++++------- .../hypixel/hytale/protocol/BlockUpdate.java | 13 +- .../hytale/protocol/BoolParamValue.java | 11 +- .../protocol/BreakBlockInteraction.java | 371 ++- .../protocol/BuilderToolInteraction.java | 367 ++- .../hypixel/hytale/protocol/CameraAxis.java | 82 +- .../hytale/protocol/CameraInteraction.java | 377 ++- .../hytale/protocol/CameraSettings.java | 79 +- .../hypixel/hytale/protocol/CameraShake.java | 63 +- .../hytale/protocol/CameraShakeConfig.java | 81 +- .../protocol/CancelChainInteraction.java | 477 +-- .../hytale/protocol/ChainFlagInteraction.java | 571 ++-- .../hytale/protocol/ChainingInteraction.java | 755 ++--- .../protocol/ChangeActiveSlotInteraction.java | 365 ++- .../protocol/ChangeBlockInteraction.java | 500 +-- .../protocol/ChangeStatInteraction.java | 469 +-- .../protocol/ChangeStateInteraction.java | 588 ++-- .../hytale/protocol/ChargingDelay.java | 19 +- .../hytale/protocol/ChargingInteraction.java | 604 ++-- .../hypixel/hytale/protocol/ClampConfig.java | 15 +- .../ClearEntityEffectInteraction.java | 368 ++- src/com/hypixel/hytale/protocol/Cloud.java | 174 +- src/com/hypixel/hytale/protocol/Color.java | 15 +- .../hypixel/hytale/protocol/ColorAlpha.java | 17 +- .../hypixel/hytale/protocol/ColorLight.java | 17 +- ...atTextEntityUIComponentAnimationEvent.java | 70 +- .../hytale/protocol/CombatTextUpdate.java | 36 +- .../hytale/protocol/ComponentUpdate.java | 6 +- .../hytale/protocol/ConditionInteraction.java | 422 +-- .../protocol/ConditionalBlockSound.java | 82 + .../protocol/ConnectedBlockRuleSet.java | 112 +- .../CooldownConditionInteraction.java | 477 +-- .../hytale/protocol/CraftingRecipe.java | 298 +- .../hypixel/hytale/protocol/DamageCause.java | 104 +- .../hytale/protocol/DamageEffects.java | 140 +- .../protocol/DamageEntityInteraction.java | 857 +++--- .../hypixel/hytale/protocol/DebugFlags.java | 28 + .../hytale/protocol/DeployableConfig.java | 65 +- .../hypixel/hytale/protocol/DetailBox.java | 50 +- .../hypixel/hytale/protocol/Direction.java | 15 +- .../hytale/protocol/DoubleParamValue.java | 11 +- .../hytale/protocol/DynamicLightUpdate.java | 11 +- .../hypixel/hytale/protocol/EasingConfig.java | 20 +- src/com/hypixel/hytale/protocol/Edge.java | 26 +- .../protocol/EffectConditionInteraction.java | 496 +-- .../hypixel/hytale/protocol/EntityEffect.java | 497 +-- .../hytale/protocol/EntityEffectUpdate.java | 89 +- .../hytale/protocol/EntityEffectsUpdate.java | 12 +- .../hytale/protocol/EntityMatcher.java | 20 +- .../hytale/protocol/EntityStatEffects.java | 62 +- .../hytale/protocol/EntityStatOnHit.java | 70 +- .../hytale/protocol/EntityStatType.java | 199 +- .../hytale/protocol/EntityStatUpdate.java | 254 +- .../hytale/protocol/EntityStatsUpdate.java | 77 +- .../hytale/protocol/EntityUIComponent.java | 157 +- .../hypixel/hytale/protocol/EntityUpdate.java | 152 +- .../hytale/protocol/EqualizerEffect.java | 64 +- .../hytale/protocol/EquipmentUpdate.java | 199 +- .../hytale/protocol/ExtraResources.java | 58 +- .../protocol/FirstClickInteraction.java | 367 ++- .../hypixel/hytale/protocol/FloatRange.java | 13 +- src/com/hypixel/hytale/protocol/Fluid.java | 676 ++-- src/com/hypixel/hytale/protocol/FluidFX.java | 191 +- .../protocol/FluidFXMovementSettings.java | 21 +- .../hytale/protocol/FluidParticle.java | 50 +- .../hypixel/hytale/protocol/FogOptions.java | 21 +- .../hytale/protocol/ForkedChainId.java | 25 +- .../hytale/protocol/FormattedMessage.java | 946 +++--- .../protocol/FormattedMessageImage.java | 123 + .../hytale/protocol/HalfFloatPosition.java | 15 +- .../hypixel/hytale/protocol/Harvesting.java | 104 +- .../hypixel/hytale/protocol/HitEntity.java | 60 +- src/com/hypixel/hytale/protocol/Hitbox.java | 21 +- .../protocol/HitboxCollisionConfig.java | 20 +- .../protocol/HitboxCollisionUpdate.java | 11 +- .../hytale/protocol/HorizontalSelector.java | 36 +- .../hypixel/hytale/protocol/HostAddress.java | 36 +- .../IncrementCooldownInteraction.java | 485 +-- .../hytale/protocol/InitialVelocity.java | 36 +- .../hypixel/hytale/protocol/InstantData.java | 13 +- .../hytale/protocol/IntParamValue.java | 11 +- .../hytale/protocol/InteractableUpdate.java | 44 +- .../hypixel/hytale/protocol/Interaction.java | 6 +- .../hytale/protocol/InteractionCamera.java | 52 +- .../protocol/InteractionCameraSettings.java | 138 +- .../hytale/protocol/InteractionChainData.java | 87 +- .../protocol/InteractionConfiguration.java | 142 +- .../hytale/protocol/InteractionCooldown.java | 125 +- .../hytale/protocol/InteractionEffects.java | 349 ++- .../hytale/protocol/InteractionPriority.java | 56 +- .../hytale/protocol/InteractionRules.java | 324 +- .../hytale/protocol/InteractionSettings.java | 11 +- .../hytale/protocol/InteractionSyncData.java | 330 +- .../hytale/protocol/InteractionsUpdate.java | 212 +- .../protocol/IntersectionHighlight.java | 26 +- .../hytale/protocol/InventorySection.java | 55 +- .../hytale/protocol/ItemAnimation.java | 272 +- .../protocol/ItemAppearanceCondition.java | 541 ++-- .../hypixel/hytale/protocol/ItemArmor.java | 972 +++--- src/com/hypixel/hytale/protocol/ItemBase.java | 2090 +++++++------ .../hytale/protocol/ItemBuilderToolData.java | 315 -- .../hypixel/hytale/protocol/ItemCategory.java | 550 ++-- .../hytale/protocol/ItemEntityConfig.java | 50 +- .../hypixel/hytale/protocol/ItemGlider.java | 17 +- .../hypixel/hytale/protocol/ItemHudUI.java | 153 + .../hytale/protocol/ItemHudUIType.java | 27 + .../hypixel/hytale/protocol/ItemLibrary.java | 202 +- .../hytale/protocol/ItemPlayerAnimations.java | 180 +- .../protocol/ItemPullbackConfiguration.java | 76 +- .../hypixel/hytale/protocol/ItemQuality.java | 388 ++- .../hypixel/hytale/protocol/ItemQuantity.java | 46 +- .../hytale/protocol/ItemResourceType.java | 46 +- .../hypixel/hytale/protocol/ItemReticle.java | 84 +- .../hytale/protocol/ItemReticleConfig.java | 268 +- .../hypixel/hytale/protocol/ItemSoundSet.java | 118 +- src/com/hypixel/hytale/protocol/ItemTool.java | 60 +- .../hypixel/hytale/protocol/ItemToolSpec.java | 48 +- .../protocol/ItemTranslationProperties.java | 104 +- .../hypixel/hytale/protocol/ItemUpdate.java | 17 +- .../hypixel/hytale/protocol/ItemUtility.java | 184 +- .../hypixel/hytale/protocol/ItemWeapon.java | 182 +- .../hytale/protocol/ItemWithAllMetadata.java | 182 +- .../hytale/protocol/LongParamValue.java | 11 +- .../hytale/protocol/MaterialQuantity.java | 108 +- .../MemoriesConditionInteraction.java | 492 +-- src/com/hypixel/hytale/protocol/Model.java | 1344 ++++---- .../hytale/protocol/ModelAttachment.java | 208 +- .../hypixel/hytale/protocol/ModelDisplay.java | 151 +- .../hytale/protocol/ModelOverride.java | 199 +- .../hytale/protocol/ModelParticle.java | 224 +- .../hypixel/hytale/protocol/ModelTexture.java | 46 +- .../hypixel/hytale/protocol/ModelTrail.java | 214 +- .../hytale/protocol/ModelTransform.java | 36 +- .../hypixel/hytale/protocol/ModelUpdate.java | 23 +- src/com/hypixel/hytale/protocol/ModelVFX.java | 171 +- src/com/hypixel/hytale/protocol/Modifier.java | 27 +- .../protocol/ModifyInventoryInteraction.java | 576 ++-- .../hytale/protocol/MountedUpdate.java | 67 +- .../hytale/protocol/MouseButtonEvent.java | 27 +- .../hytale/protocol/MouseMotionEvent.java | 82 +- .../MovementConditionInteraction.java | 383 +-- .../hytale/protocol/MovementEffects.java | 23 +- .../hytale/protocol/MovementSettings.java | 139 +- .../hytale/protocol/MovementStates.java | 74 +- .../hytale/protocol/MovementStatesUpdate.java | 23 +- .../hytale/protocol/NameplateUpdate.java | 22 +- src/com/hypixel/hytale/protocol/NearFar.java | 13 +- .../hytale/protocol/NetworkChannel.java | 3 +- .../hypixel/hytale/protocol/NoiseConfig.java | 33 +- .../hypixel/hytale/protocol/Objective.java | 203 +- .../hytale/protocol/ObjectiveTask.java | 25 +- .../hypixel/hytale/protocol/OffsetNoise.java | 203 +- .../hytale/protocol/PacketRegistry.java | 329 +- .../hytale/protocol/ParallelInteraction.java | 486 +-- .../hypixel/hytale/protocol/ParamValue.java | 6 +- src/com/hypixel/hytale/protocol/Particle.java | 228 +- .../protocol/ParticleAnimationFrame.java | 44 +- .../hytale/protocol/ParticleAttractor.java | 102 +- .../hytale/protocol/ParticleCollision.java | 32 +- .../hytale/protocol/ParticleSpawner.java | 410 +-- .../hytale/protocol/ParticleSpawnerGroup.java | 194 +- .../hytale/protocol/ParticleSystem.java | 129 +- .../hytale/protocol/PhysicalMaterial.java | 171 ++ .../hytale/protocol/PhysicsConfig.java | 65 +- .../hytale/protocol/PickBlockInteraction.java | 369 ++- .../protocol/PlaceBlockInteraction.java | 373 ++- .../hypixel/hytale/protocol/PlayerSkin.java | 1040 ++++--- .../hytale/protocol/PlayerSkinUpdate.java | 21 +- src/com/hypixel/hytale/protocol/Position.java | 15 +- .../hytale/protocol/PredictionUpdate.java | 11 +- .../hytale/protocol/ProjectileConfig.java | 176 +- .../protocol/ProjectileInteraction.java | 477 +-- .../hytale/protocol/ProtocolEmote.java | 427 +++ .../hytale/protocol/ProtocolSettings.java | 12 +- .../hypixel/hytale/protocol/RailConfig.java | 64 +- .../hypixel/hytale/protocol/RailPoint.java | 72 +- src/com/hypixel/hytale/protocol/Range.java | 13 +- .../hytale/protocol/RangeVector2f.java | 30 +- .../hytale/protocol/RangeVector3f.java | 36 +- src/com/hypixel/hytale/protocol/Rangeb.java | 13 +- src/com/hypixel/hytale/protocol/Rangef.java | 13 +- .../hytale/protocol/RaycastSelector.java | 42 +- .../protocol/RemoveEntityInteraction.java | 366 ++- .../hytale/protocol/RepeatInteraction.java | 371 ++- .../hytale/protocol/ReplaceInteraction.java | 475 +-- .../hytale/protocol/RepulsionConfig.java | 15 +- .../hytale/protocol/RepulsionUpdate.java | 11 +- .../protocol/RequiredBlockFaceSupport.java | 407 +-- .../protocol/ResetCooldownInteraction.java | 414 +-- .../hypixel/hytale/protocol/ResourceType.java | 104 +- .../hypixel/hytale/protocol/ReverbEffect.java | 72 +- .../protocol/RoofConnectedBlockRuleSet.java | 118 +- .../{SortType.java => RoofState.java} | 16 +- .../hytale/protocol/RootInteraction.java | 320 +- .../protocol/RootInteractionSettings.java | 23 +- .../hytale/protocol/RotationNoise.java | 203 +- .../hytale/protocol/RunRootInteraction.java | 369 ++- .../hytale/protocol/SavedMovementStates.java | 11 +- .../hytale/protocol/SelectInteraction.java | 570 ++-- .../hytale/protocol/SelectedHitEntity.java | 48 +- src/com/hypixel/hytale/protocol/Selector.java | 6 +- .../hytale/protocol/SerialInteraction.java | 486 +-- .../hytale/protocol/ServerCameraSettings.java | 183 +- .../hypixel/hytale/protocol/ShelterType.java | 30 + .../protocol/SimpleBlockInteraction.java | 369 ++- .../hytale/protocol/SimpleInteraction.java | 367 ++- src/com/hypixel/hytale/protocol/Size.java | 13 +- .../hypixel/hytale/protocol/SoftBlock.java | 106 +- .../hytale/protocol/SoundCategory.java | 3 +- .../hypixel/hytale/protocol/SoundEvent.java | 176 +- .../hytale/protocol/SoundEventLayer.java | 98 +- .../SoundEventLayerRandomSettings.java | 19 +- src/com/hypixel/hytale/protocol/SoundSet.java | 268 +- .../hypixel/hytale/protocol/SpaceSize.java | 31 + ...SpawnDeployableFromRaycastInteraction.java | 549 ++-- .../hypixel/hytale/protocol/StabSelector.java | 29 +- .../protocol/StairConnectedBlockRuleSet.java | 54 +- .../protocol/StatsConditionInteraction.java | 501 +-- .../hytale/protocol/StringParamValue.java | 44 +- .../protocol/SubCategoryDefinition.java | 343 +++ .../hypixel/hytale/protocol/SurfaceType.java | 29 + .../hypixel/hytale/protocol/TagPattern.java | 177 +- .../hytale/protocol/TargetedDamage.java | 25 +- .../hypixel/hytale/protocol/TeleportAck.java | 11 +- src/com/hypixel/hytale/protocol/Tint.java | 21 +- .../protocol/ToggleGliderInteraction.java | 367 ++- src/com/hypixel/hytale/protocol/Trail.java | 237 +- .../hypixel/hytale/protocol/Transform.java | 30 +- .../hytale/protocol/TransformUpdate.java | 11 +- .../protocol/TriggerCooldownInteraction.java | 414 +-- .../hytale/protocol/UIComponentsUpdate.java | 12 +- src/com/hypixel/hytale/protocol/UVMotion.java | 91 +- .../hytale/protocol/UseBlockInteraction.java | 369 ++- .../hytale/protocol/UseEntityInteraction.java | 367 ++- src/com/hypixel/hytale/protocol/Vector2f.java | 75 - src/com/hypixel/hytale/protocol/Vector2i.java | 13 +- src/com/hypixel/hytale/protocol/Vector3d.java | 15 +- src/com/hypixel/hytale/protocol/Vector3f.java | 81 - src/com/hypixel/hytale/protocol/Vector3i.java | 15 +- .../hytale/protocol/VelocityConfig.java | 28 +- .../hypixel/hytale/protocol/ViewBobbing.java | 21 +- src/com/hypixel/hytale/protocol/Weather.java | 1403 +++++---- .../hytale/protocol/WeatherParticle.java | 54 +- .../hytale/protocol/WieldingInteraction.java | 665 ++-- .../hytale/protocol/WiggleWeights.java | 29 +- .../hytale/protocol/WorldEnvironment.java | 184 +- .../hytale/protocol/WorldInteraction.java | 32 +- .../hytale/protocol/WorldParticle.java | 75 +- .../hypixel/hytale/protocol/io/PacketIO.java | 101 + .../hypixel/hytale/protocol/io/VarInt.java | 138 +- .../protocol/io/netty/ProtocolUtil.java | 22 +- .../AssetEditorActivateButton.java | 44 +- .../packets/asseteditor/AssetEditorAsset.java | 77 +- .../AssetEditorAssetListSetup.java | 222 +- .../AssetEditorAssetListUpdate.java | 190 +- .../AssetEditorAssetPackSetup.java | 79 +- .../asseteditor/AssetEditorAssetType.java | 369 ++- .../asseteditor/AssetEditorAssetUpdated.java | 106 +- .../asseteditor/AssetEditorAuthorization.java | 11 +- .../asseteditor/AssetEditorCapabilities.java | 19 +- .../asseteditor/AssetEditorCreateAsset.java | 176 +- .../AssetEditorCreateAssetPack.java | 51 +- .../AssetEditorCreateDirectory.java | 23 +- .../asseteditor/AssetEditorDeleteAsset.java | 23 +- .../AssetEditorDeleteAssetPack.java | 44 +- .../AssetEditorDeleteDirectory.java | 23 +- .../AssetEditorDiscardChanges.java | 58 +- .../AssetEditorExportAssetInitialize.java | 67 +- .../AssetEditorExportAssetPart.java | 60 +- .../asseteditor/AssetEditorExportAssets.java | 58 +- .../AssetEditorExportComplete.java | 58 +- .../AssetEditorExportDeleteAssets.java | 58 +- .../asseteditor/AssetEditorFetchAsset.java | 25 +- .../AssetEditorFetchAssetReply.java | 62 +- .../AssetEditorFetchAutoCompleteData.java | 106 +- ...AssetEditorFetchAutoCompleteDataReply.java | 82 +- .../AssetEditorFetchJsonAssetWithParents.java | 25 +- ...tEditorFetchJsonAssetWithParentsReply.java | 81 +- .../asseteditor/AssetEditorFileEntry.java | 46 +- .../AssetEditorJsonAssetUpdated.java | 110 +- .../AssetEditorLastModifiedAssets.java | 58 +- .../AssetEditorModifiedAssetsCount.java | 11 +- .../AssetEditorModsDirectories.java | 206 ++ .../AssetEditorPopupNotification.java | 44 +- .../AssetEditorPreviewCameraSettings.java | 75 +- .../asseteditor/AssetEditorRebuildCaches.java | 19 +- .../asseteditor/AssetEditorRedoChanges.java | 23 +- .../asseteditor/AssetEditorRenameAsset.java | 65 +- .../AssetEditorRenameDirectory.java | 65 +- .../AssetEditorRequestChildrenList.java | 21 +- .../AssetEditorRequestChildrenListReply.java | 132 +- .../AssetEditorRequestDataset.java | 44 +- .../AssetEditorRequestDatasetReply.java | 149 +- .../asseteditor/AssetEditorSelectAsset.java | 21 +- .../asseteditor/AssetEditorSetGameTime.java | 26 +- .../AssetEditorSetupAssetTypes.java | 58 +- .../asseteditor/AssetEditorSetupSchemas.java | 58 +- ...tEditorSubscribeModifiedAssetsChanges.java | 11 +- .../asseteditor/AssetEditorUndoChanges.java | 23 +- .../asseteditor/AssetEditorUndoRedoReply.java | 23 +- .../asseteditor/AssetEditorUpdateAsset.java | 154 +- .../AssetEditorUpdateAssetPack.java | 77 +- .../AssetEditorUpdateJsonAsset.java | 158 +- .../AssetEditorUpdateModelPreview.java | 132 +- .../AssetEditorUpdateSecondsPerGameDay.java | 13 +- .../AssetEditorUpdateWeatherPreviewLock.java | 11 +- .../packets/asseteditor/AssetInfo.java | 122 +- .../asseteditor/AssetPackManifest.java | 381 ++- .../packets/asseteditor/AssetPath.java | 104 +- .../packets/asseteditor/AuthorInfo.java | 156 +- .../packets/asseteditor/FailureReply.java | 23 +- .../asseteditor/JsonUpdateCommand.java | 489 +-- .../packets/asseteditor/SchemaFile.java | 44 +- .../packets/asseteditor/SuccessReply.java | 23 +- .../TimestampedAssetReference.java | 83 +- .../assets/TrackOrUpdateObjective.java | 21 +- .../packets/assets/UntrackObjective.java | 11 +- .../packets/assets/UpdateAmbienceFX.java | 102 +- .../packets/assets/UpdateAudioCategories.java | 102 +- .../assets/UpdateBlockBreakingDecals.java | 136 +- .../packets/assets/UpdateBlockGroups.java | 136 +- .../packets/assets/UpdateBlockHitboxes.java | 144 +- .../assets/UpdateBlockParticleSets.java | 136 +- .../packets/assets/UpdateBlockSets.java | 136 +- .../packets/assets/UpdateBlockSoundSets.java | 102 +- .../packets/assets/UpdateBlockTypes.java | 110 +- .../packets/assets/UpdateCameraShake.java | 100 +- .../protocol/packets/assets/UpdateEmotes.java | 222 ++ .../packets/assets/UpdateEntityEffects.java | 102 +- .../packets/assets/UpdateEntityStatTypes.java | 102 +- .../assets/UpdateEntityUIComponents.java | 102 +- .../packets/assets/UpdateEnvironments.java | 104 +- .../assets/UpdateEqualizerEffects.java | 102 +- .../assets/UpdateFieldcraftCategories.java | 105 +- .../packets/assets/UpdateFluidFX.java | 102 +- .../protocol/packets/assets/UpdateFluids.java | 102 +- .../assets/UpdateHitboxCollisionConfig.java | 102 +- .../packets/assets/UpdateInteractions.java | 102 +- .../packets/assets/UpdateItemCategories.java | 105 +- .../assets/UpdateItemPlayerAnimations.java | 136 +- .../packets/assets/UpdateItemQualities.java | 102 +- .../packets/assets/UpdateItemReticles.java | 102 +- .../packets/assets/UpdateItemSoundSets.java | 102 +- .../protocol/packets/assets/UpdateItems.java | 317 +- .../packets/assets/UpdateModelvfxs.java | 102 +- .../packets/assets/UpdateObjectiveTask.java | 25 +- .../assets/UpdateParticleSpawners.java | 313 +- .../packets/assets/UpdateParticleSystems.java | 313 +- .../assets/UpdatePhysicalMaterials.java | 222 ++ .../assets/UpdateProjectileConfigs.java | 313 +- .../packets/assets/UpdateRecipes.java | 313 +- .../packets/assets/UpdateRepulsionConfig.java | 102 +- .../packets/assets/UpdateResourceTypes.java | 136 +- .../packets/assets/UpdateReverbEffects.java | 102 +- .../assets/UpdateRootInteractions.java | 102 +- .../packets/assets/UpdateSoundEvents.java | 102 +- .../packets/assets/UpdateSoundSets.java | 102 +- .../packets/assets/UpdateTagPatterns.java | 102 +- .../protocol/packets/assets/UpdateTrails.java | 136 +- .../packets/assets/UpdateTranslations.java | 178 +- .../assets/UpdateUnarmedInteractions.java | 93 +- .../packets/assets/UpdateViewBobbing.java | 93 +- .../packets/assets/UpdateWeathers.java | 102 +- .../protocol/packets/auth/AuthGrant.java | 106 +- .../protocol/packets/auth/AuthToken.java | 106 +- .../protocol/packets/auth/ClientReferral.java | 106 +- .../protocol/packets/auth/ConnectAccept.java | 60 +- .../packets/auth/PasswordRejected.java | 62 +- .../packets/auth/PasswordResponse.java | 60 +- .../packets/auth/ServerAuthToken.java | 117 +- .../packets/buildertools/BrushOrigin.java | 4 +- .../packets/buildertools/BuilderToolArg.java | 377 ++- .../buildertools/BuilderToolArgUpdate.java | 152 +- .../buildertools/BuilderToolBlockArg.java | 46 +- .../buildertools/BuilderToolBoolArg.java | 11 +- .../buildertools/BuilderToolBrushAxisArg.java | 18 +- .../buildertools/BuilderToolBrushData.java | 976 ------ .../BuilderToolBrushOriginArg.java | 18 +- .../BuilderToolBrushShapeArg.java | 18 +- .../buildertools/BuilderToolEntityAction.java | 20 +- .../BuilderToolExtrudeAction.java | 21 +- .../buildertools/BuilderToolFloatArg.java | 15 +- .../BuilderToolGeneralAction.java | 18 +- .../buildertools/BuilderToolIntArg.java | 15 +- .../buildertools/BuilderToolLaserPointer.java | 27 +- .../buildertools/BuilderToolLineAction.java | 21 +- .../buildertools/BuilderToolMaskArg.java | 44 +- .../BuilderToolOnUseInteraction.java | 77 +- .../buildertools/BuilderToolOptionArg.java | 149 +- .../BuilderToolPasteClipboard.java | 15 +- .../BuilderToolResetClipboardRotation.java | 64 + .../BuilderToolRotateClipboard.java | 20 +- .../buildertools/BuilderToolRotationArg.java | 18 +- ...erToolSelectionToolReplyWithClipboard.java | 279 +- .../BuilderToolSelectionTransform.java | 89 +- .../BuilderToolSelectionUpdate.java | 21 +- .../BuilderToolSetEntityCollision.java | 46 +- .../BuilderToolSetEntityLight.java | 26 +- .../BuilderToolSetEntityPickupEnabled.java | 13 +- .../BuilderToolSetEntityScale.java | 13 +- .../BuilderToolSetEntityTransform.java | 26 +- .../buildertools/BuilderToolSetNPCDebug.java | 13 +- ...BuilderToolSetTransformationModeState.java | 11 +- .../buildertools/BuilderToolShowAnchor.java | 15 +- .../buildertools/BuilderToolStackArea.java | 38 +- .../buildertools/BuilderToolState.java | 291 +- .../buildertools/BuilderToolStringArg.java | 44 +- .../buildertools/BuilderToolsSetSoundSet.java | 11 +- .../buildertools/ClipboardEntityChange.java | 316 ++ .../packets/buildertools/PrefabSetAnchor.java | 103 + ...derToolArgGroup.java => RotationFace.java} | 19 +- .../packets/camera/CameraShakeEffect.java | 22 +- .../packets/camera/RequestFlyCameraMode.java | 11 +- .../packets/camera/SetFlyCameraMode.java | 11 +- .../packets/camera/SetServerCamera.java | 29 +- .../packets/connection/ClientDisconnect.java | 109 + .../connection/ClientDisconnectReason.java | 29 + .../protocol/packets/connection/Connect.java | 397 +-- .../protocol/packets/connection/Ping.java | 36 +- .../protocol/packets/connection/Pong.java | 35 +- .../connection/QuicApplicationErrorCode.java | 32 + ...{Disconnect.java => ServerDisconnect.java} | 94 +- .../packets/entities/ApplyKnockback.java | 33 +- .../packets/entities/ChangeVelocity.java | 33 +- .../packets/entities/EntityUpdates.java | 134 +- .../packets/entities/MountMovement.java | 48 +- .../packets/entities/PlayAnimation.java | 189 +- .../protocol/packets/entities/PlayEmote.java | 158 + .../packets/entities/SetEntitySeed.java | 11 +- .../packets/entities/SpawnModelParticles.java | 60 +- .../interaction/CancelInteractionChain.java | 23 +- .../packets/interaction/MountNPC.java | 17 +- .../interaction/PlayInteractionFor.java | 154 +- .../interaction/SyncInteractionChain.java | 623 ++-- .../interaction/SyncInteractionChains.java | 20 +- .../interface_/AddToServerPlayerList.java | 58 +- .../interface_/ArgCacheInvalidation.java | 206 ++ .../packets/interface_/ArgValuesRequest.java | 268 ++ .../packets/interface_/ArgValuesResponse.java | 426 +++ .../packets/interface_/BlockChange.java | 19 +- .../packets/interface_/ChatMessage.java | 52 +- .../packets/interface_/CommandArgInfo.java | 427 +++ .../interface_/CommandOptionalArgEntry.java | 420 +++ .../interface_/CommandSuggestionOverride.java | 155 + .../interface_/CommandSuggestionsRequest.java | 172 ++ .../CommandSuggestionsResponse.java | 1133 +++++++ .../packets/interface_/CommandTreeEntry.java | 1196 ++++++++ .../packets/interface_/CommandTreeSync.java | 186 ++ .../interface_/CommandVariantEntry.java | 578 ++++ .../packets/interface_/CustomHud.java | 222 +- .../packets/interface_/CustomPage.java | 339 ++- .../packets/interface_/CustomPageEvent.java | 81 +- .../packets/interface_/CustomUICommand.java | 277 +- .../interface_/CustomUIEventBinding.java | 189 +- .../interface_/EditorBlocksChange.java | 326 +- .../packets/interface_/EditorSelection.java | 21 +- .../ExecuteServersidePageCommand.java | 282 ++ .../packets/interface_/FluidChange.java | 19 +- .../packets/interface_/HideEventTitle.java | 11 +- .../interface_/InitServersideUICommand.java | 269 ++ ...extCollectionItemServersideUIProperty.java | 188 ++ .../packets/interface_/KillFeedMessage.java | 114 +- .../packets/interface_/Notification.java | 244 +- .../interface_/OpenChatWithCommand.java | 44 +- .../protocol/packets/interface_/Page.java | 3 +- .../packets/interface_/PortalDef.java | 48 +- .../packets/interface_/PortalState.java | 13 +- ...extCollectionItemServersideUIProperty.java | 121 + .../RemoveFromServerPlayerList.java | 60 +- .../packets/interface_/ServerInfo.java | 198 +- .../packets/interface_/ServerMessage.java | 44 +- .../interface_/ServerPlayerListPlayer.java | 52 +- .../interface_/ServerPlayerListUpdate.java | 13 +- .../interface_/ServersideUICommand.java | 85 + ...taContextPropertyServersideUIProperty.java | 182 ++ ...SetElementPropertyServersideUICommand.java | 242 ++ .../protocol/packets/interface_/SetPage.java | 20 +- .../packets/interface_/ShowEventTitle.java | 112 +- .../packets/interface_/UIBoolDataValue.java | 78 + .../packets/interface_/UIByteDataValue.java | 78 + .../interface_/UICommandDataValue.java | 109 + .../packets/interface_/UIDataValue.java | 140 + .../packets/interface_/UIDoubleDataValue.java | 78 + .../packets/interface_/UIEnumDataValue.java | 78 + .../packets/interface_/UIFloatDataValue.java | 78 + .../packets/interface_/UIIntDataValue.java | 78 + .../packets/interface_/UIListDataValue.java | 237 ++ .../packets/interface_/UILongDataValue.java | 78 + .../packets/interface_/UIObjectDataValue.java | 267 ++ .../packets/interface_/UISByteDataValue.java | 78 + .../packets/interface_/UIShortDataValue.java | 78 + .../packets/interface_/UIStringDataValue.java | 109 + .../packets/interface_/UIUIntDataValue.java | 78 + .../packets/interface_/UIULongDataValue.java | 78 + .../packets/interface_/UIUShortDataValue.java | 78 + .../packets/interface_/UpdateAnchorUI.java | 192 +- .../interface_/UpdateKnownRecipes.java | 79 +- .../packets/interface_/UpdateLanguage.java | 44 +- .../packets/interface_/UpdatePortal.java | 27 +- .../interface_/UpdateServerPlayerList.java | 58 +- .../UpdateServerPlayerListPing.java | 53 +- .../interface_/UpdateServersideUIPage.java | 156 + .../UpdateVisibleHudComponents.java | 70 +- .../packets/interface_/WorldSavingStatus.java | 11 +- .../packets/inventory/DropItemStack.java | 15 +- .../packets/inventory/InventoryAction.java | 22 +- .../packets/inventory/MoveItemStack.java | 19 +- .../packets/inventory/SetActiveSlot.java | 13 +- .../packets/inventory/SetCreativeItem.java | 21 +- .../inventory/SmartGiveCreativeItem.java | 34 +- .../packets/inventory/SmartMoveItemStack.java | 24 +- .../inventory/SwitchHotbarBlockSet.java | 44 +- .../inventory/UpdatePlayerInventory.java | 308 +- .../machinima/RequestMachinimaActorModel.java | 156 +- .../machinima/SetMachinimaActorModel.java | 153 +- .../machinima/UpdateMachinimaScene.java | 292 +- .../packets/player/ClientMovement.java | 88 +- .../packets/player/ClientPlaceBlock.java | 57 +- .../protocol/packets/player/ClientReady.java | 13 +- .../packets/player/ClientTeleport.java | 28 +- .../protocol/packets/player/DamageInfo.java | 29 +- .../protocol/packets/player/DisplayDebug.java | 282 +- .../protocol/packets/player/JoinWorld.java | 15 +- .../protocol/packets/player/LoadHotbar.java | 11 +- .../packets/player/MouseInteraction.java | 137 +- .../packets/player/RemoveMapMarker.java | 44 +- .../protocol/packets/player/ReticleEvent.java | 11 +- .../protocol/packets/player/SaveHotbar.java | 11 +- .../player/SetBlockPlacementOverride.java | 11 +- .../protocol/packets/player/SetClientId.java | 11 +- .../protocol/packets/player/SetGameMode.java | 18 +- .../packets/player/SetMovementStates.java | 24 +- .../packets/player/SyncPlayerPreferences.java | 60 +- .../player/UpdateMemoriesFeatureStatus.java | 11 +- .../player/UpdateMovementSettings.java | 24 +- .../serveraccess/RequestServerAccess.java | 20 +- .../packets/serveraccess/SetServerAccess.java | 81 +- .../serveraccess/UpdateServerAccess.java | 105 +- .../packets/setup/AssetInitialize.java | 17 +- .../protocol/packets/setup/AssetPart.java | 60 +- .../protocol/packets/setup/PlayerOptions.java | 21 +- .../protocol/packets/setup/RemoveAssets.java | 58 +- .../protocol/packets/setup/RequestAssets.java | 58 +- .../protocol/packets/setup/ServerTags.java | 79 +- .../packets/setup/SetTimeDilation.java | 11 +- .../protocol/packets/setup/SetUpdateRate.java | 11 +- .../packets/setup/UpdateFeatures.java | 58 +- .../protocol/packets/setup/ViewRadius.java | 11 +- .../packets/setup/WorldLoadProgress.java | 25 +- .../protocol/packets/setup/WorldSettings.java | 60 +- .../protocol/packets/stream/StreamOpen.java | 97 + .../packets/stream/StreamOpenResponse.java | 178 ++ .../protocol/packets/stream/StreamType.java | 27 + .../packets/voice/RelayedVoiceData.java | 222 ++ .../protocol/packets/voice/VoiceCodec.java | 26 + .../protocol/packets/voice/VoiceConfig.java | 166 + .../protocol/packets/voice/VoiceData.java | 156 + .../packets/window/ChangeBlockAction.java | 11 +- .../packets/window/ClientOpenWindow.java | 18 +- .../protocol/packets/window/CloseWindow.java | 11 +- .../packets/window/CraftRecipeAction.java | 46 +- .../protocol/packets/window/OpenWindow.java | 193 +- .../packets/window/SelectSlotAction.java | 11 +- .../packets/window/SendWindowAction.java | 17 +- .../packets/window/SetActiveAction.java | 11 +- .../packets/window/SortItemsAction.java | 44 +- .../packets/window/UpdateCategoryAction.java | 150 +- .../protocol/packets/window/UpdateWindow.java | 106 +- .../protocol/packets/window/WindowAction.java | 6 +- .../packets/world/PlaySoundEvent2D.java | 24 +- .../packets/world/PlaySoundEvent3D.java | 33 +- .../packets/world/PlaySoundEventEntity.java | 17 +- .../world/PlaySoundEventLocalPlayer.java | 130 + .../packets/world/ServerSetBlock.java | 21 +- .../packets/world/ServerSetBlocks.java | 48 +- .../packets/world/ServerSetFluid.java | 19 +- .../packets/world/ServerSetFluids.java | 48 +- .../packets/world/ServerSetPaused.java | 11 +- .../protocol/packets/world/SetBlockCmd.java | 17 +- .../protocol/packets/world/SetChunk.java | 197 +- .../packets/world/SetChunkEnvironments.java | 64 +- .../packets/world/SetChunkHeightmap.java | 64 +- .../packets/world/SetChunkTintmap.java | 64 +- .../protocol/packets/world/SetFluidCmd.java | 15 +- .../protocol/packets/world/SetFluids.java | 66 +- .../protocol/packets/world/SetPaused.java | 11 +- .../protocol/packets/world/SleepClock.java | 34 +- .../packets/world/SleepMultiplayer.java | 64 +- .../world/SpawnBlockParticleSystem.java | 29 +- .../packets/world/SpawnParticleSystem.java | 66 +- .../protocol/packets/world/UnloadChunk.java | 13 +- .../packets/world/UpdateBlockDamage.java | 28 +- .../world/UpdateEditorTimeOverride.java | 26 +- .../world/UpdateEditorWeatherOverride.java | 11 +- .../packets/world/UpdateEnvironmentMusic.java | 11 +- .../packets/world/UpdatePostFxSettings.java | 19 +- .../packets/world/UpdateSleepState.java | 31 +- .../packets/world/UpdateSunSettings.java | 13 +- .../protocol/packets/world/UpdateTime.java | 24 +- .../packets/world/UpdateTimeSettings.java | 17 +- .../protocol/packets/world/UpdateWeather.java | 13 +- .../protocol/packets/worldmap/BiomeData.java | 108 +- .../packets/worldmap/ContextMenuItem.java | 150 +- .../packets/worldmap/CreateUserMarker.java | 126 +- .../worldmap/HeightDeltaIconComponent.java | 108 +- .../protocol/packets/worldmap/MapChunk.java | 27 +- .../protocol/packets/worldmap/MapImage.java | 258 +- .../protocol/packets/worldmap/MapMarker.java | 581 ++-- .../packets/worldmap/MapMarkerComponent.java | 6 +- .../worldmap/PlacedByMarkerComponent.java | 17 +- .../worldmap/PlayerMarkerComponent.java | 11 +- .../worldmap/TeleportToWorldMapMarker.java | 44 +- .../worldmap/TeleportToWorldMapPosition.java | 13 +- .../packets/worldmap/TintComponent.java | 11 +- .../packets/worldmap/UpdateWorldMap.java | 235 +- .../worldmap/UpdateWorldMapSettings.java | 73 +- .../worldmap/UpdateWorldMapVisible.java | 11 +- .../hypixel/hytale/server/core/Constants.java | 10 +- .../hytale/server/core/HytaleServer.java | 188 +- .../server/core/HytaleServerConfig.java | 70 +- .../hypixel/hytale/server/core/Options.java | 29 +- .../hytale/server/core/ShutdownReason.java | 21 +- .../hytale/server/core/asset/AssetModule.java | 137 +- .../core/asset/AssetRegistryLoader.java | 179 +- .../core/asset/GenerateSchemaEvent.java | 48 - .../server/core/asset/HytaleAssetStore.java | 2 + .../core/asset/common/CommonAssetModule.java | 6 + .../asset/common/CommonAssetValidator.java | 2 + .../config/AmbienceFXConditions.java | 163 +- .../config/AmbienceFXPhysicalMaterial.java | 76 + .../ambiencefx/config/AmbienceFXSound.java | 36 +- .../type/blockhitbox/BlockBoundingBoxes.java | 4 +- .../config/BlockParticleSet.java | 5 +- .../type/blocksound/config/BlockSoundSet.java | 8 +- .../type/blocktype/config/BlockFace.java | 47 +- .../blocktype/config/BlockFaceSupport.java | 5 +- .../type/blocktype/config/BlockFlipType.java | 54 +- .../config/BlockPlacementSettings.java | 191 +- .../type/blocktype/config/BlockType.java | 105 +- .../config/ConditionalBlockSound.java | 79 + .../config/RequiredBlockFaceSupport.java | 5 +- .../asset/type/blocktype/config/Rotation.java | 187 +- .../type/blocktype/config/RotationTuple.java | 167 +- .../type/blocktype/config/StateData.java | 25 +- .../config/farming/FarmingStageData.java | 33 + .../config/mountpoints/BlockMountPoint.java | 32 +- .../type/buildertool/config/BrushData.java | 800 ----- .../type/buildertool/config/BuilderTool.java | 159 +- .../buildertool/config/BuilderToolData.java | 67 - .../buildertool/config/args/BlockArg.java | 6 +- .../type/buildertool/config/args/BoolArg.java | 3 +- .../config/args/BrushOriginArg.java | 3 +- .../config/args/BrushShapeArg.java | 3 +- .../buildertool/config/args/FloatArg.java | 9 +- .../type/buildertool/config/args/IntArg.java | 9 +- .../type/buildertool/config/args/MaskArg.java | 3 +- .../buildertool/config/args/OptionArg.java | 8 +- .../buildertool/config/args/StringArg.java | 3 +- .../type/buildertool/config/args/ToolArg.java | 15 +- .../entityeffect/config/EntityEffect.java | 16 + .../type/environment/config/Environment.java | 13 + .../asset/type/fluid/DefaultFluidTicker.java | 2 +- .../asset/type/fluid/FiniteFluidTicker.java | 10 +- .../asset/type/fluid/FireFluidTicker.java | 50 +- .../server/core/asset/type/fluid/Fluid.java | 4 +- .../core/asset/type/fluid/FluidTicker.java | 2 +- .../asset/type/gameplay/GameplayConfig.java | 111 +- .../type/item/config/AssetIconProperties.java | 18 +- .../type/item/config/CraftingRecipe.java | 10 +- .../core/asset/type/item/config/Item.java | 68 +- .../asset/type/item/config/ItemCategory.java | 60 +- .../asset/type/item/config/ItemHudUI.java | 40 + .../type/item/config/ItemPullbackConfig.java | 21 +- .../config/ItemTranslationProperties.java | 23 + .../asset/type/model/config/DetailBox.java | 18 +- .../core/asset/type/model/config/Model.java | 18 +- .../asset/type/model/config/ModelAsset.java | 10 +- .../type/model/config/ModelParticle.java | 5 +- .../model/config/camera/CameraSettings.java | 6 +- .../asset/type/modelvfx/config/ModelVFX.java | 11 +- .../commands/ParticleSpawnCommand.java | 6 +- .../particle/config/ParticleAttractor.java | 14 +- .../particle/config/ParticleSpawnerGroup.java | 5 +- .../type/particle/config/WorldParticle.java | 5 +- .../particle/pages/ParticleSpawnPage.java | 23 +- .../PhysicalMaterialPacketGenerator.java | 78 + .../config/PhysicalMaterial.java | 151 + .../type/portalworld/PortalSpawnConfig.java | 49 + .../asset/type/portalworld/PortalType.java | 8 + .../responsecurve/config/ResponseCurve.java | 1 + .../config/SwitchResponseCurve.java | 51 + .../type/soundevent/config/SoundEvent.java | 23 +- .../asset/type/trail/config/Animation.java | 5 +- .../core/asset/type/trail/config/Trail.java | 4 +- .../hytale/server/core/auth/AuthConfig.java | 7 + .../core/auth/HttpResponseException.java | 26 + .../hytale/server/core/auth/JWTValidator.java | 86 + .../server/core/auth/ServerAuthManager.java | 296 +- .../core/auth/SessionServiceClient.java | 26 +- .../server/core/auth/oauth/OAuthClient.java | 25 +- .../core/blocktype/BlockTypeModule.java | 108 - .../blocktype/component/BlockPhysics.java | 10 +- .../server/core/codec/ProtocolCodecs.java | 42 +- .../hytale/server/core/codec/ShapeCodecs.java | 8 +- .../debug/DebugPlayerPositionCommand.java | 13 +- .../debug/packs/PacksListCommand.java | 6 +- .../commands/debug/stresstest/Bot.java | 51 +- .../commands/debug/stresstest/BotConfig.java | 2 +- .../stresstest/StressTestStartCommand.java | 5 +- .../command/commands/player/SudoCommand.java | 72 +- .../commands/player/WhereAmICommand.java | 26 +- .../commands/player/camera/CameraDemo.java | 34 +- .../PlayerCameraSideScrollerCommand.java | 2 +- .../camera/PlayerCameraTopdownCommand.java | 2 +- .../player/inventory/GiveArmorCommand.java | 19 +- .../inventory/InventoryBackpackCommand.java | 39 +- .../inventory/InventoryClearCommand.java | 11 +- .../inventory/InventoryItemCommand.java | 33 +- .../player/inventory/InventorySeeCommand.java | 6 +- .../player/inventory/ItemStateCommand.java | 29 +- .../command/commands/server/KickCommand.java | 2 +- .../commands/server/MaxPlayersCommand.java | 12 +- .../utility/ConvertPrefabsCommand.java | 89 +- .../commands/utility/NotifyCommand.java | 78 +- .../commands/utility/StashCommand.java | 25 +- .../commands/utility/git/GitCommand.java | 11 - .../utility/git/UpdateAssetsCommand.java | 127 - .../utility/git/UpdatePrefabsCommand.java | 174 -- .../utility/lighting/LightingGetCommand.java | 2 +- .../lighting/LightingInvalidateCommand.java | 8 +- .../commands/utility/net/NetworkCommand.java | 2 +- .../utility/sound/SoundPlay3DCommand.java | 4 +- .../utility/worldmap/WorldMapCommand.java | 20 + .../WorldMapViewRadiusSetCommand.java | 15 +- .../commands/world/SpawnBlockCommand.java | 13 +- .../world/chunk/ChunkFixHeightMapCommand.java | 4 +- .../world/chunk/ChunkForceTickCommand.java | 2 +- .../world/chunk/ChunkInfoCommand.java | 2 +- .../world/chunk/ChunkLightingCommand.java | 6 +- .../world/chunk/ChunkLoadCommand.java | 2 +- .../world/chunk/ChunkMarkSaveCommand.java | 2 +- .../world/chunk/ChunkRegenerateCommand.java | 2 +- .../world/chunk/ChunkResendCommand.java | 6 +- .../world/chunk/ChunkTintCommand.java | 6 +- .../world/chunk/ChunkUnloadCommand.java | 2 +- .../world/entity/EntityEffectCommand.java | 6 +- ...EntityHideFromAdventurePlayersCommand.java | 6 +- .../world/entity/EntityIntangibleCommand.java | 6 +- .../entity/EntityInvulnerableCommand.java | 6 +- .../entity/EntityMakeInteractableCommand.java | 6 +- .../EntitySnapshotHistoryCommand.java | 6 +- .../entity/stats/EntityStatsAddCommand.java | 5 +- .../entity/stats/EntityStatsDumpCommand.java | 5 +- .../entity/stats/EntityStatsGetCommand.java | 5 +- .../entity/stats/EntityStatsResetCommand.java | 5 +- .../entity/stats/EntityStatsSetCommand.java | 5 +- .../stats/EntityStatsSetToMaxCommand.java | 5 +- .../worldgen/WorldGenBenchmarkCommand.java | 2 +- .../world/worldgen/WorldGenReloadCommand.java | 6 +- .../core/command/system/AbbreviationMap.java | 72 +- .../core/command/system/AbstractCommand.java | 94 +- .../core/command/system/CommandContext.java | 6 +- .../core/command/system/CommandManager.java | 82 +- .../core/command/system/CommandSender.java | 2 +- .../command/system/CommandTreeBuilder.java | 212 ++ .../core/command/system/ParserContext.java | 52 +- .../server/core/command/system/Tokenizer.java | 6 +- .../system/arguments/system/RequiredArg.java | 15 + .../types/AbstractAssetArgumentType.java | 42 + .../system/arguments/types/ArgTypes.java | 379 ++- .../system/arguments/types/ArgumentType.java | 21 + .../arguments/types/EnumArgumentType.java | 18 + .../arguments/types/GameModeArgumentType.java | 18 + .../arguments/types/ListArgumentType.java | 13 + .../types/ProcessedArgumentType.java | 13 + .../types/RelativeChunkPosition.java | 4 +- .../arguments/types/RelativeDirection.java | 8 +- .../types/RelativeDoublePosition.java | 8 +- .../arguments/types/RelativeIntPosition.java | 6 +- .../arguments/types/RelativeVector3i.java | 2 +- .../arguments/types/SingleArgumentType.java | 9 + .../arguments/types/WrappedArgumentType.java | 13 + .../AbstractCommandCollection.java | 4 + .../AbstractTargetEntityCommand.java | 19 +- .../command/system/pages/CommandListPage.java | 89 +- .../system/suggestion/SuggestionResult.java | 47 +- .../core/config/ServerWorldMapConfig.java | 54 + .../server/core/config/WorldMapConfig.java | 56 + .../core/config/WorldWorldMapConfig.java | 30 + .../server/core/console/ConsoleSender.java | 2 +- .../core/console/command/SayCommand.java | 42 +- .../core/cosmetics/CosmeticRegistry.java | 9 + .../server/core/cosmetics/CosmeticType.java | 1 + .../core/cosmetics/CosmeticsModule.java | 15 + .../server/core/cosmetics/EmoteAsset.java | 87 + .../cosmetics/EmoteAssetPacketGenerator.java | 53 + .../core/cosmetics/commands/EmoteCommand.java | 16 +- .../server/core/entity/AnimationUtils.java | 6 +- .../hytale/server/core/entity/Entity.java | 42 +- .../server/core/entity/EntitySnapshot.java | 20 +- .../server/core/entity/EntityUtils.java | 18 - .../server/core/entity/ExplosionUtils.java | 77 +- .../core/entity/InteractionContext.java | 84 +- .../core/entity/InteractionManager.java | 13 +- .../hytale/server/core/entity/ItemUtils.java | 34 +- .../server/core/entity/LivingEntity.java | 132 +- .../core/entity/StatModifiersManager.java | 40 +- .../effect/EffectControllerComponent.java | 83 +- .../core/entity/entities/BlockEntity.java | 6 +- .../server/core/entity/entities/Player.java | 115 +- .../entity/entities/ProjectileComponent.java | 50 +- .../entity/entities/player/CameraManager.java | 11 +- .../entity/entities/player/HotbarManager.java | 31 +- .../player/data/PlayerConfigData.java | 6 +- .../player/data/PlayerRespawnPointData.java | 10 +- .../entities/player/hud/CustomUIHud.java | 30 +- .../entities/player/hud/HudManager.java | 63 +- .../pages/itemrepair/ItemRepairPage.java | 5 +- .../entities/player/windows/BlockWindow.java | 2 +- .../player/windows/ContainerBlockWindow.java | 4 +- .../server/core/entity/group/EntityGroup.java | 8 +- .../entity/knockback/KnockbackComponent.java | 4 +- .../movement/MovementStatesSystems.java | 10 + .../core/entity/reference/PersistentRef.java | 7 + .../event/events/ecs/BreakBlockEvent.java | 2 +- .../event/events/ecs/DamageBlockEvent.java | 2 +- .../event/events/ecs/PlaceBlockEvent.java | 6 +- .../core/event/events/ecs/UseBlockEvent.java | 2 +- .../LivingEntityInventoryChangeEvent.java | 31 - .../events/player/AddPlayerToWorldEvent.java | 27 +- .../events/player/PlayerInteractEvent.java | 2 +- .../events/player/PlayerMouseButtonEvent.java | 10 +- .../events/player/PlayerMouseMotionEvent.java | 10 +- .../player/PlayerSetupConnectEvent.java | 15 +- .../player/RemovedPlayerFromWorldEvent.java | 64 + .../server/core/inventory/Inventory.java | 941 +++--- .../core/inventory/InventoryChangeEvent.java | 42 + .../core/inventory/InventoryComponent.java | 637 ++++ .../core/inventory/InventorySystems.java | 337 ++ .../server/core/inventory/ItemStack.java | 2 +- .../container/CombinedItemContainer.java | 125 +- .../container/DelegateItemContainer.java | 28 +- .../container/EmptyItemContainer.java | 16 + .../container/FetchedItemContainer.java | 777 +++++ .../InternalContainerUtilItemStack.java | 14 +- .../InternalContainerUtilMaterial.java | 4 +- .../inventory/container/ItemContainer.java | 25 +- .../container/ItemStackItemContainer.java | 20 + .../container/SimpleItemContainer.java | 138 +- .../core/inventory/container/SortType.java | 20 +- .../server/core/io/NetworkSerializers.java | 12 +- .../hytale/server/core/io/PacketHandler.java | 100 +- .../io/handlers/InitialPacketHandler.java | 50 +- .../core/io/handlers/SetupPacketHandler.java | 39 +- .../io/handlers/game/GamePacketHandler.java | 312 +- .../handlers/game/InventoryPacketHandler.java | 119 +- .../login/AuthenticationPacketHandler.java | 3 +- .../io/handlers/login/HandshakeHandler.java | 49 +- .../handlers/login/PasswordPacketHandler.java | 21 +- .../io/netty/HytaleChannelInitializer.java | 93 +- .../server/core/io/netty/NettyUtil.java | 30 +- .../core/io/netty/RateLimitHandler.java | 3 +- .../core/io/stream/PendingStreamHandler.java | 109 + .../server/core/io/stream/StreamManager.java | 70 + .../core/io/transport/QUICTransport.java | 19 +- .../core/liveconfig/LiveConfigModule.java | 122 + .../core/liveconfig/LiveConfigService.java | 147 + .../core/liveconfig/LiveConfigSnapshot.java | 65 + .../server/core/meta/AbstractMetaStore.java | 5 + .../hytale/server/core/meta/MetaRegistry.java | 7 + .../server/core/modules/LegacyModule.java | 164 - .../accesscontrol/AccessControlModule.java | 9 +- .../accesscontrol/ban/InfiniteBan.java | 10 +- .../modules/accesscontrol/ban/TimedBan.java | 20 +- .../accesscontrol/commands/BanCommand.java | 11 +- .../provider/AccessProvider.java | 3 +- .../provider/ClientDelegatingProvider.java | 3 +- .../provider/HytaleBanProvider.java | 3 +- .../provider/HytaleWhitelistProvider.java | 5 +- .../core/modules/block/BlockEntity.java | 129 + .../core/modules/block/BlockModule.java | 161 +- .../core/modules/block/BlockReplaceEvent.java | 54 + .../block/components/ItemContainerBlock.java | 88 + ...a => ItemContainerBlockSpatialSystem.java} | 12 +- .../block/system/ItemContainerSystems.java | 206 ++ .../modules/blockhealth/BlockHealthChunk.java | 10 +- .../blockhealth/BlockHealthModule.java | 4 +- .../modules/collision/BasicCollisionData.java | 4 +- .../collision/BlockCollisionProvider.java | 4 +- .../modules/collision/BlockContactData.java | 6 +- .../core/modules/collision/BlockTracker.java | 2 +- .../BoxBlockIntersectionEvaluator.java | 27 +- .../modules/collision/BoxCollisionData.java | 4 +- .../collision/CharacterCollisionData.java | 4 +- .../core/modules/collision/CollisionMath.java | 16 +- .../modules/collision/CollisionModule.java | 33 +- .../modules/collision/CollisionResult.java | 11 +- .../collision/EntityCollisionProvider.java | 10 +- .../modules/collision/EntityContactData.java | 4 +- .../collision/EntityRefCollisionProvider.java | 14 +- .../collision/IBlockCollisionConsumer.java | 2 +- .../core/modules/collision/IBlockTracker.java | 2 +- .../MovingBoxBoxCollisionEvaluator.java | 32 +- .../TangiableEntitySpatialSystem.java | 2 +- .../server/core/modules/debug/DebugUtils.java | 164 +- .../commands/DebugShapeArrowCommand.java | 28 +- .../debug/commands/DebugShapeConeCommand.java | 15 +- .../debug/commands/DebugShapeCubeCommand.java | 15 +- .../commands/DebugShapeCylinderCommand.java | 15 +- .../commands/DebugShapeSphereCommand.java | 15 +- .../debug/commands/DebugShapeSubCommand.java | 21 + .../core/modules/entity/EntityModule.java | 224 +- .../component/CachedStatsComponent.java | 31 + .../component/CollisionResultComponent.java | 4 +- .../entity/component/HeadRotation.java | 46 +- .../entity/component/SnapshotBuffer.java | 6 +- .../entity/component/TransformComponent.java | 51 +- .../condition/AliveCondition.java | 2 +- .../condition/ChargingCondition.java | 2 +- .../CheckPlayerGameModeCondition.java} | 22 +- .../asset => entity}/condition/Condition.java | 2 +- .../condition/EntityStatBoundCondition.java | 2 +- .../condition/EnvironmentCondition.java | 6 +- .../condition/GlidingCondition.java | 2 +- .../entity/condition/HasEffectCondition.java | 57 + .../entity/condition/InFluidCondition.java | 136 + .../entity/condition/IsPlayerCondition.java | 29 + .../condition/LogicCondition.java | 2 +- .../condition/NoDamageTakenCondition.java | 2 +- .../condition/OutOfCombatCondition.java | 2 +- .../condition/RegenHealthCondition.java | 2 +- .../condition/SprintingCondition.java | 2 +- .../condition/StatCondition.java | 2 +- .../condition/SuffocatingCondition.java | 4 +- .../condition/WieldingCondition.java | 2 +- .../core/modules/entity/damage/Damage.java | 4 +- .../modules/entity/damage/DamageModule.java | 3 + .../modules/entity/damage/DamageSystems.java | 150 +- .../modules/entity/damage/DeathSystems.java | 140 +- .../entity/damage/DeferredCorpseRemoval.java | 21 +- .../modules/entity/damage/RespawnSystems.java | 47 +- .../modules/entity/item/ItemComponent.java | 20 +- .../modules/entity/item/ItemMergeSystem.java | 6 +- .../entity/item/ItemPhysicsComponent.java | 2 +- .../entity/item/ItemPhysicsSystem.java | 17 +- .../entity/item/ItemPrePhysicsSystem.java | 6 +- .../entity/item/PickupItemComponent.java | 2 +- .../modules/entity/item/PickupItemSystem.java | 8 +- .../LivingEntityEffectSystem.java | 99 +- .../modules/entity/player/ChunkTracker.java | 18 +- .../player/KnockbackPredictionSystems.java | 43 +- .../entity/player/KnockbackSimulation.java | 6 +- .../modules/entity/player/PlayerInput.java | 6 +- .../player/PlayerItemEntityPickupSystem.java | 8 +- .../player/PlayerProcessMovementSystem.java | 11 +- .../entity/player/PlayerSavingSystems.java | 96 +- .../player/PlayerSendInventorySystem.java | 30 +- .../modules/entity/player/PlayerSystems.java | 158 +- .../entity/repulsion/RepulsionSystems.java | 14 +- .../modules/entity/system/AudioSystems.java | 2 +- .../entity/system/EntitySpatialSystem.java | 2 +- .../entity/system/ItemSpatialSystem.java | 2 +- .../system/NetworkSendableSpatialSystem.java | 2 +- .../entity/system/PlayerSpatialSystem.java | 2 +- .../entity/system/RotateObjectSystem.java | 4 +- .../entity/system/TransformSystems.java | 9 +- .../entity/system/UpdateLocationSystems.java | 14 +- .../entity/teleport/PendingTeleport.java | 6 +- .../modules/entity/teleport/Teleport.java | 51 +- .../entity/teleport/TeleportSystems.java | 18 +- .../entity/tracker/EntityTrackerSystems.java | 36 +- .../tracker/LegacyEntityTrackerSystems.java | 4 +- .../modules/entitystats/EntityStatMap.java | 35 +- .../entitystats/EntityStatsModule.java | 40 +- .../entitystats/EntityStatsSystems.java | 73 +- .../entitystats/RegeneratingValue.java | 2 +- .../entitystats/asset/EntityStatType.java | 2 +- .../asset/modifier/RegeneratingModifier.java | 2 +- ...TextUIComponentPositionAnimationEvent.java | 6 +- .../entityui/asset/EntityUIComponent.java | 6 +- .../server/core/modules/i18n/I18nModule.java | 11 +- .../interaction/BlockHarvestUtils.java | 257 +- .../modules/interaction/BlockPlaceUtils.java | 137 +- .../interaction/InteractionModule.java | 147 +- .../interaction/config/Interaction.java | 9 +- .../config/InteractionCameraSettings.java | 5 +- .../config/client/AddItemInteraction.java | 11 +- .../config/client/ApplyForceInteraction.java | 28 +- .../client/BlockConditionInteraction.java | 2 +- .../config/client/BreakBlockInteraction.java | 313 +- .../config/client/ChangeBlockInteraction.java | 10 +- .../config/client/ChangeStateInteraction.java | 4 +- .../client/CycleBlockGroupInteraction.java | 16 +- .../client/DestroyBlockInteraction.java | 2 +- .../config/client/ExplodeInteraction.java | 4 +- .../config/client/PickBlockInteraction.java | 2 +- .../config/client/PlaceBlockInteraction.java | 11 +- .../config/client/PlaceFluidInteraction.java | 7 +- .../config/client/SimpleBlockInteraction.java | 78 +- .../config/client/UseBlockInteraction.java | 2 +- .../none/ChangeActiveSlotInteraction.java | 2 +- ...StatsConditionWithModifierInteraction.java | 20 +- .../none/simple/CommandInteraction.java | 46 + .../config/selector/AOECircleSelector.java | 33 +- .../config/selector/AOECylinderSelector.java | 12 +- .../selector/ClientSourcedSelector.java | 4 +- .../config/selector/HorizontalSelector.java | 43 +- .../config/selector/RaycastSelector.java | 49 +- .../interaction/config/selector/Selector.java | 10 +- .../config/selector/StabSelector.java | 43 +- .../ChangeStatWithModifierInteraction.java | 57 +- .../server/DamageEntityInteraction.java | 105 +- .../server/DestroyConditionInteraction.java | 60 - .../config/server/DoorInteraction.java | 81 +- .../config/server/EquipItemInteraction.java | 24 +- .../IncreaseBackpackCapacityInteraction.java | 26 +- .../config/server/LaunchPadInteraction.java | 11 +- .../server/LaunchProjectileInteraction.java | 13 +- .../server/ModifyInventoryInteraction.java | 11 +- .../server/OpenContainerInteraction.java | 137 +- .../server/OpenCustomUIInteraction.java | 60 - .../server/RefillContainerInteraction.java | 209 +- .../server/RunOnBlockTypesInteraction.java | 9 +- .../config/server/SpawnPrefabInteraction.java | 12 +- .../config/server/combat/DamageEffects.java | 17 +- .../server/combat/DirectionalKnockback.java | 12 +- .../config/server/combat/ForceKnockback.java | 13 +- .../config/server/combat/Knockback.java | 2 +- .../config/server/combat/PointKnockback.java | 9 +- .../suppliers/ItemRepairPageSupplier.java | 18 +- .../system/InteractionSystems.java | 11 + .../item/commands/SpawnItemCommand.java | 8 +- .../modules/migrations/EntityMigration.java | 22 - .../modules/migrations/MigrationModule.java | 4 +- .../core/modules/physics/RestingSupport.java | 2 +- .../physics/SimplePhysicsProvider.java | 83 +- .../modules/physics/component/Velocity.java | 31 +- .../physics/util/ForceAccumulator.java | 6 +- .../physics/util/ForceProviderStandard.java | 4 +- .../util/ForceProviderStandardState.java | 20 +- .../physics/util/PhysicsBodyState.java | 2 +- .../physics/util/PhysicsBodyStateUpdater.java | 19 +- .../modules/physics/util/PhysicsMath.java | 15 +- ...wnerState.java => PrefabSpawnerBlock.java} | 65 +- .../prefabspawner/PrefabSpawnerModule.java | 51 +- .../commands/PrefabSpawnerGetCommand.java | 4 +- .../commands/PrefabSpawnerSetCommand.java | 4 +- .../commands/PrefabSpawnerWeightCommand.java | 4 +- .../commands/TargetPrefabSpawnerCommand.java | 19 +- .../modules/projectile/ProjectileModule.java | 30 +- .../projectile/config/BounceConsumer.java | 2 +- .../projectile/config/ImpactConsumer.java | 2 +- .../projectile/config/PhysicsConfig.java | 2 +- .../projectile/config/ProjectileConfig.java | 8 +- .../config/StandardPhysicsConfig.java | 2 +- .../config/StandardPhysicsProvider.java | 53 +- .../interaction/ProjectileInteraction.java | 17 +- .../system/StandardPhysicsTickSystem.java | 65 +- .../ServerPlayerListModule.java | 6 +- .../modules/splitvelocity/VelocityConfig.java | 24 + .../core/modules/time/WorldTimeResource.java | 25 +- .../modules/time/commands/TimeCommand.java | 8 +- .../core/modules/voice/VoiceModule.java | 466 +++ .../core/modules/voice/VoiceModuleConfig.java | 77 + .../modules/voice/VoicePacketHandler.java | 38 + .../core/modules/voice/VoicePlayerState.java | 148 + .../core/modules/voice/VoiceRouter.java | 233 ++ .../modules/voice/VoiceStreamHandler.java | 171 ++ .../modules/voice/commands/VoiceCommand.java | 219 ++ .../core/permissions/PermissionsModule.java | 14 + .../hytale/server/core/plugin/PluginBase.java | 14 +- .../server/core/plugin/PluginClassLoader.java | 32 +- .../server/core/plugin/PluginManager.java | 183 +- .../plugin/pending/PendingLoadJavaPlugin.java | 13 +- .../plugin/pending/PendingLoadPlugin.java | 140 +- .../server/core/prefab/PrefabRotation.java | 13 +- .../server/core/prefab/PrefabStore.java | 28 + .../config/SelectionPrefabSerializer.java | 232 +- .../buffer/BinaryPrefabBufferCodec.java | 442 ++- .../buffer/BsonPrefabBufferDeserializer.java | 335 +- .../selection/buffer/PrefabBufferCodec.java | 4 - .../buffer/PrefabBufferDeserializer.java | 8 - .../buffer/PrefabBufferSerializer.java | 7 - .../selection/buffer/PrefabBufferUtil.java | 58 +- .../prefab/selection/buffer/PrefabLoader.java | 34 +- .../selection/buffer/impl/IPrefabBuffer.java | 2 - .../selection/buffer/impl/PrefabBuffer.java | 742 +++-- .../buffer/impl/PrefabBufferColumn.java | 22 +- .../prefab/selection/mask/BlockFilter.java | 28 +- .../core/prefab/selection/mask/BlockMask.java | 12 +- .../prefab/selection/mask/BlockPattern.java | 13 + .../prefab/selection/mask/MultiBlockMask.java | 2 +- .../selection/standard/BlockSelection.java | 533 ++-- .../server/core/schema/SchemaGenerator.java | 299 ++ .../core/ui/browser/AssetPackSaveBrowser.java | 634 ++++ .../browser/AssetPackSaveBrowserConfig.java | 10 + .../AssetPackSaveBrowserEventData.java | 123 + .../core/ui/browser/ServerFileBrowser.java | 9 + .../server/core/universe/PlayerRef.java | 29 +- .../server/core/universe/StorageManager.java | 202 ++ .../hytale/server/core/universe/Universe.java | 478 ++- .../playerdata/DiskPlayerStorageProvider.java | 31 +- .../universe/playerdata/PlayerStorage.java | 6 +- .../PlayerVelocityInstructionSystem.java | 2 +- .../system/WorldConfigSaveSystem.java | 4 +- .../core/universe/world/ParticleUtil.java | 52 +- .../server/core/universe/world/SoundUtil.java | 37 +- .../server/core/universe/world/SpawnUtil.java | 6 +- .../server/core/universe/world/World.java | 93 +- .../core/universe/world/WorldConfig.java | 51 +- .../core/universe/world/WorldMapTracker.java | 72 +- .../world/WorldNotificationHandler.java | 60 - .../world/accessor/BlockAccessor.java | 22 +- .../world/accessor/EmptyBlockAccessor.java | 11 - .../world/accessor/IChunkAccessorSync.java | 23 +- .../world/chunk/AbstractCachedAccessor.java | 4 +- .../core/universe/world/chunk/BlockChunk.java | 45 +- .../world/chunk/BlockComponentChunk.java | 39 +- .../universe/world/chunk/BlockOperations.java | 66 + .../world/chunk/BlockRotationUtil.java | 51 +- .../universe/world/chunk/EntityChunk.java | 18 +- .../core/universe/world/chunk/WorldChunk.java | 223 +- .../chunk/environment/EnvironmentChunk.java | 2 +- .../world/chunk/palette/BitFieldArr.java | 87 +- .../world/chunk/section/BlockSection.java | 395 +-- .../world/chunk/section/FluidSection.java | 12 +- .../blockpositions/BlockPositionProvider.java | 4 +- .../palette/AbstractByteSectionPalette.java | 282 -- .../palette/AbstractSectionPalette.java | 85 + .../palette/AbstractShortSectionPalette.java | 283 -- .../section/palette/ByteSectionPalette.java | 395 ++- .../section/palette/EmptySectionPalette.java | 24 +- .../palette/HalfByteSectionPalette.java | 369 ++- .../section/palette/ISectionPalette.java | 77 - .../section/palette/PaletteSetProvider.java | 28 + .../section/palette/PaletteTypeEnum.java | 6 +- .../section/palette/ShortSectionPalette.java | 351 ++- .../world/chunk/state/TickableBlockState.java | 22 - .../world/chunk/systems/ChunkSystems.java | 84 - .../world/commands/WorldSettingsCommand.java | 239 +- .../block/BlockInspectFillerCommand.java | 20 +- .../block/BlockInspectPhysicsCommand.java | 25 +- .../block/BlockInspectRotationCommand.java | 10 +- .../world/commands/block/BlockRowCommand.java | 16 +- .../commands/block/SimpleBlockCommand.java | 2 +- .../block/bulk/BlockBulkFindCommand.java | 16 +- .../block/bulk/BlockBulkFindHereCommand.java | 32 +- .../block/bulk/BlockBulkReplaceCommand.java | 10 +- .../commands/world/perf/WorldPerfCommand.java | 4 +- .../WorldConfigPauseTimeCommand.java | 7 +- .../WorldConfigSetSpawnCommand.java | 27 +- .../ConnectedBlockFaceTags.java | 34 +- .../ConnectedBlockPatternRule.java | 30 +- .../ConnectedBlockRuleSet.java | 4 +- .../connectedblocks/ConnectedBlocksUtil.java | 36 +- .../CustomConnectedBlockPattern.java | 19 +- .../CustomConnectedBlockTemplateAsset.java | 6 +- .../CustomTemplateConnectedBlockPattern.java | 6 +- .../CustomTemplateConnectedBlockRuleSet.java | 4 +- .../PatternRotationDefinition.java | 8 +- .../world/connectedblocks/Rotation3D.java | 2 +- .../builtin/RoofConnectedBlockRuleSet.java | 34 +- .../builtin/StairConnectedBlockRuleSet.java | 26 +- .../world/lighting/ChunkLightingManager.java | 21 +- .../world/lighting/FloodLightCalculation.java | 102 +- .../lighting/FullBrightLightCalculation.java | 49 +- .../world/lighting/LightCalculation.java | 7 +- .../core/universe/world/map/WorldMap.java | 8 +- .../core/universe/world/meta/BlockState.java | 293 -- .../universe/world/meta/BlockStateModule.java | 518 ---- .../world/meta/BlockStateRegistration.java | 29 - .../world/meta/BlockStateRegistry.java | 30 - .../world/meta/state/BlockMapMarker.java | 5 +- .../meta/state/BlockMapMarkersResource.java | 5 +- .../meta/state/BreakValidatedBlockState.java | 10 - .../meta/state/DestroyableBlockState.java | 6 - .../meta/state/ItemContainerBlockState.java | 7 - .../world/meta/state/ItemContainerState.java | 181 -- .../world/meta/state/PlacedByBlockState.java | 11 - .../world/meta/state/RespawnBlock.java | 2 +- .../world/meta/state/SendableBlockState.java | 16 - .../universe/world/path/IPathWaypoint.java | 6 +- .../world/path/SimplePathWaypoint.java | 6 +- .../spawn/FitToHeightMapSpawnProvider.java | 12 +- .../world/spawn/GlobalSpawnProvider.java | 6 +- .../universe/world/spawn/ISpawnProvider.java | 2 +- .../world/spawn/IndividualSpawnProvider.java | 6 +- .../universe/world/storage/ChunkStore.java | 247 +- .../universe/world/storage/EntityStore.java | 6 +- .../universe/world/storage/IChunkSaver.java | 7 + .../storage/component/ChunkSavingSystems.java | 7 +- .../storage/provider/BackupChunkLoader.java | 124 + .../provider/DefaultChunkStorageProvider.java | 18 + .../provider/IChunkStorageProvider.java | 21 +- .../IndexedStorageChunkStorageProvider.java | 39 +- .../provider/RocksDbChunkStorageProvider.java | 15 + .../world/system/WorldPregenerateSystem.java | 4 +- .../worldgen/GeneratedBlockStateChunk.java | 10 +- .../world/worldgen/GeneratedChunkSection.java | 52 +- .../world/worldgen/GeneratedEntityChunk.java | 7 +- .../world/worldmap/WorldMapManager.java | 35 +- .../world/worldmap/WorldMapSettings.java | 13 + .../worldmap/markers/MapMarkerBuilder.java | 4 +- .../worldmap/markers/MapMarkerTracker.java | 4 +- .../worldmap/markers/MarkersCollector.java | 2 +- .../providers/OtherPlayersMarkerProvider.java | 4 +- .../providers/PersonalMarkersProvider.java | 2 +- .../providers/RespawnMarkerProvider.java | 2 +- .../providers/SharedMarkersProvider.java | 2 +- .../providers/SpawnMarkerProvider.java | 2 +- .../markers/user/UserMarkerValidator.java | 6 +- .../worldmap/provider/chunk/ImageBuilder.java | 169 +- .../core/update/command/UpdateCommand.java | 1 + .../update/command/UpdateDownloadCommand.java | 134 +- .../update/command/UpdateSetupCommand.java | 83 + .../hytale/server/core/util/BsonUtil.java | 22 + .../server/core/util/FillerBlockUtil.java | 296 ++ .../hytale/server/core/util/MessageUtil.java | 62 +- .../hytale/server/core/util/PositionUtil.java | 27 +- .../hytale/server/core/util/PrefabUtil.java | 20 +- .../hytale/server/core/util/TargetUtil.java | 76 +- .../server/core/util/backup/BackupTask.java | 2 + .../server/core/util/backup/BackupUtil.java | 3 +- .../server/core/util/io/ByteBufUtil.java | 47 +- .../core/util/thread/TickingThread.java | 4 +- .../hypixel/hytale/server/flock/Flock.java | 3 +- .../hytale/server/flock/FlockPlugin.java | 55 +- .../hytale/server/flock/FlockSystems.java | 16 +- .../flock/commands/NPCFlockCommand.java | 38 +- .../corecomponents/ActionFlockBeacon.java | 4 +- .../corecomponents/ActionFlockLeave.java | 5 +- .../corecomponents/ActionFlockState.java | 2 +- .../flock/corecomponents/BodyMotionFlock.java | 32 +- .../conditions/FlockSizeCondition.java | 2 - .../RenameSpawnMarkerMigration.java | 63 - .../hypixel/hytale/server/npc/NPCPlugin.java | 57 +- .../server/npc/asset/builder/BuilderBase.java | 24 +- .../npc/asset/builder/BuilderManager.java | 10 + .../npc/asset/builder/BuilderSupport.java | 2 +- .../npc/asset/builder/FeatureOverride.java | 27 + .../validators/SubTypeTypeAdapterFactory.java | 5 + .../npc/blackboard/view/BlockRegionView.java | 6 +- .../view/BlockRegionViewManager.java | 2 +- .../view/IBlackboardViewManager.java | 2 +- .../view/SingletonBlackboardViewManager.java | 2 +- .../BlockPositionEntryGenerator.java | 6 +- .../view/blocktype/BlockTypeView.java | 8 +- .../view/event/EventNotification.java | 4 +- .../view/event/EventTypeRegistration.java | 4 +- .../view/event/block/BlockEventView.java | 2 +- .../view/event/entity/EntityEventView.java | 2 +- .../view/resource/ResourceView.java | 4 +- .../server/npc/commands/NPCAllCommand.java | 18 +- .../npc/commands/NPCBlackboardCommand.java | 4 +- .../server/npc/commands/NPCCommand.java | 1 + .../npc/commands/NPCDescriptorsCommand.java | 39 + .../server/npc/commands/NPCGiveCommand.java | 4 +- .../commands/NPCMultiSelectCommandBase.java | 17 +- .../server/npc/commands/NPCPathCommand.java | 6 +- .../npc/commands/NPCRunTestsCommand.java | 12 +- .../npc/commands/NPCSensorStatsCommand.java | 2 +- .../server/npc/commands/NPCSpawnCommand.java | 39 +- .../server/npc/commands/NPCTestCommand.java | 2 +- .../messaging/EntityEventSupport.java | 14 +- .../components/messaging/EventMessage.java | 6 +- .../components/messaging/EventSupport.java | 14 +- .../server/npc/corecomponents/ActionBase.java | 5 +- .../npc/corecomponents/BlockTarget.java | 7 +- .../npc/corecomponents/BodyMotionBase.java | 35 + .../ISensorEntityPrioritiser.java | 2 +- .../npc/corecomponents/WeightedAction.java | 9 +- .../audiovisual/ActionAppearance.java | 5 +- .../audiovisual/ActionDisplayName.java | 5 +- .../audiovisual/ActionModelAttachment.java | 2 +- .../audiovisual/ActionPlayAnimation.java | 2 +- .../audiovisual/ActionPlaySound.java | 7 +- .../audiovisual/ActionSpawnParticles.java | 13 +- .../builders/BuilderActionSpawnParticles.java | 4 +- .../builders/BuilderActionBase.java | 5 +- .../builders/BuilderWeightedAction.java | 5 +- .../corecomponents/combat/ActionAttack.java | 8 +- .../combat/BodyMotionAimCharge.java | 18 +- .../corecomponents/combat/HeadMotionAim.java | 63 +- .../npc/corecomponents/debug/ActionLog.java | 5 +- .../debug/BodyMotionTestProbe.java | 35 +- .../builders/BuilderBodyMotionTestProbe.java | 46 +- .../corecomponents/entity/ActionBeacon.java | 29 +- .../entity/ActionIgnoreForAvoidance.java | 3 +- .../entity/ActionReleaseTarget.java | 3 +- .../entity/ActionSetMarkedTarget.java | 8 +- .../corecomponents/entity/ActionSetStat.java | 5 +- .../entity/HeadMotionWatch.java | 8 +- .../corecomponents/entity/SensorBeacon.java | 4 +- .../entity/SensorEntityBase.java | 2 +- .../npc/corecomponents/entity/SensorKill.java | 2 +- .../corecomponents/entity/SensorTarget.java | 4 +- .../entity/builders/BuilderActionBeacon.java | 16 +- .../filters/EntityFilterEntityEffect.java | 33 + .../filters/EntityFilterHeightDifference.java | 2 +- .../filters/EntityFilterInsideBlock.java | 2 +- .../entity/filters/EntityFilterInventory.java | 8 +- .../entity/filters/EntityFilterSpotsMe.java | 4 +- .../filters/EntityFilterStandingOnBlock.java | 2 +- .../filters/EntityFilterViewSector.java | 28 +- .../BuilderEntityFilterEntityEffect.java | 53 + .../BuilderEntityFilterViewSector.java | 2 +- .../SensorEntityPrioritiserAttitude.java | 59 +- .../SensorEntityPrioritiserDefault.java | 2 +- .../ActionLockOnInteractionTarget.java | 5 +- .../interaction/ActionSetInteractable.java | 4 +- .../interaction/SensorCanInteract.java | 8 +- .../corecomponents/items/ActionDropItem.java | 14 +- .../corecomponents/items/ActionInventory.java | 55 +- .../items/ActionPickUpItem.java | 19 +- .../items/SensorDroppedItem.java | 6 +- .../lifecycle/ActionDelayDespawn.java | 3 +- .../lifecycle/ActionDespawn.java | 3 +- .../corecomponents/lifecycle/ActionDie.java | 3 +- .../corecomponents/lifecycle/ActionRole.java | 4 +- .../corecomponents/lifecycle/ActionSpawn.java | 34 +- .../corecomponents/movement/ActionCrouch.java | 3 +- .../movement/ActionOverrideAltitude.java | 5 +- .../movement/BodyMotionFind.java | 27 +- .../movement/BodyMotionFindBase.java | 125 +- .../movement/BodyMotionFindWithTarget.java | 35 +- .../movement/BodyMotionLand.java | 2 +- .../movement/BodyMotionLeave.java | 2 +- .../movement/BodyMotionMaintainDistance.java | 65 +- .../movement/BodyMotionMatchLook.java | 2 +- .../movement/BodyMotionMoveAway.java | 38 +- .../movement/BodyMotionTakeOff.java | 4 +- .../movement/BodyMotionTeleport.java | 32 +- .../movement/BodyMotionWander.java | 2 +- .../movement/BodyMotionWanderBase.java | 91 +- .../movement/BodyMotionWanderInCircle.java | 13 +- .../movement/BodyMotionWanderInRect.java | 6 +- .../builders/BuilderBodyMotionFindBase.java | 45 +- .../builders/BuilderBodyMotionWanderBase.java | 46 +- .../statemachine/ActionParentState.java | 3 +- .../statemachine/ActionState.java | 3 +- .../ActionToggleStateEvaluator.java | 13 +- .../BuilderActionToggleStateEvaluator.java | 8 +- .../corecomponents/timer/ActionSetAlarm.java | 3 +- .../npc/corecomponents/timer/ActionTimer.java | 3 +- .../corecomponents/utility/ActionRandom.java | 4 +- .../utility/ActionResetInstructions.java | 3 +- .../utility/ActionSequence.java | 4 +- .../corecomponents/utility/ActionSetFlag.java | 3 +- .../corecomponents/utility/ActionTimeout.java | 4 +- .../utility/SensorAdjustPosition.java | 2 +- .../builders/BuilderSensorAdjustPosition.java | 2 +- .../corecomponents/world/ActionMakePath.java | 4 +- .../world/ActionPlaceBlock.java | 16 +- .../world/ActionResetBlockSensors.java | 3 +- .../corecomponents/world/ActionResetPath.java | 3 +- .../world/ActionResetSearchRays.java | 3 +- .../world/ActionSetBlockToPlace.java | 5 +- .../world/ActionSetLeashPosition.java | 10 +- .../world/ActionTriggerSpawners.java | 10 +- .../corecomponents/world/BodyMotionPath.java | 27 +- .../world/HeadMotionObserve.java | 10 +- .../npc/corecomponents/world/SensorBlock.java | 9 +- .../world/SensorBlockChange.java | 2 +- .../corecomponents/world/SensorCanPlace.java | 42 +- .../world/SensorEntityEvent.java | 2 +- .../npc/corecomponents/world/SensorLeash.java | 4 +- .../npc/corecomponents/world/SensorPath.java | 21 +- .../world/SensorReadPosition.java | 7 +- .../corecomponents/world/SensorSearchRay.java | 25 +- .../npc/decisionmaker/core/Evaluator.java | 27 +- .../server/npc/decisionmaker/core/Option.java | 24 +- .../conditions/SelfHasEffectCondition.java | 62 + .../conditions/TargetDistanceCondition.java | 4 +- .../conditions/TargetHasEffectCondition.java | 65 + .../core/conditions/base/Condition.java | 4 + .../stateevaluator/StateEvaluator.java | 2 + .../hytale/server/npc/entities/NPCEntity.java | 33 +- .../server/npc/instructions/ActionList.java | 4 +- .../server/npc/instructions/BodyMotion.java | 18 + .../npc/interactions/SpawnNPCInteraction.java | 31 +- .../npc/interactions/UseNPCInteraction.java | 10 +- .../npc/metadata/CapturedNPCMetadata.java | 20 + .../movement/GroupSteeringAccumulator.java | 83 +- .../hytale/server/npc/movement/Steering.java | 93 +- .../constraints/RelaxedConstraint.java | 25 + .../controllers/MotionController.java | 87 +- .../controllers/MotionControllerBase.java | 421 +-- .../controllers/MotionControllerDive.java | 98 +- .../controllers/MotionControllerFly.java | 93 +- .../controllers/MotionControllerWalk.java | 1582 ++++++---- .../movement/controllers/ProbeMoveData.java | 125 +- .../builders/BuilderMotionControllerWalk.java | 15 + .../SteeringForceAvoidCollision.java | 37 +- .../steeringforces/SteeringForceEvade.java | 6 +- .../steeringforces/SteeringForcePursue.java | 10 +- .../steeringforces/SteeringForceRotate.java | 2 +- .../steeringforces/SteeringForceWander.java | 2 +- .../SteeringForceWithGroup.java | 4 +- .../SteeringForceWithTarget.java | 14 +- .../server/npc/navigation/AStarBase.java | 34 +- .../server/npc/navigation/AStarEvaluator.java | 2 +- .../server/npc/navigation/AStarNode.java | 8 +- .../npc/navigation/AStarWithTarget.java | 4 +- .../server/npc/navigation/IWaypoint.java | 2 +- .../server/npc/navigation/PathFollower.java | 43 +- .../server/npc/pages/EntitySpawnPage.java | 36 +- .../hypixel/hytale/server/npc/role/Role.java | 421 ++- .../server/npc/role/RoleDebugDisplay.java | 8 +- .../server/npc/role/RoleDebugFlags.java | 12 +- .../hytale/server/npc/role/RoleUtils.java | 9 +- .../hytale/server/npc/role/SpawnEffect.java | 58 +- .../server/npc/role/builders/BuilderRole.java | 273 +- .../server/npc/role/support/DebugSupport.java | 47 +- .../server/npc/role/support/EntityList.java | 4 +- .../npc/role/support/MarkedEntitySupport.java | 8 +- .../npc/role/support/PositionCache.java | 55 +- .../server/npc/role/support/StateSupport.java | 13 +- .../server/npc/role/support/WorldSupport.java | 7 +- .../npc/sensorinfo/IPositionProvider.java | 2 +- .../npc/sensorinfo/PositionProvider.java | 4 +- .../StateTransitionController.java | 12 +- .../server/npc/systems/AvoidanceSystem.java | 104 +- .../npc/systems/ComputeVelocitySystem.java | 8 +- .../server/npc/systems/NPCDamageSystems.java | 101 +- .../server/npc/systems/NPCDeathSystems.java | 4 +- .../server/npc/systems/NPCPreTickSystem.java | 2 +- .../server/npc/systems/NPCSpatialSystem.java | 2 +- .../hytale/server/npc/systems/NPCSystems.java | 57 +- .../systems/NPCVelocityInstructionSystem.java | 2 +- .../systems/NewSpawnStartTickingSystem.java | 8 +- .../npc/systems/PositionCacheSystems.java | 17 +- .../server/npc/systems/RoleBuilderSystem.java | 10 +- .../server/npc/systems/RoleSystems.java | 112 +- .../npc/systems/SpawnReferenceSystems.java | 54 +- .../server/npc/systems/SteeringSystem.java | 12 +- .../hytale/server/npc/util/DamageData.java | 12 +- .../server/npc/util/InventoryHelper.java | 87 +- .../server/npc/util/NPCPhysicsMath.java | 122 +- .../server/npc/util/PositionProbeAir.java | 2 +- .../server/npc/util/PositionProbeBase.java | 2 +- .../server/npc/util/PositionProbeWater.java | 2 +- .../server/npc/util/RayBlockHitTest.java | 19 +- .../hytale/server/npc/util/VisHelper.java | 58 + .../expression/compile/ast/ASTOperand.java | 205 +- .../server/spawning/SpawningContext.java | 10 +- .../server/spawning/SpawningPlugin.java | 86 +- .../beacons/LegacySpawnBeaconEntity.java | 11 +- .../server/spawning/beacons/SpawnBeacon.java | 6 +- .../spawning/beacons/SpawnBeaconSystems.java | 21 +- ...rBlockState.java => SpawnMarkerBlock.java} | 48 +- .../SpawnMarkerBlockReference.java | 5 +- .../SpawnMarkerBlockStateSystems.java | 125 +- .../commands/SpawnBeaconsCommand.java | 12 +- .../spawning/commands/SpawnStatsCommand.java | 2 +- .../commands/SpawnSuppressionCommand.java | 4 +- .../controllers/BeaconSpawnController.java | 11 +- .../ActionTriggerSpawnBeacon.java | 5 +- .../TriggerSpawnMarkersInteraction.java | 10 +- .../local/LocalSpawnControllerSystem.java | 17 +- .../spawning/local/LocalSpawnState.java | 3 +- .../spawnmarkers/SpawnMarkerEntity.java | 22 +- .../spawnmarkers/SpawnMarkerSystems.java | 96 +- .../suppression/SpawnSuppressorEntry.java | 5 +- .../component/ChunkSuppressionQueue.java | 3 +- .../system/SpawnMarkerSuppressionSystem.java | 2 +- .../system/SpawnSuppressionSystems.java | 63 +- .../spawning/systems/BeaconSpatialSystem.java | 2 +- .../systems/LegacyBeaconSpatialSystem.java | 2 +- .../systems/SpawnMarkerSpatialSystem.java | 2 +- .../util/FloodFillPositionSelector.java | 12 +- .../spawning/util/LightRangePredicate.java | 8 +- .../world/WorldEnvironmentSpawnData.java | 18 +- .../world/system/WorldSpawnJobSystems.java | 6 +- .../system/WorldSpawnTrackingSystem.java | 16 +- .../world/system/WorldSpawningSystem.java | 126 +- .../server/worldgen/BiomeDataSystem.java | 6 +- .../worldgen/ChunkGeneratorResource.java | 6 +- .../server/worldgen/cave/CaveGenerator.java | 15 +- .../server/worldgen/cave/CaveNodeType.java | 5 +- .../hytale/server/worldgen/cave/CaveType.java | 2 + .../worldgen/cave/element/CaveNode.java | 2 +- .../cave/prefab/CavePrefabContainer.java | 2 + .../worldgen/cave/shape/CaveNodeShape.java | 4 +- .../cave/shape/CaveNodeShapeEnum.java | 2 +- .../cave/shape/CaveNodeShapeUtils.java | 15 +- .../cave/shape/CylinderCaveNodeShape.java | 6 +- .../cave/shape/DistortedCaveNodeShape.java | 11 +- .../cave/shape/EllipsoidCaveNodeShape.java | 4 +- .../cave/shape/EmptyLineCaveNodeShape.java | 6 +- .../cave/shape/PipeCaveNodeShape.java | 6 +- .../cave/shape/PrefabCaveNodeShape.java | 54 +- .../cave/shape/TetrahedronCaveNodeShape.java | 18 +- .../distorted/AbstractDistortedBody.java | 2 +- .../distorted/AbstractDistortedExtrusion.java | 2 +- .../distorted/AbstractDistortedShape.java | 2 +- .../distorted/DistortedCylinderShape.java | 4 +- .../distorted/DistortedEllipsoidShape.java | 2 +- .../shape/distorted/DistortedPipeShape.java | 2 +- .../cave/shape/distorted/DistortedShape.java | 6 +- .../server/worldgen/chunk/ChunkGenerator.java | 49 +- .../chunk/populator/PrefabPopulator.java | 12 +- .../worldgen/climate/ClimateSearch.java | 4 +- .../climate/UniqueClimateGenerator.java | 2 +- .../worldgen/container/CoverContainer.java | 2 + .../container/EnvironmentContainer.java | 4 + .../worldgen/container/LayerContainer.java | 3 + .../worldgen/container/PrefabContainer.java | 2 + .../worldgen/container/TintContainer.java | 2 + .../container/UniquePrefabContainer.java | 6 +- .../worldgen/container/WaterContainer.java | 2 + .../loader/ChunkGeneratorJsonLoader.java | 16 +- .../loader/MaskProviderJsonLoader.java | 6 +- .../loader/WorldGenPrefabSupplier.java | 5 + .../loader/biome/BiomeJsonLoader.java | 12 +- .../BiomePatternGeneratorJsonLoader.java | 5 + .../loader/cave/CaveGeneratorJsonLoader.java | 1 + .../cave/CaveNodeChildEntryJsonLoader.java | 10 +- .../loader/cave/CaveNodeTypeJsonLoader.java | 79 +- .../cave/CavePrefabContainerJsonLoader.java | 44 +- .../loader/cave/CaveTypeJsonLoader.java | 33 +- .../loader/cave/CaveTypesJsonLoader.java | 48 +- .../climate/UniqueClimateJsonLoader.java | 2 +- .../container/CoverContainerJsonLoader.java | 50 +- .../EnvironmentContainerJsonLoader.java | 54 +- .../container/LayerContainerJsonLoader.java | 70 +- .../container/PrefabContainerJsonLoader.java | 50 +- .../container/TintContainerJsonLoader.java | 43 +- .../container/WaterContainerJsonLoader.java | 117 +- .../loader/context/BiomeFileContext.java | 2 +- .../loader/context/CaveFileContext.java | 19 + .../worldgen/loader/context/FileContext.java | 75 +- .../loader/context/FileContextLoader.java | 6 +- .../loader/context/FileLoadingContext.java | 11 +- .../loader/context/ZoneFileContext.java | 2 +- .../UniquePrefabConfigurationJsonLoader.java | 4 +- .../loader/util/Vector2dJsonLoader.java | 2 +- .../loader/util/Vector3dJsonLoader.java | 2 +- .../worldgen/prefab/PrefabLoadingCache.java | 5 +- .../worldgen/prefab/PrefabPasteUtil.java | 10 +- .../unique/UniquePrefabConfiguration.java | 4 +- .../prefab/unique/UniquePrefabGenerator.java | 12 +- .../hytale/server/worldgen/util/ListPool.java | 67 + .../worldgen/util/bounds/IChunkBounds.java | 21 +- .../worldgen/util/bounds/IWorldBounds.java | 7 +- .../util/condition/BlockMaskCondition.java | 2 +- .../hytale/server/worldgen/zone/Zone.java | 2 +- .../server/worldgen/zoom/ExactZoom.java | 2 +- .../hytale/storage/IndexedStorageFile.java | 141 +- .../hytale/storage/IndexedStorageFile_v0.java | 1001 ------ src/manifests.json | 51 +- 2534 files changed, 108160 insertions(+), 70978 deletions(-) delete mode 100644 src/com/hypixel/hytale/builtin/adventure/farming/states/FarmingBlockState.java rename src/com/hypixel/hytale/builtin/adventure/objectives/blockstates/{TreasureChestState.java => TreasureChestBlock.java} (73%) create mode 100644 src/com/hypixel/hytale/builtin/adventure/objectives/interactions/DestroyTreasureConditionInteraction.java create mode 100644 src/com/hypixel/hytale/builtin/adventure/objectives/interactions/OpenTreasureContainerInteraction.java create mode 100644 src/com/hypixel/hytale/builtin/adventure/objectives/systems/ObjectiveInventoryChangeSystem.java create mode 100644 src/com/hypixel/hytale/builtin/adventure/objectives/task/InventoryChangeAware.java delete mode 100644 src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabSetAnchorInteraction.java create mode 100644 src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditBackCommand.java create mode 100644 src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorLoadOptionsPage.java create mode 100644 src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/ClearRotationOperation.java create mode 100644 src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/transforms/RotateOperation.java create mode 100644 src/com/hypixel/hytale/builtin/buildertools/tooloperations/RevolveOperation.java create mode 100644 src/com/hypixel/hytale/builtin/crafting/component/BenchBlock.java create mode 100644 src/com/hypixel/hytale/builtin/crafting/component/ProcessingBenchBlock.java delete mode 100644 src/com/hypixel/hytale/builtin/crafting/state/BenchState.java delete mode 100644 src/com/hypixel/hytale/builtin/crafting/state/ProcessingBenchState.java create mode 100644 src/com/hypixel/hytale/builtin/crafting/system/BenchSystems.java create mode 100644 src/com/hypixel/hytale/builtin/fluid/DisabledFluidResource.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{props/entity => }/EntityPlacementData.java (84%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem => }/GridUtils.java (59%) delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/PropField.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/PropRuntime.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/NViewport.java => Viewport.java} (88%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{datastructures => }/WeightedMap.java (98%) rename src/com/hypixel/hytale/builtin/hytalegenerator/assets/{propassignments => assignments}/AssignmentsAsset.java (82%) rename src/com/hypixel/hytale/builtin/hytalegenerator/assets/{propassignments => assignments}/ConstantAssignmentsAsset.java (69%) rename src/com/hypixel/hytale/builtin/hytalegenerator/assets/{propassignments => assignments}/FieldFunctionAssignmentsAsset.java (92%) rename src/com/hypixel/hytale/builtin/hytalegenerator/assets/{propassignments => assignments}/ImportedAssignmentsAsset.java (80%) rename src/com/hypixel/hytale/builtin/hytalegenerator/assets/{propassignments => assignments}/SandwichAssignmentsAsset.java (90%) rename src/com/hypixel/hytale/builtin/hytalegenerator/assets/{propassignments => assignments}/WeightedAssignmentsAsset.java (87%) delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/GapPatternAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/RotatorPatternAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ClustersPositionProviderAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/EmptyPositionProviderAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Jitter2dPositionProviderAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Jitter3dPositionProviderAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ScalerPositionProviderAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SquareGrid2dPositionProviderAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SquareGrid3dPositionProviderAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/TriangularGrid2dPositionProviderAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/AssignedPropDistributionAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/ConstantPropDistributionAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/ImportedPropDistributionAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/NoPropDistributionAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/PositionsPropDistributionAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/PropDistributionAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/UnionPropDistributionAsset.java rename src/com/hypixel/hytale/builtin/hytalegenerator/assets/{propstageiterations => propruntime}/PropRuntimeAsset.java (56%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/CuboidPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/DensitySelectorPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/EmptyPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/LocatorPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ManualPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/MaskPropAsset.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/NoPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/OrienterPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/RandomRotatorPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/StaticRotatorPropAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/DirectScannerAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/LinearScannerAsset.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/OriginScannerAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/QueueScannerAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/RadialScannerAsset.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/RandomScannerAsset.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{propdistributions => assignments}/Assignments.java (58%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{propdistributions => assignments}/ConstantAssignments.java (60%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{propdistributions => assignments}/FieldFunctionAssignments.java (50%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{propdistributions => assignments}/SandwichAssignments.java (55%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{propdistributions => assignments}/WeightedAssignments.java (50%) delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/bounds/SpaceSize.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/conveyor/stagedconveyor/ContextDependency.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/CollectionFactory.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/TieredList.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/BiCoordinateCache.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/HashedBiCoordinateCache.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/WrappedBiCoordinateCache.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/WrappedBiCoordinateDoubleCache.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/compression/Compressor.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/ArrayVoxelSpace.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/BooleanVoxelSpace.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/NullSpace.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelConsumer.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelCoordinate.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelSpace.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelSpaceUtil.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/WindowVoxelSpace.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem => engine}/TerrainDensityProvider.java (55%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/bufferbundle/NBufferBundle.java => engine/bufferbundle/BufferBundle.java} (67%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/Buffer.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/bufferbundle/buffers/NCountedPixelBuffer.java => engine/bufferbundle/buffers/CountedPixelBuffer.java} (75%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/bufferbundle/buffers/NEntityBuffer.java => engine/bufferbundle/buffers/EntityBuffer.java} (76%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/bufferbundle/buffers/NPixelBuffer.java => engine/bufferbundle/buffers/PixelBuffer.java} (68%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/bufferbundle/buffers/NSimplePixelBuffer.java => engine/bufferbundle/buffers/SimplePixelBuffer.java} (70%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/bufferbundle/buffers/NVoxelBuffer.java => engine/bufferbundle/buffers/VoxelBuffer.java} (59%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/bufferbundle/buffers/type/NBufferType.java => engine/bufferbundle/buffers/type/BufferType.java} (65%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/bufferbundle/buffers/type/NParametrizedBufferType.java => engine/bufferbundle/buffers/type/ParametrizedBufferType.java} (66%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{ => engine}/chunkgenerator/ChunkGenerator.java (74%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{ => engine}/chunkgenerator/ChunkRequest.java (96%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{ => engine}/chunkgenerator/FallbackGenerator.java (81%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/NStagedChunkGenerator.java => engine/chunkgenerator/StagedChunkGenerator.java} (77%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem => engine}/containers/FloatContainer3d.java (85%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/entityfunnel/EntityFunnel.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/entityfunnel/RotationEntityFunnel.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem => engine}/performanceinstruments/MemInstrument.java (88%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem => engine}/performanceinstruments/TimeInstrument.java (91%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/stages/NBiomeDistanceStage.java => engine/stages/BiomeDistanceStage.java} (65%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/BiomeStage.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/stages/NEnvironmentStage.java => engine/stages/EnvironmentStage.java} (57%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/PropStage.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/Stage.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/stages/NTerrainStage.java => engine/stages/TerrainStage.java} (71%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{newsystem/stages/NTintStage.java => engine/stages/TintStage.java} (58%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/EntityBufferView.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/PixelBufferView.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/VoxelBufferView.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/ImageCarta.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/LayeredCarta.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/SingleElementCarta.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/BiDouble2DoubleFunction.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/QuadDoubleFunction.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/QuintoDoubleFunction.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriCarta.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriDoubleFunction.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriFunction.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/BitConverter.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/CoPrimeGenerator.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Combiner.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/MultipliedIteration.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Probability.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/RegionGrid.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/SeedGenerator.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Splitter.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Stepinizer.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/MaskShader.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/RelationalShader.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/Shader.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/SimpleShader.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/WeighedShader.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/iterators/BackwardIntIterator.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/iterators/ForwardIntIterator.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/iterators/IntIterators.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{framework => }/math/Calculator.java (93%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{framework => }/math/InterpolatedCurve.java (96%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{framework => }/math/Interpolation.java (80%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{framework => }/math/NodeFunction.java (97%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{framework => }/math/Normalizer.java (88%) delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NBuffer.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NBiomeStage.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NPropStage.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NStage.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTestPropStage.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTestTerrainStage.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/EntityContainer.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NEntityBufferView.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NPixelBufferView.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NVoxelBufferView.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{fields => }/noise/CellNoiseField.java (96%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{fields => noise}/FastNoiseLite.java (99%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{fields => }/noise/NoiseField.java (93%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{fields => }/noise/Simplex.java (99%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{fields => }/noise/SimplexNoiseField.java (98%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{fields/points => noise/pointprovider}/JitterPointField.java (82%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{fields/points => noise/pointprovider}/PointField.java (88%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{fields/points => noise/pointprovider}/PointProvider.java (80%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/patterns/ConstantPattern.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/patterns/GapPattern.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/patterns/RotatorPattern.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/pipe/Control.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/pipe/Pipe.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ClustersPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/EmptyPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Jitter2dPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Jitter3dPositionProvider.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Mesh2DPositionProvider.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Mesh3DPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ScalerPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SquareGrid2dPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SquareGrid3dPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/TriangularGrid2dPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/deprecated/Mesh2DPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/deprecated/Mesh3DPositionProvider.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/AssignedPropDistribution.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/ConstantPropDistribution.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/NoPropDistribution.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/PositionsPropDistribution.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/PropDistribution.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/UnionPropDistribution.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/BoxProp.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/ClusterProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/CuboidProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/DensitySelectorProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/EmptyProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/LocatorProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/ManualProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/MaskProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/OrienterProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/PondFillerProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/PrefabProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/StaticRotatorProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/BoxProp.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ClusterProp.java rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/ColumnProp.java (66%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/DensityProp.java rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/PositionListScanResult.java (89%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/PositionScanResult.java (76%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/ScanResult.java (81%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/directionality/Directionality.java (64%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/directionality/OrthogonalDirection.java (80%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/directionality/PatternDirectionality.java (77%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/directionality/RandomDirectionality.java (65%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/directionality/RotatedPosition.java (77%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/directionality/RotatedPositionsScanResult.java (82%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/directionality/StaticDirectionality.java (75%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/filler/FillerPropScanResult.java (82%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/filler/PondFillerProp.java (55%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/prefab/MoldingDirection.java (82%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/prefab/PrefabMoldingConfiguration.java (91%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{ => deprecated}/prefab/PrefabProp.java (56%) rename src/com/hypixel/hytale/builtin/hytalegenerator/props/{prefab/PropPrefabUtil.java => deprecated/prefab/PrefabPropUtil.java} (78%) create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/rng/Rng.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/rng/RngField.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{seed => rng}/SeedBox.java (78%) delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/AreaScanner.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/ColumnLinearScanner.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/ColumnRandomScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/DirectScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/EmptyScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/LinearScanner.java delete mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/OriginScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/QueueScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/RadialScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/RandomScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/AreaScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/ColumnLinearScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/ColumnRandomScanner.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/ArrayVoxelSpace.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/MaskVoxelSpace.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/NullSpace.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/RotationVoxelSpace.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/VoxelSpace.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/VoxelSpaceUtil.java create mode 100644 src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/WindowVoxelSpace.java rename src/com/hypixel/hytale/builtin/hytalegenerator/{threadindexer => workerindexer}/WorkerIndexer.java (93%) rename src/com/hypixel/hytale/builtin/hytalegenerator/{framework/interfaces/functions => worldstructure}/BiCarta.java (61%) delete mode 100644 src/com/hypixel/hytale/builtin/portals/utils/spatial/OctTree.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/EventHandler.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/Target.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/WorldGenModifier.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/Codecs.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/Content.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/FileContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/CaveTypeContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/CaveTypeGenerator.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/ore/OreCluster.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/BlockEntry.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/BlockMask.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/HeightMask.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/NoiseMask.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/PointGrid.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/BiomeCoverContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/CaveCoverContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/CoverContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/fluid/BiomeFluidContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/BiomeDynamicLayerContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/BiomeStaticLayerContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/LayerContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/NoiseBlockEntry.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/BiomePrefabContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/CavePrefabContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/PrefabContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/content/tint/BiomeTintContent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/event/ModifyEvent.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/event/ModifyEvents.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/op/AddOp.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/op/LogOp.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/op/Op.java create mode 100644 src/com/hypixel/hytale/builtin/worldgen/modifier/op/RemoveOp.java create mode 100644 src/com/hypixel/hytale/common/plugin/Mod.java create mode 100644 src/com/hypixel/hytale/common/plugin/ModLoadOrderException.java create mode 100644 src/com/hypixel/hytale/component/event/EntityHolderEventType.java create mode 100644 src/com/hypixel/hytale/component/system/EntityHolderEventSystem.java create mode 100644 src/com/hypixel/hytale/function/consumer/BiIntConsumer.java create mode 100644 src/com/hypixel/hytale/function/consumer/TriIntObjectConsumer.java delete mode 100644 src/com/hypixel/hytale/math/Mat4f.java delete mode 100644 src/com/hypixel/hytale/math/Quatf.java delete mode 100644 src/com/hypixel/hytale/math/Vec2f.java delete mode 100644 src/com/hypixel/hytale/math/Vec3f.java delete mode 100644 src/com/hypixel/hytale/math/Vec4f.java create mode 100644 src/com/hypixel/hytale/math/data/Int3ObjectOpenHashMap.java delete mode 100644 src/com/hypixel/hytale/math/matrix/Matrix4d.java create mode 100644 src/com/hypixel/hytale/math/matrix/Matrix4dUtil.java create mode 100644 src/com/hypixel/hytale/math/vector/Rotation3f.java create mode 100644 src/com/hypixel/hytale/math/vector/Rotation3fc.java delete mode 100644 src/com/hypixel/hytale/math/vector/Vector2d.java create mode 100644 src/com/hypixel/hytale/math/vector/Vector2dUtil.java create mode 100644 src/com/hypixel/hytale/math/vector/Vector2fUtil.java delete mode 100644 src/com/hypixel/hytale/math/vector/Vector2i.java create mode 100644 src/com/hypixel/hytale/math/vector/Vector2iUtil.java delete mode 100644 src/com/hypixel/hytale/math/vector/Vector2l.java create mode 100644 src/com/hypixel/hytale/math/vector/Vector3LUtil.java delete mode 100644 src/com/hypixel/hytale/math/vector/Vector3d.java create mode 100644 src/com/hypixel/hytale/math/vector/Vector3dUtil.java delete mode 100644 src/com/hypixel/hytale/math/vector/Vector3f.java create mode 100644 src/com/hypixel/hytale/math/vector/Vector3fUtil.java delete mode 100644 src/com/hypixel/hytale/math/vector/Vector3i.java create mode 100644 src/com/hypixel/hytale/math/vector/Vector3iUtil.java delete mode 100644 src/com/hypixel/hytale/math/vector/Vector3l.java delete mode 100644 src/com/hypixel/hytale/math/vector/Vector4d.java create mode 100644 src/com/hypixel/hytale/math/vector/Vector4dUtil.java delete mode 100644 src/com/hypixel/hytale/math/vector/relative/RelativeVector2d.java delete mode 100644 src/com/hypixel/hytale/math/vector/relative/RelativeVector2i.java delete mode 100644 src/com/hypixel/hytale/math/vector/relative/RelativeVector2l.java delete mode 100644 src/com/hypixel/hytale/math/vector/relative/RelativeVector3d.java delete mode 100644 src/com/hypixel/hytale/math/vector/relative/RelativeVector3i.java delete mode 100644 src/com/hypixel/hytale/math/vector/relative/RelativeVector3l.java create mode 100644 src/com/hypixel/hytale/protocol/AmbienceFXPhysicalMaterial.java create mode 100644 src/com/hypixel/hytale/protocol/ConditionalBlockSound.java create mode 100644 src/com/hypixel/hytale/protocol/DebugFlags.java create mode 100644 src/com/hypixel/hytale/protocol/FormattedMessageImage.java delete mode 100644 src/com/hypixel/hytale/protocol/ItemBuilderToolData.java create mode 100644 src/com/hypixel/hytale/protocol/ItemHudUI.java create mode 100644 src/com/hypixel/hytale/protocol/ItemHudUIType.java create mode 100644 src/com/hypixel/hytale/protocol/PhysicalMaterial.java create mode 100644 src/com/hypixel/hytale/protocol/ProtocolEmote.java rename src/com/hypixel/hytale/protocol/{SortType.java => RoofState.java} (54%) create mode 100644 src/com/hypixel/hytale/protocol/ShelterType.java create mode 100644 src/com/hypixel/hytale/protocol/SpaceSize.java create mode 100644 src/com/hypixel/hytale/protocol/SubCategoryDefinition.java create mode 100644 src/com/hypixel/hytale/protocol/SurfaceType.java delete mode 100644 src/com/hypixel/hytale/protocol/Vector2f.java delete mode 100644 src/com/hypixel/hytale/protocol/Vector3f.java create mode 100644 src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorModsDirectories.java create mode 100644 src/com/hypixel/hytale/protocol/packets/assets/UpdateEmotes.java create mode 100644 src/com/hypixel/hytale/protocol/packets/assets/UpdatePhysicalMaterials.java delete mode 100644 src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushData.java create mode 100644 src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolResetClipboardRotation.java create mode 100644 src/com/hypixel/hytale/protocol/packets/buildertools/ClipboardEntityChange.java create mode 100644 src/com/hypixel/hytale/protocol/packets/buildertools/PrefabSetAnchor.java rename src/com/hypixel/hytale/protocol/packets/buildertools/{BuilderToolArgGroup.java => RotationFace.java} (50%) create mode 100644 src/com/hypixel/hytale/protocol/packets/connection/ClientDisconnect.java create mode 100644 src/com/hypixel/hytale/protocol/packets/connection/ClientDisconnectReason.java create mode 100644 src/com/hypixel/hytale/protocol/packets/connection/QuicApplicationErrorCode.java rename src/com/hypixel/hytale/protocol/packets/connection/{Disconnect.java => ServerDisconnect.java} (51%) create mode 100644 src/com/hypixel/hytale/protocol/packets/entities/PlayEmote.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/ArgCacheInvalidation.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/ArgValuesRequest.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/ArgValuesResponse.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/CommandArgInfo.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/CommandOptionalArgEntry.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionOverride.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionsRequest.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionsResponse.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/CommandTreeEntry.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/CommandTreeSync.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/CommandVariantEntry.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/ExecuteServersidePageCommand.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/InitServersideUICommand.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/InsertDataContextCollectionItemServersideUIProperty.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/RemoveDataContextCollectionItemServersideUIProperty.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/ServersideUICommand.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/SetDataContextPropertyServersideUIProperty.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/SetElementPropertyServersideUICommand.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIBoolDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIByteDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UICommandDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIDoubleDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIEnumDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIFloatDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIIntDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIListDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UILongDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIObjectDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UISByteDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIShortDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIStringDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIUIntDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIULongDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UIUShortDataValue.java create mode 100644 src/com/hypixel/hytale/protocol/packets/interface_/UpdateServersideUIPage.java create mode 100644 src/com/hypixel/hytale/protocol/packets/stream/StreamOpen.java create mode 100644 src/com/hypixel/hytale/protocol/packets/stream/StreamOpenResponse.java create mode 100644 src/com/hypixel/hytale/protocol/packets/stream/StreamType.java create mode 100644 src/com/hypixel/hytale/protocol/packets/voice/RelayedVoiceData.java create mode 100644 src/com/hypixel/hytale/protocol/packets/voice/VoiceCodec.java create mode 100644 src/com/hypixel/hytale/protocol/packets/voice/VoiceConfig.java create mode 100644 src/com/hypixel/hytale/protocol/packets/voice/VoiceData.java create mode 100644 src/com/hypixel/hytale/protocol/packets/world/PlaySoundEventLocalPlayer.java delete mode 100644 src/com/hypixel/hytale/server/core/asset/GenerateSchemaEvent.java create mode 100644 src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXPhysicalMaterial.java create mode 100644 src/com/hypixel/hytale/server/core/asset/type/blocktype/config/ConditionalBlockSound.java delete mode 100644 src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BrushData.java delete mode 100644 src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BuilderToolData.java create mode 100644 src/com/hypixel/hytale/server/core/asset/type/item/config/ItemHudUI.java create mode 100644 src/com/hypixel/hytale/server/core/asset/type/physicalmaterial/PhysicalMaterialPacketGenerator.java create mode 100644 src/com/hypixel/hytale/server/core/asset/type/physicalmaterial/config/PhysicalMaterial.java create mode 100644 src/com/hypixel/hytale/server/core/asset/type/portalworld/PortalSpawnConfig.java create mode 100644 src/com/hypixel/hytale/server/core/asset/type/responsecurve/config/SwitchResponseCurve.java create mode 100644 src/com/hypixel/hytale/server/core/auth/HttpResponseException.java delete mode 100644 src/com/hypixel/hytale/server/core/command/commands/utility/git/GitCommand.java delete mode 100644 src/com/hypixel/hytale/server/core/command/commands/utility/git/UpdateAssetsCommand.java delete mode 100644 src/com/hypixel/hytale/server/core/command/commands/utility/git/UpdatePrefabsCommand.java create mode 100644 src/com/hypixel/hytale/server/core/command/system/CommandTreeBuilder.java create mode 100644 src/com/hypixel/hytale/server/core/config/ServerWorldMapConfig.java create mode 100644 src/com/hypixel/hytale/server/core/config/WorldMapConfig.java create mode 100644 src/com/hypixel/hytale/server/core/config/WorldWorldMapConfig.java create mode 100644 src/com/hypixel/hytale/server/core/cosmetics/EmoteAsset.java create mode 100644 src/com/hypixel/hytale/server/core/cosmetics/EmoteAssetPacketGenerator.java delete mode 100644 src/com/hypixel/hytale/server/core/event/events/entity/LivingEntityInventoryChangeEvent.java create mode 100644 src/com/hypixel/hytale/server/core/event/events/player/RemovedPlayerFromWorldEvent.java create mode 100644 src/com/hypixel/hytale/server/core/inventory/InventoryChangeEvent.java create mode 100644 src/com/hypixel/hytale/server/core/inventory/InventoryComponent.java create mode 100644 src/com/hypixel/hytale/server/core/inventory/InventorySystems.java create mode 100644 src/com/hypixel/hytale/server/core/inventory/container/FetchedItemContainer.java create mode 100644 src/com/hypixel/hytale/server/core/io/stream/PendingStreamHandler.java create mode 100644 src/com/hypixel/hytale/server/core/io/stream/StreamManager.java create mode 100644 src/com/hypixel/hytale/server/core/liveconfig/LiveConfigModule.java create mode 100644 src/com/hypixel/hytale/server/core/liveconfig/LiveConfigService.java create mode 100644 src/com/hypixel/hytale/server/core/liveconfig/LiveConfigSnapshot.java create mode 100644 src/com/hypixel/hytale/server/core/modules/block/BlockEntity.java create mode 100644 src/com/hypixel/hytale/server/core/modules/block/BlockReplaceEvent.java create mode 100644 src/com/hypixel/hytale/server/core/modules/block/components/ItemContainerBlock.java rename src/com/hypixel/hytale/server/core/modules/block/system/{ItemContainerStateSpatialSystem.java => ItemContainerBlockSpatialSystem.java} (79%) create mode 100644 src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerSystems.java create mode 100644 src/com/hypixel/hytale/server/core/modules/entity/component/CachedStatsComponent.java rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/AliveCondition.java (93%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/ChargingCondition.java (96%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset/condition/PlayerCondition.java => entity/condition/CheckPlayerGameModeCondition.java} (59%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/Condition.java (97%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/EntityStatBoundCondition.java (97%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/EnvironmentCondition.java (94%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/GlidingCondition.java (94%) create mode 100644 src/com/hypixel/hytale/server/core/modules/entity/condition/HasEffectCondition.java create mode 100644 src/com/hypixel/hytale/server/core/modules/entity/condition/InFluidCondition.java create mode 100644 src/com/hypixel/hytale/server/core/modules/entity/condition/IsPlayerCondition.java rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/LogicCondition.java (97%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/NoDamageTakenCondition.java (96%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/OutOfCombatCondition.java (96%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/RegenHealthCondition.java (92%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/SprintingCondition.java (94%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/StatCondition.java (97%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/SuffocatingCondition.java (95%) rename src/com/hypixel/hytale/server/core/modules/{entitystats/asset => entity}/condition/WieldingCondition.java (94%) create mode 100644 src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/simple/CommandInteraction.java delete mode 100644 src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DestroyConditionInteraction.java delete mode 100644 src/com/hypixel/hytale/server/core/modules/migrations/EntityMigration.java rename src/com/hypixel/hytale/server/core/modules/prefabspawner/{PrefabSpawnerState.java => PrefabSpawnerBlock.java} (77%) create mode 100644 src/com/hypixel/hytale/server/core/modules/voice/VoiceModule.java create mode 100644 src/com/hypixel/hytale/server/core/modules/voice/VoiceModuleConfig.java create mode 100644 src/com/hypixel/hytale/server/core/modules/voice/VoicePacketHandler.java create mode 100644 src/com/hypixel/hytale/server/core/modules/voice/VoicePlayerState.java create mode 100644 src/com/hypixel/hytale/server/core/modules/voice/VoiceRouter.java create mode 100644 src/com/hypixel/hytale/server/core/modules/voice/VoiceStreamHandler.java create mode 100644 src/com/hypixel/hytale/server/core/modules/voice/commands/VoiceCommand.java delete mode 100644 src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferCodec.java delete mode 100644 src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferDeserializer.java delete mode 100644 src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferSerializer.java create mode 100644 src/com/hypixel/hytale/server/core/schema/SchemaGenerator.java create mode 100644 src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowser.java create mode 100644 src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowserConfig.java create mode 100644 src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowserEventData.java create mode 100644 src/com/hypixel/hytale/server/core/universe/StorageManager.java create mode 100644 src/com/hypixel/hytale/server/core/universe/world/chunk/BlockOperations.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractByteSectionPalette.java create mode 100644 src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractSectionPalette.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractShortSectionPalette.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ISectionPalette.java create mode 100644 src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/PaletteSetProvider.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/chunk/state/TickableBlockState.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/BlockState.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateModule.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateRegistration.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateRegistry.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/state/BreakValidatedBlockState.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/state/DestroyableBlockState.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/state/ItemContainerBlockState.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/state/ItemContainerState.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/state/PlacedByBlockState.java delete mode 100644 src/com/hypixel/hytale/server/core/universe/world/meta/state/SendableBlockState.java create mode 100644 src/com/hypixel/hytale/server/core/universe/world/storage/provider/BackupChunkLoader.java create mode 100644 src/com/hypixel/hytale/server/core/update/command/UpdateSetupCommand.java delete mode 100644 src/com/hypixel/hytale/server/migrations/RenameSpawnMarkerMigration.java create mode 100644 src/com/hypixel/hytale/server/npc/asset/builder/FeatureOverride.java create mode 100644 src/com/hypixel/hytale/server/npc/commands/NPCDescriptorsCommand.java create mode 100644 src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterEntityEffect.java create mode 100644 src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/builders/BuilderEntityFilterEntityEffect.java create mode 100644 src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/SelfHasEffectCondition.java create mode 100644 src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/TargetHasEffectCondition.java create mode 100644 src/com/hypixel/hytale/server/npc/movement/constraints/RelaxedConstraint.java create mode 100644 src/com/hypixel/hytale/server/npc/util/VisHelper.java rename src/com/hypixel/hytale/server/spawning/blockstates/{SpawnMarkerBlockState.java => SpawnMarkerBlock.java} (57%) create mode 100644 src/com/hypixel/hytale/server/worldgen/loader/context/CaveFileContext.java create mode 100644 src/com/hypixel/hytale/server/worldgen/util/ListPool.java delete mode 100644 src/com/hypixel/hytale/storage/IndexedStorageFile_v0.java diff --git a/src/com/hypixel/hytale/assetstore/AssetPack.java b/src/com/hypixel/hytale/assetstore/AssetPack.java index 54a883dc..32b99455 100644 --- a/src/com/hypixel/hytale/assetstore/AssetPack.java +++ b/src/com/hypixel/hytale/assetstore/AssetPack.java @@ -1,12 +1,13 @@ package com.hypixel.hytale.assetstore; +import com.hypixel.hytale.common.plugin.Mod; import com.hypixel.hytale.common.plugin.PluginManifest; import java.nio.file.FileSystem; import java.nio.file.Path; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class AssetPack { +public class AssetPack implements Mod { @Nonnull private final String name; @Nonnull @@ -41,6 +42,7 @@ public class AssetPack { return this.fileSystem; } + @Override public PluginManifest getManifest() { return this.manifest; } @@ -53,6 +55,11 @@ public class AssetPack { return this.packLocation; } + @Override + public boolean isCoreMod() { + return "Hytale:Hytale".equals(this.name); + } + @Override public boolean equals(@Nullable Object o) { if (this == o) { diff --git a/src/com/hypixel/hytale/assetstore/AssetStore.java b/src/com/hypixel/hytale/assetstore/AssetStore.java index c2fb8e47..ba229f3f 100644 --- a/src/com/hypixel/hytale/assetstore/AssetStore.java +++ b/src/com/hypixel/hytale/assetstore/AssetStore.java @@ -315,6 +315,10 @@ public abstract class AssetStore, M extends final ArrayList files = new ArrayList<>(); Set optionsSet = Set.of(); Files.walkFileTree(assetsPath, optionsSet, Integer.MAX_VALUE, new SimpleFileVisitor() { + { + Objects.requireNonNull(AssetStore.this); + } + @Nonnull public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) throws IOException { if (attrs.isRegularFile() && file.toString().endsWith(AssetStore.this.extension)) { diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/FarmingPlugin.java b/src/com/hypixel/hytale/builtin/adventure/farming/FarmingPlugin.java index bb9deae9..0fd64e21 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/FarmingPlugin.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/FarmingPlugin.java @@ -20,7 +20,6 @@ import com.hypixel.hytale.builtin.adventure.farming.interactions.UseCoopInteract import com.hypixel.hytale.builtin.adventure.farming.interactions.UseWateringCanInteraction; import com.hypixel.hytale.builtin.adventure.farming.states.CoopBlock; import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock; -import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlockState; import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock; import com.hypixel.hytale.builtin.tagset.config.NPCGroup; import com.hypixel.hytale.component.ComponentRegistryProxy; @@ -51,7 +50,6 @@ public class FarmingPlugin extends JavaPlugin { protected static FarmingPlugin instance; private ComponentType tiledSoilBlockComponentType; private ComponentType farmingBlockComponentType; - private ComponentType farmingBlockStateComponentType; private ComponentType coopBlockStateComponentType; private ComponentType coopResidentComponentType; @@ -106,7 +104,6 @@ public class FarmingPlugin extends JavaPlugin { this.getCodecRegistry(SpreadGrowthBehaviour.CODEC).register("Directional", DirectionalGrowthBehaviour.class, DirectionalGrowthBehaviour.CODEC); this.tiledSoilBlockComponentType = chunkStoreRegistry.registerComponent(TilledSoilBlock.class, "TilledSoil", TilledSoilBlock.CODEC); this.farmingBlockComponentType = chunkStoreRegistry.registerComponent(FarmingBlock.class, "FarmingBlock", FarmingBlock.CODEC); - this.farmingBlockStateComponentType = chunkStoreRegistry.registerComponent(FarmingBlockState.class, "Farming", FarmingBlockState.CODEC); this.coopBlockStateComponentType = chunkStoreRegistry.registerComponent(CoopBlock.class, "Coop", CoopBlock.CODEC); this.coopResidentComponentType = entityStoreRegistry.registerComponent(CoopResidentComponent.class, "CoopResident", CoopResidentComponent.CODEC); ComponentType blockStateInfoComponentType = BlockModule.BlockStateInfo.getComponentType(); @@ -124,7 +121,6 @@ public class FarmingPlugin extends JavaPlugin { this.coopBlockStateComponentType ) ); - chunkStoreRegistry.registerSystem(new FarmingSystems.MigrateFarming()); chunkStoreRegistry.registerSystem(new FarmingSystems.OnCoopAdded(blockStateInfoComponentType, this.coopBlockStateComponentType)); entityStoreRegistry.registerSystem(new FarmingSystems.CoopResidentEntitySystem(this.coopResidentComponentType, uuidComponentType)); entityStoreRegistry.registerSystem(new FarmingSystems.CoopResidentTicking(this.coopResidentComponentType)); @@ -154,10 +150,6 @@ public class FarmingPlugin extends JavaPlugin { return this.farmingBlockComponentType; } - public ComponentType getFarmingBlockStateComponentType() { - return this.farmingBlockStateComponentType; - } - public ComponentType getCoopBlockStateComponentType() { return this.coopBlockStateComponentType; } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/FarmingSystems.java b/src/com/hypixel/hytale/builtin/adventure/farming/FarmingSystems.java index 41adae04..46bec450 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/FarmingSystems.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/FarmingSystems.java @@ -6,13 +6,11 @@ import com.hypixel.hytale.builtin.adventure.farming.config.stages.BlockStateFarm import com.hypixel.hytale.builtin.adventure.farming.config.stages.BlockTypeFarmingStageData; import com.hypixel.hytale.builtin.adventure.farming.states.CoopBlock; import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock; -import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlockState; import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; @@ -20,8 +18,7 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.Rangef; import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -47,6 +44,8 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class FarmingSystems { private static boolean hasCropAbove(@Nonnull BlockChunk blockChunk, int x, int y, int z) { @@ -202,30 +201,6 @@ public class FarmingSystems { } } - @Deprecated(forRemoval = true) - public static class MigrateFarming extends BlockModule.MigrationSystem { - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - FarmingBlockState oldState = holder.getComponent(FarmingPlugin.get().getFarmingBlockStateComponentType()); - FarmingBlock farming = new FarmingBlock(); - farming.setGrowthProgress(oldState.getCurrentFarmingStageIndex()); - farming.setCurrentStageSet(oldState.getCurrentFarmingStageSetName()); - farming.setSpreadRate(oldState.getSpreadRate()); - holder.putComponent(FarmingBlock.getComponentType(), farming); - holder.removeComponent(FarmingPlugin.get().getFarmingBlockStateComponentType()); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nullable - @Override - public Query getQuery() { - return FarmingPlugin.get().getFarmingBlockStateComponentType(); - } - } - public static class OnCoopAdded extends RefSystem { @Nonnull private final ComponentType blockStateInfoComponentType; @@ -519,6 +494,7 @@ public class FarmingSystems { int blockId = blockSection.get(x, y, z); BlockType blockType = BlockType.getAssetMap().getAsset(blockId); FarmingSystems.updateSoilDecayTime(commandBuffer, soilComponent, blockType); + decayTime = soilComponent.getDecayTime(); } if (decayTime == null) { @@ -760,13 +736,13 @@ public class FarmingSystems { long chunkIndex = ChunkUtil.indexChunkFromBlock(worldX, worldZ); WorldChunk chunk = world.getChunkIfInMemory(chunkIndex); double blockRotation = chunk.getRotation(worldX, worldY, worldZ).yaw().getRadians(); - Vector3d spawnOffset = new Vector3d().assign(coopAsset.getResidentSpawnOffset()).rotateY((float)blockRotation); + Vector3d spawnOffset = new Vector3d().set(coopAsset.getResidentSpawnOffset()).rotateY((float)blockRotation); Vector3i coopLocation = new Vector3i(worldX, worldY, worldZ); boolean tryCapture = coopAsset.getCaptureWildNPCsInRange(); float captureRange = coopAsset.getWildCaptureRadius(); if (tryCapture && captureRange >= 0.0F) { world.execute(() -> { - for (Ref entity : TargetUtil.getAllEntitiesInSphere(coopLocation.toVector3d(), captureRange, store)) { + for (Ref entity : TargetUtil.getAllEntitiesInSphere(Vector3iUtil.toVector3d(coopLocation), captureRange, store)) { coopBlock.tryPutWildResidentFromWild(store, entity, worldTimeResource, coopLocation); } }); @@ -776,7 +752,7 @@ public class FarmingSystems { world.execute(() -> coopBlock.ensureNoResidentsInWorld(store)); } else { world.execute(() -> { - coopBlock.ensureSpawnResidentsInWorld(world, store, coopLocation.toVector3d(), spawnOffset); + coopBlock.ensureSpawnResidentsInWorld(world, store, Vector3iUtil.toVector3d(coopLocation), spawnOffset); coopBlock.generateProduceToInventory(worldTimeResource); Vector3i blockPos = new Vector3i(worldX, worldY, worldZ); BlockType currentBlockType = world.getBlockType(blockPos); diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/FarmingUtil.java b/src/com/hypixel/hytale/builtin/adventure/farming/FarmingUtil.java index a4034352..50d5245d 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/FarmingUtil.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/FarmingUtil.java @@ -10,9 +10,8 @@ import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.Rangef; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockGathering; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.HarvestingDropType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingData; @@ -38,11 +37,23 @@ import java.time.Instant; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class FarmingUtil { private static final int MAX_SECONDS_BETWEEN_TICKS = 15; private static final int BETWEEN_RANDOM = 10; + private static long cappedTickNanos(long desiredNanos, @Nonnull World world, @Nonnull FarmingBlock farmingBlock, int worldX, int worldY, int worldZ) { + long maxNanos = (long)( + (15.0 + 10.0 * HashUtil.random(farmingBlock.getGeneration() ^ 3405692655L, worldX, worldY, worldZ)) + * world.getTps() + * WorldTimeResource.getSecondsPerTick(world) + * 1.0E9 + ); + return Math.min(desiredNanos, maxNanos); + } + public static void tickFarming( @Nonnull CommandBuffer commandBuffer, @Nonnull BlockChunk blockChunk, @@ -132,24 +143,30 @@ public class FarmingUtil { currentProgress += (float)(remainingTimeSeconds / (baseDuration / growthMultiplier)); farmingBlock.setGrowthProgress(currentProgress); long nextGrowthInNanos = (remainingDurationSeconds - remainingTimeSeconds) * 1000000000L; - long randCap = (long)( - (15.0 + 10.0 * HashUtil.random(farmingBlock.getGeneration() ^ 3405692655L, worldX, worldY, worldZ)) - * world.getTps() - * WorldTimeResource.getSecondsPerTick(world) - * 1.0E9 + blockSection.scheduleTick( + ChunkUtil.indexBlock(x, y, z), + currentTime.plusNanos(cappedTickNanos(nextGrowthInNanos, world, farmingBlock, worldX, worldY, worldZ)) ); - long cappedNextGrowthInNanos = Math.min(nextGrowthInNanos, randCap); - blockSection.scheduleTick(ChunkUtil.indexBlock(x, y, z), currentTime.plusNanos(cappedNextGrowthInNanos)); break; } remainingTimeSeconds -= remainingDurationSeconds; - currentProgress = ++currentStage; + int nextStage = currentStage + 1; + if (nextStage < stages.length && !stages[nextStage].canApply(commandBuffer, sectionRef, blockRef, x, y, z)) { + farmingBlock.setGrowthProgress(currentStage); + blockChunk.markNeedsSaving(); + long regrowNanos = Math.round(baseDuration / growthMultiplier) * 1000000000L; + blockSection.scheduleTick(ChunkUtil.indexBlock(x, y, z), currentTime.plusNanos(regrowNanos)); + break; + } + + currentStage = nextStage; + currentProgress = nextStage; farmingBlock.setGrowthProgress(currentProgress); blockChunk.markNeedsSaving(); farmingBlock.setGeneration(farmingBlock.getGeneration() + 1); - if (currentStage >= stages.length) { - if (stages[currentStage - 1].implementsShouldStop()) { + if (nextStage >= stages.length) { + if (stages[nextStage - 1].implementsShouldStop()) { currentStage = stages.length - 1; farmingBlock.setGrowthProgress(currentStage); stages[currentStage].apply(commandBuffer, sectionRef, blockRef, x, y, z, stages[currentStage]); @@ -159,7 +176,7 @@ public class FarmingUtil { } } else { farmingBlock.setExecutions(0); - stages[currentStage].apply(commandBuffer, sectionRef, blockRef, x, y, z, stages[currentStage - 1]); + stages[nextStage].apply(commandBuffer, sectionRef, blockRef, x, y, z, stages[nextStage - 1]); } } @@ -211,115 +228,118 @@ public class FarmingUtil { int rotationIndex, @Nonnull Vector3i blockPosition ) { - FarmingData farmingConfig = blockType.getFarming(); - boolean isFarmable = true; - if (farmingConfig == null || farmingConfig.getStages() == null) { - isFarmable = false; - } - - if (blockType.getGathering().getHarvest() == null) { + BlockGathering gathering = blockType.getGathering(); + if (gathering == null) { return false; } else { - World world = store.getExternalData().getWorld(); - Vector3d centerPosition = new Vector3d(); - blockType.getBlockCenter(rotationIndex, centerPosition); - centerPosition.add(blockPosition); - if (isFarmable && farmingConfig.getStageSetAfterHarvest() != null) { - giveDrops(store, ref, centerPosition, blockType); - Map stageSets = farmingConfig.getStages(); - FarmingStageData[] stages = stageSets.get(farmingConfig.getStartingStageSet()); - if (stages == null) { - return false; - } else { - int currentStageIndex = stages.length - 1; - FarmingStageData previousStage = stages[currentStageIndex]; - String newStageSet = farmingConfig.getStageSetAfterHarvest(); - FarmingStageData[] newStages = stageSets.get(newStageSet); - if (newStages != null && newStages.length != 0) { - Store chunkStore = world.getChunkStore().getStore(); - Ref chunkRef = world.getChunkStore().getChunkReference(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z)); - if (chunkRef == null) { - return false; - } else { - BlockComponentChunk blockComponentChunk = chunkStore.getComponent(chunkRef, BlockComponentChunk.getComponentType()); - if (blockComponentChunk == null) { + HarvestingDropType harvestingDropType = gathering.getHarvest(); + if (harvestingDropType == null) { + return false; + } else { + World world = store.getExternalData().getWorld(); + Vector3d centerPosition = new Vector3d(); + blockType.getBlockCenter(rotationIndex, centerPosition); + centerPosition.add(blockPosition.x, blockPosition.y, blockPosition.z); + FarmingData farmingConfig = blockType.getFarming(); + boolean isFarmable = farmingConfig != null && farmingConfig.getStages() != null; + if (isFarmable && farmingConfig.getStageSetAfterHarvest() != null) { + giveDrops(store, ref, centerPosition, blockType, harvestingDropType); + Map stageSets = farmingConfig.getStages(); + FarmingStageData[] stages = stageSets.get(farmingConfig.getStartingStageSet()); + if (stages == null) { + return false; + } else { + int currentStageIndex = stages.length - 1; + FarmingStageData previousStage = stages[currentStageIndex]; + String newStageSet = farmingConfig.getStageSetAfterHarvest(); + FarmingStageData[] newStages = stageSets.get(newStageSet); + if (newStages != null && newStages.length != 0) { + Store chunkStore = world.getChunkStore().getStore(); + Ref chunkRef = world.getChunkStore().getChunkReference(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z)); + if (chunkRef == null) { return false; } else { - Instant now = store.getExternalData() - .getWorld() - .getEntityStore() - .getStore() - .getResource(WorldTimeResource.getResourceType()) - .getGameTime(); - int blockIndexColumn = ChunkUtil.indexBlockInColumn(blockPosition.x, blockPosition.y, blockPosition.z); - Ref blockRef = blockComponentChunk.getEntityReference(blockIndexColumn); - FarmingBlock farmingBlock; - if (blockRef == null) { - Holder blockEntity = ChunkStore.REGISTRY.newHolder(); - blockEntity.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(blockIndexColumn, chunkRef)); - farmingBlock = new FarmingBlock(); - farmingBlock.setLastTickGameTime(now); - farmingBlock.setCurrentStageSet(newStageSet); - blockEntity.addComponent(FarmingBlock.getComponentType(), farmingBlock); - blockRef = chunkStore.addEntity(blockEntity, AddReason.SPAWN); - } else { - farmingBlock = chunkStore.ensureAndGetComponent(blockRef, FarmingBlock.getComponentType()); - } - - farmingBlock.setCurrentStageSet(newStageSet); - farmingBlock.setGrowthProgress(0.0F); - farmingBlock.setExecutions(0); - farmingBlock.setGeneration(farmingBlock.getGeneration() + 1); - farmingBlock.setLastTickGameTime(now); - Ref sectionRef = world.getChunkStore() - .getChunkSectionReference( - ChunkUtil.chunkCoordinate(blockPosition.x), - ChunkUtil.chunkCoordinate(blockPosition.y), - ChunkUtil.chunkCoordinate(blockPosition.z) - ); - if (sectionRef == null) { - return false; - } else if (blockRef == null) { + BlockComponentChunk blockComponentChunk = chunkStore.getComponent(chunkRef, BlockComponentChunk.getComponentType()); + if (blockComponentChunk == null) { return false; } else { - BlockSection section = chunkStore.getComponent(sectionRef, BlockSection.getComponentType()); - if (section != null) { - int blockIndex = ChunkUtil.indexBlock(blockPosition.x, blockPosition.y, blockPosition.z); - section.scheduleTick(blockIndex, now); + Instant now = store.getExternalData() + .getWorld() + .getEntityStore() + .getStore() + .getResource(WorldTimeResource.getResourceType()) + .getGameTime(); + int blockIndexColumn = ChunkUtil.indexBlockInColumn(blockPosition.x, blockPosition.y, blockPosition.z); + Ref blockRef = blockComponentChunk.getEntityReference(blockIndexColumn); + FarmingBlock farmingBlock; + if (blockRef == null) { + Holder blockEntity = ChunkStore.REGISTRY.newHolder(); + blockEntity.putComponent( + BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(blockIndexColumn, chunkRef) + ); + farmingBlock = new FarmingBlock(); + farmingBlock.setLastTickGameTime(now); + farmingBlock.setCurrentStageSet(newStageSet); + blockEntity.addComponent(FarmingBlock.getComponentType(), farmingBlock); + blockRef = chunkStore.addEntity(blockEntity, AddReason.SPAWN); + } else { + farmingBlock = chunkStore.ensureAndGetComponent(blockRef, FarmingBlock.getComponentType()); } - newStages[0].apply(chunkStore, sectionRef, blockRef, blockPosition.x, blockPosition.y, blockPosition.z, previousStage); - return true; + farmingBlock.setCurrentStageSet(newStageSet); + farmingBlock.setGrowthProgress(0.0F); + farmingBlock.setExecutions(0); + farmingBlock.setGeneration(farmingBlock.getGeneration() + 1); + farmingBlock.setLastTickGameTime(now); + Ref sectionRef = world.getChunkStore() + .getChunkSectionReferenceAtBlock(blockPosition.x, blockPosition.y, blockPosition.z); + if (sectionRef == null) { + return false; + } else if (blockRef == null) { + return false; + } else { + BlockSection section = chunkStore.getComponent(sectionRef, BlockSection.getComponentType()); + if (section != null) { + int blockIndex = ChunkUtil.indexBlock(blockPosition.x, blockPosition.y, blockPosition.z); + section.scheduleTick(blockIndex, now); + } + + newStages[0].apply(chunkStore, sectionRef, blockRef, blockPosition.x, blockPosition.y, blockPosition.z, previousStage); + return true; + } } } - } - } else { - WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z)); - if (chunk != null) { - chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z); - } + } else { + WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z)); + if (chunk != null) { + chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z); + } - return false; + return false; + } + } + } else { + giveDrops(store, ref, centerPosition, blockType, harvestingDropType); + WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z)); + if (chunk != null) { + chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z); } - } - } else { - giveDrops(store, ref, centerPosition, blockType); - WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z)); - if (chunk != null) { - chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z); - } - return true; + return true; + } } } } protected static void giveDrops( - @Nonnull ComponentAccessor store, @Nonnull Ref ref, @Nonnull Vector3d origin, @Nonnull BlockType blockType + @Nonnull ComponentAccessor store, + @Nonnull Ref ref, + @Nonnull Vector3d origin, + @Nonnull BlockType blockType, + @Nonnull HarvestingDropType harvestingDropType ) { - HarvestingDropType harvest = blockType.getGathering().getHarvest(); - String itemId = harvest.getItemId(); - String dropListId = harvest.getDropListId(); + String itemId = harvestingDropType.getItemId(); + String dropListId = harvestingDropType.getDropListId(); for (ItemStack itemStack : BlockHarvestUtils.getDrops(blockType, 1, itemId, dropListId)) { ItemUtils.interactivelyPickupItem(ref, itemStack, origin, store); diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/component/CoopResidentComponent.java b/src/com/hypixel/hytale/builtin/adventure/farming/component/CoopResidentComponent.java index 1175cd22..7164100b 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/component/CoopResidentComponent.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/component/CoopResidentComponent.java @@ -5,15 +5,16 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class CoopResidentComponent implements Component { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder(CoopResidentComponent.class, CoopResidentComponent::new) - .append(new KeyedCodec<>("CoopLocation", Vector3i.CODEC), (comp, ref) -> comp.coopLocation = ref, comp -> comp.coopLocation) + .append(new KeyedCodec<>("CoopLocation", Vector3iUtil.CODEC), (comp, ref) -> comp.coopLocation = ref, comp -> comp.coopLocation) .add() .append( new KeyedCodec<>("MarkedForDespawn", BuilderCodec.BOOLEAN), @@ -51,7 +52,7 @@ public class CoopResidentComponent implements Component { @Override public Component clone() { CoopResidentComponent component = new CoopResidentComponent(); - component.coopLocation.assign(this.coopLocation); + component.coopLocation.set(this.coopLocation); component.markedForDespawn = this.markedForDespawn; return component; } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/config/FarmingCoopAsset.java b/src/com/hypixel/hytale/builtin/adventure/farming/config/FarmingCoopAsset.java index 5788042c..5453d024 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/config/FarmingCoopAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/config/FarmingCoopAsset.java @@ -12,12 +12,13 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.codecs.map.MapCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.math.range.IntRange; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class FarmingCoopAsset implements JsonAssetWithMap> { @Nonnull @@ -39,8 +40,8 @@ public class FarmingCoopAsset implements JsonAssetWithMapappend( - new KeyedCodec<>("ResidentSpawnOffset", Vector3d.CODEC), - (asset, residentSpawnOffset) -> asset.residentSpawnOffset.assign(residentSpawnOffset), + new KeyedCodec<>("ResidentSpawnOffset", Vector3dUtil.CODEC), + (asset, residentSpawnOffset) -> asset.residentSpawnOffset.set(residentSpawnOffset), asset -> asset.residentSpawnOffset ) .addValidator(Validators.nonNull()) diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/config/modifiers/LightLevelGrowthModifierAsset.java b/src/com/hypixel/hytale/builtin/adventure/farming/config/modifiers/LightLevelGrowthModifierAsset.java index 1c288c87..d81c4764 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/config/modifiers/LightLevelGrowthModifierAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/config/modifiers/LightLevelGrowthModifierAsset.java @@ -97,11 +97,11 @@ public class LightLevelGrowthModifierAsset extends GrowthModifierAsset { boolean onlySunlight = false; if (this.requireBoth) { active = this.checkArtificialLight(redLight, greenLight, blueLight) && this.checkSunLight(worldTimeResource, skyLight); - } else if (this.checkArtificialLight(redLight, greenLight, blueLight)) { - active = true; } else if (this.checkSunLight(worldTimeResource, skyLight)) { active = true; onlySunlight = true; + } else if (this.checkArtificialLight(redLight, greenLight, blueLight)) { + active = true; } if (active) { diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/BlockStateFarmingStageData.java b/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/BlockStateFarmingStageData.java index c95da9cb..2da90a50 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/BlockStateFarmingStageData.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/BlockStateFarmingStageData.java @@ -32,6 +32,35 @@ public class BlockStateFarmingStageData extends FarmingStageData { return this.state; } + @Override + public boolean canApply( + @Nonnull ComponentAccessor commandBuffer, @Nonnull Ref sectionRef, @Nonnull Ref blockRef, int x, int y, int z + ) { + ChunkSection chunkSectionComponent = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType()); + if (chunkSectionComponent == null) { + return true; + } else { + WorldChunk worldChunkComponent = commandBuffer.getComponent(chunkSectionComponent.getChunkColumnReference(), WorldChunk.getComponentType()); + if (worldChunkComponent == null) { + return true; + } else { + int originBlockId = worldChunkComponent.getBlock(x, y, z); + BlockType originBlockType = BlockType.getAssetMap().getAsset(originBlockId); + if (originBlockType == null) { + return true; + } else { + BlockType blockType = originBlockType.getBlockForState(this.state); + if (blockType == null) { + return true; + } else { + int newBlockId = BlockType.getAssetMap().getIndex(blockType.getId()); + return originBlockId == newBlockId ? true : testFillerPositions(worldChunkComponent, blockType, x, y, z); + } + } + } + } + } + @Override public void apply( @Nonnull ComponentAccessor commandBuffer, @@ -71,9 +100,11 @@ public class BlockStateFarmingStageData extends FarmingStageData { int newBlockId = BlockType.getAssetMap().getIndex(blockType.getId()); if (originBlockId != newBlockId) { int rotationIndex = worldChunkComponent.getRotationIndex(x, y, z); - commandBuffer.getExternalData() - .getWorld() - .execute(() -> worldChunkComponent.setBlock(x, y, z, newBlockId, blockType, rotationIndex, 0, 2)); + commandBuffer.getExternalData().getWorld().execute(() -> { + if (testFillerPositions(worldChunkComponent, blockType, x, y, z)) { + worldChunkComponent.setBlock(x, y, z, newBlockId, blockType, rotationIndex, 0, 2); + } + }); } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/BlockTypeFarmingStageData.java b/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/BlockTypeFarmingStageData.java index 2406e680..dc925277 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/BlockTypeFarmingStageData.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/BlockTypeFarmingStageData.java @@ -33,6 +33,29 @@ public class BlockTypeFarmingStageData extends FarmingStageData { return this.block; } + @Override + public boolean canApply( + @Nonnull ComponentAccessor commandBuffer, @Nonnull Ref sectionRef, @Nonnull Ref blockRef, int x, int y, int z + ) { + BlockType blockType = BlockType.getAssetMap().getAsset(this.block); + if (blockType == null) { + return true; + } else { + ChunkSection chunkSectionComponent = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType()); + if (chunkSectionComponent == null) { + return true; + } else { + WorldChunk worldChunkComponent = commandBuffer.getComponent(chunkSectionComponent.getChunkColumnReference(), WorldChunk.getComponentType()); + if (worldChunkComponent == null) { + return true; + } else { + int blockId = BlockType.getAssetMap().getIndex(this.block); + return blockId == worldChunkComponent.getBlock(x, y, z) ? true : testFillerPositions(worldChunkComponent, blockType, x, y, z); + } + } + } + } + @Override public void apply( @Nonnull ComponentAccessor commandBuffer, @@ -66,7 +89,9 @@ public class BlockTypeFarmingStageData extends FarmingStageData { } else { commandBuffer.getExternalData().getWorld().execute(() -> { int rotationIndex = worldChunkComponent.getRotationIndex(x, y, z); - worldChunkComponent.setBlock(x, y, z, blockId, blockType, rotationIndex, 0, 2); + if (testFillerPositions(worldChunkComponent, blockType, x, y, z)) { + worldChunkComponent.setBlock(x, y, z, blockId, blockType, rotationIndex, 0, 2); + } }); } } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/PrefabFarmingStageData.java b/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/PrefabFarmingStageData.java index a123a185..b322c338 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/PrefabFarmingStageData.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/config/stages/PrefabFarmingStageData.java @@ -17,7 +17,6 @@ import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.FastRandom; import com.hypixel.hytale.math.util.HashUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; @@ -42,6 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PrefabFarmingStageData extends FarmingStageData { @Nonnull @@ -197,14 +197,14 @@ public class PrefabFarmingStageData extends FarmingStageData { int bx = worldX + px; int by = worldY + py; int bz = worldZ + pz; - if ((secondBlockId == 0 || secondBlockId == Integer.MIN_VALUE) && blockId != 0 && blockId != Integer.MIN_VALUE) { + if (blockId != 0 && blockId != Integer.MIN_VALUE) { long chunkIndex = ChunkUtil.indexChunkFromBlock(bx, bz); - WorldChunk nonTickingChunk = chunkAccessor.getNonTickingChunk(chunkIndex); - if (nonTickingChunk == null) { + WorldChunk nonTickingWorldChunkComponent = chunkAccessor.getNonTickingChunk(chunkIndex); + if (nonTickingWorldChunkComponent == null) { return false; } else { - int worldBlock = nonTickingChunk.getBlock(bx, by, bz); - return !this.doesBlockObstruct(blockId, worldBlock); + int worldBlockId = nonTickingWorldChunkComponent.getBlock(bx, by, bz); + return worldBlockId == secondBlockId ? true : !this.doesBlockObstruct(blockId, worldBlockId); } } else { return true; @@ -229,6 +229,7 @@ public class PrefabFarmingStageData extends FarmingStageData { updatedSetBlockSettings |= 4; } + int worldBlockId = nonTickingChunk.getBlock(bx, by, bz); if (blockId != 0 && blockId != Integer.MIN_VALUE) { BlockType block = blockTypeMap.getAsset(blockId); if (block == null) { @@ -251,16 +252,22 @@ public class PrefabFarmingStageData extends FarmingStageData { return true; } - int worldBlock = nonTickingChunk.getBlock(bx, by, bz); - if ((secondBlockId == 0 || secondBlockId == Integer.MIN_VALUE) && !this.canReplace(worldBlock, blockTypeMap)) { + if ((secondBlockId == 0 || secondBlockId == Integer.MIN_VALUE) && !this.canReplace(worldBlockId, blockTypeMap)) { + return true; + } + + if (secondBlockId != 0 + && secondBlockId != Integer.MIN_VALUE + && secondBlockId != worldBlockId + && !this.canReplace(worldBlockId, blockTypeMap)) { return true; } nonTickingChunk.setBlock(bx, by, bz, blockId, block, rotation, filler, updatedSetBlockSettings); if (stateWrapper != null) { - nonTickingChunk.setState(bx, by, bz, stateWrapper.clone()); + nonTickingChunk.setState(bx, by, bz, block, rotation, stateWrapper.clone()); } - } else if (secondBlockId != 0 && secondBlockId != Integer.MIN_VALUE) { + } else if (secondBlockId != 0 && secondBlockId != Integer.MIN_VALUE && worldBlockId == secondBlockId) { nonTickingChunk.breakBlock(bx, by, bz, updatedSetBlockSettings); } @@ -282,7 +289,7 @@ public class PrefabFarmingStageData extends FarmingStageData { IPrefabBuffer.iterateAllColumns(), (blockX, blockY, blockZ, blockId, chance, holder, supportValue, rotation, filler, t) -> { int bx = worldX + prefabRotation.getX(blockX, blockZ); int by = worldY + blockY; - int bz = worldZ + prefabRotation.getX(blockZ, blockX); + int bz = worldZ + prefabRotation.getZ(blockX, blockZ); if (blockId != 0 && blockId != Integer.MIN_VALUE) { long chunkIndex = ChunkUtil.indexChunkFromBlock(bx, bz); WorldChunk nonTickingWorldChunkComponent = chunkAccessor.getNonTickingChunk(chunkIndex); @@ -336,7 +343,7 @@ public class PrefabFarmingStageData extends FarmingStageData { nonTickingWorldChunkComponent.setBlock(bx, by, bz, blockId, blockTypeAsset, rotation, filler, 2); if (holder != null) { - nonTickingWorldChunkComponent.setState(bx, by, bz, holder.clone()); + nonTickingWorldChunkComponent.setState(bx, by, bz, blockTypeAsset, rotation, holder.clone()); } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/ChangeFarmingStageInteraction.java b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/ChangeFarmingStageInteraction.java index 145daccf..c4c59cb4 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/ChangeFarmingStageInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/ChangeFarmingStageInteraction.java @@ -11,7 +11,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -33,6 +32,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.time.Instant; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class ChangeFarmingStageInteraction extends SimpleBlockInteraction { @Nonnull @@ -99,9 +99,9 @@ public class ChangeFarmingStageInteraction extends SimpleBlockInteraction { @Nonnull Vector3i targetBlock, @Nonnull CooldownHandler cooldownHandler ) { - int x = targetBlock.getX(); - int y = targetBlock.getY(); - int z = targetBlock.getZ(); + int x = targetBlock.x(); + int y = targetBlock.y(); + int z = targetBlock.z(); LOGGER.atInfo() .log( "[ChangeFarmingStage] Starting interaction at pos=(%d, %d, %d), increaseBy=%s, decreaseBy=%s, targetStage=%d, targetStageSet=%s", @@ -270,8 +270,7 @@ public class ChangeFarmingStageInteraction extends SimpleBlockInteraction { stageIndex, farmingBlock.getGeneration() ); - Ref sectionRef = world.getChunkStore() - .getChunkSectionReference(ChunkUtil.chunkCoordinate(x), ChunkUtil.chunkCoordinate(y), ChunkUtil.chunkCoordinate(z)); + Ref sectionRef = world.getChunkStore().getChunkSectionReferenceAtBlock(x, y, z); if (sectionRef != null && sectionRef.isValid()) { BlockSection blockSectionComponent = chunkStore.getComponent(sectionRef, BlockSection.getComponentType()); if (blockSectionComponent != null) { diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/FertilizeSoilInteraction.java b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/FertilizeSoilInteraction.java index 983a5046..f93c9e65 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/FertilizeSoilInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/FertilizeSoilInteraction.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -24,6 +23,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class FertilizeSoilInteraction extends SimpleBlockInteraction { @Nonnull @@ -55,14 +55,14 @@ public class FertilizeSoilInteraction extends SimpleBlockInteraction { @Nonnull Vector3i targetBlock, @Nonnull CooldownHandler cooldownHandler ) { - int x = targetBlock.getX(); - int z = targetBlock.getZ(); + int x = targetBlock.x(); + int z = targetBlock.z(); long chunkIndex = ChunkUtil.indexChunkFromBlock(x, z); WorldChunk worldChunkComponent = world.getChunk(chunkIndex); if (worldChunkComponent == null) { context.getState().state = InteractionState.Failed; } else { - Ref blockRef = worldChunkComponent.getBlockComponentEntity(x, targetBlock.getY(), z); + Ref blockRef = worldChunkComponent.getBlockComponentEntity(x, targetBlock.y(), z); if (blockRef == null || !blockRef.isValid()) { blockRef = BlockModule.ensureBlockEntity(worldChunkComponent, targetBlock.x, targetBlock.y, targetBlock.z); } @@ -72,20 +72,20 @@ public class FertilizeSoilInteraction extends SimpleBlockInteraction { TilledSoilBlock tilledSoilComponent = chunkStore.getComponent(blockRef, TilledSoilBlock.getComponentType()); if (tilledSoilComponent != null && !tilledSoilComponent.isFertilized()) { tilledSoilComponent.setFertilized(true); - worldChunkComponent.setTicking(x, targetBlock.getY(), z, true); - worldChunkComponent.setTicking(x, targetBlock.getY() + 1, z, true); + worldChunkComponent.setTicking(x, targetBlock.y(), z, true); + worldChunkComponent.setTicking(x, targetBlock.y() + 1, z, true); } else { FarmingBlock farmingBlockComponent = chunkStore.getComponent(blockRef, FarmingBlock.getComponentType()); if (farmingBlockComponent == null) { context.getState().state = InteractionState.Failed; } else { - Ref soilBlockRef = worldChunkComponent.getBlockComponentEntity(x, targetBlock.getY() - 1, z); + Ref soilBlockRef = worldChunkComponent.getBlockComponentEntity(x, targetBlock.y() - 1, z); if (soilBlockRef != null && soilBlockRef.isValid()) { tilledSoilComponent = chunkStore.getComponent(soilBlockRef, TilledSoilBlock.getComponentType()); if (tilledSoilComponent != null && !tilledSoilComponent.isFertilized()) { tilledSoilComponent.setFertilized(true); - worldChunkComponent.setTicking(x, targetBlock.getY() - 1, z, true); - worldChunkComponent.setTicking(x, targetBlock.getY(), z, true); + worldChunkComponent.setTicking(x, targetBlock.y() - 1, z, true); + worldChunkComponent.setTicking(x, targetBlock.y(), z, true); } else { context.getState().state = InteractionState.Failed; } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/HarvestCropInteraction.java b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/HarvestCropInteraction.java index e8e94f27..12a752d5 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/HarvestCropInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/HarvestCropInteraction.java @@ -7,7 +7,6 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -23,6 +22,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class HarvestCropInteraction extends SimpleBlockInteraction { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseCaptureCrateInteraction.java b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseCaptureCrateInteraction.java index 9bff1703..9c5516e6 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseCaptureCrateInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseCaptureCrateInteraction.java @@ -11,18 +11,15 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFace; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel; @@ -38,8 +35,12 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.NPCPlugin; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.metadata.CapturedNPCMetadata; +import com.hypixel.hytale.server.npc.storage.AlarmStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class UseCaptureCrateInteraction extends SimpleBlockInteraction { @Nonnull @@ -85,13 +86,13 @@ public class UseCaptureCrateInteraction extends SimpleBlockInteraction { super.tick0(firstRun, time, type, context, cooldownHandler); } else { Ref ref = context.getEntity(); - if (!(EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity)) { + InventoryComponent.Hotbar hotbarComponent = commandBuffer.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent == null) { context.getState().state = InteractionState.Failed; super.tick0(firstRun, time, type, context, cooldownHandler); } else { - Inventory inventory = livingEntity.getInventory(); - byte activeHotbarSlot = inventory.getActiveHotbarSlot(); - ItemStack inHandItemStack = inventory.getActiveHotbarItem(); + byte activeHotbarSlot = hotbarComponent.getActiveSlot(); + ItemStack inHandItemStack = hotbarComponent.getActiveItem(); if (inHandItemStack == null) { context.getState().state = InteractionState.Failed; super.tick0(firstRun, time, type, context, cooldownHandler); @@ -149,8 +150,9 @@ public class UseCaptureCrateInteraction extends SimpleBlockInteraction { itemMetaData.setFullItemIcon(this.fullIcon); } + itemMetaData.setAlarmStore(npcComponent.getAlarmStore()); ItemStack itemWithNPC = inHandItemStack.withMetadata(CapturedNPCMetadata.KEYED_CODEC, itemMetaData); - inventory.getHotbar().replaceItemStackInSlot(activeHotbarSlot, item, itemWithNPC); + hotbarComponent.getInventory().replaceItemStackInSlot(activeHotbarSlot, item, itemWithNPC); commandBuffer.removeEntity(targetEntity, RemoveReason.REMOVE); } } @@ -178,9 +180,11 @@ public class UseCaptureCrateInteraction extends SimpleBlockInteraction { context.getState().state = InteractionState.Failed; } else { Ref ref = context.getEntity(); - if (EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity) { - Inventory inventory = livingEntity.getInventory(); - byte activeHotbarSlot = inventory.getActiveHotbarSlot(); + InventoryComponent.Hotbar hotbarComponent = commandBuffer.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent == null) { + context.getState().state = InteractionState.Failed; + } else { + byte activeHotbarSlot = hotbarComponent.getActiveSlot(); CapturedNPCMetadata existingMeta = item.getFromMetadataOrNull("CapturedEntity", CapturedNPCMetadata.CODEC); if (existingMeta == null) { context.getState().state = InteractionState.Failed; @@ -208,10 +212,10 @@ public class UseCaptureCrateInteraction extends SimpleBlockInteraction { if (coopBlockComponent.tryPutResident(existingMeta, worldTimeResource)) { world.execute( () -> coopBlockComponent.ensureSpawnResidentsInWorld( - world, world.getEntityStore().getStore(), new Vector3d(pos.x, pos.y, pos.z), new Vector3d().assign(Vector3d.FORWARD) + world, world.getEntityStore().getStore(), new Vector3d(pos.x, pos.y, pos.z), new Vector3d().set(Vector3dUtil.FORWARD) ) ); - inventory.getHotbar().replaceItemStackInSlot(activeHotbarSlot, item, noMetaItemStack); + hotbarComponent.getInventory().replaceItemStackInSlot(activeHotbarSlot, item, noMetaItemStack); context.getState().state = InteractionState.Finished; } else { context.getState().state = InteractionState.Failed; @@ -225,19 +229,23 @@ public class UseCaptureCrateInteraction extends SimpleBlockInteraction { if (context.getClientState() != null) { BlockFace blockFace = BlockFace.fromProtocolFace(context.getClientState().blockFace); if (blockFace != null) { - spawnPos.add(blockFace.getDirection()); + Vector3ic direction = blockFace.getDirection(); + spawnPos.add(direction.x(), direction.y(), direction.z()); } } String roleId = existingMeta.getNpcNameKey(); int roleIndex = NPCPlugin.get().getIndex(roleId); - commandBuffer.run(_store -> NPCPlugin.get().spawnEntity(_store, roleIndex, spawnPos, Vector3f.ZERO, null, null)); - inventory.getHotbar().replaceItemStackInSlot(activeHotbarSlot, item, noMetaItemStack); + AlarmStore savedAlarmStore = existingMeta.getAlarmStore(); + commandBuffer.run(_store -> NPCPlugin.get().spawnEntity(_store, roleIndex, spawnPos, Rotation3f.IDENTITY, null, (npc, _holder, _s) -> { + if (savedAlarmStore != null) { + npc.setAlarmStore(savedAlarmStore); + } + }, null)); + hotbarComponent.getInventory().replaceItemStackInSlot(activeHotbarSlot, item, noMetaItemStack); } } } - } else { - context.getState().state = InteractionState.Failed; } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseCoopInteraction.java b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseCoopInteraction.java index 5b9e0b2d..f058d9b5 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseCoopInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseCoopInteraction.java @@ -6,13 +6,11 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.modules.block.BlockModule; @@ -24,6 +22,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class UseCoopInteraction extends SimpleBlockInteraction { @Nonnull @@ -42,14 +41,14 @@ public class UseCoopInteraction extends SimpleBlockInteraction { @Nonnull Vector3i targetBlock, @Nonnull CooldownHandler cooldownHandler ) { - int x = targetBlock.getX(); - int z = targetBlock.getZ(); + int x = targetBlock.x(); + int z = targetBlock.z(); long chunkIndex = ChunkUtil.indexChunkFromBlock(x, z); WorldChunk worldChunk = world.getChunk(chunkIndex); if (worldChunk == null) { context.getState().state = InteractionState.Failed; } else { - Ref blockRef = worldChunk.getBlockComponentEntity(x, targetBlock.getY(), z); + Ref blockRef = worldChunk.getBlockComponentEntity(x, targetBlock.y(), z); if (blockRef == null || !blockRef.isValid()) { blockRef = BlockModule.ensureBlockEntity(worldChunk, targetBlock.x, targetBlock.y, targetBlock.z); } @@ -61,19 +60,13 @@ public class UseCoopInteraction extends SimpleBlockInteraction { context.getState().state = InteractionState.Failed; } else { Ref ref = context.getEntity(); - if (EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity) { - CombinedItemContainer inventoryContainer = livingEntity.getInventory().getCombinedHotbarFirst(); - if (inventoryContainer != null) { - coopBlockComponent.gatherProduceFromContainer(inventoryContainer); - BlockType currentBlockType = worldChunk.getBlockType(targetBlock); + CombinedItemContainer inventoryContainer = InventoryComponent.getCombined(commandBuffer, ref, InventoryComponent.HOTBAR_FIRST); + coopBlockComponent.gatherProduceFromContainer(inventoryContainer); + BlockType currentBlockType = worldChunk.getBlockType(targetBlock); - assert currentBlockType != null; + assert currentBlockType != null; - worldChunk.setBlockInteractionState(targetBlock, currentBlockType, coopBlockComponent.hasProduce() ? "Produce_Ready" : "default"); - } - } else { - context.getState().state = InteractionState.Failed; - } + worldChunk.setBlockInteractionState(targetBlock, currentBlockType, coopBlockComponent.hasProduce() ? "Produce_Ready" : "default"); } } else { context.getState().state = InteractionState.Failed; diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseWateringCanInteraction.java b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseWateringCanInteraction.java index 327c1b26..ae2686aa 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseWateringCanInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/interactions/UseWateringCanInteraction.java @@ -1,6 +1,5 @@ package com.hypixel.hytale.builtin.adventure.farming.interactions; -import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock; import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -9,13 +8,12 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; -import com.hypixel.hytale.protocol.WaitForDataFrom; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.block.BlockModule; +import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; @@ -27,28 +25,27 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class UseWateringCanInteraction extends SimpleBlockInteraction { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( UseWateringCanInteraction.class, UseWateringCanInteraction::new, SimpleBlockInteraction.CODEC ) - .documentation("Waters the target farmable block.") + .documentation("Waters the target farmable block. Supports configurable width and depth for directional area-of-effect watering.") .addField(new KeyedCodec<>("Duration", Codec.LONG), (interaction, duration) -> interaction.duration = duration, interaction -> interaction.duration) .addField( new KeyedCodec<>("RefreshModifiers", Codec.STRING_ARRAY), (interaction, refreshModifiers) -> interaction.refreshModifiers = refreshModifiers, interaction -> interaction.refreshModifiers ) + .addField(new KeyedCodec<>("RadiusX", Codec.INTEGER), (interaction, radiusX) -> interaction.radiusX = radiusX, interaction -> interaction.radiusX) + .addField(new KeyedCodec<>("RadiusZ", Codec.INTEGER), (interaction, radiusZ) -> interaction.radiusZ = radiusZ, interaction -> interaction.radiusZ) .build(); protected long duration; protected String[] refreshModifiers; - - @Nonnull - @Override - public WaitForDataFrom getWaitForDataFrom() { - return WaitForDataFrom.Server; - } + protected int radiusX; + protected int radiusZ; @Override protected void interactWithBlock( @@ -60,54 +57,82 @@ public class UseWateringCanInteraction extends SimpleBlockInteraction { @Nonnull Vector3i targetBlock, @Nonnull CooldownHandler cooldownHandler ) { - int x = targetBlock.getX(); - int z = targetBlock.getZ(); + WorldTimeResource worldTimeResource = commandBuffer.getResource(WorldTimeResource.getResourceType()); + Instant gameTime = worldTimeResource.getGameTime(); + Instant wateredUntil = gameTime.plus(this.duration, ChronoUnit.SECONDS); + int facingX = 0; + int facingZ = -1; + HeadRotation headRotation = commandBuffer.getComponent(context.getEntity(), HeadRotation.getComponentType()); + if (headRotation != null) { + Vector3i facing = headRotation.getHorizontalAxisDirection(); + facingX = facing.x(); + facingZ = facing.z(); + } + + if (facingX != 0 && facingZ != 0) { + facingX = 0; + } + + int lateralX = facingZ != 0 ? 1 : 0; + int lateralZ = facingX != 0 ? 1 : 0; + int width = Math.max(this.radiusX, 1); + int depth = Math.max(this.radiusZ, 1); + int halfLeft = (width - 1) / 2; + int halfRight = width - 1 - halfLeft; + boolean anyWatered = false; + + for (int forward = 0; forward < depth; forward++) { + for (int lateral = -halfLeft; lateral <= halfRight; lateral++) { + int bx = targetBlock.x() + lateral * lateralX + forward * facingX; + int bz = targetBlock.z() + lateral * lateralZ + forward * facingZ; + if (this.waterBlockAt(world, bx, targetBlock.y(), bz, wateredUntil)) { + anyWatered = true; + } + } + } + + if (!anyWatered) { + context.getState().state = InteractionState.Failed; + } + } + + private boolean waterBlockAt(@Nonnull World world, int x, int y, int z, @Nonnull Instant wateredUntil) { long chunkIndex = ChunkUtil.indexChunkFromBlock(x, z); WorldChunk worldChunk = world.getChunk(chunkIndex); if (worldChunk == null) { - context.getState().state = InteractionState.Failed; + return false; } else { - Ref blockRef = worldChunk.getBlockComponentEntity(x, targetBlock.getY(), z); + Store chunkStore = world.getChunkStore().getStore(); + Ref blockRef = worldChunk.getBlockComponentEntity(x, y, z); if (blockRef == null) { - blockRef = BlockModule.ensureBlockEntity(worldChunk, targetBlock.x, targetBlock.y, targetBlock.z); + blockRef = BlockModule.ensureBlockEntity(worldChunk, x, y, z); } if (blockRef != null && blockRef.isValid()) { - Store chunkStore = world.getChunkStore().getStore(); - WorldTimeResource worldTimeResource = commandBuffer.getResource(WorldTimeResource.getResourceType()); TilledSoilBlock tilledSoilBlockComponent = chunkStore.getComponent(blockRef, TilledSoilBlock.getComponentType()); if (tilledSoilBlockComponent != null) { - Instant wateredUntil = worldTimeResource.getGameTime().plus(this.duration, ChronoUnit.SECONDS); tilledSoilBlockComponent.setWateredUntil(wateredUntil); - worldChunk.setTicking(x, targetBlock.getY(), z, true); - worldChunk.getBlockChunk().getSectionAtBlockY(targetBlock.y).scheduleTick(ChunkUtil.indexBlock(x, targetBlock.y, z), wateredUntil); - worldChunk.setTicking(x, targetBlock.getY() + 1, z, true); + worldChunk.setTicking(x, y, z, true); + worldChunk.getBlockChunk().getSectionAtBlockY(y).scheduleTick(ChunkUtil.indexBlock(x, y, z), wateredUntil); + worldChunk.setTicking(x, y + 1, z, true); + return true; + } + } + + Ref soilBlockRef = worldChunk.getBlockComponentEntity(x, y - 1, z); + if (soilBlockRef != null && soilBlockRef.isValid()) { + TilledSoilBlock tilledSoilBlockComponent = chunkStore.getComponent(soilBlockRef, TilledSoilBlock.getComponentType()); + if (tilledSoilBlockComponent == null) { + return false; } else { - FarmingBlock farmingBlockComponent = chunkStore.getComponent(blockRef, FarmingBlock.getComponentType()); - if (farmingBlockComponent == null) { - context.getState().state = InteractionState.Failed; - } else { - Ref soilBlockRef = worldChunk.getBlockComponentEntity(x, targetBlock.getY() - 1, z); - if (soilBlockRef != null && soilBlockRef.isValid()) { - tilledSoilBlockComponent = chunkStore.getComponent(soilBlockRef, TilledSoilBlock.getComponentType()); - if (tilledSoilBlockComponent == null) { - context.getState().state = InteractionState.Failed; - } else { - Instant wateredUntil = worldTimeResource.getGameTime().plus(this.duration, ChronoUnit.SECONDS); - tilledSoilBlockComponent.setWateredUntil(wateredUntil); - worldChunk.getBlockChunk() - .getSectionAtBlockY(targetBlock.y - 1) - .scheduleTick(ChunkUtil.indexBlock(x, targetBlock.y - 1, z), wateredUntil); - worldChunk.setTicking(x, targetBlock.getY() - 1, z, true); - worldChunk.setTicking(x, targetBlock.getY(), z, true); - } - } else { - context.getState().state = InteractionState.Failed; - } - } + tilledSoilBlockComponent.setWateredUntil(wateredUntil); + worldChunk.getBlockChunk().getSectionAtBlockY(y - 1).scheduleTick(ChunkUtil.indexBlock(x, y - 1, z), wateredUntil); + worldChunk.setTicking(x, y - 1, z, true); + worldChunk.setTicking(x, y, z, true); + return true; } } else { - context.getState().state = InteractionState.Failed; + return false; } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/states/CoopBlock.java b/src/com/hypixel/hytale/builtin/adventure/farming/states/CoopBlock.java index 1697573a..01d338ed 100644 --- a/src/com/hypixel/hytale/builtin/adventure/farming/states/CoopBlock.java +++ b/src/com/hypixel/hytale/builtin/adventure/farming/states/CoopBlock.java @@ -19,9 +19,9 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.range.IntRange; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.asset.type.item.config.ItemDrop; import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList; import com.hypixel.hytale.server.core.entity.UUIDComponent; @@ -58,6 +58,8 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class CoopBlock implements Component { @Nonnull @@ -254,21 +256,21 @@ public class CoopBlock implements Component { boolean residentDeployed = resident.getDeployedToWorld(); PersistentRef residentEntityId = resident.getPersistentRef(); if (!residentDeployed && residentEntityId == null) { - Vector3d residentSpawnLocation = new Vector3d().assign(coopLocation).add(spawnOffsetIteration); + Vector3d residentSpawnLocation = new Vector3d().set(coopLocation).add(spawnOffsetIteration); Builder roleBuilder = NPCPlugin.get().tryGetCachedValidRole(npcRoleIndex); if (roleBuilder != null) { spawningContext.setSpawnable((ISpawnableWithModel)roleBuilder); if (spawningContext.set(world, residentSpawnLocation.x, residentSpawnLocation.y, residentSpawnLocation.z) && spawningContext.canSpawn() == SpawnTestResult.TEST_OK) { Pair, NPCEntity> npcPair = NPCPlugin.get() - .spawnEntity(store, npcRoleIndex, spawningContext.newPosition(), Vector3f.ZERO, null, null); + .spawnEntity(store, npcRoleIndex, spawningContext.newPosition(), Rotation3f.IDENTITY, null, null); if (npcPair == null) { resident.setPersistentRef(null); resident.setDeployedToWorld(false); } else { Ref npcRef = npcPair.first(); NPCEntity npcComponent = npcPair.second(); - npcComponent.getLeashPoint().assign(coopLocation); + npcComponent.getLeashPoint().set(coopLocation); if (npcRef != null && npcRef.isValid()) { UUIDComponent uuidComponent = store.getComponent(npcRef, UUIDComponent.getComponentType()); if (uuidComponent == null) { @@ -276,7 +278,7 @@ public class CoopBlock implements Component { resident.setDeployedToWorld(false); } else { CoopResidentComponent coopResidentComponent = new CoopResidentComponent(); - coopResidentComponent.setCoopLocation(coopLocation.toVector3i()); + coopResidentComponent.setCoopLocation(Vector3dUtil.toVector3i(coopLocation)); store.addComponent(npcRef, CoopResidentComponent.getComponentType(), coopResidentComponent); PersistentRef persistentRef = new PersistentRef(); persistentRef.setEntity(npcRef, uuidComponent.getUuid()); @@ -319,6 +321,17 @@ public class CoopBlock implements Component { } else if (!this.getCoopAcceptsNPC(resident.metadata.getNpcNameKey())) { residentsToRemove.add(resident); } else { + ComponentType npcComponentType = NPCEntity.getComponentType(); + if (npcComponentType != null && resident.persistentRef != null) { + NPCEntity npcComponent = store.getComponent(entityRef, npcComponentType); + if (npcComponent != null && !resident.getMetadata().getNpcNameKey().equals(npcComponent.getRoleName())) { + CapturedNPCMetadata metadata = FarmingUtil.generateCapturedNPCMetadata(store, entityRef, npcComponent.getRoleName()); + if (metadata != null) { + resident.metadata = metadata; + } + } + } + coopResidentComponent.setMarkedForDespawn(true); resident.setPersistentRef(null); resident.setDeployedToWorld(false); @@ -398,10 +411,12 @@ public class CoopBlock implements Component { @Nonnull World world, @Nonnull WorldTimeResource worldTimeResource, @Nonnull Store store, int blockX, int blockY, int blockZ ) { Vector3i location = new Vector3i(blockX, blockY, blockZ); - world.execute(() -> this.ensureSpawnResidentsInWorld(world, store, location.toVector3d(), new Vector3d().assign(Vector3d.FORWARD))); + world.execute(() -> this.ensureSpawnResidentsInWorld(world, store, Vector3iUtil.toVector3d(location), new Vector3d().set(Vector3dUtil.FORWARD))); this.generateProduceToInventory(worldTimeResource); Vector3d dropPosition = new Vector3d(blockX + 0.5F, blockY, blockZ + 0.5F); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(store, this.itemContainer.removeAllItemStacks(), dropPosition, Vector3f.ZERO); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops( + store, this.itemContainer.removeAllItemStacks(), dropPosition, Rotation3f.IDENTITY + ); if (itemEntityHolders.length > 0) { world.execute(() -> store.addEntities(itemEntityHolders, AddReason.SPAWN)); } diff --git a/src/com/hypixel/hytale/builtin/adventure/farming/states/FarmingBlockState.java b/src/com/hypixel/hytale/builtin/adventure/farming/states/FarmingBlockState.java deleted file mode 100644 index 803338b4..00000000 --- a/src/com/hypixel/hytale/builtin/adventure/farming/states/FarmingBlockState.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.hypixel.hytale.builtin.adventure.farming.states; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.component.Component; -import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; -import java.time.Instant; -import java.util.Arrays; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -@Deprecated(forRemoval = true) -public class FarmingBlockState implements Component { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(FarmingBlockState.class, FarmingBlockState::new) - .append(new KeyedCodec<>("BaseCrop", Codec.STRING), (state, crop) -> state.baseCrop = crop, state -> state.baseCrop) - .add() - .append(new KeyedCodec<>("StageStart", Codec.INSTANT), (state, start) -> state.stageStart = start, state -> state.stageStart) - .add() - .append( - new KeyedCodec<>("CurrentFarmingStageIndex", Codec.INTEGER), - (baseFarmingBlockState, integer) -> baseFarmingBlockState.currentFarmingStageIndex = integer, - baseFarmingBlockState -> baseFarmingBlockState.currentFarmingStageIndex - ) - .add() - .append( - new KeyedCodec<>("CurrentFarmingStageSetName", Codec.STRING), - (farmingBlockState, s) -> farmingBlockState.currentFarmingStageSetName = s, - farmingBlockState -> farmingBlockState.currentFarmingStageSetName - ) - .add() - .append(new KeyedCodec<>("SpreadRate", Codec.FLOAT), (blockState, aFloat) -> blockState.spreadRate = aFloat, blockState -> blockState.spreadRate) - .add() - .build(); - public boolean loaded; - public String baseCrop; - public Instant stageStart; - public String currentFarmingStageSetName; - public int currentFarmingStageIndex; - public Instant[] stageCompletionTimes; - public String stageSetAfterHarvest; - public double lastGrowthMultiplier; - public float spreadRate = 1.0F; - - public String getCurrentFarmingStageSetName() { - return this.currentFarmingStageSetName; - } - - public void setCurrentFarmingStageSetName(String currentFarmingStageSetName) { - this.currentFarmingStageSetName = currentFarmingStageSetName; - } - - public int getCurrentFarmingStageIndex() { - return this.currentFarmingStageIndex; - } - - public void setCurrentFarmingStageIndex(int currentFarmingStageIndex) { - this.currentFarmingStageIndex = currentFarmingStageIndex; - } - - public String getStageSetAfterHarvest() { - return this.stageSetAfterHarvest; - } - - public void setStageSetAfterHarvest(String stageSetAfterHarvest) { - this.stageSetAfterHarvest = stageSetAfterHarvest; - } - - public float getSpreadRate() { - return this.spreadRate; - } - - public void setSpreadRate(float spreadRate) { - this.spreadRate = spreadRate; - } - - @Nonnull - @Override - public String toString() { - return "FarmingBlockState{loaded=" - + this.loaded - + ", baseCrop=" - + this.baseCrop - + ", stageStart=" - + this.stageStart - + ", currentFarmingStageSetName='" - + this.currentFarmingStageSetName - + "', currentFarmingStageIndex=" - + this.currentFarmingStageIndex - + ", stageCompletionTimes=" - + Arrays.toString((Object[])this.stageCompletionTimes) - + ", stageSetAfterHarvest='" - + this.stageSetAfterHarvest - + "', lastGrowthMultiplier=" - + this.lastGrowthMultiplier - + "} " - + super.toString(); - } - - @Nullable - @Override - public Component clone() { - return this; - } - - protected static class RefreshFlags { - protected static final int REFRESH_ALL_FLAG = 1; - protected static final int UNLOADING_FLAG = 2; - protected static final int RETROACTIVE_FLAG = 4; - protected static final int DEFAULT = 1; - protected static final int ON_UNLOADING = 3; - protected static final int ON_LOADING = 5; - protected static final int NONE = 0; - } -} diff --git a/src/com/hypixel/hytale/builtin/adventure/memories/MemoriesGameplayConfig.java b/src/com/hypixel/hytale/builtin/adventure/memories/MemoriesGameplayConfig.java index 9e3bf1bc..3e5ad3c3 100644 --- a/src/com/hypixel/hytale/builtin/adventure/memories/MemoriesGameplayConfig.java +++ b/src/com/hypixel/hytale/builtin/adventure/memories/MemoriesGameplayConfig.java @@ -7,6 +7,7 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle; +import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -56,12 +57,32 @@ public class MemoriesGameplayConfig { ) .addValidator(Validators.greaterThan(16)) .add() + .appendInherited( + new KeyedCodec<>("MemoriesRestoreSoundEventId", Codec.STRING), + (activationEffects, s) -> activationEffects.memoriesRestoreSoundEventId = s, + activationEffects -> activationEffects.memoriesRestoreSoundEventId, + (activationEffects, parent) -> activationEffects.memoriesRestoreSoundEventId = parent.memoriesRestoreSoundEventId + ) + .addValidator(Validators.nonNull()) + .addValidator(SoundEvent.VALIDATOR_CACHE.getValidator()) + .add() + .appendInherited( + new KeyedCodec<>("MemoriesCatchSoundEventId", Codec.STRING), + (memoriesGameplayConfig, s) -> memoriesGameplayConfig.memoriesCatchSoundEventId = s, + memoriesGameplayConfig -> memoriesGameplayConfig.memoriesCatchSoundEventId, + (memoriesGameplayConfig, parent) -> memoriesGameplayConfig.memoriesCatchSoundEventId = parent.memoriesCatchSoundEventId + ) + .addValidator(Validators.nonNull()) + .addValidator(SoundEvent.VALIDATOR_CACHE.getValidator()) + .add() .build(); private int[] memoriesAmountPerLevel; private String memoriesRecordParticles; private String memoriesCatchItemId; private ModelParticle memoriesCatchEntityParticle; private int memoriesCatchParticleViewDistance = 64; + private String memoriesRestoreSoundEventId; + private String memoriesCatchSoundEventId; @Nullable public static MemoriesGameplayConfig get(@Nonnull GameplayConfig config) { @@ -80,6 +101,14 @@ public class MemoriesGameplayConfig { return this.memoriesCatchItemId; } + public String getMemoriesRestoreSoundEventId() { + return this.memoriesRestoreSoundEventId; + } + + public String getMemoriesCatchSoundEventId() { + return this.memoriesCatchSoundEventId; + } + public ModelParticle getMemoriesCatchEntityParticle() { return this.memoriesCatchEntityParticle; } diff --git a/src/com/hypixel/hytale/builtin/adventure/memories/MemoriesPlugin.java b/src/com/hypixel/hytale/builtin/adventure/memories/MemoriesPlugin.java index 4ff8eecd..b171fdf3 100644 --- a/src/com/hypixel/hytale/builtin/adventure/memories/MemoriesPlugin.java +++ b/src/com/hypixel/hytale/builtin/adventure/memories/MemoriesPlugin.java @@ -47,6 +47,7 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.config.ser import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.BsonUtil; import com.hypixel.hytale.server.core.util.Config; @@ -56,7 +57,6 @@ import it.unimi.dsi.fastutil.objects.Object2DoubleMaps; import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; @@ -65,9 +65,12 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.bson.BsonDocument; public class MemoriesPlugin extends JavaPlugin { @Nonnull @@ -128,15 +131,14 @@ public class MemoriesPlugin extends JavaPlugin { @Override protected void start() { - try { - Path path = Constants.UNIVERSE_PATH.resolve("memories.json"); - if (Files.exists(path)) { - this.recordedMemories = RawJsonReader.readSync(path, MemoriesPlugin.RecordedMemories.CODEC, this.getLogger()); - } else { + Path path = Constants.UNIVERSE_PATH.resolve("memories.json"); + if (Files.exists(path)) { + this.recordedMemories = RawJsonReader.readSyncWithBak(path, MemoriesPlugin.RecordedMemories.CODEC, this.getLogger()); + if (this.recordedMemories == null) { this.recordedMemories = new MemoriesPlugin.RecordedMemories(); } - } catch (IOException var2) { - throw new RuntimeException(var2); + } else { + this.recordedMemories = new MemoriesPlugin.RecordedMemories(); } this.hasInitializedMemories = true; @@ -146,16 +148,29 @@ public class MemoriesPlugin extends JavaPlugin { @Override protected void shutdown() { if (this.hasInitializedMemories) { + this.saveMemories().join(); + } + } + + @Nonnull + private CompletableFuture saveMemories() { + Path path = Constants.UNIVERSE_PATH.resolve("memories.json"); + return Universe.get().getStorageManager().doSave(path, () -> { this.recordedMemories.lock.readLock().lock(); + CompletableFuture var3; try { - BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger()); - } catch (IOException var5) { - throw new RuntimeException(var5); + BsonDocument document = MemoriesPlugin.RecordedMemories.CODEC.encode(this.recordedMemories).asDocument(); + var3 = BsonUtil.writeDocument(path, document); } finally { this.recordedMemories.lock.readLock().unlock(); } - } + + return var3; + }).exceptionally(e -> { + this.getLogger().at(Level.SEVERE).withCause(e).log("Failed to save memories"); + return null; + }); } private void onAssetsLoad() { @@ -224,11 +239,9 @@ public class MemoriesPlugin extends JavaPlugin { try { if (playerMemories.takeMemories(this.recordedMemories.memories)) { - BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger()); + this.saveMemories(); return true; } - } catch (IOException var6) { - throw new RuntimeException(var6); } finally { this.recordedMemories.lock.writeLock().unlock(); } @@ -255,9 +268,7 @@ public class MemoriesPlugin extends JavaPlugin { try { this.recordedMemories.memories.clear(); - BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger()); - } catch (IOException var5) { - throw new RuntimeException(var5); + this.saveMemories(); } finally { this.recordedMemories.lock.writeLock().unlock(); } @@ -271,9 +282,7 @@ public class MemoriesPlugin extends JavaPlugin { this.recordedMemories.memories.addAll(entry.getValue()); } - BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger()); - } catch (IOException var6) { - throw new RuntimeException(var6); + this.saveMemories(); } finally { this.recordedMemories.lock.writeLock().unlock(); } @@ -286,7 +295,7 @@ public class MemoriesPlugin extends JavaPlugin { this.recordedMemories.lock.writeLock().lock(); - int var12; + int var10; try { this.recordedMemories.memories.clear(); List allAvailableMemories = new ObjectArrayList<>(); @@ -301,15 +310,13 @@ public class MemoriesPlugin extends JavaPlugin { this.recordedMemories.memories.add(allAvailableMemories.get(i)); } - BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger()); - var12 = actualCount; - } catch (IOException var8) { - throw new RuntimeException(var8); + this.saveMemories(); + var10 = actualCount; } finally { this.recordedMemories.lock.writeLock().unlock(); } - return var12; + return var10; } public static class MemoriesPluginConfig { diff --git a/src/com/hypixel/hytale/builtin/adventure/memories/memories/npc/NPCMemory.java b/src/com/hypixel/hytale/builtin/adventure/memories/memories/npc/NPCMemory.java index 821be62d..8ee29394 100644 --- a/src/com/hypixel/hytale/builtin/adventure/memories/memories/npc/NPCMemory.java +++ b/src/com/hypixel/hytale/builtin/adventure/memories/memories/npc/NPCMemory.java @@ -22,11 +22,12 @@ import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialStructure; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.GameMode; +import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.packets.entities.SpawnModelParticles; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle; +import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.entity.EntityModule; @@ -36,6 +37,7 @@ import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; import com.hypixel.hytale.server.core.modules.entity.item.PickupItemComponent; import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.NotificationUtil; @@ -44,10 +46,11 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.worldgen.chunk.ChunkGenerator; import com.hypixel.hytale.server.worldgen.chunk.ZoneBiomeResult; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class NPCMemory extends Memory { @Nonnull @@ -222,7 +225,7 @@ public class NPCMemory extends Memory { Vector3d position = transformComponent.getPosition(); SpatialResource, EntityStore> npcSpatialResource = store.getResource(NPCPlugin.get().getNpcSpatialResource()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); npcSpatialResource.getSpatialStructure().collect(position, this.radius, results); if (!results.isEmpty()) { PlayerRef playerRefComponent = archetypeChunk.getComponent(index, this.playerRefComponentType); @@ -262,7 +265,7 @@ public class NPCMemory extends Memory { MemoriesGameplayConfig memoriesGameplayConfig = MemoriesGameplayConfig.get(store.getExternalData().getWorld().getGameplayConfig()); if (memoriesGameplayConfig != null) { ItemStack memoryItemStack = new ItemStack(memoriesGameplayConfig.getMemoriesCatchItemId()); - Vector3d memoryItemHolderPosition = npcTransformComponent.getPosition().clone(); + Vector3d memoryItemHolderPosition = new Vector3d(npcTransformComponent.getPosition()); BoundingBox boundingBoxComponent = commandBuffer.getComponent(npcRef, BoundingBox.getComponentType()); if (boundingBoxComponent != null) { memoryItemHolderPosition.y = memoryItemHolderPosition.y + boundingBoxComponent.getBoundingBox().middleY(); @@ -279,6 +282,13 @@ public class NPCMemory extends Memory { pickupItemComponent.setInitialLifeTime(0.62F); commandBuffer.addEntity(memoryItemHolder, AddReason.SPAWN); displayCatchEntityParticles(memoriesGameplayConfig, memoryItemHolderPosition, npcRef, commandBuffer); + String memoriesCatchSoundEventId = memoriesGameplayConfig.getMemoriesCatchSoundEventId(); + if (memoriesCatchSoundEventId != null) { + int soundEventIndex = SoundEvent.getAssetMap().getIndex(memoriesCatchSoundEventId); + if (soundEventIndex != 0) { + SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, npcTransformComponent.getPosition(), commandBuffer); + } + } } } } @@ -321,7 +331,7 @@ public class NPCMemory extends Memory { SpawnModelParticles packet = new SpawnModelParticles(networkIdComponent.getId(), modelParticlesProtocol); SpatialResource, EntityStore> spatialResource = commandBuffer.getResource(EntityModule.get().getPlayerSpatialResourceType()); SpatialStructure> spatialStructure = spatialResource.getSpatialStructure(); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialStructure.ordered(targetPosition, memoriesGameplayConfig.getMemoriesCatchParticleViewDistance(), results); for (Ref ref : results) { diff --git a/src/com/hypixel/hytale/builtin/adventure/memories/page/MemoriesPage.java b/src/com/hypixel/hytale/builtin/adventure/memories/page/MemoriesPage.java index 9e4b057a..972b2d7c 100644 --- a/src/com/hypixel/hytale/builtin/adventure/memories/page/MemoriesPage.java +++ b/src/com/hypixel/hytale/builtin/adventure/memories/page/MemoriesPage.java @@ -12,12 +12,13 @@ import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.BlockPosition; +import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; +import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage; import com.hypixel.hytale.server.core.modules.entity.EntityModule; @@ -29,18 +30,20 @@ import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder; import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.ParticleUtil; +import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.time.Instant; import java.time.ZoneOffset; import java.util.Comparator; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class MemoriesPage extends InteractiveCustomUIPage { @Nullable @@ -251,9 +254,16 @@ public class MemoriesPage extends InteractiveCustomUIPage, EntityStore> playerSpatialResource = store.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(this.recordMemoriesParticlesPosition, 75.0, results); ParticleUtil.spawnParticleEffect(memoriesGameplayConfig.getMemoriesRecordParticles(), this.recordMemoriesParticlesPosition, results, store); + String restoreSoundEvent = memoriesGameplayConfig.getMemoriesRestoreSoundEventId(); + if (restoreSoundEvent != null) { + int soundEventId = SoundEvent.getAssetMap().getIndex(restoreSoundEvent); + if (soundEventId != 0) { + SoundUtil.playSoundEvent3d(soundEventId, SoundCategory.SFX, this.recordMemoriesParticlesPosition, store); + } + } } this.close(); diff --git a/src/com/hypixel/hytale/builtin/adventure/memories/temple/TempleRespawnPlayersSystem.java b/src/com/hypixel/hytale/builtin/adventure/memories/temple/TempleRespawnPlayersSystem.java index 4ab9fc19..a0dd915f 100644 --- a/src/com/hypixel/hytale/builtin/adventure/memories/temple/TempleRespawnPlayersSystem.java +++ b/src/com/hypixel/hytale/builtin/adventure/memories/temple/TempleRespawnPlayersSystem.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.DelayedEntitySystem; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TempleRespawnPlayersSystem extends DelayedEntitySystem { @Nonnull @@ -54,7 +54,7 @@ public class TempleRespawnPlayersSystem extends DelayedEntitySystem assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - if (!(position.getY() > config.getMinYRespawn())) { + if (!(position.y() > config.getMinYRespawn())) { Ref ref = archetypeChunk.getReferenceTo(index); ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider(); Transform spawnTransform = spawnProvider.getSpawnPoint(ref, commandBuffer); diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/BountyObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/BountyObjectiveTaskAsset.java index c8c37a6e..45b21184 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/BountyObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/BountyObjectiveTaskAsset.java @@ -7,9 +7,9 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.Objects; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BountyObjectiveTaskAsset extends ObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillObjectiveTaskAsset.java index 3499421b..74fcc0d4 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillObjectiveTaskAsset.java @@ -8,8 +8,8 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class KillObjectiveTaskAsset extends CountObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillSpawnBeaconObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillSpawnBeaconObjectiveTaskAsset.java index ec2acc7d..04aeaa25 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillSpawnBeaconObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillSpawnBeaconObjectiveTaskAsset.java @@ -8,12 +8,13 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.spawning.assets.spawns.config.BeaconNPCSpawn; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class KillSpawnBeaconObjectiveTaskAsset extends KillObjectiveTaskAsset { @Nonnull @@ -83,7 +84,7 @@ public class KillSpawnBeaconObjectiveTaskAsset extends KillObjectiveTaskAsset { .addValidator(BeaconNPCSpawn.VALIDATOR_CACHE.getValidator()) .add() .append( - new KeyedCodec<>("Offset", Vector3d.CODEC), + new KeyedCodec<>("Offset", Vector3dUtil.CODEC), (objectiveSpawnBeacon, vector3d) -> objectiveSpawnBeacon.offset = vector3d, objectiveSpawnBeacon -> objectiveSpawnBeacon.offset ) diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillSpawnMarkerObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillSpawnMarkerObjectiveTaskAsset.java index 16e9a073..6d59c468 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillSpawnMarkerObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/assets/KillSpawnMarkerObjectiveTaskAsset.java @@ -8,10 +8,10 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.LegacyValidator; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.codec.validation.validator.ArrayValidator; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker; import java.util.Arrays; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class KillSpawnMarkerObjectiveTaskAsset extends KillObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/npc/ActionCompleteTask.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/npc/ActionCompleteTask.java index cac6d0b3..b9c8d61e 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/npc/ActionCompleteTask.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/npc/ActionCompleteTask.java @@ -14,6 +14,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import java.util.List; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionCompleteTask extends ActionPlayAnimation { protected final boolean playAnimation; @@ -24,14 +25,14 @@ public class ActionCompleteTask extends ActionPlayAnimation { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { Ref targetRef = role.getStateSupport().getInteractionIterationTarget(); boolean targetExists = targetRef != null && targetRef.isValid() && !store.getArchetype(targetRef).contains(DeathComponent.getComponentType()); return super.canExecute(ref, role, sensorInfo, dt, store) && targetExists; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { UUIDComponent parentUuidComponent = store.getComponent(ref, UUIDComponent.getComponentType()); if (parentUuidComponent == null) { return false; diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/npc/ActionStartObjective.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/npc/ActionStartObjective.java index 0559b5ab..e7e0bddf 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/npc/ActionStartObjective.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/npc/ActionStartObjective.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.npc.corecomponents.ActionBase; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionStartObjective extends ActionBase { protected final String objectiveId; @@ -20,12 +21,12 @@ public class ActionStartObjective extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && role.getStateSupport().getInteractionIterationTarget() != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); Ref interactionIterationTarget = role.getStateSupport().getInteractionIterationTarget(); if (interactionIterationTarget == null) { diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/BountyObjectiveTask.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/BountyObjectiveTask.java index 99c875c3..234f7cd1 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/BountyObjectiveTask.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/BountyObjectiveTask.java @@ -17,10 +17,10 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.modules.entity.damage.Damage; @@ -34,6 +34,8 @@ import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class BountyObjectiveTask extends ObjectiveTask implements KillTask { @Nonnull @@ -80,13 +82,16 @@ public class BountyObjectiveTask extends ObjectiveTask implements KillTask { if (objectivePosition == null) { return null; } else { - Vector3i spawnPosition = this.getAsset().getWorldLocationProvider().runCondition(world, objectivePosition.clone().floor().toVector3i()); + Vector3i spawnPosition = this.getAsset() + .getWorldLocationProvider() + .runCondition(world, Vector3dUtil.toVector3i(new Vector3d(objectivePosition).floor())); if (spawnPosition == null) { return null; } else { TransactionRecord[] transactionRecords = new TransactionRecord[2]; String npcId = this.getAsset().getNpcId(); - Pair, INonPlayerCharacter> npcPair = NPCPlugin.get().spawnNPC(store, npcId, null, spawnPosition.toVector3d(), Vector3f.ZERO); + Pair, INonPlayerCharacter> npcPair = NPCPlugin.get() + .spawnNPC(store, npcId, null, Vector3iUtil.toVector3d(spawnPosition), Rotation3f.IDENTITY); if (npcPair == null) { return null; } else { diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/KillSpawnBeaconObjectiveTask.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/KillSpawnBeaconObjectiveTask.java index 702bde19..cb5f30f7 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/KillSpawnBeaconObjectiveTask.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/KillSpawnBeaconObjectiveTask.java @@ -16,9 +16,9 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -30,6 +30,8 @@ import it.unimi.dsi.fastutil.Pair; import java.util.Arrays; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class KillSpawnBeaconObjectiveTask extends KillObjectiveTask { @Nonnull @@ -77,7 +79,7 @@ public class KillSpawnBeaconObjectiveTask extends KillObjectiveTask { HytaleLogger logger = ObjectivePlugin.get().getLogger(); for (int i = 0; i < spawnBeaconConfigs.length; i++) { - Vector3d spawnPosition = position.clone(); + Vector3d spawnPosition = new Vector3d(position); KillSpawnBeaconObjectiveTaskAsset.ObjectiveSpawnBeacon spawnBeaconConfig = spawnBeaconConfigs[i]; String spawnBeaconId = spawnBeaconConfig.getSpawnBeaconId(); int index = BeaconNPCSpawn.getAssetMap().getIndex(spawnBeaconId); @@ -93,9 +95,9 @@ public class KillSpawnBeaconObjectiveTask extends KillObjectiveTask { WorldLocationProvider worldLocationCondition = spawnBeaconConfig.getWorldLocationProvider(); if (worldLocationCondition != null) { - Vector3i potentialSpawnLocation = worldLocationCondition.runCondition(world, spawnPosition.toVector3i()); + Vector3i potentialSpawnLocation = worldLocationCondition.runCondition(world, Vector3dUtil.toVector3i(spawnPosition)); if (potentialSpawnLocation != null) { - spawnPosition = potentialSpawnLocation.toVector3d(); + spawnPosition = Vector3iUtil.toVector3d(potentialSpawnLocation); } else { spawnPosition = null; } @@ -106,7 +108,7 @@ public class KillSpawnBeaconObjectiveTask extends KillObjectiveTask { } else { BeaconSpawnWrapper wrapper = SpawningPlugin.get().getBeaconSpawnWrapper(index); Pair, LegacySpawnBeaconEntity> spawnBeaconPair = LegacySpawnBeaconEntity.create( - wrapper, spawnPosition, Vector3f.FORWARD, componentAccessor + wrapper, spawnPosition, Rotation3f.IDENTITY, componentAccessor ); spawnBeaconPair.second().setObjectiveUUID(objective.getObjectiveUUID()); UUIDComponent spawnBeaconUuidComponent = componentAccessor.getComponent(spawnBeaconPair.first(), UUIDComponent.getComponentType()); diff --git a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/KillSpawnMarkerObjectiveTask.java b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/KillSpawnMarkerObjectiveTask.java index 639b956f..5fbe6204 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/KillSpawnMarkerObjectiveTask.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcobjectives/task/KillSpawnMarkerObjectiveTask.java @@ -13,15 +13,15 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.SpawningPlugin; import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class KillSpawnMarkerObjectiveTask extends KillObjectiveTask { @Nonnull @@ -51,7 +51,7 @@ public class KillSpawnMarkerObjectiveTask extends KillObjectiveTask { Vector3d objectivePosition = objective.getPosition(store); if (objectivePosition != null) { KillSpawnMarkerObjectiveTaskAsset asset = this.getAsset(); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> spatialResource = store.getResource(SpawningPlugin.get().getSpawnMarkerSpatialResource()); spatialResource.getSpatialStructure().collect(objectivePosition, asset.getRadius(), results); String[] spawnMarkerIds = asset.getSpawnMarkerIds(); diff --git a/src/com/hypixel/hytale/builtin/adventure/npcshop/npc/ActionOpenBarterShop.java b/src/com/hypixel/hytale/builtin/adventure/npcshop/npc/ActionOpenBarterShop.java index 871d1672..175650ed 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcshop/npc/ActionOpenBarterShop.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcshop/npc/ActionOpenBarterShop.java @@ -12,6 +12,7 @@ import com.hypixel.hytale.server.npc.corecomponents.ActionBase; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionOpenBarterShop extends ActionBase { @Nonnull @@ -23,12 +24,12 @@ public class ActionOpenBarterShop extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && role.getStateSupport().getInteractionIterationTarget() != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); Ref playerReference = role.getStateSupport().getInteractionIterationTarget(); if (playerReference == null) { diff --git a/src/com/hypixel/hytale/builtin/adventure/npcshop/npc/ActionOpenShop.java b/src/com/hypixel/hytale/builtin/adventure/npcshop/npc/ActionOpenShop.java index db6ded60..47190db4 100644 --- a/src/com/hypixel/hytale/builtin/adventure/npcshop/npc/ActionOpenShop.java +++ b/src/com/hypixel/hytale/builtin/adventure/npcshop/npc/ActionOpenShop.java @@ -12,6 +12,7 @@ import com.hypixel.hytale.server.npc.corecomponents.ActionBase; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionOpenShop extends ActionBase { @Nonnull @@ -23,12 +24,12 @@ public class ActionOpenShop extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && role.getStateSupport().getInteractionIterationTarget() != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); Ref playerReference = role.getStateSupport().getInteractionIterationTarget(); if (playerReference == null) { diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/Objective.java b/src/com/hypixel/hytale/builtin/adventure/objectives/Objective.java index 94d02a52..38094310 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/Objective.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/Objective.java @@ -18,7 +18,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.function.consumer.TriConsumer; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.packets.assets.TrackOrUpdateObjective; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.inventory.ItemStack; @@ -41,6 +40,7 @@ import java.util.function.Consumer; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Objective implements NetworkSerializable { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/ObjectivePlugin.java b/src/com/hypixel/hytale/builtin/adventure/objectives/ObjectivePlugin.java index 173131ef..c6603080 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/ObjectivePlugin.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/ObjectivePlugin.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.builtin.adventure.objectives; import com.hypixel.hytale.assetstore.AssetRegistry; import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; -import com.hypixel.hytale.builtin.adventure.objectives.blockstates.TreasureChestState; +import com.hypixel.hytale.builtin.adventure.objectives.blockstates.TreasureChestBlock; import com.hypixel.hytale.builtin.adventure.objectives.commands.ObjectiveCommand; import com.hypixel.hytale.builtin.adventure.objectives.completion.ClearObjectiveItemsCompletion; import com.hypixel.hytale.builtin.adventure.objectives.completion.GiveItemsCompletion; @@ -29,6 +29,8 @@ import com.hypixel.hytale.builtin.adventure.objectives.historydata.ObjectiveHist import com.hypixel.hytale.builtin.adventure.objectives.historydata.ObjectiveLineHistoryData; import com.hypixel.hytale.builtin.adventure.objectives.historydata.ObjectiveRewardHistoryData; import com.hypixel.hytale.builtin.adventure.objectives.interactions.CanBreakRespawnPointInteraction; +import com.hypixel.hytale.builtin.adventure.objectives.interactions.DestroyTreasureConditionInteraction; +import com.hypixel.hytale.builtin.adventure.objectives.interactions.OpenTreasureContainerInteraction; import com.hypixel.hytale.builtin.adventure.objectives.interactions.StartObjectiveInteraction; import com.hypixel.hytale.builtin.adventure.objectives.markers.ObjectiveMarkerProvider; import com.hypixel.hytale.builtin.adventure.objectives.markers.objectivelocation.ObjectiveLocationMarker; @@ -36,6 +38,7 @@ import com.hypixel.hytale.builtin.adventure.objectives.markers.objectivelocation import com.hypixel.hytale.builtin.adventure.objectives.markers.reachlocation.ReachLocationMarker; import com.hypixel.hytale.builtin.adventure.objectives.markers.reachlocation.ReachLocationMarkerAsset; import com.hypixel.hytale.builtin.adventure.objectives.markers.reachlocation.ReachLocationMarkerSystems; +import com.hypixel.hytale.builtin.adventure.objectives.systems.ObjectiveInventoryChangeSystem; import com.hypixel.hytale.builtin.adventure.objectives.systems.ObjectiveItemEntityRemovalSystem; import com.hypixel.hytale.builtin.adventure.objectives.systems.ObjectivePlayerSetupSystem; import com.hypixel.hytale.builtin.adventure.objectives.task.CraftObjectiveTask; @@ -60,7 +63,7 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.function.function.TriFunction; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.packets.assets.TrackOrUpdateObjective; import com.hypixel.hytale.protocol.packets.assets.UntrackObjective; import com.hypixel.hytale.server.core.HytaleServer; @@ -77,10 +80,7 @@ import com.hypixel.hytale.server.core.asset.type.weather.config.Weather; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerConfigData; -import com.hypixel.hytale.server.core.event.events.entity.LivingEntityInventoryChangeEvent; import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent; -import com.hypixel.hytale.server.core.inventory.ItemStack; -import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel; @@ -96,7 +96,7 @@ import com.hypixel.hytale.server.core.universe.datastore.DataStoreProvider; import com.hypixel.hytale.server.core.universe.datastore.DiskDataStoreProvider; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.events.AddWorldEvent; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.Config; import java.util.Arrays; @@ -127,6 +127,7 @@ public class ObjectivePlugin extends JavaPlugin { private ComponentType objectiveHistoryComponentType; private ComponentType reachLocationMarkerComponentType; private ComponentType objectiveLocationMarkerComponentType; + private ComponentType treasureChestComponentType; @Nullable private ObjectiveDataStore objectiveDataStore; @@ -248,7 +249,6 @@ public class ObjectivePlugin extends JavaPlugin { eventRegistry.register(PlayerDisconnectEvent.class, this::onPlayerDisconnect); eventRegistry.register(LoadedAssetsEvent.class, ObjectiveLocationMarkerAsset.class, ObjectivePlugin::onObjectiveLocationMarkerChange); eventRegistry.register(LoadedAssetsEvent.class, ModelAsset.class, this::onModelAssetChange); - eventRegistry.registerGlobal(LivingEntityInventoryChangeEvent.class, this::onLivingEntityInventoryChange); eventRegistry.registerGlobal(AddWorldEvent.class, this::onWorldAdded); this.getCommandRegistry().registerCommand(new ObjectiveCommand()); EntityModule entityModule = EntityModule.get(); @@ -291,16 +291,15 @@ public class ObjectivePlugin extends JavaPlugin { ); entityStoreRegistry.registerSystem(new ObjectivePlayerSetupSystem(this.objectiveHistoryComponentType, Player.getComponentType())); entityStoreRegistry.registerSystem(new ObjectiveItemEntityRemovalSystem()); + entityStoreRegistry.registerSystem(new ObjectiveInventoryChangeSystem()); this.getCodecRegistry(Interaction.CODEC).register("StartObjective", StartObjectiveInteraction.class, StartObjectiveInteraction.CODEC); this.getCodecRegistry(Interaction.CODEC).register("CanBreakRespawnPoint", CanBreakRespawnPointInteraction.class, CanBreakRespawnPointInteraction.CODEC); - BlockStateModule.get().registerBlockState(TreasureChestState.class, "TreasureChest", TreasureChestState.CODEC); + this.getCodecRegistry(Interaction.CODEC) + .register("DestroyTreasureCondition", DestroyTreasureConditionInteraction.class, DestroyTreasureConditionInteraction.CODEC); + this.getCodecRegistry(Interaction.CODEC) + .register("OpenTreasureContainer", OpenTreasureContainerInteraction.class, OpenTreasureContainerInteraction.CODEC); + this.treasureChestComponentType = this.getChunkStoreRegistry().registerComponent(TreasureChestBlock.class, "TreasureChest", TreasureChestBlock.CODEC); this.getCodecRegistry(GameplayConfig.PLUGIN_CODEC).register(ObjectiveGameplayConfig.class, "Objective", ObjectiveGameplayConfig.CODEC); - entityStoreRegistry.registerSystem( - new EntityModule.TangibleMigrationSystem(Query.or(ObjectiveLocationMarker.getComponentType(), ReachLocationMarker.getComponentType())), true - ); - entityStoreRegistry.registerSystem( - new EntityModule.HiddenFromPlayerMigrationSystem(Query.or(ObjectiveLocationMarker.getComponentType(), ReachLocationMarker.getComponentType())), true - ); } @Override @@ -331,6 +330,10 @@ public class ObjectivePlugin extends JavaPlugin { return this.objectiveLocationMarkerComponentType; } + public ComponentType getTreasureChestComponentType() { + return this.treasureChestComponentType; + } + public void registerTask( String id, Class assetClass, @@ -414,15 +417,15 @@ public class ObjectivePlugin extends JavaPlugin { participantReference -> { Player playerComponent = store.getComponent(participantReference, Player.getComponentType()); if (playerComponent != null) { + PlayerRef playerRefComponent = store.getComponent(participantReference, PlayerRef.getComponentType()); + + assert playerRefComponent != null; + if (!this.canPlayerDoObjective(playerComponent, objectiveAssetId)) { - playerComponent.sendMessage( + playerRefComponent.sendMessage( Message.translation("server.modules.objective.playerAlreadyDoingObjective").param("title", assetTitleMessage) ); } else { - PlayerRef playerRefComponent = store.getComponent(participantReference, PlayerRef.getComponentType()); - - assert playerRefComponent != null; - UUIDComponent uuidComponent = store.getComponent(participantReference, UUIDComponent.getComponentType()); assert uuidComponent != null; @@ -831,8 +834,8 @@ public class ObjectivePlugin extends JavaPlugin { assert transformComponent != null; - Vector3f rotation = transformComponent.getRotation(); - objectiveLocationMarkerComponent.updateLocationMarkerValues(objectiveLocationMarkerAsset, rotation.getYaw(), store); + Rotation3f rotation = transformComponent.getRotation(); + objectiveLocationMarkerComponent.updateLocationMarkerValues(objectiveLocationMarkerAsset, rotation.yaw(), store); ModelComponent modelComponent = archetypeChunk.getComponent(index, ModelComponent.getComponentType()); assert modelComponent != null; @@ -885,56 +888,6 @@ public class ObjectivePlugin extends JavaPlugin { } } - private void onLivingEntityInventoryChange(@Nonnull LivingEntityInventoryChangeEvent event) { - if (this.objectiveDataStore != null) { - if (event.getEntity() instanceof Player player) { - Set activeObjectiveUUIDs = player.getPlayerConfigData().getActiveObjectiveUUIDs(); - if (!activeObjectiveUUIDs.isEmpty()) { - Set inventoryItemObjectiveUUIDs = null; - CombinedItemContainer inventory = player.getInventory().getCombinedHotbarFirst(); - - for (short i = 0; i < inventory.getCapacity(); i++) { - ItemStack itemStack = inventory.getItemStack(i); - if (!ItemStack.isEmpty(itemStack)) { - UUID objectiveUUID = itemStack.getFromMetadataOrNull(StartObjectiveInteraction.OBJECTIVE_UUID); - if (objectiveUUID != null) { - if (inventoryItemObjectiveUUIDs == null) { - inventoryItemObjectiveUUIDs = new HashSet<>(activeObjectiveUUIDs); - } - - inventoryItemObjectiveUUIDs.add(objectiveUUID); - } - } - } - - Ref reference = player.getReference(); - if (reference != null && reference.isValid()) { - Store store = reference.getStore(); - World world = store.getExternalData().getWorld(); - - for (UUID activeObjectiveUUID : activeObjectiveUUIDs) { - if (inventoryItemObjectiveUUIDs == null || !inventoryItemObjectiveUUIDs.contains(activeObjectiveUUID)) { - Objective objective = this.objectiveDataStore.getObjective(activeObjectiveUUID); - if (objective != null) { - ObjectiveAsset objectiveAsset = objective.getObjectiveAsset(); - if (objectiveAsset != null && objectiveAsset.isRemoveOnItemDrop()) { - world.execute(() -> { - UUIDComponent uuidComponent = store.getComponent(reference, UUIDComponent.getComponentType()); - - assert uuidComponent != null; - - get().removePlayerFromExistingObjective(store, uuidComponent.getUuid(), activeObjectiveUUID); - }); - } - } - } - } - } - } - } - } - } - private void onWorldAdded(@Nonnull AddWorldEvent event) { event.getWorld().getWorldMapManager().addMarkerProvider("objectives", ObjectiveMarkerProvider.INSTANCE); } diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/blockstates/TreasureChestState.java b/src/com/hypixel/hytale/builtin/adventure/objectives/blockstates/TreasureChestBlock.java similarity index 73% rename from src/com/hypixel/hytale/builtin/adventure/objectives/blockstates/TreasureChestState.java rename to src/com/hypixel/hytale/builtin/adventure/objectives/blockstates/TreasureChestBlock.java index caeb3a06..f74077fa 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/blockstates/TreasureChestState.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/blockstates/TreasureChestBlock.java @@ -6,25 +6,24 @@ import com.hypixel.hytale.builtin.adventure.objectives.events.TreasureChestOpeni import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.event.IEventDispatcher; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.entity.UUIDComponent; -import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.BreakValidatedBlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerState; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import java.util.List; import java.util.UUID; import javax.annotation.Nonnull; +import javax.annotation.Nullable; -public class TreasureChestState extends ItemContainerState implements BreakValidatedBlockState { +public class TreasureChestBlock implements Component { @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(TreasureChestState.class, TreasureChestState::new, BlockState.BASE_CODEC) + public static final BuilderCodec CODEC = BuilderCodec.builder(TreasureChestBlock.class, TreasureChestBlock::new) .append( new KeyedCodec<>("ObjectiveUUID", Codec.UUID_BINARY), (treasureChestState, uuid) -> treasureChestState.objectiveUUID = uuid, @@ -48,9 +47,21 @@ public class TreasureChestState extends ItemContainerState implements BreakValid protected UUID chestUUID; protected boolean opened; - @Override + public static ComponentType getComponentType() { + return ObjectivePlugin.get().getTreasureChestComponentType(); + } + + public TreasureChestBlock() { + } + + public TreasureChestBlock(UUID objectiveUUID, UUID chestUUID, boolean opened) { + this.objectiveUUID = objectiveUUID; + this.chestUUID = chestUUID; + this.opened = opened; + } + public boolean canOpen(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - if (!this.opened) { + if (!this.opened && this.objectiveUUID != null) { UUIDComponent uuidComponent = componentAccessor.getComponent(ref, UUIDComponent.getComponentType()); assert uuidComponent != null; @@ -62,17 +73,15 @@ public class TreasureChestState extends ItemContainerState implements BreakValid } } - @Override public boolean canDestroy(@Nonnull Ref playerRef, @Nonnull ComponentAccessor componentAccessor) { return this.opened; } - @Override public void onOpen(@Nonnull Ref ref, @Nonnull World world, @Nonnull Store store) { IEventDispatcher dispatcher = HytaleServer.get() .getEventBus() .dispatchFor(TreasureChestOpeningEvent.class, world.getName()); - if (dispatcher.hasListener()) { + if (dispatcher.hasListener() && this.objectiveUUID != null) { dispatcher.dispatch(new TreasureChestOpeningEvent(this.objectiveUUID, this.chestUUID, ref, store)); } @@ -83,9 +92,18 @@ public class TreasureChestState extends ItemContainerState implements BreakValid this.opened = opened; } - public void setObjectiveData(UUID objectiveUUID, UUID chestUUID, List itemStacks) { + public boolean isOpened() { + return this.opened; + } + + public void setObjectiveData(UUID objectiveUUID, UUID chestUUID) { this.objectiveUUID = objectiveUUID; this.chestUUID = chestUUID; - this.itemContainer.addItemStacks(itemStacks); + } + + @Nullable + @Override + public Component clone() { + return new TreasureChestBlock(this.objectiveUUID, this.chestUUID, this.opened); } } diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/completion/ClearObjectiveItemsCompletion.java b/src/com/hypixel/hytale/builtin/adventure/objectives/completion/ClearObjectiveItemsCompletion.java index 491264ed..3313f449 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/completion/ClearObjectiveItemsCompletion.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/completion/ClearObjectiveItemsCompletion.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.builtin.adventure.objectives.config.completion.ClearOb import com.hypixel.hytale.builtin.adventure.objectives.config.completion.ObjectiveCompletionAsset; import com.hypixel.hytale.builtin.adventure.objectives.interactions.StartObjectiveInteraction; import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.server.core.entity.EntityUtils; -import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -26,16 +25,14 @@ public class ClearObjectiveItemsCompletion extends ObjectiveCompletion { @Override public void handle(@Nonnull Objective objective, @Nonnull ComponentAccessor componentAccessor) { objective.forEachParticipant((participantReference, objectiveUuid) -> { - if (EntityUtils.getEntity(participantReference, componentAccessor) instanceof LivingEntity livingEntity) { - CombinedItemContainer inventory = livingEntity.getInventory().getCombinedHotbarFirst(); + CombinedItemContainer inventory = InventoryComponent.getCombined(componentAccessor, participantReference, InventoryComponent.HOTBAR_FIRST); - for (short i = 0; i < inventory.getCapacity(); i++) { - ItemStack itemStack = inventory.getItemStack(i); - if (itemStack != null) { - UUID savedObjectiveUuid = itemStack.getFromMetadataOrNull(StartObjectiveInteraction.OBJECTIVE_UUID); - if (objectiveUuid.equals(savedObjectiveUuid)) { - inventory.removeItemStackFromSlot(i); - } + for (short i = 0; i < inventory.getCapacity(); i++) { + ItemStack itemStack = inventory.getItemStack(i); + if (itemStack != null) { + UUID savedObjectiveUuid = itemStack.getFromMetadataOrNull(StartObjectiveInteraction.OBJECTIVE_UUID); + if (objectiveUuid.equals(savedObjectiveUuid)) { + inventory.removeItemStackFromSlot(i); } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/completion/GiveItemsCompletion.java b/src/com/hypixel/hytale/builtin/adventure/objectives/completion/GiveItemsCompletion.java index 0796c4f4..951bce23 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/completion/GiveItemsCompletion.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/completion/GiveItemsCompletion.java @@ -6,12 +6,11 @@ import com.hypixel.hytale.builtin.adventure.objectives.historydata.ItemObjective import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.Message; -import com.hypixel.hytale.server.core.entity.EntityUtils; -import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; import com.hypixel.hytale.server.core.modules.item.ItemModule; import com.hypixel.hytale.server.core.universe.PlayerRef; @@ -41,33 +40,33 @@ public class GiveItemsCompletion extends ObjectiveCompletion { boolean showItemNotification = world.getGameplayConfig().getShowItemPickupNotifications(); objective.forEachParticipant( (participantReference, asset, objectiveHistoryData) -> { - if (EntityUtils.getEntity(participantReference, componentAccessor) instanceof LivingEntity livingEntity) { - Inventory inventory = livingEntity.getInventory(); - List itemStacks = ItemModule.get().getRandomItemDrops(asset.getDropListId()); - SimpleItemContainer.addOrDropItemStacks(store, participantReference, inventory.getCombinedHotbarFirst(), itemStacks); - Player playerComponent = componentAccessor.getComponent(participantReference, Player.getComponentType()); - if (playerComponent != null) { - PlayerRef playerRefComponent = componentAccessor.getComponent(participantReference, PlayerRef.getComponentType()); + CombinedItemContainer hotbarFirstCombinedItemContainer = InventoryComponent.getCombined( + componentAccessor, participantReference, InventoryComponent.HOTBAR_FIRST + ); + List itemStacks = ItemModule.get().getRandomItemDrops(asset.getDropListId()); + SimpleItemContainer.addOrDropItemStacks(store, participantReference, hotbarFirstCombinedItemContainer, itemStacks); + Player playerComponent = componentAccessor.getComponent(participantReference, Player.getComponentType()); + if (playerComponent != null) { + PlayerRef playerRefComponent = componentAccessor.getComponent(participantReference, PlayerRef.getComponentType()); - assert playerRefComponent != null; + assert playerRefComponent != null; - UUIDComponent uuidComponent = store.getComponent(participantReference, UUIDComponent.getComponentType()); + UUIDComponent uuidComponent = store.getComponent(participantReference, UUIDComponent.getComponentType()); - assert uuidComponent != null; + assert uuidComponent != null; - UUID uuid = uuidComponent.getUuid(); + UUID uuid = uuidComponent.getUuid(); - for (ItemStack itemStack : itemStacks) { - objectiveHistoryData.addRewardForPlayerUUID(uuid, new ItemObjectiveRewardHistoryData(itemStack.getItemId(), itemStack.getQuantity())); - if (showItemNotification) { - Message itemNameMessage = Message.translation(itemStack.getItem().getTranslationKey()); - NotificationUtil.sendNotification( - playerRefComponent.getPacketHandler(), - Message.translation("server.objectives.itemObjectiveCompletion").param("item", itemNameMessage), - null, - itemStack.toPacket() - ); - } + for (ItemStack itemStack : itemStacks) { + objectiveHistoryData.addRewardForPlayerUUID(uuid, new ItemObjectiveRewardHistoryData(itemStack.getItemId(), itemStack.getQuantity())); + if (showItemNotification) { + Message itemNameMessage = Message.translation(itemStack.getItem().getTranslationKey()); + NotificationUtil.sendNotification( + playerRefComponent.getPacketHandler(), + Message.translation("server.objectives.itemObjectiveCompletion").param("item", itemNameMessage), + null, + itemStack.toPacket() + ); } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationAreaBox.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationAreaBox.java index d01c2762..c0e08a58 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationAreaBox.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationAreaBox.java @@ -8,12 +8,12 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ObjectiveLocationAreaBox extends ObjectiveLocationMarkerArea { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationAreaRadius.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationAreaRadius.java index 33c53497..fe71c59c 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationAreaRadius.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationAreaRadius.java @@ -9,13 +9,13 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.math.vector.VectorSphereUtil; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ObjectiveLocationAreaRadius extends ObjectiveLocationMarkerArea { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationMarkerArea.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationMarkerArea.java index f350bf7b..05f3a08b 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationMarkerArea.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/markerarea/ObjectiveLocationMarkerArea.java @@ -6,11 +6,11 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class ObjectiveLocationMarkerArea { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/CountObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/CountObjectiveTaskAsset.java index 4b66db38..cf453cb3 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/CountObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/CountObjectiveTaskAsset.java @@ -5,8 +5,8 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public abstract class CountObjectiveTaskAsset extends ObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/CraftObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/CraftObjectiveTaskAsset.java index cabe34d8..b430e019 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/CraftObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/CraftObjectiveTaskAsset.java @@ -5,9 +5,9 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class CraftObjectiveTaskAsset extends CountObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/GatherObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/GatherObjectiveTaskAsset.java index 43316a73..a6b8b9e1 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/GatherObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/GatherObjectiveTaskAsset.java @@ -4,8 +4,8 @@ import com.hypixel.hytale.builtin.adventure.objectives.config.taskcondition.Task import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class GatherObjectiveTaskAsset extends CountObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/ObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/ObjectiveTaskAsset.java index d553de67..9045b1d0 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/ObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/ObjectiveTaskAsset.java @@ -6,11 +6,12 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.lookup.CodecMapCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.text.MessageFormat; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public abstract class ObjectiveTaskAsset { @Nonnull @@ -30,7 +31,7 @@ public abstract class ObjectiveTaskAsset { ) .add() .append( - new KeyedCodec<>("MapMarkerPositions", new ArrayCodec<>(Vector3i.CODEC, Vector3i[]::new)), + new KeyedCodec<>("MapMarkerPositions", new ArrayCodec<>(Vector3iUtil.CODEC, Vector3i[]::new)), (taskAsset, positions) -> taskAsset.mapMarkers = positions, taskAsset -> taskAsset.mapMarkers ) diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/TreasureMapObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/TreasureMapObjectiveTaskAsset.java index 285a4f64..5ec74434 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/TreasureMapObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/TreasureMapObjectiveTaskAsset.java @@ -8,12 +8,12 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class TreasureMapObjectiveTaskAsset extends ObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/UseBlockObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/UseBlockObjectiveTaskAsset.java index 25670e5d..a7720b18 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/UseBlockObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/UseBlockObjectiveTaskAsset.java @@ -4,8 +4,8 @@ import com.hypixel.hytale.builtin.adventure.objectives.config.taskcondition.Task import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class UseBlockObjectiveTaskAsset extends CountObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/UseEntityObjectiveTaskAsset.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/UseEntityObjectiveTaskAsset.java index 3beb9bcc..a4176493 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/UseEntityObjectiveTaskAsset.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/task/UseEntityObjectiveTaskAsset.java @@ -5,10 +5,10 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class UseEntityObjectiveTaskAsset extends CountObjectiveTaskAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/taskcondition/SoloInventoryCondition.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/taskcondition/SoloInventoryCondition.java index 13085880..6adb1da9 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/taskcondition/SoloInventoryCondition.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/taskcondition/SoloInventoryCondition.java @@ -7,9 +7,9 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Set; import java.util.UUID; @@ -69,32 +69,24 @@ public class SoloInventoryCondition extends TaskConditionAsset { @Override public boolean isConditionFulfilled(@Nonnull ComponentAccessor componentAccessor, @Nonnull Ref ref, Set objectivePlayers) { - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - if (playerComponent == null) { - return false; - } else { - Inventory inventory = playerComponent.getInventory(); - if (this.holdInHand) { - ItemStack itemInHand = inventory.getItemInHand(); - if (itemInHand == null) { - return false; - } else { - return !this.blockTypeOrTagTask.isBlockTypeIncluded(itemInHand.getItemId()) ? false : inventory.getItemInHand().getQuantity() >= this.quantity; - } + if (this.holdInHand) { + ItemStack itemInHand = InventoryComponent.getItemInHand(componentAccessor, ref); + if (itemInHand == null) { + return false; } else { - return inventory.getCombinedHotbarFirst().countItemStacks(itemStack -> this.blockTypeOrTagTask.isBlockTypeIncluded(itemStack.getItemId())) - >= this.quantity; + return !this.blockTypeOrTagTask.isBlockTypeIncluded(itemInHand.getItemId()) ? false : itemInHand.getQuantity() >= this.quantity; } + } else { + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); + return combinedInventory.countItemStacks(itemStack -> this.blockTypeOrTagTask.isBlockTypeIncluded(itemStack.getItemId())) >= this.quantity; } } @Override public void consumeCondition(@Nonnull ComponentAccessor componentAccessor, @Nonnull Ref ref, Set objectivePlayers) { - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - if (playerComponent != null) { - if (this.consumeOnCompletion) { - this.blockTypeOrTagTask.consumeItemStacks(playerComponent.getInventory().getCombinedHotbarFirst(), this.quantity); - } + if (this.consumeOnCompletion) { + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); + this.blockTypeOrTagTask.consumeItemStacks(combinedInventory, this.quantity); } } diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/CheckTagWorldHeightRadiusProvider.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/CheckTagWorldHeightRadiusProvider.java index 30aaf29b..2b0201df 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/CheckTagWorldHeightRadiusProvider.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/CheckTagWorldHeightRadiusProvider.java @@ -8,13 +8,13 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.math.iterator.SpiralIterator; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class CheckTagWorldHeightRadiusProvider extends WorldLocationProvider { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/LocationRadiusProvider.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/LocationRadiusProvider.java index 7765f40c..db564661 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/LocationRadiusProvider.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/LocationRadiusProvider.java @@ -7,11 +7,11 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class LocationRadiusProvider extends WorldLocationProvider { @Nonnull @@ -52,7 +52,7 @@ public class LocationRadiusProvider extends WorldLocationProvider { public Vector3i runCondition(@Nonnull World world, @Nonnull Vector3i position) { double angle = Math.random() * (float) (Math.PI * 2); int radius = MathUtil.randomInt(this.minRadius, this.maxRadius); - Vector3i newPosition = position.clone(); + Vector3i newPosition = new Vector3i(position); newPosition.add((int)(radius * TrigMathUtil.cos(angle)), 0, (int)(radius * TrigMathUtil.sin(angle))); long chunkIndex = ChunkUtil.indexChunkFromBlock(newPosition.x, newPosition.z); WorldChunk worldChunkComponent = world.getChunk(chunkIndex); diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/LookBlocksBelowProvider.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/LookBlocksBelowProvider.java index 415b0412..a1326d0e 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/LookBlocksBelowProvider.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/LookBlocksBelowProvider.java @@ -6,13 +6,13 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class LookBlocksBelowProvider extends WorldLocationProvider { @Nonnull @@ -96,7 +96,7 @@ public class LookBlocksBelowProvider extends WorldLocationProvider { @Nullable @Override public Vector3i runCondition(@Nonnull World world, @Nonnull Vector3i position) { - Vector3i newPosition = position.clone(); + Vector3i newPosition = new Vector3i(position); long chunkIndex = ChunkUtil.indexChunkFromBlock(newPosition.x, newPosition.z); WorldChunk worldChunkComponent = world.getChunk(chunkIndex); if (worldChunkComponent == null) { diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/WorldLocationProvider.java b/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/WorldLocationProvider.java index 05978739..9fee1786 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/WorldLocationProvider.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/config/worldlocationproviders/WorldLocationProvider.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.builtin.adventure.objectives.config.worldlocationprov import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.lookup.CodecMapCodec; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.universe.world.World; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public abstract class WorldLocationProvider { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/CanBreakRespawnPointInteraction.java b/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/CanBreakRespawnPointInteraction.java index 0a942101..f1752e08 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/CanBreakRespawnPointInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/CanBreakRespawnPointInteraction.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -21,6 +20,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class CanBreakRespawnPointInteraction extends SimpleBlockInteraction { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/DestroyTreasureConditionInteraction.java b/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/DestroyTreasureConditionInteraction.java new file mode 100644 index 00000000..a699c985 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/DestroyTreasureConditionInteraction.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.builtin.adventure.objectives.interactions; + +import com.hypixel.hytale.builtin.adventure.objectives.blockstates.TreasureChestBlock; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.protocol.InteractionState; +import com.hypixel.hytale.protocol.InteractionType; +import com.hypixel.hytale.protocol.WaitForDataFrom; +import com.hypixel.hytale.server.core.entity.InteractionContext; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; +import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3i; + +public class DestroyTreasureConditionInteraction extends SimpleBlockInteraction { + public static final BuilderCodec CODEC = BuilderCodec.builder( + DestroyTreasureConditionInteraction.class, DestroyTreasureConditionInteraction::new, SimpleBlockInteraction.CODEC + ) + .documentation("Checks if the target treasure block is destroyable") + .build(); + + @Nonnull + @Override + public WaitForDataFrom getWaitForDataFrom() { + return WaitForDataFrom.Server; + } + + @Override + protected void interactWithBlock( + @Nonnull World world, + @Nonnull CommandBuffer commandBuffer, + @Nonnull InteractionType type, + @Nonnull InteractionContext context, + @Nullable ItemStack itemInHand, + @Nonnull Vector3i pos, + @Nonnull CooldownHandler cooldownHandler + ) { + Ref ref = context.getEntity(); + Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); + if (playerComponent != null) { + ChunkStore chunkStore = world.getChunkStore(); + Ref chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); + if (chunkRef != null) { + Store chunkEntityStore = chunkStore.getStore(); + BlockComponentChunk blockComponentChunk = chunkEntityStore.getComponent(chunkRef, BlockComponentChunk.getComponentType()); + if (blockComponentChunk != null) { + Ref blockRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(pos.x, pos.y, pos.z)); + if (blockRef != null) { + TreasureChestBlock treasureBlock = chunkEntityStore.getComponent(blockRef, TreasureChestBlock.getComponentType()); + if (treasureBlock != null) { + if (treasureBlock.canDestroy(ref, commandBuffer)) { + context.getState().state = InteractionState.Finished; + } else { + context.getState().state = InteractionState.Failed; + } + } + } + } + } + } + } + + @Override + protected void simulateInteractWithBlock( + @Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock + ) { + } +} diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/OpenTreasureContainerInteraction.java b/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/OpenTreasureContainerInteraction.java new file mode 100644 index 00000000..cb41deb2 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/interactions/OpenTreasureContainerInteraction.java @@ -0,0 +1,146 @@ +package com.hypixel.hytale.builtin.adventure.objectives.interactions; + +import com.hypixel.hytale.builtin.adventure.objectives.blockstates.TreasureChestBlock; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.protocol.InteractionType; +import com.hypixel.hytale.protocol.packets.interface_.Page; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.entity.InteractionContext; +import com.hypixel.hytale.server.core.entity.UUIDComponent; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerBlockWindow; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.block.components.ItemContainerBlock; +import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; +import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.SoundUtil; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; + +public class OpenTreasureContainerInteraction extends SimpleBlockInteraction { + public static final BuilderCodec CODEC = BuilderCodec.builder( + OpenTreasureContainerInteraction.class, OpenTreasureContainerInteraction::new, SimpleBlockInteraction.CODEC + ) + .documentation("Opens the container of the block currently being interacted with.") + .build(); + + @Override + protected void interactWithBlock( + @Nonnull World world, + @Nonnull CommandBuffer commandBuffer, + @Nonnull InteractionType type, + @Nonnull InteractionContext context, + @Nullable ItemStack itemInHand, + @Nonnull Vector3i pos, + @Nonnull CooldownHandler cooldownHandler + ) { + Ref ref = context.getEntity(); + Store store = ref.getStore(); + Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); + if (playerComponent != null) { + ChunkStore chunkStore = world.getChunkStore(); + Ref chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); + if (chunkRef != null) { + BlockComponentChunk blockComponentChunk = chunkStore.getStore().getComponent(chunkRef, BlockComponentChunk.getComponentType()); + if (blockComponentChunk != null) { + Ref blockRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(pos.x, pos.y, pos.z)); + if (blockRef != null) { + ItemContainerBlock itemContainerBlock = chunkStore.getStore().getComponent(blockRef, ItemContainerBlock.getComponentType()); + TreasureChestBlock treasureBlock = chunkStore.getStore().getComponent(blockRef, TreasureChestBlock.getComponentType()); + if (itemContainerBlock != null && treasureBlock != null) { + BlockType blockType = world.getBlockType(pos.x, pos.y, pos.z); + if (treasureBlock.canOpen(ref, commandBuffer)) { + UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); + + assert uuidComponent != null; + + UUID uuid = uuidComponent.getUuid(); + WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); + ContainerBlockWindow window = new ContainerBlockWindow( + pos.x, pos.y, pos.z, chunk.getRotationIndex(pos.x, pos.y, pos.z), blockType, itemContainerBlock.getItemContainer() + ); + Map windows = itemContainerBlock.getWindows(); + if (windows.putIfAbsent(uuid, window) == null) { + if (playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, window)) { + window.registerCloseEvent(event -> { + windows.remove(uuid, window); + BlockType currentBlockType = world.getBlockType(pos); + if (windows.isEmpty()) { + world.setBlockInteractionState(pos, currentBlockType, "CloseWindow"); + } + + BlockType interactionStatex = currentBlockType.getBlockForState("CloseWindow"); + if (interactionStatex != null) { + int soundEventIndexx = interactionStatex.getInteractionSoundEventIndex(); + if (soundEventIndexx != 0) { + int rotationIndexx = chunk.getRotationIndex(pos.x, pos.y, pos.z); + Vector3d soundPosx = new Vector3d(); + blockType.getBlockCenter(rotationIndexx, soundPosx); + soundPosx.add(pos.x, pos.y, pos.z); + SoundUtil.playSoundEvent3d(ref, soundEventIndexx, soundPosx, commandBuffer); + } + } + }); + if (windows.size() == 1) { + world.setBlockInteractionState(pos, blockType, "OpenWindow"); + } + + BlockType interactionState = blockType.getBlockForState("OpenWindow"); + if (interactionState == null) { + return; + } + + int soundEventIndex = interactionState.getInteractionSoundEventIndex(); + if (soundEventIndex == 0) { + return; + } + + int rotationIndex = chunk.getRotationIndex(pos.x, pos.y, pos.z); + Vector3d soundPos = new Vector3d(); + blockType.getBlockCenter(rotationIndex, soundPos); + soundPos.add(pos.x, pos.y, pos.z); + SoundUtil.playSoundEvent3d(ref, soundEventIndex, soundPos, commandBuffer); + } else { + windows.remove(uuid, window); + } + } + + treasureBlock.onOpen(ref, world, store); + } + } else { + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent != null) { + playerRefComponent.sendMessage( + Message.translation("server.interactions.invalidBlockState") + .param("interaction", this.getClass().getSimpleName()) + .param("blockState", chunkStore.getStore().getArchetype(blockRef).toString()) + ); + } + } + } + } + } + } + } + + @Override + protected void simulateInteractWithBlock( + @Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock + ) { + } +} diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/markers/ObjectiveTaskMarker.java b/src/com/hypixel/hytale/builtin/adventure/objectives/markers/ObjectiveTaskMarker.java index d614ba1c..55fcb83b 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/markers/ObjectiveTaskMarker.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/markers/ObjectiveTaskMarker.java @@ -53,6 +53,6 @@ public class ObjectiveTaskMarker { } public MapMarker toProto() { - return new MapMarker(this.id, this.name.getFormattedMessage(), null, this.icon, PositionUtil.toTransformPacket(this.transform), null, null); + return new MapMarker(this.id, this.name.getFormattedMessage(), this.icon, PositionUtil.toTransformPacket(this.transform), null, null); } } diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/markers/objectivelocation/ObjectiveLocationMarkerSystems.java b/src/com/hypixel/hytale/builtin/adventure/objectives/markers/objectivelocation/ObjectiveLocationMarkerSystems.java index ed0f426b..ad3580e5 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/markers/objectivelocation/ObjectiveLocationMarkerSystems.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/markers/objectivelocation/ObjectiveLocationMarkerSystems.java @@ -29,8 +29,7 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.packets.assets.TrackOrUpdateObjective; import com.hypixel.hytale.protocol.packets.assets.UntrackObjective; import com.hypixel.hytale.server.core.asset.type.model.config.Model; @@ -44,15 +43,16 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ObjectiveLocationMarkerSystems { public static class EnsureNetworkSendableSystem extends HolderSystem { @@ -154,8 +154,8 @@ public class ObjectiveLocationMarkerSystems { assert transformComponent != null; - Vector3f rotation = transformComponent.getRotation(); - objectiveLocationMarkerComponent.updateLocationMarkerValues(markerAsset, rotation.getYaw(), store); + Rotation3f rotation = transformComponent.getRotation(); + objectiveLocationMarkerComponent.updateLocationMarkerValues(markerAsset, rotation.yaw(), store); ModelComponent modelComponent = store.getComponent(ref, this.modelComponentType); assert modelComponent != null; @@ -304,7 +304,7 @@ public class ObjectiveLocationMarkerSystems { this.setupMarker(store, objectiveLocationMarkerComponent, entityReference, position, uuid, commandBuffer); } else if (!activeObjective.isCompleted()) { SpatialResource, EntityStore> spatialResource = store.getResource(this.playerSpatialComponent); - ObjectList> playerRefs = SpatialResource.getThreadLocalReferenceList(); + List> playerRefs = SpatialResource.getThreadLocalReferenceList(); objectiveLocationMarkerComponent.area.getPlayersInExitArea(spatialResource, playerRefs, position); HashSet playersInExitArea = new HashSet<>(playerRefs.size()); PlayerRef[] playersInEntryArea = new PlayerRef[playerRefs.size()]; diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/markers/reachlocation/ReachLocationMarkerSystems.java b/src/com/hypixel/hytale/builtin/adventure/objectives/markers/reachlocation/ReachLocationMarkerSystems.java index b9cf40d2..3c192caa 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/markers/reachlocation/ReachLocationMarkerSystems.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/markers/reachlocation/ReachLocationMarkerSystems.java @@ -24,18 +24,18 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.system.PlayerSpatialSystem; import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ReachLocationMarkerSystems { @Nonnull @@ -198,7 +198,7 @@ public class ReachLocationMarkerSystems { Set players = reachLocationMarkerComponent.getPlayers(); players.clear(); SpatialResource, EntityStore> spatialResource = store.getResource(this.playerSpatialComponent); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); TransformComponent transformComponent = archetypeChunk.getComponent(index, this.transformComponentType); assert transformComponent != null; diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/systems/ObjectiveInventoryChangeSystem.java b/src/com/hypixel/hytale/builtin/adventure/objectives/systems/ObjectiveInventoryChangeSystem.java new file mode 100644 index 00000000..6146d726 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/systems/ObjectiveInventoryChangeSystem.java @@ -0,0 +1,111 @@ +package com.hypixel.hytale.builtin.adventure.objectives.systems; + +import com.hypixel.hytale.builtin.adventure.objectives.Objective; +import com.hypixel.hytale.builtin.adventure.objectives.ObjectiveDataStore; +import com.hypixel.hytale.builtin.adventure.objectives.ObjectivePlugin; +import com.hypixel.hytale.builtin.adventure.objectives.config.ObjectiveAsset; +import com.hypixel.hytale.builtin.adventure.objectives.interactions.StartObjectiveInteraction; +import com.hypixel.hytale.builtin.adventure.objectives.task.InventoryChangeAware; +import com.hypixel.hytale.builtin.adventure.objectives.task.ObjectiveTask; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.server.core.entity.UUIDComponent; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryChangeEvent; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ObjectiveInventoryChangeSystem extends EntityEventSystem { + public ObjectiveInventoryChangeSystem() { + super(InventoryChangeEvent.class); + } + + public void handle( + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer, + @Nonnull InventoryChangeEvent event + ) { + ObjectiveDataStore objectiveDataStore = ObjectivePlugin.get().getObjectiveDataStore(); + if (objectiveDataStore != null) { + Player playerComponent = archetypeChunk.getComponent(index, Player.getComponentType()); + + assert playerComponent != null; + + Set activeObjectiveUUIDs = playerComponent.getPlayerConfigData().getActiveObjectiveUUIDs(); + if (!activeObjectiveUUIDs.isEmpty()) { + Ref ref = archetypeChunk.getReferenceTo(index); + handleRemoveOnItemDrop(ref, activeObjectiveUUIDs, objectiveDataStore, store); + + for (UUID objectiveUUID : activeObjectiveUUIDs) { + Objective objective = objectiveDataStore.getObjective(objectiveUUID); + if (objective != null) { + ObjectiveTask[] currentTasks = objective.getCurrentTasks(); + if (currentTasks != null) { + for (ObjectiveTask task : currentTasks) { + if (task instanceof InventoryChangeAware aware) { + aware.onInventoryChange(objective, ref, store, event); + } + } + } + } + } + } + } + } + + private static void handleRemoveOnItemDrop( + @Nonnull Ref ref, @Nonnull Set activeObjectiveUUIDs, @Nonnull ObjectiveDataStore objectiveDataStore, @Nonnull Store store + ) { + Set inventoryItemObjectiveUUIDs = null; + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(store, ref, InventoryComponent.HOTBAR_FIRST); + + for (short i = 0; i < combinedInventory.getCapacity(); i++) { + ItemStack itemStack = combinedInventory.getItemStack(i); + if (!ItemStack.isEmpty(itemStack)) { + UUID objectiveUUID = itemStack.getFromMetadataOrNull(StartObjectiveInteraction.OBJECTIVE_UUID); + if (objectiveUUID != null) { + if (inventoryItemObjectiveUUIDs == null) { + inventoryItemObjectiveUUIDs = new HashSet<>(activeObjectiveUUIDs); + } + + inventoryItemObjectiveUUIDs.add(objectiveUUID); + } + } + } + + UUIDComponent uuidComponent = store.getComponent(ref, UUIDComponent.getComponentType()); + + assert uuidComponent != null; + + for (UUID activeObjectiveUUID : activeObjectiveUUIDs) { + if (inventoryItemObjectiveUUIDs == null || !inventoryItemObjectiveUUIDs.contains(activeObjectiveUUID)) { + Objective objective = objectiveDataStore.getObjective(activeObjectiveUUID); + if (objective != null) { + ObjectiveAsset objectiveAsset = objective.getObjectiveAsset(); + if (objectiveAsset != null && objectiveAsset.isRemoveOnItemDrop()) { + ObjectivePlugin.get().removePlayerFromExistingObjective(store, uuidComponent.getUuid(), activeObjectiveUUID); + } + } + } + } + } + + @Nullable + @Override + public Query getQuery() { + return Player.getComponentType(); + } +} diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/task/GatherObjectiveTask.java b/src/com/hypixel/hytale/builtin/adventure/objectives/task/GatherObjectiveTask.java index caf79621..099ccb80 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/task/GatherObjectiveTask.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/task/GatherObjectiveTask.java @@ -3,17 +3,15 @@ package com.hypixel.hytale.builtin.adventure.objectives.task; import com.hypixel.hytale.builtin.adventure.objectives.Objective; import com.hypixel.hytale.builtin.adventure.objectives.config.task.BlockTagOrItemIdField; import com.hypixel.hytale.builtin.adventure.objectives.config.task.GatherObjectiveTaskAsset; -import com.hypixel.hytale.builtin.adventure.objectives.transaction.RegistrationTransactionRecord; import com.hypixel.hytale.builtin.adventure.objectives.transaction.TransactionRecord; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.UUIDComponent; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.event.events.entity.LivingEntityInventoryChangeEvent; +import com.hypixel.hytale.server.core.inventory.InventoryChangeEvent; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.Universe; @@ -24,7 +22,7 @@ import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class GatherObjectiveTask extends CountObjectiveTask { +public class GatherObjectiveTask extends CountObjectiveTask implements InventoryChangeAware { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( GatherObjectiveTask.class, GatherObjectiveTask::new, CountObjectiveTask.CODEC @@ -57,28 +55,21 @@ public class GatherObjectiveTask extends CountObjectiveTask { } } - this.eventRegistry.register(LivingEntityInventoryChangeEvent.class, world.getName(), event -> { - LivingEntity livingEntity = event.getEntity(); - if (livingEntity instanceof Player) { - Ref ref = livingEntity.getReference(); - if (ref != null && ref.isValid()) { - World refWorld = store.getExternalData().getWorld(); - refWorld.execute(() -> { - if (ref.isValid()) { - UUIDComponent uuidComponent = store.getComponent(ref, UUIDComponent.getComponentType()); - if (uuidComponent != null) { - Set activePlayerUUIDs = objective.getActivePlayerUUIDs(); - if (activePlayerUUIDs.contains(uuidComponent.getUuid())) { - int count = this.countObjectiveItemInInventories(activePlayerUUIDs, store); - this.setTaskCompletion(store, ref, count, objective); - } - } - } - }); - } + return null; + } + + @Override + public void onInventoryChange( + @Nonnull Objective objective, @Nonnull Ref playerRef, @Nonnull Store store, @Nonnull InventoryChangeEvent event + ) { + UUIDComponent uuidComponent = store.getComponent(playerRef, UUIDComponent.getComponentType()); + if (uuidComponent != null) { + Set activePlayerUUIDs = objective.getActivePlayerUUIDs(); + if (activePlayerUUIDs.contains(uuidComponent.getUuid())) { + int count = this.countObjectiveItemInInventories(activePlayerUUIDs, store); + this.setTaskCompletion(store, playerRef, count, objective); } - }); - return RegistrationTransactionRecord.wrap(this.eventRegistry); + } } private int countObjectiveItemInInventories(@Nonnull Set participatingPlayers, @Nonnull ComponentAccessor componentAccessor) { @@ -90,12 +81,8 @@ public class GatherObjectiveTask extends CountObjectiveTask { if (playerRefComponent != null) { Ref playerRef = playerRefComponent.getReference(); if (playerRef != null && playerRef.isValid()) { - Player playerComponent = componentAccessor.getComponent(playerRef, Player.getComponentType()); - - assert playerComponent != null; - - CombinedItemContainer inventory = playerComponent.getInventory().getCombinedHotbarFirst(); - count += inventory.countItemStacks(itemStack -> blockTypeOrSet.isBlockTypeIncluded(itemStack.getItemId())); + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(componentAccessor, playerRef, InventoryComponent.HOTBAR_FIRST); + count += combinedInventory.countItemStacks(itemStack -> blockTypeOrSet.isBlockTypeIncluded(itemStack.getItemId())); } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/task/InventoryChangeAware.java b/src/com/hypixel/hytale/builtin/adventure/objectives/task/InventoryChangeAware.java new file mode 100644 index 00000000..486ff61c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/task/InventoryChangeAware.java @@ -0,0 +1,12 @@ +package com.hypixel.hytale.builtin.adventure.objectives.task; + +import com.hypixel.hytale.builtin.adventure.objectives.Objective; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.inventory.InventoryChangeEvent; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import javax.annotation.Nonnull; + +public interface InventoryChangeAware { + void onInventoryChange(@Nonnull Objective var1, @Nonnull Ref var2, @Nonnull Store var3, @Nonnull InventoryChangeEvent var4); +} diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/task/ObjectiveTask.java b/src/com/hypixel/hytale/builtin/adventure/objectives/task/ObjectiveTask.java index 55f29570..6c1f9078 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/task/ObjectiveTask.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/task/ObjectiveTask.java @@ -18,7 +18,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.assets.UpdateObjectiveTask; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.io.NetworkSerializer; @@ -34,6 +33,7 @@ import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public abstract class ObjectiveTask implements NetworkSerializer { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/task/ReachLocationTask.java b/src/com/hypixel/hytale/builtin/adventure/objectives/task/ReachLocationTask.java index a61945e6..5414e7eb 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/task/ReachLocationTask.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/task/ReachLocationTask.java @@ -14,16 +14,16 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ReachLocationTask extends ObjectiveTask { @Nonnull @@ -79,7 +79,7 @@ public class ReachLocationTask extends ObjectiveTask { return null; } else { String targetLocationId = this.getAsset().getTargetLocationId(); - List> reachLocationMarkerEntities = new ObjectArrayList<>(); + List> reachLocationMarkerEntities = new ReferenceArrayList<>(); store.forEachChunk(REACH_LOCATION_MARKER_COMPONENT_TYPE, (archetypeChunk, componentStoreCommandBuffer) -> { for (int index = 0; index < archetypeChunk.size(); index++) { ReachLocationMarker reachLocationMarkerComponent = archetypeChunk.getComponent(index, REACH_LOCATION_MARKER_COMPONENT_TYPE); @@ -100,7 +100,7 @@ public class ReachLocationTask extends ObjectiveTask { assert closestMarkerReachComponent != null; Vector3d closestPosition = closestMarkerTransformComponent.getPosition(); - double shortestDistance = closestPosition.distanceSquaredTo(currentLocation); + double shortestDistance = closestPosition.distanceSquared(currentLocation); String closestLocationName = closestMarkerReachComponent.getLocationName(); for (int i = 1; i < reachLocationMarkerEntities.size(); i++) { @@ -114,7 +114,7 @@ public class ReachLocationTask extends ObjectiveTask { assert markerReachLocationComponent != null; Vector3d pos = markerTransformComponent.getPosition(); - double distance = pos.distanceSquaredTo(currentLocation); + double distance = pos.distanceSquared(currentLocation); String locationName = markerReachLocationComponent.getLocationName(); if (distance < shortestDistance && locationName != null) { shortestDistance = distance; diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/task/TreasureMapObjectiveTask.java b/src/com/hypixel/hytale/builtin/adventure/objectives/task/TreasureMapObjectiveTask.java index a11d640f..5945408e 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/task/TreasureMapObjectiveTask.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/task/TreasureMapObjectiveTask.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.builtin.adventure.objectives.task; import com.hypixel.hytale.builtin.adventure.objectives.Objective; import com.hypixel.hytale.builtin.adventure.objectives.ObjectivePlugin; -import com.hypixel.hytale.builtin.adventure.objectives.blockstates.TreasureChestState; +import com.hypixel.hytale.builtin.adventure.objectives.blockstates.TreasureChestBlock; import com.hypixel.hytale.builtin.adventure.objectives.config.task.TreasureMapObjectiveTaskAsset; import com.hypixel.hytale.builtin.adventure.objectives.events.TreasureChestOpeningEvent; import com.hypixel.hytale.builtin.adventure.objectives.markers.ObjectiveTaskMarker; @@ -20,16 +20,14 @@ import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.block.components.ItemContainerBlock; import com.hypixel.hytale.server.core.modules.item.ItemModule; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerState; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Collections; @@ -38,6 +36,8 @@ import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class TreasureMapObjectiveTask extends ObjectiveTask { @Nonnull @@ -140,15 +140,23 @@ public class TreasureMapObjectiveTask extends ObjectiveTask { if (conditionPosition == null) { return transactionRecord.fail("Position not safe to spawn chest"); } else { - TreasureChestState treasureChestState = this.spawnChestBlock(world, conditionPosition, chestConfig.getChestBlockTypeKey(), transactionRecord); - if (treasureChestState == null) { + Ref treasureChestRef = this.spawnChestBlock(world, conditionPosition, chestConfig.getChestBlockTypeKey(), transactionRecord); + if (treasureChestRef == null) { return transactionRecord; } else { + TreasureChestBlock treasureChest = treasureChestRef.getStore().getComponent(treasureChestRef, TreasureChestBlock.getComponentType()); + + assert treasureChest != null; + + ItemContainerBlock container = treasureChestRef.getStore().getComponent(treasureChestRef, ItemContainerBlock.getComponentType()); + + assert container != null; + UUID chestUUID = UUID.randomUUID(); List stacks = ItemModule.get().getRandomItemDrops(chestConfig.getDroplistId()); - treasureChestState.setObjectiveData(objective.getObjectiveUUID(), chestUUID, stacks); + treasureChest.setObjectiveData(objective.getObjectiveUUID(), chestUUID); + container.getItemContainer().addItemStacks(stacks); this.chestUUIDs.add(chestUUID); - treasureChestState.getChunk().setState(conditionPosition.getX(), conditionPosition.getY(), conditionPosition.getZ(), treasureChestState); ObjectivePlugin.get().getLogger().at(Level.INFO).log("Spawned chest at: " + conditionPosition); ObjectiveTaskMarker marker = new ObjectiveTaskMarker( this.getChestMarkerIDFromUUID(chestUUID), new Transform(conditionPosition), "Home.png", Message.translation("server.objectives.treasure.marker") @@ -160,7 +168,7 @@ public class TreasureMapObjectiveTask extends ObjectiveTask { } @Nullable - private TreasureChestState spawnChestBlock( + private Ref spawnChestBlock( @Nonnull World world, @Nonnull Vector3i conditionPosition, String chestBlockTypeKey, @Nonnull SpawnTreasureChestTransactionRecord transactionRecord ) { long chunkIndex = ChunkUtil.indexChunkFromBlock(conditionPosition.x, conditionPosition.z); @@ -169,18 +177,23 @@ public class TreasureMapObjectiveTask extends ObjectiveTask { return null; } else { worldChunk.setBlock(conditionPosition.x, conditionPosition.y, conditionPosition.z, chestBlockTypeKey); - BlockState blockState = worldChunk.getState(conditionPosition.x, conditionPosition.y, conditionPosition.z); - if (!(blockState instanceof ItemContainerState)) { - transactionRecord.fail("BlockState is not a container"); + Ref blockEntity = worldChunk.getBlockComponentEntity(conditionPosition.x, conditionPosition.y, conditionPosition.z); + if (blockEntity == null) { + transactionRecord.fail("Missing block entity"); return null; } else { - TreasureChestState treasureChestState = BlockStateModule.get() - .createBlockState(TreasureChestState.class, worldChunk, conditionPosition.clone(), blockState.getBlockType()); + TreasureChestBlock treasureChestState = world.getChunkStore().getStore().getComponent(blockEntity, TreasureChestBlock.getComponentType()); if (treasureChestState == null) { - transactionRecord.fail("Failed to create TreasureChestState!"); + transactionRecord.fail("Missing TreasureChestBlock!"); return null; } else { - return treasureChestState; + ItemContainerBlock container = world.getChunkStore().getStore().getComponent(blockEntity, ItemContainerBlock.getComponentType()); + if (container == null) { + transactionRecord.fail("Missing ItemContainerBlock!"); + return null; + } else { + return blockEntity; + } } } } @@ -200,10 +213,10 @@ public class TreasureMapObjectiveTask extends ObjectiveTask { double angle = Math.random() * (float) (Math.PI * 2); float radius = MathUtil.randomFloat(chestConfig.getMinRadius(), chestConfig.getMaxRadius()); Vector3d objectivePosition = objective.getPosition(componentAccessor); - Vector3d position = objectivePosition.clone().floor(); + Vector3d position = new Vector3d(objectivePosition).floor(); position.add(radius * TrigMathUtil.cos(angle), 0.0, radius * TrigMathUtil.sin(angle)); position.y = world.getChunk(ChunkUtil.indexChunkFromBlock(position.x, position.z)).getHeight(MathUtil.floor(position.x), MathUtil.floor(position.z)); - conditionPosition = chestConfig.getWorldLocationProvider().runCondition(world, position.toVector3i()); + conditionPosition = chestConfig.getWorldLocationProvider().runCondition(world, Vector3dUtil.toVector3i(position)); } return conditionPosition; diff --git a/src/com/hypixel/hytale/builtin/adventure/objectives/transaction/SpawnTreasureChestTransactionRecord.java b/src/com/hypixel/hytale/builtin/adventure/objectives/transaction/SpawnTreasureChestTransactionRecord.java index f6f68c65..d35e3de3 100644 --- a/src/com/hypixel/hytale/builtin/adventure/objectives/transaction/SpawnTreasureChestTransactionRecord.java +++ b/src/com/hypixel/hytale/builtin/adventure/objectives/transaction/SpawnTreasureChestTransactionRecord.java @@ -1,17 +1,19 @@ package com.hypixel.hytale.builtin.adventure.objectives.transaction; -import com.hypixel.hytale.builtin.adventure.objectives.blockstates.TreasureChestState; +import com.hypixel.hytale.builtin.adventure.objectives.blockstates.TreasureChestBlock; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class SpawnTreasureChestTransactionRecord extends TransactionRecord { @Nonnull @@ -25,7 +27,7 @@ public class SpawnTreasureChestTransactionRecord extends TransactionRecord { ) .add() .append( - new KeyedCodec<>("BlockPosition", Vector3i.CODEC), + new KeyedCodec<>("BlockPosition", Vector3iUtil.CODEC), (spawnTreasureChestTransactionRecord, vector3d) -> spawnTreasureChestTransactionRecord.blockPosition = vector3d, spawnTreasureChestTransactionRecord -> spawnTreasureChestTransactionRecord.blockPosition ) @@ -47,9 +49,12 @@ public class SpawnTreasureChestTransactionRecord extends TransactionRecord { World world = Universe.get().getWorld(this.worldUUID); if (world != null) { WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(this.blockPosition.x, this.blockPosition.z)); - BlockState blockState = worldChunk.getState(this.blockPosition.x, this.blockPosition.y, this.blockPosition.z); - if (blockState instanceof TreasureChestState) { - ((TreasureChestState)blockState).setOpened(true); + Ref blockEntityRef = worldChunk.getBlockComponentEntity(this.blockPosition.x, this.blockPosition.y, this.blockPosition.z); + if (blockEntityRef != null) { + TreasureChestBlock treasureChest = blockEntityRef.getStore().getComponent(blockEntityRef, TreasureChestBlock.getComponentType()); + if (treasureChest != null) { + treasureChest.setOpened(true); + } } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/shop/GiveItemInteraction.java b/src/com/hypixel/hytale/builtin/adventure/shop/GiveItemInteraction.java index eb6433aa..58b0fee3 100644 --- a/src/com/hypixel/hytale/builtin/adventure/shop/GiveItemInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/shop/GiveItemInteraction.java @@ -7,9 +7,10 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.asset.type.item.config.Item; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.choices.ChoiceInteraction; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -56,10 +57,8 @@ public class GiveItemInteraction extends ChoiceInteraction { @Override public void run(@Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - if (playerComponent != null) { - playerComponent.getInventory().getCombinedHotbarFirst().addItemStack(new ItemStack(this.itemId, this.quantity)); - } + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(store, ref, InventoryComponent.HOTBAR_FIRST); + combinedInventory.addItemStack(new ItemStack(this.itemId, this.quantity)); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/shop/barter/BarterPage.java b/src/com/hypixel/hytale/builtin/adventure/shop/barter/BarterPage.java index cfc02d2f..6684b8eb 100644 --- a/src/com/hypixel/hytale/builtin/adventure/shop/barter/BarterPage.java +++ b/src/com/hypixel/hytale/builtin/adventure/shop/barter/BarterPage.java @@ -11,7 +11,7 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.ItemUtils; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; @@ -79,7 +79,7 @@ public class BarterPage extends InteractiveCustomUIPage stash(@Nonnull ItemContainerState containerState, boolean clearDropList) { + public static ListTransaction stash( + BlockModule.BlockStateInfo blockStateInfo, @Nonnull ItemContainerBlock containerState, boolean clearDropList + ) { String droplist = containerState.getDroplist(); if (droplist == null) { return null; @@ -61,7 +61,7 @@ public class StashPlugin extends JavaPlugin { if (stacks.isEmpty()) { return ListTransaction.getEmptyTransaction(true); } else { - ItemContainer itemContainer = containerState.getItemContainer(); + SimpleItemContainer itemContainer = containerState.getItemContainer(); short capacity = itemContainer.getCapacity(); ShortArrayList slots = new ShortArrayList(capacity); @@ -69,8 +69,13 @@ public class StashPlugin extends JavaPlugin { slots.add(s); } - Vector3i blockPosition = containerState.getBlockPosition(); - long positionHash = blockPosition.hashCode(); + WorldChunk wc = blockStateInfo.getChunkRef().getStore().getComponent(blockStateInfo.getChunkRef(), WorldChunk.getComponentType()); + int x = ChunkUtil.xFromBlockInColumn(blockStateInfo.getIndex()); + int y = ChunkUtil.yFromBlockInColumn(blockStateInfo.getIndex()); + int z = ChunkUtil.zFromBlockInColumn(blockStateInfo.getIndex()); + int worldX = ChunkUtil.worldCoordFromLocalCoord(wc.getX(), x); + int worldZ = ChunkUtil.worldCoordFromLocalCoord(wc.getZ(), z); + long positionHash = HashUtil.hash(worldX, y, worldZ); Random rnd = new Random(positionHash); ShortLists.shuffle(slots, rnd); boolean anySucceeded = false; @@ -79,8 +84,7 @@ public class StashPlugin extends JavaPlugin { short slot = slots.getShort(idx); ItemStackSlotTransaction transaction = itemContainer.addItemStackToSlot(slot, stacks.get(idx)); if (transaction.getRemainder() != null && !transaction.getRemainder().isEmpty()) { - LOGGER.at(Level.WARNING) - .log("Could not add Item to Stash at %d, %d, %d: %s", blockPosition.x, blockPosition.y, blockPosition.z, transaction.getRemainder()); + LOGGER.at(Level.WARNING).log("Could not add Item to Stash at %d, %d, %d: %s", worldX, y, worldZ, transaction.getRemainder()); } else { anySucceeded = true; } @@ -97,18 +101,20 @@ public class StashPlugin extends JavaPlugin { private static class StashSystem extends RefSystem { @Nonnull - private final ComponentType itemContainerStateComponentType; + private final ComponentType itemContainerStateComponentType; @Nonnull - private final Set> dependencies; + private final ComponentType blockStateInfoComponentType = BlockModule.BlockStateInfo.getComponentType(); + @Nonnull + private final Query query; - public StashSystem(@Nonnull ComponentType itemContainerStateComponentType) { + public StashSystem(@Nonnull ComponentType itemContainerStateComponentType) { this.itemContainerStateComponentType = itemContainerStateComponentType; - this.dependencies = Set.of(new SystemDependency<>(Order.AFTER, BlockStateModule.LegacyBlockStateRefSystem.class)); + this.query = Query.and(itemContainerStateComponentType, this.blockStateInfoComponentType); } @Override public Query getQuery() { - return this.itemContainerStateComponentType; + return this.query; } @Override @@ -117,13 +123,17 @@ public class StashPlugin extends JavaPlugin { ) { World world = store.getExternalData().getWorld(); if (world.getWorldConfig().getGameMode() != GameMode.Creative) { - ItemContainerState itemContainerStateComponent = store.getComponent(ref, this.itemContainerStateComponentType); + ItemContainerBlock itemContainerStateComponent = store.getComponent(ref, this.itemContainerStateComponentType); assert itemContainerStateComponent != null; + BlockModule.BlockStateInfo blockStateInfo = store.getComponent(ref, this.blockStateInfoComponentType); + + assert blockStateInfo != null; + StashGameplayConfig stashGameplayConfig = StashGameplayConfig.getOrDefault(world.getGameplayConfig()); boolean clearContainerDropList = stashGameplayConfig.isClearContainerDropList(); - StashPlugin.stash(itemContainerStateComponent, clearContainerDropList); + StashPlugin.stash(blockStateInfo, itemContainerStateComponent, clearContainerDropList); } } @@ -132,11 +142,5 @@ public class StashPlugin extends JavaPlugin { @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { } - - @Nonnull - @Override - public Set> getDependencies() { - return this.dependencies; - } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/teleporter/component/Teleporter.java b/src/com/hypixel/hytale/builtin/adventure/teleporter/component/Teleporter.java index 18dea2c1..cc8ea10d 100644 --- a/src/com/hypixel/hytale/builtin/adventure/teleporter/component/Teleporter.java +++ b/src/com/hypixel/hytale/builtin/adventure/teleporter/component/Teleporter.java @@ -8,10 +8,8 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.wordlist.WordList; import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport; import com.hypixel.hytale.server.core.universe.Universe; @@ -20,6 +18,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class Teleporter implements Component { @Nonnull @@ -158,7 +158,7 @@ public class Teleporter implements Component { } @Nullable - public Teleport toTeleport(@Nonnull Vector3d currentPosition, @Nonnull Vector3f currentRotation, @Nonnull Vector3i blockPosition) { + public Teleport toTeleport(@Nonnull Vector3d currentPosition, @Nonnull Rotation3f currentRotation, @Nonnull Vector3i blockPosition) { if (this.warp != null && !this.warp.isEmpty()) { Warp targetWarp = TeleportPlugin.get().getWarps().get(this.warp.toLowerCase()); return targetWarp != null ? targetWarp.toTeleport() : null; @@ -167,7 +167,7 @@ public class Teleporter implements Component { World world = Universe.get().getWorld(this.worldUuid); if (world != null) { if (this.relativeMask != 0) { - Transform teleportTransform = this.transform.clone(); + Transform teleportTransform = new Transform(this.transform); Transform.applyMaskedRelativeTransform(teleportTransform, this.relativeMask, currentPosition, currentRotation, blockPosition); return Teleport.createForPlayer(world, teleportTransform); } @@ -177,7 +177,7 @@ public class Teleporter implements Component { } if (this.relativeMask != 0) { - Transform teleportTransform = this.transform.clone(); + Transform teleportTransform = new Transform(this.transform); Transform.applyMaskedRelativeTransform(teleportTransform, this.relativeMask, currentPosition, currentRotation, blockPosition); return Teleport.createForPlayer(teleportTransform); } else { diff --git a/src/com/hypixel/hytale/builtin/adventure/teleporter/interaction/server/TeleporterInteraction.java b/src/com/hypixel/hytale/builtin/adventure/teleporter/interaction/server/TeleporterInteraction.java index 801be16b..2aa525c5 100644 --- a/src/com/hypixel/hytale/builtin/adventure/teleporter/interaction/server/TeleporterInteraction.java +++ b/src/com/hypixel/hytale/builtin/adventure/teleporter/interaction/server/TeleporterInteraction.java @@ -10,8 +10,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -32,9 +30,11 @@ import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class TeleporterInteraction extends SimpleBlockInteraction { @Nonnull @@ -99,7 +99,7 @@ public class TeleporterInteraction extends SimpleBlockInteraction { @Nonnull World world, @Nonnull CommandBuffer commandBuffer, @Nonnull InteractionContext context, @Nonnull Vector3i targetBlock ) { ChunkStore chunkStore = world.getChunkStore(); - long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.getX(), targetBlock.getZ()); + long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x(), targetBlock.z()); BlockComponentChunk blockComponentChunk = chunkStore.getChunkComponent(chunkIndex, BlockComponentChunk.getComponentType()); if (blockComponentChunk == null) { return false; @@ -171,7 +171,7 @@ public class TeleporterInteraction extends SimpleBlockInteraction { SpatialResource, EntityStore> playerSpatialResource = commandBuffer.getResource( EntityModule.get().getPlayerSpatialResourceType() ); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(particlePosition, 75.0, results); ParticleUtil.spawnParticleEffect(this.particle, particlePosition, results, commandBuffer); } @@ -196,5 +196,8 @@ public class TeleporterInteraction extends SimpleBlockInteraction { protected void simulateInteractWithBlock( @Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock ) { + if (context.getServerState() != null) { + context.getState().state = context.getServerState().state; + } } } diff --git a/src/com/hypixel/hytale/builtin/adventure/teleporter/interaction/server/UsedTeleporter.java b/src/com/hypixel/hytale/builtin/adventure/teleporter/interaction/server/UsedTeleporter.java index 4720e935..0743e6cf 100644 --- a/src/com/hypixel/hytale/builtin/adventure/teleporter/interaction/server/UsedTeleporter.java +++ b/src/com/hypixel/hytale/builtin/adventure/teleporter/interaction/server/UsedTeleporter.java @@ -3,11 +3,11 @@ package com.hypixel.hytale.builtin.adventure.teleporter.interaction.server; import com.hypixel.hytale.builtin.adventure.teleporter.TeleporterPlugin; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nullable; import org.checkerframework.checker.nullness.compatqual.NullableDecl; +import org.joml.Vector3d; public class UsedTeleporter implements Component { @Nullable @@ -58,7 +58,7 @@ public class UsedTeleporter implements Component { public Component clone() { UsedTeleporter clone = new UsedTeleporter(); clone.destinationWorldUuid = this.destinationWorldUuid; - clone.destinationPosition = this.destinationPosition.clone(); + clone.destinationPosition = new Vector3d(this.destinationPosition); clone.clearOutXZ = this.clearOutXZ; clone.clearOutXZSquared = this.clearOutXZSquared; clone.clearOutY = this.clearOutY; diff --git a/src/com/hypixel/hytale/builtin/adventure/teleporter/page/TeleporterSettingsPage.java b/src/com/hypixel/hytale/builtin/adventure/teleporter/page/TeleporterSettingsPage.java index 5880bad8..1c2bf26c 100644 --- a/src/com/hypixel/hytale/builtin/adventure/teleporter/page/TeleporterSettingsPage.java +++ b/src/com/hypixel/hytale/builtin/adventure/teleporter/page/TeleporterSettingsPage.java @@ -71,18 +71,18 @@ public class TeleporterSettingsPage extends InteractiveCustomUIPage { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/adventure/teleporter/system/CreateWarpWhenTeleporterPlacedSystem.java b/src/com/hypixel/hytale/builtin/adventure/teleporter/system/CreateWarpWhenTeleporterPlacedSystem.java index 80ebef23..4ed62c4e 100644 --- a/src/com/hypixel/hytale/builtin/adventure/teleporter/system/CreateWarpWhenTeleporterPlacedSystem.java +++ b/src/com/hypixel/hytale/builtin/adventure/teleporter/system/CreateWarpWhenTeleporterPlacedSystem.java @@ -11,9 +11,8 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.modules.block.BlockModule; @@ -29,6 +28,7 @@ import java.time.Instant; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CreateWarpWhenTeleporterPlacedSystem extends RefChangeSystem { @Nonnull @@ -107,7 +107,7 @@ public class CreateWarpWhenTeleporterPlacedSystem extends RefChangeSystem 1.0) { + if (transformComponent.getPosition().distanceSquared(ownedEmitterTransform.getPosition()) > 1.0) { ownedEmitterTransform.setPosition(transformComponent.getPosition()); } } diff --git a/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorGamePacketHandler.java b/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorGamePacketHandler.java index a8536fa0..e1ef7808 100644 --- a/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorGamePacketHandler.java +++ b/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorGamePacketHandler.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAuthorization; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorInitialize; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUpdateJsonAsset; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.io.handlers.IPacketHandler; import com.hypixel.hytale.server.core.io.handlers.SubPacketHandler; import com.hypixel.hytale.server.core.universe.PlayerRef; @@ -38,23 +37,17 @@ public class AssetEditorGamePacketHandler implements SubPacketHandler { public void handle(AssetEditorInitialize packet) { PlayerRef playerRef = this.packetHandler.getPlayerRef(); Ref ref = playerRef.getReference(); - if (ref != null && ref.isValid()) { + if (ref == null || !ref.isValid()) { + throw new RuntimeException("Unable to process AssetEditorInitialize packet. Player ref is invalid!"); + } else if (this.lacksPermission(playerRef, false)) { + this.packetHandler.getPlayerRef().getPacketHandler().write(new AssetEditorAuthorization(false)); + } else { Store store = ref.getStore(); World world = store.getExternalData().getWorld(); world.execute(() -> { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - if (this.lacksPermission(playerComponent, false)) { - this.packetHandler.getPlayerRef().getPacketHandler().write(new AssetEditorAuthorization(false)); - } else { - this.packetHandler.getPlayerRef().getPacketHandler().write(new AssetEditorAuthorization(true)); - AssetEditorPlugin.get().handleInitializeEditor(ref, store); - } + this.packetHandler.getPlayerRef().getPacketHandler().write(new AssetEditorAuthorization(true)); + AssetEditorPlugin.get().handleInitializeEditor(ref, store); }); - } else { - throw new RuntimeException("Unable to process AssetEditorInitialize packet. Player ref is invalid!"); } } @@ -62,40 +55,26 @@ public class AssetEditorGamePacketHandler implements SubPacketHandler { public void handle(@Nonnull AssetEditorUpdateJsonAsset packet) { PlayerRef playerRef = this.packetHandler.getPlayerRef(); Ref ref = playerRef.getReference(); - if (ref != null && ref.isValid()) { - Store store = ref.getStore(); - World world = store.getExternalData().getWorld(); - world.execute( + if (ref == null || !ref.isValid()) { + throw new RuntimeException("Unable to process AssetEditorUpdateJsonAsset packet. Player ref is invalid!"); + } else if (!this.lacksPermission(playerRef, true)) { + CompletableFuture.runAsync( () -> { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - if (!this.lacksPermission(playerComponent, true)) { - CompletableFuture.runAsync( - () -> { - LOGGER.at(Level.INFO).log("%s updating json asset at %s", this.packetHandler.getPlayerRef().getUsername(), packet.path); - EditorClient mockClient = new EditorClient(playerRef); - AssetEditorPlugin.get() - .handleJsonAssetUpdate( - mockClient, - packet.path != null ? new AssetPath(packet.path) : null, - packet.assetType, - packet.assetIndex, - packet.commands, - packet.token - ); - } + LOGGER.at(Level.INFO).log("%s updating json asset at %s", this.packetHandler.getPlayerRef().getUsername(), packet.path); + EditorClient mockClient = new EditorClient(playerRef); + AssetEditorPlugin.get() + .handleJsonAssetUpdate( + mockClient, packet.path != null ? new AssetPath(packet.path) : null, packet.assetType, packet.assetIndex, packet.commands, packet.token ); - } } ); - } else { - throw new RuntimeException("Unable to process AssetEditorUpdateJsonAsset packet. Player ref is invalid!"); } } - private boolean lacksPermission(@Nonnull Player player, boolean shouldShowDenialMessage) { - if (!player.hasPermission("hytale.editor.asset")) { + private boolean lacksPermission(@Nonnull PlayerRef playerRef, boolean shouldShowDenialMessage) { + if (!playerRef.hasPermission("hytale.editor.asset")) { if (shouldShowDenialMessage) { - player.sendMessage(Messages.USAGE_DENIED); + playerRef.sendMessage(Messages.USAGE_DENIED); } return true; diff --git a/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorPacketHandler.java b/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorPacketHandler.java index 8b00e1ae..df05b819 100644 --- a/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorPacketHandler.java +++ b/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorPacketHandler.java @@ -36,7 +36,7 @@ import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUpdateAsset; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUpdateAssetPack; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUpdateJsonAsset; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUpdateWeatherPreviewLock; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnect; import com.hypixel.hytale.protocol.packets.connection.DisconnectType; import com.hypixel.hytale.protocol.packets.connection.Pong; import com.hypixel.hytale.protocol.packets.interface_.UpdateLanguage; @@ -110,8 +110,8 @@ public class AssetEditorPacketHandler extends GenericPacketHandler { } public void registerHandlers() { - this.registerHandler(1, p -> this.handle((Disconnect)p)); - this.registerHandler(3, p -> this.handlePong((Pong)p)); + this.registerHandler(1, p -> this.handle((ClientDisconnect)p)); + this.registerHandler(4, p -> this.handlePong((Pong)p)); this.registerHandler(321, p -> this.handle((AssetEditorRequestChildrenList)p)); this.registerHandler(324, p -> this.handle((AssetEditorUpdateAsset)p)); this.registerHandler(323, p -> this.handle((AssetEditorUpdateJsonAsset)p)); @@ -382,12 +382,19 @@ public class AssetEditorPacketHandler extends GenericPacketHandler { public void handle(@Nonnull AssetEditorCreateAssetPack packet) { if (!this.lacksPermission(packet.token, "hytale.editor.packs.create")) { - LOGGER.at(Level.INFO).log("%s is creating a new asset pack: %s:%s", this.editorClient.getUsername(), packet.manifest.group, packet.manifest.name); - AssetEditorPlugin.get().handleCreateAssetPack(this.editorClient, packet.manifest, packet.token); + LOGGER.at(Level.INFO) + .log( + "%s is creating a new asset pack: %s:%s (directory index: %d)", + this.editorClient.getUsername(), + packet.manifest.group, + packet.manifest.name, + packet.targetDirectoryIndex + ); + AssetEditorPlugin.get().handleCreateAssetPack(this.editorClient, packet.manifest, packet.token, packet.targetDirectoryIndex); } } - public void handle(@Nonnull Disconnect packet) { + public void handle(@Nonnull ClientDisconnect packet) { switch (packet.type) { case Disconnect: this.disconnectReason.setClientDisconnectType(DisconnectType.Disconnect); @@ -403,7 +410,7 @@ public class AssetEditorPacketHandler extends GenericPacketHandler { this.editorClient.getUsername(), NettyUtil.formatRemoteAddress(this.getChannel()), packet.type.name(), - packet.reason + packet.reason.name() ); this.getChannel().close(); } diff --git a/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorPlugin.java b/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorPlugin.java index 5f8e3721..56d7b6a7 100644 --- a/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorPlugin.java +++ b/src/com/hypixel/hytale/builtin/asseteditor/AssetEditorPlugin.java @@ -56,6 +56,7 @@ import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorFetchJsonAsset import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorFileEntry; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorJsonAssetUpdated; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorLastModifiedAssets; +import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorModsDirectories; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorPopupNotificationType; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorRebuildCaches; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorRequestChildrenListReply; @@ -76,7 +77,6 @@ import com.hypixel.hytale.server.core.Options; import com.hypixel.hytale.server.core.asset.AssetModule; import com.hypixel.hytale.server.core.asset.AssetPackRegisterEvent; import com.hypixel.hytale.server.core.asset.AssetPackUnregisterEvent; -import com.hypixel.hytale.server.core.asset.AssetRegistryLoader; import com.hypixel.hytale.server.core.asset.common.events.CommonAssetMonitorEvent; import com.hypixel.hytale.server.core.config.ModConfig; import com.hypixel.hytale.server.core.io.PacketHandler; @@ -88,6 +88,7 @@ import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.plugin.PluginManager; import com.hypixel.hytale.server.core.plugin.PluginState; +import com.hypixel.hytale.server.core.schema.SchemaGenerator; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.BsonUtil; @@ -215,7 +216,9 @@ public class AssetEditorPlugin extends JavaPlugin { @Override protected void shutdown() { InitialPacketHandler.EDITOR_PACKET_HANDLER_SUPPLIER = null; - String message = HytaleServer.get().isShuttingDown() ? "Server is shutting down!" : "Asset editor was disabled!"; + Message message = HytaleServer.get().isShuttingDown() + ? Message.translation("server.general.disconnect.stoppingServer") + : Message.translation("server.general.disconnect.assetEditorDisabled"); for (Set clients : this.uuidToEditorClients.values()) { for (EditorClient client : clients) { @@ -276,7 +279,7 @@ public class AssetEditorPlugin extends JavaPlugin { client.getPacketHandler().sendPing(); } catch (Exception var6) { this.getLogger().at(Level.SEVERE).withCause(var6).log("Failed to send ping to " + client); - client.getPacketHandler().disconnect("Exception when sending ping packet!"); + client.getPacketHandler().disconnect(Message.translation("server.general.disconnect.pingException")); } } } @@ -578,7 +581,7 @@ public class AssetEditorPlugin extends JavaPlugin { private void initializeAssetEditor(boolean updateLoadedAssets) { long start = System.nanoTime(); - Map schemas = AssetRegistryLoader.generateSchemas(new SchemaContext(), new BsonDocument()); + Map schemas = SchemaGenerator.generateAssetSchemas(); schemas.remove("NPCRole.json"); schemas.remove("other.json"); AssetEditorSetupSchemas setupSchemasPacket = new AssetEditorSetupSchemas(new SchemaFile[schemas.size()]); @@ -650,6 +653,16 @@ public class AssetEditorPlugin extends JavaPlugin { } editorClient.getPacketHandler().write(packSetupPacket); + if (canCreateAssetPacks) { + List modsDirectories = this.getModsDirectories(); + String[] dirStrings = new String[modsDirectories.size()]; + + for (int j = 0; j < modsDirectories.size(); j++) { + dirStrings[j] = modsDirectories.get(j).toString(); + } + + editorClient.getPacketHandler().write(new AssetEditorModsDirectories(dirStrings)); + } for (DataSource dataSource : this.assetPackDataSources.values()) { dataSource.getAssetTree().sendPackets(editorClient); @@ -824,7 +837,19 @@ public class AssetEditorPlugin extends JavaPlugin { } } - public void handleCreateAssetPack(@Nonnull EditorClient editorClient, @Nonnull AssetPackManifest packetManifest, int requestToken) { + @Nonnull + private List getModsDirectories() { + List directories = new ObjectArrayList<>(); + directories.add(PluginManager.MODS_PATH.toAbsolutePath()); + + for (Path modsPath : Options.getOptionSet().valuesOf(Options.MODS_DIRECTORIES)) { + directories.add(modsPath.toAbsolutePath()); + } + + return directories; + } + + public void handleCreateAssetPack(@Nonnull EditorClient editorClient, @Nonnull AssetPackManifest packetManifest, int requestToken, int targetDirectoryIndex) { if (packetManifest.name == null || packetManifest.name.isEmpty()) { editorClient.sendFailureReply(requestToken, Messages.PACK_NAME_REQUIRED); } else if (packetManifest.group != null && !packetManifest.group.isEmpty()) { @@ -842,8 +867,8 @@ public class AssetEditorPlugin extends JavaPlugin { if (packetManifest.version != null && !packetManifest.version.isEmpty()) { try { manifest.setVersion(Semver.fromString(packetManifest.version)); - } catch (IllegalArgumentException var13) { - this.getLogger().at(Level.WARNING).withCause(var13).log("Invalid version format: %s", packetManifest.version); + } catch (IllegalArgumentException var15) { + this.getLogger().at(Level.WARNING).withCause(var15).log("Invalid version format: %s", packetManifest.version); editorClient.sendFailureReply(requestToken, Messages.INVALID_VERSION_FORMAT); return; } @@ -868,39 +893,44 @@ public class AssetEditorPlugin extends JavaPlugin { if (this.assetPackDataSources.containsKey(packId)) { editorClient.sendFailureReply(requestToken, Messages.PACK_ALREADY_EXISTS); } else { - Path modsPath = PluginManager.MODS_PATH; - String dirName = AssetPathUtil.removeInvalidFileNameChars( - packetManifest.group != null ? packetManifest.group + "." + packetManifest.name : packetManifest.name - ); - Path normalized = Path.of(dirName).normalize(); - if (AssetPathUtil.isInvalidFileName(normalized)) { - editorClient.sendFailureReply(requestToken, Messages.INVALID_FILE_NAME); - } else { - Path packPath = modsPath.resolve(normalized).normalize(); - if (!packPath.startsWith(modsPath)) { - editorClient.sendFailureReply(requestToken, Messages.PACK_OUTSIDE_DIRECTORY); - } else if (Files.exists(packPath)) { - editorClient.sendFailureReply(requestToken, Messages.PACK_ALREADY_EXISTS_AT_PATH); + List modsDirectories = this.getModsDirectories(); + if (targetDirectoryIndex >= 0 && targetDirectoryIndex < modsDirectories.size()) { + Path modsPath = modsDirectories.get(targetDirectoryIndex); + String dirName = AssetPathUtil.removeInvalidFileNameChars( + packetManifest.group != null ? packetManifest.group + "." + packetManifest.name : packetManifest.name + ); + Path normalized = Path.of(dirName).normalize(); + if (AssetPathUtil.isInvalidFileName(normalized)) { + editorClient.sendFailureReply(requestToken, Messages.INVALID_FILE_NAME); } else { - try { - Files.createDirectories(packPath); - Path manifestPath = packPath.resolve("manifest.json"); - BsonUtil.writeSync(manifestPath, PluginManifest.CODEC, manifest, this.getLogger()); - HytaleServerConfig serverConfig = HytaleServer.get().getConfig(); - HytaleServerConfig.setBoot(serverConfig, new PluginIdentifier(manifest), true); - serverConfig.markChanged(); - if (serverConfig.consumeHasChanged()) { - HytaleServerConfig.save(serverConfig).join(); - } + Path packPath = modsPath.resolve(normalized).normalize(); + if (!packPath.startsWith(modsPath)) { + editorClient.sendFailureReply(requestToken, Messages.PACK_OUTSIDE_DIRECTORY); + } else if (Files.exists(packPath)) { + editorClient.sendFailureReply(requestToken, Messages.PACK_ALREADY_EXISTS_AT_PATH); + } else { + try { + Files.createDirectories(packPath); + Path manifestPath = packPath.resolve("manifest.json"); + BsonUtil.writeSync(manifestPath, PluginManifest.CODEC, manifest, this.getLogger()); + HytaleServerConfig serverConfig = HytaleServer.get().getConfig(); + HytaleServerConfig.setBoot(serverConfig, new PluginIdentifier(manifest), true); + serverConfig.markChanged(); + if (serverConfig.consumeHasChanged()) { + HytaleServerConfig.save(serverConfig).join(); + } - AssetModule.get().registerPack(packId, packPath, manifest, false); - editorClient.sendSuccessReply(requestToken, Messages.PACK_CREATED); - this.getLogger().at(Level.INFO).log("Created new pack: %s at %s", packId, packPath); - } catch (IOException var12) { - this.getLogger().at(Level.SEVERE).withCause(var12).log("Failed to create pack %s", packId); - editorClient.sendFailureReply(requestToken, Messages.PACK_CREATION_FAILED); + AssetModule.get().registerPack(packId, packPath, manifest, false); + editorClient.sendSuccessReply(requestToken, Messages.PACK_CREATED); + this.getLogger().at(Level.INFO).log("Created new pack: %s at %s", packId, packPath); + } catch (IOException var14) { + this.getLogger().at(Level.SEVERE).withCause(var14).log("Failed to create pack %s", packId); + editorClient.sendFailureReply(requestToken, Messages.PACK_CREATION_FAILED); + } } } + } else { + editorClient.sendFailureReply(requestToken, Messages.INVALID_TARGET_DIRECTORY); } } } else { diff --git a/src/com/hypixel/hytale/builtin/asseteditor/AssetSpecificFunctionality.java b/src/com/hypixel/hytale/builtin/asseteditor/AssetSpecificFunctionality.java index 4c3b1f5e..d379d96d 100644 --- a/src/com/hypixel/hytale/builtin/asseteditor/AssetSpecificFunctionality.java +++ b/src/com/hypixel/hytale/builtin/asseteditor/AssetSpecificFunctionality.java @@ -20,8 +20,6 @@ import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.protocol.InstantData; import com.hypixel.hytale.protocol.Model; -import com.hypixel.hytale.protocol.Vector2f; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorPopupNotificationType; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorPreviewCameraSettings; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUpdateModelPreview; @@ -38,7 +36,9 @@ import com.hypixel.hytale.server.core.asset.type.item.config.ItemArmor; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; import com.hypixel.hytale.server.core.asset.type.weather.config.Weather; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.player.PlayerSkinComponent; @@ -60,6 +60,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2f; +import org.joml.Vector3f; @Deprecated public class AssetSpecificFunctionality { @@ -214,21 +216,35 @@ public class AssetSpecificFunctionality { private static void equipItem(@Nonnull Path assetPath, @Nonnull EditorClient editorClient) { PlayerRef playerRef = tryGetPlayer(editorClient); - if (playerRef != null) { - Player player = playerRef.getComponent(Player.getComponentType()); - String key = Item.getAssetStore().decodeFilePathKey(assetPath); - Item item = Item.getAssetMap().getAsset(key); - if (item == null) { - editorClient.sendPopupNotification( - AssetEditorPopupNotificationType.Error, Message.translation("server.assetEditor.messages.unknownItem").param("id", key.toString()) + if (playerRef != null && playerRef.isValid()) { + Ref ref = playerRef.getReference(); + if (ref != null && ref.isValid()) { + Store store = ref.getStore(); + World world = store.getExternalData().getWorld(); + world.execute( + () -> { + String key = Item.getAssetStore().decodeFilePathKey(assetPath); + if (key != null) { + Item item = Item.getAssetMap().getAsset(key); + if (item == null) { + editorClient.sendPopupNotification( + AssetEditorPopupNotificationType.Error, Message.translation("server.assetEditor.messages.unknownItem").param("id", key) + ); + } else { + ItemArmor itemArmor = item.getArmor(); + if (itemArmor != null) { + InventoryComponent.Armor armorComponent = store.getComponent(ref, InventoryComponent.Armor.getComponentType()); + if (armorComponent != null) { + armorComponent.getInventory().setItemStackForSlot((short)itemArmor.getArmorSlot().ordinal(), new ItemStack(key)); + } + } else { + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(store, ref, InventoryComponent.HOTBAR_FIRST); + combinedInventory.addItemStack(new ItemStack(key)); + } + } + } + } ); - } else { - ItemArmor itemArmor = item.getArmor(); - if (itemArmor != null) { - player.getInventory().getArmor().setItemStackForSlot((short)itemArmor.getArmorSlot().ordinal(), new ItemStack(key)); - } else { - player.getInventory().getCombinedHotbarFirst().addItemStack(new ItemStack(key)); - } } } } diff --git a/src/com/hypixel/hytale/builtin/asseteditor/Messages.java b/src/com/hypixel/hytale/builtin/asseteditor/Messages.java index c152158d..28ed6d07 100644 --- a/src/com/hypixel/hytale/builtin/asseteditor/Messages.java +++ b/src/com/hypixel/hytale/builtin/asseteditor/Messages.java @@ -90,4 +90,6 @@ public class Messages { public static final Message PACK_GROUP_REQUIRED = Message.translation("server.assetEditor.messages.packGroupRequired"); @Nonnull public static final Message PACK_ALREADY_EXISTS = Message.translation("server.assetEditor.messages.packAlreadyExists"); + @Nonnull + public static final Message INVALID_TARGET_DIRECTORY = Message.translation("server.assetEditor.messages.invalidTargetDirectory"); } diff --git a/src/com/hypixel/hytale/builtin/beds/interactions/BedInteraction.java b/src/com/hypixel/hytale/builtin/beds/interactions/BedInteraction.java index f46d563c..c0337f04 100644 --- a/src/com/hypixel/hytale/builtin/beds/interactions/BedInteraction.java +++ b/src/com/hypixel/hytale/builtin/beds/interactions/BedInteraction.java @@ -14,8 +14,6 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.Message; @@ -39,6 +37,8 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class BedInteraction extends SimpleBlockInteraction { @Nonnull @@ -90,16 +90,16 @@ public class BedInteraction extends SimpleBlockInteraction { boolean isOwner = playerUuid.equals(ownerUUID); if (isOwner) { BlockPosition targetBlockPosition = context.getMetaStore().getMetaObject(TARGET_BLOCK_RAW); - Vector3f whereWasHit = new Vector3f(targetBlockPosition.x + 0.5F, targetBlockPosition.y + 0.5F, targetBlockPosition.z + 0.5F); + Vector3d whereWasHit = new Vector3d(targetBlockPosition.x + 0.5, targetBlockPosition.y + 0.5, targetBlockPosition.z + 0.5); BlockMountAPI.BlockMountResult result = BlockMountAPI.mountOnBlock(ref, commandBuffer, pos, whereWasHit); if (result instanceof BlockMountAPI.DidNotMount) { - playerComponent.sendMessage(Message.translation("server.interactions.didNotMount").param("state", result.toString())); + playerRefComponent.sendMessage(Message.translation("server.interactions.didNotMount").param("state", result.toString())); } else if (result instanceof BlockMountAPI.Mounted) { commandBuffer.putComponent(ref, PlayerSomnolence.getComponentType(), PlayerSleep.NoddingOff.createComponent()); commandBuffer.run(s -> SleepNotificationSystem.maybeDoNotification(s, false)); } } else if (ownerUUID != null) { - playerComponent.sendMessage(MESSAGE_SERVER_CUSTOM_UI_RESPAWN_POINT_CLAIMED); + playerRefComponent.sendMessage(MESSAGE_SERVER_CUSTOM_UI_RESPAWN_POINT_CLAIMED); } else { PlayerRespawnPointData[] respawnPoints = playerComponent.getPlayerConfigData() .getPerWorldData(world.getName()) @@ -148,9 +148,7 @@ public class BedInteraction extends SimpleBlockInteraction { for (int i = 0; i < respawnPoints.length; i++) { PlayerRespawnPointData respawnPoint = respawnPoints[i]; Vector3i respawnPointPosition = respawnPoint.getBlockPosition(); - if (respawnPointPosition.distanceTo(currentRespawnPointPosition.x, respawnPointPosition.y, currentRespawnPointPosition.z) < radiusLimitRespawnPoint - ) - { + if (respawnPointPosition.distance(currentRespawnPointPosition.x, respawnPointPosition.y, currentRespawnPointPosition.z) < radiusLimitRespawnPoint) { nearbyRespawnPointList.add(respawnPoint); } } diff --git a/src/com/hypixel/hytale/builtin/beds/respawn/OverrideNearbyRespawnPointPage.java b/src/com/hypixel/hytale/builtin/beds/respawn/OverrideNearbyRespawnPointPage.java index c0f995b6..43a2f877 100644 --- a/src/com/hypixel/hytale/builtin/beds/respawn/OverrideNearbyRespawnPointPage.java +++ b/src/com/hypixel/hytale/builtin/beds/respawn/OverrideNearbyRespawnPointPage.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.builtin.beds.respawn; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.Page; @@ -17,6 +16,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.meta.state.RespawnBlock; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class OverrideNearbyRespawnPointPage extends RespawnPointPage { @Nonnull @@ -53,7 +53,7 @@ public class OverrideNearbyRespawnPointPage extends RespawnPointPage { if (headRotationComponent != null) { PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType()); if (playerRefComponent != null) { - double direction = Math.toDegrees(headRotationComponent.getRotation().getYaw()); + double direction = Math.toDegrees(headRotationComponent.getRotation().yaw()); commandBuilder.set( "#DescriptionLabel.Text", Message.translation("server.customUI.overrideNearbyRespawnPoint.label") @@ -68,8 +68,7 @@ public class OverrideNearbyRespawnPointPage extends RespawnPointPage { commandBuilder.set(selector + ".Disabled", true); commandBuilder.set(selector + " #Name.Text", nearbyRespawnPoint.getName()); Vector3i nearbyRespawnPointPosition = nearbyRespawnPoint.getBlockPosition(); - int distance = (int)this.respawnPointPosition - .distanceTo(nearbyRespawnPointPosition.x, this.respawnPointPosition.y, nearbyRespawnPointPosition.z); + int distance = (int)this.respawnPointPosition.distance(nearbyRespawnPointPosition.x, this.respawnPointPosition.y, nearbyRespawnPointPosition.z); commandBuilder.set(selector + " #Distance.Text", Message.translation("server.customUI.respawnPointDistance").param("distance", distance)); double angle = Math.atan2(nearbyRespawnPointPosition.z - this.respawnPointPosition.z, nearbyRespawnPointPosition.x - this.respawnPointPosition.x); commandBuilder.set(selector + " #Icon.Angle", Math.toDegrees(angle) + direction + 90.0); diff --git a/src/com/hypixel/hytale/builtin/beds/respawn/RespawnPointPage.java b/src/com/hypixel/hytale/builtin/beds/respawn/RespawnPointPage.java index 4042c536..83f2d2d4 100644 --- a/src/com/hypixel/hytale/builtin/beds/respawn/RespawnPointPage.java +++ b/src/com/hypixel/hytale/builtin/beds/respawn/RespawnPointPage.java @@ -8,8 +8,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.Page; @@ -31,6 +29,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public abstract class RespawnPointPage extends InteractiveCustomUIPage { @Nonnull @@ -70,7 +70,7 @@ public abstract class RespawnPointPage extends InteractiveCustomUIPage("Weight", Codec.DOUBLE), (entry, d) -> entry.weight = d, entry -> entry.weight) .add() - .append( - new KeyedCodec<>("State", Codec.BSON_DOCUMENT), - (entry, wrapper, extraInfo) -> entry.blockComponents = SelectionPrefabSerializer.legacyStateDecode(wrapper), - (entry, extraInfo) -> null - ) - .add() .append( new KeyedCodec<>("Components", new StoredCodec<>(ChunkStore.HOLDER_CODEC_KEY)), (entry, holder) -> entry.blockComponents = holder, diff --git a/src/com/hypixel/hytale/builtin/blockspawner/BlockSpawnerPlugin.java b/src/com/hypixel/hytale/builtin/blockspawner/BlockSpawnerPlugin.java index 1896fccd..5dbc353f 100644 --- a/src/com/hypixel/hytale/builtin/blockspawner/BlockSpawnerPlugin.java +++ b/src/com/hypixel/hytale/builtin/blockspawner/BlockSpawnerPlugin.java @@ -12,7 +12,6 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.component.data.unknown.UnknownComponents; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.logger.HytaleLogger; @@ -26,6 +25,7 @@ import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.asset.type.blocktype.config.VariantRotation; import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList; import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; @@ -35,7 +35,6 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import java.util.logging.Level; import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class BlockSpawnerPlugin extends JavaPlugin { @Nonnull @@ -62,12 +61,11 @@ public class BlockSpawnerPlugin extends JavaPlugin { .setPath("Item/Block/Spawners")) .setCodec(BlockSpawnerTable.CODEC)) .setKeyFunction(BlockSpawnerTable::getId)) - .loadsAfter(Item.class, BlockType.class)) + .loadsAfter(Item.class, BlockType.class, ItemDropList.class)) .build() ); this.blockSpawnerComponentType = this.getChunkStoreRegistry().registerComponent(BlockSpawner.class, "BlockSpawner", BlockSpawner.CODEC); this.getChunkStoreRegistry().registerSystem(new BlockSpawnerPlugin.BlockSpawnerSystem()); - this.getChunkStoreRegistry().registerSystem(new BlockSpawnerPlugin.MigrateBlockSpawner()); this.getEventRegistry().registerGlobal(PrefabBufferValidator.ValidateBlockEvent.class, BlockSpawnerPlugin::validatePrefabBlock); } @@ -202,7 +200,7 @@ public class BlockSpawnerPlugin extends JavaPlugin { BlockType blockType = BlockType.getAssetMap().getAsset(blockId); worldChunkComponent.setBlock(x, y, z, blockId, blockType, rotation.index(), 0, flags); if (holder != null) { - worldChunkComponent.setState(x, y, z, holder.clone()); + worldChunkComponent.setState(x, y, z, blockType, rotation.index(), holder.clone()); } }); } @@ -219,29 +217,4 @@ public class BlockSpawnerPlugin extends JavaPlugin { ) { } } - - @Deprecated(forRemoval = true) - public static class MigrateBlockSpawner extends BlockModule.MigrationSystem { - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - UnknownComponents unknown = holder.getComponent(ChunkStore.REGISTRY.getUnknownComponentType()); - - assert unknown != null; - - BlockSpawner blockSpawner = unknown.removeComponent("blockspawner", BlockSpawner.CODEC); - if (blockSpawner != null) { - holder.putComponent(BlockSpawner.getComponentType(), blockSpawner); - } - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nullable - @Override - public Query getQuery() { - return ChunkStore.REGISTRY.getUnknownComponentType(); - } - } } diff --git a/src/com/hypixel/hytale/builtin/blockspawner/command/BlockSpawnerGetCommand.java b/src/com/hypixel/hytale/builtin/blockspawner/command/BlockSpawnerGetCommand.java index 28387395..838ba5e4 100644 --- a/src/com/hypixel/hytale/builtin/blockspawner/command/BlockSpawnerGetCommand.java +++ b/src/com/hypixel/hytale/builtin/blockspawner/command/BlockSpawnerGetCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.builtin.blockspawner.state.BlockSpawner; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BlockSpawnerGetCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/blockspawner/command/BlockSpawnerSetCommand.java b/src/com/hypixel/hytale/builtin/blockspawner/command/BlockSpawnerSetCommand.java index a0ec6065..6ce7d3c7 100644 --- a/src/com/hypixel/hytale/builtin/blockspawner/command/BlockSpawnerSetCommand.java +++ b/src/com/hypixel/hytale/builtin/blockspawner/command/BlockSpawnerSetCommand.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.builtin.blockspawner.state.BlockSpawner; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -23,6 +22,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BlockSpawnerSetCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/blocktick/BlockTickPlugin.java b/src/com/hypixel/hytale/builtin/blocktick/BlockTickPlugin.java index 6c8ca34c..d68e6c2a 100644 --- a/src/com/hypixel/hytale/builtin/blocktick/BlockTickPlugin.java +++ b/src/com/hypixel/hytale/builtin/blocktick/BlockTickPlugin.java @@ -1,6 +1,5 @@ package com.hypixel.hytale.builtin.blocktick; -import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.builtin.blocktick.procedure.BasicChanceBlockGrowthProcedure; import com.hypixel.hytale.builtin.blocktick.procedure.SplitChanceBlockGrowthProcedure; import com.hypixel.hytale.builtin.blocktick.system.ChunkBlockTickSystem; @@ -19,6 +18,9 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.events.ChunkPreLoadProcessEvent; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import java.util.function.IntConsumer; import javax.annotation.Nonnull; public class BlockTickPlugin extends JavaPlugin implements IBlockTickProvider { @@ -59,7 +61,7 @@ public class BlockTickPlugin extends JavaPlugin implements IBlockTickProvider { if (!this.isEnabled()) { return 0; } else { - BlockChunk blockChunkComponent = worldChunk.getBlockChunk(); + BlockChunk blockChunkComponent = holder.getComponent(BlockChunk.getComponentType()); if (blockChunkComponent != null && blockChunkComponent.consumeNeedsPhysics()) { ChunkColumn chunkColumnComponent = holder.getComponent(ChunkColumn.getComponentType()); if (chunkColumnComponent == null) { @@ -69,25 +71,26 @@ public class BlockTickPlugin extends JavaPlugin implements IBlockTickProvider { if (sections == null) { return 0; } else { - BlockTypeAssetMap assetMap = BlockType.getAssetMap(); + BlockTickPlugin.Preprocessor preprocessor = BlockTickPlugin.Preprocessor.LOCAL.get(); int count = 0; for (int i = 0; i < sections.length; i++) { Holder sectionHolder = sections[i]; - BlockSection blockSectionComponent = sectionHolder.ensureAndGetComponent(BlockSection.getComponentType()); - if (!blockSectionComponent.isSolidAir()) { - for (int blockIdx = 0; blockIdx < 32768; blockIdx++) { - int blockId = blockSectionComponent.get(blockIdx); - BlockType blockType = assetMap.getAsset(blockId); - if (blockType != null && blockType.getTickProcedure() != null) { - blockSectionComponent.setTicking(blockIdx, true); - blockChunkComponent.markNeedsSaving(); - count++; - } + BlockSection section = sectionHolder.ensureAndGetComponent(BlockSection.getComponentType()); + if (!section.isSolidAir()) { + preprocessor.clear(); + section.forEachValue(preprocessor.typeCollector); + if (!preprocessor.tickingIds.isEmpty()) { + section.find(preprocessor.tickingIds, preprocessor.indexCollector); + count += section.setTicking(preprocessor.tickingIndices, true); } } } + if (count > 0) { + blockChunkComponent.markNeedsSaving(); + } + return count; } } @@ -96,4 +99,28 @@ public class BlockTickPlugin extends JavaPlugin implements IBlockTickProvider { } } } + + public static final class Preprocessor { + public static final ThreadLocal LOCAL = ThreadLocal.withInitial(BlockTickPlugin.Preprocessor::new); + public final IntList tickingIds = new IntArrayList(); + public final IntList tickingIndices = new IntArrayList(); + public final IntConsumer typeCollector = this::collectType; + public final IntConsumer indexCollector = this::collectIndex; + + public void clear() { + this.tickingIds.clear(); + this.tickingIndices.clear(); + } + + private void collectType(int value) { + BlockType type = BlockType.getAssetMap().getAsset(value); + if (type != null && type.getTickProcedure() != null) { + this.tickingIds.add(value); + } + } + + private void collectIndex(int index) { + this.tickingIndices.add(index); + } + } } diff --git a/src/com/hypixel/hytale/builtin/blocktick/system/ChunkBlockTickSystem.java b/src/com/hypixel/hytale/builtin/blocktick/system/ChunkBlockTickSystem.java index 3016e5cd..1f1e5b61 100644 --- a/src/com/hypixel/hytale/builtin/blocktick/system/ChunkBlockTickSystem.java +++ b/src/com/hypixel/hytale/builtin/blocktick/system/ChunkBlockTickSystem.java @@ -72,11 +72,15 @@ public class ChunkBlockTickSystem { @Nonnull private static final ComponentType COMPONENT_TYPE_WORLD_CHUNK = WorldChunk.getComponentType(); @Nonnull + private static final ComponentType COMPONENT_TYPE_BLOCK_CHUNK = BlockChunk.getComponentType(); + @Nonnull + private static final Query QUERY = Query.and(COMPONENT_TYPE_WORLD_CHUNK, COMPONENT_TYPE_BLOCK_CHUNK); + @Nonnull private static final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, ChunkBlockTickSystem.PreTick.class)); @Override public Query getQuery() { - return COMPONENT_TYPE_WORLD_CHUNK; + return QUERY; } @Nonnull @@ -98,29 +102,26 @@ public class ChunkBlockTickSystem { assert worldChunkComponent != null; + BlockChunk blockChunk = archetypeChunk.getComponent(index, COMPONENT_TYPE_BLOCK_CHUNK); + + assert blockChunk != null; + try { - tick(reference, worldChunkComponent); - } catch (Throwable var9) { - ChunkBlockTickSystem.LOGGER.at(Level.SEVERE).withCause(var9).log("Failed to tick chunk: %s", worldChunkComponent); + tick(reference, blockChunk, worldChunkComponent); + } catch (Throwable var10) { + ChunkBlockTickSystem.LOGGER.at(Level.SEVERE).withCause(var10).log("Failed to tick chunk: %s", worldChunkComponent); } } - protected static void tick(@Nonnull Ref ref, @Nonnull WorldChunk worldChunk) { - BlockChunk blockChunkComponent = worldChunk.getBlockChunk(); - if (blockChunkComponent == null) { - ChunkBlockTickSystem.LOGGER - .at(Level.WARNING) - .log("World chunk (%d, %d) has no BlockChunk component, skipping block ticking.", worldChunk.getX(), worldChunk.getZ()); - } else { - int ticked = blockChunkComponent.forEachTicking(ref, worldChunk, (r, c, localX, localY, localZ, blockId) -> { - World world = c.getWorld(); - int blockX = c.getX() << 5 | localX; - int blockZ = c.getZ() << 5 | localZ; - return tickProcedure(world, c, blockX, localY, blockZ, blockId); - }); - if (ticked > 0) { - ChunkBlockTickSystem.LOGGER.at(Level.FINER).log("Ticked %d blocks in chunk (%d, %d)", ticked, worldChunk.getX(), worldChunk.getZ()); - } + protected static void tick(@Nonnull Ref ref, @Nonnull BlockChunk blockChunkComponent, @Nonnull WorldChunk worldChunk) { + int ticked = blockChunkComponent.forEachTicking(ref, worldChunk, (r, c, localX, localY, localZ, blockId) -> { + World world = c.getWorld(); + int blockX = c.getX() << 5 | localX; + int blockZ = c.getZ() << 5 | localZ; + return tickProcedure(world, c, blockX, localY, blockZ, blockId); + }); + if (ticked > 0) { + ChunkBlockTickSystem.LOGGER.at(Level.FINER).log("Ticked %d blocks in chunk (%d, %d)", ticked, worldChunk.getX(), worldChunk.getZ()); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsPacketHandler.java b/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsPacketHandler.java index 1c12ef5e..79852f4e 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsPacketHandler.java +++ b/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsPacketHandler.java @@ -3,15 +3,14 @@ package com.hypixel.hytale.builtin.buildertools; import com.hypixel.hytale.builtin.buildertools.commands.CopyCommand; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSession; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionManager; +import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditingMetadata; import com.hypixel.hytale.builtin.buildertools.tooloperations.ToolOperation; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.ColorLight; import com.hypixel.hytale.protocol.InteractionType; @@ -26,6 +25,7 @@ import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolGeneralAction import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolLineAction; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolPasteClipboard; +import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolResetClipboardRotation; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolRotateClipboard; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolSelectionToolAskForClipboard; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolSelectionToolReplyWithClipboard; @@ -39,14 +39,16 @@ import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolSetEntityTran import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolSetNPCDebug; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolSetTransformationModeState; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolStackArea; +import com.hypixel.hytale.protocol.packets.buildertools.ClipboardEntityChange; +import com.hypixel.hytale.protocol.packets.buildertools.PrefabSetAnchor; import com.hypixel.hytale.protocol.packets.buildertools.PrefabUnselectPrefab; import com.hypixel.hytale.protocol.packets.interface_.BlockChange; import com.hypixel.hytale.protocol.packets.interface_.EditorBlocksChange; import com.hypixel.hytale.protocol.packets.interface_.FluidChange; +import com.hypixel.hytale.protocol.packets.interface_.NotificationStyle; import com.hypixel.hytale.protocol.packets.player.LoadHotbar; import com.hypixel.hytale.protocol.packets.player.SaveHotbar; import com.hypixel.hytale.server.core.Message; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.BrushData; import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderTool; import com.hypixel.hytale.server.core.command.commands.world.entity.EntityCloneCommand; import com.hypixel.hytale.server.core.command.commands.world.entity.EntityRemoveCommand; @@ -75,6 +77,7 @@ import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.core.util.NotificationUtil; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -84,6 +87,8 @@ import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.joml.Quaterniond; +import org.joml.Vector3d; +import org.joml.Vector3i; public class BuilderToolsPacketHandler implements SubPacketHandler { @Nonnull @@ -123,7 +128,7 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { @Override public void registerHandlers() { if (BuilderToolsPlugin.get().isDisabled()) { - this.packetHandler.registerNoOpHandlers(400, 401, 412, 409, 403, 406, 407, 413, 414, 417); + this.packetHandler.registerNoOpHandlers(400, 401, 412, 409, 403, 406, 427, 407, 413, 414, 417, 426); } else { IWorldPacketHandler.registerHandler(this.packetHandler, 106, this::handleLoadHotbar, BuilderToolsPacketHandler::hasPermission); IWorldPacketHandler.registerHandler(this.packetHandler, 107, this::handleSaveHotbar, BuilderToolsPacketHandler::hasPermission); @@ -134,6 +139,7 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { this.packetHandler, 408, this::handleBuilderToolSetTransformationModeState, BuilderToolsPacketHandler::hasPermission ); IWorldPacketHandler.registerHandler(this.packetHandler, 417, this::handlePrefabUnselectPrefab, BuilderToolsPacketHandler::hasPermission); + IWorldPacketHandler.registerHandler(this.packetHandler, 426, this::handlePrefabSetAnchor, BuilderToolsPacketHandler::hasPermission); IWorldPacketHandler.registerHandler(this.packetHandler, 421, this::handleBuilderToolSetEntityPickupEnabled, BuilderToolsPacketHandler::hasPermission); IWorldPacketHandler.registerHandler(this.packetHandler, 422, this::handleBuilderToolSetEntityLight, BuilderToolsPacketHandler::hasPermission); IWorldPacketHandler.registerHandler(this.packetHandler, 423, this::handleBuilderToolSetNPCDebug, BuilderToolsPacketHandler::hasPermission); @@ -148,6 +154,9 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { IWorldPacketHandler.registerHandler( this.packetHandler, 406, this::handleBuilderToolRotateClipboard, p -> hasPermission(p, "hytale.editor.selection.clipboard") ); + IWorldPacketHandler.registerHandler( + this.packetHandler, 427, this::handleBuilderToolResetClipboardRotation, p -> hasPermission(p, "hytale.editor.selection.clipboard") + ); IWorldPacketHandler.registerHandler( this.packetHandler, 407, this::handleBuilderToolPasteClipboard, p -> hasPermission(p, "hytale.editor.selection.clipboard") ); @@ -215,33 +224,30 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { @Nonnull World world, @Nonnull Store store ) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - if (playerComponent != null) { - int targetId = packet.entityId; - Ref targetRef = world.getEntityStore().getRefFromNetworkId(targetId); - if (targetRef != null && targetRef.isValid()) { - Player targetPlayerComponent = store.getComponent(targetRef, Player.getComponentType()); - if (targetPlayerComponent != null) { - playerComponent.sendMessage(Message.translation("server.builderTools.entityTool.cannotTargetPlayer")); - } else { - LOGGER.at(Level.INFO).log("%s: %s", this.packetHandler.getIdentifier(), packet); - switch (packet.action) { - case Freeze: - UUIDComponent uuidComponent = store.getComponent(targetRef, UUIDComponent.getComponentType()); - if (uuidComponent != null) { - CommandManager.get().handleCommand(playerComponent, "npc freeze --toggle --entity " + uuidComponent.getUuid()); - } - break; - case Clone: - world.execute(() -> EntityCloneCommand.cloneEntity(playerComponent, targetRef, store)); - break; - case Remove: - world.execute(() -> EntityRemoveCommand.removeEntity(ref, targetRef, store)); - } - } + int targetId = packet.entityId; + Ref targetRef = world.getEntityStore().getRefFromNetworkId(targetId); + if (targetRef != null && targetRef.isValid()) { + Player targetPlayerComponent = store.getComponent(targetRef, Player.getComponentType()); + if (targetPlayerComponent != null) { + playerRef.sendMessage(Message.translation("server.builderTools.entityTool.cannotTargetPlayer")); } else { - playerComponent.sendMessage(Message.translation("server.general.entityNotFound").param("id", targetId)); + LOGGER.at(Level.INFO).log("%s: %s", this.packetHandler.getIdentifier(), packet); + switch (packet.action) { + case Freeze: + UUIDComponent uuidComponent = store.getComponent(targetRef, UUIDComponent.getComponentType()); + if (uuidComponent != null) { + CommandManager.get().handleCommand(playerRef, "npc freeze --toggle --entity " + uuidComponent.getUuid()); + } + break; + case Clone: + world.execute(() -> EntityCloneCommand.cloneEntity(playerRef, targetRef, store)); + break; + case Remove: + world.execute(() -> EntityRemoveCommand.removeEntity(ref, targetRef, store)); + } } + } else { + playerRef.sendMessage(Message.translation("server.general.entityNotFound").param("id", targetId)); } } @@ -290,7 +296,7 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); Vector3d position = transformComponent.getPosition(); - Vector3i intTriple = new Vector3i(MathUtil.floor(position.getX()), MathUtil.floor(position.getY()), MathUtil.floor(position.getZ())); + Vector3i intTriple = new Vector3i(MathUtil.floor(position.x()), MathUtil.floor(position.y()), MathUtil.floor(position.z())); BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> { if (packet.action == BuilderToolAction.SelectionPosition1) { builderState.pos1(intTriple, componentAccessor); @@ -366,22 +372,13 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { ); prototypeSettings.setFluidChangesForPlaySelectionToolPasteMode(fluidChangesArray); ArrayList entityChanges = new ArrayList<>(); - int selectionX = selection.getX(); - int selectionY = selection.getY(); - int selectionZ = selection.getZ(); - selection.forEachEntity( - holder -> { - TransformComponent transform = holder.getComponent(TransformComponent.getComponentType()); - if (transform != null && transform.getPosition() != null) { - Vector3d pos = transform.getPosition(); - entityChanges.add( - new PrototypePlayerBuilderToolSettings.EntityChange( - pos.getX() + selectionX, pos.getY() + selectionY, pos.getZ() + selectionZ, holder.clone() - ) - ); - } + selection.forEachEntity(holder -> { + TransformComponent transform = holder.getComponent(TransformComponent.getComponentType()); + if (transform != null && transform.getPosition() != null) { + Vector3d pos = transform.getPosition(); + entityChanges.add(new PrototypePlayerBuilderToolSettings.EntityChange(pos.x(), pos.y(), pos.z(), holder.clone())); } - ); + }); prototypeSettings.setEntityChangesForPlaySelectionToolPasteMode(entityChanges.toArray(PrototypePlayerBuilderToolSettings.EntityChange[]::new)); FluidChange[] packetFluids = new FluidChange[fluidChangesArray.length]; @@ -390,7 +387,24 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { packetFluids[i] = new FluidChange(fc.x(), fc.y(), fc.z(), fc.fluidId(), fc.fluidLevel()); } - playerRef.getPacketHandler().write(new BuilderToolSelectionToolReplyWithClipboard(blocksChange, packetFluids)); + ClipboardEntityChange[] packetEntities = new ClipboardEntityChange[entityChanges.size()]; + + for (int i = 0; i < entityChanges.size(); i++) { + PrototypePlayerBuilderToolSettings.EntityChange ec = entityChanges.get(i); + packetEntities[i] = BlockSelection.toClipboardEntityChange(ec.entityHolder(), anchorX, anchorY, anchorZ); + } + + if (blocksChange != null && blocksChange.length > 4000000) { + NotificationUtil.sendNotification( + playerRef.getPacketHandler(), + Message.translation("server.builderTools.copycut.tooLarge"), + Message.translation("server.builderTools.copycut.tooLarge.detail").param("overCount", blocksChange.length - 4000000), + NotificationStyle.Warning + ); + return; + } + + playerRef.getPacketHandler().write(new BuilderToolSelectionToolReplyWithClipboard(blocksChange, packetFluids, packetEntities)); } } ); @@ -417,11 +431,11 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { } boolean finalKeepEmptyBlocks = keepEmptyBlocks; - Quaterniond rotation = new Quaterniond(packet.rotation.x, packet.rotation.y, packet.rotation.z, packet.rotation.w); + Quaterniond rotation = new Quaterniond(packet.rotation); Vector3i translationOffset = new Vector3i(packet.translationOffset.x, packet.translationOffset.y, packet.translationOffset.z); Vector3i initialSelectionMin = new Vector3i(packet.initialSelectionMin.x, packet.initialSelectionMin.y, packet.initialSelectionMin.z); Vector3i initialSelectionMax = new Vector3i(packet.initialSelectionMax.x, packet.initialSelectionMax.y, packet.initialSelectionMax.z); - Vector3f rotationOrigin = new Vector3f(packet.initialRotationOrigin.x, packet.initialRotationOrigin.y, packet.initialRotationOrigin.z); + Rotation3f rotationOrigin = new Rotation3f(packet.initialRotationOrigin.x(), packet.initialRotationOrigin.y(), packet.initialRotationOrigin.z()); PrototypePlayerBuilderToolSettings prototypeSettings = ToolOperation.getOrCreatePrototypeSettings(playerRef.getUuid()); BuilderToolsPlugin.addToQueue( playerComponent, @@ -430,105 +444,120 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { int blockCount = s.getSelection().getSelectionVolume(); boolean large = blockCount > 20000; if (large) { - playerComponent.sendMessage(Message.translation("server.builderTools.selection.large.warning")); + playerRef.sendMessage(Message.translation("server.builderTools.selection.large.warning")); } - if (prototypeSettings.getBlockChangesForPlaySelectionToolPasteMode() == null) { - s.select(initialSelectionMin, initialSelectionMax, "server.builderTools.selectReasons.selectionTranslatePacket", componentAccessor); - List> lastTransformRefs = prototypeSettings.getLastTransformEntityRefs(); - HashSet> skipSet = lastTransformRefs != null ? new HashSet<>(lastTransformRefs) : null; - if (packet.cutOriginal) { - s.copyOrCut( - r, - initialSelectionMin.x, - initialSelectionMin.y, - initialSelectionMin.z, - initialSelectionMax.x, - initialSelectionMax.y, - initialSelectionMax.z, - 154, - null, - skipSet, - store - ); - } else { - s.copyOrCut( - r, - initialSelectionMin.x, - initialSelectionMin.y, - initialSelectionMin.z, - initialSelectionMax.x, - initialSelectionMax.y, - initialSelectionMax.z, - 152, - store - ); - } + try { + if (prototypeSettings.getBlockChangesForPlaySelectionToolPasteMode() == null) { + s.select(initialSelectionMin, initialSelectionMax, "server.builderTools.selectReasons.selectionTranslatePacket", componentAccessor); + List> lastTransformRefs = prototypeSettings.getLastTransformEntityRefs(); + HashSet> skipSet = lastTransformRefs != null ? new HashSet<>(lastTransformRefs) : null; + if (packet.cutOriginal) { + s.copyOrCut( + r, + initialSelectionMin.x, + initialSelectionMin.y, + initialSelectionMin.z, + initialSelectionMax.x, + initialSelectionMax.y, + initialSelectionMax.z, + 154, + null, + skipSet, + store + ); + } else { + s.copyOrCut( + r, + initialSelectionMin.x, + initialSelectionMin.y, + initialSelectionMin.z, + initialSelectionMax.x, + initialSelectionMax.y, + initialSelectionMax.z, + 152, + store + ); + } - BlockSelection selection = s.getSelection(); - BlockChange[] blocksChange = selection.toPacket().blocksChange; - prototypeSettings.setBlockChangesForPlaySelectionToolPasteMode(blocksChange); - ArrayList fluidChanges = new ArrayList<>(); - int anchorX = selection.getAnchorX(); - int anchorY = selection.getAnchorY(); - int anchorZ = selection.getAnchorZ(); - selection.forEachFluid( - (x, y, z, fluidId, fluidLevel) -> fluidChanges.add( - new PrototypePlayerBuilderToolSettings.FluidChange(x - anchorX, y - anchorY, z - anchorZ, fluidId, fluidLevel) - ) - ); - prototypeSettings.setFluidChangesForPlaySelectionToolPasteMode(fluidChanges.toArray(PrototypePlayerBuilderToolSettings.FluidChange[]::new)); - ArrayList entityChanges = new ArrayList<>(); - int selectionX = selection.getX(); - int selectionY = selection.getY(); - int selectionZ = selection.getZ(); - selection.forEachEntity( - holder -> { + BlockSelection selection = s.getSelection(); + BlockChange[] blocksChange = selection.toPacket().blocksChange; + prototypeSettings.setBlockChangesForPlaySelectionToolPasteMode(blocksChange); + ArrayList fluidChanges = new ArrayList<>(); + int anchorX = selection.getAnchorX(); + int anchorY = selection.getAnchorY(); + int anchorZ = selection.getAnchorZ(); + selection.forEachFluid( + (x, y, z, fluidId, fluidLevel) -> fluidChanges.add( + new PrototypePlayerBuilderToolSettings.FluidChange(x - anchorX, y - anchorY, z - anchorZ, fluidId, fluidLevel) + ) + ); + prototypeSettings.setFluidChangesForPlaySelectionToolPasteMode(fluidChanges.toArray(PrototypePlayerBuilderToolSettings.FluidChange[]::new)); + ArrayList entityChanges = new ArrayList<>(); + selection.forEachEntity(holder -> { TransformComponent transform = holder.getComponent(TransformComponent.getComponentType()); if (transform != null && transform.getPosition() != null) { Vector3d pos = transform.getPosition(); - entityChanges.add( - new PrototypePlayerBuilderToolSettings.EntityChange( - pos.getX() + selectionX, pos.getY() + selectionY, pos.getZ() + selectionZ, holder.clone() - ) - ); + entityChanges.add(new PrototypePlayerBuilderToolSettings.EntityChange(pos.x(), pos.y(), pos.z(), holder.clone())); } - } - ); - prototypeSettings.setEntityChangesForPlaySelectionToolPasteMode(entityChanges.toArray(PrototypePlayerBuilderToolSettings.EntityChange[]::new)); - prototypeSettings.setBlockChangeOffsetOrigin(new Vector3i(selection.getX(), selection.getY(), selection.getZ())); - } - - Vector3i blockChangeOffsetOrigin = prototypeSettings.getBlockChangeOffsetOrigin(); - if (packet.initialPastePointForClipboardPaste != null) { - blockChangeOffsetOrigin = new Vector3i( - packet.initialPastePointForClipboardPaste.x, packet.initialPastePointForClipboardPaste.y, packet.initialPastePointForClipboardPaste.z - ); - } - - if (blockChangeOffsetOrigin == null) { - playerComponent.sendMessage(Message.translation("server.builderTools.selection.noBlockChangeOffsetOrigin")); - } else { - s.transformThenPasteClipboard( - prototypeSettings.getBlockChangesForPlaySelectionToolPasteMode(), - prototypeSettings.getFluidChangesForPlaySelectionToolPasteMode(), - prototypeSettings.getEntityChangesForPlaySelectionToolPasteMode(), - rotation, - translationOffset, - rotationOrigin, - blockChangeOffsetOrigin, - finalKeepEmptyBlocks, - prototypeSettings, - componentAccessor - ); - s.select(initialSelectionMin, initialSelectionMax, "server.builderTools.selectReasons.selectionTranslatePacket", componentAccessor); - s.transformSelectionPoints(rotation, translationOffset, rotationOrigin); - if (large) { - playerComponent.sendMessage(Message.translation("server.builderTools.selection.large.complete")); + }); + prototypeSettings.setEntityChangesForPlaySelectionToolPasteMode( + entityChanges.toArray(PrototypePlayerBuilderToolSettings.EntityChange[]::new) + ); + prototypeSettings.setBlockChangeOffsetOrigin(new Vector3i(selection.getX(), selection.getY(), selection.getZ())); } + BlockChange[] localBlockChanges = prototypeSettings.getBlockChangesForPlaySelectionToolPasteMode(); + PrototypePlayerBuilderToolSettings.FluidChange[] localFluidChanges = prototypeSettings.getFluidChangesForPlaySelectionToolPasteMode(); + PrototypePlayerBuilderToolSettings.EntityChange[] localEntityChanges = prototypeSettings.getEntityChangesForPlaySelectionToolPasteMode(); + Vector3i blockChangeOffsetOrigin = prototypeSettings.getBlockChangeOffsetOrigin(); + if (packet.initialPastePointForClipboardPaste != null) { + blockChangeOffsetOrigin = new Vector3i( + packet.initialPastePointForClipboardPaste.x, packet.initialPastePointForClipboardPaste.y, packet.initialPastePointForClipboardPaste.z + ); + } + + if (blockChangeOffsetOrigin != null) { + prototypeSettings.setLastTransformEntityRefs(null); + s.transformThenPasteClipboard( + localBlockChanges, + localFluidChanges, + localEntityChanges, + rotation, + translationOffset, + rotationOrigin, + blockChangeOffsetOrigin, + finalKeepEmptyBlocks, + prototypeSettings, + componentAccessor + ); + s.select(initialSelectionMin, initialSelectionMax, "server.builderTools.selectReasons.selectionTranslatePacket", componentAccessor); + s.transformSelectionPoints(rotation, translationOffset, rotationOrigin); + if (!packet.isExitingTransformMode) { + prototypeSettings.setBlockChangeOffsetOrigin( + new Vector3i( + blockChangeOffsetOrigin.x + translationOffset.x, + blockChangeOffsetOrigin.y + translationOffset.y, + blockChangeOffsetOrigin.z + translationOffset.z + ) + ); + } + + if (large) { + playerRef.sendMessage(Message.translation("server.builderTools.selection.large.complete")); + } + + return; + } + + playerRef.sendMessage(Message.translation("server.builderTools.selection.noBlockChangeOffsetOrigin")); + } catch (Exception var27) { + LOGGER.at(Level.WARNING).log("Error during selection transform", var27); + return; + } finally { if (packet.isExitingTransformMode) { prototypeSettings.setInSelectionTransformationMode(false); + prototypeSettings.setLastTransformEntityRefs(null); } } } @@ -619,7 +648,27 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { ? Axis.X : (packet.axis == com.hypixel.hytale.protocol.packets.buildertools.Axis.Y ? Axis.Y : Axis.Z); LOGGER.at(Level.INFO).log("%s: %s", this.packetHandler.getIdentifier(), packet); - BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.rotate(r, axis, packet.angle, componentAccessor)); + BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> { + s.setSkipNextPreviewRebuild(true); + s.rotate(r, axis, packet.angle, componentAccessor); + }); + } + } + + public void handleBuilderToolResetClipboardRotation( + @Nonnull BuilderToolResetClipboardRotation packet, + @Nonnull PlayerRef playerRef, + @Nonnull Ref ref, + @Nonnull World world, + @Nonnull Store store + ) { + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + if (playerComponent != null) { + LOGGER.at(Level.INFO).log("%s: %s", this.packetHandler.getIdentifier(), packet); + BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> { + s.setSkipNextPreviewRebuild(true); + s.resetClipboardRotation(r, componentAccessor); + }); } } @@ -649,7 +698,6 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { BuilderTool builderTool = BuilderTool.getActiveBuilderTool(playerComponent); if (builderTool != null && builderTool.getId().equals("Line")) { BuilderTool.ArgData args = builderTool.getItemArgData(playerComponent.getInventory().getItemInHand()); - BrushData.Values brushData = args.brush(); Map tool = args.tool(); if (tool != null) { int lineWidth = (Integer)tool.get("bLineWidth"); @@ -679,7 +727,7 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { lineOrigin, lineSpacing, lineDensity, - ToolOperation.combineMasks(brushData, s.getGlobalMask()), + ToolOperation.combineMasks(args, s.getGlobalMask()), componentAccessor ) ); @@ -720,17 +768,16 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { boolean hasLookOrientation = modelTransform.lookOrientation != null; boolean hasBodyOrientation = modelTransform.bodyOrientation != null; if (hasPosition) { - transformComponent.getPosition().assign(modelTransform.position.x, modelTransform.position.y, modelTransform.position.z); + transformComponent.getPosition().set(modelTransform.position.x, modelTransform.position.y, modelTransform.position.z); } if (hasLookOrientation && headRotation != null) { - headRotation.getRotation() - .assign(modelTransform.lookOrientation.pitch, modelTransform.lookOrientation.yaw, modelTransform.lookOrientation.roll); + headRotation.getRotation().set(modelTransform.lookOrientation.pitch, modelTransform.lookOrientation.yaw, modelTransform.lookOrientation.roll); } if (hasBodyOrientation) { transformComponent.getRotation() - .assign(modelTransform.bodyOrientation.pitch, modelTransform.bodyOrientation.yaw, modelTransform.bodyOrientation.roll); + .set(modelTransform.bodyOrientation.pitch, modelTransform.bodyOrientation.yaw, modelTransform.bodyOrientation.roll); } if (hasPosition || hasLookOrientation || hasBodyOrientation) { @@ -748,20 +795,46 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { @Nonnull World world, @Nonnull Store store ) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - if (playerComponent != null) { - LOGGER.at(Level.INFO).log("%s: %s", this.packetHandler.getIdentifier(), packet); - PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); - PrefabEditSession prefabEditSession = prefabEditSessionManager.getPrefabEditSession(playerRef.getUuid()); - if (prefabEditSession == null) { - playerComponent.sendMessage(Message.translation("server.commands.editprefab.notInEditSession")); + LOGGER.at(Level.INFO).log("%s: %s", this.packetHandler.getIdentifier(), packet); + PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); + PrefabEditSession prefabEditSession = prefabEditSessionManager.getPrefabEditSession(playerRef.getUuid()); + if (prefabEditSession == null) { + playerRef.sendMessage(Message.translation("server.commands.editprefab.notInEditSession")); + } else { + if (prefabEditSession.clearSelectedPrefab(ref, store)) { + playerRef.sendMessage(Message.translation("server.commands.editprefab.unselected")); } else { - if (prefabEditSession.clearSelectedPrefab(ref, store)) { - playerComponent.sendMessage(Message.translation("server.commands.editprefab.unselected")); - } else { - playerComponent.sendMessage(Message.translation("server.commands.editprefab.noPrefabSelected")); + playerRef.sendMessage(Message.translation("server.commands.editprefab.noPrefabSelected")); + } + } + } + + public void handlePrefabSetAnchor( + @Nonnull PrefabSetAnchor packet, @Nonnull PlayerRef playerRef, @Nonnull Ref ref, @Nonnull World world, @Nonnull Store store + ) { + LOGGER.at(Level.INFO).log("%s: %s", this.packetHandler.getIdentifier(), packet); + PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); + PrefabEditSession prefabEditSession = prefabEditSessionManager.getPrefabEditSession(playerRef.getUuid()); + if (prefabEditSession == null) { + playerRef.sendMessage(Message.translation("server.commands.editprefab.notInEditSession")); + } else { + PrefabEditingMetadata prefabEditingMetadata = null; + Vector3i targetBlockPos = new Vector3i(packet.x, packet.y, packet.z); + + for (PrefabEditingMetadata value : prefabEditSession.getLoadedPrefabMetadata().values()) { + boolean isWithinPrefab = value.isLocationWithinPrefabBoundingBox(new Vector3i(packet.x, packet.y, packet.z)); + if (isWithinPrefab) { + prefabEditingMetadata = value; + break; } } + + if (prefabEditingMetadata == null) { + playerRef.sendMessage(Message.translation("server.commands.editprefab.select.error.noPrefabFound")); + } else { + prefabEditingMetadata.setAnchorPoint(targetBlockPos, world); + prefabEditingMetadata.sendAnchorHighlightingPacket(playerRef.getPacketHandler()); + } } } @@ -774,15 +847,12 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { ) { Ref targetRef = world.getEntityStore().getRefFromNetworkId(packet.entityId); if (targetRef != null && targetRef.isValid()) { - PropComponent propComponent = store.getComponent(targetRef, PropComponent.getComponentType()); - if (propComponent != null) { - EntityScaleComponent scaleComponent = store.getComponent(targetRef, EntityScaleComponent.getComponentType()); - if (scaleComponent == null) { - scaleComponent = new EntityScaleComponent(packet.scale); - store.addComponent(targetRef, EntityScaleComponent.getComponentType(), scaleComponent); - } else { - scaleComponent.setScale(packet.scale); - } + EntityScaleComponent scaleComponent = store.getComponent(targetRef, EntityScaleComponent.getComponentType()); + if (scaleComponent == null) { + scaleComponent = new EntityScaleComponent(packet.scale); + store.addComponent(targetRef, EntityScaleComponent.getComponentType(), scaleComponent); + } else { + scaleComponent.setScale(packet.scale); } } } @@ -854,18 +924,15 @@ public class BuilderToolsPacketHandler implements SubPacketHandler { @Nonnull World world, @Nonnull Store store ) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - if (playerComponent != null) { - Ref targetRef = world.getEntityStore().getRefFromNetworkId(packet.entityId); - if (targetRef != null && targetRef.isValid()) { - NPCMarkerComponent npcMarkerComponent = store.getComponent(targetRef, NPCMarkerComponent.getComponentType()); - if (npcMarkerComponent != null) { - UUIDComponent uuidComponent = store.getComponent(targetRef, UUIDComponent.getComponentType()); - if (uuidComponent != null) { - UUID uuid = uuidComponent.getUuid(); - String command = packet.enabled ? "npc debug set display --entity " + uuid : "npc debug clear --entity " + uuid; - CommandManager.get().handleCommand(playerComponent, command); - } + Ref targetRef = world.getEntityStore().getRefFromNetworkId(packet.entityId); + if (targetRef != null && targetRef.isValid()) { + NPCMarkerComponent npcMarkerComponent = store.getComponent(targetRef, NPCMarkerComponent.getComponentType()); + if (npcMarkerComponent != null) { + UUIDComponent uuidComponent = store.getComponent(targetRef, UUIDComponent.getComponentType()); + if (uuidComponent != null) { + UUID uuid = uuidComponent.getUuid(); + String command = packet.enabled ? "npc debug set display --entity " + uuid : "npc debug clear --entity " + uuid; + CommandManager.get().handleCommand(playerRef, command); } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsPlugin.java b/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsPlugin.java index 799406ac..8e4fac0d 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsPlugin.java +++ b/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsPlugin.java @@ -1,7 +1,10 @@ package com.hypixel.hytale.builtin.buildertools; import com.hypixel.fastutil.ints.Int2ObjectConcurrentHashMap; +import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.assetstore.AssetRegistry; +import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent; +import com.hypixel.hytale.assetstore.event.RemovedAssetsEvent; import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; @@ -53,8 +56,8 @@ import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionMan import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditorCreationSettings; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabMarkerProvider; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabSelectionInteraction; -import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabSetAnchorInteraction; import com.hypixel.hytale.builtin.buildertools.prefabeditor.commands.PrefabEditCommand; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfig; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigCommandExecutor; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigEditStore; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.ScriptedBrushAsset; @@ -65,6 +68,7 @@ import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.global import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.BlockPatternOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.BreakpointOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.ClearOperationMaskOperation; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.ClearRotationOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.DeleteOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.EchoOnceOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.EchoOperation; @@ -111,6 +115,7 @@ import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequen import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.saveandload.PersistentDataOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.saveandload.SaveBrushConfigOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.saveandload.SaveIndexOperation; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.transforms.RotateOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.system.BrushOperation; import com.hypixel.hytale.builtin.buildertools.snapshot.BlockSelectionSnapshot; import com.hypixel.hytale.builtin.buildertools.snapshot.ClipboardBoundsSnapshot; @@ -145,14 +150,12 @@ import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.function.predicate.TriIntObjPredicate; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.block.BlockCubeUtil; -import com.hypixel.hytale.math.block.BlockSphereUtil; import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.iterator.LineIterator; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.math.vector.VectorBoxUtil; import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricResults; @@ -170,13 +173,12 @@ import com.hypixel.hytale.protocol.packets.interface_.EditorBlocksChange; import com.hypixel.hytale.protocol.packets.interface_.NotificationStyle; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.AssetModule; import com.hypixel.hytale.server.core.asset.HytaleAssetStore; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderTool; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderToolData; import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BlockArg; import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BoolArg; import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BrushOriginArg; @@ -190,6 +192,7 @@ import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.ToolArg import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.ToolArgException; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; +import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.CommandManager; import com.hypixel.hytale.server.core.command.system.CommandRegistry; import com.hypixel.hytale.server.core.command.system.CommandSender; @@ -198,9 +201,11 @@ import com.hypixel.hytale.server.core.entity.entities.BlockEntity; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent; import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent; +import com.hypixel.hytale.server.core.event.events.player.PlayerReadyEvent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.io.ServerManager; +import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems; @@ -208,7 +213,7 @@ import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction; -import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerState; +import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerBlock; import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; @@ -235,8 +240,6 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; import com.hypixel.hytale.server.core.universe.world.events.AddWorldEvent; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.Config; @@ -258,6 +261,7 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -274,21 +278,25 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.StampedLock; import java.util.function.Consumer; -import java.util.function.IntPredicate; import java.util.function.Predicate; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.joml.Quaterniond; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, MetricProvider { public static final String EDITOR_BLOCK = "Editor_Block"; public static final String EDITOR_BLOCK_PREFAB_AIR = "Editor_Empty"; public static final String EDITOR_BLOCK_PREFAB_ANCHOR = "Editor_Anchor"; protected static final float SPHERE_SIZE = 1.0F; + static final int MAX_CLIPBOARD_BLOCK_COUNT = 4000000; + static final double CLIPBOARD_PRE_LIMIT_FACTOR = 1.65; private static final FeedbackConsumer FEEDBACK_CONSUMER = BuilderToolsPlugin::sendFeedback; private static final MetricsRegistry PLUGIN_METRICS_REGISTRY = new MetricsRegistry() .register( @@ -313,6 +321,8 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, private static final float SMOOTHING_KERNEL_TOTAL = 27.0F; private static final int[] SMOOTHING_KERNEL = new int[]{1, 2, 1, 2, 3, 2, 1, 2, 1, 2, 3, 2, 3, 4, 3, 2, 3, 2, 1, 2, 1, 2, 3, 2, 1, 2, 1}; private final Config config = this.withConfig("BuilderToolsModule", BuilderToolsPlugin.BuilderToolsConfig.CODEC); + private static final Message MESSAGE_PACK_NOT_FOUND = Message.translation("server.commands.editprefab.save.pack.notFound"); + private static final Message MESSAGE_PACK_IMMUTABLE = Message.translation("server.commands.editprefab.save.pack.immutable"); private ResourceType prefabEditSessionResourceType; public BuilderToolsPlugin(@Nonnull JavaPluginInit init) { @@ -367,6 +377,58 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, getState(player, playerRef).addToQueue(task); } + @Nullable + public static AssetPack resolveTargetPack(@Nonnull String explicitPackName, @Nonnull Player playerComponent, @Nonnull CommandContext context) { + return resolveTargetPack(explicitPackName, null, playerComponent, context); + } + + @Nullable + public static AssetPack resolveTargetPack( + @Nonnull String explicitPackName, @Nullable Path prefabPath, @Nonnull Player playerComponent, @Nonnull CommandContext context + ) { + AssetModule assetModule = AssetModule.get(); + if (!explicitPackName.isEmpty()) { + AssetPack pack = assetModule.getAssetPack(explicitPackName); + if (pack == null) { + context.sendMessage(MESSAGE_PACK_NOT_FOUND.param("name", explicitPackName)); + return null; + } else if (pack.isImmutable()) { + context.sendMessage(MESSAGE_PACK_IMMUTABLE.param("name", explicitPackName)); + return null; + } else { + return pack; + } + } else { + if (prefabPath != null) { + AssetPack sourcePack = PrefabStore.get().findAssetPackForPrefabPath(prefabPath); + if (sourcePack != null) { + if (!sourcePack.isImmutable()) { + return sourcePack; + } + + context.sendMessage(Message.translation("server.commands.editprefab.save.noPack")); + return null; + } + } + + String lastPack = BuilderToolsUserData.get(playerComponent).getLastSavePack(); + if (lastPack != null) { + AssetPack pack = assetModule.getAssetPack(lastPack); + if (pack != null && !pack.isImmutable()) { + return pack; + } + } + + AssetPack basePack = assetModule.getBaseAssetPack(); + if (!basePack.isImmutable()) { + return basePack; + } else { + context.sendMessage(Message.translation("server.commands.editprefab.save.noPack")); + return null; + } + } + } + @Override protected void setup() { CommandRegistry commandRegistry = this.getCommandRegistry(); @@ -375,15 +437,17 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, ServerManager.get().registerSubPacketHandlers(BuilderToolsPacketHandler::new); eventRegistry.register(PlayerConnectEvent.class, this::onPlayerConnect); eventRegistry.register(PlayerDisconnectEvent.class, this::onPlayerDisconnect); + eventRegistry.registerGlobal(PlayerReadyEvent.class, this::onPlayerReady); eventRegistry.registerGlobal( AddWorldEvent.class, event -> event.getWorld().getWorldMapManager().addMarkerProvider("prefabs", PrefabMarkerProvider.INSTANCE) ); entityStoreRegistry.registerSystem(new BuilderToolsPlugin.PrefabPasteEventSystem(this)); entityStoreRegistry.registerSystem(new PrefabDirtySystems.BlockBreakDirtySystem()); entityStoreRegistry.registerSystem(new PrefabDirtySystems.BlockPlaceDirtySystem()); + this.getEventRegistry().register(LoadedAssetsEvent.class, Item.class, event -> ScriptedBrushAsset.invalidateBrushToItemCache()); + this.getEventRegistry().register(RemovedAssetsEvent.class, Item.class, event -> ScriptedBrushAsset.invalidateBrushToItemCache()); this.prefabAnchorComponentType = entityStoreRegistry.registerComponent(PrefabAnchor.class, "PrefabAnchor", PrefabAnchor.CODEC); Interaction.CODEC.register("PrefabSelectionInteraction", PrefabSelectionInteraction.class, PrefabSelectionInteraction.CODEC); - Interaction.CODEC.register("PrefabSetAnchorInteraction", PrefabSetAnchorInteraction.class, PrefabSetAnchorInteraction.CODEC); Interaction.CODEC.register("PickupItem", PickupItemInteraction.class, PickupItemInteraction.CODEC); Interaction.getAssetStore().loadAssets("Hytale:Hytale", List.of(new PickupItemInteraction("*PickupItem"))); RootInteraction.getAssetStore().loadAssets("Hytale:Hytale", List.of(PickupItemInteraction.DEFAULT_ROOT)); @@ -449,12 +513,18 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, commandRegistry.registerCommand(new ObjImportCommand()); commandRegistry.registerCommand(new ImageImportCommand()); commandRegistry.registerCommand(new LayerCommand()); - OpenCustomUIInteraction.registerBlockCustomPage( + OpenCustomUIInteraction.registerBlockEntityCustomPage( this, - PrefabSpawnerState.PrefabSpawnerSettingsPage.class, + PrefabSpawnerBlock.PrefabSpawnerSettingsPage.class, "PrefabSpawner", - PrefabSpawnerState.class, - (playerRef, state) -> new PrefabSpawnerState.PrefabSpawnerSettingsPage(playerRef, state, CustomPageLifetime.CanDismissOrCloseThroughInteraction) + (playerRef, blockRef) -> { + Store store = blockRef.getStore(); + BlockModule.BlockStateInfo info = store.getComponent(blockRef, BlockModule.BlockStateInfo.getComponentType()); + PrefabSpawnerBlock state = store.getComponent(blockRef, PrefabSpawnerBlock.getComponentType()); + return info != null && state != null + ? new PrefabSpawnerBlock.PrefabSpawnerSettingsPage(playerRef, info, state, CustomPageLifetime.CanDismissOrCloseThroughInteraction) + : null; + } ); SelectionManager.setSelectionProvider(this); ToolArg.CODEC.register("Bool", BoolArg.class, BoolArg.CODEC); @@ -510,6 +580,8 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, BrushOperation.OPERATION_CODEC.register("set", SetOperation.class, SetOperation.CODEC); BrushOperation.OPERATION_CODEC.register("smooth", SmoothOperation.class, SmoothOperation.CODEC); BrushOperation.OPERATION_CODEC.register("shape", ShapeOperation.class, ShapeOperation.CODEC); + BrushOperation.OPERATION_CODEC.register("rotation", RotateOperation.class, RotateOperation.CODEC); + BrushOperation.OPERATION_CODEC.register("clearrotation", ClearRotationOperation.class, ClearRotationOperation.CODEC); BrushOperation.OPERATION_CODEC.register("offset", OffsetOperation.class, OffsetOperation.CODEC); BrushOperation.OPERATION_CODEC.register("layer", LayerOperation.class, LayerOperation.CODEC); BrushOperation.OPERATION_CODEC.register("heightmaplayer", HeightmapLayerOperation.class, HeightmapLayerOperation.CODEC); @@ -575,6 +647,20 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.releaseBuilderState(event.getPlayerRef().getUuid()); } + private void onPlayerReady(@Nonnull PlayerReadyEvent event) { + Ref playerRef = event.getPlayer().getReference(); + if (playerRef != null && playerRef.isValid()) { + Store store = playerRef.getStore(); + UUIDComponent uuidComponent = store.getComponent(playerRef, UUIDComponent.getComponentType()); + if (uuidComponent != null) { + BuilderToolsPlugin.BuilderState state = this.builderStates.get(uuidComponent.getUuid()); + if (state != null && state.getSelection() != null) { + state.sendSelectionToClient(); + } + } + } + } + public void onToolArgUpdate(@Nonnull PlayerRef playerRef, @Nonnull Player player, @Nonnull BuilderToolArgUpdate packet) { ItemContainer section = player.getInventory().getSectionById(packet.section); ItemStack itemStack = section.getItemStack((short)packet.slot); @@ -582,20 +668,18 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, MessageUtil.sendFailureReply(playerRef, packet.token, Message.translation("server.builderTools.invalidTool").param("item", "Empty")); } else { Item item = itemStack.getItem(); - BuilderToolData builderToolData = item.getBuilderToolData(); + BuilderTool builderToolData = item.getBuilderTool(); if (builderToolData == null) { Message itemMessage = Message.translation(item.getTranslationKey()); MessageUtil.sendFailureReply(playerRef, packet.token, Message.translation("server.builderTools.invalidTool").param("item", itemMessage)); } else { - BuilderTool tool = builderToolData.getTools()[0]; - try { - ItemStack updatedItemStack = tool.updateArgMetadata(itemStack, packet.group, packet.id, packet.value); + ItemStack updatedItemStack = builderToolData.updateArgMetadata(itemStack, packet.id, packet.value); section.setItemStackForSlot((short)packet.slot, updatedItemStack); MessageUtil.sendSuccessReply(playerRef, packet.token); - } catch (ToolArgException var10) { - MessageUtil.sendFailureReply(playerRef, packet.token, var10.getTranslationMessage()); - } catch (IllegalArgumentException var11) { + } catch (ToolArgException var9) { + MessageUtil.sendFailureReply(playerRef, packet.token, var9.getTranslationMessage()); + } catch (IllegalArgumentException var10) { MessageUtil.sendFailureReply( playerRef, packet.token, Message.translation("server.builderTools.toolArgParseError").param("arg", packet.id).param("value", packet.value) ); @@ -764,26 +848,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, return null; } else { BlockType type = BlockType.getAssetMap().getAsset(newId); - if (type.getBlockEntity() != null) { - return type.getBlockEntity().clone(); - } else { - String stateId = type.getState() != null ? type.getState().getId() : null; - if (stateId == null) { - return null; - } else { - if (copy && oldHolder != null) { - BlockType currentType = BlockType.getAssetMap().getAsset(oldId); - String currentStateId = currentType.getState() != null ? currentType.getState().getId() : null; - if (stateId.equals(currentStateId)) { - return oldHolder.clone(); - } - } - - Vector3i position = new Vector3i(x, y, z); - BlockState state = BlockStateModule.get().createBlockState(stateId, chunk, position, type); - return state == null ? null : state.toHolder(); - } - } + return type.getBlockEntity() != null ? type.getBlockEntity().clone() : null; } } @@ -874,25 +939,35 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } public static enum Action { - EDIT, - EDIT_SELECTION, - EDIT_LINE, - CUT_COPY, - CUT_REMOVE, - COPY, - PASTE, - CLEAR, - ROTATE, - FLIP, - MOVE, - STACK, - SET, - REPLACE, - EXTRUDE, - UPDATE_SELECTION, - WALLS, - HOLLOW, - LAYER; + EDIT("server.builderTools.action.edit"), + EDIT_SELECTION("server.builderTools.action.editSelection"), + EDIT_LINE("server.builderTools.action.editLine"), + CUT_COPY("server.builderTools.action.cutCopy"), + CUT_REMOVE("server.builderTools.action.cutRemove"), + COPY("server.builderTools.action.copy"), + PASTE("server.builderTools.action.paste"), + CLEAR("server.builderTools.action.clear"), + ROTATE("server.builderTools.action.rotate"), + FLIP("server.builderTools.action.flip"), + MOVE("server.builderTools.action.move"), + STACK("server.builderTools.action.stack"), + SET("server.builderTools.action.set"), + REPLACE("server.builderTools.action.replace"), + EXTRUDE("server.builderTools.action.extrude"), + UPDATE_SELECTION("server.builderTools.action.updateSelection"), + WALLS("server.builderTools.action.walls"), + HOLLOW("server.builderTools.action.hollow"), + LAYER("server.builderTools.action.layer"); + + private final String translationKey; + + private Action(String translationKey) { + this.translationKey = translationKey; + } + + public Message toMessage() { + return Message.translation(this.translationKey); + } } public static class ActionEntry { @@ -913,17 +988,19 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } @Nonnull - public BuilderToolsPlugin.ActionEntry restore(Ref ref, Player player, World world, ComponentAccessor componentAccessor) { + public BuilderToolsPlugin.ActionEntry restore(Ref ref, PlayerRef playerRef, World world, ComponentAccessor componentAccessor) { List> collector = Collections.emptyList(); List> recreatedEntityRefs = null; + boolean handledViaLastTransformRefs = false; if (this.action == BuilderToolsPlugin.Action.ROTATE) { - PrototypePlayerBuilderToolSettings protoSettings = ToolOperation.getOrCreatePrototypeSettings(player.getUuid()); + PrototypePlayerBuilderToolSettings protoSettings = ToolOperation.getOrCreatePrototypeSettings(playerRef.getUuid()); List> currentRefs = protoSettings.getLastTransformEntityRefs(); if (currentRefs != null) { + handledViaLastTransformRefs = true; Store entityStore = world.getEntityStore().getStore(); for (Ref currentRef : currentRefs) { - if (currentRef.isValid()) { + if (currentRef != null && currentRef.isValid()) { collector = (List>)(collector.isEmpty() ? new ObjectArrayList<>() : collector); collector.add(new EntityRemoveSnapshot(currentRef)); entityStore.removeEntity(currentRef, RemoveReason.UNLOAD); @@ -935,14 +1012,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } for (SelectionSnapshot snapshot : this.snapshots) { - if (this.action != BuilderToolsPlugin.Action.ROTATE || !(snapshot instanceof EntityAddSnapshot)) { - SelectionSnapshot nextSnapshot = snapshot.restore(ref, player, world, componentAccessor); + if (!handledViaLastTransformRefs || !(snapshot instanceof EntityAddSnapshot)) { + SelectionSnapshot nextSnapshot = snapshot.restore(ref, playerRef, world, componentAccessor); if (nextSnapshot != null) { collector = (List>)(collector.isEmpty() ? new ObjectArrayList<>() : collector); collector.add(nextSnapshot); if (nextSnapshot instanceof EntityAddSnapshot entityAddSnapshot) { if (recreatedEntityRefs == null) { - recreatedEntityRefs = new ArrayList<>(); + recreatedEntityRefs = new ReferenceArrayList<>(); } recreatedEntityRefs.add(entityAddSnapshot.getEntityRef()); @@ -951,8 +1028,10 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - if (this.action == BuilderToolsPlugin.Action.ROTATE && recreatedEntityRefs != null && !recreatedEntityRefs.isEmpty()) { - PrototypePlayerBuilderToolSettings prototypeSettings = ToolOperation.getOrCreatePrototypeSettings(player.getUuid()); + if ((this.action == BuilderToolsPlugin.Action.ROTATE || this.action == BuilderToolsPlugin.Action.CUT_REMOVE) + && recreatedEntityRefs != null + && !recreatedEntityRefs.isEmpty()) { + PrototypePlayerBuilderToolSettings prototypeSettings = ToolOperation.getOrCreatePrototypeSettings(playerRef.getUuid()); prototypeSettings.setLastTransformEntityRefs(recreatedEntityRefs); } @@ -962,7 +1041,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, public static class BuilderState { private static final MetricsRegistry STATE_METRICS_REGISTRY = new MetricsRegistry() - .register("Uuid", state -> state.player.getUuid(), Codec.UUID_STRING) + .register("Uuid", state -> state.playerRef.getUuid(), Codec.UUID_STRING) .register("Username", BuilderToolsPlugin.BuilderState::getDisplayName, Codec.STRING) .register("ActivePrefabPath", BuilderToolsPlugin.BuilderState::getActivePrefabPath, Codec.UUID_STRING) .register("Selection", BuilderToolsPlugin.BuilderState::getSelection, BlockSelection.METRICS_REGISTRY) @@ -970,6 +1049,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, .register("TaskCount", BuilderToolsPlugin.BuilderState::getTaskCount, Codec.INTEGER) .register("UndoCount", BuilderToolsPlugin.BuilderState::getUndoCount, Codec.INTEGER) .register("RedoCount", BuilderToolsPlugin.BuilderState::getRedoCount, Codec.INTEGER); + @Deprecated(forRemoval = true) private Player player; private PlayerRef playerRef; @Nonnull @@ -983,6 +1063,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, private volatile CompletableFuture taskFuture; private volatile long timestamp = Long.MAX_VALUE; private BlockSelection selection; + private boolean skipNextPreviewRebuild; + @Nullable + private BlockSelection preRotationSnapshot; private BlockMask globalMask; @Nonnull private Random random = new Random(26061984L); @@ -993,6 +1076,11 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, private Path prefabListPath; @Nullable private String prefabListSearchQuery; + @Nullable + private BlockSelection pendingUndoSnapshot; + private List pendingEntitySnapshots = new ArrayList<>(); + private List pendingEntityTransformSnapshots = new ArrayList<>(); + private int executionCountInGroup; private BuilderState(@Nonnull Player player, @Nonnull PlayerRef playerRef) { this.player = player; @@ -1028,7 +1116,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, BuilderToolsPlugin.get() .getLogger() .at(Level.FINE) - .log("[%s] Add task with ComponentAccessor to queue %s: %s, %s, %s", this.getDisplayName(), task, this.taskFuture, this.tasks); + .log("[%s] Add task with ComponentAccessor to queue %s: %s, %s", this.getDisplayName(), task, this.taskFuture, this.tasks); this.tasks.enqueue(new BuilderToolsPlugin.QueuedTask(task)); if (this.taskFuture == null || this.taskFuture.isDone()) { this.taskFuture = CompletableFutureUtil._catch(CompletableFuture.runAsync(this::runTask, this.player.getWorld())); @@ -1069,7 +1157,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } public void runTask() { - Ref ref = this.player.getReference(); + Ref ref = this.playerRef.getReference(); if (ref != null && ref.isValid()) { Store store = ref.getStore(); @@ -1182,6 +1270,11 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, public void setSelection(@Nonnull BlockSelection selection) { this.selection = selection; + this.preRotationSnapshot = null; + } + + public void setSkipNextPreviewRebuild(boolean skip) { + this.skipNextPreviewRebuild = skip; } public void sendSelectionToClient() { @@ -1215,21 +1308,21 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } private void sendFeedback(@Nonnull Message message, @Nonnull ComponentAccessor componentAccessor) { - BuilderToolsPlugin.sendFeedback(message, this.player, NotificationStyle.Default, componentAccessor); + BuilderToolsPlugin.sendFeedback(message, this.playerRef, NotificationStyle.Default, componentAccessor); } private void sendFeedback( @Nonnull Message message, @Nonnull NotificationStyle notificationStyle, @Nonnull ComponentAccessor componentAccessor ) { - BuilderToolsPlugin.sendFeedback(message, this.player, notificationStyle, componentAccessor); + BuilderToolsPlugin.sendFeedback(message, this.playerRef, notificationStyle, componentAccessor); } private void sendFeedback(@Nonnull String key, int total, @Nonnull ComponentAccessor componentAccessor) { - BuilderToolsPlugin.sendFeedback(key, total, this.player, componentAccessor); + BuilderToolsPlugin.sendFeedback(key, total, this.playerRef, componentAccessor); } private void sendFeedback(@Nonnull String key, int total, int num, @Nonnull ComponentAccessor componentAccessor) { - BuilderToolsPlugin.sendFeedback(key, total, num, this.player, componentAccessor); + BuilderToolsPlugin.sendFeedback(key, total, num, this.playerRef, componentAccessor); } public void setActivePrefabPath(UUID path) { @@ -1277,9 +1370,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, ToolOperation toolOperation; try { - toolOperation = ToolOperation.fromPacket(ref, this.player, packet, componentAccessor); - } catch (Exception var22) { - this.player.sendMessage(Message.translation("server.builderTools.interaction.toolParseError").param("error", var22.getMessage())); + toolOperation = ToolOperation.fromPacket(ref, this.player, this.playerRef, packet, componentAccessor); + } catch (Exception var23) { + this.playerRef.sendMessage(Message.translation("server.builderTools.interaction.toolParseError").param("error", var23.getMessage())); return 0; } @@ -1309,7 +1402,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, brushConfigCommandExecutor.getSequentialOperations().clear(); brushConfigCommandExecutor.getGlobalOperations().clear(); protoSettings.setLoadingBrush(true); - CommandManager.get().handleCommand(this.player, brushConfigId).thenAccept(unused -> { + CommandManager.get().handleCommand(this.playerRef, brushConfigId).thenAccept(unused -> { PrototypePlayerBuilderToolSettings protoSettingsIntl = ToolOperation.PROTOTYPE_TOOL_SETTINGS.get(uuidComponent.getUuid()); protoSettingsIntl.setLoadingBrush(false); protoSettingsIntl.setUsePrototypeBrushConfigurations(false); @@ -1322,8 +1415,11 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } if (protoSettings.usePrototypeBrushConfigurations()) { - toolOperation.executeAsBrushConfig(protoSettings, packet, componentAccessor); - return 0; + ItemStack activeItem = this.player.getInventory().getItemInHand(); + if (activeItem != null && activeItem.getItemId().equals(protoSettings.getPrototypeItemId())) { + toolOperation.executeAsBrushConfig(protoSettings, packet, componentAccessor); + return 0; + } } } @@ -1336,32 +1432,29 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, return 0; } else { for (Vector3i position : positionsToExecute) { - toolOperation.executeAt(position.getX(), position.getY(), position.getZ(), componentAccessor); + toolOperation.executeAt(position.x(), position.y(), position.z(), componentAccessor); } if (protoSettings != null) { - if (packet.isHoldDownInteraction) { - protoSettings.setLastBrushPosition(positionsToExecute.get(positionsToExecute.size() - 1)); - } else { - protoSettings.clearLastBrushPosition(); - } + protoSettings.setLastBrushPosition(positionsToExecute.get(positionsToExecute.size() - 1)); } EditOperation edit = toolOperation.getEditOperation(); BlockSelection before = edit.getBefore(); BlockSelection after = edit.getAfter(); - this.pushHistory(BuilderToolsPlugin.Action.EDIT, new BlockSelectionSnapshot(before)); - after.placeNoReturn("Use Builder Tool ?/?", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + int undoGroupSize = packet.undoGroupSize > 0 ? packet.undoGroupSize : 10; + this.handleBrushUndoGrouping(before, edit.getSpawnedEntityRefs(), edit.getMovedEntitySnapshots(), undoGroupSize, packet.isHoldDownInteraction); + after.placeNoReturn("Use Builder Tool ?/?", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; - int size = after.getBlockCount(); + int size = after.getBlockCount() + after.getFluidCount() + after.getTintCount(); int interpolatedCount = positionsToExecute.size(); BuilderToolsPlugin.get() .getLogger() .at(Level.FINE) .log("Took: %dns (%dms) to execute edit of %d blocks (%d positions)", diff, TimeUnit.NANOSECONDS.toMillis(diff), size, interpolatedCount); - if (protoSettings != null && protoSettings.isShouldShowEditorSettings()) { + if (size > 0 && protoSettings != null && protoSettings.isShouldShowEditorSettings() && toolOperation.showEditNotification()) { this.sendFeedback("Edit", size, componentAccessor); } @@ -1382,18 +1475,21 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, World world = componentAccessor.getExternalData().getWorld(); BlockSelection after = brushConfigEditStore.getAfter(); BlockSelection before = brushConfigEditStore.getBefore(); - this.pushHistory(BuilderToolsPlugin.Action.EDIT, new BlockSelectionSnapshot(before)); - after.placeNoReturn("Use Builder Tool ?/?", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + PrototypePlayerBuilderToolSettings prototypePlayerBuilderToolSettings = ToolOperation.PROTOTYPE_TOOL_SETTINGS.get(playerRefComponent.getUuid()); + BrushConfig brushConfig = brushConfigEditStore.getBrushConfig(); + int undoGroupSize = prototypePlayerBuilderToolSettings != null ? prototypePlayerBuilderToolSettings.getUndoGroupSize() : 10; + boolean isHoldDown = brushConfig != null && brushConfig.isHoldDownInteraction(); + this.handleBrushUndoGrouping(before, Collections.emptyList(), Collections.emptyList(), undoGroupSize, isHoldDown); + after.placeNoReturn("Use Builder Tool ?/?", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - startTime; - int size = after.getBlockCount(); + int size = after.getBlockCount() + after.getFluidCount() + after.getTintCount(); BuilderToolsPlugin.get() .getLogger() .at(Level.FINE) .log("Took: %dns (%dms) to execute edit of %d blocks", diff, TimeUnit.NANOSECONDS.toMillis(diff), size); - PrototypePlayerBuilderToolSettings prototypePlayerBuilderToolSettings = ToolOperation.PROTOTYPE_TOOL_SETTINGS.get(playerRefComponent.getUuid()); - if (prototypePlayerBuilderToolSettings != null && prototypePlayerBuilderToolSettings.isShouldShowEditorSettings()) { + if (size > 0 && prototypePlayerBuilderToolSettings != null && prototypePlayerBuilderToolSettings.isShouldShowEditorSettings()) { this.sendFeedback("Edit", size, componentAccessor); } } @@ -1459,7 +1555,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, int x = BlockUtil.unpackX(blockPosition); int y = BlockUtil.unpackY(blockPosition); int z = BlockUtil.unpackZ(blockPosition); - if (x >= min.getX() && y >= min.getY() && z >= min.getZ() && x <= max.getX() && y <= max.getY() && z <= max.getZ()) { + if (x >= min.x() && y >= min.y() && z >= min.z() && x <= max.x() && y <= max.y() && z <= max.z()) { BlockTypeAssetMap assetMap = BlockType.getAssetMap(); BlockType blockType = assetMap.getAsset(accessor.getBlock(x, y, z)); return accessor.getBlock(x, y, z) == targetBlockId || blockType.getDrawType() != DrawType.Cube && blockType.getDrawType() != DrawType.CubeWithModel; @@ -1561,28 +1657,6 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, return data; } - public void editLine( - int x1, - int y1, - int z1, - int x2, - int y2, - int z2, - BlockPattern material, - int lineWidth, - int lineHeight, - int wallThickness, - BrushShape shape, - BrushOrigin origin, - int spacing, - int density, - ComponentAccessor componentAccessor - ) { - this.editLine( - x1, y1, z1, x2, y2, z2, material, lineWidth, lineHeight, wallThickness, shape, origin, spacing, density, this.getGlobalMask(), componentAccessor - ); - } - public void editLine( int x1, int y1, @@ -1645,14 +1719,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, for (int sx = -iHalfWidth; sx <= iHalfWidth; sx++) { for (int sz = iHalfWidth; sz >= -iHalfWidth; sz--) { - int blockX = coord.getX() + sx; - int blockZ = coord.getZ() + sz; + int blockX = coord.x() + sx; + int blockZ = coord.z() + sz; WorldChunk chunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(blockX, blockZ)); for (int sy = -iHalfHeight; sy <= iHalfHeight; sy++) { - rel.assign(sx, sy, sz); + rel.set(sx, sy, sz); if (isInShape.test(rel)) { - int blockY = coord.getY() + sy + originOffset; + int blockY = coord.y() + sy + originOffset; int currentBlockId = chunk.getBlock(blockX, blockY, blockZ); int currentFluidId = chunk.getFluidId(blockX, blockY, blockZ); if ((mask == null || !mask.isExcluded(accessor, blockX, blockY, blockZ, min, max, currentBlockId, currentFluidId)) @@ -1677,17 +1751,17 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - after.placeNoReturn("Edit 1/1", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Edit 1/1", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; int size = after.getBlockCount(); - double length = new Vector3i(x1, y1, z1).distanceTo(x2, y2, z2); + double length = new Vector3i(x1, y1, z1).distance(x2, y2, z2); BuilderToolsPlugin.get() .getLogger() .at(Level.FINE) .log("Took: %dns (%dms) to execute editLine of %d blocks with length %s", diff, TimeUnit.NANOSECONDS.toMillis(diff), size, length); - this.sendFeedback(Message.translation("server.builderTools.drawLineOf").param("count", length), componentAccessor); + this.sendFeedback(Message.translation("server.builderTools.drawLineOf").param("count", Math.round(length)), componentAccessor); } private Predicate createShapePredicate( @@ -1700,9 +1774,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, return switch (shape) { case Cube -> coord -> { - double ax = Math.abs(coord.getX()); - double ay = Math.abs(coord.getY()); - double az = Math.abs(coord.getZ()); + double ax = Math.abs(coord.x()); + double ay = Math.abs(coord.y()); + double az = Math.abs(coord.z()); boolean inOuter = ax <= hw && ay <= hh && az <= hw; if (!hollow) { return inOuter; @@ -1712,9 +1786,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case Sphere -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); double outerDist = sx * sx / (hw * hw) + sy * sy / (hh * hh) + sz * sz / (hw * hw); boolean inOuter = outerDist <= 1.0; if (!hollow) { @@ -1726,9 +1800,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case Cylinder -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); double outerRadialDist = (sx * sx + sz * sz) / (hw * hw); boolean inOuterRadius = outerRadialDist <= 1.0 && Math.abs(sy) <= hh; if (!hollow) { @@ -1740,9 +1814,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case Cone -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); double normalizedY = (sy + hh) / (2.0F * hh); if (!(normalizedY < 0.0) && !(normalizedY > 1.0)) { double currentRadius = hw * (1.0 - normalizedY); @@ -1760,9 +1834,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case InvertedCone -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); double normalizedY = (sy + hh) / (2.0F * hh); if (!(normalizedY < 0.0) && !(normalizedY > 1.0)) { double currentRadius = hw * normalizedY; @@ -1780,9 +1854,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case Pyramid -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); double normalizedY = (sy + hh) / (2.0F * hh); if (!(normalizedY < 0.0) && !(normalizedY > 1.0)) { double currentHalfSize = hw * (1.0 - normalizedY); @@ -1799,9 +1873,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case InvertedPyramid -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); double normalizedY = (sy + hh) / (2.0F * hh); if (!(normalizedY < 0.0) && !(normalizedY > 1.0)) { double currentHalfSize = hw * normalizedY; @@ -1818,9 +1892,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case Dome -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); if (sy < 0.0) { return false; } else { @@ -1836,9 +1910,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case InvertedDome -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); if (sy > 0.0) { return false; } else { @@ -1854,9 +1928,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case Diamond -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); double normalizedY = Math.abs(sy) / hh; if (normalizedY > 1.0) { return false; @@ -1873,9 +1947,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } }; case Torus -> coord -> { - double sx = coord.getX(); - double sy = coord.getY(); - double sz = coord.getZ(); + double sx = coord.x(); + double sy = coord.y(); + double sz = coord.z(); double minorRadius = Math.max(1.0F, hh / 2.0F); double majorRadius = Math.max(1.0, hw - minorRadius); double minorRadiusAdjusted = minorRadius + 0.41F; @@ -1914,17 +1988,17 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, if (min == null) { min = new Vector3i(x - radiusAllowed, y - radiusAllowed, z - radiusAllowed); } else { - int minX = min.getX(); + int minX = min.x(); if (x - radiusAllowed > minX) { minX = x - radiusAllowed; } - int minY = min.getY(); + int minY = min.y(); if (y - radiusAllowed > minY) { minY = y - radiusAllowed; } - int minZ = min.getZ(); + int minZ = min.z(); if (z - radiusAllowed > minZ) { minZ = z - radiusAllowed; } @@ -1935,17 +2009,17 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, if (max == null) { max = new Vector3i(x + radiusAllowed, y + radiusAllowed, z + radiusAllowed); } else { - int maxX = max.getX(); + int maxX = max.x(); if (x + radiusAllowed < maxX) { maxX = x + radiusAllowed; } - int maxY = max.getY(); + int maxY = max.y(); if (y + radiusAllowed < maxY) { maxY = y + radiusAllowed; } - int maxZ = max.getZ(); + int maxZ = max.z(); if (z + radiusAllowed < maxZ) { maxZ = z + radiusAllowed; } @@ -1953,16 +2027,16 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, max = new Vector3i(maxX, maxY, maxZ); } - int totalBlocks = (max.getX() - min.getX() + 1) * (max.getZ() - min.getZ() + 1) * (max.getY() - min.getY() + 1); + int totalBlocks = (max.x() - min.x() + 1) * (max.z() - min.z() + 1) * (max.y() - min.y() + 1); BlockSelection before = new BlockSelection(totalBlocks, 0); before.setPosition(x + normalX, y + normalY, z + normalZ); before.setSelectionArea(min, max); this.pushHistory(BuilderToolsPlugin.Action.EXTRUDE, new BlockSelectionSnapshot(before)); BlockSelection after = new BlockSelection(totalBlocks, 0); after.copyPropertiesFrom(before); - if (x >= min.getX() && x <= max.getX()) { - if (y >= min.getY() && y <= max.getY()) { - if (z >= min.getZ() && z <= max.getZ()) { + if (x >= min.x() && x <= max.x()) { + if (y >= min.y() && y <= max.y()) { + if (z >= min.z() && z <= max.z()) { int testBlock = accessor.getBlock(x - normalX, y - normalY, z - normalZ); BlockType testBlockType = BlockType.getAssetMap().getAsset(testBlock); if (testBlockType != null && (testBlockType.getDrawType() == DrawType.Cube || testBlockType.getDrawType() == DrawType.CubeWithModel)) { @@ -1972,7 +2046,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, Vector3i surfaceMin = new Vector3i(x - radiusAllowed * xMod, y - radiusAllowed * yMod, z - radiusAllowed * zMod); Vector3i surfaceMax = new Vector3i(x + radiusAllowed * xMod, y + radiusAllowed * yMod, z + radiusAllowed * zMod); this.extendFaceFindBlocks(accessor, before, after, normalX, normalY, normalZ, extrudeDepth, blockId, min, max, surfaceMin, surfaceMax); - after.placeNoReturn("Set", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Set", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -2002,12 +2076,12 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, @Nonnull Vector3i surfaceMin, @Nonnull Vector3i surfaceMax ) { - int xMin = surfaceMin.getX(); - int yMin = surfaceMin.getY(); - int zMin = surfaceMin.getZ(); - int xMax = surfaceMax.getX(); - int yMax = surfaceMax.getY(); - int zMax = surfaceMax.getZ(); + int xMin = surfaceMin.x(); + int yMin = surfaceMin.y(); + int zMin = surfaceMin.z(); + int xMax = surfaceMax.x(); + int yMax = surfaceMax.y(); + int zMax = surfaceMax.z(); for (int x = xMin; x <= xMax; x++) { for (int z = zMin; z <= zMax; z++) { @@ -2072,102 +2146,26 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } else { World world = componentAccessor.getExternalData().getWorld(); int count = 0; - int minX = this.selection.getSelectionMin().getX(); - int minZ = this.selection.getSelectionMin().getZ(); - int maxX = this.selection.getSelectionMax().getX(); - int maxZ = this.selection.getSelectionMax().getZ(); + int minX = this.selection.getSelectionMin().x(); + int minZ = this.selection.getSelectionMin().z(); + int maxX = this.selection.getSelectionMax().x(); + int maxZ = this.selection.getSelectionMax().z(); + BlockSelection place = new BlockSelection(); + place.setPosition(minX, 0, minZ); - for (int cx = ChunkUtil.chunkCoordinate(minX); cx <= ChunkUtil.chunkCoordinate(maxX); cx++) { - for (int cz = ChunkUtil.chunkCoordinate(minZ); cz <= ChunkUtil.chunkCoordinate(maxZ); cz++) { - int startX = Math.max(0, minX - ChunkUtil.minBlock(cx)); - int startZ = Math.max(0, minZ - ChunkUtil.minBlock(cz)); - int endX = Math.min(32, maxX - ChunkUtil.minBlock(cx)); - int endZ = Math.min(32, maxZ - ChunkUtil.minBlock(cz)); - WorldChunk chunk = world.getNonTickingChunk(ChunkUtil.indexChunk(cx, cz)); - - for (int z = startZ; z < endZ; z++) { - for (int x = startX; x < endX; x++) { - chunk.getBlockChunk().setTint(x, z, color); - count++; - } - } - - world.getNotificationHandler().updateChunk(chunk.getIndex()); + for (int x = minX; x < maxX; x++) { + for (int z = minZ; z < maxZ; z++) { + place.addTintAtWorldPos(x, z, color); + count++; } } + BlockSelection before = place.place(null, world); + this.pushHistory(BuilderToolsPlugin.Action.EDIT, new BlockSelectionSnapshot(before)); this.sendFeedback(Message.translation("server.builderTools.setColumnsTint").param("count", count), componentAccessor); } } - public void tint( - int x, int y, int z, int color, @Nonnull BrushShape shape, int shapeRange, int shapeHeight, ComponentAccessor componentAccessor - ) { - if (y >= 0 && y < 320) { - World world = componentAccessor.getExternalData().getWorld(); - LongSet dirtyChunks = new LongOpenHashSet(); - AtomicInteger count = new AtomicInteger(0); - LocalCachedChunkAccessor accessor = LocalCachedChunkAccessor.atWorldCoords(world, x, z, shapeRange + 1); - TriIntObjPredicate tintBlock = (pxx, py, pzx, aVoid) -> { - WorldChunk chunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(pxx, pzx)); - chunk.getBlockChunk().setTint(pxx, pzx, color); - dirtyChunks.add(chunk.getIndex()); - count.getAndIncrement(); - return true; - }; - if (shapeRange <= 1) { - tintBlock.test(x, y, z, null); - } else { - int radiusXZ = shapeRange / 2; - switch (shape) { - case Cube: - case Pyramid: - case InvertedPyramid: - case Diamond: - label55: - for (int px = -radiusXZ; px <= radiusXZ; px++) { - for (int pzx = -radiusXZ; pzx <= radiusXZ; pzx++) { - if (!tintBlock.test(x + px, y, z + pzx, null)) { - break label55; - } - } - } - break; - case Sphere: - case Cylinder: - case Cone: - case InvertedCone: - case Dome: - case InvertedDome: - BlockSphereUtil.forEachBlock(x, y, z, radiusXZ, 1, radiusXZ, null, tintBlock); - break; - case Torus: - int minorRadius = Math.max(1, shapeHeight / 4); - int majorRadius = Math.max(1, radiusXZ - minorRadius); - int sizeXZ = majorRadius + minorRadius; - float minorRadiusAdjusted = minorRadius + 0.5F; - - for (int px = -sizeXZ; px <= sizeXZ; px++) { - for (int pz = -sizeXZ; pz <= sizeXZ; pz++) { - double distFromCenter = Math.sqrt(px * px + pz * pz); - double distFromRing = Math.abs(distFromCenter - majorRadius); - if (distFromRing <= minorRadiusAdjusted) { - tintBlock.test(x + px, y, z + pz, null); - } - } - } - break; - default: - this.sendFeedback(Message.translation("server.builderTools.errorWithUsedShape"), componentAccessor); - return; - } - } - - dirtyChunks.forEach(value -> world.getNotificationHandler().updateChunk(value)); - this.sendFeedback(Message.translation("server.builderTools.setColumnsTint").param("count", count.intValue()), componentAccessor); - } - } - public void environment(@Nonnull Ref ref, int environmentId, @Nonnull ComponentAccessor componentAccessor) { if (this.selection == null) { this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelection"), componentAccessor); @@ -2178,12 +2176,12 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, LongSet dirtyChunks = new LongOpenHashSet(); int count = 0; - for (int x = this.selection.getSelectionMin().getX(); x < this.selection.getSelectionMax().getX(); x++) { - for (int z = this.selection.getSelectionMin().getZ(); z < this.selection.getSelectionMax().getZ(); z++) { + for (int x = this.selection.getSelectionMin().x(); x < this.selection.getSelectionMax().x(); x++) { + for (int z = this.selection.getSelectionMin().z(); z < this.selection.getSelectionMax().z(); z++) { WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)); dirtyChunks.add(chunk.getIndex()); - for (int y = this.selection.getSelectionMin().getY(); y < this.selection.getSelectionMax().getY(); y++) { + for (int y = this.selection.getSelectionMin().y(); y < this.selection.getSelectionMax().y(); y++) { chunk.getBlockChunk().setEnvironment(x, y, z, environmentId); count++; } @@ -2254,176 +2252,193 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, int width = xMax - xMin; int height = yMax - yMin; int depth = zMax - zMin; - int halfWidth = width / 2; - int halfDepth = depth / 2; - if (cut) { - before = new BlockSelection(); - before.setPosition(xMin + halfWidth, yMin, zMin + halfDepth); - after = new BlockSelection(before); - snapshots = new ObjectArrayList<>(); - this.pushHistory(BuilderToolsPlugin.Action.CUT_COPY, ClipboardContentsSnapshot.copyOf(this.selection)); + long selectionVolume = (long)(width + 1) * (depth + 1) * (Math.abs(height) + 1); + if (selectionVolume > 6600000L) { + NotificationUtil.sendNotification( + this.playerRef.getPacketHandler(), + Message.translation("server.builderTools.copycut.tooLarge"), + Message.translation("server.builderTools.copycut.tooLarge.detail").param("overCount", selectionVolume - 4000000L), + NotificationStyle.Warning + ); + SoundUtil.playSoundEvent2d(ref, TempAssetIdUtil.getSoundEventIndex("CREATE_ERROR"), SoundCategory.UI, componentAccessor); + return 0; } else { - this.pushHistory(BuilderToolsPlugin.Action.COPY, ClipboardContentsSnapshot.copyOf(this.selection)); - } - - LocalCachedChunkAccessor accessor = LocalCachedChunkAccessor.atWorldCoords(world, xMin + halfWidth, zMin + halfDepth, Math.max(width, depth)); - BlockTypeAssetMap assetMap = BlockType.getAssetMap(); - int editorBlock = assetMap.getIndex("Editor_Block"); - if (editorBlock == Integer.MIN_VALUE) { - throw new IllegalArgumentException("Unknown key! Editor_Block"); - } else { - int editorBlockPrefabAir = assetMap.getIndex("Editor_Empty"); - if (editorBlockPrefabAir == Integer.MIN_VALUE) { - throw new IllegalArgumentException("Unknown key! Editor_Empty"); + int halfWidth = width / 2; + int halfDepth = depth / 2; + if (cut) { + before = new BlockSelection(); + before.setPosition(xMin + halfWidth, yMin, zMin + halfDepth); + after = new BlockSelection(before); + snapshots = new ObjectArrayList<>(); + this.pushHistory(BuilderToolsPlugin.Action.CUT_COPY, ClipboardContentsSnapshot.copyOf(this.selection)); } else { - int editorBlockPrefabAnchor = assetMap.getIndex("Editor_Anchor"); - if (editorBlockPrefabAnchor == Integer.MIN_VALUE) { - throw new IllegalArgumentException("Unknown key! Editor_Anchor"); + this.pushHistory(BuilderToolsPlugin.Action.COPY, ClipboardContentsSnapshot.copyOf(this.selection)); + } + + LocalCachedChunkAccessor accessor = LocalCachedChunkAccessor.atWorldCoords(world, xMin + halfWidth, zMin + halfDepth, Math.max(width, depth)); + BlockTypeAssetMap assetMap = BlockType.getAssetMap(); + int editorBlock = assetMap.getIndex("Editor_Block"); + if (editorBlock == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! Editor_Block"); + } else { + int editorBlockPrefabAir = assetMap.getIndex("Editor_Empty"); + if (editorBlockPrefabAir == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! Editor_Empty"); } else { - Set anchors = new HashSet<>(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - this.selection = new BlockSelection(); - this.selection.setPosition(xMin + halfWidth, yMin, zMin + halfDepth); - this.selection.setSelectionArea(min, max); - int count = 0; - int counter = 0; - int top = Math.max(yMin, yMax); - int bottom = Math.min(yMin, yMax); - int totalBlocks = (width + 1) * (depth + 1) * (top - bottom + 1); + int editorBlockPrefabAnchor = assetMap.getIndex("Editor_Anchor"); + if (editorBlockPrefabAnchor == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! Editor_Anchor"); + } else { + Set anchors = new HashSet<>(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + this.selection = new BlockSelection(); + this.selection.setPosition(xMin + halfWidth, yMin, zMin + halfDepth); + this.selection.setSelectionArea(min, max); + int count = 0; + int counter = 0; + int top = Math.max(yMin, yMax); + int bottom = Math.min(yMin, yMax); + int totalBlocks = (width + 1) * (depth + 1) * (top - bottom + 1); - for (int x = xMin; x <= xMax; x++) { - for (int z = zMin; z <= zMax; z++) { - WorldChunk chunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(x, z)); - Store store = chunk.getReference().getStore(); - ChunkColumn chunkColumn = store.getComponent(chunk.getReference(), ChunkColumn.getComponentType()); - int lastSection = -1; - BlockPhysics blockPhysics = null; + for (int x = xMin; x <= xMax; x++) { + for (int z = zMin; z <= zMax; z++) { + WorldChunk chunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(x, z)); + Store store = chunk.getReference().getStore(); + ChunkColumn chunkColumn = store.getComponent(chunk.getReference(), ChunkColumn.getComponentType()); + int lastSection = -1; + BlockPhysics blockPhysics = null; - for (int y = top; y >= bottom; y--) { - int block = chunk.getBlock(x, y, z); - int fluid = chunk.getFluidId(x, y, z); - if (lastSection != ChunkUtil.chunkCoordinate(y)) { - lastSection = ChunkUtil.chunkCoordinate(y); - Ref section = chunkColumn.getSection(lastSection); - if (section != null) { - blockPhysics = store.getComponent(section, BlockPhysics.getComponentType()); - } else { - blockPhysics = null; - } - } - - if (blocks && cut && (block != 0 || fluid != 0 || empty)) { - before.copyFromAtWorld(x, y, z, chunk, blockPhysics); - after.addEmptyAtWorldPos(x, y, z); - } - - if (block == editorBlockPrefabAnchor && !keepAnchors && playerAnchor == null) { - anchors.add(new Vector3i(x, y, z)); - this.selection.setAnchorAtWorldPos(x, y, z); - if (blocks) { - int id = BuilderToolsPlugin.getNonEmptyNeighbourBlock(accessor, x, y, z); - if (id > 0 && id != editorBlockPrefabAir) { - this.selection.addBlockAtWorldPos(x, y, z, id, 0, 0, 0); - count++; - } else if (id == editorBlockPrefabAir) { - this.selection.addBlockAtWorldPos(x, y, z, 0, 0, 0, 0); - count++; + for (int y = top; y >= bottom; y--) { + int block = chunk.getBlock(x, y, z); + int fluid = chunk.getFluidId(x, y, z); + if (lastSection != ChunkUtil.chunkCoordinate(y)) { + lastSection = ChunkUtil.chunkCoordinate(y); + Ref section = chunkColumn.getSection(lastSection); + if (section != null) { + blockPhysics = store.getComponent(section, BlockPhysics.getComponentType()); + } else { + blockPhysics = null; } } - } else if (blocks && (block != 0 || fluid != 0 || empty) && block != editorBlock) { - if (block == editorBlockPrefabAir) { - this.selection.addBlockAtWorldPos(x, y, z, 0, 0, 0, 0); - } else { + + if (blocks && cut && (block != 0 || fluid != 0 || empty)) { + before.copyFromAtWorld(x, y, z, chunk, blockPhysics); + after.addEmptyAtWorldPos(x, y, z); + } + + if (block == editorBlockPrefabAnchor && !keepAnchors && playerAnchor == null) { + anchors.add(new Vector3i(x, y, z)); + this.selection.setAnchorAtWorldPos(x, y, z); + if (blocks) { + int id = BuilderToolsPlugin.getNonEmptyNeighbourBlock(accessor, x, y, z); + if (id > 0 && id != editorBlockPrefabAir) { + this.selection.addBlockAtWorldPos(x, y, z, id, 0, 0, 0); + count++; + } else if (id == editorBlockPrefabAir) { + this.selection.addBlockAtWorldPos(x, y, z, 0, 0, 0, 0); + count++; + } + } + } else if (blocks && (block != 0 || fluid != 0 || empty) && block != editorBlock) { this.selection.copyFromAtWorld(x, y, z, chunk, blockPhysics); + count++; } - count++; + counter++; + this.sendFeedback(cut ? "Gather 1/2" : "Gather 1/1", totalBlocks, counter, componentAccessor); + } + } + } + + if (count > 4000000) { + this.selection = new BlockSelection(); + NotificationUtil.sendNotification( + this.playerRef.getPacketHandler(), + Message.translation("server.builderTools.copycut.tooLarge"), + Message.translation("server.builderTools.copycut.tooLarge.detail").param("overCount", count - 4000000), + NotificationStyle.Warning + ); + SoundUtil.playSoundEvent2d(ref, TempAssetIdUtil.getSoundEventIndex("CREATE_ERROR"), SoundCategory.UI, componentAccessor); + return 0; + } else if (anchors.size() > 1 && playerAnchor == null) { + StringBuilder sb = new StringBuilder("Anchors: "); + boolean first = true; + + for (Vector3i anchor : anchors) { + if (!first) { + sb.append(", "); } - counter++; - this.sendFeedback(cut ? "Gather 1/2" : "Gather 1/1", totalBlocks, counter, componentAccessor); - } - } - } - - if (anchors.size() > 1 && playerAnchor == null) { - StringBuilder sb = new StringBuilder("Anchors: "); - boolean first = true; - - for (Vector3i anchor : anchors) { - if (!first) { - sb.append(", "); + first = false; + sb.append('[').append(anchor.x()).append(", ").append(anchor.y()).append(", ").append(anchor.z()).append(']'); } - first = false; - sb.append('[').append(anchor.getX()).append(", ").append(anchor.getY()).append(", ").append(anchor.getZ()).append(']'); - } + throw new PrefabCopyException("Prefab has multiple anchor blocks!\n" + sb); + } else { + if (playerAnchor != null) { + this.selection.setAnchorAtWorldPos(playerAnchor.x(), playerAnchor.y(), playerAnchor.z()); + } - throw new PrefabCopyException("Prefab has multiple anchor blocks!\n" + sb); - } else { - if (playerAnchor != null) { - this.selection.setAnchorAtWorldPos(playerAnchor.getX(), playerAnchor.getY(), playerAnchor.getZ()); - } - - if (entities) { - Store store = world.getEntityStore().getStore(); - ArrayList> entitiesToRemove = cut ? new ArrayList<>() : null; - BuilderToolsPlugin.forEachCopyableInSelection(world, xMin, yMin, zMin, width, height, depth, e -> { - Holder holder = store.copyEntity(e); - this.selection.addEntityFromWorld(holder); - if (cut) { - boolean shouldSkip = skipEntityRemoveSnapshotFor != null && skipEntityRemoveSnapshotFor.contains(e); - if (!shouldSkip) { - snapshots.add(new EntityRemoveSnapshot(e)); - entitiesToRemove.add(e); + if (entities) { + Store store = world.getEntityStore().getStore(); + ReferenceArrayList> entitiesToRemove = cut ? new ReferenceArrayList<>() : null; + BuilderToolsPlugin.forEachCopyableInSelection(world, xMin, yMin, zMin, width, height, depth, e -> { + Holder holder = store.copyEntity(e); + this.selection.addEntityFromWorld(holder); + if (cut) { + boolean shouldSkip = skipEntityRemoveSnapshotFor != null && skipEntityRemoveSnapshotFor.contains(e); + if (!shouldSkip) { + snapshots.add(new EntityRemoveSnapshot(e)); + entitiesToRemove.add(e); + } + } + }); + if (cut && entitiesToRemove != null) { + for (Ref e : entitiesToRemove) { + store.removeEntity(e, RemoveReason.UNLOAD); } } - }); - if (cut && entitiesToRemove != null) { - for (Ref e : entitiesToRemove) { - store.removeEntity(e, RemoveReason.UNLOAD); - } } - } - if (cut) { - snapshots.add(new BlockSelectionSnapshot(before)); - this.pushHistory(BuilderToolsPlugin.Action.CUT_REMOVE, snapshots); - } + if (cut) { + snapshots.add(new BlockSelectionSnapshot(before)); + this.pushHistory(BuilderToolsPlugin.Action.CUT_REMOVE, snapshots); + } - if (after != null) { - after.placeNoReturn("Cut 2/2", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); - BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); - } + if (after != null) { + after.placeNoReturn("Cut 2/2", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); + } - long end = System.nanoTime(); - long diff = end - start; - BuilderToolsPlugin.get() - .getLogger() - .at(Level.FINE) - .log("Took: %dns (%dms) to execute copy of %d blocks", diff, TimeUnit.NANOSECONDS.toMillis(diff), count); - if (cut) { - this.sendUpdate(); - } else { - this.player.getPlayerConnection().write(Objects.requireNonNullElseGet(this.selection, BlockSelection::new).toPacketWithSelection()); - } + long end = System.nanoTime(); + long diff = end - start; + BuilderToolsPlugin.get() + .getLogger() + .at(Level.FINE) + .log("Took: %dns (%dms) to execute copy of %d blocks", diff, TimeUnit.NANOSECONDS.toMillis(diff), count); + if (cut) { + this.sendUpdate(); + } else { + this.playerRef.getPacketHandler().write(Objects.requireNonNullElseGet(this.selection, BlockSelection::new).toPacketWithSelection()); + } - int entityCount = entities ? this.selection.getEntityCount() : 0; - String translationKey; - if (cut) { - translationKey = entityCount > 0 ? "server.builderTools.cutWithEntities" : "server.builderTools.cut"; - } else { - translationKey = entityCount > 0 ? "server.builderTools.copiedWithEntities" : "server.builderTools.copied"; - } + int entityCount = entities ? this.selection.getEntityCount() : 0; + String translationKey; + if (cut) { + translationKey = entityCount > 0 ? "server.builderTools.cutWithEntities" : "server.builderTools.cut"; + } else { + translationKey = entityCount > 0 ? "server.builderTools.copiedWithEntities" : "server.builderTools.copied"; + } - this.sendFeedback( - ref, - Message.translation(translationKey).param("blockCount", count).param("entityCount", entityCount), - cut ? "SFX_CREATE_CUT" : "SFX_CREATE_COPY", - componentAccessor - ); - return count; + this.sendFeedback( + ref, + Message.translation(translationKey).param("blockCount", count).param("entityCount", entityCount), + cut ? "SFX_CREATE_CUT" : "SFX_CREATE_COPY", + componentAccessor + ); + return count; + } } } } @@ -2480,7 +2495,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - after.placeNoReturn("Clear 2/2", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Clear 2/2", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -2493,22 +2508,30 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, return size; } + private static Vector3d rotateByEulerMatrix(@Nonnull Vector3dc v, @Nonnull RotationTuple t) { + Vector3d r = new Vector3d(v); + t.roll().rotateZ(r, r); + t.pitch().rotateX(r, r); + t.yaw().rotateY(r, r); + return r; + } + public static RotationTuple transformRotation(RotationTuple prevRot, Quaterniond rotation) { - Vector3f forwardVec = new Vector3f(1.0F, 0.0F, 0.0F); - Vector3f upVec = new Vector3f(0.0F, 1.0F, 0.0F); - forwardVec = Rotation.rotate(forwardVec, prevRot.yaw(), prevRot.pitch(), prevRot.roll()); - upVec = Rotation.rotate(upVec, prevRot.yaw(), prevRot.pitch(), prevRot.roll()); - org.joml.Vector3d fwd = rotation.transform(new org.joml.Vector3d(forwardVec.x, forwardVec.y, forwardVec.z)); - org.joml.Vector3d up = rotation.transform(new org.joml.Vector3d(upVec.x, upVec.y, upVec.z)); - Vector3f newForward = new Vector3f((float)fwd.x, (float)fwd.y, (float)fwd.z); - Vector3f newUp = new Vector3f((float)up.x, (float)up.y, (float)up.z); - float bestScore = Float.MIN_VALUE; + Vector3d forwardVec = new Vector3d(1.0, 0.0, 0.0); + Vector3d upVec = new Vector3d(0.0, 1.0, 0.0); + forwardVec = rotateByEulerMatrix(forwardVec, prevRot); + upVec = rotateByEulerMatrix(upVec, prevRot); + Vector3d fwd = rotation.transform(new Vector3d(forwardVec.x, forwardVec.y, forwardVec.z)); + Vector3d up = rotation.transform(new Vector3d(upVec.x, upVec.y, upVec.z)); + Vector3d newForward = new Vector3d(fwd.x, fwd.y, fwd.z); + Vector3d newUp = new Vector3d(up.x, up.y, up.z); + double bestScore = Float.MIN_VALUE; RotationTuple bestRot = prevRot; for (RotationTuple rot : RotationTuple.VALUES) { - Vector3f rotForward = Rotation.rotate(new Vector3f(1.0F, 0.0F, 0.0F), rot.yaw(), rot.pitch(), rot.roll()); - Vector3f rotUp = Rotation.rotate(new Vector3f(0.0F, 1.0F, 0.0F), rot.yaw(), rot.pitch(), rot.roll()); - float score = rotForward.dot(newForward) + rotUp.dot(newUp); + Vector3d rotForward = rotateByEulerMatrix(new Vector3d(1.0, 0.0, 0.0), rot); + Vector3d rotUp = rotateByEulerMatrix(new Vector3d(0.0, 1.0, 0.0), rot); + double score = rotForward.dot(newForward) + rotUp.dot(newUp); if (score > bestScore) { bestScore = score; bestRot = rot; @@ -2518,20 +2541,13 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, return bestRot; } - private void transformEntityRotation(Vector3f rotation, Quaterniond deltaQuat) { - Quaterniond originalQuat = new Quaterniond().rotationYXZ(rotation.y, rotation.x, rotation.z); - Quaterniond resultQuat = deltaQuat.mul(originalQuat, new Quaterniond()); - org.joml.Vector3d eulerAngles = resultQuat.getEulerAnglesYXZ(new org.joml.Vector3d()); - rotation.assign((float)eulerAngles.x, (float)eulerAngles.y, (float)eulerAngles.z); - } - public void transformThenPasteClipboard( @Nonnull BlockChange[] blockChanges, @Nullable PrototypePlayerBuilderToolSettings.FluidChange[] fluidChanges, @Nullable PrototypePlayerBuilderToolSettings.EntityChange[] entityChanges, @Nonnull Quaterniond rotation, @Nonnull Vector3i translationOffset, - @Nonnull Vector3f rotationOrigin, + @Nonnull Rotation3f rotationOrigin, @Nonnull Vector3i initialPastePoint, boolean keepEmptyBlocks, @Nonnull PrototypePlayerBuilderToolSettings prototypeSettings, @@ -2540,7 +2556,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, World world = componentAccessor.getExternalData().getWorld(); long start = System.nanoTime(); BlockTypeAssetMap assetMap = BlockType.getAssetMap(); - int editorBlockPrefabAir = keepEmptyBlocks ? assetMap.getIndex("Editor_Empty") : 0; + int editorBlockPrefabAir = assetMap.getIndex("Editor_Empty"); int yOffsetOutOfGround = 0; for (BlockChange blockChange : blockChanges) { @@ -2562,7 +2578,13 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, int maxX = Integer.MIN_VALUE; int maxY = Integer.MIN_VALUE; int maxZ = Integer.MIN_VALUE; - org.joml.Vector3d mutableVec = new org.joml.Vector3d(); + + record RotatedBlock(Vector3i location, int blockId, int newRotation, Holder holder, BlockType blockType, BlockBoundingBoxes hitbox) { + } + + ObjectArrayList rotatedBlocks = new ObjectArrayList<>(blockChanges.length); + LongOpenHashSet basePositions = new LongOpenHashSet(blockChanges.length); + Vector3d mutableVec = new Vector3d(); for (BlockChange blockChangex : blockChanges) { mutableVec.set( @@ -2583,47 +2605,74 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, maxX = Math.max(maxX, rotatedLocation.x); maxY = Math.max(maxY, rotatedLocation.y); maxZ = Math.max(maxZ, rotatedLocation.z); - WorldChunk currentChunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(rotatedLocation.x, rotatedLocation.z)); - Holder holder = currentChunk.getBlockComponentHolder(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); - int blockIdInRotatedLocation = currentChunk.getBlock(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); - int filler = currentChunk.getFiller(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); - int blockRotation = currentChunk.getRotationIndex(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); - before.addBlockAtWorldPos(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z, blockIdInRotatedLocation, blockRotation, filler, 0, holder); - int originalFluidId = currentChunk.getFluidId(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); - byte originalFluidLevel = currentChunk.getFluidLevel(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); - before.addFluidAtWorldPos(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z, originalFluidId, originalFluidLevel); int newRotation = transformRotation(RotationTuple.get(blockChangex.rotation), rotation).index(); int blockIdToPlace = blockChangex.block; - if (blockChangex.block == 0 && keepEmptyBlocks) { - blockIdToPlace = editorBlockPrefabAir; + if (blockChangex.block == editorBlockPrefabAir && !keepEmptyBlocks) { + blockIdToPlace = 0; } BlockType blockType = assetMap.getAsset(blockIdToPlace); if (blockType != null) { BlockBoundingBoxes hitbox = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); if (hitbox != null) { - int finalBlockIdToPlace = blockIdToPlace; - if (hitbox.protrudesUnitBox()) { - FillerBlockUtil.forEachFillerBlock( - hitbox.get(newRotation), - (x, y, z) -> after.addBlockAtWorldPos( - rotatedLocation.x + x, - rotatedLocation.y + y, - rotatedLocation.z + z, - finalBlockIdToPlace, - newRotation, - FillerBlockUtil.pack(x, y, z), - 0, - holder - ) - ); - } else { - after.addBlockAtWorldPos(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z, blockIdToPlace, newRotation, 0, 0, holder); - } + WorldChunk currentChunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(rotatedLocation.x, rotatedLocation.z)); + Holder holder = currentChunk.getBlockComponentHolder(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); + rotatedBlocks.add(new RotatedBlock(rotatedLocation, blockIdToPlace, newRotation, holder, blockType, hitbox)); + basePositions.add(BlockUtil.pack(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z)); } } } + for (RotatedBlock rb : rotatedBlocks) { + Vector3i rotatedLocationx = rb.location(); + int blockIdToPlacex = rb.blockId(); + int newRotationx = rb.newRotation(); + Holder holder = rb.holder(); + WorldChunk currentChunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(rotatedLocationx.x, rotatedLocationx.z)); + int blockIdInRotatedLocation = currentChunk.getBlock(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z); + int filler = currentChunk.getFiller(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z); + int blockRotation = currentChunk.getRotationIndex(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z); + before.addBlockAtWorldPos(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z, blockIdInRotatedLocation, blockRotation, filler, 0, holder); + int originalFluidId = currentChunk.getFluidId(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z); + byte originalFluidLevel = currentChunk.getFluidLevel(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z); + before.addFluidAtWorldPos(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z, originalFluidId, originalFluidLevel); + if (rb.hitbox().protrudesUnitBox()) { + FillerBlockUtil.forEachFillerBlock( + rb.hitbox().get(newRotationx), + (x, y, z) -> { + if (x != 0 || y != 0 || z != 0) { + int fx = rotatedLocation.x + x; + int fy = rotatedLocation.y + y; + int fz = rotatedLocation.z + z; + if (!before.hasBlockAtWorldPos(fx, fy, fz)) { + WorldChunk fillerChunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(fx, fz)); + before.addBlockAtWorldPos( + fx, + fy, + fz, + fillerChunk.getBlock(fx, fy, fz), + fillerChunk.getRotationIndex(fx, fy, fz), + fillerChunk.getFiller(fx, fy, fz), + 0, + fillerChunk.getBlockComponentHolder(fx, fy, fz) + ); + } + } + } + ); + FillerBlockUtil.forEachFillerBlock(rb.hitbox().get(newRotationx), (x, y, z) -> { + int fx = rotatedLocation.x + x; + int fy = rotatedLocation.y + y; + int fz = rotatedLocation.z + z; + if (x == 0 && y == 0 && z == 0 || !basePositions.contains(BlockUtil.pack(fx, fy, fz))) { + after.addBlockAtWorldPos(fx, fy, fz, blockIdToPlace, newRotation, FillerBlockUtil.pack(x, y, z), 0, holder); + } + }); + } else { + after.addBlockAtWorldPos(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z, blockIdToPlacex, newRotationx, 0, 0, holder); + } + } + int finalYOffsetOutOfGround = yOffsetOutOfGround; if (fluidChanges != null) { for (PrototypePlayerBuilderToolSettings.FluidChange fluidChange : fluidChanges) { @@ -2649,24 +2698,24 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, Store entityStore = world.getEntityStore().getStore(); for (Ref ref : previousEntityRefs) { - if (ref.isValid()) { + if (ref != null && ref.isValid()) { previousEntitySnapshots.add(new EntityRemoveSnapshot(ref)); entityStore.removeEntity(ref, RemoveReason.UNLOAD); } } } - List> addedEntityRefs = new ArrayList<>(); + List> addedEntityRefs = new ReferenceArrayList<>(); if (entityChanges != null && entityChanges.length > 0) { - org.joml.Vector3d mutableEntityPos = new org.joml.Vector3d(); + Vector3d mutableEntityPos = new Vector3d(); for (PrototypePlayerBuilderToolSettings.EntityChange entityChange : entityChanges) { boolean isBlockEntity = entityChange.entityHolder().getComponent(BlockEntity.getComponentType()) != null; double blockCenterOffset = isBlockEntity ? 0.5 : 0.0; mutableEntityPos.set( - entityChange.x() - rotationOrigin.x, - entityChange.y() + blockCenterOffset - rotationOrigin.y + finalYOffsetOutOfGround, - entityChange.z() - rotationOrigin.z + entityChange.x() + initialPastePoint.x - rotationOrigin.x, + entityChange.y() + blockCenterOffset + initialPastePoint.y - rotationOrigin.y + finalYOffsetOutOfGround, + entityChange.z() + initialPastePoint.z - rotationOrigin.z ); rotation.transform(mutableEntityPos); mutableEntityPos.add(translationOffset.x, translationOffset.y, translationOffset.z); @@ -2676,23 +2725,31 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, Holder clonedHolder = entityChange.entityHolder().clone(); TransformComponent transformComponent = clonedHolder.getComponent(TransformComponent.getComponentType()); if (transformComponent != null && transformComponent.getPosition() != null) { - transformComponent.getPosition().assign(newX, newY, newZ); - Vector3f entityRotation = transformComponent.getRotation(); + transformComponent.getPosition().set(newX, newY, newZ); + Rotation3f entityRotation = transformComponent.getRotation(); if (entityRotation != null) { - this.transformEntityRotation(entityRotation, rotation); + entityRotation.premul(rotation); } } HeadRotation headRotation = clonedHolder.getComponent(HeadRotation.getComponentType()); if (headRotation != null && headRotation.getRotation() != null) { - this.transformEntityRotation(headRotation.getRotation(), rotation); + headRotation.getRotation().premul(rotation); } clonedHolder.putComponent(UUIDComponent.getComponentType(), new UUIDComponent(UUID.randomUUID())); - clonedHolder.removeComponent(EntityTrackerSystems.Visible.getComponentType()); - clonedHolder.removeComponent(NetworkId.getComponentType()); + if (clonedHolder.getComponent(EntityTrackerSystems.Visible.getComponentType()) != null) { + clonedHolder.removeComponent(EntityTrackerSystems.Visible.getComponentType()); + } + + if (clonedHolder.getComponent(NetworkId.getComponentType()) != null) { + clonedHolder.removeComponent(NetworkId.getComponentType()); + } + Ref entityRef = componentAccessor.addEntity(clonedHolder, AddReason.LOAD); - addedEntityRefs.add(entityRef); + if (entityRef != null) { + addedEntityRefs.add(entityRef); + } } } @@ -2713,7 +2770,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, snapshots.add(new BlockSelectionSnapshot(before)); this.pushHistory(BuilderToolsPlugin.Action.ROTATE, snapshots); - after.placeNoReturn("Transform 1/1", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Transform 1/1", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -2725,19 +2782,19 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendArea(); } - public void transformSelectionPoints(@Nonnull Quaterniond rotation, @Nonnull Vector3i translationOffset, @Nonnull Vector3f rotationOrigin) { + public void transformSelectionPoints(@Nonnull Quaterniond rotation, @Nonnull Vector3i translationOffset, @Nonnull Rotation3f rotationOrigin) { Vector3i newMin = this.transformBlockLocation(this.selection.getSelectionMin(), rotation, translationOffset, rotationOrigin); Vector3i newMax = this.transformBlockLocation(this.selection.getSelectionMax(), rotation, translationOffset, rotationOrigin); - this.selection.setSelectionArea(Vector3i.min(newMin, newMax), Vector3i.max(newMin, newMax)); + this.selection.setSelectionArea(Vector3iUtil.min(newMin, newMax), Vector3iUtil.max(newMin, newMax)); this.sendUpdate(); this.sendArea(); } @Nonnull public Vector3i transformBlockLocation( - @Nonnull Vector3i blockLocation, @Nonnull Quaterniond rotation, @Nonnull Vector3i translationOffset, @Nonnull Vector3f rotationOrigin + @Nonnull Vector3i blockLocation, @Nonnull Quaterniond rotation, @Nonnull Vector3i translationOffset, @Nonnull Rotation3f rotationOrigin ) { - org.joml.Vector3d relative = new org.joml.Vector3d( + Vector3d relative = new Vector3d( blockLocation.x - rotationOrigin.x + 0.5, blockLocation.y - rotationOrigin.y + 0.5, blockLocation.z - rotationOrigin.z + 0.5 ); rotation.transform(relative); @@ -2755,14 +2812,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, int z, @Nonnull List> layers, int depth, - Vector3i direction, + Vector3ic direction, WorldChunk chunk, BlockSelection before, BlockSelection after ) { - int xModifier = direction.x == 1 ? -1 : (direction.x == -1 ? 1 : 0); - int yModifier = direction.y == 1 ? -1 : (direction.y == -1 ? 1 : 0); - int zModifier = direction.z == 1 ? -1 : (direction.z == -1 ? 1 : 0); + int xModifier = direction.x() == 1 ? -1 : (direction.x() == -1 ? 1 : 0); + int yModifier = direction.y() == 1 ? -1 : (direction.y() == -1 ? 1 : 0); + int zModifier = direction.z() == 1 ? -1 : (direction.z() == -1 ? 1 : 0); for (int i = 0; i < depth; i++) { if (chunk.getBlock(x + i * xModifier + xModifier, y + i * yModifier + yModifier, z + i * zModifier + zModifier) <= 0 @@ -2772,7 +2829,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - public void layer(@Nonnull List> layers, Vector3i direction, ComponentAccessor componentAccessor) { + public void layer(@Nonnull List> layers, Vector3ic direction, ComponentAccessor componentAccessor) { if (this.selection == null) { this.sendFeedback(Message.translation("server.builderTools.noSelection"), componentAccessor); } else if (!this.selection.hasSelectionBounds()) { @@ -2785,14 +2842,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); BlockSelection before = new BlockSelection(); int width = xMax - xMin; int depth = zMax - zMin; @@ -2819,7 +2876,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - after.placeNoReturn("Finished layer", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Finished layer", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -2875,6 +2932,8 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, BlockSelection selectionToPlace = this.selection; if (technicalPaste) { selectionToPlace = this.convertEmptyBlocksToEditorEmpty(this.selection); + } else { + selectionToPlace = this.convertEditorEmptyToAir(this.selection); } selectionToPlace.setPosition(x, y, z); @@ -2891,7 +2950,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, collector = e -> snapshots.add(new EntityAddSnapshot(e)); } - BlockSelection before = selectionToPlace.place(this.player, world, Vector3i.ZERO, this.globalMask, collector); + BlockSelection before = selectionToPlace.place(this.playerRef, world, Vector3iUtil.ZERO, this.globalMask, collector); before.setSelectionArea(pasteMin, pasteMax); snapshots.add(new BlockSelectionSnapshot(before)); this.pushHistory(BuilderToolsPlugin.Action.PASTE, snapshots); @@ -2925,8 +2984,38 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, converted.setPosition(original.getX(), original.getY(), original.getZ()); converted.setAnchor(original.getAnchorX(), original.getAnchorY(), original.getAnchorZ()); converted.setSelectionArea(original.getSelectionMin(), original.getSelectionMax()); + LongOpenHashSet fluidPositions = new LongOpenHashSet(); + original.forEachFluid((x, y, z, fluidId, fluidLevel) -> { + if (fluidId != 0) { + fluidPositions.add(BlockUtil.packUnchecked(x, y, z)); + } + }); original.forEachBlock((x, y, z, block) -> { - int blockId = block.blockId() == 0 ? editorBlockPrefabAir : block.blockId(); + int blockId = block.blockId(); + if (blockId == 0 && !fluidPositions.contains(BlockUtil.packUnchecked(x, y, z))) { + blockId = editorBlockPrefabAir; + } + + converted.addBlockAtLocalPos(x, y, z, blockId, block.rotation(), block.filler(), block.supportValue(), block.holder()); + }); + original.forEachFluid((x, y, z, fluidId, fluidLevel) -> converted.addFluidAtLocalPos(x, y, z, fluidId, fluidLevel)); + original.forEachEntity(holder -> converted.addEntityHolderRaw(holder.clone())); + return converted; + } + } + + private BlockSelection convertEditorEmptyToAir(@Nonnull BlockSelection original) { + BlockTypeAssetMap assetMap = BlockType.getAssetMap(); + int editorBlockPrefabAir = assetMap.getIndex("Editor_Empty"); + if (editorBlockPrefabAir == Integer.MIN_VALUE) { + return original; + } else { + BlockSelection converted = new BlockSelection(original.getBlockCount(), original.getEntityCount()); + converted.setPosition(original.getX(), original.getY(), original.getZ()); + converted.setAnchor(original.getAnchorX(), original.getAnchorY(), original.getAnchorZ()); + converted.setSelectionArea(original.getSelectionMin(), original.getSelectionMax()); + original.forEachBlock((x, y, z, block) -> { + int blockId = block.blockId() == editorBlockPrefabAir ? 0 : block.blockId(); converted.addBlockAtLocalPos(x, y, z, blockId, block.rotation(), block.filler(), block.supportValue(), block.holder()); }); original.forEachFluid((x, y, z, fluidId, fluidLevel) -> converted.addFluidAtLocalPos(x, y, z, fluidId, fluidLevel)); @@ -2938,6 +3027,10 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, public void rotate(@Nonnull Ref ref, @Nonnull Axis axis, int angle, @Nonnull ComponentAccessor componentAccessor) { if (this.selection != null) { long start = System.nanoTime(); + if (this.preRotationSnapshot == null) { + this.preRotationSnapshot = this.selection.cloneSelection(); + } + this.pushHistory(BuilderToolsPlugin.Action.ROTATE, ClipboardContentsSnapshot.copyOf(this.selection)); this.selection = this.selection.rotate(axis, angle); long end = System.nanoTime(); @@ -2955,29 +3048,15 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - public void rotate( - @Nonnull Ref ref, - @Nonnull Axis axis, - int angle, - @Nonnull Vector3f originOfRotation, - @Nonnull ComponentAccessor componentAccessor - ) { - if (this.selection != null) { - long start = System.nanoTime(); - this.pushHistory(BuilderToolsPlugin.Action.ROTATE, ClipboardContentsSnapshot.copyOf(this.selection)); - this.selection = this.selection.rotate(axis, angle, originOfRotation); - long end = System.nanoTime(); - long diff = end - start; - BuilderToolsPlugin.get() - .getLogger() - .at(Level.FINE) - .log("Took: %dns (%dms) to execute rotate of %d blocks", diff, TimeUnit.NANOSECONDS.toMillis(diff), this.selection.getBlockCount()); - this.sendUpdate(); - this.sendFeedback( - Message.translation("server.builderTools.clipboardRotatedBy").param("angle", angle).param("axis", axis.toString()), componentAccessor - ); + public void resetClipboardRotation(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { + if (this.preRotationSnapshot == null) { + this.sendErrorFeedback(ref, Message.translation("server.builderTools.noRotationToReset"), componentAccessor); } else { - this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionClipboardEmpty"), componentAccessor); + this.pushHistory(BuilderToolsPlugin.Action.ROTATE, ClipboardContentsSnapshot.copyOf(this.selection)); + this.selection = this.preRotationSnapshot; + this.preRotationSnapshot = null; + this.sendUpdate(); + this.sendFeedback(Message.translation("server.builderTools.clipboardRotationReset"), componentAccessor); } } @@ -3032,8 +3111,8 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { long start = System.nanoTime(); - final Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - final Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + final Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + final Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); final BlockSelection before = new BlockSelection(); before.setPosition(min.x, min.y, min.z); before.setSelectionArea(min, max); @@ -3051,11 +3130,17 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, true, null, new TriIntObjPredicate() { - private int previousX = Integer.MIN_VALUE; - private int previousZ = Integer.MIN_VALUE; + private int previousX; + private int previousZ; @Nullable private WorldChunk currentChunk; + { + Objects.requireNonNull(BuilderState.this); + this.previousX = Integer.MIN_VALUE; + this.previousZ = Integer.MIN_VALUE; + } + public boolean test(int x, int y, int z, Void unused) { if (this.previousX != x || this.previousZ != z) { this.previousX = x; @@ -3084,7 +3169,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } ); this.pushHistory(BuilderToolsPlugin.Action.HOLLOW, new BlockSelectionSnapshot(before)); - after.placeNoReturn("Hollow 1/1", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Hollow 1/1", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -3097,17 +3182,6 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - public void walls( - @Nonnull Ref ref, - int blockId, - int thickness, - boolean cappedTop, - boolean cappedBottom, - @Nonnull ComponentAccessor componentAccessor - ) { - this.walls(ref, BlockPattern.parse(BlockType.getAssetMap().getAsset(blockId).getId()), thickness, cappedTop, cappedBottom, componentAccessor); - } - public void walls( @Nonnull Ref ref, @Nonnull final BlockPattern pattern, @@ -3124,8 +3198,8 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } else { World world = componentAccessor.getExternalData().getWorld(); long start = System.nanoTime(); - final Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - final Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + final Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + final Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); final BlockSelection before = new BlockSelection(); before.setPosition(min.x, min.y, min.z); before.setSelectionArea(min, max); @@ -3142,11 +3216,17 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, false, null, new TriIntObjPredicate() { - private int previousX = Integer.MIN_VALUE; - private int previousZ = Integer.MIN_VALUE; + private int previousX; + private int previousZ; @Nullable private WorldChunk currentChunk; + { + Objects.requireNonNull(BuilderState.this); + this.previousX = Integer.MIN_VALUE; + this.previousZ = Integer.MIN_VALUE; + } + public boolean test(int x, int y, int z, Void unused) { if (this.previousX != x || this.previousZ != z) { this.previousX = x; @@ -3193,7 +3273,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } ); this.pushHistory(BuilderToolsPlugin.Action.WALLS, new BlockSelectionSnapshot(before)); - after.placeNoReturn("Walls 1/1", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Walls 1/1", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -3207,10 +3287,6 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - public void set(int blockId, ComponentAccessor componentAccessor) { - this.set(BlockPattern.parse(BlockType.getAssetMap().getAsset(blockId).getId()), componentAccessor); - } - public void set(@Nonnull BlockPattern pattern, ComponentAccessor componentAccessor) { if (!pattern.isEmpty()) { if (this.selection == null) { @@ -3219,14 +3295,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendFeedback(Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); int totalBlocks = (xMax - xMin + 1) * (zMax - zMin + 1) * (yMax - yMin + 1); int width = xMax - xMin; int depth = zMax - zMin; @@ -3283,7 +3359,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - after.placeNoReturn("Set 2/2", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Set 2/2", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -3305,14 +3381,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendFeedback(Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); int totalBlocks = (xMax - xMin + 1) * (zMax - zMin + 1) * (yMax - yMin + 1); int width = xMax - xMin; int depth = zMax - zMin; @@ -3361,7 +3437,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - after.placeNoReturn("Fill 2/2", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Fill 2/2", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -3384,14 +3460,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendFeedback(Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); BlockSelection before = new BlockSelection(); int width = xMax - xMin; int depth = zMax - zMin; @@ -3475,7 +3551,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - after.placeNoReturn("Replace 2/2", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Replace 2/2", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -3588,7 +3664,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, public void replace( @Nonnull Ref ref, - @Nullable IntPredicate doReplace, + @Nullable BlockMask fromMask, @Nonnull BlockPattern toPattern, @Nonnull ComponentAccessor componentAccessor ) { @@ -3598,14 +3674,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); BlockSelection before = new BlockSelection(); int width = xMax - xMin; int depth = zMax - zMin; @@ -3630,35 +3706,47 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendFeedback("Gather 1/2", totalBlocks, ++counter, componentAccessor); } else { int block = chunk.getBlock(x, y, z); - if (doReplace == null && block != 0 || doReplace != null && doReplace.test(block)) { - Holder holder = chunk.getBlockComponentHolder(x, y, z); - Material material = Material.fromPattern(toPattern, this.random); - int newBlockId = material.getBlockId(); - int newRotation = material.hasRotation() ? material.getRotation() : chunk.getRotationIndex(x, y, z); - Holder newHolder = BuilderToolsPlugin.createBlockComponent(chunk, x, y, z, newBlockId, block, holder, true); - int rotationIndex = chunk.getRotationIndex(x, y, z); - before.addBlockAtWorldPos( - x, y, z, block, rotationIndex, filler, chunk.getSupportValue(x, y, z), chunk.getBlockComponentHolder(x, y, z) - ); - after.addBlockAtWorldPos(x, y, z, newBlockId, newRotation, 0, 0, newHolder); - this.replaceMultiBlockStructure(x, y, z, block, newBlockId, newRotation, accessor, before, after); - if (newBlockId == 0) { + if (block >= 0 && block != 1) { + boolean shouldReplace; + if (fromMask == null) { + shouldReplace = true; + } else { int fluidId = chunk.getFluidId(x, y, z); - byte fluidLevel = chunk.getFluidLevel(x, y, z); - if (fluidId != 0) { - before.addFluidAtWorldPos(x, y, z, fluidId, fluidLevel); - after.addFluidAtWorldPos(x, y, z, 0, (byte)0); + shouldReplace = !fromMask.isExcluded(accessor, x, y, z, min, max, block, fluidId); + } + + if (shouldReplace) { + Holder holder = chunk.getBlockComponentHolder(x, y, z); + Material material = Material.fromPattern(toPattern, this.random); + int newBlockId = material.getBlockId(); + int newRotation = material.hasRotation() ? material.getRotation() : chunk.getRotationIndex(x, y, z); + Holder newHolder = BuilderToolsPlugin.createBlockComponent(chunk, x, y, z, newBlockId, block, holder, true); + int rotationIndex = chunk.getRotationIndex(x, y, z); + before.addBlockAtWorldPos( + x, y, z, block, rotationIndex, filler, chunk.getSupportValue(x, y, z), chunk.getBlockComponentHolder(x, y, z) + ); + after.addBlockAtWorldPos(x, y, z, newBlockId, newRotation, 0, 0, newHolder); + this.replaceMultiBlockStructure(x, y, z, block, newBlockId, newRotation, accessor, before, after); + if (newBlockId == 0) { + int fluidId = chunk.getFluidId(x, y, z); + byte fluidLevel = chunk.getFluidLevel(x, y, z); + if (fluidId != 0) { + before.addFluidAtWorldPos(x, y, z, fluidId, fluidLevel); + after.addFluidAtWorldPos(x, y, z, 0, (byte)0); + } } } - } - this.sendFeedback("Gather 1/2", totalBlocks, ++counter, componentAccessor); + this.sendFeedback("Gather 1/2", totalBlocks, ++counter, componentAccessor); + } else { + this.sendFeedback("Gather 1/2", totalBlocks, ++counter, componentAccessor); + } } } } } - after.placeNoReturn("Replace 2/2", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Replace 2/2", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -3672,21 +3760,23 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - public void replace(@Nonnull Ref ref, @Nonnull Int2IntFunction function, @Nonnull ComponentAccessor componentAccessor) { + public int replace(@Nonnull Ref ref, @Nonnull Int2IntFunction function, @Nonnull ComponentAccessor componentAccessor) { if (this.selection == null) { this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelection"), componentAccessor); + return 0; } else if (!this.selection.hasSelectionBounds()) { this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); + return 0; } else { long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); BlockSelection before = new BlockSelection(); int width = xMax - xMin; int depth = zMax - zMin; @@ -3735,16 +3825,18 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - after.placeNoReturn("Replace 2/2", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Replace 2/2", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; + int replacedCount = after.getBlockCount(); BuilderToolsPlugin.get() .getLogger() .at(Level.FINE) - .log("Took: %dns (%dms) to execute replace of %d blocks", diff, TimeUnit.NANOSECONDS.toMillis(diff), after.getBlockCount()); + .log("Took: %dns (%dms) to execute replace of %d blocks", diff, TimeUnit.NANOSECONDS.toMillis(diff), replacedCount); this.sendUpdate(); this.sendArea(); + return replacedCount; } } @@ -3757,14 +3849,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); BlockSelection selected = new BlockSelection(); int width = xMax - xMin; int height = yMax - yMin; @@ -3843,25 +3935,27 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - BlockSelection beforeCleared = cleared.place(this.player, world); - selected.setPosition(xPos + direction.getX(), yMin + direction.getY(), zPos + direction.getZ()); - BlockSelection beforePlace = selected.place(this.player, world); + BlockSelection beforeCleared = cleared.place(this.playerRef, world); + selected.setPosition(xPos + direction.x(), yMin + direction.y(), zPos + direction.z()); + BlockSelection beforePlace = selected.place(this.playerRef, world); List> snapshots = new ObjectArrayList<>(); if (entities) { - for (Ref targetEntityRef : TargetUtil.getAllEntitiesInBox(min.toVector3d(), max.toVector3d(), componentAccessor)) { + for (Ref targetEntityRef : TargetUtil.getAllEntitiesInBox( + Vector3iUtil.toVector3d(min), Vector3iUtil.toVector3d(max), componentAccessor + )) { snapshots.add(new EntityTransformSnapshot(targetEntityRef, componentAccessor)); TransformComponent transformComponent = componentAccessor.getComponent(targetEntityRef, TransformComponent.getComponentType()); if (transformComponent != null) { - transformComponent.getPosition().add(direction); + transformComponent.getPosition().add(direction.x, direction.y, direction.z); } } } beforePlace.add(beforeCleared); ClipboardBoundsSnapshot clipboardSnapshot = new ClipboardBoundsSnapshot(min, max); - Vector3i destMin = min.clone().add(direction); - Vector3i destMax = max.clone().add(direction); - beforePlace.setSelectionArea(Vector3i.min(min, destMin), Vector3i.max(max, destMax)); + Vector3i destMin = new Vector3i(min).add(direction); + Vector3i destMax = new Vector3i(max).add(direction); + beforePlace.setSelectionArea(Vector3iUtil.min(min, destMin), Vector3iUtil.max(max, destMax)); snapshots.add(new BlockSelectionSnapshot(beforePlace)); snapshots.add(clipboardSnapshot); this.pushHistory(BuilderToolsPlugin.Action.MOVE, snapshots); @@ -3877,10 +3971,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendUpdate(); this.sendArea(); this.sendFeedback( - Message.translation("server.builderTools.selectionMovedBy") - .param("x", direction.getX()) - .param("y", direction.getY()) - .param("z", direction.getZ()), + Message.translation("server.builderTools.selectionMovedBy").param("x", direction.x()).param("y", direction.y()).param("z", direction.z()), componentAccessor ); } @@ -3896,17 +3987,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.selection.setSelectionArea(this.selection.getSelectionMin().add(direction), this.selection.getSelectionMax().add(direction)); this.sendArea(); this.sendFeedback( - Message.translation("server.builderTools.selectionShiftedBy") - .param("x", direction.getX()) - .param("y", direction.getY()) - .param("z", direction.getZ()), + Message.translation("server.builderTools.selectionShiftedBy").param("x", direction.x()).param("y", direction.y()).param("z", direction.z()), componentAccessor ); } } public void pos1(@Nonnull Vector3i pos1, ComponentAccessor componentAccessor) { - if (this.selection != null && !this.selection.getSelectionMax().equals(Vector3i.ZERO)) { + if (this.selection != null && !this.selection.getSelectionMax().equals(Vector3iUtil.ZERO)) { this.pushHistory(BuilderToolsPlugin.Action.UPDATE_SELECTION, new ClipboardBoundsSnapshot(this.selection)); this.selection.setSelectionArea(pos1, this.selection.getSelectionMax()); this.sendArea(); @@ -3921,13 +4009,13 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } this.sendFeedback( - Message.translation("server.builderTools.setPosTo").param("num", 1).param("x", pos1.getX()).param("y", pos1.getY()).param("z", pos1.getZ()), + Message.translation("server.builderTools.setPosTo").param("num", 1).param("x", pos1.x()).param("y", pos1.y()).param("z", pos1.z()), componentAccessor ); } public void pos2(@Nonnull Vector3i pos2, ComponentAccessor componentAccessor) { - if (this.selection != null && !this.selection.getSelectionMin().equals(Vector3i.ZERO)) { + if (this.selection != null && !this.selection.getSelectionMin().equals(Vector3iUtil.ZERO)) { this.pushHistory(BuilderToolsPlugin.Action.UPDATE_SELECTION, new ClipboardBoundsSnapshot(this.selection)); this.selection.setSelectionArea(this.selection.getSelectionMin(), pos2); this.sendArea(); @@ -3942,13 +4030,13 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } this.sendFeedback( - Message.translation("server.builderTools.setPosTo").param("num", 2).param("x", pos2.getX()).param("y", pos2.getY()).param("z", pos2.getZ()), + Message.translation("server.builderTools.setPosTo").param("num", 2).param("x", pos2.x()).param("y", pos2.y()).param("z", pos2.z()), componentAccessor ); } public void select(@Nonnull Vector3i pos1, @Nonnull Vector3i pos2, @Nullable String reason, ComponentAccessor componentAccessor) { - if (this.selection != null && !this.selection.getSelectionMax().equals(Vector3i.ZERO)) { + if (this.selection != null && !this.selection.getSelectionMax().equals(Vector3iUtil.ZERO)) { this.pushHistory(BuilderToolsPlugin.Action.UPDATE_SELECTION, new ClipboardBoundsSnapshot(this.selection)); this.selection.setSelectionArea(pos1, pos2); this.sendArea(); @@ -3965,23 +4053,23 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, if (reason != null) { this.sendFeedback( Message.translation(reason) - .param("x1", pos1.getX()) - .param("y1", pos1.getY()) - .param("z1", pos1.getZ()) - .param("x2", pos2.getX()) - .param("y2", pos2.getY()) - .param("z2", pos2.getZ()), + .param("x1", pos1.x()) + .param("y1", pos1.y()) + .param("z1", pos1.z()) + .param("x2", pos2.x()) + .param("y2", pos2.y()) + .param("z2", pos2.z()), componentAccessor ); } else { this.sendFeedback( Message.translation("server.builderTools.selected") - .param("x1", pos1.getX()) - .param("y1", pos1.getY()) - .param("z1", pos1.getZ()) - .param("x2", pos2.getX()) - .param("y2", pos2.getY()) - .param("z2", pos2.getZ()), + .param("x1", pos1.x()) + .param("y1", pos1.y()) + .param("z1", pos1.z()) + .param("x2", pos2.x()) + .param("y2", pos2.y()) + .param("z2", pos2.z()), componentAccessor ); } @@ -3989,10 +4077,11 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, public void deselect(ComponentAccessor componentAccessor) { if (this.selection != null && this.selection.hasSelectionBounds()) { - this.selection.setSelectionArea(Vector3i.ZERO, Vector3i.ZERO); + this.pushHistory(BuilderToolsPlugin.Action.UPDATE_SELECTION, new ClipboardBoundsSnapshot(this.selection)); + this.selection.setSelectionArea(Vector3iUtil.ZERO, Vector3iUtil.ZERO); EditorBlocksChange packet = new EditorBlocksChange(); packet.selection = null; - this.player.getPlayerConnection().write(packet); + this.playerRef.getPacketHandler().write(packet); this.sendFeedback(Message.translation("server.builderTools.deselected"), componentAccessor); } else { this.sendFeedback(Message.translation("server.builderTools.noSelectionToDeselect"), componentAccessor); @@ -4013,14 +4102,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); BlockSelection selected = new BlockSelection(); int width = xMax - xMin; int depth = zMax - zMin; @@ -4059,24 +4148,22 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, BlockSelection before = new BlockSelection(); before.setAnchor(selected.getAnchorX(), selected.getAnchorY(), selected.getAnchorZ()); before.setPosition(selected.getX(), selected.getY(), selected.getZ()); - Vector3i size = max.subtract(min).add(1, 1, 1); + Vector3i size = max.sub(min).add(1, 1, 1); for (int i = 1; i <= count; i++) { selected.setPosition( - before.getX() + (size.getX() + spacing) * direction.getX() * i, - before.getY() + (size.getY() + spacing) * direction.getY() * i, - before.getZ() + (size.getZ() + spacing) * direction.getZ() * i + before.getX() + (size.x() + spacing) * direction.x() * i, + before.getY() + (size.y() + spacing) * direction.y() * i, + before.getZ() + (size.z() + spacing) * direction.z() * i ); - before.add(selected.place(this.player, world)); + before.add(selected.place(this.playerRef, world)); } Vector3i stackOffset = new Vector3i( - (size.getX() + spacing) * direction.getX() * count, - (size.getY() + spacing) * direction.getY() * count, - (size.getZ() + spacing) * direction.getZ() * count + (size.x() + spacing) * direction.x() * count, (size.y() + spacing) * direction.y() * count, (size.z() + spacing) * direction.z() * count ); - Vector3i totalMin = Vector3i.min(min, min.add(stackOffset)); - Vector3i totalMax = Vector3i.max(max, max.add(stackOffset)); + Vector3i totalMin = Vector3iUtil.min(min, min.add(stackOffset)); + Vector3i totalMax = Vector3iUtil.max(max, max.add(stackOffset)); before.setSelectionArea(totalMin, totalMax); this.pushHistory(BuilderToolsPlugin.Action.STACK, new BlockSelectionSnapshot(before)); BuilderToolsPlugin.invalidateWorldMapForSelection(before, world); @@ -4091,9 +4178,9 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendFeedback( Message.translation("server.builderTools.selectionStacked") .param("count", count) - .param("x", direction.getX()) - .param("y", direction.getY()) - .param("z", direction.getZ()), + .param("x", direction.x()) + .param("y", direction.y()) + .param("z", direction.z()), componentAccessor ); } @@ -4106,33 +4193,30 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { this.pushHistory(BuilderToolsPlugin.Action.UPDATE_SELECTION, new ClipboardBoundsSnapshot(this.selection)); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - if (direction.getX() < 0) { - min = min.add(direction.getX(), 0, 0); - } else if (direction.getX() > 0) { - max = max.add(direction.getX(), 0, 0); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + if (direction.x() < 0) { + min = min.add(direction.x(), 0, 0); + } else if (direction.x() > 0) { + max = max.add(direction.x(), 0, 0); } - if (direction.getY() < 0) { - min = min.add(0, direction.getY(), 0); - } else if (direction.getY() > 0) { - max = max.add(0, direction.getY(), 0); + if (direction.y() < 0) { + min = min.add(0, direction.y(), 0); + } else if (direction.y() > 0) { + max = max.add(0, direction.y(), 0); } - if (direction.getZ() < 0) { - min = min.add(0, 0, direction.getZ()); - } else if (direction.getZ() > 0) { - max = max.add(0, 0, direction.getZ()); + if (direction.z() < 0) { + min = min.add(0, 0, direction.z()); + } else if (direction.z() > 0) { + max = max.add(0, 0, direction.z()); } this.selection.setSelectionArea(min, max); this.sendArea(); this.sendFeedback( - Message.translation("server.builderTools.selectionExpanded") - .param("x", direction.getX()) - .param("y", direction.getY()) - .param("z", direction.getZ()), + Message.translation("server.builderTools.selectionExpanded").param("x", direction.x()).param("y", direction.y()).param("z", direction.z()), componentAccessor ); } @@ -4145,34 +4229,31 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { this.pushHistory(BuilderToolsPlugin.Action.UPDATE_SELECTION, new ClipboardBoundsSnapshot(this.selection)); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - if (direction.getX() > 0) { - min = min.add(direction.getX(), 0, 0); - } else if (direction.getX() < 0) { - max = max.add(direction.getX(), 0, 0); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + if (direction.x() > 0) { + min = min.add(direction.x(), 0, 0); + } else if (direction.x() < 0) { + max = max.add(direction.x(), 0, 0); } - if (direction.getY() > 0) { - min = min.add(0, direction.getY(), 0); - } else if (direction.getY() < 0) { - max = max.add(0, direction.getY(), 0); + if (direction.y() > 0) { + min = min.add(0, direction.y(), 0); + } else if (direction.y() < 0) { + max = max.add(0, direction.y(), 0); } - if (direction.getZ() > 0) { - min = min.add(0, 0, direction.getZ()); - } else if (direction.getZ() < 0) { - max = max.add(0, 0, direction.getZ()); + if (direction.z() > 0) { + min = min.add(0, 0, direction.z()); + } else if (direction.z() < 0) { + max = max.add(0, 0, direction.z()); } this.selection.setSelectionArea(min, max); this.sendArea(); this.sendFeedback( ref, - Message.translation("server.builderTools.selectionContracted") - .param("x", direction.getX()) - .param("y", direction.getY()) - .param("z", direction.getZ()), + Message.translation("server.builderTools.selectionContracted").param("x", direction.x()).param("y", direction.y()).param("z", direction.z()), direction.length() > 0.0 ? "CREATE_SCALE_INCREASE" : "CREATE_SCALE_DECREASE", componentAccessor ); @@ -4186,14 +4267,14 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, this.sendErrorFeedback(ref, Message.translation("server.builderTools.noSelectionBounds"), componentAccessor); } else { long start = System.nanoTime(); - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int xMax = max.getX(); - int yMin = min.getY(); - int yMax = max.getY(); - int zMin = min.getZ(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int xMax = max.x(); + int yMin = min.y(); + int yMax = max.y(); + int zMin = min.z(); + int zMax = max.z(); int totalBlocks = (xMax - xMin + 1) * (zMax - zMin + 1) * (yMax - yMin + 1); int width = xMax - xMin; int height = yMax - yMin; @@ -4260,7 +4341,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } after.tryFixFiller(false); - after.placeNoReturn("Set 2/2", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); + after.placeNoReturn("Set 2/2", this.playerRef, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); long end = System.nanoTime(); long diff = end - start; @@ -4275,6 +4356,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, @Nonnull public List undo(@Nonnull Ref ref, int count, @Nonnull ComponentAccessor componentAccessor) { + this.commitPendingUndoGroup(); long start = System.nanoTime(); BlockSelection before = this.selection; List list = new ObjectArrayList<>(); @@ -4290,6 +4372,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, if (before != this.selection) { this.sendUpdate(); + this.sendArea(); } long end = System.nanoTime(); @@ -4306,7 +4389,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, for (BuilderToolsPlugin.ActionEntry pair : list) { this.sendFeedback( ref, - Message.translation("server.builderTools.undoStatus").param("index", ++i).param("action", pair.getAction().name()), + Message.translation("server.builderTools.undoStatus").param("index", ++i).param("action", pair.getAction().toMessage()), "CREATE_UNDO", componentAccessor ); @@ -4319,6 +4402,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, @Nonnull public List redo(@Nonnull Ref ref, int count, @Nonnull ComponentAccessor componentAccessor) { long start = System.nanoTime(); + BlockSelection before = this.selection; List list = new ObjectArrayList<>(); for (int i = 0; i < count; i++) { @@ -4330,6 +4414,11 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, list.add(action); } + if (before != this.selection) { + this.sendUpdate(); + this.sendArea(); + } + long end = System.nanoTime(); long diff = end - start; BuilderToolsPlugin.get() @@ -4344,7 +4433,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, for (BuilderToolsPlugin.ActionEntry pair : list) { this.sendFeedback( ref, - Message.translation("server.builderTools.redoStatus").param("index", ++i).param("action", pair.getAction().name()), + Message.translation("server.builderTools.redoStatus").param("index", ++i).param("action", pair.getAction().toMessage()), "CREATE_REDO", componentAccessor ); @@ -4354,18 +4443,13 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, return list; } - public void save( - @Nonnull Ref ref, @Nonnull String name, boolean relativize, boolean overwrite, ComponentAccessor componentAccessor - ) { - this.save(ref, name, relativize, overwrite, false, componentAccessor); - } - public void save( @Nonnull Ref ref, @Nonnull String name, boolean relativize, boolean overwrite, boolean clearSupport, + @Nullable AssetPack targetPack, ComponentAccessor componentAccessor ) { if (this.selection == null) { @@ -4377,8 +4461,8 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } PrefabStore prefabStore = PrefabStore.get(); - Path serverPrefabsPath = prefabStore.getServerPrefabsPath(); - if (!PathUtil.isChildOf(serverPrefabsPath, serverPrefabsPath.resolve(name)) && !SingleplayerModule.isOwner(this.playerRef)) { + Path basePath = prefabStore.getPrefabsPathForPack(targetPack); + if (!PathUtil.isChildOf(basePath, basePath.resolve(name)) && !SingleplayerModule.isOwner(this.playerRef)) { this.sendFeedback(Message.translation("server.builderTools.attemptedToSaveOutsidePrefabsDir"), componentAccessor); } else { try { @@ -4387,15 +4471,26 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, postClone.clearAllSupportValues(); } - prefabStore.saveServerPrefab(name, postClone, overwrite); + if (targetPack != null) { + prefabStore.savePrefabToPack(targetPack, name, postClone, overwrite); + } else { + prefabStore.saveServerPrefab(name, postClone, overwrite); + } + this.sendUpdate(); - this.sendFeedback(Message.translation("server.builderTools.savedSelectionToPrefab").param("name", name), componentAccessor); - } catch (PrefabSaveException var15) { - switch (var15.getType()) { + String savedKey = targetPack != null ? "server.builderTools.savedSelectionToPrefab.pack" : "server.builderTools.savedSelectionToPrefab"; + Message savedMsg = Message.translation(savedKey).param("name", name); + if (targetPack != null) { + savedMsg = savedMsg.param("pack", targetPack.getName()); + } + + this.sendFeedback(savedMsg, componentAccessor); + } catch (PrefabSaveException var16) { + switch (var16.getType()) { case ERROR: - BuilderToolsPlugin.get().getLogger().at(Level.WARNING).withCause(var15).log("Exception saving prefab %s", name); + BuilderToolsPlugin.get().getLogger().at(Level.WARNING).withCause(var16).log("Exception saving prefab %s", name); this.sendFeedback( - Message.translation("server.builderTools.errorSavingPrefab").param("name", name).param("message", var15.getCause().getMessage()), + Message.translation("server.builderTools.errorSavingPrefab").param("name", name).param("message", var16.getCause().getMessage()), componentAccessor ); break; @@ -4415,18 +4510,6 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - public void saveFromSelection( - @Nonnull Ref ref, - @Nonnull String name, - boolean relativize, - boolean overwrite, - boolean includeEntities, - boolean includeEmpty, - @Nonnull ComponentAccessor componentAccessor - ) { - this.saveFromSelection(ref, name, relativize, overwrite, includeEntities, includeEmpty, null, false, componentAccessor); - } - public void saveFromSelection( @Nonnull Ref ref, @Nonnull String name, @@ -4436,9 +4519,11 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, boolean includeEmpty, @Nullable Vector3i playerAnchor, boolean clearSupport, + @Nullable AssetPack targetPack, @Nonnull ComponentAccessor componentAccessor ) { - if (this.selection != null && (!this.selection.getSelectionMin().equals(Vector3i.ZERO) || !this.selection.getSelectionMax().equals(Vector3i.ZERO))) { + if (this.selection != null + && (!this.selection.getSelectionMin().equals(Vector3iUtil.ZERO) || !this.selection.getSelectionMax().equals(Vector3iUtil.ZERO))) { World world = componentAccessor.getExternalData().getWorld(); long start = System.nanoTime(); if (!name.endsWith(".prefab.json")) { @@ -4446,18 +4531,18 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } PrefabStore prefabStore = PrefabStore.get(); - Path serverPrefabsPath = prefabStore.getServerPrefabsPath(); - if (!PathUtil.isChildOf(serverPrefabsPath, serverPrefabsPath.resolve(name)) && !SingleplayerModule.isOwner(this.playerRef)) { + Path basePath = prefabStore.getPrefabsPathForPack(targetPack); + if (!PathUtil.isChildOf(basePath, basePath.resolve(name)) && !SingleplayerModule.isOwner(this.playerRef)) { this.sendFeedback(Message.translation("server.builderTools.attemptedToSaveOutsidePrefabsDir"), componentAccessor); } else { - Vector3i min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - Vector3i max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - int xMin = min.getX(); - int yMin = min.getY(); - int zMin = min.getZ(); - int xMax = max.getX(); - int yMax = max.getY(); - int zMax = max.getZ(); + Vector3i min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + int xMin = min.x(); + int yMin = min.y(); + int zMin = min.z(); + int xMax = max.x(); + int yMax = max.y(); + int zMax = max.z(); int width = xMax - xMin; int height = yMax - yMin; int depth = zMax - zMin; @@ -4520,7 +4605,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } if (playerAnchor != null) { - tempSelection.setAnchorAtWorldPos(playerAnchor.getX(), playerAnchor.getY(), playerAnchor.getZ()); + tempSelection.setAnchorAtWorldPos(playerAnchor.x(), playerAnchor.y(), playerAnchor.z()); } if (includeEntities) { @@ -4537,14 +4622,25 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, postClone.clearAllSupportValues(); } - prefabStore.saveServerPrefab(name, postClone, overwrite); - this.sendFeedback(Message.translation("server.builderTools.savedSelectionToPrefab").param("name", name), componentAccessor); - } catch (PrefabSaveException var48) { - switch (var48.getType()) { + if (targetPack != null) { + prefabStore.savePrefabToPack(targetPack, name, postClone, overwrite); + } else { + prefabStore.saveServerPrefab(name, postClone, overwrite); + } + + String savedKey = targetPack != null ? "server.builderTools.savedSelectionToPrefab.pack" : "server.builderTools.savedSelectionToPrefab"; + Message savedMsg = Message.translation(savedKey).param("name", name); + if (targetPack != null) { + savedMsg = savedMsg.param("pack", targetPack.getName()); + } + + this.sendFeedback(savedMsg, componentAccessor); + } catch (PrefabSaveException var49) { + switch (var49.getType()) { case ERROR: - BuilderToolsPlugin.get().getLogger().at(Level.WARNING).withCause(var48).log("Exception saving prefab %s", name); + BuilderToolsPlugin.get().getLogger().at(Level.WARNING).withCause(var49).log("Exception saving prefab %s", name); this.sendFeedback( - Message.translation("server.builderTools.errorSavingPrefab").param("name", name).param("message", var48.getCause().getMessage()), + Message.translation("server.builderTools.errorSavingPrefab").param("name", name).param("message", var49.getCause().getMessage()), componentAccessor ); break; @@ -4566,25 +4662,17 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } - public void load(@Nonnull String name, ComponentAccessor componentAccessor) { - if (!name.endsWith(".prefab.json")) { - name = name + ".prefab.json"; - } - - this.load(name, PrefabStore.get().getServerPrefab(name), componentAccessor); - } - public void load(@Nonnull String name, @Nonnull BlockSelection serverPrefab, ComponentAccessor componentAccessor) { long start = System.nanoTime(); try { - Vector3i min = Vector3i.ZERO; - Vector3i max = Vector3i.ZERO; + Vector3ic min = Vector3iUtil.ZERO; + Vector3ic max = Vector3iUtil.ZERO; if (this.selection != null) { Objects.requireNonNull(this.selection.getSelectionMin(), "min is null"); Objects.requireNonNull(this.selection.getSelectionMax(), "max is null"); - min = Vector3i.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); - max = Vector3i.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + min = Vector3iUtil.min(this.selection.getSelectionMin(), this.selection.getSelectionMax()); + max = Vector3iUtil.max(this.selection.getSelectionMin(), this.selection.getSelectionMax()); } this.selection = serverPrefab.cloneSelection(); @@ -4637,16 +4725,19 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } private void sendUpdate() { - this.player.getPlayerConnection().write(Objects.requireNonNullElseGet(this.selection, BlockSelection::new).toPacket()); + EditorBlocksChange packet = Objects.requireNonNullElseGet(this.selection, BlockSelection::new).toPacket(); + packet.skipPreviewRebuild = this.skipNextPreviewRebuild; + this.skipNextPreviewRebuild = false; + this.playerRef.getPacketHandler().write(packet); } public void sendArea() { if (this.selection != null) { - this.player.getPlayerConnection().write(this.selection.toSelectionPacket()); + this.playerRef.getPacketHandler().write(this.selection.toSelectionPacket()); } else { EditorBlocksChange packet = new EditorBlocksChange(); packet.selection = null; - this.player.getPlayerConnection().write(packet); + this.playerRef.getPacketHandler().write(packet); } } @@ -4677,6 +4768,90 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, } } + private void handleBrushUndoGrouping( + @Nonnull BlockSelection before, + @Nonnull List> spawnedRefs, + @Nonnull List movedSnapshots, + int undoGroupSize, + boolean isHoldDown + ) { + if (!isHoldDown) { + this.commitPendingUndoGroup(); + } + + if (before.getBlockCount() != 0 || before.getFluidCount() != 0 || before.getEntityCount() != 0 || before.getTintCount() != 0) { + if (this.pendingUndoSnapshot == null) { + this.pendingUndoSnapshot = before; + } else { + this.mergeBeforeSnapshotPreservingOriginal(before); + } + + this.executionCountInGroup++; + + for (Ref ref : spawnedRefs) { + this.pendingEntitySnapshots.add(new EntityAddSnapshot(ref)); + } + + this.pendingEntityTransformSnapshots.addAll(movedSnapshots); + if (this.executionCountInGroup >= undoGroupSize) { + this.commitPendingUndoGroup(); + } + } + } + + private void mergeBeforeSnapshotPreservingOriginal(@Nonnull BlockSelection newBefore) { + newBefore.forEachBlock( + (x, y, z, block) -> { + int worldX = x + newBefore.getX(); + int worldY = y + newBefore.getY(); + int worldZ = z + newBefore.getZ(); + if (!this.pendingUndoSnapshot.hasBlockAtWorldPos(worldX, worldY, worldZ)) { + this.pendingUndoSnapshot + .addBlockAtWorldPos( + worldX, + worldY, + worldZ, + block.blockId(), + block.rotation(), + block.filler(), + block.supportValue(), + block.holder() != null ? block.holder().clone() : null + ); + } + } + ); + newBefore.forEachFluid((x, y, z, fluidId, fluidLevel) -> { + int worldX = x + newBefore.getX(); + int worldY = y + newBefore.getY(); + int worldZ = z + newBefore.getZ(); + if (this.pendingUndoSnapshot.getFluidAtWorldPos(worldX, worldY, worldZ) < 0) { + this.pendingUndoSnapshot.addFluidAtWorldPos(worldX, worldY, worldZ, fluidId, fluidLevel); + } + }); + newBefore.forEachTint((x, z, color) -> { + int worldX = x + newBefore.getX(); + int worldZ = z + newBefore.getZ(); + if (!this.pendingUndoSnapshot.hasTintAtWorldPos(worldX, worldZ)) { + this.pendingUndoSnapshot.addTintAtWorldPos(worldX, worldZ, color); + } + }); + newBefore.forEachEntity(entity -> this.pendingUndoSnapshot.addEntityHolderRaw(entity)); + } + + private void commitPendingUndoGroup() { + if (this.pendingUndoSnapshot != null && this.executionCountInGroup > 0) { + List> snapshots = new ArrayList<>(); + snapshots.add(new BlockSelectionSnapshot(this.pendingUndoSnapshot)); + snapshots.addAll(this.pendingEntitySnapshots); + snapshots.addAll(this.pendingEntityTransformSnapshots); + this.pushHistory(BuilderToolsPlugin.Action.EDIT, snapshots); + this.pendingUndoSnapshot = null; + this.pendingEntitySnapshots.clear(); + this.pendingEntityTransformSnapshots.clear(); + this.executionCountInGroup = 0; + } + } + private void markPrefabsDirtyFromSnapshots(@Nonnull List> snapshots) { PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); Map activeEditSessions = prefabEditSessionManager.getActiveEditSessions(); @@ -4708,7 +4883,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, try { if (!from.isEmpty()) { builderAction = from.dequeueLast(); - to.enqueue(builderAction.restore(ref, this.player, componentAccessor.getExternalData().getWorld(), componentAccessor)); + to.enqueue(builderAction.restore(ref, this.playerRef, componentAccessor.getExternalData().getWorld(), componentAccessor)); while (to.size() > BuilderToolsPlugin.get().historyCount) { to.dequeue(); diff --git a/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsSystems.java b/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsSystems.java index d058f3af..6d826948 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsSystems.java +++ b/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsSystems.java @@ -5,6 +5,9 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.dependency.Dependency; +import com.hypixel.hytale.component.dependency.Order; +import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.server.core.asset.type.item.config.BuilderToolItemReferenceAsset; @@ -12,16 +15,19 @@ import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.modules.entity.player.PlayerSystems; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import javax.annotation.Nonnull; public class BuilderToolsSystems { public static class EnsureBuilderTools extends HolderSystem { @Nonnull private static final ComponentType PLAYER_COMPONENT_TYPE = Player.getComponentType(); + private final Set> dependencies = Set.of(new SystemDependency<>(Order.AFTER, PlayerSystems.PlayerInitSystem.class)); @Nonnull @Override @@ -57,5 +63,11 @@ public class BuilderToolsSystems { @Override public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { } + + @Nonnull + @Override + public Set> getDependencies() { + return this.dependencies; + } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsUserData.java b/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsUserData.java index b8182186..176ea3d2 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsUserData.java +++ b/src/com/hypixel/hytale/builtin/buildertools/BuilderToolsUserData.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -15,6 +16,7 @@ public class BuilderToolsUserData implements Component { public static final String ID = "BuilderTools"; private static final String SELECTION_HISTORY_KEY = "SelectionHistory"; private static final String SELECTION_HISTORY_DOC = "Controls whether changes to the block selection box are recorded in the undo/redo history."; + private static final String LAST_SAVE_PACK_KEY = "LastSavePack"; public static final BuilderCodec CODEC = BuilderCodec.builder(BuilderToolsUserData.class, BuilderToolsUserData::new) .append( new KeyedCodec<>("SelectionHistory", Codec.BOOLEAN), @@ -24,8 +26,11 @@ public class BuilderToolsUserData implements Component { .addValidator(Validators.nonNull()) .documentation("Controls whether changes to the block selection box are recorded in the undo/redo history.") .add() + .addField(new KeyedCodec<>("LastSavePack", Codec.STRING), BuilderToolsUserData::setLastSavePack, BuilderToolsUserData::getLastSavePack) .build(); private boolean selectionHistory = true; + @Nullable + private String lastSavePack; @Nonnull public static BuilderToolsUserData get(@Nonnull Player player) { @@ -45,10 +50,19 @@ public class BuilderToolsUserData implements Component { this.selectionHistory = selectionHistory; } + @Nullable + public String getLastSavePack() { + return this.lastSavePack; + } + + public void setLastSavePack(@Nullable String lastSavePack) { + this.lastSavePack = lastSavePack; + } + @Nonnull @Override public String toString() { - return "BuilderToolsUserData{selectionHistory=" + this.selectionHistory + "}"; + return "BuilderToolsUserData{selectionHistory=" + this.selectionHistory + ", lastSavePack=" + this.lastSavePack + "}"; } @Override @@ -57,7 +71,7 @@ public class BuilderToolsUserData implements Component { return true; } else if (o != null && this.getClass() == o.getClass()) { BuilderToolsUserData that = (BuilderToolsUserData)o; - return this.selectionHistory == that.selectionHistory; + return this.selectionHistory == that.selectionHistory && Objects.equals(this.lastSavePack, that.lastSavePack); } else { return false; } @@ -65,7 +79,7 @@ public class BuilderToolsUserData implements Component { @Override public int hashCode() { - return this.selectionHistory ? 1 : 0; + return Objects.hash(this.selectionHistory, this.lastSavePack); } @Nonnull @@ -73,6 +87,7 @@ public class BuilderToolsUserData implements Component { public Component clone() { BuilderToolsUserData settings = new BuilderToolsUserData(); settings.selectionHistory = this.selectionHistory; + settings.lastSavePack = this.lastSavePack; return settings; } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/EditOperation.java b/src/com/hypixel/hytale/builtin/buildertools/EditOperation.java index db92e8a7..cfd2a7cd 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/EditOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/EditOperation.java @@ -1,15 +1,27 @@ package com.hypixel.hytale.builtin.buildertools; +import com.hypixel.hytale.builtin.buildertools.snapshot.EntityTransformSnapshot; import com.hypixel.hytale.builtin.buildertools.utils.Material; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.RemoveReason; +import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.accessor.BlockAccessor; import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor; import com.hypixel.hytale.server.core.universe.world.accessor.OverridableChunkAccessor; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import java.util.ArrayList; +import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class EditOperation { private final BlockMask blockMask; @@ -19,12 +31,17 @@ public class EditOperation { private final BlockSelection before; @Nonnull private final BlockSelection after; + @Nonnull + private final World world; private final Vector3i min; private final Vector3i max; + private final List> spawnedEntityRefs = new ReferenceArrayList<>(); + private final List movedEntitySnapshots = new ArrayList<>(); public EditOperation(@Nonnull World world, int x, int y, int z, int editRange, Vector3i min, Vector3i max, BlockMask blockMask) { this.blockMask = blockMask; this.accessor = LocalCachedChunkAccessor.atWorldCoords(world, x, z, editRange); + this.world = world; this.min = min; this.max = max; this.before = new BlockSelection(); @@ -130,4 +147,53 @@ public class EditOperation { ? this.setFluid(x, y, z, material.getFluidId(), material.getFluidLevel()) : this.setBlock(x, y, z, material.getBlockId(), material.getRotation()); } + + public boolean setTint(int x, int z, int color, double opacity) { + if (!this.before.hasTintAtWorldPos(x, z)) { + long chunkIdx = ChunkUtil.indexChunkFromBlock(x, z); + WorldChunk chunk = this.world.getNonTickingChunk(chunkIdx); + int beforeColor = chunk.getBlockChunk().getTint(x, z); + int r = (int)MathUtil.lerp((double)(beforeColor >> 16 & 0xFF), (double)(color >> 16 & 0xFF), 1.0 - opacity); + int g = (int)MathUtil.lerp((double)(beforeColor >> 8 & 0xFF), (double)(color >> 8 & 0xFF), 1.0 - opacity); + int b = (int)MathUtil.lerp((double)(beforeColor & 0xFF), (double)(color & 0xFF), 1.0 - opacity); + int merged = r << 16 | g << 8 | b; + this.before.addTintAtWorldPos(x, z, beforeColor); + this.after.addTintAtWorldPos(x, z, merged); + return true; + } else { + return false; + } + } + + public int getTint(int x, int z) { + long chunkIdx = ChunkUtil.indexChunkFromBlock(x, z); + WorldChunk chunk = this.world.getNonTickingChunk(chunkIdx); + if (chunk == null) { + return 0; + } else { + return chunk.getBlockChunk() == null ? 0 : chunk.getBlockChunk().getTint(x, z); + } + } + + public void removeEntity(Ref entityRef, Holder entityStoreHolder) { + this.before.addEntityFromWorld(entityStoreHolder.clone()); + Store entityStore = this.world.getEntityStore().getStore(); + this.world.execute(() -> entityStore.removeEntity(entityRef, RemoveReason.UNLOAD)); + } + + public void trackSpawnedEntity(Ref ref) { + this.spawnedEntityRefs.add(ref); + } + + public List> getSpawnedEntityRefs() { + return this.spawnedEntityRefs; + } + + public void trackMovedEntity(Ref entityRef, ComponentAccessor componentAccessor) { + this.movedEntitySnapshots.add(new EntityTransformSnapshot(entityRef, componentAccessor)); + } + + public List getMovedEntitySnapshots() { + return this.movedEntitySnapshots; + } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/PrototypePlayerBuilderToolSettings.java b/src/com/hypixel/hytale/builtin/buildertools/PrototypePlayerBuilderToolSettings.java index a1d9f7f2..311d794c 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/PrototypePlayerBuilderToolSettings.java +++ b/src/com/hypixel/hytale/builtin/buildertools/PrototypePlayerBuilderToolSettings.java @@ -7,11 +7,10 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.block.BlockUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.interface_.BlockChange; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.UUIDComponent; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import java.util.LinkedList; @@ -19,6 +18,7 @@ import java.util.List; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PrototypePlayerBuilderToolSettings { @Nonnull @@ -42,7 +42,10 @@ public class PrototypePlayerBuilderToolSettings { @Nullable private PrototypePlayerBuilderToolSettings.EntityChange[] entityChangesForPlaySelectionToolPasteMode = null; @Nullable + private String prototypeItemId; + @Nullable private Vector3i lastBrushPosition = null; + private int undoGroupSize = 10; @Nullable private Vector3i blockChangeOffsetOrigin = null; @Nullable @@ -57,6 +60,15 @@ public class PrototypePlayerBuilderToolSettings { return this.player; } + @Nullable + public String getPrototypeItemId() { + return this.prototypeItemId; + } + + public void setPrototypeItemId(@Nullable String prototypeItemId) { + this.prototypeItemId = prototypeItemId; + } + public boolean isInSelectionTransformationMode() { return this.isInSelectionTransformationMode; } @@ -217,14 +229,22 @@ public class PrototypePlayerBuilderToolSettings { this.lastBrushPosition = null; } - public static boolean isOkayToDoCommandsOnSelection(Ref ref, @Nonnull Player player, ComponentAccessor componentAccessor) { + public int getUndoGroupSize() { + return this.undoGroupSize; + } + + public void setUndoGroupSize(int undoGroupSize) { + this.undoGroupSize = undoGroupSize > 0 ? undoGroupSize : 10; + } + + public static boolean isOkayToDoCommandsOnSelection(Ref ref, @Nonnull PlayerRef playerRef, ComponentAccessor componentAccessor) { UUIDComponent uuidComponent = componentAccessor.getComponent(ref, UUIDComponent.getComponentType()); assert uuidComponent != null; PrototypePlayerBuilderToolSettings prototypeSettings = ToolOperation.getOrCreatePrototypeSettings(uuidComponent.getUuid()); if (prototypeSettings.isInSelectionTransformationMode()) { - player.sendMessage(MESSAGE_BUILDER_TOOLS_CANNOT_PERFORM_COMMAND_IN_TRANSFORMATION_MODE); + playerRef.sendMessage(MESSAGE_BUILDER_TOOLS_CANNOT_PERFORM_COMMAND_IN_TRANSFORMATION_MODE); return false; } else { return true; diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/ClearBlocksCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/ClearBlocksCommand.java index 41904577..708ba99d 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/ClearBlocksCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/ClearBlocksCommand.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -20,7 +19,10 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.Objects; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class ClearBlocksCommand extends AbstractPlayerCommand { @Nonnull @@ -35,13 +37,15 @@ public class ClearBlocksCommand extends AbstractPlayerCommand { this.addUsageVariant( new AbstractPlayerCommand("server.commands.clear.desc") { @Nonnull - private final RequiredArg positionOneArg = this.withRequiredArg( - "positionOne", "server.commands.clear.positionOne.desc", ArgTypes.RELATIVE_BLOCK_POSITION - ); + private final RequiredArg positionOneArg; @Nonnull - private final RequiredArg positionTwoArg = this.withRequiredArg( - "positionTwo", "server.commands.clear.positionTwo.desc", ArgTypes.RELATIVE_BLOCK_POSITION - ); + private final RequiredArg positionTwoArg; + + { + Objects.requireNonNull(ClearBlocksCommand.this); + this.positionOneArg = this.withRequiredArg("positionOne", "server.commands.clear.positionOne.desc", ArgTypes.RELATIVE_BLOCK_POSITION); + this.positionTwoArg = this.withRequiredArg("positionTwo", "server.commands.clear.positionTwo.desc", ArgTypes.RELATIVE_BLOCK_POSITION); + } @Override protected void execute( @@ -60,14 +64,14 @@ public class ClearBlocksCommand extends AbstractPlayerCommand { assert transformComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { Vector3d position = transformComponent.getPosition(); RelativeIntPosition relativeIntPositionOne = this.positionOneArg.get(context); RelativeIntPosition relativeIntPositionTwo = this.positionTwoArg.get(context); Vector3i posOne = relativeIntPositionOne.getBlockPosition(position, chunkStore); Vector3i posTwo = relativeIntPositionTwo.getBlockPosition(position, chunkStore); - Vector3i min = Vector3i.min(posOne, posTwo); - Vector3i max = Vector3i.max(posOne, posTwo); + Vector3i min = Vector3iUtil.min(posOne, posTwo); + Vector3i max = Vector3iUtil.max(posOne, posTwo); BuilderToolsPlugin.addToQueue( playerComponent, playerRef, (r, s, componentAccessor) -> s.clear(min.x, min.y, min.z, max.x, max.y, max.z, componentAccessor) ); @@ -86,7 +90,7 @@ public class ClearBlocksCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); if (builderState.getSelection() == null) { playerRef.sendMessage(MESSAGE_COMMANDS_CLEAR_NO_SELECTION); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/ClearEntitiesCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/ClearEntitiesCommand.java index 52992e37..c1b12b68 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/ClearEntitiesCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/ClearEntitiesCommand.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -15,8 +14,9 @@ import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import java.util.ArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ClearEntitiesCommand extends AbstractPlayerCommand { @Nonnull @@ -36,7 +36,7 @@ public class ClearEntitiesCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); BlockSelection selection = builderState.getSelection(); if (selection == null) { @@ -44,11 +44,11 @@ public class ClearEntitiesCommand extends AbstractPlayerCommand { } else { Vector3i min = selection.getSelectionMin(); Vector3i max = selection.getSelectionMax(); - int width = max.getX() - min.getX(); - int height = max.getY() - min.getY(); - int depth = max.getZ() - min.getZ(); - ArrayList> entitiesToRemove = new ArrayList<>(); - BuilderToolsPlugin.forEachCopyableInSelection(world, min.getX(), min.getY(), min.getZ(), width, height, depth, entitiesToRemove::add); + int width = max.x() - min.x(); + int height = max.y() - min.y(); + int depth = max.z() - min.z(); + ReferenceArrayList> entitiesToRemove = new ReferenceArrayList<>(); + BuilderToolsPlugin.forEachCopyableInSelection(world, min.x(), min.y(), min.z(), width, height, depth, entitiesToRemove::add); Store entityStore = world.getEntityStore().getStore(); for (Ref entityRef : entitiesToRemove) { diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/ContractSelectionCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/ContractSelectionCommand.java index 77dcdf36..1b30b8a4 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/ContractSelectionCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/ContractSelectionCommand.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.Axis; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -20,6 +19,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ContractSelectionCommand extends AbstractPlayerCommand { @Nonnull @@ -43,7 +43,7 @@ public class ContractSelectionCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); assert headRotationComponent != null; @@ -52,10 +52,10 @@ public class ContractSelectionCommand extends AbstractPlayerCommand { List directions = new ObjectArrayList<>(); if (this.axisArg.provided(context)) { for (Axis axis : this.axisArg.get(context)) { - directions.add(axis.getDirection().scale(distance)); + directions.add(axis.getDirection().mul(distance, new Vector3i())); } } else { - directions.add(headRotationComponent.getAxisDirection().scale(distance)); + directions.add(headRotationComponent.getAxisDirection().mul(distance)); } for (Vector3i direction : directions) { diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/CopyCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/CopyCommand.java index 43d81c65..61c5f6c8 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/CopyCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/CopyCommand.java @@ -7,8 +7,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.Message; @@ -27,6 +25,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TempAssetIdUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class CopyCommand extends AbstractPlayerCommand { @Nonnull @@ -57,7 +57,7 @@ public class CopyCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); boolean entitiesOnly = this.entitiesOnlyFlag.get(context); boolean noEntities = this.noEntitiesFlag.get(context); @@ -80,27 +80,21 @@ public class CopyCommand extends AbstractPlayerCommand { int settingsFinal = settings; Vector3i playerAnchor = getPlayerAnchor(ref, store, this.playerAnchorFlag.get(context)); - BuilderToolsPlugin.addToQueue( - playerComponent, - playerRef, - (r, s, componentAccessor) -> { - try { - BlockSelection selection = builderState.getSelection(); - if (selection == null || !selection.hasSelectionBounds()) { - context.sendMessage(MESSAGE_BUILDER_TOOLS_COPY_CUT_NO_SELECTION); - return; - } - - Vector3i min = selection.getSelectionMin(); - Vector3i max = selection.getSelectionMax(); - builderState.copyOrCut( - r, min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ(), settingsFinal, playerAnchor, componentAccessor - ); - } catch (PrefabCopyException var10x) { - context.sendMessage(Message.translation("server.builderTools.copycut.copyFailedReason").param("reason", var10x.getMessage())); + BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> { + try { + BlockSelection selection = builderState.getSelection(); + if (selection == null || !selection.hasSelectionBounds()) { + context.sendMessage(MESSAGE_BUILDER_TOOLS_COPY_CUT_NO_SELECTION); + return; } + + Vector3i min = selection.getSelectionMin(); + Vector3i max = selection.getSelectionMax(); + builderState.copyOrCut(r, min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), settingsFinal, playerAnchor, componentAccessor); + } catch (PrefabCopyException var10x) { + context.sendMessage(Message.translation("server.builderTools.copycut.copyFailedReason").param("reason", var10x.getMessage())); } - ); + }); } } @@ -114,7 +108,7 @@ public class CopyCommand extends AbstractPlayerCommand { return null; } else { Vector3d position = transformComponent.getPosition(); - return new Vector3i(MathUtil.floor(position.getX()), MathUtil.floor(position.getY()), MathUtil.floor(position.getZ())); + return new Vector3i(MathUtil.floor(position.x()), MathUtil.floor(position.y()), MathUtil.floor(position.z())); } } } @@ -149,15 +143,15 @@ public class CopyCommand extends AbstractPlayerCommand { try { BlockSelection selection = builderState.getSelection(); if (selection == null || !selection.hasSelectionBounds()) { - playerComponent.sendMessage(MESSAGE_BUILDER_TOOLS_COPY_CUT_NO_SELECTION); + playerRefComponent.sendMessage(MESSAGE_BUILDER_TOOLS_COPY_CUT_NO_SELECTION); return; } Vector3i min = selection.getSelectionMin(); Vector3i max = selection.getSelectionMax(); - builderState.copyOrCut(r, min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ(), settings, c); + builderState.copyOrCut(r, min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), settings, c); } catch (PrefabCopyException var9) { - playerComponent.sendMessage(Message.translation("server.builderTools.copycut.copyFailedReason").param("reason", var9.getMessage())); + playerRefComponent.sendMessage(Message.translation("server.builderTools.copycut.copyFailedReason").param("reason", var9.getMessage())); } }); } @@ -198,7 +192,7 @@ public class CopyCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); boolean entitiesOnly = this.entitiesOnlyFlag.get(context); boolean noEntities = this.noEntitiesFlag.get(context); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/CutCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/CutCommand.java index ec10f627..700b7db5 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/CutCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/CutCommand.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrefabCopyException; import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.Message; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TempAssetIdUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class CutCommand extends AbstractPlayerCommand { @Nonnull @@ -50,7 +50,7 @@ public class CutCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); boolean entitiesOnly = this.entitiesOnlyFlag.get(context); boolean noEntities = this.noEntitiesFlag.get(context); @@ -82,7 +82,7 @@ public class CutCommand extends AbstractPlayerCommand { Vector3i min = selection.getSelectionMin(); Vector3i max = selection.getSelectionMax(); - builderState.copyOrCut(r, min.getX(), min.getY(), min.getZ(), max.getX(), max.getY(), max.getZ(), settingsFinal, componentAccessor); + builderState.copyOrCut(r, min.x(), min.y(), min.z(), max.x(), max.y(), max.z(), settingsFinal, componentAccessor); } catch (PrefabCopyException var9x) { context.sendMessage(Message.translation("server.builderTools.copycut.copyFailedReason").param("reason", var9x.getMessage())); } @@ -124,7 +124,7 @@ public class CutCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); boolean entitiesOnly = this.entitiesOnlyFlag.get(context); boolean noEntities = this.noEntitiesFlag.get(context); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/DeselectCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/DeselectCommand.java index d4263605..e46d1413 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/DeselectCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/DeselectCommand.java @@ -28,7 +28,7 @@ public class DeselectCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.deselect(componentAccessor)); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/EditLineCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/EditLineCommand.java index 31dc985f..f55c8e6a 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/EditLineCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/EditLineCommand.java @@ -4,8 +4,6 @@ import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.packets.buildertools.BrushOrigin; import com.hypixel.hytale.protocol.packets.buildertools.BrushShape; @@ -22,6 +20,8 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class EditLineCommand extends AbstractPlayerCommand { @Nonnull @@ -65,9 +65,9 @@ public class EditLineCommand extends AbstractPlayerCommand { assert transformComponent != null; Vector3d playerPos = transformComponent.getPosition(); - int baseX = MathUtil.floor(playerPos.getX()); - int baseY = MathUtil.floor(playerPos.getY()); - int baseZ = MathUtil.floor(playerPos.getZ()); + int baseX = MathUtil.floor(playerPos.x()); + int baseY = MathUtil.floor(playerPos.y()); + int baseZ = MathUtil.floor(playerPos.z()); Vector3i start = this.startArg.get(context).resolve(baseX, baseY, baseZ); Vector3i end = this.endArg.get(context).resolve(baseX, baseY, baseZ); BlockPattern material = BlockPattern.parse(this.materialArg.get(context)); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/ExpandCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/ExpandCommand.java index 23be46fd..8fdd1ca2 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/ExpandCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/ExpandCommand.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.Axis; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ExpandCommand extends AbstractPlayerCommand { @Nonnull @@ -41,17 +41,17 @@ public class ExpandCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { Integer distance = this.distanceArg.get(context); Vector3i direction; if (this.axisArg.provided(context)) { - direction = this.axisArg.get(context).getDirection().scale(distance); + direction = this.axisArg.get(context).getDirection().mul(distance, new Vector3i()); } else { HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); assert headRotationComponent != null; - direction = headRotationComponent.getAxisDirection().scale(distance); + direction = headRotationComponent.getAxisDirection().mul(distance); } BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.expand(r, direction, componentAccessor)); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/ExtendFaceCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/ExtendFaceCommand.java index f4730a16..4d0f59d2 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/ExtendFaceCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/ExtendFaceCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ExtendFaceCommand extends AbstractCommandCollection { public ExtendFaceCommand() { @@ -59,7 +59,7 @@ public class ExtendFaceCommand extends AbstractCommandCollection { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { int x = this.xArg.get(context); int y = this.yArg.get(context); int z = this.zArg.get(context); @@ -131,7 +131,7 @@ public class ExtendFaceCommand extends AbstractCommandCollection { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { int x = this.xArg.get(context); int y = this.yArg.get(context); int z = this.zArg.get(context); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/FillCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/FillCommand.java index 30e0f04a..81f5a85e 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/FillCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/FillCommand.java @@ -35,7 +35,7 @@ public class FillCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BlockPattern pattern = this.patternArg.get(context); if (pattern != null && !pattern.isEmpty()) { BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.fill(pattern, componentAccessor)); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/FlipCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/FlipCommand.java index 4edca813..c5e2a27c 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/FlipCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/FlipCommand.java @@ -39,7 +39,7 @@ public class FlipCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); if (headRotationComponent != null) { Axis axis; diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/HollowCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/HollowCommand.java index 9d1d7703..8d8d612b 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/HollowCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/HollowCommand.java @@ -48,7 +48,7 @@ public class HollowCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { int blockTypeIndex = BlockType.getAssetMap().getIndex(this.blockTypeArg.get(context)); Boolean floor = this.floorArg.get(context); Boolean roof = this.roofArg.get(context); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/LayerCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/LayerCommand.java index 55269d41..1ff88552 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/LayerCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/LayerCommand.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -20,10 +20,22 @@ import it.unimi.dsi.fastutil.Pair; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; +import org.joml.Vector3ic; public class LayerCommand extends AbstractPlayerCommand { - private static Map directions = Map.of( - "up", Vector3i.UP, "down", Vector3i.DOWN, "north", Vector3i.NORTH, "south", Vector3i.SOUTH, "east", Vector3i.EAST, "west", Vector3i.WEST + private static final Map DIRECTIONS = Map.of( + "up", + Vector3iUtil.UP, + "down", + Vector3iUtil.DOWN, + "north", + Vector3iUtil.NORTH, + "south", + Vector3iUtil.SOUTH, + "east", + Vector3iUtil.EAST, + "west", + Vector3iUtil.WEST ); @Nonnull private final RequiredArg layerDirectionArg = this.withRequiredArg("direction", "server.commands.layer.direction.desc", ArgTypes.STRING); @@ -46,22 +58,22 @@ public class LayerCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { String direction = this.layerDirectionArg.get(context).toLowerCase(); List> layers = this.layersArg.get(context); if (layers != null && direction != null) { - boolean directionValid = directions.containsKey(direction); + boolean directionValid = DIRECTIONS.containsKey(direction); if (directionValid) { BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> { HeadRotation headRotationComponent = componentAccessor.getComponent(ref, HeadRotation.getComponentType()); assert headRotationComponent != null; - Vector3i layerDirection = Vector3i.ZERO; + Vector3ic layerDirection; if (direction.equalsIgnoreCase("camera")) { layerDirection = headRotationComponent.getAxisDirection(); } else { - layerDirection = directions.get(direction); + layerDirection = DIRECTIONS.get(direction); } s.layer(layers, layerDirection, componentAccessor); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/MoveCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/MoveCommand.java index 4e40d0ff..645fc858 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/MoveCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/MoveCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class MoveCommand extends AbstractPlayerCommand { @Nonnull @@ -53,12 +53,12 @@ public class MoveCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); assert headRotationComponent != null; - Vector3i directionVector = RelativeDirection.toDirectionVector(direction, headRotationComponent).scale(distance); + Vector3i directionVector = RelativeDirection.toDirectionVector(direction, headRotationComponent).mul(distance); BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.move(r, directionVector, empty, entities, componentAccessor)); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/PasteCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/PasteCommand.java index 63cd79be..b5becaff 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/PasteCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/PasteCommand.java @@ -4,8 +4,6 @@ import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -20,6 +18,8 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PasteCommand extends AbstractPlayerCommand { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/Pos1Command.java b/src/com/hypixel/hytale/builtin/buildertools/commands/Pos1Command.java index 366a863c..08193299 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/Pos1Command.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/Pos1Command.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -18,6 +16,8 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class Pos1Command extends AbstractPlayerCommand { @Nonnull @@ -41,7 +41,7 @@ public class Pos1Command extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { Vector3i intTriple; if (this.xArg.provided(context) && this.yArg.provided(context) && this.zArg.provided(context)) { intTriple = new Vector3i(this.xArg.get(context), this.yArg.get(context), this.zArg.get(context)); @@ -52,7 +52,7 @@ public class Pos1Command extends AbstractPlayerCommand { } Vector3d position = transformComponent.getPosition(); - intTriple = new Vector3i(MathUtil.floor(position.getX()), MathUtil.floor(position.getY()), MathUtil.floor(position.getZ())); + intTriple = new Vector3i(MathUtil.floor(position.x()), MathUtil.floor(position.y()), MathUtil.floor(position.z())); } BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.pos1(intTriple, componentAccessor)); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/Pos2Command.java b/src/com/hypixel/hytale/builtin/buildertools/commands/Pos2Command.java index fcc801ab..21bd59f9 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/Pos2Command.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/Pos2Command.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -18,6 +16,8 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class Pos2Command extends AbstractPlayerCommand { @Nonnull @@ -41,7 +41,7 @@ public class Pos2Command extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { Vector3i intTriple; if (this.xArg.provided(context) && this.yArg.provided(context) && this.zArg.provided(context)) { intTriple = new Vector3i(this.xArg.get(context), this.yArg.get(context), this.zArg.get(context)); @@ -51,7 +51,7 @@ public class Pos2Command extends AbstractPlayerCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - intTriple = new Vector3i(MathUtil.floor(position.getX()), MathUtil.floor(position.getY()), MathUtil.floor(position.getZ())); + intTriple = new Vector3i(MathUtil.floor(position.x()), MathUtil.floor(position.y()), MathUtil.floor(position.z())); } BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.pos2(intTriple, componentAccessor)); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/PrefabCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/PrefabCommand.java index 2923e11d..55311b0e 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/PrefabCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/PrefabCommand.java @@ -1,6 +1,8 @@ package com.hypixel.hytale.builtin.buildertools.commands; +import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.BuilderToolsUserData; import com.hypixel.hytale.builtin.buildertools.prefablist.PrefabPage; import com.hypixel.hytale.builtin.buildertools.prefablist.PrefabSavePage; import com.hypixel.hytale.builtin.buildertools.utils.RecursivePrefabLoader; @@ -8,8 +10,6 @@ import com.hypixel.hytale.common.util.PathUtil; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -38,11 +38,14 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; +import java.util.Objects; import java.util.Random; import java.util.function.BiFunction; import java.util.function.Function; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PrefabCommand extends AbstractCommandCollection { public PrefabCommand() { @@ -154,6 +157,10 @@ public class PrefabCommand extends AbstractCommandCollection { final String packPrefix = packPath.isBasePack() ? "" : "[" + packPath.getPackName() + "] "; if (Files.isDirectory(path)) { Files.walkFileTree(path, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor() { + { + Objects.requireNonNull(PrefabListCommand.this); + } + @Nonnull public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) { String fileName = file.getFileName().toString(); @@ -168,6 +175,10 @@ public class PrefabCommand extends AbstractCommandCollection { } } else if (Files.isDirectory(prefabStorePath)) { Files.walkFileTree(prefabStorePath, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor() { + { + Objects.requireNonNull(PrefabListCommand.this); + } + @Nonnull public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) { String fileName = file.getFileName().toString(); @@ -292,6 +303,8 @@ public class PrefabCommand extends AbstractCommandCollection { } private static class PrefabSaveCommand extends AbstractPlayerCommand { + private static final Message MESSAGE_NO_SELECTION = Message.translation("server.builderTools.noSelection"); + public PrefabSaveCommand() { super("save", "server.commands.prefab.save.desc"); this.requirePermission("hytale.editor.prefab.manage"); @@ -306,7 +319,13 @@ public class PrefabCommand extends AbstractCommandCollection { assert playerComponent != null; - playerComponent.getPageManager().openCustomPage(ref, store, new PrefabSavePage(playerRef)); + BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); + BlockSelection selection = builderState.getSelection(); + if (selection != null && selection.hasSelectionBounds()) { + playerComponent.getPageManager().openCustomPage(ref, store, new PrefabSavePage(playerRef)); + } else { + context.sendMessage(MESSAGE_NO_SELECTION); + } } } @@ -323,6 +342,10 @@ public class PrefabCommand extends AbstractCommandCollection { private final FlagArg playerAnchorFlag = this.withFlagArg("playerAnchor", "server.commands.prefab.save.playerAnchor.desc"); @Nonnull private final FlagArg clearSupportFlag = this.withFlagArg("clearSupport", "server.commands.editprefab.save.clearSupport.desc"); + @Nonnull + private final DefaultArg packArg = this.withDefaultArg( + "pack", "server.commands.prefab.save.pack.desc", ArgTypes.STRING, "", "server.commands.prefab.save.pack.desc" + ); public PrefabSaveDirectCommand() { super("server.commands.prefab.save.desc"); @@ -336,17 +359,24 @@ public class PrefabCommand extends AbstractCommandCollection { assert playerComponent != null; - String name = this.nameArg.get(context); - boolean overwrite = this.overwriteFlag.get(context); - boolean entities = this.entitiesFlag.get(context); - boolean empty = this.emptyFlag.get(context); - boolean clearSupport = this.clearSupportFlag.get(context); - Vector3i playerAnchor = this.getPlayerAnchor(ref, store, this.playerAnchorFlag.get(context)); - BuilderToolsPlugin.addToQueue( - playerComponent, - playerRef, - (r, s, componentAccessor) -> s.saveFromSelection(r, name, true, overwrite, entities, empty, playerAnchor, clearSupport, componentAccessor) - ); + String packName = this.packArg.get(context); + AssetPack targetPack = BuilderToolsPlugin.resolveTargetPack(packName != null ? packName : "", playerComponent, context); + if (targetPack != null) { + BuilderToolsUserData.get(playerComponent).setLastSavePack(targetPack.getName()); + String name = this.nameArg.get(context); + boolean overwrite = this.overwriteFlag.get(context); + boolean entities = this.entitiesFlag.get(context); + boolean empty = this.emptyFlag.get(context); + boolean clearSupport = this.clearSupportFlag.get(context); + Vector3i playerAnchor = this.getPlayerAnchor(ref, store, this.playerAnchorFlag.get(context)); + BuilderToolsPlugin.addToQueue( + playerComponent, + playerRef, + (r, s, componentAccessor) -> s.saveFromSelection( + r, name, true, overwrite, entities, empty, playerAnchor, clearSupport, targetPack, componentAccessor + ) + ); + } } @Nullable @@ -359,7 +389,7 @@ public class PrefabCommand extends AbstractCommandCollection { return null; } else { Vector3d position = transformComponent.getPosition(); - return new Vector3i(MathUtil.floor(position.getX()), MathUtil.floor(position.getY()), MathUtil.floor(position.getZ())); + return new Vector3i(MathUtil.floor(position.x()), MathUtil.floor(position.y()), MathUtil.floor(position.z())); } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/RepairFillersCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/RepairFillersCommand.java index cb6db08a..91c1a928 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/RepairFillersCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/RepairFillersCommand.java @@ -31,7 +31,7 @@ public class RepairFillersCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, c) -> s.repairFillers(r, c)); context.sendMessage(MESSAGE_BUILDER_TOOLS_REPAIR_FILLERS); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/ReplaceCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/ReplaceCommand.java index 39ec5cff..cbc94134 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/ReplaceCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/ReplaceCommand.java @@ -10,16 +10,17 @@ import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.command.system.CommandContext; -import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockPattern; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; +import java.util.ArrayList; import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -29,44 +30,43 @@ import javax.annotation.Nullable; public class ReplaceCommand extends AbstractPlayerCommand { @Nonnull private final RequiredArg toArg = this.withRequiredArg("to", "server.commands.replace.toBlock.desc", ArgTypes.BLOCK_PATTERN); - @Nonnull - private final FlagArg substringSwapFlag = this.withFlagArg("substringSwap", "server.commands.replace.substringSwap.desc"); - @Nonnull - private final FlagArg regexFlag = this.withFlagArg("regex", "server.commands.replace.regex.desc"); public ReplaceCommand() { super("replace", "server.commands.replace.desc"); this.setPermissionGroup(GameMode.Creative); this.addUsageVariant(new ReplaceCommand.ReplaceFromToCommand()); + this.addSubCommand(new ReplaceCommand.ReplaceSwapCommand()); + this.addSubCommand(new ReplaceCommand.ReplaceRegexCommand()); } @Override protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - executeReplace(context, store, ref, playerRef, null, this.toArg.get(context), this.substringSwapFlag.get(context), this.regexFlag.get(context)); + executeBlockReplace(context, store, ref, playerRef, null, this.toArg.get(context)); } - private static void executeReplace( + static void executeBlockReplace( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, - @Nullable String fromValue, - @Nonnull BlockPattern toPattern, - boolean substringSwap, - boolean regex + @Nullable BlockMask fromMask, + @Nonnull BlockPattern toPattern ) { Player playerComponent = store.getComponent(ref, Player.getComponentType()); assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { - if (toPattern != null && !toPattern.isEmpty()) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { + if (fromMask != null && fromMask.hasInvalidBlocks()) { + context.sendMessage(Message.translation("server.builderTools.invalidBlockType").param("key", fromMask.toString())); + } else { String toValue = toPattern.toString(); + String fromValue = fromMask != null ? fromMask.toString() : null; Material fromMaterial = fromValue != null ? Material.fromKey(fromValue) : null; Material toMaterial = Material.fromPattern(toPattern, ThreadLocalRandom.current()); - if (toMaterial.isFluid() && !substringSwap && !regex) { + if (toMaterial.isFluid()) { if (fromMaterial == null) { context.sendMessage(Message.translation("server.commands.replace.fromRequired")); } else { @@ -78,88 +78,22 @@ public class ReplaceCommand extends AbstractPlayerCommand { } else if (fromMaterial != null && fromMaterial.isFluid()) { BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.replace(r, fromMaterial, toMaterial, componentAccessor)); context.sendMessage(Message.translation("server.builderTools.replace.replacementBlockDone").param("from", fromValue).param("to", toValue)); + } else if (fromMask == null) { + BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.replace(r, null, toPattern, componentAccessor)); + context.sendMessage(Message.translation("server.builderTools.replace.replacementAllDone").param("to", toValue)); } else { - BlockTypeAssetMap assetMap = BlockType.getAssetMap(); - if (fromValue == null && !substringSwap && !regex) { - BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.replace(r, null, toPattern, componentAccessor)); - context.sendMessage(Message.translation("server.builderTools.replace.replacementAllDone").param("to", toValue)); - } else if (fromValue == null) { - context.sendMessage(Message.translation("server.commands.replace.fromRequired")); - } else if (regex) { - Pattern pattern; - try { - pattern = Pattern.compile(fromValue); - } catch (PatternSyntaxException var24) { - context.sendMessage(Message.translation("server.commands.replace.invalidRegex").param("error", var24.getMessage())); - return; - } - - BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> { - s.replace(r, value -> { - String valueKey = assetMap.getAsset(value).getId(); - return pattern.matcher(valueKey).matches(); - }, toPattern, componentAccessor); - context.sendMessage(Message.translation("server.commands.replace.success").param("regex", fromValue).param("replacement", toValue)); - }); - } else if (fromMaterial == null) { - context.sendMessage(Message.translation("server.builderTools.invalidBlockType").param("name", fromValue).param("key", fromValue)); - } else if (!substringSwap) { - int fromBlockId = fromMaterial.getBlockId(); - BuilderToolsPlugin.addToQueue( - playerComponent, playerRef, (r, s, componentAccessor) -> s.replace(r, block -> block == fromBlockId, toPattern, componentAccessor) - ); - context.sendMessage(Message.translation("server.builderTools.replace.replacementBlockDone").param("from", fromValue).param("to", toValue)); - } else { - String[] blockKeys = fromValue.split(","); - Int2IntArrayMap swapMap = new Int2IntArrayMap(); - - for (int blockId = 0; blockId < assetMap.getAssetCount(); blockId++) { - BlockType blockType = assetMap.getAsset(blockId); - String blockKeyStr = blockType.getId(); - - for (String from : blockKeys) { - if (blockKeyStr.contains(from.trim())) { - String replacedKey; - try { - replacedKey = blockKeyStr.replace(from.trim(), toValue); - } catch (Exception var25) { - continue; - } - - int index = assetMap.getIndex(replacedKey); - if (index != Integer.MIN_VALUE) { - swapMap.put(blockId, index); - break; - } - } - } - } - - if (!swapMap.isEmpty()) { - BuilderToolsPlugin.addToQueue( - playerComponent, playerRef, (r, s, componentAccessor) -> s.replace(r, value -> swapMap.getOrDefault(value, value), componentAccessor) - ); - context.sendMessage(Message.translation("server.builderTools.replace.replacementDone").param("nb", swapMap.size()).param("to", toValue)); - } else { - context.sendMessage(Message.translation("server.commands.replace.noMatchingBlocks").param("blockType", fromValue)); - } - } + BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.replace(r, fromMask, toPattern, componentAccessor)); + context.sendMessage(Message.translation("server.builderTools.replace.replacementBlockDone").param("from", fromValue).param("to", toValue)); } - } else { - context.sendMessage(Message.translation("server.builderTools.invalidBlockType").param("name", "").param("key", "")); } } } private static class ReplaceFromToCommand extends AbstractPlayerCommand { @Nonnull - private final RequiredArg fromArg = this.withRequiredArg("from", "server.commands.replace.from.desc", ArgTypes.STRING); + private final RequiredArg fromArg = this.withRequiredArg("from", "server.commands.replace.from.desc", ArgTypes.BLOCK_MASK); @Nonnull private final RequiredArg toArg = this.withRequiredArg("to", "server.commands.replace.toBlock.desc", ArgTypes.BLOCK_PATTERN); - @Nonnull - private final FlagArg substringSwapFlag = this.withFlagArg("substringSwap", "server.commands.replace.substringSwap.desc"); - @Nonnull - private final FlagArg regexFlag = this.withFlagArg("regex", "server.commands.replace.regex.desc"); public ReplaceFromToCommand() { super("server.commands.replace.desc"); @@ -170,16 +104,125 @@ public class ReplaceCommand extends AbstractPlayerCommand { protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - ReplaceCommand.executeReplace( - context, - store, - ref, - playerRef, - this.fromArg.get(context), - this.toArg.get(context), - this.substringSwapFlag.get(context), - this.regexFlag.get(context) - ); + ReplaceCommand.executeBlockReplace(context, store, ref, playerRef, this.fromArg.get(context), this.toArg.get(context)); + } + } + + private static class ReplaceRegexCommand extends AbstractPlayerCommand { + @Nonnull + private final RequiredArg fromArg = this.withRequiredArg("from", "server.commands.replace.regex.from.desc", ArgTypes.STRING); + @Nonnull + private final RequiredArg toArg = this.withRequiredArg("to", "server.commands.replace.regex.to.desc", ArgTypes.BLOCK_PATTERN); + + public ReplaceRegexCommand() { + super("regex", "server.commands.replace.regex.desc"); + this.setPermissionGroup(GameMode.Creative); + } + + @Override + protected void execute( + @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world + ) { + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + + assert playerComponent != null; + + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { + String fromValue = this.fromArg.get(context); + BlockPattern toPattern = this.toArg.get(context); + + Pattern pattern; + try { + pattern = Pattern.compile(fromValue); + } catch (PatternSyntaxException var14) { + context.sendMessage(Message.translation("server.commands.replace.invalidRegex").param("error", var14.getMessage())); + return; + } + + BlockTypeAssetMap assetMap = BlockType.getAssetMap(); + ArrayList matchedNames = new ArrayList<>(); + + for (int blockId = 0; blockId < assetMap.getAssetCount(); blockId++) { + BlockType blockType = assetMap.getAsset(blockId); + if (blockType != null && pattern.matcher(blockType.getId()).matches()) { + matchedNames.add(blockType.getId()); + } + } + + if (matchedNames.isEmpty()) { + context.sendMessage(Message.translation("server.commands.replace.noMatchingBlocks").param("blockType", fromValue)); + } else { + BlockMask regexMask = BlockMask.parse(matchedNames.toArray(new String[0])); + BuilderToolsPlugin.addToQueue( + playerComponent, + playerRef, + (r, s, componentAccessor) -> { + s.replace(r, regexMask, toPattern, componentAccessor); + context.sendMessage( + Message.translation("server.commands.replace.success").param("regex", fromValue).param("replacement", toPattern.toString()) + ); + } + ); + } + } + } + } + + private static class ReplaceSwapCommand extends AbstractPlayerCommand { + @Nonnull + private final RequiredArg fromArg = this.withRequiredArg("from", "server.commands.replace.swap.from.desc", ArgTypes.STRING); + @Nonnull + private final RequiredArg toArg = this.withRequiredArg("to", "server.commands.replace.swap.to.desc", ArgTypes.STRING); + + public ReplaceSwapCommand() { + super("swap", "server.commands.replace.swap.desc"); + this.setPermissionGroup(GameMode.Creative); + } + + @Override + protected void execute( + @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world + ) { + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + + assert playerComponent != null; + + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { + String fromValue = this.fromArg.get(context); + String toValue = this.toArg.get(context); + BlockTypeAssetMap assetMap = BlockType.getAssetMap(); + String[] blockKeys = fromValue.split(","); + Int2IntArrayMap swapMap = new Int2IntArrayMap(); + + for (int blockId = 0; blockId < assetMap.getAssetCount(); blockId++) { + BlockType blockType = assetMap.getAsset(blockId); + String blockKeyStr = blockType.getId(); + + for (String from : blockKeys) { + String trimmedFrom = from.trim(); + String blockKeyLower = blockKeyStr.toLowerCase(); + String fromLower = trimmedFrom.toLowerCase(); + int matchIdx = blockKeyLower.indexOf(fromLower); + if (matchIdx != -1) { + String replacedKey = blockKeyStr.substring(0, matchIdx) + toValue + blockKeyStr.substring(matchIdx + trimmedFrom.length()); + int index = assetMap.getIndex(replacedKey); + if (index != Integer.MIN_VALUE) { + swapMap.put(blockId, index); + break; + } + } + } + } + + if (!swapMap.isEmpty()) { + BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> { + int replaced = s.replace(r, value -> swapMap.getOrDefault(value, value), componentAccessor); + context.sendMessage(Message.translation("server.builderTools.replace.replacementDone").param("nb", replaced).param("to", toValue)); + }); + } else { + context.sendMessage(Message.translation("server.commands.replace.noMatchingBlocks").param("blockType", fromValue)); + } + } } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/RotateCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/RotateCommand.java index ccef0e6d..3ff95d9a 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/RotateCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/RotateCommand.java @@ -48,7 +48,7 @@ public class RotateCommand extends AbstractCommandCollection { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { float yaw = this.yawArg.get(context); float pitch = this.pitchArg.get(context); float roll = this.rollArg.get(context); @@ -84,7 +84,7 @@ public class RotateCommand extends AbstractCommandCollection { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { int angle = this.angleArg.get(context); Axis axis = this.axisArg.get(context); if (angle % 90 != 0) { diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/SelectChunkCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/SelectChunkCommand.java index 9879076f..de2e9d13 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/SelectChunkCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/SelectChunkCommand.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; @@ -16,6 +14,8 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class SelectChunkCommand extends AbstractPlayerCommand { public SelectChunkCommand() { @@ -31,14 +31,14 @@ public class SelectChunkCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; Vector3i min = new Vector3i(chunkX << 5, 0, chunkZ << 5); Vector3i max = new Vector3i((chunkX + 1 << 5) - 1, 319, (chunkZ + 1 << 5) - 1); BuilderToolsPlugin.addToQueue( diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/SelectChunkSectionCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/SelectChunkSectionCommand.java index 8ef33b1c..44f9f0de 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/SelectChunkSectionCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/SelectChunkSectionCommand.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; @@ -16,6 +14,8 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class SelectChunkSectionCommand extends AbstractPlayerCommand { public SelectChunkSectionCommand() { @@ -31,15 +31,15 @@ public class SelectChunkSectionCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkY = MathUtil.floor(position.getY()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkY = MathUtil.floor(position.y()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; Vector3i min = new Vector3i(chunkX << 5, chunkY << 5, chunkZ << 5); Vector3i max = new Vector3i((chunkX + 1 << 5) - 1, (chunkY + 1 << 5) - 1, (chunkZ + 1 << 5) - 1); BuilderToolsPlugin.addToQueue( diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/SetCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/SetCommand.java index 6962a441..08466393 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/SetCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/SetCommand.java @@ -36,7 +36,7 @@ public class SetCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BlockPattern pattern = this.patternArg.get(context); if (pattern != null && !pattern.isEmpty()) { BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.set(pattern, componentAccessor)); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/ShiftCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/ShiftCommand.java index 8c99346f..632dede8 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/ShiftCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/ShiftCommand.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.Axis; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ShiftCommand extends AbstractPlayerCommand { @Nonnull @@ -41,17 +41,17 @@ public class ShiftCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { Integer distance = this.distanceArg.get(context); Vector3i direction; if (this.axisArg.provided(context)) { - direction = this.axisArg.get(context).getDirection().scale(distance); + direction = this.axisArg.get(context).getDirection().mul(distance, new Vector3i()); } else { HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); assert headRotationComponent != null; - direction = headRotationComponent.getAxisDirection().scale(distance); + direction = headRotationComponent.getAxisDirection().mul(distance); } BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.shift(r, direction, componentAccessor)); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/StackCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/StackCommand.java index 527458fe..3cf8863a 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/StackCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/StackCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -20,6 +19,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class StackCommand extends AbstractPlayerCommand { @Nonnull @@ -52,7 +52,7 @@ public class StackCommand extends AbstractPlayerCommand { assert playerRefComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRefComponent, store)) { HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); assert headRotationComponent != null; diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/SubmergeCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/SubmergeCommand.java index 17777f2b..54d7c4d9 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/SubmergeCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/SubmergeCommand.java @@ -36,7 +36,7 @@ public class SubmergeCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { String fluidItemKey = this.fluidItemArg.get(context); if (!FluidPatternHelper.isFluidItem(fluidItemKey)) { context.sendMessage(Message.translation("server.builderTools.invalidBlockType").param("name", fluidItemKey).param("key", fluidItemKey)); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/TintCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/TintCommand.java index c2c97a5e..e06da48f 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/TintCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/TintCommand.java @@ -33,7 +33,7 @@ public class TintCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { String color = this.colorArg.get(context); int colorI; diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/UpdateSelectionCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/UpdateSelectionCommand.java index 3f9bfd3a..76dbdc71 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/UpdateSelectionCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/UpdateSelectionCommand.java @@ -42,7 +42,7 @@ public class UpdateSelectionCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { int xMin = this.xMinArg.get(context); int yMin = this.yMinArg.get(context); int zMin = this.zMinArg.get(context); diff --git a/src/com/hypixel/hytale/builtin/buildertools/commands/WallsCommand.java b/src/com/hypixel/hytale/builtin/buildertools/commands/WallsCommand.java index a4d65db1..9247a0ef 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/commands/WallsCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/commands/WallsCommand.java @@ -49,7 +49,7 @@ public class WallsCommand extends AbstractPlayerCommand { assert playerComponent != null; - if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerComponent, store)) { + if (PrototypePlayerBuilderToolSettings.isOkayToDoCommandsOnSelection(ref, playerRef, store)) { BlockPattern pattern = this.patternArg.get(context); if (pattern != null && !pattern.isEmpty()) { Boolean floor = this.floorArg.get(context); diff --git a/src/com/hypixel/hytale/builtin/buildertools/imageimport/ImageImportPage.java b/src/com/hypixel/hytale/builtin/buildertools/imageimport/ImageImportPage.java index 8c89424c..72acecc0 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/imageimport/ImageImportPage.java +++ b/src/com/hypixel/hytale/builtin/buildertools/imageimport/ImageImportPage.java @@ -9,14 +9,15 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.common.util.StringUtil; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.Page; +import com.hypixel.hytale.server.core.Constants; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.AssetModule; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage; +import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; import com.hypixel.hytale.server.core.ui.DropdownEntryInfo; import com.hypixel.hytale.server.core.ui.LocalizableString; @@ -38,6 +39,7 @@ import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.imageio.ImageIO; +import org.joml.Vector3i; public class ImageImportPage extends InteractiveCustomUIPage { private static final int DEFAULT_MAX_SIZE = 128; @@ -56,7 +58,7 @@ public class ImageImportPage extends InteractiveCustomUIPage ref, @Nonnull Store store) { if (this.imagePath.isEmpty()) { - this.setError("Please enter a path to an image file"); + this.setError(Message.translation("server.builderTools.imageImport.emptyPath")); } else { Path path = Paths.get(this.imagePath); - if (!AssetModule.get().isWithinPackSubDir(path, "Server/Imports/Images")) { - this.setError("File must be within an asset pack's imports directory"); + boolean isSingleplayerWorldOwner = Constants.SINGLEPLAYER && SingleplayerModule.isOwner(this.playerRef); + if (!isSingleplayerWorldOwner && !AssetModule.get().isWithinPackSubDir(path, "Server/Imports/Images")) { + this.setError(Message.translation("server.builderTools.imageImport.notInImportsDir")); } else if (!Files.exists(path)) { - this.setError("File not found: " + this.imagePath); + this.setError(Message.translation("server.builderTools.imageImport.fileNotFound").param("path", this.imagePath)); } else { this.isProcessing = true; - this.setStatus("Processing..."); + this.setStatus(Message.translation("server.builderTools.imageImport.processing")); Player playerComponent = store.getComponent(ref, Player.getComponentType()); PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType()); if (playerComponent != null && playerRefComponent != null) { @@ -287,7 +290,7 @@ public class ImageImportPage extends InteractiveCustomUIPage CODEC = BuilderCodec.builder( diff --git a/src/com/hypixel/hytale/builtin/buildertools/objimport/ObjImportPage.java b/src/com/hypixel/hytale/builtin/buildertools/objimport/ObjImportPage.java index ff8026c0..60718c95 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/objimport/ObjImportPage.java +++ b/src/com/hypixel/hytale/builtin/buildertools/objimport/ObjImportPage.java @@ -10,15 +10,16 @@ import com.hypixel.hytale.common.util.PathUtil; import com.hypixel.hytale.common.util.StringUtil; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.Page; +import com.hypixel.hytale.server.core.Constants; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.AssetModule; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage; +import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; import com.hypixel.hytale.server.core.ui.DropdownEntryInfo; import com.hypixel.hytale.server.core.ui.LocalizableString; @@ -44,6 +45,7 @@ import java.util.Map.Entry; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class ObjImportPage extends InteractiveCustomUIPage { private static final String DEFAULT_BLOCK = "Rock_Stone"; @@ -73,7 +75,7 @@ public class ObjImportPage extends InteractiveCustomUIPage ref, @Nonnull Store store) { if (this.objPath.isEmpty()) { - this.setError("Please enter a path to an OBJ file"); + this.setError(Message.translation("server.builderTools.objImport.emptyPath")); } else if (!this.objPath.toLowerCase().endsWith(".obj")) { - this.setError("File must be a .obj file"); + this.setError(Message.translation("server.builderTools.objImport.invalidExtension")); } else { Path path = Paths.get(this.objPath); - if (!AssetModule.get().isWithinPackSubDir(path, "Server/Imports/Models")) { - this.setError("File must be within an asset pack's imports directory"); + boolean isSingleplayerWorldOwner = Constants.SINGLEPLAYER && SingleplayerModule.isOwner(this.playerRef); + if (!isSingleplayerWorldOwner && !AssetModule.get().isWithinPackSubDir(path, "Server/Imports/Models")) { + this.setError(Message.translation("server.builderTools.objImport.notInImportsDir")); } else if (!Files.exists(path)) { - this.setError("File not found: " + this.objPath); + this.setError(Message.translation("server.builderTools.objImport.fileNotFound").param("path", this.objPath)); } else { List blocks = this.parseBlockPattern(this.blockPattern); if (blocks == null) { - this.setError("Invalid block pattern: " + this.blockPattern); + this.setError(Message.translation("server.builderTools.objImport.invalidPattern").param("pattern", this.blockPattern)); } else { this.isProcessing = true; - this.setStatus("Processing..."); + this.setStatus(Message.translation("server.builderTools.objImport.processing")); Player playerComponent = store.getComponent(ref, Player.getComponentType()); PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType()); if (playerComponent != null && playerRefComponent != null) { @@ -455,7 +458,7 @@ public class ObjImportPage extends InteractiveCustomUIPage { @Nonnull @@ -106,7 +106,7 @@ public class PrefabEditSession implements Resource { @Nonnull Path prefabPath, @Nonnull Vector3i minPoint, @Nonnull Vector3i maxPoint, @Nonnull Vector3i anchorPoint, @Nonnull Vector3i pastePosition ) { if (this.loadedPrefabMetadata.isEmpty()) { - this.spawnPoint.assign(maxPoint); + this.spawnPoint.set(maxPoint); } PrefabEditingMetadata prefabEditingMetadata = new PrefabEditingMetadata( @@ -204,11 +204,19 @@ public class PrefabEditSession implements Resource { return this.worldArrivedFrom; } + public void setWorldArrivedFrom(UUID worldUuid) { + this.worldArrivedFrom = worldUuid; + } + @Nullable public Transform getTransformArrivedFrom() { return this.transformArrivedFrom; } + public void setTransformArrivedFrom(Transform transform) { + this.transformArrivedFrom = transform; + } + public UUID getWorldCreator() { return this.worldCreator; } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditSessionManager.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditSessionManager.java index dbf34010..afeeae8e 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditSessionManager.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditSessionManager.java @@ -13,7 +13,6 @@ import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.FastRandom; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.Color; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.MovementStates; @@ -66,6 +65,7 @@ import java.util.function.Consumer; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PrefabEditSessionManager { @Nonnull @@ -104,38 +104,40 @@ public class PrefabEditSessionManager { } private void onPlayerReady(@Nonnull PlayerReadyEvent event) { - Ref playerRef = event.getPlayer().getReference(); - if (playerRef != null && playerRef.isValid()) { - Store store = playerRef.getStore(); + Ref ref = event.getPlayer().getReference(); + if (ref != null && ref.isValid()) { + Store store = ref.getStore(); World world = store.getExternalData().getWorld(); world.execute(() -> { - UUIDComponent uuidComponent = store.getComponent(playerRef, UUIDComponent.getComponentType()); + UUIDComponent uuidComponent = store.getComponent(ref, UUIDComponent.getComponentType()); assert uuidComponent != null; UUID playerUUID = uuidComponent.getUuid(); if (this.inProgressTeleportations.containsKey(playerUUID)) { this.inProgressTeleportations.remove(playerUUID); - MovementStatesComponent movementStatesComponent = store.getComponent(playerRef, MovementStatesComponent.getComponentType()); + MovementStatesComponent movementStatesComponent = store.getComponent(ref, MovementStatesComponent.getComponentType()); assert movementStatesComponent != null; MovementStates movementStates = movementStatesComponent.getMovementStates(); - Player playerComponent = store.getComponent(playerRef, Player.getComponentType()); + Player playerComponent = store.getComponent(ref, Player.getComponentType()); assert playerComponent != null; - playerComponent.applyMovementStates(playerRef, new SavedMovementStates(true), movementStates, store); - PlayerRef playerRefComponent = store.getComponent(playerRef, PlayerRef.getComponentType()); + playerComponent.applyMovementStates(ref, new SavedMovementStates(true), movementStates, store); + PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType()); if (playerRefComponent != null) { - this.givePrefabSelectorTool(playerComponent, playerRefComponent); + givePrefabSelectorTool(ref, playerComponent, playerRefComponent, store); } } }); } } - private void givePrefabSelectorTool(@Nonnull Player playerComponent, @Nonnull PlayerRef playerRef) { + private static void givePrefabSelectorTool( + @Nonnull Ref ref, @Nonnull Player playerComponent, @Nonnull PlayerRef playerRef, @Nonnull ComponentAccessor componentAccessor + ) { Inventory inventory = playerComponent.getInventory(); ItemContainer hotbar = inventory.getHotbar(); int hotbarSize = hotbar.getCapacity(); @@ -143,7 +145,7 @@ public class PrefabEditSessionManager { for (short slot = 0; slot < hotbarSize; slot++) { ItemStack itemStack = hotbar.getItemStack(slot); if (itemStack != null && !itemStack.isEmpty() && "EditorTool_PrefabEditing_SelectPrefab".equals(itemStack.getItemId())) { - inventory.setActiveHotbarSlot((byte)slot); + inventory.setActiveHotbarSlot(ref, (byte)slot, componentAccessor); playerRef.getPacketHandler().writeNoCache(new SetActiveSlot(-1, (byte)slot)); return; } @@ -164,21 +166,22 @@ public class PrefabEditSessionManager { } hotbar.setItemStackForSlot(emptySlot, new ItemStack("EditorTool_PrefabEditing_SelectPrefab")); - inventory.setActiveHotbarSlot((byte)emptySlot); + inventory.setActiveHotbarSlot(ref, (byte)emptySlot, componentAccessor); playerRef.getPacketHandler().writeNoCache(new SetActiveSlot(-1, (byte)emptySlot)); } public void onPlayerAddedToWorld(@Nonnull AddPlayerToWorldEvent event) { World world = event.getWorld(); + Holder playerHolder = event.getHolder(); + UUIDComponent uuidComponent = playerHolder.getComponent(UUIDComponent.getComponentType()); + + assert uuidComponent != null; + if (world.getName().startsWith("prefabEditor-")) { - world.execute(() -> { - Holder playerHolder = event.getHolder(); - UUIDComponent uuidComponent = playerHolder.getComponent(UUIDComponent.getComponentType()); - - assert uuidComponent != null; - - this.inProgressTeleportations.put(uuidComponent.getUuid(), world.getWorldConfig().getUuid()); - }); + world.execute(() -> this.inProgressTeleportations.put(uuidComponent.getUuid(), world.getWorldConfig().getUuid())); + } else if (this.isEditingAPrefab(uuidComponent.getUuid())) { + PrefabEditSession editSession = this.getPrefabEditSession(uuidComponent.getUuid()); + editSession.setWorldArrivedFrom(world.getWorldConfig().getUuid()); } } @@ -387,7 +390,7 @@ public class PrefabEditSessionManager { config.setIsSpawnMarkersEnabled(false); config.setObjectiveMarkersEnabled(false); config.setGameMode(GameMode.Creative); - config.setDeleteOnRemove(true); + config.setDeleteOnRemove(false); config.setUuid(UUID.randomUUID()); config.setGameTimePaused(true); config.setIsAllNPCFrozen(true); @@ -413,7 +416,7 @@ public class PrefabEditSessionManager { assert transformComponent != null; - Transform transform = transformComponent.getTransform().clone(); + Transform transform = new Transform(transformComponent.getTransform()); PrefabEditSession prefabEditSession = new PrefabEditSession(worldName, playerUUID, sourceWorld.getWorldConfig().getUuid(), transform); CompletableFuture future; if (createNewPrefab) { @@ -582,7 +585,7 @@ public class PrefabEditSessionManager { world -> CompletableFuture.supplyAsync( () -> { Vector3i pastePosition = new Vector3i(0, context.getPasteLevelGoal(), 0); - Vector3i anchorPosition = pastePosition.clone(); + Vector3i anchorPosition = new Vector3i(pastePosition); editSession.addPrefab( context.getPrefabPaths().getFirst(), new Vector3i(-1, context.getPasteLevelGoal() - 1, -1), @@ -618,7 +621,7 @@ public class PrefabEditSessionManager { for (int i = 0; i < context.getPrefabPaths().size(); i++) { Path prefabPath = context.getPrefabPaths().get(i); - CompletableFuture prefabLoadingFuture = this.getPrefabBuffer(context.getEditor(), prefabPath); + CompletableFuture prefabLoadingFuture = this.getPrefabBuffer(context.getEditorRef(), prefabPath); if (prefabLoadingFuture == null) { if (loadingState != null) { loadingState.addError("server.commands.editprefab.error.prefabLoadFailed", prefabPath.toString()); @@ -750,9 +753,7 @@ public class PrefabEditSessionManager { Vector3i pastePosition; if (context.getAlignment().equals(PrefabAlignment.ZERO)) { pastePosition = new Vector3i(0, yLevelToPastePrefabsAt, 0); - pastePosition.subtract( - Math.min(prefabAccessorx.getMinX(), 0), prefabAccessorx.getMinY(), Math.min(prefabAccessorx.getMinZ(), 0) - ); + pastePosition.sub(Math.min(prefabAccessorx.getMinX(), 0), prefabAccessorx.getMinY(), Math.min(prefabAccessorx.getMinZ(), 0)); if (context.getStackingAxis().equals(PrefabStackingAxis.X)) { pastePosition.add(lineOffset, 0, rowOffset); lineOffset += prefabXSize + context.getBlocksBetweenEachPrefab() + 1; @@ -805,7 +806,7 @@ public class PrefabEditSessionManager { context.shouldLoadEntities(), store ); - editSession.addPrefab(prefabPathx, minPoint, maxPoint, anchorPosition, pastePosition.clone()); + editSession.addPrefab(prefabPathx, minPoint, maxPoint, anchorPosition, new Vector3i(pastePosition)); if (loadingState != null) { loadingState.onPrefabPasted(prefabPathx); this.notifyProgress(progressCallback, loadingState); @@ -909,6 +910,28 @@ public class PrefabEditSessionManager { } } + public boolean isInEditWorld(@Nonnull PlayerRef playerRef, @Nonnull Store store) { + String currentWorldName = store.getExternalData().getWorld().getName(); + String editWorldName = this.getPrefabEditSession(playerRef.getUuid()).getWorldName(); + return currentWorldName.equals(editWorldName); + } + + @Nullable + public CompletableFuture sendToEditWorld(@Nonnull Ref ref, @Nonnull World world, @Nonnull PlayerRef playerRef) { + PrefabEditSession prefabEditSession = this.getPrefabEditSession(playerRef.getUuid()); + if (prefabEditSession == null) { + return CompletableFuture.completedFuture(null); + } else { + String sessionWorldName = prefabEditSession.getWorldName(); + World editSessionWorld = Universe.get().getWorld(sessionWorldName); + Teleport teleportComponent = Teleport.createForPlayer( + editSessionWorld, + ref.getStore().getComponent(ref, Player.getComponentType()).getPlayerConfigData().getPerWorldData(sessionWorldName).getLastPosition() + ); + return CompletableFuture.runAsync(() -> ref.getStore().putComponent(ref, Teleport.getComponentType(), teleportComponent), world); + } + } + @Nullable public CompletableFuture exitEditSession( @Nonnull Ref ref, @Nonnull World world, @Nonnull PlayerRef playerRef, @Nonnull ComponentAccessor componentAccessor @@ -918,35 +941,42 @@ public class PrefabEditSessionManager { return null; } else { prefabEditSession.hidePrefabAnchors(playerRef.getPacketHandler()); - World returnWorld = Universe.get().getWorld(prefabEditSession.getWorldArrivedFrom()); - Transform returnLocation = prefabEditSession.getTransformArrivedFrom(); - if (returnWorld == null || returnLocation == null) { - LOGGER.at(Level.WARNING) - .log( - "Prefab editor exit fallback triggered for player %s: returnWorld=%s (worldArrivedFrom=%s), returnLocation=%s. Using default world spawn.", - playerRef.getUuid(), - returnWorld != null ? returnWorld.getName() : "null", - prefabEditSession.getWorldArrivedFrom(), - returnLocation - ); - returnWorld = Universe.get().getDefaultWorld(); - returnLocation = returnWorld.getWorldConfig().getSpawnProvider().getSpawnPoint(ref, componentAccessor); + Runnable closeEditSession = () -> { + World worldToRemove = Universe.get().getWorld(prefabEditSession.getWorldName()); + if (worldToRemove != null) { + worldToRemove.getWorldConfig().setDeleteOnRemove(true); + Universe.get().removeWorld(prefabEditSession.getWorldName()); + } + + for (PrefabEditingMetadata prefab : prefabEditSession.getLoadedPrefabMetadata().values()) { + this.prefabsBeingEdited.remove(prefab.getPrefabPath()); + } + + this.activeEditSessions.remove(playerRef.getUuid()); + playerRef.sendMessage(Message.translation("server.commands.editprefab.closedEditSession")); + }; + if (!this.isInEditWorld(playerRef, ref.getStore())) { + return CompletableFuture.runAsync(closeEditSession); + } else { + World returnWorld = Universe.get().getWorld(prefabEditSession.getWorldArrivedFrom()); + Transform returnLocation = prefabEditSession.getTransformArrivedFrom(); + if (returnWorld == null || returnLocation == null) { + LOGGER.at(Level.WARNING) + .log( + "Prefab editor exit fallback triggered for player %s: returnWorld=%s (worldArrivedFrom=%s), returnLocation=%s. Using default world spawn.", + playerRef.getUuid(), + returnWorld != null ? returnWorld.getName() : "null", + prefabEditSession.getWorldArrivedFrom(), + returnLocation + ); + returnWorld = Universe.get().getDefaultWorld(); + returnLocation = returnWorld.getWorldConfig().getSpawnProvider().getSpawnPoint(ref, componentAccessor); + } + + Teleport teleportComponent = Teleport.createForPlayer(returnWorld, returnLocation); + return CompletableFuture.runAsync(() -> componentAccessor.putComponent(ref, Teleport.getComponentType(), teleportComponent), world) + .thenRunAsync(closeEditSession); } - - Teleport teleportComponent = Teleport.createForPlayer(returnWorld, returnLocation); - return CompletableFuture.runAsync(() -> componentAccessor.putComponent(ref, Teleport.getComponentType(), teleportComponent), world) - .thenRunAsync(() -> { - World worldToRemove = Universe.get().getWorld(prefabEditSession.getWorldName()); - if (worldToRemove != null) { - Universe.get().removeWorld(prefabEditSession.getWorldName()); - } - - for (PrefabEditingMetadata prefab : prefabEditSession.getLoadedPrefabMetadata().values()) { - this.prefabsBeingEdited.remove(prefab.getPrefabPath()); - } - - this.activeEditSessions.remove(playerRef.getUuid()); - }); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditingMetadata.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditingMetadata.java index 11c67459..06952199 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditingMetadata.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditingMetadata.java @@ -9,11 +9,12 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.codec.Vector3iArrayCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolShowAnchor; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.BlockEntity; import com.hypixel.hytale.server.core.io.PacketHandler; +import com.hypixel.hytale.server.core.modules.entity.DespawnComponent; import com.hypixel.hytale.server.core.modules.entity.component.EntityScaleComponent; import com.hypixel.hytale.server.core.modules.entity.component.Intangible; import com.hypixel.hytale.server.core.modules.time.TimeResource; @@ -24,6 +25,7 @@ import java.nio.file.Path; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PrefabEditingMetadata { private static final float PREFAB_ANCHOR_ENTITY_SCALE = 2.1F; @@ -51,6 +53,12 @@ public class PrefabEditingMetadata { o -> o.anchorEntityPosition ) .add() + .append( + new KeyedCodec<>("OriginalFileAnchor", new Vector3iArrayCodec(), false), + (o, originalFileAnchor) -> o.originalFileAnchor = originalFileAnchor, + o -> o.originalFileAnchor + ) + .add() .append(new KeyedCodec<>("Uuid", Codec.UUID_STRING), (o, uuid) -> o.uuid = uuid, o -> o.uuid) .add() .append(new KeyedCodec<>("Dirty", Codec.BOOLEAN, true), (o, dirty) -> o.dirty = dirty, o -> o.dirty) @@ -98,7 +106,7 @@ public class PrefabEditingMetadata { } private void createAnchorEntityAt(@Nonnull Vector3i position, @Nonnull World world) { - this.anchorEntityPosition = position.clone(); + this.anchorEntityPosition = new Vector3i(position); Store store = world.getEntityStore().getStore(); if (this.anchorEntityUuid != null) { Ref entityReference = store.getExternalData().getRefFromUUID(this.anchorEntityUuid); @@ -108,7 +116,10 @@ public class PrefabEditingMetadata { } TimeResource timeResource = store.getResource(TimeResource.getResourceType()); - Holder blockEntityHolder = BlockEntity.assembleDefaultBlockEntity(timeResource, "Editor_Anchor", position.toVector3d().add(0.5, 0.0, 0.5)); + Holder blockEntityHolder = BlockEntity.assembleDefaultBlockEntity( + timeResource, "Editor_Anchor", Vector3iUtil.toVector3d(position).add(0.5, 0.0, 0.5) + ); + blockEntityHolder.removeComponent(DespawnComponent.getComponentType()); blockEntityHolder.addComponent(Intangible.getComponentType(), Intangible.INSTANCE); blockEntityHolder.addComponent(PrefabAnchor.getComponentType(), PrefabAnchor.INSTANCE); blockEntityHolder.addComponent(EntityScaleComponent.getComponentType(), new EntityScaleComponent(2.1F)); @@ -129,6 +140,12 @@ public class PrefabEditingMetadata { } public void recreateAnchorEntity(@Nonnull World world) { + if (this.originalFileAnchor == null && this.anchorPoint != null && this.pastePosition != null) { + this.originalFileAnchor = new Vector3i( + this.anchorPoint.x - this.pastePosition.x, this.anchorPoint.y - this.pastePosition.y, this.anchorPoint.z - this.pastePosition.z + ); + } + if (this.anchorEntityPosition != null) { this.createAnchorEntityAt(this.anchorEntityPosition, world); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditorCreationSettings.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditorCreationSettings.java index e43125cc..11c94cae 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditorCreationSettings.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditorCreationSettings.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.builtin.buildertools.prefabeditor; import com.hypixel.hytale.assetstore.AssetExtraInfo; +import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.assetstore.AssetRegistry; import com.hypixel.hytale.assetstore.AssetStore; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; @@ -189,7 +190,7 @@ public class PrefabEditorCreationSettings inputPrefabName = inputPrefabName.replace('/', File.separatorChar); inputPrefabName = inputPrefabName.replace('\\', File.separatorChar); if (!SingleplayerModule.isOwner(playerRef) && !inputPrefabName.isEmpty() && Path.of(inputPrefabName).isAbsolute()) { - this.player.sendMessage(Message.translation("server.commands.editprefab.error.absolutePathNotAllowed")); + playerRef.sendMessage(Message.translation("server.commands.editprefab.error.absolutePathNotAllowed")); return null; } @@ -203,14 +204,14 @@ public class PrefabEditorCreationSettings String relativePath = this.getRelativePathForInput(inputPrefabName); Path resolvedPath = rootPath.resolve(relativePath); if (!SingleplayerModule.isOwner(playerRef) && !PathUtil.isChildOf(rootPath, resolvedPath)) { - this.player.sendMessage(Message.translation("server.commands.editprefab.error.pathTraversal")); + playerRef.sendMessage(Message.translation("server.commands.editprefab.error.pathTraversal")); return null; } this.prefabPaths.add(resolvedPath); } catch (Exception var13) { var13.printStackTrace(); - this.player.sendMessage(Message.translation("server.commands.editprefab.finishProcessingError").param("error", var13.getMessage())); + playerRef.sendMessage(Message.translation("server.commands.editprefab.finishProcessingError").param("error", var13.getMessage())); return null; } } else { @@ -218,12 +219,12 @@ public class PrefabEditorCreationSettings String relativePath = this.getRelativePathForInput(inputPrefabName); Path resolvedDir = rootPath.resolve(relativePath); if (!SingleplayerModule.isOwner(playerRef) && !PathUtil.isChildOf(rootPath, resolvedDir)) { - this.player.sendMessage(Message.translation("server.commands.editprefab.error.pathTraversal")); + playerRef.sendMessage(Message.translation("server.commands.editprefab.error.pathTraversal")); return null; } try (Stream walk = Files.walk(resolvedDir, this.recursive ? 10 : 1)) { - walk.filter(x$0 -> Files.isRegularFile(x$0)).filter(path -> path.toString().endsWith(".prefab.json")).forEach(this.prefabPaths::add); + walk.filter(x$0 -> Files.isRegularFile(x$0)).filter(path -> path.toString().endsWith(".prefab.json")).sorted().forEach(this.prefabPaths::add); } catch (IOException var15) { var15.printStackTrace(); } @@ -233,8 +234,7 @@ public class PrefabEditorCreationSettings if (!creatingNewPrefab) { for (Path processedPrefabPath : this.prefabPaths) { if (!Files.exists(processedPrefabPath)) { - this.player - .sendMessage(Message.translation("server.commands.editprefab.load.error.prefabNotFound").param("path", processedPrefabPath.toString())); + playerRef.sendMessage(Message.translation("server.commands.editprefab.load.error.prefabNotFound").param("path", processedPrefabPath.toString())); return null; } } @@ -248,7 +248,7 @@ public class PrefabEditorCreationSettings .map(Path::toString) .map(Message::raw) .collect(Collectors.toSet()); - this.player.sendMessage(MessageFormat.list(header, values)); + playerRef.sendMessage(MessageFormat.list(header, values)); return null; } else { return this; @@ -304,11 +304,16 @@ public class PrefabEditorCreationSettings @Nonnull public static CompletableFuture save(@Nonnull String name, PrefabEditorCreationSettings settings) { + return save(name, settings, AssetModule.get().getBaseAssetPack()); + } + + @Nonnull + public static CompletableFuture save(@Nonnull String name, PrefabEditorCreationSettings settings, @Nonnull AssetPack pack) { return CompletableFuture.runAsync(() -> { try { - getAssetStore().writeAssetToDisk(AssetModule.get().getBaseAssetPack(), Map.of(Path.of(name + ".json"), settings)); - } catch (IOException var3) { - var3.printStackTrace(); + getAssetStore().writeAssetToDisk(pack, Map.of(Path.of(name + ".json"), settings)); + } catch (IOException var4) { + var4.printStackTrace(); } }); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabSelectionInteraction.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabSelectionInteraction.java index bb8fb09c..abeb3687 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabSelectionInteraction.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabSelectionInteraction.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.InteractionContext; @@ -15,11 +14,14 @@ import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PrefabSelectionInteraction extends SimpleInstantInteraction { @Nonnull @@ -49,6 +51,10 @@ public class PrefabSelectionInteraction extends SimpleInstantInteraction { Ref ref = context.getEntity(); Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); if (playerComponent != null) { + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + + assert playerRefComponent != null; + if (type == InteractionType.Primary || type == InteractionType.Secondary) { UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); @@ -58,7 +64,7 @@ public class PrefabSelectionInteraction extends SimpleInstantInteraction { PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); PrefabEditSession prefabEditSession = prefabEditSessionManager.getPrefabEditSession(uuid); if (prefabEditSession == null) { - playerComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION); + playerRefComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION); } else { TransformComponent transformComponent = commandBuffer.getComponent(ref, TransformComponent.getComponentType()); @@ -67,15 +73,15 @@ public class PrefabSelectionInteraction extends SimpleInstantInteraction { Vector3d playerPosition = transformComponent.getPosition(); PrefabEditingMetadata prefabEditingMetadata = null; if (type == InteractionType.Secondary) { - Vector3d playerLocation = playerPosition.clone(); - playerLocation.setY(0.0); + Vector3d playerLocation = new Vector3d(playerPosition); + playerLocation.y = 0.0; double distance = 2.147483647E9; for (PrefabEditingMetadata value : prefabEditSession.getLoadedPrefabMetadata().values()) { Vector3d centerPoint = new Vector3d( (value.getMaxPoint().x + value.getMinPoint().x) / 2.0, 0.0, (value.getMaxPoint().z + value.getMinPoint().z) / 2.0 ); - double distanceTo = centerPoint.distanceTo(playerLocation); + double distanceTo = centerPoint.distance(playerLocation); if (distance > distanceTo) { distance = distanceTo; prefabEditingMetadata = value; @@ -84,7 +90,7 @@ public class PrefabSelectionInteraction extends SimpleInstantInteraction { } else { Vector3i targetLocation = getTargetLocation(ref, commandBuffer); if (targetLocation == null) { - playerComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_SELECT_ERROR_NO_TARGET_FOUND); + playerRefComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_SELECT_ERROR_NO_TARGET_FOUND); return; } @@ -98,7 +104,7 @@ public class PrefabSelectionInteraction extends SimpleInstantInteraction { } if (prefabEditingMetadata == null) { - playerComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_SELECT_ERROR_NO_PREFAB_FOUND); + playerRefComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_SELECT_ERROR_NO_PREFAB_FOUND); } else { prefabEditSession.setSelectedPrefab(ref, prefabEditingMetadata, commandBuffer); } @@ -116,7 +122,7 @@ public class PrefabSelectionInteraction extends SimpleInstantInteraction { Ref targetEntityRef = TargetUtil.getTargetEntity(ref, 50.0F, componentAccessor); if (targetEntityRef != null && targetEntityRef.isValid()) { TransformComponent entityTransformComponent = componentAccessor.getComponent(targetEntityRef, TransformComponent.getComponentType()); - return entityTransformComponent == null ? null : entityTransformComponent.getPosition().toVector3i(); + return entityTransformComponent == null ? null : Vector3dUtil.toVector3i(entityTransformComponent.getPosition()); } else { return null; } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabSetAnchorInteraction.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabSetAnchorInteraction.java deleted file mode 100644 index 6eb42742..00000000 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabSetAnchorInteraction.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.hypixel.hytale.builtin.buildertools.prefabeditor; - -import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.component.CommandBuffer; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.protocol.BlockPosition; -import com.hypixel.hytale.protocol.InteractionType; -import com.hypixel.hytale.server.core.Message; -import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.UUIDComponent; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; -import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction; -import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import java.util.UUID; -import javax.annotation.Nonnull; - -public class PrefabSetAnchorInteraction extends SimpleInstantInteraction { - @Nonnull - private static final Message MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION = Message.translation("server.commands.editprefab.notInEditSession"); - @Nonnull - private static final Message MESSAGE_COMMANDS_EDIT_PREFAB_ANCHOR_ERROR_NO_ANCHOR_FOUND = Message.translation( - "server.commands.editprefab.anchor.error.noAnchorFound" - ); - @Nonnull - private static final Message MESSAGE_COMMANDS_EDIT_PREFAB_SELECT_ERROR_NO_PREFAB_FOUND = Message.translation( - "server.commands.editprefab.select.error.noPrefabFound" - ); - @Nonnull - private static final Message MESSAGE_COMMANDS_EDIT_PREFAB_ANCHOR_SUCCESS = Message.translation("server.commands.editprefab.anchor.success"); - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder( - PrefabSetAnchorInteraction.class, PrefabSetAnchorInteraction::new, SimpleInstantInteraction.CODEC - ) - .documentation("Sets the prefab anchor.") - .build(); - - @Override - protected void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) { - CommandBuffer commandBuffer = context.getCommandBuffer(); - - assert commandBuffer != null; - - Ref ref = context.getEntity(); - Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); - if (playerComponent != null) { - if (type == InteractionType.Primary || type == InteractionType.Secondary) { - UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); - - assert uuidComponent != null; - - PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); - - assert playerRefComponent != null; - - UUID playerUuid = uuidComponent.getUuid(); - PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); - PrefabEditSession prefabEditSession = prefabEditSessionManager.getPrefabEditSession(playerUuid); - if (prefabEditSession == null) { - playerRefComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION); - } else { - PrefabEditingMetadata prefabEditingMetadata = null; - BlockPosition targetBlock = context.getTargetBlock(); - if (targetBlock == null) { - playerRefComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_ANCHOR_ERROR_NO_ANCHOR_FOUND); - } else { - Vector3i targetBlockPos = new Vector3i(targetBlock.x, targetBlock.y, targetBlock.z); - - for (PrefabEditingMetadata value : prefabEditSession.getLoadedPrefabMetadata().values()) { - boolean isWithinPrefab = value.isLocationWithinPrefabBoundingBox(targetBlockPos); - if (isWithinPrefab) { - prefabEditingMetadata = value; - break; - } - } - - if (prefabEditingMetadata == null) { - playerRefComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_SELECT_ERROR_NO_PREFAB_FOUND); - } else { - prefabEditSession.setSelectedPrefab(ref, prefabEditingMetadata, commandBuffer); - prefabEditingMetadata.setAnchorPoint(targetBlockPos, commandBuffer.getExternalData().getWorld()); - prefabEditingMetadata.sendAnchorHighlightingPacket(playerRefComponent.getPacketHandler()); - playerRefComponent.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_ANCHOR_SUCCESS); - } - } - } - } - } - } -} diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditBackCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditBackCommand.java new file mode 100644 index 00000000..1bf91502 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditBackCommand.java @@ -0,0 +1,63 @@ +package com.hypixel.hytale.builtin.buildertools.prefabeditor.commands; + +import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSession; +import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionManager; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Transform; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncPlayerCommand; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nonnull; + +public class PrefabEditBackCommand extends AbstractAsyncPlayerCommand { + @Nonnull + private static final Message MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION = Message.translation("server.commands.editprefab.notInEditSession"); + @Nonnull + private static final Message MESSAGE_COMMANDS_ALREADY_IN_EDIT_SESSION = Message.translation("server.commands.editprefab.alreadyInEditSession"); + + public PrefabEditBackCommand() { + super("back", "server.commands.editprefab.back.desc"); + } + + @Nonnull + @Override + protected CompletableFuture executeAsync( + @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world + ) { + PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); + if (!prefabEditSessionManager.isEditingAPrefab(playerRef.getUuid())) { + context.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION); + return CompletableFuture.completedFuture(null); + } else { + PrefabEditSession prefabEditSession = prefabEditSessionManager.getPrefabEditSession(playerRef.getUuid()); + String sessionWorldName = prefabEditSession.getWorldName(); + String currentWorldName = Universe.get().getWorld(playerRef.getWorldUuid()).getName(); + if (currentWorldName.equalsIgnoreCase(sessionWorldName)) { + Player playerComponent = ref.getStore().getComponent(ref, Player.getComponentType()); + World previousWorld = Universe.get().getWorld(prefabEditSession.getWorldArrivedFrom()); + Transform previousPosition; + if (previousWorld != null) { + previousPosition = playerComponent.getPlayerConfigData().getPerWorldData(previousWorld.getName()).getLastPosition(); + } else { + previousWorld = Universe.get().getDefaultWorld(); + previousPosition = previousWorld.getWorldConfig().getSpawnProvider().getSpawnPoint(previousWorld, playerRef.getUuid()); + } + + Teleport teleportComponent = Teleport.createForPlayer(previousWorld, previousPosition); + prefabEditSession.clearSelectedPrefab(ref, store); + return CompletableFuture.runAsync(() -> ref.getStore().putComponent(ref, Teleport.getComponentType(), teleportComponent), world); + } else { + return prefabEditSessionManager.sendToEditWorld(ref, world, playerRef); + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditCommand.java index 0ea495c2..07bb2d75 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditCommand.java @@ -18,5 +18,6 @@ public class PrefabEditCommand extends AbstractCommandCollection { this.addSubCommand(new PrefabEditInfoCommand()); this.addSubCommand(new PrefabEditTeleportCommand()); this.addSubCommand(new PrefabEditModifiedCommand()); + this.addSubCommand(new PrefabEditBackCommand()); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditInfoCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditInfoCommand.java index cff246b3..46a9050b 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditInfoCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditInfoCommand.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionMan import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditingMetadata; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; @@ -16,6 +15,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class PrefabEditInfoCommand extends AbstractPlayerCommand { @Nonnull @@ -47,9 +47,9 @@ public class PrefabEditInfoCommand extends AbstractPlayerCommand { } else { Vector3i minPoint = selectedPrefab.getMinPoint(); Vector3i maxPoint = selectedPrefab.getMaxPoint(); - int xWidth = maxPoint.getX() - minPoint.getX(); - int zWidth = maxPoint.getZ() - minPoint.getZ(); - int yHeight = maxPoint.getY() - minPoint.getY(); + int xWidth = maxPoint.x() - minPoint.x(); + int zWidth = maxPoint.z() - minPoint.z(); + int yHeight = maxPoint.y() - minPoint.y(); context.sendMessage( Message.translation("server.commands.editprefab.info.format") .param("path", selectedPrefab.getPrefabPath().toString()) diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditKillEntitiesCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditKillEntitiesCommand.java index c7290e61..206a1be2 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditKillEntitiesCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditKillEntitiesCommand.java @@ -1,14 +1,13 @@ package com.hypixel.hytale.builtin.buildertools.prefabeditor.commands; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabAnchor; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSession; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionManager; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditingMetadata; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; @@ -18,6 +17,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PrefabEditKillEntitiesCommand extends AbstractPlayerCommand { @Nonnull @@ -44,16 +45,20 @@ public class PrefabEditKillEntitiesCommand extends AbstractPlayerCommand { } else { Vector3i selectionMax = selectedPrefab.getMaxPoint(); Vector3i selectionMin = selectedPrefab.getMinPoint(); - Vector3i lengths = selectionMax.subtract(selectionMin); + Vector3i lengths = selectionMax.sub(selectionMin); Vector3d min = new Vector3d(selectionMin.x, selectionMin.y, selectionMin.z); Vector3d max = new Vector3d(selectionMax.x + 1, selectionMax.y + 1, selectionMax.z + 1); List> entitiesInBox = TargetUtil.getAllEntitiesInBox(min, max, store); + int removed = 0; for (Ref entityRef : entitiesInBox) { - store.removeEntity(entityRef, RemoveReason.REMOVE); + if (store.getComponent(entityRef, PrefabAnchor.getComponentType()) == null) { + store.removeEntity(entityRef, RemoveReason.REMOVE); + removed++; + } } - context.sendMessage(Message.translation("server.commands.editprefab.kill.done").param("amount", entitiesInBox.size())); + context.sendMessage(Message.translation("server.commands.editprefab.kill.done").param("amount", removed)); } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditLoadCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditLoadCommand.java index ab49e402..694d52f4 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditLoadCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditLoadCommand.java @@ -1,12 +1,14 @@ package com.hypixel.hytale.builtin.buildertools.prefabeditor.commands; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionManager; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditorCreationSettings; import com.hypixel.hytale.builtin.buildertools.prefabeditor.enums.PrefabAlignment; import com.hypixel.hytale.builtin.buildertools.prefabeditor.enums.PrefabRootDirectory; import com.hypixel.hytale.builtin.buildertools.prefabeditor.enums.PrefabRowSplitMode; import com.hypixel.hytale.builtin.buildertools.prefabeditor.enums.PrefabStackingAxis; import com.hypixel.hytale.builtin.buildertools.prefabeditor.enums.WorldGenType; +import com.hypixel.hytale.builtin.buildertools.prefabeditor.ui.PrefabEditorLoadOptionsPage; import com.hypixel.hytale.builtin.buildertools.prefabeditor.ui.PrefabEditorLoadSettingsPage; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.Ref; @@ -24,6 +26,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; @@ -120,6 +123,10 @@ public class PrefabEditLoadCommand extends AbstractAsyncPlayerCommand { "server.commands.prefabeditsessionmanager.existingEditSession" ); + { + Objects.requireNonNull(PrefabEditLoadCommand.this); + } + @Override protected void execute( @Nonnull CommandContext context, @@ -128,13 +135,18 @@ public class PrefabEditLoadCommand extends AbstractAsyncPlayerCommand { @Nonnull PlayerRef playerRef, @Nonnull World world ) { - if (BuilderToolsPlugin.get().getPrefabEditSessionManager().isEditingAPrefab(playerRef.getUuid())) { - context.sendMessage(MESSAGE_COMMANDS_PREFAB_EDIT_SESSION_MANAGER_EXISTING_EDIT_SESSION); + PrefabEditSessionManager editSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + + assert playerComponent != null; + + if (editSessionManager.isEditingAPrefab(playerRef.getUuid())) { + if (!editSessionManager.isInEditWorld(playerRef, store)) { + playerComponent.getPageManager().openCustomPage(ref, store, new PrefabEditorLoadOptionsPage(playerRef, world)); + } else { + context.sendMessage(MESSAGE_COMMANDS_PREFAB_EDIT_SESSION_MANAGER_EXISTING_EDIT_SESSION); + } } else { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - playerComponent.getPageManager().openCustomPage(ref, store, new PrefabEditorLoadSettingsPage(playerRef)); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveAsCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveAsCommand.java index 5142c349..69eb4c68 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveAsCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveAsCommand.java @@ -1,10 +1,11 @@ package com.hypixel.hytale.builtin.buildertools.prefabeditor.commands; +import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.BuilderToolsUserData; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSession; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionManager; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditingMetadata; -import com.hypixel.hytale.builtin.buildertools.prefabeditor.enums.PrefabRootDirectory; import com.hypixel.hytale.builtin.buildertools.prefabeditor.saving.PrefabSaver; import com.hypixel.hytale.builtin.buildertools.prefabeditor.saving.PrefabSaverSettings; import com.hypixel.hytale.common.util.PathUtil; @@ -19,6 +20,7 @@ import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncPlayerCommand; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule; +import com.hypixel.hytale.server.core.prefab.PrefabStore; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; @@ -29,19 +31,17 @@ import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; public class PrefabEditSaveAsCommand extends AbstractAsyncPlayerCommand { + @Nonnull + private static final Message MESSAGE_NOT_IN_EDIT_WORLD = Message.translation("server.commands.editprefab.notInEditWorldWarning"); private final RequiredArg fileNameArg = this.withRequiredArg("fileNameArg", "server.commands.editprefab.save.saveAs.desc", ArgTypes.STRING); - private final DefaultArg prefabPathArg = this.withDefaultArg( - "prefabPath", - "server.commands.editprefab.save.path.desc", - ArgTypes.forEnum("PrefabPath", PrefabRootDirectory.class), - PrefabRootDirectory.SERVER, - "server.commands.editprefab.save.path.default.desc" - ); private final FlagArg noEntitiesArg = this.withFlagArg("noEntities", "server.commands.editprefab.save.noEntities.desc"); private final FlagArg overwriteArg = this.withFlagArg("overwrite", "server.commands.editprefab.save.overwrite.desc"); private final FlagArg emptyArg = this.withFlagArg("empty", "server.commands.editprefab.save.empty.desc"); private final FlagArg noUpdateArg = this.withFlagArg("noUpdate", "server.commands.editprefab.saveAs.noUpdate.desc"); private final FlagArg clearSupportArg = this.withFlagArg("clearSupport", "server.commands.editprefab.save.clearSupport.desc"); + private final DefaultArg packArg = this.withDefaultArg( + "pack", "server.commands.editprefab.save.pack.desc", ArgTypes.STRING, "", "server.commands.editprefab.save.pack.desc" + ); public PrefabEditSaveAsCommand() { super("saveas", "server.commands.editprefab.saveAs.desc"); @@ -62,6 +62,9 @@ public class PrefabEditSaveAsCommand extends AbstractAsyncPlayerCommand { if (prefabEditSession == null) { context.sendMessage(Message.translation("server.commands.editprefab.notInEditSession")); return CompletableFuture.completedFuture(null); + } else if (!prefabEditSessionManager.isInEditWorld(playerRef, store)) { + context.sendMessage(MESSAGE_NOT_IN_EDIT_WORLD); + return CompletableFuture.completedFuture(null); } else { PrefabSaverSettings prefabSaverSettings = new PrefabSaverSettings(); prefabSaverSettings.setBlocks(true); @@ -69,52 +72,61 @@ public class PrefabEditSaveAsCommand extends AbstractAsyncPlayerCommand { prefabSaverSettings.setOverwriteExisting(this.overwriteArg.get(context)); prefabSaverSettings.setEmpty(this.emptyArg.get(context)); prefabSaverSettings.setClearSupportValues(this.clearSupportArg.get(context)); - Path prefabRootPath = this.prefabPathArg.get(context).getPrefabPath(); - if (!PathUtil.isChildOf(prefabRootPath, prefabRootPath.resolve(this.fileNameArg.get(context))) && !SingleplayerModule.isOwner(playerRef)) { - context.sendMessage(Message.translation("server.builderTools.attemptedToSaveOutsidePrefabsDir")); + PrefabEditingMetadata selectedPrefab = prefabEditSession.getSelectedPrefab(uuid); + String packName = this.packArg.get(context); + Path sourcePrefabPath = selectedPrefab != null ? selectedPrefab.getPrefabPath() : null; + AssetPack targetPack = BuilderToolsPlugin.resolveTargetPack(packName != null ? packName : "", sourcePrefabPath, playerComponent, context); + if (targetPack == null) { return CompletableFuture.completedFuture(null); } else { - Path prefabSavePath = prefabRootPath.resolve(this.fileNameArg.get(context)); - if (prefabSavePath.toString().endsWith("/")) { - context.sendMessage(Message.translation("server.commands.editprefab.saveAs.errors.notAFile")); + BuilderToolsUserData.get(playerComponent).setLastSavePack(targetPack.getName()); + Path prefabRootPath = PrefabStore.get().getAssetPrefabsPathForPack(targetPack); + if (!PathUtil.isChildOf(prefabRootPath, prefabRootPath.resolve(this.fileNameArg.get(context))) && !SingleplayerModule.isOwner(playerRef)) { + context.sendMessage(Message.translation("server.builderTools.attemptedToSaveOutsidePrefabsDir")); return CompletableFuture.completedFuture(null); } else { - if (!prefabEditSession.toString().endsWith(".prefab.json")) { - prefabSavePath = Path.of(prefabSavePath + ".prefab.json"); - } - - PrefabEditingMetadata selectedPrefab = prefabEditSession.getSelectedPrefab(uuid); - if (selectedPrefab == null) { - context.sendMessage(Message.translation("server.commands.editprefab.noPrefabSelected")); + Path prefabSavePath = prefabRootPath.resolve(this.fileNameArg.get(context)); + if (prefabSavePath.toString().endsWith("/")) { + context.sendMessage(Message.translation("server.commands.editprefab.saveAs.errors.notAFile")); return CompletableFuture.completedFuture(null); } else { - BlockSelection selection = BuilderToolsPlugin.getState(playerComponent, playerRef).getSelection(); - if (selectedPrefab.getMinPoint().equals(selection.getSelectionMin()) && selectedPrefab.getMaxPoint().equals(selection.getSelectionMax())) { - if (!this.noUpdateArg.provided(context)) { - prefabEditSessionManager.updatePathOfLoadedPrefab(selectedPrefab.getPrefabPath(), prefabSavePath); - selectedPrefab.setPrefabPath(prefabSavePath); - } + if (!prefabEditSession.toString().endsWith(".prefab.json")) { + prefabSavePath = Path.of(prefabSavePath + ".prefab.json"); + } - return PrefabSaver.savePrefab( - playerComponent, - world, - prefabSavePath, - selectedPrefab.getAnchorPoint(), - selectedPrefab.getMinPoint(), - selectedPrefab.getMaxPoint(), - selectedPrefab.getPastePosition(), - selectedPrefab.getOriginalFileAnchor(), - prefabSaverSettings - ) - .thenAccept( - success -> context.sendMessage( - Message.translation("server.commands.editprefab.save." + (success ? "success" : "failure")) - .param("name", selectedPrefab.getPrefabPath().toString()) - ) - ); - } else { - context.sendMessage(Message.translation("server.commands.editprefab.save.selectionMismatch")); + if (selectedPrefab == null) { + context.sendMessage(Message.translation("server.commands.editprefab.noPrefabSelected")); return CompletableFuture.completedFuture(null); + } else { + BlockSelection selection = BuilderToolsPlugin.getState(playerComponent, playerRef).getSelection(); + if (selectedPrefab.getMinPoint().equals(selection.getSelectionMin()) && selectedPrefab.getMaxPoint().equals(selection.getSelectionMax())) { + if (!this.noUpdateArg.provided(context)) { + prefabEditSessionManager.updatePathOfLoadedPrefab(selectedPrefab.getPrefabPath(), prefabSavePath); + selectedPrefab.setPrefabPath(prefabSavePath); + } + + return PrefabSaver.savePrefab( + playerRef, + world, + prefabSavePath, + selectedPrefab.getAnchorPoint(), + selectedPrefab.getMinPoint(), + selectedPrefab.getMaxPoint(), + selectedPrefab.getPastePosition(), + selectedPrefab.getOriginalFileAnchor(), + prefabSaverSettings + ) + .thenAccept( + success -> context.sendMessage( + Message.translation("server.commands.editprefab.save." + (success ? "success.pack" : "failure.pack")) + .param("name", selectedPrefab.getPrefabPath().toString()) + .param("pack", targetPack.getName()) + ) + ); + } else { + context.sendMessage(Message.translation("server.commands.editprefab.save.selectionMismatch")); + return CompletableFuture.completedFuture(null); + } } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveCommand.java index 853d8995..77530779 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveCommand.java @@ -1,6 +1,8 @@ package com.hypixel.hytale.builtin.buildertools.prefabeditor.commands; +import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.BuilderToolsUserData; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSession; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionManager; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditingMetadata; @@ -12,7 +14,9 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.AssetModule; import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncPlayerCommand; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule; @@ -26,6 +30,7 @@ import java.nio.file.Path; import java.util.List; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class PrefabEditSaveCommand extends AbstractAsyncPlayerCommand { @Nonnull @@ -33,8 +38,14 @@ public class PrefabEditSaveCommand extends AbstractAsyncPlayerCommand { @Nonnull private static final Message MESSAGE_PATH_OUTSIDE_PREFABS_DIR = Message.translation("server.builderTools.attemptedToSaveOutsidePrefabsDir"); @Nonnull + private static final Message MESSAGE_NOT_IN_EDIT_WORLD = Message.translation("server.commands.editprefab.notInEditWorldWarning"); + @Nonnull private final FlagArg saveAllArg = this.withFlagArg("saveAll", "server.commands.editprefab.save.saveAll.desc").addAliases("all"); @Nonnull + private final DefaultArg packArg = this.withDefaultArg( + "pack", "server.commands.editprefab.save.pack.desc", ArgTypes.STRING, "", "server.commands.editprefab.save.pack.desc" + ); + @Nonnull private final FlagArg noEntitiesArg = this.withFlagArg("noEntities", "server.commands.editprefab.save.noEntities.desc"); @Nonnull private final FlagArg emptyArg = this.withFlagArg("empty", "server.commands.editprefab.save.empty.desc"); @@ -73,6 +84,9 @@ public class PrefabEditSaveCommand extends AbstractAsyncPlayerCommand { if (prefabEditSession == null) { context.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION); return CompletableFuture.completedFuture(null); + } else if (!prefabEditSessionManager.isInEditWorld(playerRef, store)) { + context.sendMessage(MESSAGE_NOT_IN_EDIT_WORLD); + return CompletableFuture.completedFuture(null); } else { PrefabSaverSettings prefabSaverSettings = new PrefabSaverSettings(); prefabSaverSettings.setBlocks(true); @@ -81,29 +95,62 @@ public class PrefabEditSaveCommand extends AbstractAsyncPlayerCommand { prefabSaverSettings.setEmpty(this.emptyArg.get(context)); prefabSaverSettings.setClearSupportValues(this.clearSupportArg.get(context)); boolean confirm = this.confirmArg.provided(context); + String packName = this.packArg.get(context); + AssetPack targetPack = null; + boolean packExplicit; + if (packName != null && !packName.isEmpty()) { + targetPack = BuilderToolsPlugin.resolveTargetPack(packName, playerComponent, context); + if (targetPack == null) { + return CompletableFuture.completedFuture(null); + } + + packExplicit = true; + } else { + packExplicit = false; + } + if (!this.saveAllArg.provided(context)) { PrefabEditingMetadata selectedPrefab = prefabEditSession.getSelectedPrefab(playerRef.getUuid()); if (selectedPrefab == null) { context.sendMessage(Message.translation("server.commands.editprefab.noPrefabSelected")); return CompletableFuture.completedFuture(null); - } else if (selectedPrefab.isReadOnly() && !confirm) { - Path redirectPath = getWritableSavePath(selectedPrefab, true); - context.sendMessage( - Message.translation("server.commands.editprefab.save.readOnlyNeedsConfirmSingle") - .param("path", selectedPrefab.getPrefabPath().toString()) - .param("redirectPath", redirectPath.toString()) - ); - return CompletableFuture.completedFuture(null); } else { + if (selectedPrefab.isReadOnly() && !packExplicit) { + targetPack = BuilderToolsPlugin.resolveTargetPack("", selectedPrefab.getPrefabPath(), playerComponent, context); + if (targetPack == null) { + return CompletableFuture.completedFuture(null); + } + + if (!confirm) { + Path redirectPath = getWritableSavePath(selectedPrefab, targetPack); + context.sendMessage( + Message.translation("server.commands.editprefab.save.readOnlyNeedsConfirmSingle") + .param("path", selectedPrefab.getPrefabPath().toString()) + .param("redirectPath", redirectPath.toString()) + ); + return CompletableFuture.completedFuture(null); + } + } + BlockSelection selection = BuilderToolsPlugin.getState(playerComponent, playerRef).getSelection(); if (selectedPrefab.getMinPoint().equals(selection.getSelectionMin()) && selectedPrefab.getMaxPoint().equals(selection.getSelectionMax())) { - Path savePath = getWritableSavePath(selectedPrefab, confirm); + if (targetPack != null) { + BuilderToolsUserData.get(playerComponent).setLastSavePack(targetPack.getName()); + } + + Path savePath = getWritableSavePath(selectedPrefab, targetPack); if (!SingleplayerModule.isOwner(playerRef) && !isPathInAllowedPrefabDirectory(savePath)) { context.sendMessage(MESSAGE_PATH_OUTSIDE_PREFABS_DIR); return CompletableFuture.completedFuture(null); } else { + AssetPack packForMessage = targetPack; + if (targetPack == null) { + packForMessage = PrefabStore.get().findAssetPackForPrefabPath(savePath); + } + + AssetPack resolvedPack = packForMessage; return PrefabSaver.savePrefab( - playerComponent, + playerRef, world, savePath, selectedPrefab.getAnchorPoint(), @@ -113,17 +160,19 @@ public class PrefabEditSaveCommand extends AbstractAsyncPlayerCommand { selectedPrefab.getOriginalFileAnchor(), prefabSaverSettings ) - .thenAccept( - success -> { - if (success) { - selectedPrefab.setDirty(false); - } - - context.sendMessage( - Message.translation("server.commands.editprefab.save." + (success ? "success" : "failure")).param("name", savePath.toString()) - ); + .thenAccept(success -> { + if (success) { + selectedPrefab.setDirty(false); } - ); + + String keySuffix = (success ? "success" : "failure") + (resolvedPack != null ? ".pack" : ""); + Message msg = Message.translation("server.commands.editprefab.save." + keySuffix).param("name", savePath.toString()); + if (resolvedPack != null) { + msg = msg.param("pack", resolvedPack.getName()); + } + + context.sendMessage(msg); + }); } } else { context.sendMessage(Message.translation("server.commands.editprefab.save.selectionMismatch")); @@ -140,76 +189,83 @@ public class PrefabEditSaveCommand extends AbstractAsyncPlayerCommand { } } - if (readOnlyCount > 0 && !confirm) { - context.sendMessage(Message.translation("server.commands.editprefab.save.readOnlyNeedsConfirm").param("count", readOnlyCount)); - return CompletableFuture.completedFuture(null); - } else { - if (!SingleplayerModule.isOwner(playerRef)) { - for (PrefabEditingMetadata valuex : values) { - Path savePath = getWritableSavePath(valuex, confirm); - if (!isPathInAllowedPrefabDirectory(savePath)) { - context.sendMessage(MESSAGE_PATH_OUTSIDE_PREFABS_DIR); - return CompletableFuture.completedFuture(null); - } + if (readOnlyCount > 0 && !packExplicit) { + if (targetPack == null) { + targetPack = BuilderToolsPlugin.resolveTargetPack("", playerComponent, context); + if (targetPack == null) { + return CompletableFuture.completedFuture(null); } } - context.sendMessage(Message.translation("server.commands.editprefab.save.saveAll.start").param("amount", values.length)); - CompletableFuture[] prefabSavingFutures = new CompletableFuture[values.length]; - - for (int i = 0; i < values.length; i++) { - PrefabEditingMetadata valuexx = values[i]; - Path savePath = getWritableSavePath(valuexx, confirm); - prefabSavingFutures[i] = PrefabSaver.savePrefab( - playerComponent, - world, - savePath, - valuexx.getAnchorPoint(), - valuexx.getMinPoint(), - valuexx.getMaxPoint(), - valuexx.getPastePosition(), - valuexx.getOriginalFileAnchor(), - prefabSaverSettings - ); + if (!confirm) { + context.sendMessage(Message.translation("server.commands.editprefab.save.readOnlyNeedsConfirm").param("count", readOnlyCount)); + return CompletableFuture.completedFuture(null); } - - return CompletableFuture.allOf(prefabSavingFutures) - .thenAccept( - unused -> { - List failedPrefabFutures = new IntArrayList(); - - for (int i1 = 0; i1 < prefabSavingFutures.length; i1++) { - if (prefabSavingFutures[i1].join()) { - values[i1].setDirty(false); - } else { - failedPrefabFutures.add(i1); - } - } - - context.sendMessage( - Message.translation("server.commands.editprefab.save.saveAll.success") - .param("successes", prefabSavingFutures.length - failedPrefabFutures.size()) - .param("failures", failedPrefabFutures.size()) - ); - } - ); } + + if (targetPack != null) { + BuilderToolsUserData.get(playerComponent).setLastSavePack(targetPack.getName()); + } + + if (!SingleplayerModule.isOwner(playerRef)) { + for (PrefabEditingMetadata valuex : values) { + Path savePath = getWritableSavePath(valuex, targetPack); + if (!isPathInAllowedPrefabDirectory(savePath)) { + context.sendMessage(MESSAGE_PATH_OUTSIDE_PREFABS_DIR); + return CompletableFuture.completedFuture(null); + } + } + } + + context.sendMessage(Message.translation("server.commands.editprefab.save.saveAll.start").param("amount", values.length)); + CompletableFuture[] prefabSavingFutures = new CompletableFuture[values.length]; + + for (int i = 0; i < values.length; i++) { + PrefabEditingMetadata valuexx = values[i]; + Path savePath = getWritableSavePath(valuexx, targetPack); + prefabSavingFutures[i] = PrefabSaver.savePrefab( + playerRef, + world, + savePath, + valuexx.getAnchorPoint(), + valuexx.getMinPoint(), + valuexx.getMaxPoint(), + valuexx.getPastePosition(), + valuexx.getOriginalFileAnchor(), + prefabSaverSettings + ); + } + + return CompletableFuture.allOf(prefabSavingFutures) + .thenAccept( + unused -> { + List failedPrefabFutures = new IntArrayList(); + + for (int i1 = 0; i1 < prefabSavingFutures.length; i1++) { + if (prefabSavingFutures[i1].join()) { + values[i1].setDirty(false); + } else { + failedPrefabFutures.add(i1); + } + } + + context.sendMessage( + Message.translation("server.commands.editprefab.save.saveAll.success") + .param("successes", prefabSavingFutures.length - failedPrefabFutures.size()) + .param("failures", failedPrefabFutures.size()) + ); + } + ); } } } @Nonnull - private static Path getWritableSavePath(@Nonnull PrefabEditingMetadata metadata, boolean confirm) { - if (metadata.isReadOnly() && confirm) { - Path originalPath = metadata.getPrefabPath(); - String fileName = originalPath.getFileName().toString(); - Path parent = originalPath.getParent(); - if (parent != null && parent.getFileName() != null) { - String parentName = parent.getFileName().toString(); - return PrefabStore.get().getServerPrefabsPath().resolve(parentName).resolve(fileName); - } else { - return PrefabStore.get().getServerPrefabsPath().resolve(fileName); - } + private static Path getWritableSavePath(@Nonnull PrefabEditingMetadata metadata, @Nullable AssetPack targetPack) { + if (targetPack != null) { + PrefabStore prefabStore = PrefabStore.get(); + Path packPrefabsPath = prefabStore.getAssetPrefabsPathForPack(targetPack); + return packPrefabsPath.resolve(prefabStore.getRelativePrefabPath(metadata.getPrefabPath())); } else { return metadata.getPrefabPath(); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveUICommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveUICommand.java index 750d44a1..d182ee95 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveUICommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSaveUICommand.java @@ -20,6 +20,8 @@ public class PrefabEditSaveUICommand extends AbstractPlayerCommand { private static final Message MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION = Message.translation("server.commands.editprefab.notInEditSession"); @Nonnull private static final Message MESSAGE_COMMANDS_EDIT_PREFAB_NO_PREFABS_LOADED = Message.translation("server.commands.editprefab.save.noPrefabsLoaded"); + @Nonnull + private static final Message MESSAGE_NOT_IN_EDIT_WORLD = Message.translation("server.commands.editprefab.notInEditWorldWarning"); public PrefabEditSaveUICommand() { super("saveui", "server.commands.editprefab.saveui.desc"); @@ -35,6 +37,8 @@ public class PrefabEditSaveUICommand extends AbstractPlayerCommand { context.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_NOT_IN_EDIT_SESSION); } else if (prefabEditSession.getLoadedPrefabMetadata().isEmpty()) { context.sendMessage(MESSAGE_COMMANDS_EDIT_PREFAB_NO_PREFABS_LOADED); + } else if (!prefabEditSessionManager.isInEditWorld(playerRef, store)) { + context.sendMessage(MESSAGE_NOT_IN_EDIT_WORLD); } else { Player playerComponent = store.getComponent(ref, Player.getComponentType()); diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSelectCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSelectCommand.java index 0b588e19..b35c5a81 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSelectCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditSelectCommand.java @@ -7,8 +7,7 @@ import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditingMetadat import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -21,6 +20,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PrefabEditSelectCommand extends AbstractPlayerCommand { @Nonnull @@ -55,15 +56,15 @@ public class PrefabEditSelectCommand extends AbstractPlayerCommand { assert transformComponent != null; - Vector3d playerLocation = transformComponent.getPosition().clone(); - playerLocation.setY(0.0); + Vector3d playerLocation = new Vector3d(transformComponent.getPosition()); + playerLocation.y = 0.0; double distance = 2.147483647E9; for (PrefabEditingMetadata value : prefabEditSession.getLoadedPrefabMetadata().values()) { Vector3d centerPoint = new Vector3d( (value.getMaxPoint().x + value.getMinPoint().x) / 2.0, 0.0, (value.getMaxPoint().z + value.getMinPoint().z) / 2.0 ); - double distanceTo = centerPoint.distanceTo(playerLocation); + double distanceTo = centerPoint.distance(playerLocation); if (distance > distanceTo) { distance = distanceTo; prefabEditingMetadata = value; @@ -106,7 +107,7 @@ public class PrefabEditSelectCommand extends AbstractPlayerCommand { Ref targetEntityRef = TargetUtil.getTargetEntity(ref, componentAccessor); if (targetEntityRef != null && targetEntityRef.isValid()) { TransformComponent entityTransformComponent = componentAccessor.getComponent(targetEntityRef, TransformComponent.getComponentType()); - return entityTransformComponent == null ? null : entityTransformComponent.getPosition().toVector3i(); + return entityTransformComponent == null ? null : Vector3dUtil.toVector3i(entityTransformComponent.getPosition()); } else { return null; } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditUpdateBoxCommand.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditUpdateBoxCommand.java index c1b21bb4..72520a9f 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditUpdateBoxCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/commands/PrefabEditUpdateBoxCommand.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionMan import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditingMetadata; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class PrefabEditUpdateBoxCommand extends AbstractPlayerCommand { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/saving/PrefabSaver.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/saving/PrefabSaver.java index 94980768..6a215bbc 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/saving/PrefabSaver.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/saving/PrefabSaver.java @@ -8,8 +8,6 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.math.vector.VectorBoxUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -28,11 +26,10 @@ import com.hypixel.hytale.server.core.universe.world.chunk.EntityChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ReferenceMap; +import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; import java.nio.file.FileSystems; @@ -47,6 +44,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PrefabSaver { protected static final String EDITOR_BLOCK = "Editor_Block"; @@ -108,7 +107,7 @@ public class PrefabSaver { pastePosition, originalFileAnchor, settings, - (Long2ObjectMap>)loadedChunks, + (Long2ReferenceMap>)loadedChunks, editorBlock, editorBlockPrefabAir, editorBlockPrefabAnchor @@ -130,7 +129,7 @@ public class PrefabSaver { @Nonnull Vector3i pastePosition, @Nonnull Vector3i originalFileAnchor, @Nonnull PrefabSaverSettings settings, - @Nonnull Long2ObjectMap> loadedChunks, + @Nonnull Long2ReferenceMap> loadedChunks, int editorBlock, int editorBlockPrefabAir, int editorBlockPrefabAnchor @@ -184,10 +183,6 @@ public class PrefabSaver { Holder holder = worldChunkComponent.getBlockComponentHolder(x, y, z); if (holder != null) { holder = holder.clone(); - BlockState blockState = BlockState.getBlockState(holder); - if (blockState != null) { - blockState.clearPositionForSerialization(); - } } int supportValue = settings.isClearSupportValues() ? 0 : (blockPhysicsComponent != null ? blockPhysicsComponent.get(x, y, z) : 0); @@ -235,7 +230,7 @@ public class PrefabSaver { TransformComponent transformx = holderx.getComponent(transformType); if (transformx != null && transformx.getPosition() != null) { - transformx.getPosition().subtract(selection.getX(), selection.getY(), selection.getZ()); + transformx.getPosition().sub(selection.getX(), selection.getY(), selection.getZ()); } selection.addEntityHolderRaw(holderx); @@ -270,7 +265,7 @@ public class PrefabSaver { Holder clonedHolder = holder.clone(); TransformComponent clonedTransform = clonedHolder.getComponent(transformType); if (clonedTransform != null && clonedTransform.getPosition() != null) { - clonedTransform.getPosition().subtract(selection.getX(), selection.getY(), selection.getZ()); + clonedTransform.getPosition().sub(selection.getX(), selection.getY(), selection.getZ()); } selection.addEntityHolderRaw(clonedHolder); @@ -293,7 +288,7 @@ public class PrefabSaver { } @Nonnull - private static CompletableFuture>> preloadChunksInSelectionAsync( + private static CompletableFuture>> preloadChunksInSelectionAsync( @Nonnull ChunkStore chunkStore, @Nonnull Vector3i minPoint, @Nonnull Vector3i maxPoint ) { LongSet chunkIndices = new LongOpenHashSet(); @@ -308,7 +303,7 @@ public class PrefabSaver { } } - Long2ObjectMap> loadedChunks = new Long2ObjectOpenHashMap<>(chunkIndices.size()); + Long2ReferenceMap> loadedChunks = new Long2ReferenceOpenHashMap<>(chunkIndices.size()); List> chunkFutures = new ArrayList<>(chunkIndices.size()); for (long chunkIndex : chunkIndices) { diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorExitConfirmPage.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorExitConfirmPage.java index 77bdbe81..f473f6bb 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorExitConfirmPage.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorExitConfirmPage.java @@ -78,17 +78,21 @@ public class PrefabEditorExitConfirmPage extends InteractiveCustomUIPage { + @Nonnull + private final World world; + + public PrefabEditorLoadOptionsPage(@Nonnull PlayerRef playerRef, @Nonnull World world) { + super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, PrefabEditorLoadOptionsPage.PageData.CODEC); + this.world = world; + } + + @Override + public void build( + @Nonnull Ref ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store store + ) { + commandBuilder.append("Pages/PrefabEditorLoadOptions.ui"); + commandBuilder.set("#WarningTitle.TextSpans", Message.translation("server.commands.editprefab.prefabEditorLoadOptions.title")); + commandBuilder.set("#WarningMessage.TextSpans", Message.translation("server.commands.editprefab.prefabEditorLoadOptions.message")); + eventBuilder.addEventBinding( + CustomUIEventBindingType.Activating, + "#LoadExistingSessionButton", + new EventData().append("Action", PrefabEditorLoadOptionsPage.Action.LoadExisting.name()) + ); + eventBuilder.addEventBinding( + CustomUIEventBindingType.Activating, "#CancelButton", new EventData().append("Action", PrefabEditorLoadOptionsPage.Action.Cancel.name()) + ); + eventBuilder.addEventBinding( + CustomUIEventBindingType.Activating, "#CreateNewSessionButton", new EventData().append("Action", PrefabEditorLoadOptionsPage.Action.CreateNew.name()) + ); + } + + public void handleDataEvent(@Nonnull Ref ref, @Nonnull Store store, @Nonnull PrefabEditorLoadOptionsPage.PageData data) { + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + + assert playerComponent != null; + + PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); + switch (data.action) { + case LoadExisting: + playerComponent.getPageManager().setPage(ref, store, Page.None); + prefabEditSessionManager.sendToEditWorld(ref, this.world, this.playerRef); + break; + case Cancel: + playerComponent.getPageManager().setPage(ref, store, Page.None); + break; + case CreateNew: + prefabEditSessionManager.exitEditSession(ref, this.world, this.playerRef, store) + .thenRun(() -> playerComponent.getPageManager().openCustomPage(ref, store, new PrefabEditorLoadSettingsPage(this.playerRef))); + } + } + + public static enum Action { + LoadExisting, + Cancel, + CreateNew; + } + + protected static class PageData { + public static final BuilderCodec CODEC = BuilderCodec.builder( + PrefabEditorLoadOptionsPage.PageData.class, PrefabEditorLoadOptionsPage.PageData::new + ) + .append( + new KeyedCodec<>("Action", new EnumCodec<>(PrefabEditorLoadOptionsPage.Action.class, EnumCodec.EnumStyle.LEGACY)), + (o, action) -> o.action = action, + o -> o.action + ) + .add() + .build(); + public PrefabEditorLoadOptionsPage.Action action; + + public PageData() { + } + } +} diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorLoadSettingsPage.java b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorLoadSettingsPage.java index 194c6f36..7a954543 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorLoadSettingsPage.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefabeditor/ui/PrefabEditorLoadSettingsPage.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.builtin.buildertools.prefabeditor.ui; import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.BuilderToolsUserData; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditSessionManager; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabEditorCreationSettings; import com.hypixel.hytale.builtin.buildertools.prefabeditor.PrefabLoadingState; @@ -33,6 +34,9 @@ import com.hypixel.hytale.server.core.prefab.PrefabStore; import com.hypixel.hytale.server.core.ui.DropdownEntryInfo; import com.hypixel.hytale.server.core.ui.LocalizableString; import com.hypixel.hytale.server.core.ui.Value; +import com.hypixel.hytale.server.core.ui.browser.AssetPackSaveBrowser; +import com.hypixel.hytale.server.core.ui.browser.AssetPackSaveBrowserConfig; +import com.hypixel.hytale.server.core.ui.browser.AssetPackSaveBrowserEventData; import com.hypixel.hytale.server.core.ui.browser.FileListProvider; import com.hypixel.hytale.server.core.ui.builder.EventData; import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder; @@ -55,6 +59,7 @@ public class PrefabEditorLoadSettingsPage extends InteractiveCustomUIPage BUTTON_HIGHLIGHTED = Value.ref("Pages/BasicTextButton.ui", "SelectedLabelStyle"); private static final String ASSETS_ROOT_KEY = "Assets"; + private final AssetPackSaveBrowser packBrowser = new AssetPackSaveBrowser(AssetPackSaveBrowserConfig.defaults()); private final List savedConfigsDropdown = new ObjectArrayList<>(); private volatile boolean isLoading; private volatile boolean loadingCancelled; @@ -82,6 +87,11 @@ public class PrefabEditorLoadSettingsPage extends InteractiveCustomUIPage ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store store ) { commandBuilder.append("Pages/PrefabEditorSettings.ui"); + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + if (playerComponent != null) { + this.packBrowser.setSelectedPackKey(BuilderToolsUserData.get(playerComponent).getLastSavePack()); + } + this.savedConfigsDropdown.add(new DropdownEntryInfo(LocalizableString.fromMessageId("server.commands.editprefab.ui.savedConfigs.noneSelected"), "")); for (String assetId : PrefabEditorCreationSettings.getAssetMap().getAssetMap().keySet()) { @@ -221,6 +231,14 @@ public class PrefabEditorLoadSettingsPage extends InteractiveCustomUIPage result = BuilderToolsPlugin.get() - .getPrefabEditSessionManager() - .loadPrefabAndCreateEditSession(ref, playerComponent, data.toCreationSettings(), store, this::onLoadingProgress); - if (result == null) { - this.onLoadingFailed(Message.translation("server.commands.editprefab.error.failedToStart")); - return; - } + if (packResult.packConfirmed() && this.packBrowser.hasSelectedPack()) { + BuilderToolsUserData.get(playerComponent).setLastSavePack(this.packBrowser.getSelectedPack().getName()); + } - result.whenComplete((unused, throwable) -> { - if (!this.loadingCancelled) { - if (throwable != null) { - this.onLoadingFailed(Message.raw(throwable.getMessage() != null ? throwable.getMessage() : "Unknown error")); - } else if (this.currentLoadingState != null && this.currentLoadingState.hasErrors()) { - this.onLoadingFailed(this.currentLoadingState.getStatusMessage()); - } else { - this.isLoading = false; - this.loadingWorldName = null; - this.currentLoadingState = null; - playerComponent.getPageManager().setPage(ref, store, Page.ContentCreation); - } + this.sendUpdate(packResult.commandBuilder(), packResult.eventBuilder(), false); + } else { + switch (data.uiAction) { + case Load: + if (this.isLoading || this.isShuttingDown) { + return; } - }); - break; - case OpenSavePropertiesDialog: { - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#MainPage.Visible", false); - commandBuilder.set("#SaveConfigPage.Visible", true); - this.sendUpdate(commandBuilder); - break; - } - case CancelSavePropertiesDialog: { - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#MainPage.Visible", true); - commandBuilder.set("#SaveConfigPage.Visible", false); - this.sendUpdate(commandBuilder); - break; - } - case SavePropertiesConfig: - PrefabEditorCreationSettings.save(data.configName, data.toCreationSettings()).thenRun(() -> { - UICommandBuilder builderx = new UICommandBuilder(); - builderx.set("#MainPage.Visible", true); - builderx.set("#SaveConfigPage.Visible", false); - builderx.set("#SaveConfigPage #Buttons.Visible", true); - builderx.set("#SaveConfigPage #SaveName #Input.Value", ""); - this.savedConfigsDropdown.add(new DropdownEntryInfo(LocalizableString.fromString(data.configName), data.configName)); - builderx.set("#SavedConfigs #Input.Entries", this.savedConfigsDropdown); - builderx.set("#SavedConfigs #Input.Value", data.configName); - this.sendUpdate(builderx); - }); - break; - case ApplySavedProperties: - if (data.configName == null || data.configName.isBlank()) { - UICommandBuilder builderx = new UICommandBuilder(); - builderx.set("#MainPage #RootDir #Input.Value", PrefabEditLoadCommand.DEFAULT_PREFAB_ROOT_DIRECTORY.name()); - builderx.set("#MainPage #PrefabPaths #Input.Value", ""); - builderx.set("#MainPage #Recursive #CheckBox.Value", false); - builderx.set("#MainPage #Children #CheckBox.Value", false); - builderx.set("#MainPage #Entities #CheckBox.Value", false); - builderx.set("#MainPage #EnableWorldTicking #CheckBox.Value", false); - builderx.set("#MainPage #DesiredYLevel #Input.Value", 55); - builderx.set("#MainPage #BlocksBetweenPrefabs #Input.Value", 15); - builderx.set("#MainPage #WorldGenType #Input.Value", PrefabEditLoadCommand.DEFAULT_WORLD_GEN_TYPE.name()); - builderx.set("#MainPage #Environment #Input.Value", "Env_Zone1_Plains"); - builderx.set("#MainPage #GrassTint #Input.Color", "#5B9E28"); - builderx.set("#MainPage #NumAirBeforeGround #Input.Value", 0); - builderx.set("#MainPage #PasteAxis #Input.Value", PrefabEditLoadCommand.DEFAULT_PREFAB_STACKING_AXIS.name()); - builderx.set("#MainPage #AlignmentMethod #Input.Value", PrefabEditLoadCommand.DEFAULT_PREFAB_ALIGNMENT.name()); - builderx.set("#MainPage #RowSplitMode #Input.Value", PrefabEditLoadCommand.DEFAULT_ROW_SPLIT_MODE.name()); - this.sendUpdate(builderx); - return; - } - PrefabEditorCreationSettings.load(data.configName).thenAccept(settings -> { - if (settings != null) { + this.isLoading = true; + this.loadingCancelled = false; + this.currentLoadingState = new PrefabLoadingState(); + this.loadingWorldName = "prefabEditor-" + playerRefComponent.getUsername(); + UICommandBuilder showLoadingBuilder = new UICommandBuilder(); + showLoadingBuilder.set("#MainPage.Visible", false); + showLoadingBuilder.set("#SaveConfigPage.Visible", false); + showLoadingBuilder.set("#LoadingPage.Visible", true); + showLoadingBuilder.set("#LoadingPage #ProgressBar.Value", 0.0F); + showLoadingBuilder.set("#LoadingPage #StatusText.TextSpans", Message.translation("server.commands.editprefab.loading.phase.initializing")); + showLoadingBuilder.set("#LoadingPage #ErrorText.Visible", false); + showLoadingBuilder.set("#LoadingPage #CancelButton.Visible", true); + this.sendUpdate(showLoadingBuilder); + this.playerRef.sendMessage(Message.translation("server.commands.editprefab.loading")); + CompletableFuture result = BuilderToolsPlugin.get() + .getPrefabEditSessionManager() + .loadPrefabAndCreateEditSession(ref, playerComponent, data.toCreationSettings(), store, this::onLoadingProgress); + if (result == null) { + this.onLoadingFailed(Message.translation("server.commands.editprefab.error.failedToStart")); + return; + } + + result.whenComplete((unused, throwable) -> { + if (!this.loadingCancelled) { + if (throwable != null) { + this.onLoadingFailed(Message.raw(throwable.getMessage() != null ? throwable.getMessage() : "Unknown error")); + } else if (this.currentLoadingState != null && this.currentLoadingState.hasErrors()) { + this.onLoadingFailed(this.currentLoadingState.getStatusMessage()); + } else { + this.isLoading = false; + this.loadingWorldName = null; + this.currentLoadingState = null; + playerComponent.getPageManager().setPage(ref, store, Page.ContentCreation); + } + } + }); + break; + case OpenSavePropertiesDialog: { + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#MainPage.Visible", false); + commandBuilder.set("#SaveConfigPage.Visible", true); + this.sendUpdate(commandBuilder); + break; + } + case CancelSavePropertiesDialog: { + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#MainPage.Visible", true); + commandBuilder.set("#SaveConfigPage.Visible", false); + this.sendUpdate(commandBuilder); + break; + } + case SavePropertiesConfig: + AssetPack targetPack = this.packBrowser.getSelectedPack(); + if (targetPack == null) { + this.playerRef.sendMessage(Message.translation("server.customUI.assetPackBrowser.packRequired")); + return; + } + + BuilderToolsUserData.get(playerComponent).setLastSavePack(targetPack.getName()); + CompletableFuture saveFuture = PrefabEditorCreationSettings.save(data.configName, data.toCreationSettings(), targetPack); + saveFuture.thenRun(() -> { UICommandBuilder builderx = new UICommandBuilder(); - builderx.set("#MainPage #RootDir #Input.Value", settings.getPrefabRootDirectory().name()); - builderx.set("#MainPage #PrefabPaths #Input.Value", String.join(",", settings.getUnprocessedPrefabPaths())); - builderx.set("#MainPage #Recursive #CheckBox.Value", settings.isRecursive()); - builderx.set("#MainPage #Children #CheckBox.Value", settings.isLoadChildren()); - builderx.set("#MainPage #Entities #CheckBox.Value", settings.shouldLoadEntities()); - builderx.set("#MainPage #EnableWorldTicking #CheckBox.Value", settings.isWorldTickingEnabled()); - builderx.set("#MainPage #DesiredYLevel #Input.Value", settings.getPasteYLevelGoal()); - builderx.set("#MainPage #BlocksBetweenPrefabs #Input.Value", settings.getBlocksBetweenEachPrefab()); - builderx.set("#MainPage #WorldGenType #Input.Value", settings.getWorldGenType().name()); - builderx.set("#MainPage #Environment #Input.Value", settings.getEnvironment()); - builderx.set("#MainPage #GrassTint #Input.Color", settings.getGrassTint()); - builderx.set("#MainPage #NumAirBeforeGround #Input.Value", settings.getBlocksAboveSurface()); - builderx.set("#MainPage #PasteAxis #Input.Value", settings.getStackingAxis().name()); - builderx.set("#MainPage #AlignmentMethod #Input.Value", settings.getAlignment().name()); - builderx.set("#MainPage #RowSplitMode #Input.Value", settings.getRowSplitMode().name()); + builderx.set("#MainPage.Visible", true); + builderx.set("#SaveConfigPage.Visible", false); + builderx.set("#SaveConfigPage #Buttons.Visible", true); + builderx.set("#SaveConfigPage #SaveName #Input.Value", ""); + this.savedConfigsDropdown.add(new DropdownEntryInfo(LocalizableString.fromString(data.configName), data.configName)); + builderx.set("#SavedConfigs #Input.Entries", this.savedConfigsDropdown); + builderx.set("#SavedConfigs #Input.Value", data.configName); + this.sendUpdate(builderx); + }); + break; + case ApplySavedProperties: + if (data.configName == null || data.configName.isBlank()) { + UICommandBuilder builderx = new UICommandBuilder(); + builderx.set("#MainPage #RootDir #Input.Value", PrefabEditLoadCommand.DEFAULT_PREFAB_ROOT_DIRECTORY.name()); + builderx.set("#MainPage #PrefabPaths #Input.Value", ""); + builderx.set("#MainPage #Recursive #CheckBox.Value", false); + builderx.set("#MainPage #Children #CheckBox.Value", false); + builderx.set("#MainPage #Entities #CheckBox.Value", false); + builderx.set("#MainPage #EnableWorldTicking #CheckBox.Value", false); + builderx.set("#MainPage #DesiredYLevel #Input.Value", 55); + builderx.set("#MainPage #BlocksBetweenPrefabs #Input.Value", 15); + builderx.set("#MainPage #WorldGenType #Input.Value", PrefabEditLoadCommand.DEFAULT_WORLD_GEN_TYPE.name()); + builderx.set("#MainPage #Environment #Input.Value", "Env_Zone1_Plains"); + builderx.set("#MainPage #GrassTint #Input.Color", "#5B9E28"); + builderx.set("#MainPage #NumAirBeforeGround #Input.Value", 0); + builderx.set("#MainPage #PasteAxis #Input.Value", PrefabEditLoadCommand.DEFAULT_PREFAB_STACKING_AXIS.name()); + builderx.set("#MainPage #AlignmentMethod #Input.Value", PrefabEditLoadCommand.DEFAULT_PREFAB_ALIGNMENT.name()); + builderx.set("#MainPage #RowSplitMode #Input.Value", PrefabEditLoadCommand.DEFAULT_ROW_SPLIT_MODE.name()); + this.sendUpdate(builderx); + return; + } + + PrefabEditorCreationSettings.load(data.configName).thenAccept(settings -> { + if (settings != null) { + UICommandBuilder builderx = new UICommandBuilder(); + builderx.set("#MainPage #RootDir #Input.Value", settings.getPrefabRootDirectory().name()); + builderx.set("#MainPage #PrefabPaths #Input.Value", String.join(",", settings.getUnprocessedPrefabPaths())); + builderx.set("#MainPage #Recursive #CheckBox.Value", settings.isRecursive()); + builderx.set("#MainPage #Children #CheckBox.Value", settings.isLoadChildren()); + builderx.set("#MainPage #Entities #CheckBox.Value", settings.shouldLoadEntities()); + builderx.set("#MainPage #EnableWorldTicking #CheckBox.Value", settings.isWorldTickingEnabled()); + builderx.set("#MainPage #DesiredYLevel #Input.Value", settings.getPasteYLevelGoal()); + builderx.set("#MainPage #BlocksBetweenPrefabs #Input.Value", settings.getBlocksBetweenEachPrefab()); + builderx.set("#MainPage #WorldGenType #Input.Value", settings.getWorldGenType().name()); + builderx.set("#MainPage #Environment #Input.Value", settings.getEnvironment()); + builderx.set("#MainPage #GrassTint #Input.Color", settings.getGrassTint()); + builderx.set("#MainPage #NumAirBeforeGround #Input.Value", settings.getBlocksAboveSurface()); + builderx.set("#MainPage #PasteAxis #Input.Value", settings.getStackingAxis().name()); + builderx.set("#MainPage #AlignmentMethod #Input.Value", settings.getAlignment().name()); + builderx.set("#MainPage #RowSplitMode #Input.Value", settings.getRowSplitMode().name()); + this.sendUpdate(builderx); + } + }); + break; + case Cancel: + playerComponent.getPageManager().setPage(ref, store, Page.None); + break; + case CancelLoading: + if (this.isShuttingDown) { + return; + } + + this.loadingCancelled = true; + this.isLoading = false; + this.isShuttingDown = true; + UICommandBuilder cancellingBuilder = new UICommandBuilder(); + cancellingBuilder.set("#LoadingPage #CancelButton.Disabled", true); + cancellingBuilder.set("#LoadingPage #StatusText.TextSpans", Message.translation("server.commands.editprefab.loading.phase.cancelling")); + cancellingBuilder.set("#LoadingPage #ProgressBar.Value", 0.1F); + cancellingBuilder.set("#LoadingPage #ErrorText.Visible", false); + this.sendUpdate(cancellingBuilder); + PrefabEditSessionManager sessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); + if (this.loadingWorldName != null) { + String worldNameToClean = this.loadingWorldName; + this.loadingWorldName = null; + sessionManager.cleanupCancelledSession(this.playerRef.getUuid(), worldNameToClean, this::onShutdownProgress) + .whenComplete((unused, throwable) -> { + this.isShuttingDown = false; + this.currentLoadingState = null; + UICommandBuilder builderx = new UICommandBuilder(); + builderx.set("#LoadingPage.Visible", false); + builderx.set("#LoadingPage #CancelButton.Disabled", false); + builderx.set("#MainPage.Visible", true); + this.sendUpdate(builderx); + if (throwable != null) { + this.playerRef.sendMessage(Message.translation("server.commands.editprefab.error.shutdownFailed")); + } + }); + } else { + this.isShuttingDown = false; + this.currentLoadingState = null; + UICommandBuilder builderx = new UICommandBuilder(); + builderx.set("#LoadingPage.Visible", false); + builderx.set("#MainPage.Visible", true); this.sendUpdate(builderx); } - }); - break; - case Cancel: - playerComponent.getPageManager().setPage(ref, store, Page.None); - break; - case CancelLoading: - if (this.isShuttingDown) { - return; - } - - this.loadingCancelled = true; - this.isLoading = false; - this.isShuttingDown = true; - UICommandBuilder cancellingBuilder = new UICommandBuilder(); - cancellingBuilder.set("#LoadingPage #CancelButton.Disabled", true); - cancellingBuilder.set("#LoadingPage #StatusText.TextSpans", Message.translation("server.commands.editprefab.loading.phase.cancelling")); - cancellingBuilder.set("#LoadingPage #ProgressBar.Value", 0.1F); - cancellingBuilder.set("#LoadingPage #ErrorText.Visible", false); - this.sendUpdate(cancellingBuilder); - PrefabEditSessionManager sessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); - if (this.loadingWorldName != null) { - String worldNameToClean = this.loadingWorldName; - this.loadingWorldName = null; - sessionManager.cleanupCancelledSession(this.playerRef.getUuid(), worldNameToClean, this::onShutdownProgress) - .whenComplete((unused, throwable) -> { - this.isShuttingDown = false; - this.currentLoadingState = null; - UICommandBuilder builderx = new UICommandBuilder(); - builderx.set("#LoadingPage.Visible", false); - builderx.set("#LoadingPage #CancelButton.Disabled", false); - builderx.set("#MainPage.Visible", true); - this.sendUpdate(builderx); - if (throwable != null) { - this.playerRef.sendMessage(Message.translation("server.commands.editprefab.error.shutdownFailed")); - } - }); - } else { - this.isShuttingDown = false; - this.currentLoadingState = null; - UICommandBuilder builderx = new UICommandBuilder(); - builderx.set("#LoadingPage.Visible", false); - builderx.set("#MainPage.Visible", true); - this.sendUpdate(builderx); - } - break; - case SavePropertiesNameChanged: - UICommandBuilder builder = new UICommandBuilder(); - builder.set("#SaveConfigPage #Buttons #SavePropertiesButton.Disabled", data.configName.isBlank()); - this.sendUpdate(builder); - break; - case OpenBrowser: { - this.inAssetsRoot = true; - this.assetsCurrentDir = Paths.get(""); - this.browserRoot = Paths.get("Assets"); - this.browserCurrent = Paths.get(""); - this.selectedPath = null; - this.browserSearchQuery = ""; - this.selectedItems.clear(); - UICommandBuilder commandBuilder = new UICommandBuilder(); - UIEventBuilder eventBuilder = new UIEventBuilder(); - commandBuilder.set("#MainPage.Visible", false); - commandBuilder.set("#BrowserPage.Visible", true); - List roots = this.buildBrowserRootEntries(); - commandBuilder.set("#BrowserPage #BrowserContent #RootSelector.Entries", roots); - commandBuilder.set("#BrowserPage #BrowserContent #RootSelector.Value", "Assets"); - commandBuilder.set("#BrowserPage #BrowserContent #SearchInput.Value", ""); - commandBuilder.set("#BrowserPage #SelectedSection #SelectedItems.Value", ""); - this.buildBrowserList(commandBuilder, eventBuilder); - this.sendUpdate(commandBuilder, eventBuilder, false); - break; - } - case BrowserNavigate: - if (data.browserFile == null) { - return; - } - - String fileName = data.browserFile; - if (this.inAssetsRoot) { - this.handleAssetsNavigation(fileName); - } else { - this.handleRegularNavigation(fileName); - } - break; - case BrowserRootChanged: { - if (data.browserRootStr == null) { - return; - } - - if (!this.isAllowedBrowserRoot(data.browserRootStr)) { - return; - } - - this.inAssetsRoot = "Assets".equals(data.browserRootStr); - this.assetsCurrentDir = Paths.get(""); - if (this.inAssetsRoot) { + break; + case SavePropertiesNameChanged: + UICommandBuilder builder = new UICommandBuilder(); + builder.set("#SaveConfigPage #Buttons #SavePropertiesButton.Disabled", data.configName.isBlank()); + this.sendUpdate(builder); + break; + case OpenBrowser: { + this.inAssetsRoot = true; + this.assetsCurrentDir = Paths.get(""); this.browserRoot = Paths.get("Assets"); this.browserCurrent = Paths.get(""); - } else { - this.browserRoot = this.findActualRootPath(data.browserRootStr); - if (this.browserRoot == null) { - this.browserRoot = Path.of(data.browserRootStr); + this.selectedPath = null; + this.browserSearchQuery = ""; + this.selectedItems.clear(); + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + commandBuilder.set("#MainPage.Visible", false); + commandBuilder.set("#BrowserPage.Visible", true); + List roots = this.buildBrowserRootEntries(); + commandBuilder.set("#BrowserPage #BrowserContent #RootSelector.Entries", roots); + commandBuilder.set("#BrowserPage #BrowserContent #RootSelector.Value", "Assets"); + commandBuilder.set("#BrowserPage #BrowserContent #SearchInput.Value", ""); + commandBuilder.set("#BrowserPage #SelectedSection #SelectedItems.Value", ""); + this.buildBrowserList(commandBuilder, eventBuilder); + this.sendUpdate(commandBuilder, eventBuilder, false); + break; + } + case BrowserNavigate: + if (data.browserFile == null) { + return; } - this.browserCurrent = this.browserRoot.getFileSystem().getPath(""); - } + String fileName = data.browserFile; + if (this.inAssetsRoot) { + this.handleAssetsNavigation(fileName); + } else { + this.handleRegularNavigation(fileName); + } + break; + case BrowserRootChanged: { + if (data.browserRootStr == null) { + return; + } - this.selectedPath = null; - this.browserSearchQuery = ""; - this.selectedItems.clear(); - UICommandBuilder commandBuilder = new UICommandBuilder(); - UIEventBuilder eventBuilder = new UIEventBuilder(); - commandBuilder.set("#BrowserPage #BrowserContent #SearchInput.Value", ""); - commandBuilder.set("#BrowserPage #SelectedSection #SelectedItems.Value", ""); - PrefabRootDirectory rootDirValue = this.getRootDirectoryForPath(data.browserRootStr); - if (rootDirValue != null) { - commandBuilder.set("#MainPage #RootDir #Input.Value", rootDirValue.name()); - } + if (!this.isAllowedBrowserRoot(data.browserRootStr)) { + return; + } - this.buildBrowserList(commandBuilder, eventBuilder); - this.sendUpdate(commandBuilder, eventBuilder, false); - break; - } - case BrowserSearch: { - this.browserSearchQuery = data.browserSearchStr != null ? data.browserSearchStr.trim().toLowerCase() : ""; - UICommandBuilder commandBuilder = new UICommandBuilder(); - UIEventBuilder eventBuilder = new UIEventBuilder(); - this.buildBrowserList(commandBuilder, eventBuilder); - this.sendUpdate(commandBuilder, eventBuilder, false); - break; - } - case AddFolderToList: { - String pathToAdd = this.getCurrentBrowserPath(); - if (!pathToAdd.isEmpty() && !this.selectedItems.contains(pathToAdd)) { - this.selectedItems.add(pathToAdd); - } + this.inAssetsRoot = "Assets".equals(data.browserRootStr); + this.assetsCurrentDir = Paths.get(""); + if (this.inAssetsRoot) { + this.browserRoot = Paths.get("Assets"); + this.browserCurrent = Paths.get(""); + } else { + this.browserRoot = this.findActualRootPath(data.browserRootStr); + if (this.browserRoot == null) { + this.browserRoot = Path.of(data.browserRootStr); + } - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#BrowserPage #SelectedSection #SelectedItems.Value", String.join("\n", this.selectedItems)); - this.sendUpdate(commandBuilder); - break; - } - case ConfirmBrowser: { - String pathsToSet; - if (!this.selectedItems.isEmpty()) { - pathsToSet = String.join(",", this.selectedItems); - } else { - pathsToSet = this.getCurrentBrowserPath(); - } + this.browserCurrent = this.browserRoot.getFileSystem().getPath(""); + } - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#MainPage #PrefabPaths #Input.Value", pathsToSet); - PrefabRootDirectory rootDirValue = this.inAssetsRoot ? PrefabRootDirectory.ASSET : this.getRootDirectoryForPath(this.browserRoot.toString()); - if (rootDirValue != null) { - commandBuilder.set("#MainPage #RootDir #Input.Value", rootDirValue.name()); - } + this.selectedPath = null; + this.browserSearchQuery = ""; + this.selectedItems.clear(); + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + commandBuilder.set("#BrowserPage #BrowserContent #SearchInput.Value", ""); + commandBuilder.set("#BrowserPage #SelectedSection #SelectedItems.Value", ""); + PrefabRootDirectory rootDirValue = this.getRootDirectoryForPath(data.browserRootStr); + if (rootDirValue != null) { + commandBuilder.set("#MainPage #RootDir #Input.Value", rootDirValue.name()); + } - commandBuilder.set("#BrowserPage.Visible", false); - commandBuilder.set("#MainPage.Visible", true); - this.sendUpdate(commandBuilder); - break; - } - case CancelBrowser: { - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#BrowserPage.Visible", false); - commandBuilder.set("#MainPage.Visible", true); - this.sendUpdate(commandBuilder); + this.buildBrowserList(commandBuilder, eventBuilder); + this.sendUpdate(commandBuilder, eventBuilder, false); + break; + } + case BrowserSearch: { + this.browserSearchQuery = data.browserSearchStr != null ? data.browserSearchStr.trim().toLowerCase() : ""; + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + this.buildBrowserList(commandBuilder, eventBuilder); + this.sendUpdate(commandBuilder, eventBuilder, false); + break; + } + case AddFolderToList: { + String pathToAdd = this.getCurrentBrowserPath(); + if (!pathToAdd.isEmpty() && !this.selectedItems.contains(pathToAdd)) { + this.selectedItems.add(pathToAdd); + } + + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#BrowserPage #SelectedSection #SelectedItems.Value", String.join("\n", this.selectedItems)); + this.sendUpdate(commandBuilder); + break; + } + case ConfirmBrowser: { + String pathsToSet; + if (!this.selectedItems.isEmpty()) { + pathsToSet = String.join(",", this.selectedItems); + } else { + pathsToSet = this.getCurrentBrowserPath(); + } + + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#MainPage #PrefabPaths #Input.Value", pathsToSet); + PrefabRootDirectory rootDirValue = this.inAssetsRoot ? PrefabRootDirectory.ASSET : this.getRootDirectoryForPath(this.browserRoot.toString()); + if (rootDirValue != null) { + commandBuilder.set("#MainPage #RootDir #Input.Value", rootDirValue.name()); + } + + commandBuilder.set("#BrowserPage.Visible", false); + commandBuilder.set("#MainPage.Visible", true); + this.sendUpdate(commandBuilder); + break; + } + case CancelBrowser: { + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#BrowserPage.Visible", false); + commandBuilder.set("#MainPage.Visible", true); + this.sendUpdate(commandBuilder); + } + case OpenPackBrowser: + case ConfirmPackBrowser: + case CancelPackBrowser: + case OpenCreatePack: + case CreatePack: + case CancelCreatePack: + case PackSearch: + case PackSelect: } } } @@ -871,7 +919,15 @@ public class PrefabEditorLoadSettingsPage extends InteractiveCustomUIPage("@BrowserSearch", Codec.STRING), (o, browserSearchStr) -> o.browserSearchStr = browserSearchStr, o -> o.browserSearchStr) .add() + .append(new KeyedCodec<>("Pack", Codec.STRING), (o, s) -> o.packBrowserData.pack = s, o -> o.packBrowserData.pack) + .add() + .append(new KeyedCodec<>("@PackSearch", Codec.STRING), (o, s) -> o.packBrowserData.search = s, o -> o.packBrowserData.search) + .add() + .append(new KeyedCodec<>("@CreateName", Codec.STRING), (o, s) -> o.packBrowserData.createName = s, o -> o.packBrowserData.createName) + .add() + .append(new KeyedCodec<>("@CreateGroup", Codec.STRING), (o, s) -> o.packBrowserData.createGroup = s, o -> o.packBrowserData.createGroup) + .add() + .append( + new KeyedCodec<>("@CreateDescription", Codec.STRING), (o, s) -> o.packBrowserData.createDescription = s, o -> o.packBrowserData.createDescription + ) + .add() + .append(new KeyedCodec<>("@CreateVersion", Codec.STRING), (o, s) -> o.packBrowserData.createVersion = s, o -> o.packBrowserData.createVersion) + .add() + .append(new KeyedCodec<>("@CreateWebsite", Codec.STRING), (o, s) -> o.packBrowserData.createWebsite = s, o -> o.packBrowserData.createWebsite) + .add() + .append(new KeyedCodec<>("@CreateAuthorName", Codec.STRING), (o, s) -> o.packBrowserData.createAuthorName = s, o -> o.packBrowserData.createAuthorName) + .add() + .append(new KeyedCodec<>("ValidateCreate", Codec.STRING), (o, s) -> o.packBrowserData.validateCreate = s, o -> o.packBrowserData.validateCreate) + .add() .build(); public String configName; public PrefabEditorLoadSettingsPage.Action uiAction; @@ -998,6 +1074,7 @@ public class PrefabEditorLoadSettingsPage extends InteractiveCustomUIPage BUTTON_SELECTED = Value.ref("Pages/BasicTextButton.ui", "SelectedLabelStyle"); @Nonnull private final PrefabEditSession prefabEditSession; + private final AssetPackSaveBrowser packBrowser = new AssetPackSaveBrowser(AssetPackSaveBrowserConfig.defaults()); + private final boolean exitOnSave; private volatile boolean isSaving = false; private volatile long lastProgressUpdateTime = 0L; private static final long PROGRESS_UPDATE_INTERVAL_MS = 100L; @@ -56,8 +67,13 @@ public class PrefabEditorSaveSettingsPage extends InteractiveCustomUIPage selectedPrefabUuids = new HashSet<>(); public PrefabEditorSaveSettingsPage(@Nonnull PlayerRef playerRef, @Nonnull PrefabEditSession prefabEditSession) { + this(playerRef, prefabEditSession, false); + } + + public PrefabEditorSaveSettingsPage(@Nonnull PlayerRef playerRef, @Nonnull PrefabEditSession prefabEditSession, boolean exitOnSave) { super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, PrefabEditorSaveSettingsPage.PageData.CODEC); this.prefabEditSession = prefabEditSession; + this.exitOnSave = exitOnSave; } @Override @@ -65,17 +81,50 @@ public class PrefabEditorSaveSettingsPage extends InteractiveCustomUIPage ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store store ) { commandBuilder.append("Pages/PrefabEditorSaveSettings.ui"); + if (this.exitOnSave) { + commandBuilder.set("#MainPage #TitleLabel.Text", Message.translation("server.customUI.prefabEditorSaveSettings.titleSaveAndExit")); + commandBuilder.set("#MainPage #SaveButton.Text", Message.translation("server.customUI.prefabEditorSaveSettings.saveAndExit")); + } + + Player playerComponent = store.getComponent(ref, Player.getComponentType()); PrefabEditingMetadata selectedPrefab = this.prefabEditSession.getSelectedPrefab(this.playerRef.getUuid()); if (selectedPrefab != null) { - String prefabPath = selectedPrefab.getPrefabPath().toString().replace('\\', '/'); + String prefabPath = this.toDisplayPath(selectedPrefab); commandBuilder.set("#MainPage #PrefabsToSave #Input.Value", prefabPath); this.selectedPrefabUuids.add(selectedPrefab.getUuid()); } + if (playerComponent != null) { + String defaultPackKey = null; + if (selectedPrefab != null) { + AssetPack sourcePack = PrefabStore.get().findAssetPackForPrefabPath(selectedPrefab.getPrefabPath()); + if (sourcePack != null) { + if (!sourcePack.isImmutable()) { + defaultPackKey = sourcePack.getName(); + } + } else if (!selectedPrefab.isReadOnly()) { + defaultPackKey = BuilderToolsUserData.get(playerComponent).getLastSavePack(); + } + } else { + defaultPackKey = BuilderToolsUserData.get(playerComponent).getLastSavePack(); + } + + this.packBrowser.setSelectedPackKey(defaultPackKey); + if (defaultPackKey != null && !this.packBrowser.hasSelectedPack()) { + this.playerRef.sendMessage(Message.translation("server.customUI.assetPackBrowser.packNoLongerAvailable")); + } + } + commandBuilder.set("#MainPage #Entities #CheckBox.Value", true); commandBuilder.set("#MainPage #Empty #CheckBox.Value", false); commandBuilder.set("#MainPage #Overwrite #CheckBox.Value", true); commandBuilder.set("#MainPage #ClearSupport #CheckBox.Value", false); + if (this.packBrowser.hasSelectedPack()) { + commandBuilder.set("#MainPage #SelectedPackLabel.Text", this.packBrowser.getSelectedPackDisplayName()); + } + + commandBuilder.set("#PackBrowserPage.Visible", false); + commandBuilder.set("#CreatePackPage.Visible", false); commandBuilder.set("#SavingPage.Visible", false); commandBuilder.set("#SavingPage #ProgressBar.Value", 0.0F); commandBuilder.set("#SavingPage #StatusText.TextSpans", Message.translation("server.commands.editprefab.save.saving")); @@ -89,7 +138,8 @@ public class PrefabEditorSaveSettingsPage extends InteractiveCustomUIPage prefabsToSave = new ObjectArrayList<>(); + AssetPackSaveBrowser.ActionResult packResult = this.packBrowser + .handleAction(data.action != null ? data.action.name() : null, data.packBrowserData, "#MainPage #SelectedPackLabel"); + if (packResult != null) { + if (packResult.errorKey() != null) { + this.playerRef.sendMessage(Message.translation(packResult.errorKey())); + } - for (String pathStr : prefabPaths) { - pathStr = pathStr.trim(); - if (!pathStr.isEmpty()) { - for (PrefabEditingMetadata metadatax : this.prefabEditSession.getLoadedPrefabMetadata().values()) { - String prefabPath = metadatax.getPrefabPath().toString().replace('\\', '/'); - if (prefabPath.equals(pathStr) || prefabPath.endsWith(pathStr)) { - prefabsToSave.add(metadatax); - break; + if (packResult.packConfirmed() && this.packBrowser.hasSelectedPack()) { + BuilderToolsUserData.get(playerComponent).setLastSavePack(this.packBrowser.getSelectedPack().getName()); + } + + this.sendUpdate(packResult.commandBuilder(), packResult.eventBuilder(), false); + } else { + switch (data.action) { + case Save: + if (this.isSaving) { + return; + } + + if (this.packBrowser.getSelectedPack() == null) { + this.playerRef.sendMessage(Message.translation("server.customUI.assetPackBrowser.packRequired")); + return; + } + + if (!AssetModule.get().validatePackExistsOnDisk(this.packBrowser.getSelectedPack())) { + this.playerRef.sendMessage(Message.translation("server.customUI.assetPackBrowser.packRequired")); + return; + } + + if (playerComponent != null) { + BuilderToolsUserData.get(playerComponent).setLastSavePack(this.packBrowser.getSelectedPack().getName()); + } + + String prefabsToSaveStr = data.prefabsToSave; + if (prefabsToSaveStr == null || prefabsToSaveStr.isBlank()) { + this.playerRef.sendMessage(Message.translation("server.commands.editprefab.save.noPrefabsSelected")); + return; + } + + this.isSaving = true; + UICommandBuilder showSavingBuilder = new UICommandBuilder(); + showSavingBuilder.set("#MainPage.Visible", false); + showSavingBuilder.set("#BrowserPage.Visible", false); + showSavingBuilder.set("#SavingPage.Visible", true); + showSavingBuilder.set("#SavingPage #ProgressBar.Value", 0.0F); + showSavingBuilder.set("#SavingPage #StatusText.TextSpans", Message.translation("server.commands.editprefab.save.saving")); + showSavingBuilder.set("#SavingPage #ErrorText.Visible", false); + showSavingBuilder.set("#SavingPage #BackButton.Visible", false); + this.sendUpdate(showSavingBuilder); + PrefabSaverSettings prefabSaverSettings = new PrefabSaverSettings(); + prefabSaverSettings.setBlocks(true); + prefabSaverSettings.setEntities(data.entities); + prefabSaverSettings.setEmpty(data.empty); + prefabSaverSettings.setOverwriteExisting(data.overwrite); + prefabSaverSettings.setClearSupportValues(data.clearSupport); + String[] prefabPaths = prefabsToSaveStr.split("[,\\n]"); + List prefabsToSave = new ObjectArrayList<>(); + + for (String pathStr : prefabPaths) { + pathStr = pathStr.trim(); + if (!pathStr.isEmpty()) { + for (PrefabEditingMetadata metadatax : this.prefabEditSession.getLoadedPrefabMetadata().values()) { + String prefabPath = metadatax.getPrefabPath().toString().replace('\\', '/'); + if (prefabPath.equals(pathStr) || prefabPath.endsWith(pathStr)) { + prefabsToSave.add(metadatax); + break; + } } } } - } - if (prefabsToSave.isEmpty()) { - this.onSavingFailed(Message.translation("server.commands.editprefab.save.noPrefabsFound")); - return; - } - - int readOnlyCount = 0; - - for (PrefabEditingMetadata metadataxx : prefabsToSave) { - if (metadataxx.isReadOnly()) { - readOnlyCount++; + if (prefabsToSave.isEmpty()) { + this.onSavingFailed(Message.translation("server.commands.editprefab.save.noPrefabsFound")); + return; } - } - if (readOnlyCount > 0) { - this.playerRef.sendMessage(Message.translation("server.commands.editprefab.save.readOnlyRedirect").param("count", readOnlyCount)); - } + int readOnlyCount = 0; - World world = store.getExternalData().getWorld(); - int totalPrefabs = prefabsToSave.size(); - CompletableFuture[] saveFutures = new CompletableFuture[totalPrefabs]; - AtomicInteger completedCount = new AtomicInteger(0); - this.lastProgressUpdateTime = 0L; + for (PrefabEditingMetadata metadataxx : prefabsToSave) { + if (metadataxx.isReadOnly()) { + readOnlyCount++; + } + } - for (int i = 0; i < totalPrefabs; i++) { - PrefabEditingMetadata metadataxxx = prefabsToSave.get(i); - Path savePath = this.getWritableSavePath(metadataxxx); - saveFutures[i] = PrefabSaver.savePrefab( - playerComponent, - world, - savePath, - metadataxxx.getAnchorPoint(), - metadataxxx.getMinPoint(), - metadataxxx.getMaxPoint(), - metadataxxx.getPastePosition(), - metadataxxx.getOriginalFileAnchor(), - prefabSaverSettings - ) - .thenApply( - success -> { - int completed = completedCount.incrementAndGet(); - long now = System.currentTimeMillis(); - if (now - this.lastProgressUpdateTime >= 100L || completed == totalPrefabs) { - this.lastProgressUpdateTime = now; - float progress = (float)completed / totalPrefabs; - UICommandBuilder progressBuilder = new UICommandBuilder(); - progressBuilder.set("#SavingPage #ProgressBar.Value", progress); - progressBuilder.set( - "#SavingPage #StatusText.TextSpans", - Message.translation("server.commands.editprefab.save.progress").param("current", completed).param("total", totalPrefabs) - ); - this.sendUpdate(progressBuilder); + if (readOnlyCount > 0) { + this.playerRef.sendMessage(Message.translation("server.commands.editprefab.save.readOnlyRedirect").param("count", readOnlyCount)); + } + + World world = store.getExternalData().getWorld(); + PrefabEditSessionManager editSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager(); + World editSessionWorld = Universe.get().getWorld(editSessionManager.getPrefabEditSession(this.playerRef.getUuid()).getWorldName()); + int totalPrefabs = prefabsToSave.size(); + CompletableFuture[] saveFutures = new CompletableFuture[totalPrefabs]; + AtomicInteger completedCount = new AtomicInteger(0); + this.lastProgressUpdateTime = 0L; + + for (int i = 0; i < totalPrefabs; i++) { + PrefabEditingMetadata metadataxxx = prefabsToSave.get(i); + Path savePath = this.getWritableSavePath(metadataxxx); + saveFutures[i] = PrefabSaver.savePrefab( + playerRefComponent, + editSessionWorld, + savePath, + metadataxxx.getAnchorPoint(), + metadataxxx.getMinPoint(), + metadataxxx.getMaxPoint(), + metadataxxx.getPastePosition(), + metadataxxx.getOriginalFileAnchor(), + prefabSaverSettings + ) + .thenApply( + success -> { + int completed = completedCount.incrementAndGet(); + long now = System.currentTimeMillis(); + if (now - this.lastProgressUpdateTime >= 100L || completed == totalPrefabs) { + this.lastProgressUpdateTime = now; + float progress = (float)completed / totalPrefabs; + UICommandBuilder progressBuilder = new UICommandBuilder(); + progressBuilder.set("#SavingPage #ProgressBar.Value", progress); + progressBuilder.set( + "#SavingPage #StatusText.TextSpans", + Message.translation("server.commands.editprefab.save.progress").param("current", completed).param("total", totalPrefabs) + ); + this.sendUpdate(progressBuilder); + } + + return (Boolean)success; + } + ); + } + + CompletableFuture.allOf(saveFutures) + .thenAccept( + unused -> { + int successes = 0; + int failures = 0; + + for (CompletableFuture future : saveFutures) { + if (future.join()) { + successes++; + } else { + failures++; + } } - return (Boolean)success; - } - ); - } - - CompletableFuture.allOf(saveFutures) - .thenAccept( - unused -> { - int successes = 0; - int failures = 0; - - for (CompletableFuture future : saveFutures) { - if (future.join()) { - successes++; + this.isSaving = false; + if (failures == 0) { + this.playerRef + .sendMessage( + Message.translation("server.commands.editprefab.save.saveAll.success") + .param("successes", successes) + .param("failures", failures) + ); + playerComponent.getPageManager().setPage(ref, store, Page.None); + if (this.exitOnSave) { + editSessionManager.exitEditSession(ref, world, this.playerRef, store); + } } else { - failures++; - } - } - - this.isSaving = false; - if (failures == 0) { - this.playerRef - .sendMessage( + this.onSavingFailed( Message.translation("server.commands.editprefab.save.saveAll.success").param("successes", successes).param("failures", failures) ); - playerComponent.getPageManager().setPage(ref, store, Page.None); - } else { - this.onSavingFailed( - Message.translation("server.commands.editprefab.save.saveAll.success").param("successes", successes).param("failures", failures) - ); + } } - } - ) - .exceptionally(throwable -> { - this.isSaving = false; - LOGGER.atWarning().withCause(throwable).log("Error saving prefabs"); - this.onSavingFailed(Message.raw(throwable.getMessage() != null ? throwable.getMessage() : "Unknown error")); - return null; - }); - break; - case Cancel: - playerComponent.getPageManager().setPage(ref, store, Page.None); - break; - case SelectAll: { - String allPaths = this.prefabEditSession - .getLoadedPrefabMetadata() - .values() - .stream() - .map(m -> m.getPrefabPath().toString().replace('\\', '/')) - .collect(Collectors.joining(",")); - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#MainPage #PrefabsToSave #Input.Value", allPaths); - this.sendUpdate(commandBuilder); - break; - } - case SelectEdited: { - String editedPaths = this.prefabEditSession - .getLoadedPrefabMetadata() - .values() - .stream() - .filter(PrefabEditingMetadata::isDirty) - .map(m -> m.getPrefabPath().toString().replace('\\', '/')) - .collect(Collectors.joining(",")); - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#MainPage #PrefabsToSave #Input.Value", editedPaths); - this.sendUpdate(commandBuilder); - if (editedPaths.isEmpty()) { - this.playerRef.sendMessage(Message.translation("server.commands.editprefab.save.noEditedPrefabs")); + ) + .exceptionally(throwable -> { + this.isSaving = false; + LOGGER.atWarning().withCause(throwable).log("Error saving prefabs"); + this.onSavingFailed(Message.raw(throwable.getMessage() != null ? throwable.getMessage() : "Unknown error")); + return null; + }); + break; + case Cancel: + playerComponent.getPageManager().setPage(ref, store, Page.None); + break; + case SelectAll: { + String allPaths = this.prefabEditSession.getLoadedPrefabMetadata().values().stream().map(this::toDisplayPath).collect(Collectors.joining("\n")); + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#MainPage #PrefabsToSave #Input.Value", allPaths); + this.sendUpdate(commandBuilder); + break; } - break; - } - case OpenBrowser: { - this.browserSearchQuery = ""; - this.selectedPrefabUuids.clear(); - UICommandBuilder commandBuilder = new UICommandBuilder(); - UIEventBuilder eventBuilder = new UIEventBuilder(); - commandBuilder.set("#MainPage.Visible", false); - commandBuilder.set("#BrowserPage.Visible", true); - commandBuilder.set("#BrowserPage #SearchInput.Value", ""); - this.buildPrefabList(commandBuilder, eventBuilder); - this.sendUpdate(commandBuilder, eventBuilder, false); - break; - } - case BrowserSearch: { - this.browserSearchQuery = data.browserSearchStr != null ? data.browserSearchStr.trim().toLowerCase() : ""; - UICommandBuilder commandBuilder = new UICommandBuilder(); - UIEventBuilder eventBuilder = new UIEventBuilder(); - this.buildPrefabList(commandBuilder, eventBuilder); - this.sendUpdate(commandBuilder, eventBuilder, false); - break; - } - case BrowserTogglePrefab: - if (data.prefabUuid != null) { - try { - UUID uuid = UUID.fromString(data.prefabUuid); - if (this.selectedPrefabUuids.contains(uuid)) { - this.selectedPrefabUuids.remove(uuid); - } else { - this.selectedPrefabUuids.add(uuid); - } - - UICommandBuilder commandBuilderx = new UICommandBuilder(); - UIEventBuilder eventBuilderx = new UIEventBuilder(); - this.buildPrefabList(commandBuilderx, eventBuilderx); - this.sendUpdate(commandBuilderx, eventBuilderx, false); - } catch (IllegalArgumentException var18) { + case SelectEdited: { + String editedPaths = this.prefabEditSession + .getLoadedPrefabMetadata() + .values() + .stream() + .filter(PrefabEditingMetadata::isDirty) + .map(this::toDisplayPath) + .collect(Collectors.joining("\n")); + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#MainPage #PrefabsToSave #Input.Value", editedPaths); + this.sendUpdate(commandBuilder); + if (editedPaths.isEmpty()) { + this.playerRef.sendMessage(Message.translation("server.commands.editprefab.save.noEditedPrefabs")); } + break; } - break; - case BrowserSelectAll: { - this.selectedPrefabUuids.clear(); - - for (PrefabEditingMetadata metadatax : this.prefabEditSession.getLoadedPrefabMetadata().values()) { - this.selectedPrefabUuids.add(metadatax.getUuid()); + case OpenBrowser: { + this.browserSearchQuery = ""; + this.selectedPrefabUuids.clear(); + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + commandBuilder.set("#MainPage.Visible", false); + commandBuilder.set("#BrowserPage.Visible", true); + commandBuilder.set("#BrowserPage #SearchInput.Value", ""); + this.buildPrefabList(commandBuilder, eventBuilder); + this.sendUpdate(commandBuilder, eventBuilder, false); + break; } + case BrowserSearch: { + this.browserSearchQuery = data.browserSearchStr != null ? data.browserSearchStr.trim().toLowerCase() : ""; + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + this.buildPrefabList(commandBuilder, eventBuilder); + this.sendUpdate(commandBuilder, eventBuilder, false); + break; + } + case BrowserTogglePrefab: + if (data.prefabUuid != null) { + try { + UUID uuid = UUID.fromString(data.prefabUuid); + if (this.selectedPrefabUuids.contains(uuid)) { + this.selectedPrefabUuids.remove(uuid); + } else { + this.selectedPrefabUuids.add(uuid); + } - UICommandBuilder commandBuilder = new UICommandBuilder(); - UIEventBuilder eventBuilder = new UIEventBuilder(); - this.buildPrefabList(commandBuilder, eventBuilder); - this.sendUpdate(commandBuilder, eventBuilder, false); - break; - } - case ConfirmBrowser: { - List selectedPaths = new ObjectArrayList<>(); - - for (PrefabEditingMetadata metadata : this.prefabEditSession.getLoadedPrefabMetadata().values()) { - if (this.selectedPrefabUuids.contains(metadata.getUuid())) { - selectedPaths.add(metadata.getPrefabPath().toString().replace('\\', '/')); + UICommandBuilder commandBuilderx = new UICommandBuilder(); + UIEventBuilder eventBuilderx = new UIEventBuilder(); + this.buildPrefabList(commandBuilderx, eventBuilderx); + this.sendUpdate(commandBuilderx, eventBuilderx, false); + } catch (IllegalArgumentException var22) { + } } - } + break; + case BrowserSelectAll: { + this.selectedPrefabUuids.clear(); - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#MainPage #PrefabsToSave #Input.Value", String.join(",", selectedPaths)); - commandBuilder.set("#BrowserPage.Visible", false); - commandBuilder.set("#MainPage.Visible", true); - this.sendUpdate(commandBuilder); - break; - } - case CancelBrowser: { - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#BrowserPage.Visible", false); - commandBuilder.set("#MainPage.Visible", true); - this.sendUpdate(commandBuilder); - break; - } - case BackFromSaving: { - this.isSaving = false; - UICommandBuilder commandBuilder = new UICommandBuilder(); - commandBuilder.set("#SavingPage.Visible", false); - commandBuilder.set("#MainPage.Visible", true); - this.sendUpdate(commandBuilder); + for (PrefabEditingMetadata metadatax : this.prefabEditSession.getLoadedPrefabMetadata().values()) { + this.selectedPrefabUuids.add(metadatax.getUuid()); + } + + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + this.buildPrefabList(commandBuilder, eventBuilder); + this.sendUpdate(commandBuilder, eventBuilder, false); + break; + } + case ConfirmBrowser: { + List selectedPaths = new ObjectArrayList<>(); + + for (PrefabEditingMetadata metadata : this.prefabEditSession.getLoadedPrefabMetadata().values()) { + if (this.selectedPrefabUuids.contains(metadata.getUuid())) { + selectedPaths.add(this.toDisplayPath(metadata)); + } + } + + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#MainPage #PrefabsToSave #Input.Value", String.join("\n", selectedPaths)); + commandBuilder.set("#BrowserPage.Visible", false); + commandBuilder.set("#MainPage.Visible", true); + this.sendUpdate(commandBuilder); + break; + } + case CancelBrowser: { + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#BrowserPage.Visible", false); + commandBuilder.set("#MainPage.Visible", true); + this.sendUpdate(commandBuilder); + break; + } + case BackFromSaving: { + this.isSaving = false; + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#SavingPage.Visible", false); + commandBuilder.set("#MainPage.Visible", true); + this.sendUpdate(commandBuilder); + } + case OpenPackBrowser: + case ConfirmPackBrowser: + case CancelPackBrowser: + case OpenCreatePack: + case CreatePack: + case CancelCreatePack: + case PackSearch: + case PackSelect: } } } @@ -429,20 +523,26 @@ public class PrefabEditorSaveSettingsPage extends InteractiveCustomUIPage("PrefabUuid", Codec.STRING), (o, prefabUuid) -> o.prefabUuid = prefabUuid, o -> o.prefabUuid) .add() + .append(new KeyedCodec<>("Pack", Codec.STRING), (o, s) -> o.packBrowserData.pack = s, o -> o.packBrowserData.pack) + .add() + .append(new KeyedCodec<>("@PackSearch", Codec.STRING), (o, s) -> o.packBrowserData.search = s, o -> o.packBrowserData.search) + .add() + .append(new KeyedCodec<>("@CreateName", Codec.STRING), (o, s) -> o.packBrowserData.createName = s, o -> o.packBrowserData.createName) + .add() + .append(new KeyedCodec<>("@CreateGroup", Codec.STRING), (o, s) -> o.packBrowserData.createGroup = s, o -> o.packBrowserData.createGroup) + .add() + .append( + new KeyedCodec<>("@CreateDescription", Codec.STRING), (o, s) -> o.packBrowserData.createDescription = s, o -> o.packBrowserData.createDescription + ) + .add() + .append(new KeyedCodec<>("@CreateVersion", Codec.STRING), (o, s) -> o.packBrowserData.createVersion = s, o -> o.packBrowserData.createVersion) + .add() + .append(new KeyedCodec<>("@CreateWebsite", Codec.STRING), (o, s) -> o.packBrowserData.createWebsite = s, o -> o.packBrowserData.createWebsite) + .add() + .append(new KeyedCodec<>("@CreateAuthorName", Codec.STRING), (o, s) -> o.packBrowserData.createAuthorName = s, o -> o.packBrowserData.createAuthorName) + .add() + .append(new KeyedCodec<>("ValidateCreate", Codec.STRING), (o, s) -> o.packBrowserData.validateCreate = s, o -> o.packBrowserData.validateCreate) + .add() .build(); public PrefabEditorSaveSettingsPage.Action action; public String prefabsToSave; @@ -534,6 +661,7 @@ public class PrefabEditorSaveSettingsPage extends InteractiveCustomUIPage { private static final Value BUTTON_HIGHLIGHTED = Value.ref("Pages/BasicTextButton.ui", "SelectedLabelStyle"); @@ -126,7 +126,7 @@ public class PrefabTeleportPage extends InteractiveCustomUIPage() { + { + Objects.requireNonNull(AssetPrefabFileProvider.this); + } + @Nonnull public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) { String fileName = file.getFileName().toString(); diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefablist/PrefabPage.java b/src/com/hypixel/hytale/builtin/buildertools/prefablist/PrefabPage.java index bb252af7..8542e29b 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefablist/PrefabPage.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefablist/PrefabPage.java @@ -187,7 +187,7 @@ public class PrefabPage extends InteractiveCustomUIPage { playerComponent.getPageManager().setPage(ref, store, Page.None); BlockSelection prefab = PrefabStore.get().getPrefab(file); BuilderToolsPlugin.addToQueue(playerComponent, playerRefComponent, (r, s, componentAccessor) -> s.load(displayPath, prefab, componentAccessor)); - PasteToolUtil.switchToPasteTool(playerComponent, playerRefComponent); + PasteToolUtil.switchToPasteTool(ref, playerComponent, playerRefComponent, store); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/prefablist/PrefabSavePage.java b/src/com/hypixel/hytale/builtin/buildertools/prefablist/PrefabSavePage.java index ab1267af..47ad4e4e 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/prefablist/PrefabSavePage.java +++ b/src/com/hypixel/hytale/builtin/buildertools/prefablist/PrefabSavePage.java @@ -1,6 +1,8 @@ package com.hypixel.hytale.builtin.buildertools.prefablist; +import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.BuilderToolsUserData; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -8,8 +10,6 @@ import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.Page; @@ -17,6 +17,9 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.ui.browser.AssetPackSaveBrowser; +import com.hypixel.hytale.server.core.ui.browser.AssetPackSaveBrowserConfig; +import com.hypixel.hytale.server.core.ui.browser.AssetPackSaveBrowserEventData; import com.hypixel.hytale.server.core.ui.builder.EventData; import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder; import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder; @@ -24,10 +27,16 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PrefabSavePage extends InteractiveCustomUIPage { @Nonnull private static final Message MESSAGE_SERVER_BUILDER_TOOLS_PREFAB_SAVE_NAME_REQUIRED = Message.translation("server.builderTools.prefabSave.nameRequired"); + @Nonnull + private static final Message MESSAGE_PACK_REQUIRED = Message.translation("server.customUI.assetPackBrowser.packRequired"); + private final AssetPackSaveBrowser packBrowser = new AssetPackSaveBrowser(AssetPackSaveBrowserConfig.defaults()); + private boolean initialized = false; public PrefabSavePage(@Nonnull PlayerRef playerRef) { super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, PrefabSavePage.PageData.CODEC); @@ -37,27 +46,49 @@ public class PrefabSavePage extends InteractiveCustomUIPage ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store store ) { + if (!this.initialized) { + this.initialized = true; + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + if (playerComponent != null) { + String lastPack = BuilderToolsUserData.get(playerComponent).getLastSavePack(); + this.packBrowser.setSelectedPackKey(lastPack); + if (lastPack != null && !this.packBrowser.hasSelectedPack()) { + this.playerRef.sendMessage(Message.translation("server.customUI.assetPackBrowser.packNoLongerAvailable")); + } + } + } + commandBuilder.append("Pages/PrefabSavePage.ui"); - commandBuilder.set("#Entities #CheckBox.Value", true); - commandBuilder.set("#Empty #CheckBox.Value", false); - commandBuilder.set("#Overwrite #CheckBox.Value", false); - commandBuilder.set("#FromClipboard #CheckBox.Value", false); - commandBuilder.set("#UsePlayerAnchor #CheckBox.Value", false); - commandBuilder.set("#ClearSupport #CheckBox.Value", false); + commandBuilder.set("#PackBrowserPage.Visible", false); + commandBuilder.set("#CreatePackPage.Visible", false); + if (this.packBrowser.hasSelectedPack()) { + commandBuilder.set("#MainPage #SelectedPackLabel.Text", this.packBrowser.getSelectedPackDisplayName()); + } + + commandBuilder.set("#MainPage #Entities #CheckBox.Value", true); + commandBuilder.set("#MainPage #Empty #CheckBox.Value", false); + commandBuilder.set("#MainPage #Overwrite #CheckBox.Value", false); + commandBuilder.set("#MainPage #FromClipboard #CheckBox.Value", false); + commandBuilder.set("#MainPage #UsePlayerAnchor #CheckBox.Value", false); + commandBuilder.set("#MainPage #ClearSupport #CheckBox.Value", false); eventBuilder.addEventBinding( CustomUIEventBindingType.Activating, - "#SaveButton", + "#MainPage #SaveButton", new EventData() .append("Action", PrefabSavePage.Action.Save.name()) - .append("@Name", "#NameInput.Value") - .append("@Entities", "#Entities #CheckBox.Value") - .append("@Empty", "#Empty #CheckBox.Value") - .append("@Overwrite", "#Overwrite #CheckBox.Value") - .append("@FromClipboard", "#FromClipboard #CheckBox.Value") - .append("@UsePlayerAnchor", "#UsePlayerAnchor #CheckBox.Value") - .append("@ClearSupport", "#ClearSupport #CheckBox.Value") + .append("@Name", "#MainPage #NameInput.Value") + .append("@Entities", "#MainPage #Entities #CheckBox.Value") + .append("@Empty", "#MainPage #Empty #CheckBox.Value") + .append("@Overwrite", "#MainPage #Overwrite #CheckBox.Value") + .append("@FromClipboard", "#MainPage #FromClipboard #CheckBox.Value") + .append("@UsePlayerAnchor", "#MainPage #UsePlayerAnchor #CheckBox.Value") + .append("@ClearSupport", "#MainPage #ClearSupport #CheckBox.Value") ); - eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#CancelButton", new EventData().append("Action", PrefabSavePage.Action.Cancel.name())); + eventBuilder.addEventBinding( + CustomUIEventBindingType.Activating, "#MainPage #CancelButton", new EventData().append("Action", PrefabSavePage.Action.Cancel.name()) + ); + this.packBrowser.buildEventBindings(eventBuilder, "#MainPage #BrowsePackButton"); + this.packBrowser.buildUI(commandBuilder, eventBuilder); } public void handleDataEvent(@Nonnull Ref ref, @Nonnull Store store, @Nonnull PrefabSavePage.PageData data) { @@ -65,26 +96,54 @@ public class PrefabSavePage extends InteractiveCustomUIPage { - if (data.fromClipboard) { - s.save(r, data.name, true, data.overwrite, data.clearSupport, componentAccessor); - } else { - s.saveFromSelection(r, data.name, true, data.overwrite, data.entities, data.empty, playerAnchor, data.clearSupport, componentAccessor); + if (packResult.packConfirmed() && this.packBrowser.hasSelectedPack()) { + BuilderToolsUserData.get(playerComponent).setLastSavePack(this.packBrowser.getSelectedPack().getName()); + } + + this.sendUpdate(packResult.commandBuilder(), packResult.eventBuilder(), false); + } else { + switch (data.action) { + case Save: + if (data.name == null || data.name.isBlank()) { + this.playerRef.sendMessage(MESSAGE_SERVER_BUILDER_TOOLS_PREFAB_SAVE_NAME_REQUIRED); + this.sendUpdate(null, null, false); + return; } - }); - break; - case Cancel: - playerComponent.getPageManager().setPage(ref, store, Page.None); + + AssetPack targetPack = this.packBrowser.getSelectedPack(); + if (targetPack == null) { + this.playerRef.sendMessage(MESSAGE_PACK_REQUIRED); + this.sendUpdate(null, null, false); + return; + } + + BuilderToolsUserData.get(playerComponent).setLastSavePack(targetPack.getName()); + playerComponent.getPageManager().setPage(ref, store, Page.None); + Vector3i playerAnchor = this.getPlayerAnchor(ref, store, data.usePlayerAnchor && !data.fromClipboard); + BuilderToolsPlugin.addToQueue( + playerComponent, + this.playerRef, + (r, s, componentAccessor) -> { + if (data.fromClipboard) { + s.save(r, data.name, true, data.overwrite, data.clearSupport, targetPack, componentAccessor); + } else { + s.saveFromSelection( + r, data.name, true, data.overwrite, data.entities, data.empty, playerAnchor, data.clearSupport, targetPack, componentAccessor + ); + } + } + ); + break; + case Cancel: + playerComponent.getPageManager().setPage(ref, store, Page.None); + } } } @@ -98,14 +157,22 @@ public class PrefabSavePage extends InteractiveCustomUIPage("@ClearSupport", Codec.BOOLEAN), (o, clearSupport) -> o.clearSupport = clearSupport, o -> o.clearSupport) .add() + .append(new KeyedCodec<>("Pack", Codec.STRING), (o, s) -> o.packBrowserData.pack = s, o -> o.packBrowserData.pack) + .add() + .append(new KeyedCodec<>("@PackSearch", Codec.STRING), (o, s) -> o.packBrowserData.search = s, o -> o.packBrowserData.search) + .add() + .append(new KeyedCodec<>("@CreateName", Codec.STRING), (o, s) -> o.packBrowserData.createName = s, o -> o.packBrowserData.createName) + .add() + .append(new KeyedCodec<>("@CreateGroup", Codec.STRING), (o, s) -> o.packBrowserData.createGroup = s, o -> o.packBrowserData.createGroup) + .add() + .append( + new KeyedCodec<>("@CreateDescription", Codec.STRING), (o, s) -> o.packBrowserData.createDescription = s, o -> o.packBrowserData.createDescription + ) + .add() + .append(new KeyedCodec<>("@CreateVersion", Codec.STRING), (o, s) -> o.packBrowserData.createVersion = s, o -> o.packBrowserData.createVersion) + .add() + .append(new KeyedCodec<>("@CreateWebsite", Codec.STRING), (o, s) -> o.packBrowserData.createWebsite = s, o -> o.packBrowserData.createWebsite) + .add() + .append(new KeyedCodec<>("@CreateAuthorName", Codec.STRING), (o, s) -> o.packBrowserData.createAuthorName = s, o -> o.packBrowserData.createAuthorName) + .add() + .append(new KeyedCodec<>("ValidateCreate", Codec.STRING), (o, s) -> o.packBrowserData.validateCreate = s, o -> o.packBrowserData.validateCreate) + .add() .build(); public PrefabSavePage.Action action; public String name; @@ -146,6 +233,7 @@ public class PrefabSavePage extends InteractiveCustomUIPage copyTo.setShapeThickness(copyFrom.getShapeThickness())), Capped((copyTo, copyFrom) -> copyTo.setCapped(copyFrom.isCapped())), + Transform((copyTo, copyFrom) -> copyTo.setTransform(copyFrom.getTransform())), Pattern((copyTo, copyFrom) -> copyTo.setPattern(copyFrom.getPattern())), Density((copyTo, copyFrom) -> copyTo.setDensity(copyFrom.getDensity())), BrushMask((copyTo, copyFrom) -> { diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigChunkAccessor.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigChunkAccessor.java index 30f88d8d..6b51b346 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigChunkAccessor.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigChunkAccessor.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.builtin.buildertools.scriptedbrushes; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor; import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BrushConfigChunkAccessor extends LocalCachedChunkAccessor { private final BrushConfigEditStore editOperation; diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigCommandExecutor.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigCommandExecutor.java index 0a4c8a73..41408b64 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigCommandExecutor.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigCommandExecutor.java @@ -5,10 +5,10 @@ import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSetting import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.system.GlobalBrushOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.system.SequenceBrushOperation; import com.hypixel.hytale.builtin.buildertools.tooloperations.ToolOperation; +import com.hypixel.hytale.builtin.buildertools.tooloperations.transform.Transform; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.UUIDComponent; @@ -28,6 +28,7 @@ import java.util.logging.Level; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class BrushConfigCommandExecutor { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @@ -44,6 +45,7 @@ public class BrushConfigCommandExecutor { private boolean enableBreakpoints; private BrushConfigCommandExecutor.DebugOutputTarget debugOutputTarget = BrushConfigCommandExecutor.DebugOutputTarget.Chat; private boolean breakOnError; + private Vector3i transformVector = new Vector3i(); @Nonnull private final Map brushConfigStoredSnapshots; private boolean allowOverwritingSavedSnapshots = true; @@ -172,7 +174,18 @@ public class BrushConfigCommandExecutor { this.brushConfig.getOriginAfterOffset().x, this.brushConfig.getOriginAfterOffset().y, this.brushConfig.getOriginAfterOffset().z, - (x, y, z, unused) -> brushOperation.modifyBlocks(ref, this.brushConfig, this, this.edit, x, y, z, componentAccessor), + (x, y, z, unused) -> { + Transform transform = this.brushConfig.getTransform(); + Vector3i transformOrigin = this.brushConfig.getTransformOrigin(); + this.transformVector.x = x - transformOrigin.x; + this.transformVector.y = y - transformOrigin.y; + this.transformVector.z = z - transformOrigin.z; + transform.apply(this.transformVector); + x = transformOrigin.x + this.transformVector.x; + y = transformOrigin.y + this.transformVector.y; + z = transformOrigin.z + this.transformVector.z; + return brushOperation.modifyBlocks(ref, this.brushConfig, this, this.edit, x, y, z, componentAccessor); + }, this.brushConfig.getShape(), this.brushConfig.getShapeWidth(), this.brushConfig.getShapeHeight(), diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigEditStore.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigEditStore.java index bf4f7ab5..68910d7a 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigEditStore.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/BrushConfigEditStore.java @@ -3,20 +3,23 @@ package com.hypixel.hytale.builtin.buildertools.scriptedbrushes; import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.builtin.buildertools.utils.Material; +import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntMaps; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntMap.Entry; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3i; public class BrushConfigEditStore { @Nonnull @@ -55,6 +58,11 @@ public class BrushConfigEditStore { return this.accessor; } + @Nonnull + public BrushConfig getBrushConfig() { + return this.brushConfig; + } + public int getOriginalBlock(int x, int y, int z) { return this.accessor.getBlockIgnoringHistory(x, y, z); } @@ -117,7 +125,57 @@ public class BrushConfigEditStore { } } - private boolean setFluid(int x, int y, int z, int fluidId, byte fluidLevel) { + public boolean setFullBlock(int x, int y, int z, int blockId, int rotation, int filler, int support, @Nullable Holder holder) { + boolean hasHistory = this.previous.hasBlockAtWorldPos(x, y, z) || this.previous.getFluidAtWorldPos(x, y, z) >= 0; + switch (this.brushConfig.getHistoryMask()) { + case Only: + if (!hasHistory) { + return false; + } + break; + case Not: + if (hasHistory) { + return false; + } + } + + if (this.brushConfig.getRandom().nextInt(100) >= this.brushConfig.getDensity()) { + return false; + } else { + if (this.getOriginalBlock(x, y, z) == 0) { + this.packedPlacedBlockPositions.add(BlockUtil.pack(x, y, z)); + } + + int currentBlock = this.getBlock(x, y, z); + int currentFluid = this.getFluid(x, y, z); + BlockMask blockMask = this.brushConfig.getBlockMask(); + if (blockMask != null && blockMask.isExcluded(this.accessor, x, y, z, null, null, currentBlock, currentFluid)) { + return false; + } else { + if (!this.before.hasBlockAtWorldPos(x, y, z)) { + WorldChunk blocks = this.accessor.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(x, z)); + if (blocks != null) { + this.before + .addBlockAtWorldPos( + x, + y, + z, + currentBlock, + blocks.getRotationIndex(x, y, z), + blocks.getFiller(x, y, z), + blocks.getSupportValue(x, y, z), + blocks.getBlockComponentHolder(x, y, z) + ); + } + } + + this.current.addBlockAtWorldPos(x, y, z, blockId, rotation, filler, support, holder); + return true; + } + } + } + + boolean setFluid(int x, int y, int z, int fluidId, byte fluidLevel) { boolean hasHistory = this.previous.hasBlockAtWorldPos(x, y, z) || this.previous.getFluidAtWorldPos(x, y, z) >= 0; switch (this.brushConfig.getHistoryMask()) { case Only: @@ -167,12 +225,16 @@ public class BrushConfigEditStore { } public boolean setMaterial(int x, int y, int z, @Nonnull Material material) { - if (material.isFluid()) { + if (material.isFluid() && material.getBlockId() == 0) { return this.setFluid(x, y, z, material.getFluidId(), material.getFluidLevel()); } else { - boolean result = this.setBlock(x, y, z, material.getBlockId()); + boolean result = this.setFullBlock( + x, y, z, material.getBlockId(), material.getRotation(), material.getFiller(), material.getSupport(), material.getHolder() + ); if (result && material.isEmpty()) { this.setFluid(x, y, z, 0, (byte)0); + } else if (result && material.getFluidId() != 0) { + this.setFluid(x, y, z, material.getFluidId(), material.getFluidLevel()); } return result; diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/ScriptedBrushAsset.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/ScriptedBrushAsset.java index 53f93d5c..ce589dab 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/ScriptedBrushAsset.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/ScriptedBrushAsset.java @@ -12,9 +12,14 @@ import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.system import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderTool; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -39,6 +44,9 @@ public class ScriptedBrushAsset implements JsonAssetWithMap ASSET_MAP; + public static final String DEFAULT_EDITOR_TOOL_ID = "EditorTool_ScriptedBrushTemplate"; + @Nullable + private static volatile Map brushToItemCache; protected AssetExtraInfo.Data data; protected String id; protected List operations = new ObjectArrayList<>(); @@ -57,6 +65,36 @@ public class ScriptedBrushAsset implements JsonAssetWithMap cache = brushToItemCache; + if (cache == null) { + cache = rebuildBrushToItemCache(); + } + + return cache.get(brushId); + } + + @Nonnull + static Map rebuildBrushToItemCache() { + Map items = Item.getAssetMap().getAssetMap(); + Object2ObjectOpenHashMap map = new Object2ObjectOpenHashMap<>(items.size() / 4); + + for (Entry entry : items.entrySet()) { + BuilderTool builderTool = entry.getValue().getBuilderTool(); + if (builderTool != null && builderTool.getBrushConfigurationCommand() != null && !builderTool.getBrushConfigurationCommand().isEmpty()) { + map.put(builderTool.getBrushConfigurationCommand(), entry.getKey()); + } + } + + brushToItemCache = map; + return map; + } + + public static void invalidateBrushToItemCache() { + brushToItemCache = null; + } + @Nonnull public String getId() { return this.id; diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigClearCommand.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigClearCommand.java index a1a57f0f..7ef01b61 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigClearCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigClearCommand.java @@ -40,6 +40,7 @@ public class BrushConfigClearCommand extends AbstractPlayerCommand { brushConfigCommandExecutor.getSequentialOperations().clear(); brushConfigCommandExecutor.getGlobalOperations().clear(); prototypeSettings.setUsePrototypeBrushConfigurations(false); + prototypeSettings.setPrototypeItemId(null); playerRef.sendMessage(MESSAGE_COMMANDS_BRUSH_CONFIG_CLEARED); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigCommand.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigCommand.java index d200e059..67649607 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigCommand.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayer import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.Objects; import javax.annotation.Nonnull; public class BrushConfigCommand extends AbstractCommandCollection { @@ -24,6 +25,10 @@ public class BrushConfigCommand extends AbstractCommandCollection { this.addSubCommand(new BrushConfigLoadCommand()); this.addSubCommand( new AbstractPlayerCommand("info", "server.commands.scriptedbrushes.info.desc") { + { + Objects.requireNonNull(BrushConfigCommand.this); + } + @Override protected void execute( @Nonnull CommandContext context, diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigLoadCommand.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigLoadCommand.java index 46ba518c..1cc075d4 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigLoadCommand.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/commands/BrushConfigLoadCommand.java @@ -14,6 +14,9 @@ import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredAr import com.hypixel.hytale.server.core.command.system.arguments.types.AssetArgumentType; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -72,6 +75,7 @@ public class BrushConfigLoadCommand extends AbstractPlayerCommand { @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { UUID playerUUID = playerRef.getUuid(); + Player playerComponent = store.getComponent(ref, Player.getComponentType()); PrototypePlayerBuilderToolSettings prototypeSettings = ToolOperation.getOrCreatePrototypeSettings(playerUUID); BrushConfig brushConfig = prototypeSettings.getBrushConfig(); BrushConfigCommandExecutor brushConfigCommandExecutor = prototypeSettings.getBrushConfigCommandExecutor(); @@ -79,10 +83,20 @@ public class BrushConfigLoadCommand extends AbstractPlayerCommand { playerRef.sendMessage(MESSAGE_COMMANDS_BRUSH_CONFIG_CANNOT_USE_COMMAND_DURING_EXEC); } else { ScriptedBrushAsset brushAssetArg = this.brushNameArg.get(context); + String brushId = brushAssetArg.getId(); brushAssetArg.loadIntoExecutor(brushConfigCommandExecutor); - prototypeSettings.setCurrentlyLoadedBrushConfigName(brushAssetArg.getId()); + prototypeSettings.setCurrentlyLoadedBrushConfigName(brushId); prototypeSettings.setUsePrototypeBrushConfigurations(true); - playerRef.sendMessage(Message.translation("server.commands.brushConfig.loaded").param("name", brushAssetArg.getId())); + Inventory inventory = playerComponent.getInventory(); + ItemContainer hotbar = inventory.getHotbar(); + String editorToolItemId = ScriptedBrushAsset.getEditorToolItemId(brushId); + if (editorToolItemId == null) { + editorToolItemId = "EditorTool_ScriptedBrushTemplate"; + } + + hotbar.setItemStackForSlot(inventory.getActiveHotbarSlot(), new ItemStack(editorToolItemId)); + prototypeSettings.setPrototypeItemId(editorToolItemId); + playerRef.sendMessage(Message.translation("server.commands.brushConfig.loaded").param("name", brushId)); } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/ClearRotationOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/ClearRotationOperation.java new file mode 100644 index 00000000..56bbc32d --- /dev/null +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/ClearRotationOperation.java @@ -0,0 +1,31 @@ +package com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential; + +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfig; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigCommandExecutor; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.system.SequenceBrushOperation; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import javax.annotation.Nonnull; + +public class ClearRotationOperation extends SequenceBrushOperation { + public static final BuilderCodec CODEC = BuilderCodec.builder(ClearRotationOperation.class, ClearRotationOperation::new) + .documentation("Reset the Brush-Config-provided rotation settings to default to undo the rotations") + .build(); + + public ClearRotationOperation() { + super("Clear Rotation Settings", "Reset the Brush-Config-provided rotation settings to default to undo the rotations", false); + } + + @Override + public void modifyBrushConfig( + @Nonnull Ref ref, + @Nonnull BrushConfig brushConfig, + @Nonnull BrushConfigCommandExecutor brushConfigCommandExecutor, + @Nonnull ComponentAccessor componentAccessor + ) { + brushConfig.resetTransform(); + brushConfig.setTransformOrigin(brushConfig.getOriginAfterOffset()); + } +} diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/ErodeOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/ErodeOperation.java index cf5440fb..82d5037d 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/ErodeOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/ErodeOperation.java @@ -10,11 +10,11 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ErodeOperation extends SequenceBrushOperation { public static final BuilderCodec CODEC = BuilderCodec.builder(ErodeOperation.class, ErodeOperation::new) diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/LayerOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/LayerOperation.java index 5ab44dcf..e0a4f0ae 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/LayerOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/LayerOperation.java @@ -11,7 +11,7 @@ import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderTool; import com.hypixel.hytale.server.core.codec.LayerEntryCodec; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -81,7 +81,7 @@ public class LayerOperation extends SequenceBrushOperation { } BlockAccessor chunk = edit.getAccessor().getChunk(ChunkUtil.indexChunkFromBlock(x, z)); - builderState.layer(x, y, z, layers, maxDepth, Vector3i.DOWN, (WorldChunk)chunk, edit.getBefore(), edit.getAfter()); + builderState.layer(x, y, z, layers, maxDepth, Vector3iUtil.DOWN, (WorldChunk)chunk, edit.getBefore(), edit.getAfter()); return true; } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/LoadIntFromToolArgOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/LoadIntFromToolArgOperation.java index e2c79a98..8e579a4e 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/LoadIntFromToolArgOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/LoadIntFromToolArgOperation.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderTool; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; @@ -18,6 +17,7 @@ import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Function; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class LoadIntFromToolArgOperation extends SequenceBrushOperation { public static final BuilderCodec CODEC = BuilderCodec.builder( diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/PastePrefabOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/PastePrefabOperation.java index 89aa1901..947a3dbf 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/PastePrefabOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/PastePrefabOperation.java @@ -1,34 +1,22 @@ package com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential; -import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfig; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigCommandExecutor; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigEditStore; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.system.SequenceBrushOperation; +import com.hypixel.hytale.builtin.buildertools.utils.Material; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.asset.type.buildertool.config.PrefabListAsset; -import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall; import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferUtil; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor; -import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; -import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.nio.file.Path; import java.util.Random; @@ -89,48 +77,27 @@ public class PastePrefabOperation extends SequenceBrushOperation { brushConfig.setErrorFlag("No prefab found in prefab list. Please double check your PrefabList asset."); return false; } else { - World world = componentAccessor.getExternalData().getWorld(); - PrefabBuffer.PrefabBufferAccessor accessor = PrefabBufferUtil.loadBuffer(prefabPath).newAccess(); + PrefabBuffer prefab = PrefabBufferUtil.loadBuffer(prefabPath); + PrefabBuffer.PrefabBufferAccessor accessor = prefab.newAccess(); this.hasBeenPlacedAlready = true; - double xLength = accessor.getMaxX() - accessor.getMinX(); - double zLength = accessor.getMaxZ() - accessor.getMinZ(); - int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength)); - LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, x, z, prefabRadius); - BlockTypeAssetMap blockTypeMap = BlockType.getAssetMap(); accessor.forEach( IPrefabBuffer.iterateAllColumns(), (xi, yi, zi, blockId, holder, supportValue, rotation, filler, call, fluidId, fluidLevel) -> { int bx = x + xi; int by = y + yi; int bz = z + zi; - WorldChunk chunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz)); - Store store = chunk.getWorld().getChunkStore().getStore(); - ChunkColumn column = store.getComponent(chunk.getReference(), ChunkColumn.getComponentType()); - Ref section = column.getSection(ChunkUtil.chunkCoordinate(by)); - FluidSection fluidSection = store.ensureAndGetComponent(section, FluidSection.getComponentType()); - fluidSection.setFluid(bx, by, bz, fluidId, (byte)fluidLevel); - BlockType block = blockTypeMap.getAsset(blockId); - String blockKey = block.getId(); - if (filler == 0) { - RotationTuple rot = RotationTuple.get(rotation); - chunk.placeBlock(bx, by, bz, blockKey, rot.yaw(), rot.pitch(), rot.roll(), 0); - if (supportValue != 0) { - Ref chunkRef = chunk.getReference(); - store = chunkRef.getStore(); - column = store.getComponent(chunkRef, ChunkColumn.getComponentType()); - BlockPhysics.setSupportValue(store, column.getSection(ChunkUtil.chunkCoordinate(by)), bx, by, bz, supportValue); - } - - if (holder != null) { - chunk.setState(bx, by, bz, holder.clone()); + if (filler != 0) { + if (fluidId != 0) { + edit.setMaterial(bx, by, bz, Material.fluid(fluidId, (byte)fluidLevel)); } + } else { + edit.setMaterial(bx, by, bz, Material.full(blockId, rotation, supportValue, 0, holder, fluidId, (byte)fluidLevel)); } }, (xi, zi, entityWrappers, t) -> {}, (xi, yi, zi, path, fitHeightmap, inheritSeed, inheritHeightCondition, weights, rotation, t) -> {}, new PrefabBufferCall(new Random(), PrefabRotation.fromRotation(Rotation.None)) ); - accessor.release(); return false; } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/RunCommandOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/RunCommandOperation.java index b3a49008..4cd5ecee 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/RunCommandOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/RunCommandOperation.java @@ -8,13 +8,13 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.command.system.CommandManager; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class RunCommandOperation extends SequenceBrushOperation { public static final BuilderCodec CODEC = BuilderCodec.builder(RunCommandOperation.class, RunCommandOperation::new) @@ -59,10 +59,10 @@ public class RunCommandOperation extends SequenceBrushOperation { commandString = commandString.replaceFirst(regexBracketPattern.pattern(), replacementValue); } - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); + PlayerRef playerRefComponent = componentAccessor.getComponent(ref, PlayerRef.getComponentType()); - assert playerComponent != null; + assert playerRefComponent != null; - CommandManager.get().handleCommand(playerComponent, commandString); + CommandManager.get().handleCommand(playerRefComponent, commandString); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/JumpIfBlockTypeOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/JumpIfBlockTypeOperation.java index 33ddfa2e..fb22238f 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/JumpIfBlockTypeOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/JumpIfBlockTypeOperation.java @@ -8,10 +8,10 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class JumpIfBlockTypeOperation extends SequenceBrushOperation { public static final BuilderCodec CODEC = BuilderCodec.builder(JumpIfBlockTypeOperation.class, JumpIfBlockTypeOperation::new) diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/loops/CircleOffsetAndLoopOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/loops/CircleOffsetAndLoopOperation.java index 2c7ef876..91e6ca71 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/loops/CircleOffsetAndLoopOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/loops/CircleOffsetAndLoopOperation.java @@ -8,11 +8,11 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class CircleOffsetAndLoopOperation extends SequenceBrushOperation { public static final int MAX_REPETITIONS = 100; @@ -50,9 +50,9 @@ public class CircleOffsetAndLoopOperation extends SequenceBrushOperation { @Nonnull private List offsetsInCircle = new ObjectArrayList<>(); @Nonnull - private Vector3i offsetWhenFirstReachedOperation = Vector3i.ZERO; + private final Vector3i offsetWhenFirstReachedOperation = new Vector3i(); @Nonnull - private Vector3i previousCircleOffset = Vector3i.ZERO; + private final Vector3i previousCircleOffset = new Vector3i(); public CircleOffsetAndLoopOperation() { super( @@ -66,8 +66,8 @@ public class CircleOffsetAndLoopOperation extends SequenceBrushOperation { public void resetInternalState() { this.repetitionsRemaining = -1; this.offsetsInCircle.clear(); - this.offsetWhenFirstReachedOperation = Vector3i.ZERO; - this.previousCircleOffset = Vector3i.ZERO; + this.offsetWhenFirstReachedOperation.zero(); + this.previousCircleOffset.zero(); int numPointsOnCircle = this.numberOfCirclePointsArg; int circleRadius = this.circleRadiusArg; double theta = (Math.PI * 2) / numPointsOnCircle; @@ -104,7 +104,7 @@ public class CircleOffsetAndLoopOperation extends SequenceBrushOperation { ) { if (this.repetitionsRemaining == -1) { this.resetInternalState(); - this.offsetWhenFirstReachedOperation = brushConfig.getOriginOffset(); + this.offsetWhenFirstReachedOperation.set(brushConfig.getOriginOffset()); if (this.numberOfCirclePointsArg > 100) { brushConfig.setErrorFlag("Cannot have more than 100 repetitions"); return; @@ -118,9 +118,9 @@ public class CircleOffsetAndLoopOperation extends SequenceBrushOperation { brushConfig.setOriginOffset(this.offsetWhenFirstReachedOperation); } else { Vector3i offsetVector = brushConfig.getOriginOffset() - .subtract(this.previousCircleOffset) - .add(this.offsetsInCircle.get(this.repetitionsRemaining - 1).clone()); - this.previousCircleOffset = this.offsetsInCircle.get(this.repetitionsRemaining - 1).clone(); + .sub(this.previousCircleOffset) + .add(new Vector3i(this.offsetsInCircle.get(this.repetitionsRemaining - 1))); + this.previousCircleOffset.set(this.offsetsInCircle.get(this.repetitionsRemaining - 1)); brushConfig.setOriginOffset(offsetVector); brushConfigCommandExecutor.loadOperatingIndex(this.indexNameArg, false); this.repetitionsRemaining--; diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/loops/CircleOffsetFromArgOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/loops/CircleOffsetFromArgOperation.java index ae84bedf..3d747c47 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/loops/CircleOffsetFromArgOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/flowcontrol/loops/CircleOffsetFromArgOperation.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderTool; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; @@ -17,6 +16,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class CircleOffsetFromArgOperation extends SequenceBrushOperation { public static final int MAX_REPETITIONS = 100; @@ -55,9 +55,9 @@ public class CircleOffsetFromArgOperation extends SequenceBrushOperation { @Nonnull private List offsetsInCircle = new ObjectArrayList<>(); @Nonnull - private Vector3i offsetWhenFirstReachedOperation = Vector3i.ZERO; + private final Vector3i offsetWhenFirstReachedOperation = new Vector3i(); @Nonnull - private Vector3i previousCircleOffset = Vector3i.ZERO; + private final Vector3i previousCircleOffset = new Vector3i(); public CircleOffsetFromArgOperation() { super( @@ -71,8 +71,8 @@ public class CircleOffsetFromArgOperation extends SequenceBrushOperation { public void resetInternalState() { this.repetitionsRemaining = -1; this.offsetsInCircle.clear(); - this.offsetWhenFirstReachedOperation = Vector3i.ZERO; - this.previousCircleOffset = Vector3i.ZERO; + this.offsetWhenFirstReachedOperation.zero(); + this.previousCircleOffset.zero(); int numPointsOnCircle = this.numCirclePointsArgVal; int circleRadius = this.circleRadiusArgVal; double theta = (Math.PI * 2) / numPointsOnCircle; @@ -161,7 +161,7 @@ public class CircleOffsetFromArgOperation extends SequenceBrushOperation { this.previousCirclePointsVal = this.numCirclePointsArgVal; this.previousCircleRadiusVal = this.circleRadiusArgVal; this.resetInternalState(); - this.offsetWhenFirstReachedOperation = brushConfig.getOriginOffset(); + this.offsetWhenFirstReachedOperation.set(brushConfig.getOriginOffset()); if (this.numCirclePointsArgVal > 100) { brushConfig.setErrorFlag("Cannot have more than 100 repetitions"); return; @@ -175,9 +175,9 @@ public class CircleOffsetFromArgOperation extends SequenceBrushOperation { brushConfig.setOriginOffset(this.offsetWhenFirstReachedOperation); } else { Vector3i offsetVector = brushConfig.getOriginOffset() - .subtract(this.previousCircleOffset) - .add(this.offsetsInCircle.get(this.repetitionsRemaining - 1).clone()); - this.previousCircleOffset = this.offsetsInCircle.get(this.repetitionsRemaining - 1).clone(); + .sub(this.previousCircleOffset) + .add(new Vector3i(this.offsetsInCircle.get(this.repetitionsRemaining - 1))); + this.previousCircleOffset.set(this.offsetsInCircle.get(this.repetitionsRemaining - 1)); brushConfig.setOriginOffset(offsetVector); brushConfigCommandExecutor.loadOperatingIndex(this.indexNameArg, false); this.repetitionsRemaining--; diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/offsets/OffsetOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/offsets/OffsetOperation.java index 5f889f2d..573db965 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/offsets/OffsetOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/offsets/OffsetOperation.java @@ -10,10 +10,10 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeVector3i; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class OffsetOperation extends SequenceBrushOperation { public static final BuilderCodec CODEC = BuilderCodec.builder(OffsetOperation.class, OffsetOperation::new) @@ -57,15 +57,15 @@ public class OffsetOperation extends SequenceBrushOperation { } if (this.offsetArg.isRelativeX()) { - offsetVector.setX(offsetVector.getX() + relativeFieldValue); + offsetVector.x = offsetVector.x() + relativeFieldValue; } if (this.offsetArg.isRelativeY()) { - offsetVector.setY(offsetVector.getY() + relativeFieldValue); + offsetVector.y = offsetVector.y() + relativeFieldValue; } if (this.offsetArg.isRelativeZ()) { - offsetVector.setZ(offsetVector.getZ() + relativeFieldValue); + offsetVector.z = offsetVector.z() + relativeFieldValue; } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/offsets/RandomOffsetOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/offsets/RandomOffsetOperation.java index 0005e244..33520264 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/offsets/RandomOffsetOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/offsets/RandomOffsetOperation.java @@ -7,10 +7,10 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeIntegerRange; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class RandomOffsetOperation extends SequenceBrushOperation { public static final BuilderCodec CODEC = BuilderCodec.builder(RandomOffsetOperation.class, RandomOffsetOperation::new) diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/transforms/RotateOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/transforms/RotateOperation.java new file mode 100644 index 00000000..9ecf0c7e --- /dev/null +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/sequential/transforms/RotateOperation.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.transforms; + +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfig; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigCommandExecutor; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.system.SequenceBrushOperation; +import com.hypixel.hytale.builtin.buildertools.tooloperations.transform.Rotate; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.protocol.Rotation; +import com.hypixel.hytale.protocol.packets.buildertools.BrushAxis; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; + +public class RotateOperation extends SequenceBrushOperation { + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + public static final BuilderCodec CODEC = BuilderCodec.builder(RotateOperation.class, RotateOperation::new) + .append(new KeyedCodec<>("RotationAxis", new EnumCodec<>(BrushAxis.class)), (op, val) -> op.rotationAxisArg = val, op -> op.rotationAxisArg) + .add() + .append(new KeyedCodec<>("RotationAngle", new EnumCodec<>(Rotation.class)), (op, val) -> op.rotationAngleArg = val, op -> op.rotationAngleArg) + .add() + .append( + new KeyedCodec<>("RotationOrigin", new EnumCodec<>(RotateOperation.RotationOrigin.class)), + (op, val) -> op.rotationOriginArg = val, + op -> op.rotationOriginArg + ) + .add() + .documentation("Rotates the brush based on axis, angle, and origin") + .build(); + @Nonnull + public Rotation rotationAngleArg = Rotation.None; + @Nonnull + public BrushAxis rotationAxisArg = BrushAxis.None; + @Nonnull + public RotateOperation.RotationOrigin rotationOriginArg = RotateOperation.RotationOrigin.OffsetCenter; + + public RotateOperation() { + super("Rotation", "Changes the orientation of the brush shape with a rotation", false); + } + + @Override + public void modifyBrushConfig( + @Nonnull Ref ref, + @Nonnull BrushConfig brushConfig, + @Nonnull BrushConfigCommandExecutor brushConfigCommandExecutor, + @Nonnull ComponentAccessor componentAccessor + ) { + if (this.rotationOriginArg == RotateOperation.RotationOrigin.OffsetCenter) { + brushConfig.setTransformOrigin(brushConfig.getOriginAfterOffset()); + } else if (this.rotationOriginArg == RotateOperation.RotationOrigin.ClickCenter) { + brushConfig.setTransformOrigin(brushConfig.getExecutionOrigin()); + } else if (this.rotationOriginArg == RotateOperation.RotationOrigin.Player) { + TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); + if (transformComponent == null) { + brushConfig.setErrorFlag("Could not get the player's position."); + return; + } + + Vector3d position = transformComponent.getPosition(); + brushConfig.setTransformOrigin(new Vector3i(MathUtil.floor(position.x()), MathUtil.floor(position.y()), MathUtil.floor(position.z()))); + } + + brushConfig.setTransform(Rotate.forAxisAndAngle(this.rotationAxisArg, this.rotationAngleArg)); + } + + public static enum RotationOrigin { + OffsetCenter, + ClickCenter, + Player; + } +} diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/system/BrushOperation.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/system/BrushOperation.java index 7fe760ba..7735de31 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/system/BrushOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/operations/system/BrushOperation.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.global import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.BlockPatternOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.BreakpointOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.ClearOperationMaskOperation; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.ClearRotationOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.DeleteOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.EchoOnceOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.EchoOperation; @@ -54,6 +55,7 @@ import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequen import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.saveandload.PersistentDataOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.saveandload.SaveBrushConfigOperation; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.saveandload.SaveIndexOperation; +import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.operations.sequential.transforms.RotateOperation; import com.hypixel.hytale.codec.lookup.CodecMapCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; @@ -155,6 +157,8 @@ public abstract class BrushOperation { BRUSH_OPERATION_REGISTRY.put("set", SetOperation::new); BRUSH_OPERATION_REGISTRY.put("smooth", SmoothOperation::new); BRUSH_OPERATION_REGISTRY.put("shape", ShapeOperation::new); + BRUSH_OPERATION_REGISTRY.put("rotation", RotateOperation::new); + BRUSH_OPERATION_REGISTRY.put("clearrotation", ClearRotationOperation::new); BRUSH_OPERATION_REGISTRY.put("offset", OffsetOperation::new); BRUSH_OPERATION_REGISTRY.put("layer", LayerOperation::new); BRUSH_OPERATION_REGISTRY.put("heightmaplayer", HeightmapLayerOperation::new); diff --git a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/ui/ScriptedBrushPage.java b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/ui/ScriptedBrushPage.java index 24b2de9a..cbb06338 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/ui/ScriptedBrushPage.java +++ b/src/com/hypixel/hytale/builtin/buildertools/scriptedbrushes/ui/ScriptedBrushPage.java @@ -1,11 +1,9 @@ package com.hypixel.hytale.builtin.buildertools.scriptedbrushes.ui; -import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigCommandExecutor; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.ScriptedBrushAsset; import com.hypixel.hytale.builtin.buildertools.tooloperations.ToolOperation; -import com.hypixel.hytale.common.util.StringCompareUtil; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; @@ -13,21 +11,19 @@ import com.hypixel.hytale.protocol.packets.interface_.Page; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage; +import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.ui.browser.FileBrowserConfig; import com.hypixel.hytale.server.core.ui.browser.FileBrowserEventData; -import com.hypixel.hytale.server.core.ui.browser.FileListProvider; import com.hypixel.hytale.server.core.ui.browser.ServerFileBrowser; import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder; import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.nio.file.Files; import java.nio.file.Path; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; import java.util.UUID; -import java.util.stream.Collectors; import javax.annotation.Nonnull; public class ScriptedBrushPage extends InteractiveCustomUIPage { @@ -39,11 +35,13 @@ public class ScriptedBrushPage extends InteractiveCustomUIPage ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store store ) { commandBuilder.append("Pages/ScriptedBrushListPage.ui"); - commandBuilder.set("#RootSelector.Visible", false); - this.browser.buildSearchInput(commandBuilder, eventBuilder); - this.browser.buildFileList(commandBuilder, eventBuilder); + this.browser.buildUI(commandBuilder, eventBuilder); } public void handleDataEvent(@Nonnull Ref ref, @Nonnull Store store, @Nonnull FileBrowserEventData data) { if (this.browser.handleEvent(data)) { UICommandBuilder commandBuilder = new UICommandBuilder(); UIEventBuilder eventBuilder = new UIEventBuilder(); - this.browser.buildFileList(commandBuilder, eventBuilder); + this.browser.buildUI(commandBuilder, eventBuilder); this.sendUpdate(commandBuilder, eventBuilder, false); } else { - String brushName = data.getFile(); - if (brushName != null) { - this.handleBrushSelection(ref, store, brushName); + String selectedPath = data.getSearchResult() != null ? data.getSearchResult() : data.getFile(); + if (selectedPath != null) { + this.handleBrushSelection(ref, store, selectedPath, data.getSearchResult() != null); } } } - private void handleBrushSelection(@Nonnull Ref ref, @Nonnull Store store, @Nonnull String brushName) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType()); - - assert playerRefComponent != null; - - ScriptedBrushAsset scriptedBrushAsset = ScriptedBrushAsset.get(brushName); - if (scriptedBrushAsset == null) { - playerRefComponent.sendMessage(Message.translation("server.commands.brushConfig.load.error.notFound").param("name", brushName)); - this.sendUpdate(); + private void handleBrushSelection(@Nonnull Ref ref, @Nonnull Store store, String selectedPath, boolean isSearchResult) { + String virtualPath; + if (isSearchResult) { + virtualPath = selectedPath; } else { - UUID playerUUID = playerRefComponent.getUuid(); - PrototypePlayerBuilderToolSettings prototypeSettings = ToolOperation.getOrCreatePrototypeSettings(playerUUID); - BrushConfigCommandExecutor brushConfigCommandExecutor = prototypeSettings.getBrushConfigCommandExecutor(); - - try { - scriptedBrushAsset.loadIntoExecutor(brushConfigCommandExecutor); - prototypeSettings.setCurrentlyLoadedBrushConfigName(scriptedBrushAsset.getId()); - prototypeSettings.setUsePrototypeBrushConfigurations(true); - playerComponent.getPageManager().setPage(ref, store, Page.None); - playerRefComponent.sendMessage(Message.translation("server.commands.brushConfig.loaded").param("name", scriptedBrushAsset.getId())); - } catch (Exception var11) { - playerRefComponent.sendMessage( - Message.translation("server.commands.brushConfig.load.error.loadFailed") - .param("name", brushName) - .param("error", var11.getMessage() != null ? var11.getMessage() : "Unknown error") - ); - this.sendUpdate(); - } + String currentPath = this.browser.getAssetPackCurrentPath(); + virtualPath = currentPath.isEmpty() ? selectedPath : currentPath + "/" + selectedPath; } - } - private static class ScriptedBrushListProvider implements FileListProvider { - @Nonnull - @Override - public List getFiles(@Nonnull Path currentDir, @Nonnull String searchQuery) { - DefaultAssetMap assetMap = ScriptedBrushAsset.getAssetMap(); - if (searchQuery.isEmpty()) { - return assetMap.getAssetMap().keySet().stream().sorted().map(namex -> new FileListProvider.FileEntry(namex, false)).collect(Collectors.toList()); + Path resolvedPath = this.browser.resolveAssetPackPath(virtualPath); + if (resolvedPath != null && !Files.isDirectory(resolvedPath)) { + String fileName = resolvedPath.getFileName().toString(); + String brushId = fileName.endsWith(".json") ? fileName.substring(0, fileName.length() - ".json".length()) : fileName; + ScriptedBrushAsset scriptedBrushAsset = ScriptedBrushAsset.get(brushId); + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + + assert playerComponent != null; + + PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType()); + + assert playerRefComponent != null; + + if (scriptedBrushAsset == null) { + playerRefComponent.sendMessage(Message.translation("server.commands.brushConfig.load.error.notFound").param("name", brushId)); + this.sendUpdate(); } else { - List results = new ObjectArrayList<>(); + UUID playerUUID = playerRefComponent.getUuid(); + PrototypePlayerBuilderToolSettings prototypeSettings = ToolOperation.getOrCreatePrototypeSettings(playerUUID); + BrushConfigCommandExecutor brushConfigCommandExecutor = prototypeSettings.getBrushConfigCommandExecutor(); - for (String name : assetMap.getAssetMap().keySet()) { - int score = StringCompareUtil.getFuzzyDistance(name, searchQuery, Locale.ENGLISH); - if (score > 0) { - results.add(new FileListProvider.FileEntry(name, name, false, false, score)); + try { + scriptedBrushAsset.loadIntoExecutor(brushConfigCommandExecutor); + Inventory inventory = playerComponent.getInventory(); + ItemContainer hotbar = inventory.getHotbar(); + String editorToolItemId = ScriptedBrushAsset.getEditorToolItemId(brushId); + if (editorToolItemId == null) { + editorToolItemId = "EditorTool_ScriptedBrushTemplate"; } - } - results.sort(Comparator.comparingInt(FileListProvider.FileEntry::matchScore).reversed()); - return results.size() > 50 ? results.subList(0, 50) : results; + hotbar.setItemStackForSlot(inventory.getActiveHotbarSlot(), new ItemStack(editorToolItemId)); + prototypeSettings.setPrototypeItemId(editorToolItemId); + prototypeSettings.setCurrentlyLoadedBrushConfigName(scriptedBrushAsset.getId()); + prototypeSettings.setUsePrototypeBrushConfigurations(true); + playerComponent.getPageManager().setPage(ref, store, Page.None); + playerRefComponent.sendMessage(Message.translation("server.commands.brushConfig.loaded").param("name", scriptedBrushAsset.getId())); + } catch (Exception var18) { + playerRefComponent.sendMessage( + Message.translation("server.commands.brushConfig.load.error.loadFailed") + .param("name", brushId) + .param("error", var18.getMessage() != null ? var18.getMessage() : "Unknown error") + ); + this.sendUpdate(); + } } + } else { + this.sendUpdate(); } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/BlockSelectionSnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/BlockSelectionSnapshot.java index 8af86736..332b6345 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/BlockSelectionSnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/BlockSelectionSnapshot.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.buildertools.snapshot; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -20,8 +20,8 @@ public class BlockSelectionSnapshot implements SelectionSnapshot ref, Player player, @Nonnull World world, ComponentAccessor componentAccessor) { - BlockSelection before = this.selection.place(player, world); + public BlockSelectionSnapshot restore(Ref ref, PlayerRef playerRef, @Nonnull World world, ComponentAccessor componentAccessor) { + BlockSelection before = this.selection.place(playerRef, world); BuilderToolsPlugin.invalidateWorldMapForSelection(before, world); return new BlockSelectionSnapshot(before); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardBoundsSnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardBoundsSnapshot.java index 41176337..3aa114de 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardBoundsSnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardBoundsSnapshot.java @@ -3,15 +3,15 @@ package com.hypixel.hytale.builtin.buildertools.snapshot; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ClipboardBoundsSnapshot implements ClipboardSnapshot { - public static final ClipboardBoundsSnapshot EMPTY = new ClipboardBoundsSnapshot(Vector3i.ZERO, Vector3i.ZERO); + public static final ClipboardBoundsSnapshot EMPTY = new ClipboardBoundsSnapshot(new Vector3i(), new Vector3i()); private final Vector3i min; private final Vector3i max; @@ -33,7 +33,7 @@ public class ClipboardBoundsSnapshot implements ClipboardSnapshot ref, Player player, World world, @Nonnull BuilderToolsPlugin.BuilderState state, ComponentAccessor componentAccessor + Ref ref, PlayerRef playerRef, World world, @Nonnull BuilderToolsPlugin.BuilderState state, ComponentAccessor componentAccessor ) { ClipboardBoundsSnapshot snapshot = new ClipboardBoundsSnapshot(state.getSelection()); state.getSelection().setSelectionArea(this.min, this.max); diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardContentsSnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardContentsSnapshot.java index f1ff8b87..b9c3e328 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardContentsSnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardContentsSnapshot.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.buildertools.snapshot; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -17,7 +17,11 @@ public class ClipboardContentsSnapshot implements ClipboardSnapshot ref, Player player, World world, @Nonnull BuilderToolsPlugin.BuilderState builderState, ComponentAccessor componentAccessor + Ref ref, + PlayerRef playerRef, + World world, + @Nonnull BuilderToolsPlugin.BuilderState builderState, + ComponentAccessor componentAccessor ) { ClipboardContentsSnapshot snapshot = new ClipboardContentsSnapshot(builderState.getSelection()); builderState.setSelection(this.selection); diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardSnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardSnapshot.java index e194f752..5168544f 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardSnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/ClipboardSnapshot.java @@ -12,16 +12,16 @@ import javax.annotation.Nullable; public interface ClipboardSnapshot> extends SelectionSnapshot { @Nullable - T restoreClipboard(Ref var1, Player var2, World var3, BuilderToolsPlugin.BuilderState var4, ComponentAccessor var5); + T restoreClipboard(Ref var1, PlayerRef var2, World var3, BuilderToolsPlugin.BuilderState var4, ComponentAccessor var5); @Override - default T restore(Ref ref, @Nonnull Player player, World world, ComponentAccessor componentAccessor) { - PlayerRef playerRefComponent = componentAccessor.getComponent(ref, PlayerRef.getComponentType()); - if (!.$assertionsDisabled && playerRefComponent == null) { + default T restore(Ref ref, @Nonnull PlayerRef playerRef, World world, ComponentAccessor componentAccessor) { + Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); + if (!.$assertionsDisabled && playerComponent == null) { throw new AssertionError(); } else { - BuilderToolsPlugin.BuilderState state = BuilderToolsPlugin.getState(player, playerRefComponent); - return state == null ? null : this.restoreClipboard(ref, player, world, state, componentAccessor); + BuilderToolsPlugin.BuilderState state = BuilderToolsPlugin.getState(playerComponent, playerRef); + return state == null ? null : this.restoreClipboard(ref, playerRef, world, state, componentAccessor); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityAddSnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityAddSnapshot.java index 34577de0..2f04a50f 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityAddSnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityAddSnapshot.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.builtin.buildertools.snapshot; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -19,7 +19,7 @@ public class EntityAddSnapshot implements EntitySnapshot { return this.entityRef; } - public EntityRemoveSnapshot restoreEntity(@Nonnull Player player, @Nonnull World world, @Nonnull ComponentAccessor componentAccessor) { + public EntityRemoveSnapshot restoreEntity(@Nonnull PlayerRef playerRef, @Nonnull World world, @Nonnull ComponentAccessor componentAccessor) { if (!this.entityRef.isValid()) { return null; } else { diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityRemoveSnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityRemoveSnapshot.java index b5b9c6a0..63046412 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityRemoveSnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityRemoveSnapshot.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -22,7 +22,7 @@ public class EntityRemoveSnapshot implements EntitySnapshot { return this.holder; } - public EntityAddSnapshot restoreEntity(@Nonnull Player player, @Nonnull World world, @Nonnull ComponentAccessor componentAccessor) { + public EntityAddSnapshot restoreEntity(@Nonnull PlayerRef playerRef, @Nonnull World world, @Nonnull ComponentAccessor componentAccessor) { Ref entityRef = componentAccessor.addEntity(this.holder, AddReason.LOAD); return new EntityAddSnapshot(entityRef); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntitySnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntitySnapshot.java index fd96b21c..f63918eb 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntitySnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntitySnapshot.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.builtin.buildertools.snapshot; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.CompletableFuture; @@ -12,13 +12,13 @@ import javax.annotation.Nullable; public interface EntitySnapshot> extends SelectionSnapshot { @Nullable - T restoreEntity(@Nonnull Player var1, @Nonnull World var2, @Nonnull ComponentAccessor var3); + T restoreEntity(@Nonnull PlayerRef var1, @Nonnull World var2, @Nonnull ComponentAccessor var3); @Override - default T restore(Ref ref, Player player, @Nonnull World world, ComponentAccessor componentAccessor) { + default T restore(Ref ref, PlayerRef playerRef, @Nonnull World world, ComponentAccessor componentAccessor) { Store store = world.getEntityStore().getStore(); return !world.isInThread() - ? CompletableFuture.supplyAsync(() -> this.restoreEntity(player, world, store), world).join() - : this.restoreEntity(player, world, store); + ? CompletableFuture.supplyAsync(() -> this.restoreEntity(playerRef, world, store), world).join() + : this.restoreEntity(playerRef, world, store); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityTransformSnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityTransformSnapshot.java index 9eb52c6e..d9f23e93 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityTransformSnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/EntityTransformSnapshot.java @@ -2,11 +2,11 @@ package com.hypixel.hytale.builtin.buildertools.snapshot; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -17,7 +17,7 @@ public class EntityTransformSnapshot implements EntitySnapshot ref, @Nonnull ComponentAccessor componentAccessor) { this.ref = ref; @@ -29,11 +29,11 @@ public class EntityTransformSnapshot implements EntitySnapshot componentAccessor) { + public EntityTransformSnapshot restoreEntity(@Nonnull PlayerRef playerRef, @Nonnull World world, @Nonnull ComponentAccessor componentAccessor) { if (!this.ref.isValid()) { return null; } else { diff --git a/src/com/hypixel/hytale/builtin/buildertools/snapshot/SelectionSnapshot.java b/src/com/hypixel/hytale/builtin/buildertools/snapshot/SelectionSnapshot.java index f0203837..e2d3580d 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/snapshot/SelectionSnapshot.java +++ b/src/com/hypixel/hytale/builtin/buildertools/snapshot/SelectionSnapshot.java @@ -2,12 +2,12 @@ package com.hypixel.hytale.builtin.buildertools.snapshot; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nullable; public interface SelectionSnapshot> { @Nullable - T restore(Ref var1, Player var2, World var3, ComponentAccessor var4); + T restore(Ref var1, PlayerRef var2, World var3, ComponentAccessor var4); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/FloodOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/FloodOperation.java index 4592931b..339841f5 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/FloodOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/FloodOperation.java @@ -14,6 +14,7 @@ public class FloodOperation extends ToolOperation { public FloodOperation( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) { @@ -24,11 +25,11 @@ public class FloodOperation extends ToolOperation { public void execute(ComponentAccessor componentAccessor) { BlockPattern targetPattern = (BlockPattern)this.args.tool().get("TargetBlock"); int targetBlock = targetPattern.isEmpty() ? this.edit.getAccessor().getBlock(this.x, this.y, this.z) : targetPattern.firstBlock(); - Player playerComponent = componentAccessor.getComponent(this.playerRef, Player.getComponentType()); + Player playerComponent = componentAccessor.getComponent(this.playerEntityRef, Player.getComponentType()); assert playerComponent != null; - PlayerRef playerRefComponent = componentAccessor.getComponent(this.playerRef, PlayerRef.getComponentType()); + PlayerRef playerRefComponent = componentAccessor.getComponent(this.playerEntityRef, PlayerRef.getComponentType()); assert playerRefComponent != null; diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/LaserPointerOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/LaserPointerOperation.java index 77b855a7..125c80fb 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/LaserPointerOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/LaserPointerOperation.java @@ -3,17 +3,18 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolLaserPointer; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.util.ColorParseUtil; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.PlayerUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class LaserPointerOperation extends ToolOperation { private static final double MAX_DISTANCE = 128.0; @@ -21,6 +22,7 @@ public class LaserPointerOperation extends ToolOperation { public LaserPointerOperation( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) { @@ -30,9 +32,9 @@ public class LaserPointerOperation extends ToolOperation { int laserColor; try { laserColor = ColorParseUtil.hexStringToRGBInt(colorText); - } catch (NumberFormatException var18) { - player.sendMessage(Message.translation("server.builderTools.laserPointer.colorParseError").param("value", colorText)); - throw var18; + } catch (NumberFormatException var19) { + playerRef.sendMessage(Message.translation("server.builderTools.laserPointer.colorParseError").param("value", colorText)); + throw var19; } Object durationObj = this.args.tool().get("Duration"); @@ -42,9 +44,9 @@ public class LaserPointerOperation extends ToolOperation { } else if (durationObj instanceof String) { try { duration = Integer.parseInt((String)durationObj); - } catch (NumberFormatException var17) { - player.sendMessage(Message.translation("server.builderTools.laserPointer.durationParseError").param("value", String.valueOf(durationObj))); - throw var17; + } catch (NumberFormatException var18) { + playerRef.sendMessage(Message.translation("server.builderTools.laserPointer.durationParseError").param("value", String.valueOf(durationObj))); + throw var18; } } else { duration = 300; @@ -59,7 +61,7 @@ public class LaserPointerOperation extends ToolOperation { Vector3d lookVecPosition = lookVec.getPosition(); Vector3d lookVecDirection = lookVec.getDirection(); Vector3d hitLocation = TargetUtil.getTargetLocation(ref, blockId -> blockId != 0, 128.0, componentAccessor); - Vector3d endLocation = hitLocation != null ? hitLocation : lookVecPosition.add(lookVecDirection.scale(128.0)); + Vector3d endLocation = hitLocation != null ? hitLocation : new Vector3d(lookVecPosition).add(new Vector3d(lookVecDirection).mul(128.0)); BuilderToolLaserPointer laserPacket = new BuilderToolLaserPointer(); laserPacket.playerNetworkId = playerNetworkId; laserPacket.startX = (float)lookVecPosition.x; @@ -73,6 +75,11 @@ public class LaserPointerOperation extends ToolOperation { PlayerUtil.broadcastPacketToPlayers(componentAccessor, laserPacket); } + @Override + public boolean showEditNotification() { + return false; + } + @Override public void execute(ComponentAccessor componentAccessor) { } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/LayersOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/LayersOperation.java index ad7e5153..88932950 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/LayersOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/LayersOperation.java @@ -3,11 +3,12 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.accessor.BlockAccessor; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -15,9 +16,10 @@ import it.unimi.dsi.fastutil.Pair; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class LayersOperation extends ToolOperation { - private final Vector3i depthDirection; + private final Vector3i depthDirection = new Vector3i(); private final int layerOneLength; private final int layerTwoLength; private final boolean enableLayerTwo; @@ -37,6 +39,7 @@ public class LayersOperation extends ToolOperation { public LayersOperation( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) { @@ -45,31 +48,31 @@ public class LayersOperation extends ToolOperation { assert headRotationComponent != null; - String var6 = (String)this.args.tool().get("aDirection"); - switch (var6) { + String var7 = (String)this.args.tool().get("aDirection"); + switch (var7) { case "Up": - this.depthDirection = Vector3i.UP; + this.depthDirection.set(Vector3iUtil.UP); break; case "Down": - this.depthDirection = Vector3i.DOWN; + this.depthDirection.set(Vector3iUtil.DOWN); break; case "North": - this.depthDirection = Vector3i.NORTH; + this.depthDirection.set(Vector3iUtil.NORTH); break; case "South": - this.depthDirection = Vector3i.SOUTH; + this.depthDirection.set(Vector3iUtil.SOUTH); break; case "East": - this.depthDirection = Vector3i.EAST; + this.depthDirection.set(Vector3iUtil.EAST); break; case "West": - this.depthDirection = Vector3i.WEST; + this.depthDirection.set(Vector3iUtil.WEST); break; case "Camera": - this.depthDirection = headRotationComponent.getAxisDirection(); + this.depthDirection.set(headRotationComponent.getAxisDirection()); break; default: - this.depthDirection = Vector3i.DOWN; + this.depthDirection.set(Vector3iUtil.DOWN); } this.brushDensity = (Integer)this.args.tool().get("jBrushDensity"); @@ -99,7 +102,7 @@ public class LayersOperation extends ToolOperation { this.maxDepthNecessary = this.layerOneLength + (this.enableLayerTwo ? this.layerTwoLength : 0) + (this.enableLayerThree ? this.layerThreeLength : 0); if (this.enableLayerThree && !this.enableLayerTwo) { - player.sendMessage(Message.translation("server.builderTools.layerOperation.layerTwoRequired")); + playerRef.sendMessage(Message.translation("server.builderTools.layerOperation.layerTwoRequired")); this.failed = true; } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/NoiseOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/NoiseOperation.java index f3867b78..0bfeb106 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/NoiseOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/NoiseOperation.java @@ -4,15 +4,15 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; public class NoiseOperation extends ToolOperation { - private final int brushDensity = this.getBrushDensity(); - public NoiseOperation( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) { @@ -22,7 +22,7 @@ public class NoiseOperation extends ToolOperation { @Override boolean execute0(int x, int y, int z) { int currentBlock = this.edit.getBlock(x, y, z); - if (currentBlock <= 0 && this.builderState.isAsideBlock(this.edit.getAccessor(), x, y, z) && this.random.nextInt(100) <= this.brushDensity) { + if (currentBlock <= 0 && this.builderState.isAsideBlock(this.edit.getAccessor(), x, y, z) && this.random.nextInt(100) <= this.density) { this.edit.setBlock(x, y, z, this.pattern.nextBlock(this.random)); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/OperationFactory.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/OperationFactory.java index 144ec1b9..b153ccad 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/OperationFactory.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/OperationFactory.java @@ -4,12 +4,17 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; public interface OperationFactory { @Nonnull ToolOperation create( - @Nonnull Ref var1, @Nonnull Player var2, @Nonnull BuilderToolOnUseInteraction var3, @Nonnull ComponentAccessor var4 + @Nonnull Ref var1, + @Nonnull Player var2, + @Nonnull PlayerRef var3, + @Nonnull BuilderToolOnUseInteraction var4, + @Nonnull ComponentAccessor var5 ); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/PaintOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/PaintOperation.java index 3aa6c3a2..a7a30238 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/PaintOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/PaintOperation.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations; import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; +import com.hypixel.hytale.builtin.buildertools.tooloperations.transform.Transform; import com.hypixel.hytale.builtin.buildertools.utils.Material; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; @@ -8,17 +9,20 @@ import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class PaintOperation extends ToolOperation { - private final int brushDensity; + private final Transform brushRotation; private LongOpenHashSet packedPlacedBlockPositions; public PaintOperation( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) { @@ -29,17 +33,22 @@ public class PaintOperation extends ToolOperation { PrototypePlayerBuilderToolSettings prototypePlayerBuilderToolSettings = ToolOperation.PROTOTYPE_TOOL_SETTINGS.get(uuidComponent.getUuid()); this.packedPlacedBlockPositions = prototypePlayerBuilderToolSettings.addIgnoredPaintOperation(); - this.brushDensity = this.getBrushDensity(); + this.brushRotation = this.getBrushRotation(componentAccessor); } @Override boolean execute0(int x, int y, int z) { + Vector3i vector = new Vector3i(x - this.currentCenterX, y - this.currentCenterY, z - this.currentCenterZ); + this.brushRotation.apply(vector); + x = this.currentCenterX + vector.x; + y = this.currentCenterY + vector.y; + z = this.currentCenterZ + vector.z; if (y >= 0 && y < 320) { if (this.edit.getBlock(x, y, z) == 0) { this.packedPlacedBlockPositions.add(BlockUtil.pack(x, y, z)); } - if (this.random.nextInt(100) <= this.brushDensity) { + if (this.random.nextInt(100) <= this.density) { this.edit.setMaterial(x, y, z, Material.fromPattern(this.pattern, this.random)); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/RevolveOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/RevolveOperation.java new file mode 100644 index 00000000..9c2537f8 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/RevolveOperation.java @@ -0,0 +1,480 @@ +package com.hypixel.hytale.builtin.buildertools.tooloperations; + +import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.utils.Material; +import com.hypixel.hytale.component.AddReason; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; +import com.hypixel.hytale.protocol.packets.interface_.NotificationStyle; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import com.hypixel.hytale.server.core.entity.UUIDComponent; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems; +import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; +import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.accessor.BlockAccessor; +import com.hypixel.hytale.server.core.universe.world.accessor.OverridableChunkAccessor; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import java.util.List; +import java.util.UUID; +import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector3i; + +public class RevolveOperation extends ToolOperation { + private Vector3f center; + private BlockSelection currentSelection; + private RevolveOperation.Sampling samplingMode = RevolveOperation.Sampling.Neighbor; + private int copyCount = 6; + private double stepRadians; + private double stepDegrees; + private int bufferX; + private int bufferZ; + private Material[][][] materialBuffer; + private static final double DISTANCE_TO_NEXT_BLOCK = 0.33; + private static final int REVOLVE_COPY_LIMIT = 1000000; + private static final int MAX_ENTITIES = 1000; + private static final int MIN_COPY_FULL = 10; + private static final int MAX_COPY_FULL = 2000; + private static final int REVOLVE_DENSITY = 12; + private static final int[][] NEIGHBOR_OFFSETS_XZ = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {-1, 1}, {1, -1}, {-1, -1}}; + + public RevolveOperation( + @Nonnull Ref ref, + @Nonnull Player player, + @Nonnull PlayerRef playerRef, + @Nonnull BuilderToolOnUseInteraction packet, + @Nonnull ComponentAccessor componentAccessor + ) { + super(ref, packet, componentAccessor); + BuilderToolsPlugin.BuilderState state = BuilderToolsPlugin.getState(player, playerRef); + BlockSelection selection = state.getSelection(); + if (selection == null) { + BuilderToolsPlugin.sendFeedback(Message.translation("server.builderTools.noSelection"), playerRef, NotificationStyle.Warning, componentAccessor); + } else if (!selection.hasSelectionBounds()) { + BuilderToolsPlugin.sendFeedback(Message.translation("server.builderTools.noSelectionBounds"), playerRef, NotificationStyle.Warning, componentAccessor); + } else { + OverridableChunkAccessor accessor = this.edit.getAccessor(); + String fullRevolve = (String)this.args.tool().getOrDefault("aSampling", "neighbor"); + switch (fullRevolve) { + case "reverse": + this.samplingMode = RevolveOperation.Sampling.Reverse; + break; + case "none": + this.samplingMode = RevolveOperation.Sampling.Disabled; + break; + default: + this.samplingMode = RevolveOperation.Sampling.Neighbor; + } + + this.copyCount = (Integer)this.args.tool().getOrDefault("bCount", 6); + boolean fullRevolve = (Boolean)this.args.tool().getOrDefault("cFullRevolve", false); + boolean useDistance = (Boolean)this.args.tool().getOrDefault("eUseDistance", false); + boolean copyEntities = (Boolean)this.args.tool().getOrDefault("eCopyEntities", false); + String customDistance = (String)this.args.tool().getOrDefault("dCenter", "target"); + byte min = -1; + switch (customDistance.hashCode()) { + case -985752863: + if (customDistance.equals("player")) { + min = 0; + } + default: + switch (min) { + case 0: + Vector3d position = playerRef.getTransform().getPosition(); + this.center = new Vector3f((float)position.x, (float)position.y, (float)position.z).floor().add(0.5F, 0.5F, 0.5F); + break; + default: + this.center = new Vector3f(this.x + 0.5F, this.y + this.originOffsetY, this.z + 0.5F); + } + + int customDistancex = 0; + if (useDistance) { + customDistancex = (Integer)this.args.tool().getOrDefault("fDistance", 20); + } + + Vector3i minx = Vector3iUtil.min(selection.getSelectionMin(), selection.getSelectionMax()); + Vector3i max = Vector3iUtil.max(selection.getSelectionMin(), selection.getSelectionMax()); + int xMin = minx.x(); + int xMax = max.x(); + int yMin = minx.y(); + int yMax = max.y(); + int zMin = minx.z(); + int zMax = max.z(); + this.currentSelection = new BlockSelection(); + this.bufferX = xMax - xMin + 1; + int bufferY = yMax - yMin + 1; + this.bufferZ = zMax - zMin + 1; + this.currentSelection.setPosition(xMin, yMin, zMin); + if (this.samplingMode.equals(RevolveOperation.Sampling.Neighbor)) { + this.materialBuffer = new Material[this.bufferX][bufferY][this.bufferZ]; + } + + int chunkXMin = xMin >> 5; + int chunkXMax = xMax >> 5; + int chunkZMin = zMin >> 5; + int chunkZMax = zMax >> 5; + + for (int chunkX = chunkXMin; chunkX <= chunkXMax; chunkX++) { + for (int chunkZ = chunkZMin; chunkZ <= chunkZMax; chunkZ++) { + BlockAccessor chunk = accessor.getChunk(ChunkUtil.indexChunk(chunkX, chunkZ)); + if (chunk != null) { + int localXMin = Math.max(xMin, chunkX << 5); + int localXMax = Math.min(xMax, (chunkX << 5) + 31); + int localZMin = Math.max(zMin, chunkZ << 5); + int localZMax = Math.min(zMax, (chunkZ << 5) + 31); + + for (int x = localXMin; x <= localXMax; x++) { + for (int z = localZMin; z <= localZMax; z++) { + for (int y = yMax; y >= yMin; y--) { + int block = chunk.getBlock(x, y, z); + int rotation = chunk.getRotationIndex(x, y, z); + int support = chunk.getSupportValue(x, y, z); + int filler = chunk.getFiller(x, y, z); + int fluid = chunk.getFluidId(x, y, z); + byte level = chunk.getFluidLevel(x, y, z); + Holder holder = chunk.getBlockComponentHolder(x, y, z); + if (block != 0 + && filler == 0 + && (this.edit.getBlockMask() == null || !this.edit.getBlockMask().isExcluded(accessor, x, y, z, minx, max, block, 0))) { + this.currentSelection.addBlockAtWorldPos(x, y, z, block, rotation, filler, support, holder); + } + + if (fluid != 0 + && (this.edit.getBlockMask() == null || !this.edit.getBlockMask().isExcluded(accessor, x, y, z, minx, max, 0, fluid))) { + this.currentSelection.addFluidAtWorldPos(x, y, z, fluid, level); + } + + if (this.samplingMode == RevolveOperation.Sampling.Neighbor) { + this.materialBuffer[x - xMin][y - yMin][z - zMin] = Material.full(block, rotation, support, filler, holder, fluid, level); + } + + this.edit.setMaterial(x, y, z, Material.EMPTY); + } + } + } + } + } + } + + List> entityRefs = new ReferenceArrayList<>(); + if (!fullRevolve && copyEntities) { + World world = componentAccessor.getExternalData().getWorld(); + Store entityStore = world.getEntityStore().getStore(); + List> entities = new ReferenceArrayList<>(); + BuilderToolsPlugin.forEachCopyableInSelection(world, xMin, yMin, zMin, xMax - xMin, yMax - yMin, zMax - zMin, entities::add); + int totalEntities = entities.size() * this.copyCount; + if (totalEntities <= 1000) { + for (Ref e : entities) { + Holder holderx = entityStore.copyEntity(e); + this.currentSelection.addEntityFromWorld(holderx); + entityRefs.add(e); + } + } else { + BuilderToolsPlugin.sendFeedback( + Message.translation("server.builderTools.errorTooManyEntities").param("count", 1000), + playerRef, + NotificationStyle.Warning, + componentAccessor + ); + } + } + + if (customDistancex > 0) { + double selCX = (xMin + xMax) / 2.0; + double selCZ = (zMin + zMax) / 2.0; + double pivotDX = selCX - this.center.x; + double pivotDZ = selCZ - this.center.z; + double naturalDist = Math.sqrt(pivotDX * pivotDX + pivotDZ * pivotDZ); + if (naturalDist > 0.0) { + double scale = customDistancex / naturalDist; + int newPosX = (int)Math.floor(this.center.x + pivotDX * scale) - (xMax - xMin) / 2; + int newPosZ = (int)Math.floor(this.center.z + pivotDZ * scale) - (zMax - zMin) / 2; + this.currentSelection.setPosition(newPosX, yMin, newPosZ); + int deltaX = newPosX - xMin; + int deltaZ = newPosZ - zMin; + + for (Ref entity : entityRefs) { + this.edit.trackMovedEntity(entity, componentAccessor); + TransformComponent transform = componentAccessor.getComponent(entity, TransformComponent.getComponentType()); + if (transform != null) { + transform.getPosition().add(deltaX, 0.0, deltaZ); + } + } + } + } + + if (fullRevolve) { + this.samplingMode = RevolveOperation.Sampling.Disabled; + double radiusXZ = computeRadiusXZ(xMin, xMax, zMin, zMax, this.center); + this.copyCount = MathUtil.clamp((int)Math.round(radiusXZ * 12.0), 10, 2000); + int selectionBFCount = this.currentSelection.getBlockCount() + this.currentSelection.getFluidCount(); + if (selectionBFCount == 0) { + return; + } + + int maxCount = MathUtil.clamp(1000000 / selectionBFCount, 10, 2000); + this.copyCount = Math.min(this.copyCount, maxCount); + } + + this.stepRadians = Math.toRadians(360.0 / this.copyCount); + this.stepDegrees = 360.0 / this.copyCount; + } + } + } + + @Override + public void execute(ComponentAccessor componentAccessor) { + } + + @Override + public void executeAt(int posX, int posY, int posZ, ComponentAccessor componentAccessor) { + if (this.currentSelection != null) { + double[] cos = new double[this.copyCount + 1]; + double[] sin = new double[this.copyCount + 1]; + double[] degrees = new double[this.copyCount + 1]; + + for (int i = 0; i <= this.copyCount; i++) { + double angle = this.stepRadians * i; + cos[i] = Math.cos(angle); + sin[i] = Math.sin(angle); + degrees[i] = this.stepDegrees * i; + } + + if (this.samplingMode.equals(RevolveOperation.Sampling.Reverse)) { + this.reverseSample(); + this.rotateEntities(componentAccessor, sin, cos, degrees); + } else { + BlockSelection placeDelayed = new BlockSelection(); + BlockSelection alreadyPlaced = new BlockSelection(); + int[][] offsets = NEIGHBOR_OFFSETS_XZ; + Vector3i rotVec = new Vector3i(); + this.currentSelection + .forEachBlock( + (bx, by, bz, blockHolder) -> { + int neighborMask = this.checkNeighbours(offsets, blockHolder.blockId(), bx, by, bz, false); + int nextCount = Integer.bitCount(neighborMask); + double deltaX = bx + this.currentSelection.getX() + 0.5 - this.center.x; + double deltaZ = bz + this.currentSelection.getZ() + 0.5 - this.center.z; + + for (int c = 1; c <= this.copyCount; c++) { + int newRotation = rotateBlock(blockHolder, degrees[c]); + if (this.samplingMode == RevolveOperation.Sampling.Neighbor && nextCount > 0) { + for (int d = 0; d < offsets.length; d++) { + if ((neighborMask & 1 << d) != 0) { + double offsetDeltaX = offsets[d][0] * 0.33 + deltaX; + double offsetDeltaZ = offsets[d][1] * 0.33 + deltaZ; + this.rotate(rotVec, c, offsetDeltaX, by, offsetDeltaZ, sin, cos); + placeDelayed.addBlockAtWorldPos( + rotVec.x, rotVec.y, rotVec.z, blockHolder.blockId(), newRotation, blockHolder.filler(), blockHolder.supportValue() + ); + } + } + } + + this.rotate(rotVec, c, deltaX, by, deltaZ, sin, cos); + if (nextCount > 0) { + placeDelayed.addBlockAtWorldPos( + rotVec.x, rotVec.y, rotVec.z, blockHolder.blockId(), newRotation, blockHolder.filler(), blockHolder.supportValue() + ); + } else { + alreadyPlaced.addBlockAtWorldPos( + rotVec.x, rotVec.y, rotVec.z, blockHolder.blockId(), newRotation, blockHolder.filler(), blockHolder.supportValue() + ); + this.edit + .setMaterial( + rotVec.x, + rotVec.y, + rotVec.z, + Material.full(blockHolder.blockId(), newRotation, blockHolder.supportValue(), blockHolder.filler(), blockHolder.holder()) + ); + } + } + } + ); + this.currentSelection.forEachFluid((bx, by, bz, fluid, level) -> { + int neighborMask = this.checkNeighbours(offsets, fluid, bx, by, bz, true); + int nextCount = Integer.bitCount(neighborMask); + double deltaX = bx + this.currentSelection.getX() + 0.5 - this.center.x; + double deltaZ = bz + this.currentSelection.getZ() + 0.5 - this.center.z; + + for (int c = 1; c <= this.copyCount; c++) { + if (this.samplingMode == RevolveOperation.Sampling.Neighbor && nextCount > 0) { + for (int d = 0; d < offsets.length; d++) { + if ((neighborMask & 1 << d) != 0) { + double offsetDeltaX = offsets[d][0] * 0.33 + deltaX; + double offsetDeltaZ = offsets[d][1] * 0.33 + deltaZ; + this.rotate(rotVec, c, offsetDeltaX, by, offsetDeltaZ, sin, cos); + this.edit.setMaterial(rotVec.x, rotVec.y, rotVec.z, Material.fluid(fluid, level)); + } + } + } + + this.rotate(rotVec, c, deltaX, by, deltaZ, sin, cos); + this.edit.setMaterial(rotVec.x, rotVec.y, rotVec.z, Material.fluid(fluid, level)); + } + }); + placeDelayed.forEachBlock( + (bx, by, bz, blockHolder) -> { + if (!alreadyPlaced.hasBlockAtWorldPos(bx, by, bz)) { + this.edit + .setMaterial( + bx, + by, + bz, + Material.full(blockHolder.blockId(), blockHolder.rotation(), blockHolder.supportValue(), blockHolder.filler(), blockHolder.holder()) + ); + } + } + ); + this.rotateEntities(componentAccessor, sin, cos, degrees); + } + } + } + + private void rotateEntities(ComponentAccessor componentAccessor, double[] sin, double[] cos, double[] degrees) { + World world = componentAccessor.getExternalData().getWorld(); + Store entityStore = world.getEntityStore().getStore(); + this.currentSelection.forEachEntity(entityHolder -> { + TransformComponent t = entityHolder.getComponent(TransformComponent.getComponentType()); + double x = t.getPosition().x(); + double y = t.getPosition().y(); + double z = t.getPosition().z(); + double deltaX = x + this.currentSelection.getX() - this.center.x; + double deltaZ = z + this.currentSelection.getZ() - this.center.z; + + for (int c = 1; c < this.copyCount; c++) { + double rotatedX = deltaX * cos[c] - deltaZ * sin[c]; + double rotatedZ = deltaX * sin[c] + deltaZ * cos[c]; + float yawRad = (float)Math.toRadians(-degrees[c]); + Holder copy = entityHolder.clone(); + TransformComponent transformComponent = copy.getComponent(TransformComponent.getComponentType()); + Rotation3f bodyRot = transformComponent.getRotation(); + bodyRot.addYaw(yawRad); + HeadRotation headRotComp = copy.getComponent(HeadRotation.getComponentType()); + if (headRotComp != null) { + headRotComp.getRotation().addYaw(yawRad); + } + + transformComponent.getPosition().set(rotatedX + this.center.x, y + this.currentSelection.getY(), rotatedZ + this.center.z); + copy.putComponent(UUIDComponent.getComponentType(), new UUIDComponent(UUID.randomUUID())); + copy.removeComponent(EntityTrackerSystems.Visible.getComponentType()); + copy.removeComponent(NetworkId.getComponentType()); + Ref entityRef = new Ref<>(entityStore); + world.execute(() -> entityStore.addEntity(copy, entityRef, AddReason.LOAD)); + this.edit.trackSpawnedEntity(entityRef); + } + }); + } + + private static int rotateBlock(BlockSelection.BlockHolder blockHolder, double snapped) { + Rotation snappedYaw = Rotation.ofDegrees((int)(Math.round(-snapped / 90.0) * 90L)); + RotationTuple blockRotation = RotationTuple.get(blockHolder.rotation()); + RotationTuple rotatedRotation = RotationTuple.of(blockRotation.yaw().add(snappedYaw), blockRotation.pitch(), blockRotation.roll()); + if (rotatedRotation == null) { + rotatedRotation = blockRotation; + } + + return rotatedRotation.index(); + } + + private int checkNeighbours(int[][] offsets, int blockID, int x, int y, int z, boolean fluid) { + int nextToSame = 0; + if (this.samplingMode == RevolveOperation.Sampling.Neighbor) { + for (int d = 0; d < offsets.length; d++) { + int neighborX = x + offsets[d][0]; + int neighborZ = z + offsets[d][1]; + if (neighborX >= 0 && neighborX < this.bufferX && neighborZ >= 0 && neighborZ < this.bufferZ) { + boolean matches = !fluid + ? this.materialBuffer[neighborX][y][neighborZ].getBlockId() == blockID + : this.materialBuffer[neighborX][y][neighborZ].getFluidId() == blockID; + if (matches) { + nextToSame |= 1 << d; + } + } + } + } + + return nextToSame; + } + + private void rotate(Vector3i v, int c, double x, int y, double z, double[] sin, double[] cos) { + double rx = x * cos[c] - z * sin[c]; + double rz = x * sin[c] + z * cos[c]; + v.set((int)Math.floor(rx + this.center.x), y + this.currentSelection.getY(), (int)Math.floor(rz + this.center.z)); + } + + private void reverseSample() { + this.currentSelection + .setAnchor( + (int)(this.center.x - this.currentSelection.getX()), + (int)this.center.y - this.currentSelection.getY(), + (int)(this.center.z - this.currentSelection.getZ()) + ); + + for (int c = 1; c <= this.copyCount; c++) { + BlockSelection calcSelection = this.currentSelection.cloneSelection().rotateArbitrary((float)this.stepDegrees * c, 0.0F, 0.0F); + calcSelection.forEachBlock( + (bx, by, bz, blockHolder) -> this.edit + .setMaterial( + bx + this.currentSelection.getX(), + by + this.currentSelection.getY(), + bz + this.currentSelection.getZ(), + Material.full(blockHolder.blockId(), blockHolder.rotation(), blockHolder.supportValue(), blockHolder.filler(), blockHolder.holder()) + ) + ); + calcSelection.forEachFluid( + (bx, by, bz, fluid, level) -> this.edit + .setMaterial( + bx + this.currentSelection.getX(), by + this.currentSelection.getY(), bz + this.currentSelection.getZ(), Material.fluid(fluid, level) + ) + ); + } + } + + private static double computeRadiusXZ(int xMin, int xMax, int zMin, int zMax, Vector3f hit) { + double maxR2 = 0.0; + int[] xs = new int[]{xMin, xMax}; + int[] zs = new int[]{zMin, zMax}; + + for (int cx : xs) { + for (int cz : zs) { + double deltaX = cx + 0.5 - hit.x; + double deltaZ = cz + 0.5 - hit.z; + double r2 = deltaX * deltaX + deltaZ * deltaZ; + if (r2 > maxR2) { + maxR2 = r2; + } + } + } + + return Math.sqrt(maxR2); + } + + @Override + public boolean execute0(int x, int y, int z) { + return false; + } + + private static enum Sampling { + Neighbor, + Reverse, + Disabled; + } +} diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/ScatterOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/ScatterOperation.java index a200728a..64a20375 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/ScatterOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/ScatterOperation.java @@ -6,16 +6,17 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; public class ScatterOperation extends ToolOperation { - private final int brushDensity = this.getBrushDensity(); private final BlockTypeAssetMap assetMap = BlockType.getAssetMap(); public ScatterOperation( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) { @@ -28,7 +29,7 @@ public class ScatterOperation extends ToolOperation { if (currentBlock <= 0 && this.builderState.isAsideBlock(this.edit.getAccessor(), x, y, z) && this.assetMap.getAsset(this.edit.getBlock(x, y - 1, z)).getFlags().isStackable - && this.random.nextInt(100) <= this.brushDensity) { + && this.random.nextInt(100) <= this.density) { this.edit.setBlock(x, y, z, this.pattern.nextBlock(this.random)); } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/SculptOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/SculptOperation.java index 53e324a6..89009c8f 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/SculptOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/SculptOperation.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import javax.annotation.Nonnull; @@ -18,12 +19,12 @@ public class SculptOperation extends ToolOperation { private final int smoothVolume; private final int smoothRadius; private final boolean isAltPlaySculptBrushModDown; - private final int brushDensity; private LongOpenHashSet packedPlacedBlockPositions; public SculptOperation( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) { @@ -39,20 +40,19 @@ public class SculptOperation extends ToolOperation { this.smoothRadius = Math.min(smoothStrength, 4); int smoothRange = this.smoothRadius * 2 + 1; this.smoothVolume = smoothRange * smoothRange * smoothRange; - this.brushDensity = this.getBrushDensity(); } @Override boolean execute0(int x, int y, int z) { int currentBlock = this.edit.getBlock(x, y, z); if (this.isAltPlaySculptBrushModDown) { - BuilderToolsPlugin.BuilderState.BlocksSampleData data = BuilderToolsPlugin.getState(this.player, this.player.getPlayerRef()) + BuilderToolsPlugin.BuilderState.BlocksSampleData data = BuilderToolsPlugin.getState(this.player, this.playerRef) .getBlocksSampleData(this.edit.getAccessor(), x, y, z, 2); if (currentBlock != data.mainBlock && data.mainBlockCount > this.smoothVolume * 0.5F) { this.edit.setMaterial(x, y, z, Material.block(data.mainBlock)); } } else if (this.interactionType == InteractionType.Primary) { - if (currentBlock > 0 && this.builderState.isAsideAir(this.edit.getAccessor(), x, y, z) && this.random.nextInt(100) <= this.brushDensity) { + if (currentBlock > 0 && this.builderState.isAsideAir(this.edit.getAccessor(), x, y, z) && this.random.nextInt(100) <= this.density) { this.edit.setMaterial(x, y, z, Material.EMPTY); } } else if (this.interactionType == InteractionType.Secondary && currentBlock <= 0 && this.builderState.isAsideBlock(this.edit.getAccessor(), x, y, z)) { @@ -60,7 +60,7 @@ public class SculptOperation extends ToolOperation { this.packedPlacedBlockPositions.add(BlockUtil.pack(x, y, z)); } - if (this.random.nextInt(100) <= this.brushDensity) { + if (this.random.nextInt(100) <= this.density) { Material material = Material.fromPattern(this.pattern, this.random); if (material.isEmpty()) { BuilderToolsPlugin.BuilderState.BlocksSampleData data = this.builderState.getBlocksSampleData(this.edit.getAccessor(), x, y, z, 1); diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/SmoothOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/SmoothOperation.java index 18306365..5b6fad7d 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/SmoothOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/SmoothOperation.java @@ -20,7 +20,7 @@ public class SmoothOperation extends ToolOperation { @Override boolean execute0(int x, int y, int z) { int currentBlock = this.edit.getBlock(x, y, z); - BuilderToolsPlugin.BuilderState.BlocksSampleData data = BuilderToolsPlugin.getState(this.player, this.player.getPlayerRef()) + BuilderToolsPlugin.BuilderState.BlocksSampleData data = BuilderToolsPlugin.getState(this.player, this.playerRef) .getBlocksSampleData(this.edit.getAccessor(), x, y, z, 2); if (currentBlock != data.mainBlock && data.mainBlockCount > this.smoothVolume * 0.5F) { this.edit.setBlock(x, y, z, data.mainBlock); diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/TintOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/TintOperation.java index 6f62b32e..8acea995 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/TintOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/TintOperation.java @@ -1,46 +1,155 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations; +import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; +import com.hypixel.hytale.builtin.buildertools.PrototypePlayerBuilderToolSettings; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.math.block.BlockUtil; +import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; +import com.hypixel.hytale.protocol.packets.interface_.NotificationStyle; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderTool; +import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.ToolArgException; import com.hypixel.hytale.server.core.asset.util.ColorParseUtil; +import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import javax.annotation.Nonnull; public class TintOperation extends ToolOperation { - private final int tintColor; + private int tintColor; + private double opacity; + private boolean blendMode = false; + private int bufferOriginX; + private int bufferOriginZ; + private int[][] colorBuffer; + private final boolean isHoldingAltModeDown; + private LongOpenHashSet packedPlacedTinsPositions; + private static final int SAMPLE_DISTANCE = 4; public TintOperation( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) { super(ref, packet, componentAccessor); - String colorText = (String)this.args.tool().get("TintColor"); + this.isHoldingAltModeDown = packet.isAltPlaySculptBrushModDown; + if (this.interactionType == InteractionType.Primary) { + this.blendMode = true; + } - try { - this.tintColor = ColorParseUtil.hexStringToRGBInt(colorText); - } catch (NumberFormatException var7) { - player.sendMessage(Message.translation("server.builderTools.tintOperation.colorParseError").param("value", colorText)); - throw var7; + if (this.blendMode && this.isHoldingAltModeDown) { + int sampledTint = this.edit.getTint(this.x, this.z); + String hexColor = ColorParseUtil.toHexString(sampledTint & 16777215); + InventoryComponent.Hotbar hotbar = componentAccessor.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + ItemStack itemStack = hotbar.getActiveItem(); + BuilderTool builderTool = BuilderTool.getActiveBuilderTool(player); + + try { + ItemStack newItemStack = builderTool.updateArgMetadata(itemStack, "bTintColor", hexColor); + hotbar.getInventory().setItemStackForSlot(hotbar.getActiveSlot(), newItemStack); + BuilderToolsPlugin.sendFeedback( + Message.translation("server.builderTools.pickedColor").param("color", hexColor), playerRef, NotificationStyle.Success, componentAccessor + ); + } catch (ToolArgException var12) { + playerRef.sendMessage(Message.translation("server.builderTools.tintOperation.colorParseError").param("value", hexColor)); + } + } else { + String colorText = (String)this.args.tool().getOrDefault("bTintColor", "ffffff"); + + try { + this.tintColor = ColorParseUtil.hexStringToRGBInt(colorText); + } catch (NumberFormatException var13) { + playerRef.sendMessage(Message.translation("server.builderTools.tintOperation.colorParseError").param("value", colorText)); + throw var13; + } + + this.opacity = ((Integer)this.args.tool().getOrDefault("cOpacity", 0)).intValue() / 100.0; + UUIDComponent uuidComponent = ref.getStore().getComponent(ref, UUIDComponent.getComponentType()); + PrototypePlayerBuilderToolSettings prototypeSettings = PROTOTYPE_TOOL_SETTINGS.get(uuidComponent.getUuid()); + if (!packet.isHoldDownInteraction) { + prototypeSettings.getIgnoredPaintOperations().clear(); + } + + this.packedPlacedTinsPositions = prototypeSettings.addIgnoredPaintOperation(); + + for (LongOpenHashSet previousSet : prototypeSettings.getIgnoredPaintOperations()) { + if (previousSet != this.packedPlacedTinsPositions) { + this.packedPlacedTinsPositions.addAll(previousSet); + } + } + + if (this.blendMode) { + int bufferSize = (this.shapeRange + 4) * 2 + 1; + this.bufferOriginX = this.x - this.shapeRange - 4; + this.bufferOriginZ = this.z - this.shapeRange - 4; + this.colorBuffer = new int[bufferSize][bufferSize]; + + for (int bufferX = 0; bufferX < bufferSize; bufferX++) { + for (int bufferZ = 0; bufferZ < bufferSize; bufferZ++) { + this.colorBuffer[bufferX][bufferZ] = this.edit.getTint(this.bufferOriginX + bufferX, this.bufferOriginZ + bufferZ); + } + } + } } } - @Override - public void execute(ComponentAccessor componentAccessor) { - this.builderState.tint(this.x, this.y, this.z, this.tintColor, this.shape, this.shapeRange, this.shapeHeight, componentAccessor); - } - - @Override - public void executeAt(int posX, int posY, int posZ, ComponentAccessor componentAccessor) { - this.builderState.tint(posX, posY, posZ, this.tintColor, this.shape, this.shapeRange, this.shapeHeight, componentAccessor); - } - @Override boolean execute0(int x, int y, int z) { - return true; + if (this.isHoldingAltModeDown && this.blendMode) { + return true; + } else { + long packed = BlockUtil.pack(x, 0, z); + if (this.packedPlacedTinsPositions.contains(packed)) { + return true; + } else { + this.packedPlacedTinsPositions.add(packed); + if (this.blendMode) { + int targetColor = this.sampleKernelBlend(x, z); + this.edit.setTint(x, z, targetColor, 0.0); + } else { + this.edit.setTint(x, z, this.tintColor, this.opacity); + } + + return true; + } + } + } + + private int sampleKernelBlend(int x, int z) { + double totalWeight = 0.0; + double r = 0.0; + double g = 0.0; + double b = 0.0; + + for (int deltaX = -4; deltaX <= 4; deltaX++) { + for (int deltaZ = -4; deltaZ <= 4; deltaZ++) { + double dist = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ); + if (!(dist > 4.0)) { + int bufferX = x + deltaX - this.bufferOriginX; + int bufferZ = z + deltaZ - this.bufferOriginZ; + if (bufferX >= 0 && bufferZ >= 0 && bufferX < this.colorBuffer.length && bufferZ < this.colorBuffer[0].length) { + double sigma = 2.0; + double weight = Math.exp(-(dist * dist) / (2.0 * sigma * sigma)); + int color = this.colorBuffer[bufferX][bufferZ]; + r += (color >> 16 & 0xFF) * weight; + g += (color >> 8 & 0xFF) * weight; + b += (color & 0xFF) * weight; + totalWeight += weight; + } + } + } + } + + return totalWeight == 0.0 + ? this.tintColor + : (int)Math.round(r / totalWeight) << 16 | (int)Math.round(g / totalWeight) << 8 | (int)Math.round(b / totalWeight); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/ToolOperation.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/ToolOperation.java index e5401b14..c3483e13 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/ToolOperation.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/ToolOperation.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.builtin.buildertools.tooloperations.transform.Transfor import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.function.predicate.TriIntObjPredicate; +import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.block.BlockConeUtil; import com.hypixel.hytale.math.block.BlockCubeUtil; import com.hypixel.hytale.math.block.BlockCylinderUtil; @@ -18,13 +19,12 @@ import com.hypixel.hytale.math.block.BlockInvertedDomeUtil; import com.hypixel.hytale.math.block.BlockPyramidUtil; import com.hypixel.hytale.math.block.BlockSphereUtil; import com.hypixel.hytale.math.block.BlockTorusUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; +import com.hypixel.hytale.protocol.Rotation; import com.hypixel.hytale.protocol.packets.buildertools.BrushAxis; import com.hypixel.hytale.protocol.packets.buildertools.BrushOrigin; import com.hypixel.hytale.protocol.packets.buildertools.BrushShape; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.BrushData; import com.hypixel.hytale.server.core.asset.type.buildertool.config.BuilderTool; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -37,15 +37,20 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public abstract class ToolOperation implements TriIntObjPredicate { + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); protected static final int RANDOM_MAX = 100; @Nonnull public static final Map OPERATIONS = new ConcurrentHashMap<>(); @@ -53,6 +58,7 @@ public abstract class ToolOperation implements TriIntObjPredicate { public static final Map PROTOTYPE_TOOL_SETTINGS = new ConcurrentHashMap<>(); public static final double MAX_DISTANCE = 400.0; public static final int DEFAULT_BRUSH_SPACING = 0; + private static final Pattern NEWLINES_PATTERN = Pattern.compile("\\r?\\n"); protected final int x; protected final int y; protected final int z; @@ -66,6 +72,8 @@ public abstract class ToolOperation implements TriIntObjPredicate { protected final int originOffsetZ; protected final BrushShape shape; protected final BlockPattern pattern; + protected final int density; + protected final int spacing; @Nonnull protected final EditOperation edit; @Nonnull @@ -75,16 +83,21 @@ public abstract class ToolOperation implements TriIntObjPredicate { @Nonnull protected final Player player; @Nonnull - protected final Ref playerRef; + protected final PlayerRef playerRef; + @Nonnull + protected final Ref playerEntityRef; @Nonnull protected final BuilderToolsPlugin.BuilderState builderState; - private final Transform transform; + private Transform transform; private final Vector3i vector = new Vector3i(); + protected int currentCenterX; + protected int currentCenterY; + protected int currentCenterZ; @Nullable private final BlockMask mask; public ToolOperation(@Nonnull Ref ref, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor) { - this.playerRef = ref; + this.playerEntityRef = ref; World world = componentAccessor.getExternalData().getWorld(); Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); @@ -95,6 +108,7 @@ public abstract class ToolOperation implements TriIntObjPredicate { assert playerRefComponent != null; this.player = playerComponent; + this.playerRef = playerRefComponent; this.builderState = BuilderToolsPlugin.getState(playerComponent, playerRefComponent); UUIDComponent uuidComponent = componentAccessor.getComponent(ref, UUIDComponent.getComponentType()); @@ -114,7 +128,23 @@ public abstract class ToolOperation implements TriIntObjPredicate { playerBuilderToolSettings.clearLastBrushPosition(); } + BuilderTool builderTool = BuilderTool.getActiveBuilderTool(playerComponent); + BuilderTool.ArgData args = this.args = builderTool.getItemArgData(playerComponent.getInventory().getItemInHand()); + Object width = args.tool().get("builtin_Width"); + Object height = args.tool().get("builtin_Height"); + Object thickness = args.tool().get("builtin_Thickness"); + Object capped = args.tool().get("builtin_Capped"); + Object shape = args.tool().get("builtin_Shape"); + Object density = args.tool().get("builtin_Density"); + Object spacing = args.tool().get("builtin_Spacing"); + Object material = args.tool().get("builtin_Material"); + int localShapeRange = width != null ? (Integer)width : 5; + int localShapeHeight = height != null ? (Integer)height : 5; + boolean evenW = localShapeRange % 2 == 0; + boolean evenH = localShapeHeight % 2 == 0; + int[] snapAxes = getRotatedEvenSnapAxes(args.tool().get("builtin_RotationFace") instanceof String s ? s : "Up", evenW, evenH); if (packet.isDoServerRaytraceForPosition && (this instanceof PaintOperation || this instanceof SculptOperation)) { + Vector3d hitPosition = new Vector3d(); Vector3i targetBlockAvoidingPaint = this.getTargetBlockAvoidingPaint( ref, 400.0, @@ -124,12 +154,34 @@ public abstract class ToolOperation implements TriIntObjPredicate { packet.raycastOriginZ, packet.raycastDirectionX, packet.raycastDirectionY, - packet.raycastDirectionZ + packet.raycastDirectionZ, + hitPosition ); if (targetBlockAvoidingPaint != null) { - this.x = targetBlockAvoidingPaint.x + packet.offsetForPaintModeX; - this.y = targetBlockAvoidingPaint.y + packet.offsetForPaintModeY; - this.z = targetBlockAvoidingPaint.z + packet.offsetForPaintModeZ; + int bufferX = targetBlockAvoidingPaint.x + packet.offsetForPaintModeX; + int bufferY = targetBlockAvoidingPaint.y + packet.offsetForPaintModeY; + int bufferZ = targetBlockAvoidingPaint.z + packet.offsetForPaintModeZ; + if (snapAxes[0] > 0 && hitPosition.x - targetBlockAvoidingPaint.x >= 0.5) { + bufferX++; + } else if (snapAxes[0] < 0 && hitPosition.x - targetBlockAvoidingPaint.x < 0.5) { + bufferX--; + } + + if (snapAxes[1] > 0 && hitPosition.y - targetBlockAvoidingPaint.y >= 0.5) { + bufferY++; + } else if (snapAxes[1] < 0 && hitPosition.y - targetBlockAvoidingPaint.y < 0.5) { + bufferY--; + } + + if (snapAxes[2] > 0 && hitPosition.z - targetBlockAvoidingPaint.z >= 0.5) { + bufferZ++; + } else if (snapAxes[2] < 0 && hitPosition.z - targetBlockAvoidingPaint.z < 0.5) { + bufferZ--; + } + + this.x = bufferX; + this.y = bufferY; + this.z = bufferZ; } else { this.x = packet.x; this.y = packet.y; @@ -142,28 +194,40 @@ public abstract class ToolOperation implements TriIntObjPredicate { } this.interactionType = packet.type; - BuilderTool builderTool = BuilderTool.getActiveBuilderTool(playerComponent); - BuilderTool.ArgData args = this.args = builderTool.getItemArgData(playerComponent.getInventory().getItemInHand()); - BrushData.Values brush = args.brush(); - if (brush == null) { - brush = new BrushData.Values(BrushData.DEFAULT); + this.transform = getTransform(ref, args, this.vector, componentAccessor); + this.shapeRange = localShapeRange; + this.shapeHeight = localShapeHeight; + this.shapeThickness = thickness != null ? (Integer)thickness : 0; + this.capped = capped != null ? (Boolean)capped : false; + this.shape = shape != null ? BrushShape.valueOf((String)shape) : BrushShape.Sphere; + this.density = density != null ? (Integer)density : 100; + this.spacing = spacing != null ? (Integer)spacing : 0; + this.pattern = this.getPattern(packet, material != null ? (BlockPattern)material : BlockPattern.EMPTY); + this.mask = combineMasks(args, this.builderState.getGlobalMask()); + Object origin = args.tool().get("builtin_Origin"); + Object rotateOrigin = args.tool().get("builtin_OriginRotation"); + BrushOrigin shapeOrigin = origin != null ? BrushOrigin.valueOf((String)origin) : BrushOrigin.Center; + boolean originRotation = rotateOrigin != null ? (Boolean)rotateOrigin : false; + int effectiveWidth = this.shapeRange; + int effectiveHeight = this.shapeHeight; + if (this.shape == BrushShape.Torus) { + int outerRadius = this.shapeRange / 2; + int minorRadius = Math.max(1, this.shapeHeight / 4); + int majorRadius = Math.max(1, outerRadius - minorRadius); + int sizeXZ = majorRadius + minorRadius; + effectiveWidth = this.shapeRange % 2 == 0 ? sizeXZ * 2 : sizeXZ * 2 + 1; + effectiveHeight = this.shapeHeight % 2 == 0 ? minorRadius * 2 : minorRadius * 2 + 1; } - this.transform = getTransform(ref, brush, this.vector, componentAccessor); - this.shapeRange = brush.getWidth(); - this.shapeHeight = brush.getHeight(); - this.shapeThickness = brush.getThickness(); - this.capped = brush.isCapped(); - this.shape = brush.getShape(); - this.pattern = this.getPattern(packet, brush); - this.mask = combineMasks(brush, this.builderState.getGlobalMask()); - BrushOrigin shapeOrigin = brush.getOrigin(); - boolean originRotation = brush.getOriginRotation(); - Vector3i offsets = getOffsets(this.shapeRange, this.shapeHeight, originRotation, shapeOrigin, this.transform, this.vector, true); - this.originOffsetX = offsets.getX(); - this.originOffsetY = offsets.getY(); - this.originOffsetZ = offsets.getZ(); + Transform brushRot = this.getBrushRotation(componentAccessor); + Vector3i offsets = getOffsets(effectiveWidth, effectiveHeight, originRotation, shapeOrigin, this.transform, brushRot, this.vector); + this.originOffsetX = offsets.x(); + this.originOffsetY = offsets.y(); + this.originOffsetZ = offsets.z(); this.random = this.builderState.getRandom(); + this.currentCenterX = this.x; + this.currentCenterY = this.y; + this.currentCenterZ = this.z; Vector3i brushMin = new Vector3i(this.x - this.shapeRange, this.y - this.shapeHeight, this.z - this.shapeRange); Vector3i brushMax = new Vector3i(this.x + this.shapeRange, this.y + this.shapeHeight, this.z + this.shapeRange); this.edit = new EditOperation(world, this.x, this.y, this.z, this.shapeRange, brushMin, brushMax, this.mask); @@ -189,9 +253,9 @@ public abstract class ToolOperation implements TriIntObjPredicate { positions.add(currentPosition); return positions; } else { - double dx = currentPosition.getX() - lastPosition.getX(); - double dy = currentPosition.getY() - lastPosition.getY(); - double dz = currentPosition.getZ() - lastPosition.getZ(); + double dx = currentPosition.x() - lastPosition.x(); + double dy = currentPosition.y() - lastPosition.y(); + double dz = currentPosition.z() - lastPosition.z(); double distance = Math.sqrt(dx * dx + dy * dy + dz * dz); if (brushSpacing == 0) { float maxBrushDimension = Math.max(brushWidth, brushHeight); @@ -205,9 +269,9 @@ public abstract class ToolOperation implements TriIntObjPredicate { for (int i = 1; i <= steps; i++) { float t = (float)i / steps; - int interpX = (int)Math.round(lastPosition.getX() + dx * t); - int interpY = (int)Math.round(lastPosition.getY() + dy * t); - int interpZ = (int)Math.round(lastPosition.getZ() + dz * t); + int interpX = (int)Math.round(lastPosition.x() + dx * t); + int interpY = (int)Math.round(lastPosition.y() + dy * t); + int interpZ = (int)Math.round(lastPosition.z() + dz * t); positions.add(new Vector3i(interpX, interpY, interpZ)); } } else if (distance >= brushSpacing) { @@ -232,12 +296,34 @@ public abstract class ToolOperation implements TriIntObjPredicate { } public int getBrushSpacing() { - Object spacingValue = this.args.tool().get("BrushSpacing"); + Object spacingValue = this.args.tool().get("builtin_BrushSpacing"); return spacingValue instanceof Number ? ((Number)spacingValue).intValue() : 0; } - public int getBrushDensity() { - return this.args.tool().get("BrushDensity") instanceof Number number ? number.intValue() : 100; + public Transform getBrushRotation(ComponentAccessor componentAccessor) { + Object rotationValue = this.args.tool().get("builtin_RotationFace"); + Transform transform = Transform.NONE; + if (rotationValue instanceof String rotationSelection) { + if (rotationSelection.equalsIgnoreCase("down")) { + transform = Rotate.forAxisAndAngle(BrushAxis.X, Rotation.OneEighty); + } else if (rotationSelection.equalsIgnoreCase("north")) { + transform = Rotate.forAxisAndAngle(BrushAxis.X, Rotation.TwoSeventy); + } else if (rotationSelection.equalsIgnoreCase("south")) { + transform = Rotate.forAxisAndAngle(BrushAxis.X, Rotation.Ninety); + } else if (rotationSelection.equalsIgnoreCase("east")) { + transform = Rotate.forAxisAndAngle(BrushAxis.Z, Rotation.TwoSeventy); + } else if (rotationSelection.equalsIgnoreCase("west")) { + transform = Rotate.forAxisAndAngle(BrushAxis.Z, Rotation.Ninety); + } else if (rotationSelection.equalsIgnoreCase("camera")) { + HeadRotation headRotationComponent = componentAccessor.getComponent(this.playerEntityRef, HeadRotation.getComponentType()); + + assert headRotationComponent != null; + + transform = Rotate.forDirection(headRotationComponent.getAxisDirection(), Rotation.None); + } + } + + return transform; } public void executeAsBrushConfig( @@ -246,27 +332,30 @@ public abstract class ToolOperation implements TriIntObjPredicate { ComponentAccessor componentAccessor ) { World world = componentAccessor.getExternalData().getWorld(); + prototypePlayerBuilderToolSettings.setUndoGroupSize(packet.undoGroupSize); prototypePlayerBuilderToolSettings.getBrushConfigCommandExecutor() - .execute(this.playerRef, world, new Vector3i(this.x, this.y, this.z), packet.isHoldDownInteraction, packet.type, bc -> { + .execute(this.playerEntityRef, world, new Vector3i(this.x, this.y, this.z), packet.isHoldDownInteraction, packet.type, bc -> { bc.setPattern(this.pattern); - bc.setDensity(this.getBrushDensity()); + bc.setDensity(this.density); bc.setShapeHeight(this.shapeHeight); bc.setShapeWidth(this.shapeRange); bc.setShape(this.shape); bc.setCapped(this.capped); + bc.setTransform(this.getBrushRotation(componentAccessor)); + bc.setTransformOrigin(new Vector3i(this.x, this.y, this.z)); bc.modifyOriginOffset(new Vector3i(this.originOffsetX, this.originOffsetY, this.originOffsetZ)); bc.setBrushMask(this.mask); bc.setShapeThickness(this.shapeThickness); }, componentAccessor); } - private BlockPattern getPattern(@Nonnull BuilderToolOnUseInteraction packet, @Nonnull BrushData.Values brush) { + private BlockPattern getPattern(@Nonnull BuilderToolOnUseInteraction packet, BlockPattern pattern) { if (packet.type == InteractionType.Primary) { return BlockPattern.EMPTY; } else { - return (this instanceof PaintOperation || this instanceof PaintOperation) && brush.getMaterial().equals(BlockPattern.EMPTY) + return (this instanceof PaintOperation || this instanceof PaintOperation) && pattern.equals(BlockPattern.EMPTY) ? BlockPattern.parse("Rock_Stone") - : brush.getMaterial(); + : pattern; } } @@ -281,6 +370,24 @@ public abstract class ToolOperation implements TriIntObjPredicate { float raycastDirectionX, float raycastDirectionY, float raycastDirectionZ + ) { + return this.getTargetBlockAvoidingPaint( + ref, maxDistance, componentAccessor, raycastOriginX, raycastOriginY, raycastOriginZ, raycastDirectionX, raycastDirectionY, raycastDirectionZ, null + ); + } + + @Nullable + public Vector3i getTargetBlockAvoidingPaint( + @Nonnull Ref ref, + double maxDistance, + @Nonnull ComponentAccessor componentAccessor, + float raycastOriginX, + float raycastOriginY, + float raycastOriginZ, + float raycastDirectionX, + float raycastDirectionY, + float raycastDirectionZ, + @Nullable Vector3d hitPositionOut ) { World world = componentAccessor.getExternalData().getWorld(); UUIDComponent uuidComponent = componentAccessor.getComponent(ref, UUIDComponent.getComponentType()); @@ -288,20 +395,8 @@ public abstract class ToolOperation implements TriIntObjPredicate { assert uuidComponent != null; PrototypePlayerBuilderToolSettings prototypePlayerBuilderToolSettings = PROTOTYPE_TOOL_SETTINGS.get(uuidComponent.getUuid()); - return prototypePlayerBuilderToolSettings != null && !prototypePlayerBuilderToolSettings.getIgnoredPaintOperations().isEmpty() - ? TargetUtil.getTargetBlockAvoidLocations( - world, - blockId -> blockId != 0, - raycastOriginX, - raycastOriginY, - raycastOriginZ, - raycastDirectionX, - raycastDirectionY, - raycastDirectionZ, - maxDistance, - prototypePlayerBuilderToolSettings.getIgnoredPaintOperations() - ) - : TargetUtil.getTargetBlock( + if (prototypePlayerBuilderToolSettings == null || prototypePlayerBuilderToolSettings.getIgnoredPaintOperations().isEmpty()) { + Vector3i blockPos = TargetUtil.getTargetBlock( world, (blockId, _fluidId) -> blockId != 0, raycastOriginX, @@ -312,6 +407,58 @@ public abstract class ToolOperation implements TriIntObjPredicate { raycastDirectionZ, maxDistance ); + if (blockPos == null) { + return null; + } else { + if (hitPositionOut != null) { + Vector3d hitLocation = TargetUtil.getTargetLocation( + world, + blockId -> blockId != 0, + raycastOriginX, + raycastOriginY, + raycastOriginZ, + raycastDirectionX, + raycastDirectionY, + raycastDirectionZ, + maxDistance + ); + if (hitLocation != null) { + hitPositionOut.x = hitLocation.x; + hitPositionOut.y = hitLocation.y; + hitPositionOut.z = hitLocation.z; + } + } + + return blockPos; + } + } else { + return hitPositionOut != null + ? TargetUtil.getTargetBlockAvoidLocations( + world, + blockId -> blockId != 0, + raycastOriginX, + raycastOriginY, + raycastOriginZ, + raycastDirectionX, + raycastDirectionY, + raycastDirectionZ, + maxDistance, + prototypePlayerBuilderToolSettings.getIgnoredPaintOperations(), + hitPositionOut + ) + : TargetUtil.getTargetBlockAvoidLocations( + world, + blockId -> blockId != 0, + raycastOriginX, + raycastOriginY, + raycastOriginZ, + raycastDirectionX, + raycastDirectionY, + raycastDirectionZ, + maxDistance, + prototypePlayerBuilderToolSettings.getIgnoredPaintOperations() + ); + } } @Nonnull @@ -321,17 +468,21 @@ public abstract class ToolOperation implements TriIntObjPredicate { public final boolean test(int x, int y, int z, Void aVoid) { if (this.transform == Transform.NONE) { - return this.execute0(x, y + this.originOffsetY, z); + return this.execute0(x + this.originOffsetX, y + this.originOffsetY, z + this.originOffsetZ); } else { - this.vector.assign(x - this.x, y - this.y, z - this.z); + this.vector.set(x - this.currentCenterX, y - this.currentCenterY, z - this.currentCenterZ); this.transform.apply(this.vector); - x = this.x + this.originOffsetX + this.vector.x; - y = this.y + this.originOffsetY + this.vector.y; - z = this.z + this.originOffsetZ + this.vector.z; + x = this.currentCenterX + this.originOffsetX + this.vector.x; + y = this.currentCenterY + this.originOffsetY + this.vector.y; + z = this.currentCenterZ + this.originOffsetZ + this.vector.z; return this.execute0(x, y, z); } } + public boolean showEditNotification() { + return true; + } + abstract boolean execute0(int var1, int var2, int var3); public void execute(ComponentAccessor componentAccessor) { @@ -339,6 +490,9 @@ public abstract class ToolOperation implements TriIntObjPredicate { } public void executeAt(int posX, int posY, int posZ, ComponentAccessor componentAccessor) { + this.currentCenterX = posX; + this.currentCenterY = posY; + this.currentCenterZ = posZ; executeShapeOperation(posX, posY, posZ, this, this.shape, this.shapeRange, this.shapeHeight, this.shapeThickness, this.capped); } @@ -353,102 +507,234 @@ public abstract class ToolOperation implements TriIntObjPredicate { int shapeThickness, boolean capped ) { - if (shapeRange <= 1 && shapeHeight <= 1) { + if (shapeRange <= 1 && shapeHeight <= 1 && shape != BrushShape.Torus) { operation.test(x, y, z, null); } else { - int radiusXZ = Math.max(shapeRange / 2, 1); - int halfHeight = Math.max(shapeHeight / 2, 1); + int radiusXZ = shapeRange / 2; + int halfHeight = shapeHeight / 2; + boolean evenXZ = shapeRange % 2 == 0; + boolean evenY = shapeHeight % 2 == 0; + if (radiusXZ < 1 && shape != BrushShape.Torus) { + BlockCubeUtil.forEachBlock(x, y, z, 0, shapeHeight, 0, false, evenY, null, operation); + return; + } + switch (shape) { case Cube: default: - BlockCubeUtil.forEachBlock(x, y, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, null, operation); + BlockCubeUtil.forEachBlock(x, y, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, capped, false, evenXZ, evenY, null, operation); break; case Sphere: - BlockSphereUtil.forEachBlock(x, y, z, radiusXZ, halfHeight, radiusXZ, shapeThickness, null, operation); + if (halfHeight < 1) { + BlockCylinderUtil.forEachBlock(x, y, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, evenXZ, false, null, operation); + } else { + BlockSphereUtil.forEachBlock(x, y, z, radiusXZ, halfHeight, radiusXZ, shapeThickness, evenXZ, evenY, null, operation); + } break; case Cylinder: - BlockCylinderUtil.forEachBlock(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, null, operation); + BlockCylinderUtil.forEachBlock(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, evenXZ, evenY, null, operation); break; case Cone: - BlockConeUtil.forEachBlock(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, null, operation); + BlockConeUtil.forEachBlock(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, evenXZ, evenY, null, operation); break; case InvertedCone: - BlockConeUtil.forEachBlockInverted(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, null, operation); + BlockConeUtil.forEachBlockInverted(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, evenXZ, evenY, null, operation); break; case Pyramid: - BlockPyramidUtil.forEachBlock(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, null, operation); + BlockPyramidUtil.forEachBlock(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, evenXZ, evenY, null, operation); break; case InvertedPyramid: - BlockPyramidUtil.forEachBlockInverted(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, null, operation); + BlockPyramidUtil.forEachBlockInverted( + x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, evenXZ, evenY, null, operation + ); break; case Dome: - BlockDomeUtil.forEachBlock(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, null, operation); + BlockDomeUtil.forEachBlock(x, y - halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, evenXZ, evenY, null, operation); break; case InvertedDome: - BlockInvertedDomeUtil.forEachBlock(x, y + halfHeight, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, null, operation); + BlockInvertedDomeUtil.forEachBlock( + x, y + (shapeHeight - 1) / 2, z, radiusXZ, shapeHeight, radiusXZ, shapeThickness, capped, evenXZ, evenY, null, operation + ); break; case Diamond: - BlockDiamondUtil.forEachBlock(x, y, z, radiusXZ, shapeHeight / 2, radiusXZ, shapeThickness, capped, null, operation); + BlockDiamondUtil.forEachBlock(x, y, z, radiusXZ, shapeHeight / 2, radiusXZ, shapeThickness, capped, evenXZ, evenY, null, operation); break; case Torus: int minorRadius = Math.max(1, shapeHeight / 4); - BlockTorusUtil.forEachBlock(x, y, z, radiusXZ, minorRadius, shapeThickness, capped, null, operation); + int outerRadius = Math.max(1, radiusXZ); + BlockTorusUtil.forEachBlock(x, y, z, outerRadius, minorRadius, shapeThickness, capped, evenXZ, evenY, null, operation); } } } @Nonnull private static Vector3i getOffsets( - int width, int height, boolean originRotation, BrushOrigin origin, @Nonnull Transform transform, @Nonnull Vector3i vector, boolean applyBottomOriginFix + int width, + int height, + boolean originRotation, + BrushOrigin origin, + @Nonnull Transform transform, + @Nonnull Transform brushRotation, + @Nonnull Vector3i vector ) { int offsetY = height / 2; int offsetXZ = originRotation ? width / 2 : 0; - vector.assign(0, offsetY, 0); - transform.apply(vector); - int ox = vector.getX(); - int oz = vector.getZ(); - vector.assign(offsetXZ, offsetY, -offsetXZ); - transform.apply(vector); - int oy = vector.getY(); - ox = origin == BrushOrigin.Center ? 0 : (origin == BrushOrigin.Bottom ? ox : -ox); - oy = origin == BrushOrigin.Center ? 0 : (origin == BrushOrigin.Bottom ? oy + (applyBottomOriginFix ? 1 : 0) : -oy); - oz = origin == BrushOrigin.Center ? 0 : (origin == BrushOrigin.Bottom ? oz : -oz); - return vector.assign(ox, oy, oz); + if (origin != BrushOrigin.Lowest && origin != BrushOrigin.Highest) { + vector.set(0, offsetY, 0); + transform.apply(vector); + int offsetXTransformed = vector.x(); + int offsetZTransformed = vector.z(); + vector.set(offsetXZ, offsetY, -offsetXZ); + transform.apply(vector); + int offsetYTransformed = vector.y(); + offsetXTransformed = origin == BrushOrigin.Center ? 0 : (origin == BrushOrigin.Bottom ? offsetXTransformed : -offsetXTransformed); + offsetYTransformed = origin == BrushOrigin.Center ? 0 : (origin == BrushOrigin.Bottom ? offsetYTransformed : -offsetYTransformed); + offsetZTransformed = origin == BrushOrigin.Center ? 0 : (origin == BrushOrigin.Bottom ? offsetZTransformed : -offsetZTransformed); + return vector.set(offsetXTransformed, offsetYTransformed, offsetZTransformed); + } else { + int halfW = width / 2; + int posW = (width - 1) / 2; + int minY = Integer.MAX_VALUE; + int maxY = Integer.MIN_VALUE; + + for (int sx = -1; sx <= 1; sx += 2) { + for (int sy = -1; sy <= 1; sy += 2) { + for (int sz = -1; sz <= 1; sz += 2) { + vector.set(sx < 0 ? -halfW : posW, sy < 0 ? -offsetY : offsetY, sz < 0 ? -halfW : posW); + brushRotation.apply(vector); + minY = Math.min(minY, vector.y()); + maxY = Math.max(maxY, vector.y()); + } + } + } + + int worldOy = origin == BrushOrigin.Lowest ? -minY : -maxY; + vector.set(0, worldOy, 0); + if (brushRotation instanceof Rotate rotate) { + rotate.inverse().apply(vector); + } + + return vector; + } + } + + private static int[] getRotatedEvenSnapAxes(String rotationFace, boolean evenW, boolean evenH) { + int[] width = new int[]{1, 0, 0}; + int[] height = new int[]{0, 1, 0}; + int[] depth = new int[]{0, 0, 1}; + String i = rotationFace.toLowerCase(); + int count; + int axis; + switch (i) { + case "down": + axis = 0; + count = 2; + break; + case "north": + axis = 0; + count = 3; + break; + case "south": + axis = 0; + count = 1; + break; + case "east": + axis = 2; + count = 3; + break; + case "west": + axis = 2; + count = 1; + break; + default: + axis = 0; + count = 0; + } + + for (int i = 0; i < count; i++) { + rotate90(width, axis); + rotate90(height, axis); + rotate90(depth, axis); + } + + return new int[]{ + evenSnapSign(evenW, width[0], depth[0], evenH, height[0]), + evenSnapSign(evenW, width[1], depth[1], evenH, height[1]), + evenSnapSign(evenW, width[2], depth[2], evenH, height[2]) + }; + } + + private static void rotate90(int[] v, int axis) { + switch (axis) { + case 0: { + int tmp = v[1]; + v[1] = -v[2]; + v[2] = tmp; + break; + } + case 1: { + int tmp = v[0]; + v[0] = v[2]; + v[2] = -tmp; + break; + } + case 2: { + int tmp = v[0]; + v[0] = -v[1]; + v[1] = tmp; + } + } + } + + private static int evenSnapSign(boolean evenW, int wComp, int dComp, boolean evenH, int hComp) { + if (evenW && wComp != 0) { + return wComp; + } else if (evenW && dComp != 0) { + return dComp; + } else { + return evenH && hComp != 0 ? hComp : 0; + } } private static Transform getTransform( - @Nonnull Ref ref, @Nonnull BrushData.Values brushData, @Nonnull Vector3i vector, @Nonnull ComponentAccessor componentAccessor + @Nonnull Ref ref, @Nonnull BuilderTool.ArgData args, @Nonnull Vector3i vector, @Nonnull ComponentAccessor componentAccessor ) { - Transform rotate = getRotation(ref, brushData, vector, componentAccessor); - Transform mirror = getMirror(ref, brushData, vector, componentAccessor); + Transform rotate = getRotation(ref, args, vector, componentAccessor); + Transform mirror = getMirror(ref, args, vector, componentAccessor); return rotate.then(mirror); } private static Transform getRotation( - @Nonnull Ref ref, @Nonnull BrushData.Values brushData, @Nonnull Vector3i vector, @Nonnull ComponentAccessor componentAccessor + @Nonnull Ref ref, @Nonnull BuilderTool.ArgData args, @Nonnull Vector3i vector, @Nonnull ComponentAccessor componentAccessor ) { - if (brushData.getRotationAxis() == BrushAxis.Auto) { + Object axis = args.tool().get("builtin_RotationAxis"); + Object angle = args.tool().get("builtin_RotationAngle"); + BrushAxis rotationAxis = axis != null ? BrushAxis.valueOf((String)axis) : BrushAxis.None; + Rotation rotationAngle = angle != null ? Rotation.valueOf((String)angle) : Rotation.None; + if (rotationAxis == BrushAxis.Auto) { HeadRotation headRotationComponent = componentAccessor.getComponent(ref, HeadRotation.getComponentType()); assert headRotationComponent != null; - return Rotate.forDirection(headRotationComponent.getAxisDirection(vector), brushData.getRotationAngle()); + return Rotate.forDirection(headRotationComponent.getAxisDirection(vector), rotationAngle); } else { - return Rotate.forAxisAndAngle(brushData.getRotationAxis(), brushData.getRotationAngle()); + return Rotate.forAxisAndAngle(rotationAxis, rotationAngle); } } private static Transform getMirror( - @Nonnull Ref ref, @Nonnull BrushData.Values brushData, @Nonnull Vector3i vector, @Nonnull ComponentAccessor componentAccessor + @Nonnull Ref ref, @Nonnull BuilderTool.ArgData args, @Nonnull Vector3i vector, @Nonnull ComponentAccessor componentAccessor ) { - if (brushData.getMirrorAxis() == BrushAxis.Auto) { + Object axis = args.tool().get("builtin_MirrorAxis"); + BrushAxis mirrorAxis = axis != null ? BrushAxis.valueOf((String)axis) : BrushAxis.None; + if (mirrorAxis == BrushAxis.Auto) { HeadRotation headRotationComponent = componentAccessor.getComponent(ref, HeadRotation.getComponentType()); assert headRotationComponent != null; return Mirror.forDirection(headRotationComponent.getAxisDirection(vector), false); } else { - return Mirror.forAxis(brushData.getMirrorAxis()); + return Mirror.forAxis(mirrorAxis); } } @@ -456,6 +742,7 @@ public abstract class ToolOperation implements TriIntObjPredicate { public static ToolOperation fromPacket( @Nonnull Ref ref, @Nonnull Player player, + @Nonnull PlayerRef playerRef, @Nonnull BuilderToolOnUseInteraction packet, @Nonnull ComponentAccessor componentAccessor ) throws Exception { @@ -468,36 +755,81 @@ public abstract class ToolOperation implements TriIntObjPredicate { if (factory == null) { throw new Exception("No tool found matching id " + toolType); } else { - return factory.create(ref, player, packet, componentAccessor); + return factory.create(ref, player, playerRef, packet, componentAccessor); } } } @Nullable - public static BlockMask combineMasks(@Nullable BrushData.Values brush, @Nullable BlockMask globalMask) { - if (brush == null) { + public static BlockMask combineMasks(@Nullable BuilderTool.ArgData args, @Nullable BlockMask globalMask) { + if (args == null) { return globalMask; - } else if (brush.shouldUseMaskCommands()) { - BlockMask mask = BlockMask.combine(brush.getParsedMaskCommands()); - if (mask != null) { - mask.setInverted(brush.shouldInvertMask()); - } - - return mask; } else { - BlockMask brushMaskAbove = brush.getMaskAbove().withOptions(BlockFilter.FilterType.AboveBlock, false); - BlockMask brushMaskNot = brush.getMaskNot().withOptions(BlockFilter.FilterType.TargetBlock, true); - BlockMask brushMaskBelow = brush.getMaskBelow().withOptions(BlockFilter.FilterType.BelowBlock, false); - BlockMask brushMaskAdjacent = brush.getMaskAdjacent().withOptions(BlockFilter.FilterType.AdjacentBlock, false); - BlockMask brushMaskNeighbor = brush.getMaskNeighbor().withOptions(BlockFilter.FilterType.NeighborBlock, false); - BlockMask combinedMask = BlockMask.combine( - brush.getMask(), brushMaskAbove, brushMaskNot, brushMaskBelow, brushMaskAdjacent, brushMaskNeighbor, globalMask - ); - if (combinedMask != null) { - combinedMask.setInverted(brush.shouldInvertMask()); - } + Object useMaskCommands = args.tool().get("builtin_UseMaskCommands"); + boolean useBrushMaskCommands = useMaskCommands != null ? (Boolean)useMaskCommands : false; + Object invertMask = args.tool().get("builtin_InvertMask"); + boolean brushInvertMask = invertMask != null ? (Boolean)invertMask : false; + if (useBrushMaskCommands) { + String maskCommands = args.tool().get("builtin_MaskCommands") != null ? (String)args.tool().get("builtin_MaskCommands") : ""; + String[] commands = NEWLINES_PATTERN.split(maskCommands); + BlockMask[] parsedMaskCommands = Arrays.stream(commands).map(m -> m.split(" ")).map(BlockMask::parse).toArray(BlockMask[]::new); + BlockMask mask = BlockMask.combine(parsedMaskCommands); + if (mask != null) { + mask.setInverted(brushInvertMask); + } - return combinedMask; + return mask; + } else { + Object mask = args.tool().get("builtin_Mask"); + Object maskAbove = args.tool().get("builtin_MaskAbove"); + Object maskNot = args.tool().get("builtin_MaskNot"); + Object maskBelow = args.tool().get("builtin_MaskBelow"); + Object maskAdjacent = args.tool().get("builtin_MaskAdjacent"); + Object maskNeighbor = args.tool().get("builtin_MaskNeighbor"); + BlockMask brushMask = BlockMask.EMPTY; + BlockMask brushMaskAbove = BlockMask.EMPTY; + BlockMask brushMaskNot = BlockMask.EMPTY; + BlockMask brushMaskBelow = BlockMask.EMPTY; + BlockMask brushMaskAdjacent = BlockMask.EMPTY; + BlockMask brushMaskNeighbor = BlockMask.EMPTY; + if (mask != null) { + brushMask = (BlockMask)mask; + } + + if (maskAbove != null) { + brushMaskAbove = (BlockMask)maskAbove; + brushMaskAbove = brushMaskAbove.withOptions(BlockFilter.FilterType.AboveBlock, false); + } + + if (maskNot != null) { + brushMaskNot = (BlockMask)maskNot; + brushMaskNot = brushMaskNot.withOptions(BlockFilter.FilterType.TargetBlock, true); + } + + if (maskBelow != null) { + brushMaskBelow = (BlockMask)maskBelow; + brushMaskBelow = brushMaskBelow.withOptions(BlockFilter.FilterType.BelowBlock, false); + } + + if (maskAdjacent != null) { + brushMaskAdjacent = (BlockMask)maskAdjacent; + brushMaskAdjacent = brushMaskAdjacent.withOptions(BlockFilter.FilterType.AdjacentBlock, false); + } + + if (maskNeighbor != null) { + brushMaskNeighbor = (BlockMask)maskNeighbor; + brushMaskNeighbor = brushMaskNeighbor.withOptions(BlockFilter.FilterType.NeighborBlock, false); + } + + BlockMask combinedMask = BlockMask.combine( + brushMask, brushMaskAbove, brushMaskNot, brushMaskBelow, brushMaskAdjacent, brushMaskNeighbor, globalMask + ); + if (combinedMask != null) { + combinedMask.setInverted(brushInvertMask); + } + + return combinedMask; + } } } @@ -505,11 +837,13 @@ public abstract class ToolOperation implements TriIntObjPredicate { OPERATIONS.put("Flood", FloodOperation::new); OPERATIONS.put("Noise", NoiseOperation::new); OPERATIONS.put("Scatter", ScatterOperation::new); - OPERATIONS.put("Smooth", (ref, player1, packet, componentAccessor) -> new SmoothOperation(ref, packet, componentAccessor)); + OPERATIONS.put("Smooth", (ref, player1, playerRef1, packet, componentAccessor) -> new SmoothOperation(ref, packet, componentAccessor)); OPERATIONS.put("Tint", TintOperation::new); OPERATIONS.put("Paint", PaintOperation::new); OPERATIONS.put("Sculpt", SculptOperation::new); OPERATIONS.put("Layers", LayersOperation::new); OPERATIONS.put("LaserPointer", LaserPointerOperation::new); + OPERATIONS.put("Revolve", RevolveOperation::new); + OPERATIONS.put("ScriptedBrushTemplate", PaintOperation::new); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Composite.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Composite.java index 17b49db4..9732c2df 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Composite.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Composite.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations.transform; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class Composite implements Transform { private final Transform first; diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Mirror.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Mirror.java index 80727d2f..fb760059 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Mirror.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Mirror.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations.transform; import com.hypixel.hytale.math.Axis; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.buildertools.BrushAxis; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class Mirror implements Transform { public static final Transform X = new Mirror(Axis.X); @@ -41,14 +41,14 @@ public class Mirror implements Transform { } public static Transform forDirection(@Nonnull Vector3i direction, boolean negativeY) { - if (direction.getX() != 0) { + if (direction.x() != 0) { return X; - } else if (direction.getZ() != 0) { + } else if (direction.z() != 0) { return Z; - } else if (direction.getY() > 0) { + } else if (direction.y() > 0) { return Y; } else { - return direction.getY() < 0 && negativeY ? Y : NONE; + return direction.y() < 0 && negativeY ? Y : NONE; } } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Rotate.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Rotate.java index 7c3fca2d..1f9ac5a4 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Rotate.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Rotate.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations.transform; import com.hypixel.hytale.math.Axis; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.Rotation; import com.hypixel.hytale.protocol.packets.buildertools.BrushAxis; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class Rotate implements Transform { public static final Transform X_90 = new Rotate(Axis.X, 90); @@ -45,6 +45,10 @@ public class Rotate implements Transform { } } + public Rotate inverse() { + return new Rotate(this.axis, (4 - this.rotations) * 90); + } + @Nonnull @Override public String toString() { @@ -52,14 +56,14 @@ public class Rotate implements Transform { } public static Transform forDirection(@Nonnull Vector3i direction, Rotation angle) { - if (direction.getX() < 0) { + if (direction.x() < 0) { return selectRotation(angle, FACING_WEST, FACING_NORTH, FACING_EAST, FACING_SOUTH); - } else if (direction.getX() > 0) { + } else if (direction.x() > 0) { return selectRotation(angle, FACING_EAST, FACING_SOUTH, FACING_WEST, FACING_NORTH); - } else if (direction.getZ() < 0) { + } else if (direction.z() < 0) { return selectRotation(angle, FACING_NORTH, FACING_EAST, FACING_SOUTH, FACING_WEST); } else { - return direction.getZ() > 0 ? selectRotation(angle, FACING_SOUTH, FACING_WEST, FACING_NORTH, FACING_EAST) : NONE; + return direction.z() > 0 ? selectRotation(angle, FACING_SOUTH, FACING_WEST, FACING_NORTH, FACING_EAST) : NONE; } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Transform.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Transform.java index 3924fbc6..35bb7700 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Transform.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Transform.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations.transform; -import com.hypixel.hytale.math.vector.Vector3i; +import org.joml.Vector3i; public interface Transform { Transform NONE = vec -> {}; diff --git a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Translate.java b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Translate.java index 7eb8f5f8..b89a972a 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Translate.java +++ b/src/com/hypixel/hytale/builtin/buildertools/tooloperations/transform/Translate.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.buildertools.tooloperations.transform; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class Translate implements Transform { private final int x; @@ -27,7 +27,7 @@ public class Translate implements Transform { @Nonnull public static Transform of(@Nonnull Vector3i vector) { - return of(vector.getX(), vector.getY(), vector.getZ()); + return of(vector.x(), vector.y(), vector.z()); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/buildertools/utils/Material.java b/src/com/hypixel/hytale/builtin/buildertools/utils/Material.java index 71afd34e..5e90dff4 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/utils/Material.java +++ b/src/com/hypixel/hytale/builtin/buildertools/utils/Material.java @@ -1,25 +1,35 @@ package com.hypixel.hytale.builtin.buildertools.utils; +import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockPattern; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import java.util.Objects; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; public final class Material { - public static final Material EMPTY = new Material(0, 0, (byte)0, 0); + public static final Material EMPTY = new Material(0, 0, (byte)0, 0, 0, 0, null); private final int blockId; private final int fluidId; private final byte fluidLevel; private final int rotation; + private final int support; + private final int filler; + @Nullable + private final Holder holder; - private Material(int blockId, int fluidId, byte fluidLevel, int rotation) { + private Material(int blockId, int fluidId, byte fluidLevel, int rotation, int support, int filler, @Nullable Holder holder) { this.blockId = blockId; this.fluidId = fluidId; this.fluidLevel = fluidLevel; this.rotation = rotation; + this.support = support; + this.filler = filler; + this.holder = holder; } @Nonnull @@ -29,12 +39,22 @@ public final class Material { @Nonnull public static Material block(int blockId, int rotation) { - return blockId == 0 ? EMPTY : new Material(blockId, 0, (byte)0, rotation); + return blockId == 0 ? EMPTY : new Material(blockId, 0, (byte)0, rotation, 0, 0, null); } @Nonnull public static Material fluid(int fluidId, byte fluidLevel) { - return fluidId == 0 ? EMPTY : new Material(0, fluidId, fluidLevel, 0); + return fluidId == 0 ? EMPTY : new Material(0, fluidId, fluidLevel, 0, 0, 0, null); + } + + @Nonnull + public static Material full(int blockId, int rotation, int support, int filler, @Nullable Holder holder) { + return blockId == 0 ? EMPTY : new Material(blockId, 0, (byte)0, rotation, support, filler, holder); + } + + @Nonnull + public static Material full(int blockId, int rotation, int support, int filler, @Nullable Holder holder, int fluidId, byte fluidLevel) { + return new Material(blockId, fluidId, fluidLevel, rotation, support, filler, holder); } @Nullable @@ -97,6 +117,19 @@ public final class Material { return this.rotation != 0; } + public int getSupport() { + return this.support; + } + + public int getFiller() { + return this.filler; + } + + @Nullable + public Holder getHolder() { + return this.holder; + } + @Override public String toString() { if (this.isEmpty()) { @@ -118,13 +151,20 @@ public final class Material { } else { return !(obj instanceof Material other) ? false - : this.blockId == other.blockId && this.fluidId == other.fluidId && this.fluidLevel == other.fluidLevel && this.rotation == other.rotation; + : this.blockId == other.blockId + && this.fluidId == other.fluidId + && this.fluidLevel == other.fluidLevel + && this.rotation == other.rotation + && this.support == other.support + && this.filler == other.filler + && Objects.equals(this.holder, other.holder); } } @Override public int hashCode() { - return 31 * (31 * (31 * this.blockId + this.fluidId) + this.fluidLevel) + this.rotation; + int result = 31 * (31 * (31 * this.blockId + this.fluidId) + this.fluidLevel) + this.rotation; + return 31 * (31 * (31 * result + this.support) + this.filler) + Objects.hashCode(this.holder); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/buildertools/utils/PasteToolUtil.java b/src/com/hypixel/hytale/builtin/buildertools/utils/PasteToolUtil.java index 2b3e3678..db16c342 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/utils/PasteToolUtil.java +++ b/src/com/hypixel/hytale/builtin/buildertools/utils/PasteToolUtil.java @@ -1,11 +1,14 @@ package com.hypixel.hytale.builtin.buildertools.utils; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.packets.inventory.SetActiveSlot; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; public final class PasteToolUtil { @@ -14,7 +17,9 @@ public final class PasteToolUtil { private PasteToolUtil() { } - public static void switchToPasteTool(@Nonnull Player player, @Nonnull PlayerRef playerRef) { + public static void switchToPasteTool( + @Nonnull Ref ref, @Nonnull Player player, @Nonnull PlayerRef playerRef, @Nonnull ComponentAccessor componentAccessor + ) { Inventory inventory = player.getInventory(); ItemContainer hotbar = inventory.getHotbar(); ItemContainer storage = inventory.getStorage(); @@ -24,7 +29,7 @@ public final class PasteToolUtil { for (short slot = 0; slot < hotbarSize; slot++) { ItemStack itemStack = hotbar.getItemStack(slot); if (itemStack != null && !itemStack.isEmpty() && "EditorTool_Paste".equals(itemStack.getItemId())) { - inventory.setActiveHotbarSlot((byte)slot); + inventory.setActiveHotbarSlot(ref, (byte)slot, componentAccessor); playerRef.getPacketHandler().writeNoCache(new SetActiveSlot(-1, (byte)slot)); return; } @@ -45,7 +50,7 @@ public final class PasteToolUtil { ItemStack itemStack = storage.getItemStack(slotxx); if (itemStack != null && !itemStack.isEmpty() && "EditorTool_Paste".equals(itemStack.getItemId())) { storage.moveItemStackFromSlotToSlot(slotxx, 1, hotbar, emptySlot); - inventory.setActiveHotbarSlot((byte)emptySlot); + inventory.setActiveHotbarSlot(ref, (byte)emptySlot, componentAccessor); playerRef.getPacketHandler().writeNoCache(new SetActiveSlot(-1, (byte)emptySlot)); return; } @@ -63,7 +68,7 @@ public final class PasteToolUtil { if (pasteToolStack != null) { hotbar.setItemStackForSlot(emptySlot, new ItemStack(pasteToolStack.getItemId())); - inventory.setActiveHotbarSlot((byte)emptySlot); + inventory.setActiveHotbarSlot(ref, (byte)emptySlot, componentAccessor); playerRef.getPacketHandler().writeNoCache(new SetActiveSlot(-1, (byte)emptySlot)); } } diff --git a/src/com/hypixel/hytale/builtin/buildertools/utils/RecursivePrefabLoader.java b/src/com/hypixel/hytale/builtin/buildertools/utils/RecursivePrefabLoader.java index 9eeaa064..3a3151fa 100644 --- a/src/com/hypixel/hytale/builtin/buildertools/utils/RecursivePrefabLoader.java +++ b/src/com/hypixel/hytale/builtin/buildertools/utils/RecursivePrefabLoader.java @@ -4,13 +4,12 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; -import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerState; +import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerBlock; import com.hypixel.hytale.server.core.prefab.PrefabLoadException; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import com.hypixel.hytale.server.core.prefab.PrefabWeights; import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabLoader; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.io.IOException; @@ -32,7 +31,7 @@ public abstract class RecursivePrefabLoader implements BiFunction prefabsLoader; protected final Set visitedFiles = new HashSet<>(); @Nullable - protected final ComponentType prefabComponentType = BlockStateModule.get().getComponentType(PrefabSpawnerState.class); + protected final ComponentType prefabComponentType = PrefabSpawnerBlock.getComponentType(); private int depthTracker = 0; public RecursivePrefabLoader(Path rootPrefabsDir, Function prefabsLoader) { @@ -141,7 +140,7 @@ public abstract class RecursivePrefabLoader implements BiFunction { Holder state = block.holder(); if (state != null) { - PrefabSpawnerState spawner = state.getComponent(this.prefabComponentType); + PrefabSpawnerBlock spawner = state.getComponent(this.prefabComponentType); if (spawner != null) { BlockType blockType = BlockType.getAssetMap().getAsset(block.blockId()); int childX = x + rotation.getX(dx, dz); diff --git a/src/com/hypixel/hytale/builtin/commandmacro/MacroCommandBase.java b/src/com/hypixel/hytale/builtin/commandmacro/MacroCommandBase.java index ec895e06..ecbdfd36 100644 --- a/src/com/hypixel/hytale/builtin/commandmacro/MacroCommandBase.java +++ b/src/com/hypixel/hytale/builtin/commandmacro/MacroCommandBase.java @@ -134,7 +134,7 @@ public class MacroCommandBase extends AbstractAsyncCommand { List commandsToExecute = new ObjectArrayList<>(); CommandSender commandSender = context.sender(); String macro = context.getCalledCommand().getName(); - LOGGER.at(Level.INFO).log("%s expanding command macro: %s", commandSender.getDisplayName(), macro); + LOGGER.at(Level.INFO).log("%s expanding command macro: %s", commandSender.getUsername(), macro); for (Pair> stringListPair : this.commandReplacements) { String command = stringListPair.key(); diff --git a/src/com/hypixel/hytale/builtin/crafting/CraftingPlugin.java b/src/com/hypixel/hytale/builtin/crafting/CraftingPlugin.java index e77b1bba..f1a0b3bd 100644 --- a/src/com/hypixel/hytale/builtin/crafting/CraftingPlugin.java +++ b/src/com/hypixel/hytale/builtin/crafting/CraftingPlugin.java @@ -5,14 +5,16 @@ import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent; import com.hypixel.hytale.assetstore.event.RemovedAssetsEvent; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.builtin.crafting.commands.RecipeCommand; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; import com.hypixel.hytale.builtin.crafting.component.CraftingManager; +import com.hypixel.hytale.builtin.crafting.component.ProcessingBenchBlock; import com.hypixel.hytale.builtin.crafting.interaction.LearnRecipeInteraction; import com.hypixel.hytale.builtin.crafting.interaction.OpenBenchPageInteraction; import com.hypixel.hytale.builtin.crafting.interaction.OpenProcessingBenchInteraction; -import com.hypixel.hytale.builtin.crafting.state.BenchState; -import com.hypixel.hytale.builtin.crafting.state.ProcessingBenchState; +import com.hypixel.hytale.builtin.crafting.system.BenchSystems; import com.hypixel.hytale.builtin.crafting.system.PlayerCraftingSystems; import com.hypixel.hytale.builtin.crafting.window.FieldCraftingWindow; +import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.common.util.ArrayUtil; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Archetype; @@ -20,9 +22,11 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentRegistryProxy; import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.data.unknown.UnknownComponents; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.event.EventRegistry; @@ -41,12 +45,13 @@ import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerConfigDa import com.hypixel.hytale.server.core.entity.entities.player.windows.Window; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.MaterialQuantity; +import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateRegistry; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; @@ -57,6 +62,7 @@ import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.bson.BsonDocument; public class CraftingPlugin extends JavaPlugin { private static CraftingPlugin instance; @@ -65,6 +71,8 @@ public class CraftingPlugin extends JavaPlugin { @Nonnull private static final Map itemGeneratedRecipes = new Object2ObjectOpenHashMap<>(); private ComponentType craftingManagerComponentType; + private ComponentType benchBlockComponentType; + private ComponentType processingBenchBlockComponentType; public CraftingPlugin(@Nonnull JavaPluginInit init) { super(init); @@ -77,17 +85,17 @@ public class CraftingPlugin extends JavaPlugin { return benchRecipeRegistry == null ? null : benchRecipeRegistry.getRecipesForCategory(benchCategoryId); } - public static boolean isValidCraftingMaterialForBench(@Nonnull BenchState benchState, @Nonnull ItemStack itemStack) { - BenchRecipeRegistry benchRecipeRegistry = registries.get(benchState.getBench().getId()); + public static boolean isValidCraftingMaterialForBench(@Nonnull Bench bench, @Nonnull ItemStack itemStack) { + BenchRecipeRegistry benchRecipeRegistry = registries.get(bench.getId()); return benchRecipeRegistry == null ? false : benchRecipeRegistry.isValidCraftingMaterial(itemStack); } - public static boolean isValidUpgradeMaterialForBench(@Nonnull BenchState benchState, @Nonnull ItemStack itemStack) { - BenchUpgradeRequirement nextLevelUpgradeMaterials = benchState.getNextLevelUpgradeMaterials(); - if (nextLevelUpgradeMaterials == null) { + public static boolean isValidUpgradeMaterialForBench(@Nonnull Bench bench, int tierLevel, @Nonnull ItemStack itemStack) { + BenchUpgradeRequirement upgradeRequirement = bench.getUpgradeRequirement(tierLevel); + if (upgradeRequirement == null) { return false; } else { - for (MaterialQuantity upgradeMaterial : nextLevelUpgradeMaterials.getInput()) { + for (MaterialQuantity upgradeMaterial : upgradeRequirement.getInput()) { if (itemStack.getItemId().equals(upgradeMaterial.getItemId())) { return true; } @@ -129,9 +137,14 @@ public class CraftingPlugin extends JavaPlugin { Bench.registerRootInteraction(BenchType.Crafting, OpenBenchPageInteraction.SIMPLE_CRAFTING_ROOT); Bench.registerRootInteraction(BenchType.DiagramCrafting, OpenBenchPageInteraction.DIAGRAM_CRAFTING_ROOT); Bench.registerRootInteraction(BenchType.StructuralCrafting, OpenBenchPageInteraction.STRUCTURAL_CRAFTING_ROOT); - BlockStateRegistry blockStateRegistry = this.getBlockStateRegistry(); - blockStateRegistry.registerBlockState(ProcessingBenchState.class, "processingBench", ProcessingBenchState.CODEC); - blockStateRegistry.registerBlockState(BenchState.class, "crafting", BenchState.CODEC); + this.benchBlockComponentType = this.getChunkStoreRegistry().registerComponent(BenchBlock.class, "BenchBlock", BenchBlock.CODEC); + this.processingBenchBlockComponentType = this.getChunkStoreRegistry() + .registerComponent(ProcessingBenchBlock.class, "ProcessingBenchBlock", ProcessingBenchBlock.CODEC); + this.getChunkStoreRegistry().registerSystem(new CraftingPlugin.MigrateCrafting()); + this.getChunkStoreRegistry().registerSystem(new BenchSystems.OnAddOrRemoved()); + this.getChunkStoreRegistry().registerSystem(new BenchSystems.ProcessingBenchTick(this.processingBenchBlockComponentType, this.benchBlockComponentType)); + this.getChunkStoreRegistry() + .registerSystem(new BenchSystems.ProcessingBenchLifecycle(this.processingBenchBlockComponentType, this.benchBlockComponentType)); Window.CLIENT_REQUESTABLE_WINDOW_TYPES.put(WindowType.PocketCrafting, FieldCraftingWindow::new); eventRegistry.register(LoadedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeLoad); eventRegistry.register(RemovedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeRemove); @@ -323,10 +336,60 @@ public class CraftingPlugin extends JavaPlugin { return this.craftingManagerComponentType; } + public ComponentType getBenchBlockComponentType() { + return this.benchBlockComponentType; + } + + public ComponentType getProcessingBenchBlockComponentType() { + return this.processingBenchBlockComponentType; + } + public static CraftingPlugin get() { return instance; } + public static class MigrateCrafting extends BlockModule.MigrationSystem { + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + UnknownComponents unknownComponents = holder.getComponent(ChunkStore.REGISTRY.getUnknownComponentType()); + + assert unknownComponents != null; + + Map unknown = unknownComponents.getUnknownComponents(); + if (unknown.containsKey("crafting")) { + BenchBlock benchBlock = unknownComponents.removeComponent("crafting", BenchBlock.CODEC); + + assert benchBlock != null; + + holder.putComponent(BenchBlock.getComponentType(), benchBlock); + } else if (unknown.containsKey("processingBench")) { + BsonDocument bsonDocument = unknown.remove("processingBench"); + ExtraInfo extraInfo = ExtraInfo.THREAD_LOCAL.get(); + BenchBlock benchBlock = BenchBlock.CODEC.decode(bsonDocument, extraInfo); + extraInfo.getValidationResults().logOrThrowValidatorExceptions(CraftingPlugin.get().getLogger()); + ProcessingBenchBlock processingBenchBlock = ProcessingBenchBlock.CODEC.decode(bsonDocument, extraInfo); + extraInfo.getValidationResults().logOrThrowValidatorExceptions(CraftingPlugin.get().getLogger()); + + assert processingBenchBlock != null; + + assert benchBlock != null; + + holder.putComponent(ProcessingBenchBlock.getComponentType(), processingBenchBlock); + holder.putComponent(BenchBlock.getComponentType(), benchBlock); + } + } + + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } + + @Nullable + @Override + public Query getQuery() { + return ChunkStore.REGISTRY.getUnknownComponentType(); + } + } + public static class PlayerAddedSystem extends RefSystem { @Nonnull private final Query query; diff --git a/src/com/hypixel/hytale/builtin/crafting/component/BenchBlock.java b/src/com/hypixel/hytale/builtin/crafting/component/BenchBlock.java new file mode 100644 index 00000000..eae83e22 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/crafting/component/BenchBlock.java @@ -0,0 +1,108 @@ +package com.hypixel.hytale.builtin.crafting.component; + +import com.hypixel.hytale.builtin.crafting.CraftingPlugin; +import com.hypixel.hytale.builtin.crafting.window.BenchWindow; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.component.Component; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class BenchBlock implements Component { + @Nonnull + public static BuilderCodec CODEC = BuilderCodec.builder(BenchBlock.class, BenchBlock::new) + .appendInherited( + new KeyedCodec<>("TierLevel", Codec.INTEGER), + (state, o) -> state.tierLevel = o, + state -> state.tierLevel, + (state, parent) -> state.tierLevel = parent.tierLevel + ) + .add() + .appendInherited( + new KeyedCodec<>("UpgradeItems", new ArrayCodec<>(ItemStack.CODEC, ItemStack[]::new)), + (state, o) -> state.upgradeItems = o, + state -> state.upgradeItems, + (state, parent) -> state.upgradeItems = parent.upgradeItems + ) + .add() + .build(); + private int tierLevel = 1; + protected ItemStack[] upgradeItems = ItemStack.EMPTY_ARRAY; + @Nonnull + protected final transient Map windows = new ConcurrentHashMap<>(); + + public static ComponentType getComponentType() { + return CraftingPlugin.get().getBenchBlockComponentType(); + } + + public BenchBlock() { + } + + public BenchBlock(int tierLevel, ItemStack[] upgradeItems) { + this.tierLevel = tierLevel; + this.upgradeItems = upgradeItems; + } + + public void addUpgradeItems(@Nonnull List consumed) { + consumed.addAll(Arrays.asList(this.upgradeItems)); + this.upgradeItems = consumed.toArray(ItemStack[]::new); + } + + public void setTierLevel(int newTierLevel) { + this.tierLevel = newTierLevel; + } + + public int getTierLevel() { + return this.tierLevel; + } + + public ItemStack[] getUpgradeItems() { + return this.upgradeItems; + } + + public void setUpgradeItems(ItemStack[] upgradeItems) { + this.upgradeItems = upgradeItems; + } + + @Nonnull + public String getTierStateName() { + return this.tierLevel > 1 ? "Tier" + this.tierLevel : "default"; + } + + @Nonnull + public Map getWindows() { + return this.windows; + } + + @Nullable + @Override + public Component clone() { + return new BenchBlock(this.tierLevel, this.upgradeItems); + } + + @Nonnull + public static BlockType getBaseBlockType(@Nonnull BlockType currentBlockType) { + String baseBlockKey = currentBlockType.getDefaultStateKey(); + if (baseBlockKey == null) { + return currentBlockType; + } else { + BlockType baseBlockType = BlockType.getAssetMap().getAsset(baseBlockKey); + if (baseBlockType == null) { + baseBlockType = currentBlockType; + } + + return baseBlockType; + } + } +} diff --git a/src/com/hypixel/hytale/builtin/crafting/component/CraftingManager.java b/src/com/hypixel/hytale/builtin/crafting/component/CraftingManager.java index aaa9155a..2c01942a 100644 --- a/src/com/hypixel/hytale/builtin/crafting/component/CraftingManager.java +++ b/src/com/hypixel/hytale/builtin/crafting/component/CraftingManager.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.builtin.crafting.component; import com.google.gson.JsonArray; import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin; import com.hypixel.hytale.builtin.crafting.CraftingPlugin; -import com.hypixel.hytale.builtin.crafting.state.BenchState; import com.hypixel.hytale.builtin.crafting.window.BenchWindow; import com.hypixel.hytale.builtin.crafting.window.CraftingWindow; import com.hypixel.hytale.component.Component; @@ -15,7 +14,7 @@ import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.event.IEventDispatcher; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.protocol.BenchRequirement; import com.hypixel.hytale.protocol.BenchType; import com.hypixel.hytale.protocol.GameMode; @@ -38,6 +37,7 @@ import com.hypixel.hytale.server.core.entity.entities.player.windows.MaterialExt import com.hypixel.hytale.server.core.event.events.ecs.CraftRecipeEvent; import com.hypixel.hytale.server.core.event.events.player.PlayerCraftEvent; import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.MaterialQuantity; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; @@ -49,13 +49,15 @@ import com.hypixel.hytale.server.core.inventory.container.filter.FilterType; import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction; import com.hypixel.hytale.server.core.inventory.transaction.MaterialSlotTransaction; import com.hypixel.hytale.server.core.inventory.transaction.MaterialTransaction; +import com.hypixel.hytale.server.core.modules.block.BlockModule; +import com.hypixel.hytale.server.core.modules.block.components.ItemContainerBlock; import com.hypixel.hytale.server.core.modules.entity.player.PlayerSettings; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerState; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.NotificationUtil; @@ -74,6 +76,7 @@ import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bson.BsonDocument; +import org.joml.Vector3d; public class CraftingManager implements Component { @Nonnull @@ -203,12 +206,17 @@ public class CraftingManager implements Component { @Nullable private static String getRecipeOutputTranslationKey(@Nonnull CraftingRecipe recipe) { - String itemId = recipe.getPrimaryOutput().getItemId(); - if (itemId == null) { + MaterialQuantity primaryOutput = recipe.getPrimaryOutput(); + if (primaryOutput == null) { return null; } else { - Item itemAsset = Item.getAssetMap().getAsset(itemId); - return itemAsset != null ? itemAsset.getTranslationKey() : null; + String itemId = primaryOutput.getItemId(); + if (itemId == null) { + return null; + } else { + Item itemAsset = Item.getAssetMap().getAsset(itemId); + return itemAsset != null ? itemAsset.getTranslationKey() : null; + } } } @@ -229,24 +237,40 @@ public class CraftingManager implements Component { if (!this.isValidBenchForRecipe(ref, componentAccessor, recipe)) { return false; } else { - float recipeTime = recipe.getTimeSeconds(); - if (recipeTime > 0.0F) { - int level = this.getBenchTierLevel(componentAccessor); - if (level > 1) { - BenchTierLevel tierLevelData = this.getBenchTierLevelData(level); - if (tierLevelData != null) { - recipeTime -= recipeTime * tierLevelData.getCraftingTimeReductionModifier(); + CraftRecipeEvent.Pre preEvent = new CraftRecipeEvent.Pre(recipe, quantity); + componentAccessor.invoke(ref, preEvent); + if (preEvent.isCancelled()) { + return false; + } else { + float recipeTime = recipe.getTimeSeconds(); + if (recipeTime > 0.0F) { + int level = this.getBenchTierLevel(componentAccessor); + if (level > 1) { + BenchTierLevel tierLevelData = this.getBenchTierLevelData(level); + if (tierLevelData != null) { + recipeTime -= recipeTime * tierLevelData.getCraftingTimeReductionModifier(); + } } } - } - this.queuedCraftingJobs - .offer(new CraftingManager.CraftingJob(window, transactionId, recipe, quantity, recipeTime, inputItemContainer, inputRemovalType)); - return true; + this.queuedCraftingJobs + .offer(new CraftingManager.CraftingJob(window, transactionId, recipe, quantity, recipeTime, inputItemContainer, inputRemovalType)); + return true; + } } } } + public int getRemainingQueueSize() { + int total = 0; + + for (CraftingManager.CraftingJob job : this.queuedCraftingJobs) { + total += job.quantity - job.quantityCompleted; + } + + return total; + } + public void tick(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor, float dt) { if (this.upgradingJob != null) { if (dt > 0.0F) { @@ -318,8 +342,13 @@ public class CraftingManager implements Component { int currentCompletedItemId = currentJob.quantityCompleted++; currentJob.timeSecondsCompleted = 0.0F; LOGGER.at(Level.FINE).log("Crafted 1 Quantity: %s", currentJob); + CraftRecipeEvent.Post postEvent = new CraftRecipeEvent.Post(currentJob.recipe, currentJob.quantity); + componentAccessor.invoke(ref, postEvent); if (currentJob.quantityCompleted == currentJob.quantity) { - giveOutput(ref, componentAccessor, currentJob, currentCompletedItemId); + if (!postEvent.isCancelled()) { + giveOutput(ref, componentAccessor, currentJob, currentCompletedItemId); + } + LOGGER.at(Level.FINE).log("Crafting Finished: %s", currentJob); this.queuedCraftingJobs.poll(); } else { @@ -328,9 +357,12 @@ public class CraftingManager implements Component { throw new RuntimeException("QuantityCompleted is greater than the Quality! " + currentJob); } - giveOutput(ref, componentAccessor, currentJob, currentCompletedItemId); + if (!postEvent.isCancelled()) { + giveOutput(ref, componentAccessor, currentJob, currentCompletedItemId); + } } + currentJob.window.updateQueueSize(this.getRemainingQueueSize()); if (this.queuedCraftingJobs.isEmpty()) { currentJob.window.setBlockInteractionState("default", componentAccessor.getExternalData().getWorld()); } @@ -362,7 +394,8 @@ public class CraftingManager implements Component { assert playerComponent != null; PlayerConfigData playerConfigData = playerComponent.getPlayerConfigData(); - String primaryOutputItemId = recipe.getPrimaryOutput() != null ? recipe.getPrimaryOutput().getItemId() : null; + MaterialQuantity primaryOutput = recipe.getPrimaryOutput(); + String primaryOutputItemId = primaryOutput != null ? primaryOutput.getItemId() : null; if (!recipe.isKnowledgeRequired() || primaryOutputItemId != null && playerConfigData.getKnownRecipes().contains(primaryOutputItemId)) { World world = componentAccessor.getExternalData().getWorld(); if (recipe.getRequiredMemoriesLevel() > 1 && MemoriesPlugin.get().getMemoriesLevel(world.getGameplayConfig()) < recipe.getRequiredMemoriesLevel()) { @@ -372,8 +405,7 @@ public class CraftingManager implements Component { BenchType benchType = this.blockType != null ? this.blockType.getBench().getType() : BenchType.Crafting; String benchName = this.blockType != null ? this.blockType.getBench().getId() : "Fieldcraft"; boolean meetsRequirements = false; - BlockState state = world.getState(this.x, this.y, this.z, true); - int benchTierLevel = state instanceof BenchState ? ((BenchState)state).getTierLevel() : 0; + int benchTierLevel = this.getBenchTierLevel(componentAccessor); BenchRequirement[] requirements = recipe.getBenchRequirement(); if (requirements != null) { for (BenchRequirement benchRequirement : requirements) { @@ -422,7 +454,6 @@ public class CraftingManager implements Component { LOGGER.at(Level.WARNING).log("Attempted to give output to a non-player entity: %s", ref); } else { List itemStacks = getOutputItemStacks(craftingRecipe, quantity); - Inventory inventory = playerComponent.getInventory(); PlayerSettings playerSettings = componentAccessor.getComponent(ref, PlayerSettings.getComponentType()); if (playerSettings == null) { playerSettings = PlayerSettings.defaults(); @@ -430,9 +461,8 @@ public class CraftingManager implements Component { for (ItemStack itemStack : itemStacks) { if (!ItemStack.isEmpty(itemStack)) { - SimpleItemContainer.addOrDropItemStack( - componentAccessor, ref, inventory.getContainerForItemPickup(itemStack.getItem(), playerSettings), itemStack - ); + ItemContainer containerForItemPickup = Inventory.getContainerForItemPickup(ref, itemStack.getItem(), playerSettings, componentAccessor); + SimpleItemContainer.addOrDropItemStack(componentAccessor, ref, containerForItemPickup, itemStack); } } } @@ -500,11 +530,8 @@ public class CraftingManager implements Component { Objects.requireNonNull(job, "Job can't be null!"); List itemStacks = job.removedItems.get(currentItemId); if (itemStacks != null) { - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, playerComponent.getInventory().getCombinedHotbarFirst(), itemStacks); + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); + SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, combinedInventory, itemStacks); } } @@ -652,8 +679,11 @@ public class CraftingManager implements Component { assert playerComponent != null; if (playerComponent.getGameMode() != GameMode.Creative) { + CombinedItemContainer combinedBackpackStorageHotbar = InventoryComponent.getCombined( + componentAccessor, ref, InventoryComponent.BACKPACK_STORAGE_HOTBAR + ); CombinedItemContainer combined = new CombinedItemContainer( - playerComponent.getInventory().getCombinedBackpackStorageHotbar(), window.getExtraResourcesSection().getItemContainer() + combinedBackpackStorageHotbar, window.getExtraResourcesSection().getItemContainer() ); if (!combined.canRemoveMaterials(input)) { return false; @@ -673,59 +703,101 @@ public class CraftingManager implements Component { return 0; } else { World world = componentAccessor.getExternalData().getWorld(); - BlockState state = world.getState(this.x, this.y, this.z, true); - BenchState benchState = state instanceof BenchState ? (BenchState)state : null; - if (benchState != null && benchState.getTierLevel() != 0) { - BenchUpgradeRequirement requirements = this.getBenchUpgradeRequirement(benchState.getTierLevel()); - if (requirements == null) { - return benchState.getTierLevel(); - } else { - List input = getInputMaterials(requirements.getInput()); - if (input.isEmpty()) { - return benchState.getTierLevel(); + ChunkStore chunkStore = world.getChunkStore(); + Ref chunk = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(this.x, this.z)); + if (chunk != null && chunk.isValid()) { + BlockComponentChunk blockComponentChunk = chunkStore.getStore().getComponent(chunk, BlockComponentChunk.getComponentType()); + + assert blockComponentChunk != null; + + BlockChunk blockChunk = chunkStore.getStore().getComponent(chunk, BlockChunk.getComponentType()); + + assert blockChunk != null; + + Ref blockEntityRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(this.x, this.y, this.z)); + if (blockEntityRef != null && blockEntityRef.isValid()) { + BlockModule.BlockStateInfo blockStateInfo = chunkStore.getStore().getComponent(blockEntityRef, BlockModule.BlockStateInfo.getComponentType()); + if (blockStateInfo == null) { + return 0; } else { - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); + BenchBlock benchBlock = chunkStore.getStore().getComponent(blockEntityRef, BenchBlock.getComponentType()); + if (benchBlock != null && benchBlock.getTierLevel() != 0) { + BenchUpgradeRequirement requirements = this.getBenchUpgradeRequirement(benchBlock.getTierLevel()); + if (requirements == null) { + return benchBlock.getTierLevel(); + } else { + List input = getInputMaterials(requirements.getInput()); + if (input.isEmpty()) { + return benchBlock.getTierLevel(); + } else { + Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - assert playerComponent != null; + assert playerComponent != null; - boolean canUpgrade = playerComponent.getGameMode() == GameMode.Creative; - if (!canUpgrade) { - CombinedItemContainer combined = new CombinedItemContainer( - playerComponent.getInventory().getCombinedBackpackStorageHotbar(), - this.upgradingJob.window.getExtraResourcesSection().getItemContainer() - ); - combined = new CombinedItemContainer(combined, this.upgradingJob.window.getExtraResourcesSection().getItemContainer()); - ListTransaction materialTransactions = combined.removeMaterials(input); - if (materialTransactions.succeeded()) { - List consumed = new ObjectArrayList<>(); + boolean canUpgrade = playerComponent.getGameMode() == GameMode.Creative; + if (!canUpgrade) { + CombinedItemContainer combinedBackpackStorageHotbar = InventoryComponent.getCombined( + componentAccessor, ref, InventoryComponent.BACKPACK_STORAGE_HOTBAR + ); + CombinedItemContainer combined = new CombinedItemContainer( + combinedBackpackStorageHotbar, this.upgradingJob.window.getExtraResourcesSection().getItemContainer() + ); + combined = new CombinedItemContainer(combined, this.upgradingJob.window.getExtraResourcesSection().getItemContainer()); + ListTransaction materialTransactions = combined.removeMaterials(input); + if (materialTransactions.succeeded()) { + List consumed = new ObjectArrayList<>(); - for (MaterialTransaction transaction : materialTransactions.getList()) { - for (MaterialSlotTransaction matSlot : transaction.getList()) { - consumed.add(matSlot.getOutput()); + for (MaterialTransaction transaction : materialTransactions.getList()) { + for (MaterialSlotTransaction matSlot : transaction.getList()) { + consumed.add(matSlot.getOutput()); + } + } + + benchBlock.addUpgradeItems(consumed); + blockStateInfo.markNeedsSaving(); + canUpgrade = true; + } } + + if (canUpgrade) { + benchBlock.setTierLevel(benchBlock.getTierLevel() + 1); + blockStateInfo.markNeedsSaving(); + BlockType baseBlockType = BenchBlock.getBaseBlockType(this.blockType); + WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(this.x, this.z)); + if (worldChunk != null) { + worldChunk.setBlockInteractionState(this.x, this.y, this.z, baseBlockType, benchBlock.getTierStateName(), true); + } + + ProcessingBenchBlock processingBlock = chunkStore.getStore() + .getComponent(blockEntityRef, ProcessingBenchBlock.getComponentType()); + if (processingBlock != null && worldChunk != null) { + int rotationIndex = worldChunk.getRotationIndex(this.x, this.y, this.z); + processingBlock.setupSlots(world, benchBlock, blockStateInfo, this.x, this.y, this.z, this.blockType, rotationIndex); + } + + int blockId = blockChunk.getBlock(this.x, this.y, this.z); + BlockType block = BlockType.getAssetMap().getAsset(blockId); + if (block != null && block.getBench().getBenchUpgradeCompletedSoundEventIndex() != 0) { + SoundUtil.playSoundEvent3d( + block.getBench().getBenchUpgradeCompletedSoundEventIndex(), + SoundCategory.SFX, + this.x + 0.5, + this.y + 0.5, + this.z + 0.5, + componentAccessor + ); + } + } + + return benchBlock.getTierLevel(); } - - benchState.addUpgradeItems(consumed); - canUpgrade = true; } + } else { + return 0; } - - if (canUpgrade) { - benchState.setTierLevel(benchState.getTierLevel() + 1); - if (benchState.getBench().getBenchUpgradeCompletedSoundEventIndex() != 0) { - SoundUtil.playSoundEvent3d( - benchState.getBench().getBenchUpgradeCompletedSoundEventIndex(), - SoundCategory.SFX, - this.x + 0.5, - this.y + 0.5, - this.z + 0.5, - componentAccessor - ); - } - } - - return benchState.getTierLevel(); } + } else { + return 0; } } else { return 0; @@ -751,14 +823,39 @@ public class CraftingManager implements Component { private int getBenchTierLevel(@Nonnull ComponentAccessor componentAccessor) { World world = componentAccessor.getExternalData().getWorld(); - BlockState state = world.getState(this.x, this.y, this.z, true); - return state instanceof BenchState ? ((BenchState)state).getTierLevel() : 0; + ChunkStore chunkStore = world.getChunkStore(); + Ref chunk = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(this.x, this.z)); + if (chunk != null && chunk.isValid()) { + BlockComponentChunk blockComponentChunk = chunkStore.getStore().getComponent(chunk, BlockComponentChunk.getComponentType()); + + assert blockComponentChunk != null; + + Ref blockEntityRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(this.x, this.y, this.z)); + if (blockEntityRef != null && blockEntityRef.isValid()) { + BenchBlock benchBlock = chunkStore.getStore().getComponent(blockEntityRef, BenchBlock.getComponentType()); + return benchBlock != null && benchBlock.getTierLevel() != 0 ? benchBlock.getTierLevel() : 0; + } else { + return 0; + } + } else { + return 0; + } } - public static int feedExtraResourcesSection(@Nonnull BenchState benchState, @Nonnull MaterialExtraResourcesSection extraResourcesSection) { - CraftingManager.ChestLookupResult result = getContainersAroundBench(benchState); + public static int feedExtraResourcesSection( + @Nonnull World world, + int x, + int y, + int z, + @Nonnull BlockType blockType, + int rotationIndex, + @Nonnull Bench benchAsset, + int tierLevel, + @Nonnull MaterialExtraResourcesSection extraResourcesSection + ) { + CraftingManager.ChestLookupResult result = getContainersAroundBench(world, x, y, z, blockType, rotationIndex); List chests = result.containers; - List chestStates = result.states; + List chestStates = result.states; ItemContainer itemContainer = EmptyItemContainer.INSTANCE; if (!chests.isEmpty()) { itemContainer = new CombinedItemContainer(chests.stream().map(container -> { @@ -771,12 +868,15 @@ public class CraftingManager implements Component { Map materials = new Object2ObjectOpenHashMap<>(); for (ItemContainer chest : chests) { - chest.forEach((i, itemStack) -> { - if (CraftingPlugin.isValidUpgradeMaterialForBench(benchState, itemStack) || CraftingPlugin.isValidCraftingMaterialForBench(benchState, itemStack)) { - ItemQuantity var10000 = materials.computeIfAbsent(itemStack.getItemId(), k -> new ItemQuantity(itemStack.getItemId(), 0)); - var10000.quantity = var10000.quantity + itemStack.getQuantity(); + chest.forEach( + (i, itemStack) -> { + if (CraftingPlugin.isValidUpgradeMaterialForBench(benchAsset, tierLevel, itemStack) + || CraftingPlugin.isValidCraftingMaterialForBench(benchAsset, itemStack)) { + ItemQuantity var10000 = materials.computeIfAbsent(itemStack.getItemId(), k -> new ItemQuantity(itemStack.getItemId(), 0)); + var10000.quantity = var10000.quantity + itemStack.getQuantity(); + } } - }); + ); } extraResourcesSection.setItemContainer(itemContainer); @@ -786,26 +886,26 @@ public class CraftingManager implements Component { } @Nonnull - protected static CraftingManager.ChestLookupResult getContainersAroundBench(@Nonnull BenchState benchState) { + protected static CraftingManager.ChestLookupResult getContainersAroundBench( + @Nonnull World world, int x, int y, int z, @Nonnull BlockType blockType, int rotationIndex + ) { List containers = new ObjectArrayList<>(); - List states = new ObjectArrayList<>(); - List spatialResults = new ObjectArrayList<>(); - List filteredOut = new ObjectArrayList<>(); - World world = benchState.getChunk().getWorld(); + List states = new ObjectArrayList<>(); + List filteredOut = new ObjectArrayList<>(); Store store = world.getChunkStore().getStore(); int limit = world.getGameplayConfig().getCraftingConfig().getBenchMaterialChestLimit(); double horizontalRadius = world.getGameplayConfig().getCraftingConfig().getBenchMaterialHorizontalChestSearchRadius(); double verticalRadius = world.getGameplayConfig().getCraftingConfig().getBenchMaterialVerticalChestSearchRadius(); - Vector3d blockPos = benchState.getBlockPosition().toVector3d(); - BlockBoundingBoxes hitboxAsset = BlockBoundingBoxes.getAssetMap().getAsset(benchState.getBlockType().getHitboxTypeIndex()); - BlockBoundingBoxes.RotatedVariantBoxes rotatedHitbox = hitboxAsset.get(benchState.getRotationIndex()); + Vector3d blockPos = new Vector3d(x, y, z); + BlockBoundingBoxes hitboxAsset = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); + BlockBoundingBoxes.RotatedVariantBoxes rotatedHitbox = hitboxAsset.get(rotationIndex); Box boundingBox = rotatedHitbox.getBoundingBox(); double benchWidth = boundingBox.width(); double benchHeight = boundingBox.height(); double benchDepth = boundingBox.depth(); double extraSearchRadius = Math.max(benchWidth, Math.max(benchDepth, benchHeight)) - 1.0; - SpatialResource, ChunkStore> blockStateSpatialStructure = store.getResource(BlockStateModule.get().getItemContainerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + SpatialResource, ChunkStore> blockStateSpatialStructure = store.getResource(BlockModule.get().getItemContainerSpatialResourceType()); + List> results = SpatialResource.getThreadLocalReferenceList(); blockStateSpatialStructure.getSpatialStructure() .ordered3DAxis(blockPos, horizontalRadius + extraSearchRadius, verticalRadius + extraSearchRadius, horizontalRadius + extraSearchRadius, results); if (!results.isEmpty()) { @@ -823,31 +923,33 @@ public class CraftingManager implements Component { double maxZ = blockPos.z + benchMaxBlockZ + horizontalRadius; for (Ref ref : results) { - if (BlockState.getBlockState(ref, ref.getStore()) instanceof ItemContainerState chest) { - spatialResults.add(chest); - } - } - - for (ItemContainerState chest : spatialResults) { - Vector3d chestBlockPos = chest.getBlockPosition().toVector3d(); - if (chestBlockPos.x >= minX - && chestBlockPos.x <= maxX - && chestBlockPos.y >= minY - && chestBlockPos.y <= maxY - && chestBlockPos.z >= minZ - && chestBlockPos.z <= maxZ) { - containers.add(chest.getItemContainer()); - states.add(chest); - if (containers.size() >= limit) { - break; + if (ref.isValid()) { + ItemContainerBlock chest = store.getComponent(ref, ItemContainerBlock.getComponentType()); + if (chest != null) { + BlockModule.BlockStateInfo blockStateInfo = store.getComponent(ref, BlockModule.BlockStateInfo.getComponentType()); + if (blockStateInfo != null) { + WorldChunk wc = store.getComponent(blockStateInfo.getChunkRef(), WorldChunk.getComponentType()); + if (wc != null) { + int cx = ChunkUtil.worldCoordFromLocalCoord(wc.getX(), ChunkUtil.xFromBlockInColumn(blockStateInfo.getIndex())); + int cy = ChunkUtil.yFromBlockInColumn(blockStateInfo.getIndex()); + int cz = ChunkUtil.worldCoordFromLocalCoord(wc.getZ(), ChunkUtil.zFromBlockInColumn(blockStateInfo.getIndex())); + if (cx >= minX && cx <= maxX && cy >= minY && cy <= maxY && cz >= minZ && cz <= maxZ) { + containers.add(chest.getItemContainer()); + states.add(chest); + if (containers.size() >= limit) { + break; + } + } else { + filteredOut.add(chest); + } + } + } } - } else { - filteredOut.add(chest); } } } - return new CraftingManager.ChestLookupResult(containers, states, spatialResults, filteredOut, blockPos); + return new CraftingManager.ChestLookupResult(containers, states, filteredOut, blockPos); } @Nonnull @@ -896,11 +998,7 @@ public class CraftingManager implements Component { } protected record ChestLookupResult( - List containers, - List states, - List spatialResults, - List filteredOut, - Vector3d benchCenteredPos + List containers, List states, List filteredOut, Vector3d benchCenteredPos ) { } diff --git a/src/com/hypixel/hytale/builtin/crafting/component/ProcessingBenchBlock.java b/src/com/hypixel/hytale/builtin/crafting/component/ProcessingBenchBlock.java new file mode 100644 index 00000000..64baa04a --- /dev/null +++ b/src/com/hypixel/hytale/builtin/crafting/component/ProcessingBenchBlock.java @@ -0,0 +1,896 @@ +package com.hypixel.hytale.builtin.crafting.component; + +import com.hypixel.hytale.builtin.crafting.CraftingPlugin; +import com.hypixel.hytale.builtin.crafting.window.BenchWindow; +import com.hypixel.hytale.builtin.crafting.window.ProcessingBenchWindow; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.AddReason; +import com.hypixel.hytale.component.Component; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.event.EventPriority; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.protocol.SoundCategory; +import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Bench; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.BenchTierLevel; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.ProcessingBench; +import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.MaterialQuantity; +import com.hypixel.hytale.server.core.inventory.ResourceQuantity; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; +import com.hypixel.hytale.server.core.inventory.container.InternalContainerUtilMaterial; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; +import com.hypixel.hytale.server.core.inventory.container.filter.FilterActionType; +import com.hypixel.hytale.server.core.inventory.container.filter.FilterType; +import com.hypixel.hytale.server.core.inventory.container.filter.ResourceFilter; +import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.MaterialTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.ResourceTransaction; +import com.hypixel.hytale.server.core.modules.block.BlockModule; +import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; +import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; +import com.hypixel.hytale.server.core.universe.world.SoundUtil; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.time.Instant; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; +import java.util.logging.Level; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.bson.BsonDocument; +import org.joml.Vector3d; +import org.joml.Vector3i; + +public class ProcessingBenchBlock implements Component { + @Nonnull + public static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + public static final boolean EXACT_RESOURCE_AMOUNTS = true; + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(ProcessingBenchBlock.class, ProcessingBenchBlock::new) + .append(new KeyedCodec<>("InputContainer", ItemContainer.CODEC), (state, o) -> state.inputContainer = o, state -> state.inputContainer) + .add() + .append(new KeyedCodec<>("FuelContainer", ItemContainer.CODEC), (state, o) -> state.fuelContainer = o, state -> state.fuelContainer) + .add() + .append(new KeyedCodec<>("OutputContainer", ItemContainer.CODEC), (state, o) -> state.outputContainer = o, state -> state.outputContainer) + .add() + .append(new KeyedCodec<>("Progress", Codec.DOUBLE), (state, d) -> state.inputProgress = d.floatValue(), state -> (double)state.inputProgress) + .add() + .append(new KeyedCodec<>("FuelTime", Codec.DOUBLE), (state, d) -> state.fuelTime = d.floatValue(), state -> (double)state.fuelTime) + .add() + .append(new KeyedCodec<>("Active", Codec.BOOLEAN), (state, b) -> state.active = b, state -> state.active) + .add() + .append(new KeyedCodec<>("NextExtra", Codec.INTEGER), (state, b) -> state.nextExtra = b, state -> state.nextExtra) + .add() + .append(new KeyedCodec<>("RecipeId", Codec.STRING), (state, o) -> state.recipeId = o, state -> state.recipeId) + .add() + .append(new KeyedCodec<>("LastTickGameTime", Codec.INSTANT), (state, t) -> state.lastTickGameTime = t, state -> state.lastTickGameTime) + .add() + .build(); + @Nonnull + public static final String PROCESSING = "Processing"; + @Nonnull + public static final String PROCESS_COMPLETED = "ProcessCompleted"; + private static final float MAX_UNLOAD_ELAPSED_SECONDS = 86400.0F; + private static final float EJECT_VELOCITY = 2.0F; + private static final float EJECT_SPREAD_VELOCITY = 1.0F; + private static final float EJECT_VERTICAL_VELOCITY = 3.25F; + @Nonnull + private final Set processingSlots = new HashSet<>(); + @Nonnull + private final Set processingFuelSlots = new HashSet<>(); + private transient Bench bench; + private transient ProcessingBench processingBench; + private ItemContainer inputContainer; + private ItemContainer fuelContainer; + private ItemContainer outputContainer; + private CombinedItemContainer combinedItemContainer; + private float inputProgress; + private float fuelTime; + private int lastConsumedFuelTotal; + private int nextExtra = -1; + @Nullable + private String recipeId; + @Nullable + private CraftingRecipe recipe; + private boolean active = false; + @Nullable + private Instant lastTickGameTime; + + public static ComponentType getComponentType() { + return CraftingPlugin.get().getProcessingBenchBlockComponentType(); + } + + @Nonnull + private static Vector3d getCenteredBlockPosition(@Nonnull BlockType blockType, int rotationIndex, int blockX, int blockY, int blockZ) { + Vector3d blockCenter = new Vector3d(); + blockType.getBlockCenter(rotationIndex, blockCenter); + return blockCenter.add(blockX, blockY, blockZ); + } + + @Nullable + public Bench getBench() { + return this.bench; + } + + @Nullable + public ProcessingBench getProcessingBench() { + return this.processingBench; + } + + public ItemContainer getOutputContainer() { + return this.outputContainer; + } + + public ItemContainer getInputContainer() { + return this.inputContainer; + } + + public ItemContainer getFuelContainer() { + return this.fuelContainer; + } + + public float getFuelTime() { + return this.fuelTime; + } + + @Nonnull + public Set getProcessingSlots() { + return this.processingSlots; + } + + @Nonnull + public Set getProcessingFuelSlots() { + return this.processingFuelSlots; + } + + @Nullable + public Instant getLastTickGameTime() { + return this.lastTickGameTime; + } + + public void setLastTickGameTime(@Nullable Instant lastTickGameTime) { + this.lastTickGameTime = lastTickGameTime; + } + + public void setLastConsumedFuelTotal(int lastConsumedFuelTotal) { + this.lastConsumedFuelTotal = lastConsumedFuelTotal; + } + + public void clearCurrentRecipe() { + this.recipeId = null; + this.recipe = null; + } + + public boolean initializeBenchConfig(@Nonnull BlockType blockType) { + Bench blockBench = blockType.getBench(); + if (blockBench == null) { + return false; + } else if (!(blockBench instanceof ProcessingBench)) { + LOGGER.at(Level.SEVERE).log("Wrong bench type for processing. Got %s", blockBench.getClass().getName()); + return false; + } else { + this.bench = blockBench; + this.processingBench = (ProcessingBench)blockBench; + if (this.nextExtra == -1) { + this.nextExtra = this.processingBench.getExtraOutput() != null ? this.processingBench.getExtraOutput().getPerFuelItemsConsumed() : 0; + } + + return true; + } + } + + public void setupSlots( + @Nonnull World world, + @Nonnull BenchBlock benchBlock, + @Nonnull BlockModule.BlockStateInfo blockStateInfo, + int blockX, + int blockY, + int blockZ, + @Nonnull BlockType blockType, + int rotationIndex + ) { + List remainder = new ObjectArrayList<>(); + int tierLevel = benchBlock.getTierLevel(); + ProcessingBench.ProcessingSlot[] input = this.processingBench.getInput(tierLevel); + short inputSlotsCount = (short)input.length; + this.inputContainer = ItemContainer.ensureContainerCapacity(this.inputContainer, inputSlotsCount, SimpleItemContainer::getNewContainer, remainder); + this.inputContainer.registerChangeEvent(EventPriority.LAST, event -> blockStateInfo.markNeedsSaving()); + + for (short slot = 0; slot < inputSlotsCount; slot++) { + ProcessingBench.ProcessingSlot inputSlot = input[slot]; + String resourceTypeId = inputSlot.getResourceTypeId(); + boolean shouldFilterValidIngredients = inputSlot.shouldFilterValidIngredients(); + if (resourceTypeId != null) { + this.inputContainer.setSlotFilter(FilterActionType.ADD, slot, new ResourceFilter(new ResourceQuantity(resourceTypeId, 1))); + } else if (shouldFilterValidIngredients) { + ObjectArrayList validIngredients = new ObjectArrayList<>(); + + for (CraftingRecipe recipe : CraftingPlugin.getBenchRecipes(this.bench.getType(), this.bench.getId())) { + if (!recipe.isRestrictedByBenchTierLevel(this.bench.getId(), tierLevel)) { + List inputMaterials = CraftingManager.getInputMaterials(recipe); + validIngredients.addAll(inputMaterials); + } + } + + this.inputContainer.setSlotFilter(FilterActionType.ADD, slot, (actionType, container, slotIndex, itemStack) -> { + if (itemStack == null) { + return true; + } else { + for (MaterialQuantity ingredient : validIngredients) { + if (CraftingManager.matches(ingredient, itemStack)) { + return true; + } + } + + return false; + } + }); + } + } + + input = this.processingBench.getFuel(); + inputSlotsCount = (short)(input != null ? input.length : 0); + this.fuelContainer = ItemContainer.ensureContainerCapacity(this.fuelContainer, inputSlotsCount, SimpleItemContainer::getNewContainer, remainder); + this.fuelContainer.registerChangeEvent(EventPriority.LAST, event -> blockStateInfo.markNeedsSaving()); + if (inputSlotsCount > 0) { + for (int i = 0; i < input.length; i++) { + ProcessingBench.ProcessingSlot fuel = input[i]; + String resourceTypeId = fuel.getResourceTypeId(); + if (resourceTypeId != null) { + this.fuelContainer.setSlotFilter(FilterActionType.ADD, (short)i, new ResourceFilter(new ResourceQuantity(resourceTypeId, 1))); + } + } + } + + short outputSlotsCount = (short)this.processingBench.getOutputSlotsCount(tierLevel); + this.outputContainer = ItemContainer.ensureContainerCapacity(this.outputContainer, outputSlotsCount, SimpleItemContainer::getNewContainer, remainder); + this.outputContainer.registerChangeEvent(EventPriority.LAST, event -> blockStateInfo.markNeedsSaving()); + if (outputSlotsCount > 0) { + this.outputContainer.setGlobalFilter(FilterType.ALLOW_OUTPUT_ONLY); + } + + this.combinedItemContainer = new CombinedItemContainer(this.fuelContainer, this.inputContainer, this.outputContainer); + Store entityStore = world.getEntityStore().getStore(); + Holder[] itemEntityHolders = this.ejectItems(entityStore, remainder, rotationIndex, blockType, blockX, blockY, blockZ); + if (itemEntityHolders.length > 0) { + world.execute(() -> entityStore.addEntities(itemEntityHolders, AddReason.SPAWN)); + } + + this.inputContainer.registerChangeEvent(EventPriority.LAST, event -> this.updateRecipe(benchBlock)); + this.outputContainer.registerChangeEvent(EventPriority.LAST, event -> this.updateRecipe(benchBlock)); + if (this.processingBench.getFuel() == null) { + this.setActive(true, benchBlock, blockStateInfo); + } + + if (this.lastTickGameTime != null) { + Instant currentGameTime = entityStore.getResource(WorldTimeResource.getResourceType()).getGameTime(); + float gameElapsedSeconds = (float)Math.max(0L, currentGameTime.toEpochMilli() - this.lastTickGameTime.toEpochMilli()) / 1000.0F; + float realElapsedSeconds = (float)(gameElapsedSeconds / WorldTimeResource.getSecondsPerTick(world)); + realElapsedSeconds = Math.min(realElapsedSeconds, 86400.0F); + this.checkForRecipeUpdate(benchBlock); + this.advanceProcessing(realElapsedSeconds, entityStore, benchBlock, blockStateInfo, blockX, blockY, blockZ, blockType, rotationIndex); + this.lastTickGameTime = currentGameTime; + blockStateInfo.markNeedsSaving(); + } + } + + public boolean isActive() { + return this.active; + } + + public boolean setActive(boolean active, @Nonnull BenchBlock benchBlock, @Nullable BlockModule.BlockStateInfo blockStateInfo) { + if (this.active != active) { + if (active && this.processingBench.getFuel() != null && this.fuelContainer.isEmpty()) { + return false; + } else { + this.active = active; + Map windows = benchBlock.getWindows(); + if (!active) { + this.processingSlots.clear(); + this.processingFuelSlots.clear(); + this.sendProcessingSlots(windows); + this.sendProcessingFuelSlots(windows); + } + + this.updateRecipe(benchBlock); + windows.forEach((uuid, window) -> ((ProcessingBenchWindow)window).setActive(active)); + if (blockStateInfo != null) { + blockStateInfo.markNeedsSaving(); + } + + return true; + } + } else { + return false; + } + } + + public void updateFuelValues(@Nonnull Map windows) { + if (this.fuelTime > this.lastConsumedFuelTotal) { + this.lastConsumedFuelTotal = MathUtil.ceil(this.fuelTime); + } + + float fuelPercent = this.lastConsumedFuelTotal > 0 ? this.fuelTime / this.lastConsumedFuelTotal : 0.0F; + windows.forEach((uuid, window) -> { + ProcessingBenchWindow processingBenchWindow = (ProcessingBenchWindow)window; + processingBenchWindow.setFuelTime(fuelPercent); + processingBenchWindow.setMaxFuel(this.lastConsumedFuelTotal); + processingBenchWindow.setProcessingFuelSlots(this.processingFuelSlots); + }); + } + + @Nullable + public CombinedItemContainer getItemContainer() { + return this.combinedItemContainer; + } + + @Nullable + public CraftingRecipe getRecipe() { + return this.recipe; + } + + public float getInputProgress() { + return this.inputProgress; + } + + public void setInputProgress(float inputProgress) { + this.inputProgress = inputProgress; + } + + public void dropFuelItems(@Nonnull List itemStacks) { + String fuelDropItemId = this.processingBench.getFuelDropItemId(); + if (fuelDropItemId != null) { + Item item = Item.getAssetMap().getAsset(fuelDropItemId); + int dropAmount = (int)this.fuelTime; + this.fuelTime = 0.0F; + + while (dropAmount > 0) { + int quantity = Math.min(dropAmount, item.getMaxStack()); + itemStacks.add(new ItemStack(fuelDropItemId, quantity)); + dropAmount -= quantity; + } + } else { + LOGGER.at(Level.WARNING).log("No FuelDropItemId defined for %s fuel value of %s will be lost!", this.bench.getId(), this.fuelTime); + } + } + + private float getCraftingTimeReductionModifier(int tierLevel) { + BenchTierLevel levelData = this.bench.getTierLevel(tierLevel); + return levelData != null ? levelData.getCraftingTimeReductionModifier() : 0.0F; + } + + public float getRecipeTimeSeconds(int tierLevel) { + if (this.recipe == null) { + return 0.0F; + } else { + float t = this.recipe.getTimeSeconds(); + float mod = this.getCraftingTimeReductionModifier(tierLevel); + return mod > 0.0F ? t - t * mod : t; + } + } + + private int countAvailableInputSets() { + if (this.recipe == null) { + return 0; + } else { + List inputMaterials = CraftingManager.getInputMaterials(this.recipe); + if (inputMaterials.isEmpty()) { + return 0; + } else { + int minSets = Integer.MAX_VALUE; + + for (MaterialQuantity material : inputMaterials) { + int required = material.getQuantity(); + int available = 0; + + for (short s = 0; s < this.inputContainer.getCapacity(); s++) { + ItemStack stack = this.inputContainer.getItemStack(s); + if (stack != null && CraftingManager.matches(material, stack)) { + available += stack.getQuantity(); + } + } + + minSets = Math.min(minSets, available / required); + } + + return minSets; + } + } + } + + private float calculateTotalAvailableFuel() { + ProcessingBench.ProcessingSlot[] fuelSlots = this.processingBench.getFuel(); + if (fuelSlots == null) { + return 0.0F; + } else { + float total = 0.0F; + + for (int i = 0; i < fuelSlots.length; i++) { + ItemStack stack = this.fuelContainer.getItemStack((short)i); + if (stack != null) { + total += (float)(stack.getQuantity() * stack.getItem().getFuelQuality()); + } + } + + return total; + } + } + + private boolean canFitScaledOutput(@Nonnull List outputPerRecipe, int count) { + if (count <= 0) { + return true; + } else { + ObjectArrayList scaled = new ObjectArrayList<>(); + + for (ItemStack item : outputPerRecipe) { + int total = item.getQuantity() * count; + int maxStack = item.getItem().getMaxStack(); + + while (total > 0) { + int qty = Math.min(total, maxStack); + scaled.add(item.withQuantity(qty)); + total -= qty; + } + } + + return this.outputContainer.canAddItemStacks(scaled, false, false); + } + } + + private void completeRecipes( + int count, @Nonnull Store entityStore, int blockX, int blockY, int blockZ, @Nonnull BlockType blockType, int rotationIndex + ) { + if (this.recipe != null && count > 0) { + List inputMaterials = CraftingManager.getInputMaterials(this.recipe); + List outputItemStacks = CraftingManager.getOutputItemStacks(this.recipe); + ObjectArrayList scaledInput = new ObjectArrayList<>(inputMaterials.size()); + + for (MaterialQuantity m : inputMaterials) { + scaledInput.add(m.clone(m.getQuantity() * count)); + } + + ListTransaction removeTransaction = this.inputContainer.removeMaterials(scaledInput, true, true, true); + if (removeTransaction.succeeded()) { + ObjectArrayList scaledOutput = new ObjectArrayList<>(); + + for (ItemStack item : outputItemStacks) { + int total = item.getQuantity() * count; + int maxStack = item.getItem().getMaxStack(); + + while (total > 0) { + int qty = Math.min(total, maxStack); + scaledOutput.add(item.withQuantity(qty)); + total -= qty; + } + } + + this.addOutputAndEjectRemainder(scaledOutput, entityStore, blockX, blockY, blockZ, blockType, rotationIndex); + } + } + } + + public int advanceProcessing( + float dt, + @Nonnull Store entityStore, + @Nonnull BenchBlock benchBlock, + @Nonnull BlockModule.BlockStateInfo blockStateInfo, + int blockX, + int blockY, + int blockZ, + @Nonnull BlockType blockType, + int rotationIndex + ) { + if (this.recipe != null && !(dt <= 0.0F)) { + boolean hasFuelSlots = this.processingBench.getFuel() != null; + if (hasFuelSlots && !this.active) { + return 0; + } else { + int tierLevel = benchBlock.getTierLevel(); + float recipeTime = this.getRecipeTimeSeconds(tierLevel); + if (recipeTime <= 0.0F) { + int completed = 0; + + while (this.tryCompleteOneRecipe(entityStore, blockX, blockY, blockZ, blockType, rotationIndex)) { + completed++; + this.updateRecipe(benchBlock); + if (this.recipe == null) { + break; + } + } + + if (hasFuelSlots && this.active && this.recipe == null) { + this.setActive(false, benchBlock, blockStateInfo); + } + + return completed; + } else { + float startProgress = this.inputProgress; + int maxFromTime = (int)((this.inputProgress + dt) / recipeTime); + int maxFromInput = this.countAvailableInputSets(); + int maxFromFuel = Integer.MAX_VALUE; + if (hasFuelSlots) { + float totalFuel = this.fuelTime + this.calculateTotalAvailableFuel(); + maxFromFuel = (int)((this.inputProgress + totalFuel) / recipeTime); + } + + int completions = Math.min(maxFromTime, Math.min(maxFromInput, maxFromFuel)); + if (completions > 0) { + List outputStacks = CraftingManager.getOutputItemStacks(this.recipe); + int lo = 0; + int hi = completions; + + while (lo < hi) { + int mid = (lo + hi + 1) / 2; + if (this.canFitScaledOutput(outputStacks, mid)) { + lo = mid; + } else { + hi = mid - 1; + } + } + + completions = lo; + } + + float timeForCompletions = completions > 0 ? completions * recipeTime - this.inputProgress : 0.0F; + timeForCompletions = Math.max(0.0F, timeForCompletions); + boolean hasMoreInput = completions < maxFromInput; + float partialTime = hasMoreInput ? Math.min(dt - timeForCompletions, recipeTime) : 0.0F; + partialTime = Math.max(0.0F, partialTime); + if (hasFuelSlots && partialTime > 0.0F) { + float totalFuel = this.fuelTime + this.calculateTotalAvailableFuel(); + partialTime = Math.min(partialTime, Math.max(0.0F, totalFuel - timeForCompletions)); + } + + float totalTimeUsed = timeForCompletions + partialTime; + if (hasFuelSlots && totalTimeUsed > 0.0F) { + this.consumeFuelForDuration(totalTimeUsed, entityStore, blockX, blockY, blockZ, blockType, rotationIndex); + } + + if (completions > 0) { + this.completeRecipes(completions, entityStore, blockX, blockY, blockZ, blockType, rotationIndex); + } + + this.inputProgress = startProgress + totalTimeUsed - completions * recipeTime; + if (this.inputProgress < 0.0F) { + this.inputProgress = 0.0F; + } + + this.updateRecipe(benchBlock); + if (hasFuelSlots && this.active && this.recipe == null) { + this.setActive(false, benchBlock, blockStateInfo); + } + + return completions; + } + } + } else { + return 0; + } + } + + private int consumeOneFuel(@Nonnull Store entityStore, int blockX, int blockY, int blockZ, @Nonnull BlockType blockType, int rotationIndex) { + ProcessingBench.ProcessingSlot[] fuelSlots = this.processingBench.getFuel(); + if (fuelSlots != null + && fuelSlots.length != 0 + && this.active + && (this.processingBench.getMaxFuel() <= 0 || this.fuelTime < this.processingBench.getMaxFuel()) + && !this.fuelContainer.isEmpty()) { + if (this.fuelTime < 0.0F) { + this.fuelTime = 0.0F; + } + + for (int i = 0; i < fuelSlots.length; i++) { + ProcessingBench.ProcessingSlot fuelSlot = fuelSlots[i]; + String resourceTypeId = fuelSlot.getResourceTypeId() != null ? fuelSlot.getResourceTypeId() : "Fuel"; + ResourceQuantity resourceQuantity = new ResourceQuantity(resourceTypeId, 1); + ItemStack slot = this.fuelContainer.getItemStack((short)i); + if (slot != null) { + double fuelQuality = slot.getItem().getFuelQuality(); + ResourceTransaction transaction = this.fuelContainer.removeResource(resourceQuantity, true, true, true); + if (transaction.getRemainder() <= 0) { + ProcessingBench.ExtraOutput extra = this.processingBench.getExtraOutput(); + if (extra != null && !extra.isIgnoredFuelSource(slot.getItem())) { + this.nextExtra--; + if (this.nextExtra <= 0) { + this.nextExtra = extra.getPerFuelItemsConsumed(); + ObjectArrayList extraItemStacks = new ObjectArrayList<>(extra.getOutputs().length); + + for (MaterialQuantity e : extra.getOutputs()) { + extraItemStacks.add(e.toItemStack()); + } + + this.addOutputAndEjectRemainder(extraItemStacks, entityStore, blockX, blockY, blockZ, blockType, rotationIndex); + } + } + + this.fuelTime = this.fuelTime + (float)(transaction.getConsumed() * fuelQuality); + return i; + } + } + } + + return -1; + } else { + return -1; + } + } + + private void addOutputAndEjectRemainder( + @Nonnull List outputItemStacks, + @Nonnull Store entityStore, + int blockX, + int blockY, + int blockZ, + @Nonnull BlockType blockType, + int rotationIndex + ) { + ListTransaction addTransaction = this.outputContainer.addItemStacks(outputItemStacks, false, false, false); + List remainderItems = new ObjectArrayList<>(); + + for (ItemStackTransaction itemStackTransaction : addTransaction.getList()) { + ItemStack remainder = itemStackTransaction.getRemainder(); + if (remainder != null && !remainder.isEmpty()) { + remainderItems.add(remainder); + } + } + + if (!remainderItems.isEmpty()) { + Holder[] holders = this.ejectItems(entityStore, remainderItems, rotationIndex, blockType, blockX, blockY, blockZ); + entityStore.addEntities(holders, AddReason.SPAWN); + } + } + + private boolean tryCompleteOneRecipe( + @Nonnull Store entityStore, int blockX, int blockY, int blockZ, @Nonnull BlockType blockType, int rotationIndex + ) { + if (this.recipe == null) { + return false; + } else { + float recipeTime = this.recipe.getTimeSeconds(); + if (this.inputProgress < recipeTime) { + return false; + } else { + List inputMaterials = CraftingManager.getInputMaterials(this.recipe); + List outputItemStacks = CraftingManager.getOutputItemStacks(this.recipe); + if (!this.outputContainer.canAddItemStacks(outputItemStacks, false, false)) { + return false; + } else if (this.inputContainer.getSlotMaterialsToRemove(inputMaterials, true, true).isEmpty()) { + return false; + } else { + ListTransaction transaction = this.inputContainer.removeMaterials(inputMaterials, true, true, true); + if (!transaction.succeeded()) { + return false; + } else { + this.inputProgress -= recipeTime; + this.addOutputAndEjectRemainder(outputItemStacks, entityStore, blockX, blockY, blockZ, blockType, rotationIndex); + return true; + } + } + } + } + } + + public void consumeFuelForDuration( + float duration, @Nonnull Store entityStore, int blockX, int blockY, int blockZ, @Nonnull BlockType blockType, int rotationIndex + ) { + if (!(duration <= 0.0F)) { + float consumed = 0.0F; + if (this.fuelTime > 0.0F) { + float use = Math.min(this.fuelTime, duration); + this.fuelTime -= use; + consumed += use; + } + + while (consumed < duration && this.consumeOneFuel(entityStore, blockX, blockY, blockZ, blockType, rotationIndex) >= 0) { + float use = Math.min(this.fuelTime, duration - consumed); + this.fuelTime -= use; + consumed += use; + } + } + } + + @Nonnull + private Holder[] ejectItems( + @Nonnull ComponentAccessor accessor, + @Nonnull List itemStacks, + int rotationIndex, + @Nullable BlockType blockType, + int blockX, + int blockY, + int blockZ + ) { + if (itemStacks.isEmpty()) { + return Holder.emptyArray(); + } else { + RotationTuple rotation = RotationTuple.get(rotationIndex); + Vector3d frontDir = new Vector3d(0.0, 0.0, 1.0); + rotation.yaw().rotateY(frontDir, frontDir); + Vector3d dropPosition; + if (blockType == null) { + dropPosition = new Vector3d(blockX + 0.5, blockY, blockZ + 0.5); + } else { + BlockBoundingBoxes hitboxAsset = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); + if (hitboxAsset == null) { + dropPosition = new Vector3d(blockX + 0.5, blockY, blockZ + 0.5); + } else { + double depth = hitboxAsset.get(0).getBoundingBox().depth(); + double frontOffset = depth / 2.0 + 0.1F; + dropPosition = getCenteredBlockPosition(blockType, rotationIndex, blockX, blockY, blockZ); + dropPosition.add(frontDir.x * frontOffset, 0.0, frontDir.z * frontOffset); + } + } + + ThreadLocalRandom random = ThreadLocalRandom.current(); + ObjectArrayList> result = new ObjectArrayList<>(itemStacks.size()); + + for (ItemStack item : itemStacks) { + float velocityX = (float)(frontDir.x * 2.0 + 2.0 * (random.nextDouble() - 0.5)); + float velocityZ = (float)(frontDir.z * 2.0 + 2.0 * (random.nextDouble() - 0.5)); + Holder holder = ItemComponent.generateItemDrop(accessor, item, dropPosition, Rotation3f.IDENTITY, velocityX, 3.25F, velocityZ); + if (holder != null) { + result.add(holder); + } + } + + return result.toArray(Holder[]::new); + } + } + + public void sendProgress(float progress, @Nonnull Map windows) { + windows.forEach((uuid, window) -> ((ProcessingBenchWindow)window).setProgress(progress)); + } + + public void sendProcessingSlots(@Nonnull Map windows) { + windows.forEach((uuid, window) -> ((ProcessingBenchWindow)window).setProcessingSlots(this.processingSlots)); + } + + public void sendProcessingFuelSlots(@Nonnull Map windows) { + windows.forEach((uuid, window) -> ((ProcessingBenchWindow)window).setProcessingFuelSlots(this.processingFuelSlots)); + } + + public void setBlockInteractionState(@Nonnull String state, @Nonnull BlockType blockType, @Nonnull World world, int blockX, int blockY, int blockZ) { + world.setBlockInteractionState(new Vector3i(blockX, blockY, blockZ), blockType, state); + } + + public void playSound( + int soundEventIndex, + @Nonnull ComponentAccessor componentAccessor, + @Nonnull BlockType blockType, + int rotationIndex, + int blockX, + int blockY, + int blockZ + ) { + if (soundEventIndex != 0) { + Vector3d soundPos = getCenteredBlockPosition(blockType, rotationIndex, blockX, blockY, blockZ); + SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, soundPos, componentAccessor); + } + } + + public void checkForRecipeUpdate(@Nonnull BenchBlock benchBlock) { + if (this.recipe == null && this.recipeId != null) { + this.updateRecipe(benchBlock); + } + } + + private void updateRecipe(@Nonnull BenchBlock benchBlock) { + Map windows = benchBlock.getWindows(); + int tierLevel = benchBlock.getTierLevel(); + List recipes = CraftingPlugin.getBenchRecipes(this.bench.getType(), this.bench.getId()); + if (recipes.isEmpty()) { + this.clearRecipe(windows); + } else { + List matching = new ObjectArrayList<>(); + + for (CraftingRecipe recipe : recipes) { + if (!recipe.isRestrictedByBenchTierLevel(this.bench.getId(), tierLevel)) { + MaterialQuantity[] input = recipe.getInput(); + int matches = 0; + IntArrayList slots = new IntArrayList(); + + for (int j = 0; j < this.inputContainer.getCapacity(); j++) { + slots.add(j); + } + + for (MaterialQuantity craftingMaterial : input) { + String itemId = craftingMaterial.getItemId(); + String resourceTypeId = craftingMaterial.getResourceTypeId(); + int materialQuantity = craftingMaterial.getQuantity(); + BsonDocument metadata = craftingMaterial.getMetadata(); + MaterialQuantity material = new MaterialQuantity(itemId, resourceTypeId, null, materialQuantity, metadata); + + for (int k = 0; k < slots.size(); k++) { + int j = slots.getInt(k); + int out = InternalContainerUtilMaterial.testRemoveMaterialFromSlot(this.inputContainer, (short)j, material, material.getQuantity(), true); + if (out == 0) { + matches++; + slots.removeInt(k); + break; + } + } + } + + if (matches == input.length) { + matching.add(recipe); + } + } + } + + if (matching.isEmpty()) { + this.clearRecipe(windows); + } else { + matching.sort(Comparator.comparingInt(o -> CraftingManager.getInputMaterials(o).size())); + Collections.reverse(matching); + if (this.recipeId != null) { + for (CraftingRecipe rec : matching) { + if (Objects.equals(this.recipeId, rec.getId())) { + LOGGER.at(Level.FINE).log("Keeping existing Recipe %s %s", this.recipeId, rec); + this.recipe = rec; + return; + } + } + } + + CraftingRecipe recipex = matching.getFirst(); + if (this.recipeId == null || !Objects.equals(this.recipeId, recipex.getId())) { + this.inputProgress = 0.0F; + this.sendProgress(0.0F, windows); + } + + this.recipeId = recipex.getId(); + this.recipe = recipex; + LOGGER.at(Level.FINE).log("Found Recipe %s %s", this.recipeId, this.recipe); + } + } + } + + private void clearRecipe(@Nonnull Map windows) { + this.recipeId = null; + this.recipe = null; + this.lastConsumedFuelTotal = 0; + this.inputProgress = 0.0F; + this.sendProgress(0.0F, windows); + LOGGER.at(Level.FINE).log("Cleared Recipe"); + } + + @Nullable + @Override + public Component clone() { + ProcessingBenchBlock clone = new ProcessingBenchBlock(); + clone.inputContainer = this.inputContainer; + clone.fuelContainer = this.fuelContainer; + clone.outputContainer = this.outputContainer; + clone.inputProgress = this.inputProgress; + clone.fuelTime = this.fuelTime; + clone.lastConsumedFuelTotal = this.lastConsumedFuelTotal; + clone.active = this.active; + clone.nextExtra = this.nextExtra; + clone.recipeId = this.recipeId; + clone.lastTickGameTime = this.lastTickGameTime; + return clone; + } +} diff --git a/src/com/hypixel/hytale/builtin/crafting/interaction/OpenBenchPageInteraction.java b/src/com/hypixel/hytale/builtin/crafting/interaction/OpenBenchPageInteraction.java index be748414..5262493a 100644 --- a/src/com/hypixel/hytale/builtin/crafting/interaction/OpenBenchPageInteraction.java +++ b/src/com/hypixel/hytale/builtin/crafting/interaction/OpenBenchPageInteraction.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.crafting.interaction; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; import com.hypixel.hytale.builtin.crafting.component.CraftingManager; -import com.hypixel.hytale.builtin.crafting.state.BenchState; import com.hypixel.hytale.builtin.crafting.window.CraftingWindow; import com.hypixel.hytale.builtin.crafting.window.DiagramCraftingWindow; import com.hypixel.hytale.builtin.crafting.window.SimpleCraftingWindow; @@ -13,9 +13,10 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.packets.interface_.Page; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -24,10 +25,14 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHa import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class OpenBenchPageInteraction extends SimpleBlockInteraction { @Nonnull @@ -89,23 +94,46 @@ public class OpenBenchPageInteraction extends SimpleBlockInteraction { if (playerComponent != null) { CraftingManager craftingManagerComponent = commandBuffer.getComponent(ref, CraftingManager.getComponentType()); if (craftingManagerComponent != null && !craftingManagerComponent.hasBenchSet()) { - if (world.getState(targetBlock.x, targetBlock.y, targetBlock.z, true) instanceof BenchState benchState) { - CraftingWindow benchWindow = (CraftingWindow)(switch (this.pageType) { - case SIMPLE_CRAFTING -> new SimpleCraftingWindow(benchState); - case DIAGRAM_CRAFTING -> new DiagramCraftingWindow(ref, commandBuffer, benchState); - case STRUCTURAL_CRAFTING -> new StructuralCraftingWindow(benchState); - }); - UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); - if (uuidComponent == null) { - return; - } + ChunkStore chunkStore = world.getChunkStore(); + Ref chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z)); + if (chunkRef != null && chunkRef.isValid()) { + BlockComponentChunk blockComponentChunk = chunkStore.getStore().getComponent(chunkRef, BlockComponentChunk.getComponentType()); + if (blockComponentChunk != null) { + Ref blockEntityRef = blockComponentChunk.getEntityReference( + ChunkUtil.indexBlockInColumn(targetBlock.x, targetBlock.y, targetBlock.z) + ); + if (blockEntityRef != null && blockEntityRef.isValid()) { + BenchBlock benchBlock = chunkStore.getStore().getComponent(blockEntityRef, BenchBlock.getComponentType()); + if (benchBlock != null) { + BlockType blockType = world.getBlockType(targetBlock.x, targetBlock.y, targetBlock.z); + if (blockType != null) { + WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z)); + int rotationIndex = worldChunk.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z); - UUID uuid = uuidComponent.getUuid(); - if (benchState.getWindows().putIfAbsent(uuid, benchWindow) == null) { - benchWindow.registerCloseEvent(event -> benchState.getWindows().remove(uuid, benchWindow)); - } + CraftingWindow benchWindow = (CraftingWindow)(switch (this.pageType) { + case SIMPLE_CRAFTING -> new SimpleCraftingWindow( + targetBlock.x, targetBlock.y, targetBlock.z, rotationIndex, blockType, benchBlock + ); + case DIAGRAM_CRAFTING -> new DiagramCraftingWindow( + ref, commandBuffer, targetBlock.x, targetBlock.y, targetBlock.z, rotationIndex, blockType, benchBlock + ); + case STRUCTURAL_CRAFTING -> new StructuralCraftingWindow( + targetBlock.x, targetBlock.y, targetBlock.z, rotationIndex, blockType, benchBlock + ); + }); + UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); + if (uuidComponent != null) { + UUID uuid = uuidComponent.getUuid(); + if (benchBlock.getWindows().putIfAbsent(uuid, benchWindow) == null) { + benchWindow.registerCloseEvent(event -> benchBlock.getWindows().remove(uuid, benchWindow)); + } - playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, benchWindow); + playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, benchWindow); + } + } + } + } + } } } } diff --git a/src/com/hypixel/hytale/builtin/crafting/interaction/OpenProcessingBenchInteraction.java b/src/com/hypixel/hytale/builtin/crafting/interaction/OpenProcessingBenchInteraction.java index e80f2fa0..a2cef7e1 100644 --- a/src/com/hypixel/hytale/builtin/crafting/interaction/OpenProcessingBenchInteraction.java +++ b/src/com/hypixel/hytale/builtin/crafting/interaction/OpenProcessingBenchInteraction.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.builtin.crafting.interaction; -import com.hypixel.hytale.builtin.crafting.state.ProcessingBenchState; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; +import com.hypixel.hytale.builtin.crafting.component.ProcessingBenchBlock; import com.hypixel.hytale.builtin.crafting.window.BenchWindow; import com.hypixel.hytale.builtin.crafting.window.ProcessingBenchWindow; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -8,29 +9,31 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.packets.interface_.Page; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Bench; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.accessor.BlockAccessor; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Map; import java.util.UUID; -import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class OpenProcessingBenchInteraction extends SimpleBlockInteraction { @Nonnull @@ -54,53 +57,70 @@ public class OpenProcessingBenchInteraction extends SimpleBlockInteraction { Store store = ref.getStore(); Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); if (playerComponent != null) { - BlockState state = world.getState(pos.x, pos.y, pos.z, true); - if (!(state instanceof ProcessingBenchState benchState)) { - playerComponent.sendMessage( - Message.translation("server.interactions.invalidBlockState") - .param("interaction", this.getClass().getSimpleName()) - .param("blockState", state != null ? state.getClass().getSimpleName() : "null") - ); - } else { - BlockType blockType = world.getBlockType(pos.x, pos.y, pos.z); - Bench blockTypeBench = blockType.getBench(); - if ((blockTypeBench == null || !blockTypeBench.equals(benchState.getBench())) && !benchState.initialize(blockType)) { - ProcessingBenchState.LOGGER.at(Level.WARNING).log("Failed to re-initialize: %s, %s", blockType.getId(), pos); - int x = pos.getX(); - int z = pos.getZ(); - world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).setState(x, pos.getY(), z, (BlockState)null); - } else { - UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); - if (uuidComponent != null) { - UUID uuid = uuidComponent.getUuid(); - ProcessingBenchWindow window = new ProcessingBenchWindow(benchState); - Map windows = benchState.getWindows(); - if (windows.putIfAbsent(uuid, window) == null) { - benchState.updateFuelValues(); - if (playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, window)) { - window.registerCloseEvent(event -> { - windows.remove(uuid, window); - BlockType currentBlockType = world.getBlockType(pos); - if (currentBlockType != null) { - String interactionState = BlockAccessor.getCurrentInteractionState(currentBlockType); - if (windows.isEmpty() && !"Processing".equals(interactionState) && !"ProcessCompleted".equals(interactionState)) { - world.setBlockInteractionState(pos, benchState.getBaseBlockType(), benchState.getTierStateName()); - } + ChunkStore chunkStore = world.getChunkStore(); + Ref chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); + if (chunkRef != null && chunkRef.isValid()) { + Store chunkStoreStore = chunkStore.getStore(); + BlockComponentChunk blockComponentChunk = chunkStoreStore.getComponent(chunkRef, BlockComponentChunk.getComponentType()); + if (blockComponentChunk != null) { + Ref blockEntityRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(pos.x, pos.y, pos.z)); + if (blockEntityRef != null && blockEntityRef.isValid()) { + ProcessingBenchBlock benchState = chunkStoreStore.getComponent(blockEntityRef, ProcessingBenchBlock.getComponentType()); + if (benchState == null) { + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); - int soundEventIndexx = blockType.getBench().getLocalCloseSoundEventIndex(); - if (soundEventIndexx != 0) { - SoundUtil.playSoundEvent2d(ref, soundEventIndexx, SoundCategory.UI, commandBuffer); + assert playerRefComponent != null; + + playerRefComponent.sendMessage( + Message.translation("server.interactions.invalidBlockState") + .param("interaction", this.getClass().getSimpleName()) + .param("blockState", "null") + ); + } else { + BenchBlock benchBlock = chunkStoreStore.getComponent(blockEntityRef, BenchBlock.getComponentType()); + if (benchBlock != null) { + BlockModule.BlockStateInfo blockStateInfo = chunkStoreStore.getComponent(blockEntityRef, BlockModule.BlockStateInfo.getComponentType()); + BlockType blockType = world.getBlockType(pos.x, pos.y, pos.z); + UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); + if (uuidComponent != null) { + UUID uuid = uuidComponent.getUuid(); + WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); + if (worldChunk != null) { + int rotationIndex = worldChunk.getRotationIndex(pos.x, pos.y, pos.z); + ProcessingBenchWindow window = new ProcessingBenchWindow( + benchState, benchBlock, blockStateInfo, pos.x, pos.y, pos.z, rotationIndex, blockType + ); + Map windows = benchBlock.getWindows(); + if (windows.putIfAbsent(uuid, window) == null) { + benchState.updateFuelValues(benchBlock.getWindows()); + if (playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, window)) { + window.registerCloseEvent(event -> { + windows.remove(uuid, window); + BlockType currentBlockType = world.getBlockType(pos); + if (currentBlockType != null && currentBlockType != BlockType.EMPTY && currentBlockType != BlockType.UNKNOWN) { + String interactionState = BlockAccessor.getCurrentInteractionState(currentBlockType); + if (windows.isEmpty() && !"Processing".equals(interactionState) && !"ProcessCompleted".equals(interactionState)) { + world.setBlockInteractionState(pos, BenchBlock.getBaseBlockType(currentBlockType), benchBlock.getTierStateName()); + } + + int soundEventIndexx = blockType.getBench().getLocalCloseSoundEventIndex(); + if (soundEventIndexx != 0) { + SoundUtil.playSoundEvent2d(ref, soundEventIndexx, SoundCategory.UI, commandBuffer); + } + } + }); + int soundEventIndex = blockType.getBench().getLocalOpenSoundEventIndex(); + if (soundEventIndex == 0) { + return; + } + + SoundUtil.playSoundEvent2d(ref, soundEventIndex, SoundCategory.UI, commandBuffer); + } else { + windows.remove(uuid, window); + } } } - }); - int soundEventIndex = blockType.getBench().getLocalOpenSoundEventIndex(); - if (soundEventIndex == 0) { - return; } - - SoundUtil.playSoundEvent2d(ref, soundEventIndex, SoundCategory.UI, commandBuffer); - } else { - windows.remove(uuid, window); } } } diff --git a/src/com/hypixel/hytale/builtin/crafting/state/BenchState.java b/src/com/hypixel/hytale/builtin/crafting/state/BenchState.java deleted file mode 100644 index c3784e24..00000000 --- a/src/com/hypixel/hytale/builtin/crafting/state/BenchState.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.hypixel.hytale.builtin.crafting.state; - -import com.hypixel.hytale.builtin.crafting.window.BenchWindow; -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.codecs.array.ArrayCodec; -import com.hypixel.hytale.component.AddReason; -import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Bench; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.BenchUpgradeRequirement; -import com.hypixel.hytale.server.core.entity.entities.player.windows.WindowManager; -import com.hypixel.hytale.server.core.inventory.ItemStack; -import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.DestroyableBlockState; -import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nonnull; - -public class BenchState extends BlockState implements DestroyableBlockState { - @Nonnull - public static BuilderCodec CODEC = BuilderCodec.builder(BenchState.class, BenchState::new, BlockState.BASE_CODEC) - .appendInherited( - new KeyedCodec<>("TierLevel", Codec.INTEGER), - (state, o) -> state.tierLevel = o, - state -> state.tierLevel, - (state, parent) -> state.tierLevel = parent.tierLevel - ) - .add() - .appendInherited( - new KeyedCodec<>("UpgradeItems", new ArrayCodec<>(ItemStack.CODEC, ItemStack[]::new)), - (state, o) -> state.upgradeItems = o, - state -> state.upgradeItems, - (state, parent) -> state.upgradeItems = parent.upgradeItems - ) - .add() - .build(); - private int tierLevel = 1; - protected ItemStack[] upgradeItems = ItemStack.EMPTY_ARRAY; - protected Bench bench; - @Nonnull - protected final Map windows = new ConcurrentHashMap<>(); - - public int getTierLevel() { - return this.tierLevel; - } - - @Override - public boolean initialize(@Nonnull BlockType blockType) { - if (!super.initialize(blockType)) { - return false; - } else { - this.bench = blockType.getBench(); - if (this.bench == null) { - if (this.upgradeItems.length > 0) { - this.dropUpgradeItems(); - } - - return false; - } else { - return true; - } - } - } - - public void addUpgradeItems(@Nonnull List consumed) { - consumed.addAll(Arrays.asList(this.upgradeItems)); - this.upgradeItems = consumed.toArray(ItemStack[]::new); - this.markNeedsSave(); - } - - private void dropUpgradeItems() { - if (this.upgradeItems.length != 0) { - World world = this.getChunk().getWorld(); - Store entityStore = world.getEntityStore().getStore(); - Vector3d dropPosition = this.getBlockPosition().toVector3d().add(0.5, 0.0, 0.5); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, List.of(this.upgradeItems), dropPosition, Vector3f.ZERO); - if (itemEntityHolders.length > 0) { - world.execute(() -> entityStore.addEntities(itemEntityHolders, AddReason.SPAWN)); - } - - this.upgradeItems = ItemStack.EMPTY_ARRAY; - } - } - - public Bench getBench() { - return this.bench; - } - - public void setTierLevel(int newTierLevel) { - if (this.tierLevel != newTierLevel) { - this.tierLevel = newTierLevel; - this.onTierLevelChange(); - this.markNeedsSave(); - } - } - - public BenchUpgradeRequirement getNextLevelUpgradeMaterials() { - return this.bench.getUpgradeRequirement(this.tierLevel); - } - - protected void onTierLevelChange() { - this.getChunk().setBlockInteractionState(this.getBlockPosition(), this.getBaseBlockType(), this.getTierStateName()); - } - - @Nonnull - public BlockType getBaseBlockType() { - BlockType currentBlockType = this.getBlockType(); - String baseBlockKey = currentBlockType.getDefaultStateKey(); - BlockType baseBlockType = BlockType.getAssetMap().getAsset(baseBlockKey); - if (baseBlockType == null) { - baseBlockType = currentBlockType; - } - - return baseBlockType; - } - - @Nonnull - public String getTierStateName() { - return this.tierLevel > 1 ? "Tier" + this.tierLevel : "default"; - } - - @Override - public void onDestroy() { - WindowManager.closeAndRemoveAll(this.windows); - this.dropUpgradeItems(); - } - - @Nonnull - public Map getWindows() { - return this.windows; - } -} diff --git a/src/com/hypixel/hytale/builtin/crafting/state/ProcessingBenchState.java b/src/com/hypixel/hytale/builtin/crafting/state/ProcessingBenchState.java deleted file mode 100644 index 25fbd709..00000000 --- a/src/com/hypixel/hytale/builtin/crafting/state/ProcessingBenchState.java +++ /dev/null @@ -1,795 +0,0 @@ -package com.hypixel.hytale.builtin.crafting.state; - -import com.google.common.flogger.LazyArgs; -import com.hypixel.hytale.builtin.crafting.CraftingPlugin; -import com.hypixel.hytale.builtin.crafting.component.CraftingManager; -import com.hypixel.hytale.builtin.crafting.window.ProcessingBenchWindow; -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.component.AddReason; -import com.hypixel.hytale.component.ArchetypeChunk; -import com.hypixel.hytale.component.CommandBuffer; -import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.event.EventPriority; -import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.protocol.SoundCategory; -import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.BenchTierLevel; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.ProcessingBench; -import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; -import com.hypixel.hytale.server.core.asset.type.item.config.Item; -import com.hypixel.hytale.server.core.inventory.ItemStack; -import com.hypixel.hytale.server.core.inventory.MaterialQuantity; -import com.hypixel.hytale.server.core.inventory.ResourceQuantity; -import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; -import com.hypixel.hytale.server.core.inventory.container.InternalContainerUtilMaterial; -import com.hypixel.hytale.server.core.inventory.container.ItemContainer; -import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; -import com.hypixel.hytale.server.core.inventory.container.TestRemoveItemSlotResult; -import com.hypixel.hytale.server.core.inventory.container.filter.FilterActionType; -import com.hypixel.hytale.server.core.inventory.container.filter.FilterType; -import com.hypixel.hytale.server.core.inventory.container.filter.ResourceFilter; -import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction; -import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction; -import com.hypixel.hytale.server.core.inventory.transaction.MaterialSlotTransaction; -import com.hypixel.hytale.server.core.inventory.transaction.MaterialTransaction; -import com.hypixel.hytale.server.core.inventory.transaction.ResourceTransaction; -import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; -import com.hypixel.hytale.server.core.universe.world.SoundUtil; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.accessor.BlockAccessor; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.chunk.state.TickableBlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.DestroyableBlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerBlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.PlacedByBlockState; -import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; -import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; -import java.util.logging.Level; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.bson.BsonDocument; - -public class ProcessingBenchState extends BenchState implements TickableBlockState, ItemContainerBlockState, DestroyableBlockState, PlacedByBlockState { - @Nonnull - public static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - public static final boolean EXACT_RESOURCE_AMOUNTS = true; - @Nonnull - public static final Codec CODEC = BuilderCodec.builder(ProcessingBenchState.class, ProcessingBenchState::new, BenchState.CODEC) - .append(new KeyedCodec<>("InputContainer", ItemContainer.CODEC), (state, o) -> state.inputContainer = o, state -> state.inputContainer) - .add() - .append(new KeyedCodec<>("FuelContainer", ItemContainer.CODEC), (state, o) -> state.fuelContainer = o, state -> state.fuelContainer) - .add() - .append(new KeyedCodec<>("OutputContainer", ItemContainer.CODEC), (state, o) -> state.outputContainer = o, state -> state.outputContainer) - .add() - .append(new KeyedCodec<>("Progress", Codec.DOUBLE), (state, d) -> state.inputProgress = d.floatValue(), state -> (double)state.inputProgress) - .add() - .append(new KeyedCodec<>("FuelTime", Codec.DOUBLE), (state, d) -> state.fuelTime = d.floatValue(), state -> (double)state.fuelTime) - .add() - .append(new KeyedCodec<>("Active", Codec.BOOLEAN), (state, b) -> state.active = b, state -> state.active) - .add() - .append(new KeyedCodec<>("NextExtra", Codec.INTEGER), (state, b) -> state.nextExtra = b, state -> state.nextExtra) - .add() - .append(new KeyedCodec<>("RecipeId", Codec.STRING), (state, o) -> state.recipeId = o, state -> state.recipeId) - .add() - .build(); - private static final float EJECT_VELOCITY = 2.0F; - private static final float EJECT_SPREAD_VELOCITY = 1.0F; - private static final float EJECT_VERTICAL_VELOCITY = 3.25F; - @Nonnull - public static final String PROCESSING = "Processing"; - @Nonnull - public static final String PROCESS_COMPLETED = "ProcessCompleted"; - private ProcessingBench processingBench; - private ItemContainer inputContainer; - private ItemContainer fuelContainer; - private ItemContainer outputContainer; - private CombinedItemContainer combinedItemContainer; - private float inputProgress; - private float fuelTime; - private int lastConsumedFuelTotal; - private int nextExtra = -1; - @Nonnull - private final Set processingSlots = new HashSet<>(); - @Nonnull - private final Set processingFuelSlots = new HashSet<>(); - @Nullable - private String recipeId; - @Nullable - private CraftingRecipe recipe; - private boolean active = false; - - @Override - public boolean initialize(@Nonnull BlockType blockType) { - if (!super.initialize(blockType)) { - if (this.bench == null) { - List itemStacks = new ObjectArrayList<>(); - if (this.inputContainer != null) { - itemStacks.addAll(this.inputContainer.dropAllItemStacks()); - } - - if (this.fuelContainer != null) { - itemStacks.addAll(this.fuelContainer.dropAllItemStacks()); - } - - if (this.outputContainer != null) { - itemStacks.addAll(this.outputContainer.dropAllItemStacks()); - } - - World world = this.getChunk().getWorld(); - Store store = world.getEntityStore().getStore(); - Holder[] itemEntityHolders = this.ejectItems(store, itemStacks); - if (itemEntityHolders.length > 0) { - world.execute(() -> store.addEntities(itemEntityHolders, AddReason.SPAWN)); - } - } - - return false; - } else if (!(this.bench instanceof ProcessingBench)) { - LOGGER.at(Level.SEVERE).log("Wrong bench type for processing. Got %s", this.bench.getClass().getName()); - return false; - } else { - this.processingBench = (ProcessingBench)this.bench; - if (this.nextExtra == -1) { - this.nextExtra = this.processingBench.getExtraOutput() != null ? this.processingBench.getExtraOutput().getPerFuelItemsConsumed() : 0; - } - - this.setupSlots(); - return true; - } - } - - private void setupSlots() { - List remainder = new ObjectArrayList<>(); - int tierLevel = this.getTierLevel(); - ProcessingBench.ProcessingSlot[] input = this.processingBench.getInput(tierLevel); - short inputSlotsCount = (short)input.length; - this.inputContainer = ItemContainer.ensureContainerCapacity(this.inputContainer, inputSlotsCount, SimpleItemContainer::getNewContainer, remainder); - this.inputContainer.registerChangeEvent(EventPriority.LAST, this::onItemChange); - - for (short slot = 0; slot < inputSlotsCount; slot++) { - ProcessingBench.ProcessingSlot inputSlot = input[slot]; - String resourceTypeId = inputSlot.getResourceTypeId(); - boolean shouldFilterValidIngredients = inputSlot.shouldFilterValidIngredients(); - if (resourceTypeId != null) { - this.inputContainer.setSlotFilter(FilterActionType.ADD, slot, new ResourceFilter(new ResourceQuantity(resourceTypeId, 1))); - } else if (shouldFilterValidIngredients) { - ObjectArrayList validIngredients = new ObjectArrayList<>(); - - for (CraftingRecipe recipe : CraftingPlugin.getBenchRecipes(this.bench.getType(), this.bench.getId())) { - if (!recipe.isRestrictedByBenchTierLevel(this.bench.getId(), tierLevel)) { - List inputMaterials = CraftingManager.getInputMaterials(recipe); - validIngredients.addAll(inputMaterials); - } - } - - this.inputContainer.setSlotFilter(FilterActionType.ADD, slot, (actionType, container, slotIndex, itemStack) -> { - if (itemStack == null) { - return true; - } else { - for (MaterialQuantity ingredient : validIngredients) { - if (CraftingManager.matches(ingredient, itemStack)) { - return true; - } - } - - return false; - } - }); - } - } - - input = this.processingBench.getFuel(); - inputSlotsCount = (short)(input != null ? input.length : 0); - this.fuelContainer = ItemContainer.ensureContainerCapacity(this.fuelContainer, inputSlotsCount, SimpleItemContainer::getNewContainer, remainder); - this.fuelContainer.registerChangeEvent(EventPriority.LAST, this::onItemChange); - if (inputSlotsCount > 0) { - for (int i = 0; i < input.length; i++) { - ProcessingBench.ProcessingSlot fuel = input[i]; - String resourceTypeId = fuel.getResourceTypeId(); - if (resourceTypeId != null) { - this.fuelContainer.setSlotFilter(FilterActionType.ADD, (short)i, new ResourceFilter(new ResourceQuantity(resourceTypeId, 1))); - } - } - } - - short outputSlotsCount = (short)this.processingBench.getOutputSlotsCount(tierLevel); - this.outputContainer = ItemContainer.ensureContainerCapacity(this.outputContainer, outputSlotsCount, SimpleItemContainer::getNewContainer, remainder); - this.outputContainer.registerChangeEvent(EventPriority.LAST, this::onItemChange); - if (outputSlotsCount > 0) { - this.outputContainer.setGlobalFilter(FilterType.ALLOW_OUTPUT_ONLY); - } - - this.combinedItemContainer = new CombinedItemContainer(this.fuelContainer, this.inputContainer, this.outputContainer); - World world = this.getChunk().getWorld(); - Store store = world.getEntityStore().getStore(); - Holder[] itemEntityHolders = this.ejectItems(store, remainder); - if (itemEntityHolders.length > 0) { - world.execute(() -> store.addEntities(itemEntityHolders, AddReason.SPAWN)); - } - - this.inputContainer.registerChangeEvent(EventPriority.LAST, event -> this.updateRecipe()); - if (this.processingBench.getFuel() == null) { - this.setActive(true); - } - } - - @Override - public void tick(float dt, int index, ArchetypeChunk archetypeChunk, @Nonnull Store store, CommandBuffer commandBuffer) { - World world = store.getExternalData().getWorld(); - Store entityStore = world.getEntityStore().getStore(); - BlockType blockType = this.getBlockType(); - String currentState = BlockAccessor.getCurrentInteractionState(blockType); - List outputItemStacks = null; - List inputMaterials = null; - this.processingSlots.clear(); - this.checkForRecipeUpdate(); - if (this.recipe != null) { - outputItemStacks = CraftingManager.getOutputItemStacks(this.recipe); - if (!this.outputContainer.canAddItemStacks(outputItemStacks, false, false)) { - if ("Processing".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getFailedSoundEventIndex(), entityStore); - } else if ("ProcessCompleted".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getEndSoundEventIndex(), entityStore); - } - - this.setActive(false); - return; - } - - inputMaterials = CraftingManager.getInputMaterials(this.recipe); - List result = this.inputContainer.getSlotMaterialsToRemove(inputMaterials, true, true); - if (result.isEmpty()) { - if ("Processing".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getFailedSoundEventIndex(), entityStore); - } else if ("ProcessCompleted".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getEndSoundEventIndex(), entityStore); - } - - this.inputProgress = 0.0F; - this.setActive(false); - this.recipeId = null; - this.recipe = null; - return; - } - - for (TestRemoveItemSlotResult item : result) { - this.processingSlots.addAll(item.getPickedSlots()); - } - - this.sendProcessingSlots(); - } else { - if (this.processingBench.getFuel() == null) { - if ("Processing".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getFailedSoundEventIndex(), entityStore); - } else if ("ProcessCompleted".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getEndSoundEventIndex(), entityStore); - } - - return; - } - - boolean allowNoInputProcessing = this.processingBench.shouldAllowNoInputProcessing(); - if (!allowNoInputProcessing && "Processing".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getFailedSoundEventIndex(), entityStore); - } else if ("ProcessCompleted".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getEndSoundEventIndex(), entityStore); - this.setActive(false); - this.sendProgress(0.0F); - return; - } - - this.sendProgress(0.0F); - if (!allowNoInputProcessing) { - this.setActive(false); - return; - } - } - - boolean needsUpdate = false; - if (this.fuelTime > 0.0F && this.active) { - this.fuelTime -= dt; - if (this.fuelTime < 0.0F) { - this.fuelTime = 0.0F; - } - - needsUpdate = true; - } - - ProcessingBench.ProcessingSlot[] fuelSlots = this.processingBench.getFuel(); - boolean hasFuelSlots = fuelSlots != null && fuelSlots.length > 0; - if ((this.processingBench.getMaxFuel() <= 0 || this.fuelTime < this.processingBench.getMaxFuel()) && !this.fuelContainer.isEmpty()) { - if (!hasFuelSlots) { - return; - } - - if (this.active) { - if (this.fuelTime > 0.0F) { - for (int i = 0; i < fuelSlots.length; i++) { - ItemStack itemInSlot = this.fuelContainer.getItemStack((short)i); - if (itemInSlot != null) { - this.processingFuelSlots.add((short)i); - break; - } - } - } else { - if (this.fuelTime < 0.0F) { - this.fuelTime = 0.0F; - } - - this.processingFuelSlots.clear(); - - for (int ix = 0; ix < fuelSlots.length; ix++) { - ProcessingBench.ProcessingSlot fuelSlot = fuelSlots[ix]; - String resourceTypeId = fuelSlot.getResourceTypeId() != null ? fuelSlot.getResourceTypeId() : "Fuel"; - ResourceQuantity resourceQuantity = new ResourceQuantity(resourceTypeId, 1); - ItemStack slot = this.fuelContainer.getItemStack((short)ix); - if (slot != null) { - double fuelQuality = slot.getItem().getFuelQuality(); - ResourceTransaction transaction = this.fuelContainer.removeResource(resourceQuantity, true, true, true); - this.processingFuelSlots.add((short)ix); - if (transaction.getRemainder() <= 0) { - ProcessingBench.ExtraOutput extra = this.processingBench.getExtraOutput(); - if (extra != null && !extra.isIgnoredFuelSource(slot.getItem())) { - this.nextExtra--; - if (this.nextExtra <= 0) { - this.nextExtra = extra.getPerFuelItemsConsumed(); - ObjectArrayList extraItemStacks = new ObjectArrayList<>(extra.getOutputs().length); - - for (MaterialQuantity e : extra.getOutputs()) { - extraItemStacks.add(e.toItemStack()); - } - - ListTransaction addTransaction = this.outputContainer.addItemStacks(extraItemStacks, false, false, false); - List remainderItems = new ObjectArrayList<>(); - - for (ItemStackTransaction itemStackTransaction : addTransaction.getList()) { - ItemStack remainder = itemStackTransaction.getRemainder(); - if (remainder != null && !remainder.isEmpty()) { - remainderItems.add(remainder); - } - } - - if (!remainderItems.isEmpty()) { - LOGGER.at(Level.WARNING).log("Dropping excess items at %s", this.getBlockPosition()); - Holder[] itemEntityHolders = this.ejectItems(entityStore, remainderItems); - entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); - } - } - } - - this.fuelTime = (float)(this.fuelTime + transaction.getConsumed() * fuelQuality); - needsUpdate = true; - break; - } - } - } - } - } - } - - if (needsUpdate) { - this.updateFuelValues(); - } - - if (!hasFuelSlots || this.active && !(this.fuelTime <= 0.0F)) { - if (!"Processing".equals(currentState)) { - this.setBlockInteractionState("Processing", blockType); - } - - if (this.recipe != null && (this.fuelTime > 0.0F || this.processingBench.getFuel() == null)) { - this.inputProgress += dt; - } - - if (this.recipe != null) { - float recipeTime = this.recipe.getTimeSeconds(); - float craftingTimeReductionModifier = this.getCraftingTimeReductionModifier(); - if (craftingTimeReductionModifier > 0.0F) { - recipeTime -= recipeTime * craftingTimeReductionModifier; - } - - if (this.inputProgress > recipeTime) { - if (recipeTime > 0.0F) { - this.inputProgress -= recipeTime; - float progressPercent = this.inputProgress / recipeTime; - this.sendProgress(progressPercent); - } else { - this.inputProgress = 0.0F; - this.sendProgress(0.0F); - } - - LOGGER.at(Level.FINE).log("Do Process for %s %s", this.recipeId, this.recipe); - if (inputMaterials != null) { - List remainderItems = new ObjectArrayList<>(); - int success = 0; - IntArrayList slots = new IntArrayList(); - - for (int j = 0; j < this.inputContainer.getCapacity(); j++) { - slots.add(j); - } - - for (MaterialQuantity material : inputMaterials) { - for (int ixx = 0; ixx < slots.size(); ixx++) { - int slot = slots.getInt(ixx); - MaterialSlotTransaction transaction = this.inputContainer.removeMaterialFromSlot((short)slot, material, true, true, true); - if (transaction.succeeded()) { - success++; - slots.removeInt(ixx); - break; - } - } - } - - ListTransaction addTransaction = this.outputContainer.addItemStacks(outputItemStacks, false, false, false); - if (!addTransaction.succeeded()) { - return; - } - - for (ItemStackTransaction itemStackTransactionx : addTransaction.getList()) { - ItemStack remainder = itemStackTransactionx.getRemainder(); - if (remainder != null && !remainder.isEmpty()) { - remainderItems.add(remainder); - } - } - - if (success == inputMaterials.size()) { - this.setBlockInteractionState("ProcessCompleted", blockType); - this.playSound(world, this.bench.getCompletedSoundEventIndex(), entityStore); - if (!remainderItems.isEmpty()) { - LOGGER.at(Level.WARNING).log("Dropping excess items at %s", this.getBlockPosition()); - Holder[] itemEntityHolders = this.ejectItems(entityStore, remainderItems); - entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); - } - - return; - } - } - - List remainderItems = new ObjectArrayList<>(); - ListTransaction transaction = this.inputContainer.removeMaterials(inputMaterials, true, true, true); - if (!transaction.succeeded()) { - LOGGER.at(Level.WARNING).log("Failed to remove input materials at %s", this.getBlockPosition()); - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getFailedSoundEventIndex(), entityStore); - return; - } - - this.setBlockInteractionState("ProcessCompleted", blockType); - this.playSound(world, this.bench.getCompletedSoundEventIndex(), entityStore); - ListTransaction addTransactionx = this.outputContainer.addItemStacks(outputItemStacks, false, false, false); - if (addTransactionx.succeeded()) { - return; - } - - LOGGER.at(Level.WARNING).log("Dropping excess items at %s", this.getBlockPosition()); - - for (ItemStackTransaction itemStackTransactionxx : addTransactionx.getList()) { - ItemStack remainder = itemStackTransactionxx.getRemainder(); - if (remainder != null && !remainder.isEmpty()) { - remainderItems.add(remainder); - } - } - - Holder[] itemEntityHolders = this.ejectItems(entityStore, remainderItems); - entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); - } else if (this.recipe != null && recipeTime > 0.0F) { - float progressPercent = this.inputProgress / recipeTime; - this.sendProgress(progressPercent); - } else { - this.sendProgress(0.0F); - } - } - } else { - this.lastConsumedFuelTotal = 0; - if ("Processing".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getFailedSoundEventIndex(), entityStore); - if (this.processingBench.getFuel() != null) { - this.setActive(false); - } - } else if ("ProcessCompleted".equals(currentState)) { - this.setBlockInteractionState("default", blockType); - this.playSound(world, this.processingBench.getFailedSoundEventIndex(), entityStore); - if (this.processingBench.getFuel() != null) { - this.setActive(false); - } - } - } - } - - private float getCraftingTimeReductionModifier() { - BenchTierLevel levelData = this.bench.getTierLevel(this.getTierLevel()); - return levelData != null ? levelData.getCraftingTimeReductionModifier() : 0.0F; - } - - @Nonnull - private Holder[] ejectItems(@Nonnull ComponentAccessor accessor, @Nonnull List itemStacks) { - if (itemStacks.isEmpty()) { - return Holder.emptyArray(); - } else { - RotationTuple rotation = RotationTuple.get(this.getRotationIndex()); - Vector3d frontDir = new Vector3d(0.0, 0.0, 1.0); - rotation.yaw().rotateY(frontDir, frontDir); - BlockType blockType = this.getBlockType(); - Vector3d dropPosition; - if (blockType == null) { - dropPosition = this.getBlockPosition().toVector3d().add(0.5, 0.0, 0.5); - } else { - BlockBoundingBoxes hitboxAsset = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); - if (hitboxAsset == null) { - dropPosition = this.getBlockPosition().toVector3d().add(0.5, 0.0, 0.5); - } else { - double depth = hitboxAsset.get(0).getBoundingBox().depth(); - double frontOffset = depth / 2.0 + 0.1F; - dropPosition = this.getCenteredBlockPosition(); - dropPosition.add(frontDir.x * frontOffset, 0.0, frontDir.z * frontOffset); - } - } - - ThreadLocalRandom random = ThreadLocalRandom.current(); - ObjectArrayList> result = new ObjectArrayList<>(itemStacks.size()); - - for (ItemStack item : itemStacks) { - float velocityX = (float)(frontDir.x * 2.0 + 2.0 * (random.nextDouble() - 0.5)); - float velocityZ = (float)(frontDir.z * 2.0 + 2.0 * (random.nextDouble() - 0.5)); - Holder holder = ItemComponent.generateItemDrop(accessor, item, dropPosition, Vector3f.ZERO, velocityX, 3.25F, velocityZ); - if (holder != null) { - result.add(holder); - } - } - - return result.toArray(Holder[]::new); - } - } - - private void sendProgress(float progress) { - this.windows.forEach((uuid, window) -> ((ProcessingBenchWindow)window).setProgress(progress)); - } - - private void sendProcessingSlots() { - this.windows.forEach((uuid, window) -> ((ProcessingBenchWindow)window).setProcessingSlots(this.processingSlots)); - } - - private void sendProcessingFuelSlots() { - this.windows.forEach((uuid, window) -> ((ProcessingBenchWindow)window).setProcessingFuelSlots(this.processingFuelSlots)); - } - - public boolean isActive() { - return this.active; - } - - public boolean setActive(boolean active) { - if (this.active != active) { - if (active && this.processingBench.getFuel() != null && this.fuelContainer.isEmpty()) { - return false; - } else { - this.active = active; - if (!active) { - this.processingSlots.clear(); - this.processingFuelSlots.clear(); - this.sendProcessingSlots(); - this.sendProcessingFuelSlots(); - } - - this.updateRecipe(); - this.windows.forEach((uuid, window) -> ((ProcessingBenchWindow)window).setActive(active)); - this.markNeedsSave(); - return true; - } - } else { - return false; - } - } - - public void updateFuelValues() { - if (this.fuelTime > this.lastConsumedFuelTotal) { - this.lastConsumedFuelTotal = MathUtil.ceil(this.fuelTime); - } - - float fuelPercent = this.lastConsumedFuelTotal > 0 ? this.fuelTime / this.lastConsumedFuelTotal : 0.0F; - this.windows.forEach((uuid, window) -> { - ProcessingBenchWindow processingBenchWindow = (ProcessingBenchWindow)window; - processingBenchWindow.setFuelTime(fuelPercent); - processingBenchWindow.setMaxFuel(this.lastConsumedFuelTotal); - processingBenchWindow.setProcessingFuelSlots(this.processingFuelSlots); - }); - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (this.combinedItemContainer != null) { - List itemStacks = this.combinedItemContainer.dropAllItemStacks(); - this.dropFuelItems(itemStacks); - World world = this.getChunk().getWorld(); - Store entityStore = world.getEntityStore().getStore(); - Vector3d dropPosition = this.getBlockPosition().toVector3d().add(0.5, 0.0, 0.5); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO); - if (itemEntityHolders.length > 0) { - world.execute(() -> entityStore.addEntities(itemEntityHolders, AddReason.SPAWN)); - } - } - } - - public CombinedItemContainer getItemContainer() { - return this.combinedItemContainer; - } - - private void checkForRecipeUpdate() { - if (this.recipe == null && this.recipeId != null) { - this.updateRecipe(); - } - } - - private void updateRecipe() { - List recipes = CraftingPlugin.getBenchRecipes(this.bench.getType(), this.bench.getId()); - if (recipes.isEmpty()) { - this.clearRecipe(); - } else { - List matching = new ObjectArrayList<>(); - - for (CraftingRecipe recipe : recipes) { - if (!recipe.isRestrictedByBenchTierLevel(this.bench.getId(), this.getTierLevel())) { - MaterialQuantity[] input = recipe.getInput(); - int matches = 0; - IntArrayList slots = new IntArrayList(); - - for (int j = 0; j < this.inputContainer.getCapacity(); j++) { - slots.add(j); - } - - for (MaterialQuantity craftingMaterial : input) { - String itemId = craftingMaterial.getItemId(); - String resourceTypeId = craftingMaterial.getResourceTypeId(); - int materialQuantity = craftingMaterial.getQuantity(); - BsonDocument metadata = craftingMaterial.getMetadata(); - MaterialQuantity material = new MaterialQuantity(itemId, resourceTypeId, null, materialQuantity, metadata); - - for (int k = 0; k < slots.size(); k++) { - int j = slots.getInt(k); - int out = InternalContainerUtilMaterial.testRemoveMaterialFromSlot(this.inputContainer, (short)j, material, material.getQuantity(), true); - if (out == 0) { - matches++; - slots.removeInt(k); - break; - } - } - } - - if (matches == input.length) { - matching.add(recipe); - } - } - } - - if (matching.isEmpty()) { - this.clearRecipe(); - } else { - matching.sort(Comparator.comparingInt(o -> CraftingManager.getInputMaterials(o).size())); - Collections.reverse(matching); - if (this.recipeId != null) { - for (CraftingRecipe rec : matching) { - if (Objects.equals(this.recipeId, rec.getId())) { - LOGGER.at(Level.FINE).log("%s - Keeping existing Recipe %s %s", LazyArgs.lazy(this::getBlockPosition), this.recipeId, rec); - this.recipe = rec; - return; - } - } - } - - CraftingRecipe recipex = matching.getFirst(); - if (this.recipeId == null || !Objects.equals(this.recipeId, recipex.getId())) { - this.inputProgress = 0.0F; - this.sendProgress(0.0F); - } - - this.recipeId = recipex.getId(); - this.recipe = recipex; - LOGGER.at(Level.FINE).log("%s - Found Recipe %s %s", LazyArgs.lazy(this::getBlockPosition), this.recipeId, this.recipe); - } - } - } - - private void clearRecipe() { - this.recipeId = null; - this.recipe = null; - this.lastConsumedFuelTotal = 0; - this.inputProgress = 0.0F; - this.sendProgress(0.0F); - LOGGER.at(Level.FINE).log("%s - Cleared Recipe", LazyArgs.lazy(this::getBlockPosition)); - } - - public void dropFuelItems(@Nonnull List itemStacks) { - String fuelDropItemId = this.processingBench.getFuelDropItemId(); - if (fuelDropItemId != null) { - Item item = Item.getAssetMap().getAsset(fuelDropItemId); - int dropAmount = (int)this.fuelTime; - this.fuelTime = 0.0F; - - while (dropAmount > 0) { - int quantity = Math.min(dropAmount, item.getMaxStack()); - itemStacks.add(new ItemStack(fuelDropItemId, quantity)); - dropAmount -= quantity; - } - } else { - LOGGER.at(Level.WARNING).log("No FuelDropItemId defined for %s fuel value of %s will be lost!", this.bench.getId(), this.fuelTime); - } - } - - @Nullable - public CraftingRecipe getRecipe() { - return this.recipe; - } - - public float getInputProgress() { - return this.inputProgress; - } - - public void onItemChange(ItemContainer.ItemContainerChangeEvent event) { - this.markNeedsSave(); - } - - public void setBlockInteractionState(@Nonnull String state, @Nonnull BlockType blockType) { - this.getChunk().setBlockInteractionState(this.getBlockPosition(), blockType, state); - } - - @Override - public void placedBy( - @Nonnull Ref playerRef, - @Nonnull String blockTypeKey, - @Nonnull BlockState blockState, - @Nonnull ComponentAccessor componentAccessor - ) { - } - - private void playSound(@Nonnull World world, int soundEventIndex, @Nonnull ComponentAccessor componentAccessor) { - if (soundEventIndex != 0) { - Vector3i pos = this.getBlockPosition(); - WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); - int rotationIndex = chunk.getRotationIndex(pos.x, pos.y, pos.z); - Vector3d soundPos = new Vector3d(); - BlockType blockType = this.getBlockType(); - if (blockType != null) { - blockType.getBlockCenter(rotationIndex, soundPos); - } - - soundPos.add(pos); - SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, soundPos, componentAccessor); - } - } - - @Override - protected void onTierLevelChange() { - super.onTierLevelChange(); - this.setupSlots(); - } -} diff --git a/src/com/hypixel/hytale/builtin/crafting/system/BenchSystems.java b/src/com/hypixel/hytale/builtin/crafting/system/BenchSystems.java new file mode 100644 index 00000000..15e662c3 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/crafting/system/BenchSystems.java @@ -0,0 +1,490 @@ +package com.hypixel.hytale.builtin.crafting.system; + +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; +import com.hypixel.hytale.builtin.crafting.component.CraftingManager; +import com.hypixel.hytale.builtin.crafting.component.ProcessingBenchBlock; +import com.hypixel.hytale.builtin.crafting.window.BenchWindow; +import com.hypixel.hytale.component.AddReason; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.RemoveReason; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.system.RefSystem; +import com.hypixel.hytale.component.system.tick.EntityTickingSystem; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Bench; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.ProcessingBench; +import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; +import com.hypixel.hytale.server.core.entity.entities.player.windows.WindowManager; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.MaterialQuantity; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; +import com.hypixel.hytale.server.core.inventory.container.TestRemoveItemSlotResult; +import com.hypixel.hytale.server.core.modules.block.BlockModule; +import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; +import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.accessor.BlockAccessor; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; + +public class BenchSystems { + private static void dropUpgradeItems( + CommandBuffer commandBuffer, @Nullable BlockType type, BenchBlock benchBlock, int rotation, int x, int y, int z + ) { + ItemStack[] upgradeItems = benchBlock.getUpgradeItems(); + if (upgradeItems.length != 0) { + World world = commandBuffer.getExternalData().getWorld(); + Store entityStore = world.getEntityStore().getStore(); + Vector3d dropPosition = new Vector3d(); + if (type != null) { + type.getBlockCenter(rotation, dropPosition); + } else { + dropPosition.set(0.5, 0.5, 0.5); + } + + dropPosition.add(x, y, z); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, List.of(upgradeItems), dropPosition, Rotation3f.IDENTITY); + if (itemEntityHolders.length > 0) { + world.execute(() -> entityStore.addEntities(itemEntityHolders, AddReason.SPAWN)); + } + + benchBlock.setUpgradeItems(ItemStack.EMPTY_ARRAY); + } + } + + public static class OnAddOrRemoved extends RefSystem { + private final ComponentType blockStateInfoComponentType = BlockModule.BlockStateInfo.getComponentType(); + private final ComponentType benchBlockComponentType = BenchBlock.getComponentType(); + private final Query query = Query.and(this.benchBlockComponentType, this.blockStateInfoComponentType); + + @Override + public void onEntityAdded( + @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + BlockModule.BlockStateInfo blockStateInfoComponent = commandBuffer.getComponent(ref, this.blockStateInfoComponentType); + + assert blockStateInfoComponent != null; + + BenchBlock benchBlockComponent = commandBuffer.getComponent(ref, this.benchBlockComponentType); + + assert benchBlockComponent != null; + + Ref chunkRef = blockStateInfoComponent.getChunkRef(); + if (chunkRef.isValid()) { + int index = blockStateInfoComponent.getIndex(); + int x = ChunkUtil.xFromBlockInColumn(index); + int y = ChunkUtil.yFromBlockInColumn(index); + int z = ChunkUtil.zFromBlockInColumn(index); + BlockChunk blockChunkComponent = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockChunk blockChunk = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + + assert blockChunk != null; + + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(y); + int blockId = blockSection.get(x, y, z); + BlockType blockType = BlockType.getAssetMap().getAsset(blockId); + if (blockType != null && blockType.getBlockEntity() != null) { + Bench bench = blockType.getBench(); + if (bench == null) { + BenchSystems.dropUpgradeItems( + commandBuffer, + blockType, + benchBlockComponent, + blockChunk.getSectionAtBlockY(y).getRotationIndex(x, y, z), + ChunkUtil.worldCoordFromLocalCoord(blockChunk.getX(), x), + y, + ChunkUtil.worldCoordFromLocalCoord(blockChunk.getZ(), z) + ); + } + } + } + } + + @Override + public void onEntityRemove( + @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + if (reason != RemoveReason.UNLOAD) { + BlockModule.BlockStateInfo blockStateInfoComponent = commandBuffer.getComponent(ref, this.blockStateInfoComponentType); + + assert blockStateInfoComponent != null; + + BenchBlock benchBlockComponent = commandBuffer.getComponent(ref, this.benchBlockComponentType); + + assert benchBlockComponent != null; + + Ref chunkRef = blockStateInfoComponent.getChunkRef(); + if (chunkRef.isValid()) { + int index = blockStateInfoComponent.getIndex(); + int x = ChunkUtil.xFromBlockInColumn(index); + int y = ChunkUtil.yFromBlockInColumn(index); + int z = ChunkUtil.zFromBlockInColumn(index); + BlockChunk blockChunkComponent = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockChunk blockChunk = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + + assert blockChunk != null; + + WindowManager.closeAndRemoveAll(benchBlockComponent.getWindows()); + BenchSystems.dropUpgradeItems( + commandBuffer, + null, + benchBlockComponent, + 0, + ChunkUtil.worldCoordFromLocalCoord(blockChunk.getX(), x), + y, + ChunkUtil.worldCoordFromLocalCoord(blockChunk.getZ(), z) + ); + } + } + } + + @Nullable + @Override + public Query getQuery() { + return this.query; + } + } + + public static class ProcessingBenchLifecycle extends RefSystem { + private final ComponentType componentType; + private final ComponentType benchBlockComponentType; + private final ComponentType blockStateInfoComponentType = BlockModule.BlockStateInfo.getComponentType(); + + public ProcessingBenchLifecycle( + @Nonnull ComponentType componentType, @Nonnull ComponentType benchBlockComponentType + ) { + this.componentType = componentType; + this.benchBlockComponentType = benchBlockComponentType; + } + + @Override + public Query getQuery() { + return this.componentType; + } + + @Override + public void onEntityAdded( + @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + ProcessingBenchBlock processingBenchBlock = commandBuffer.getComponent(ref, this.componentType); + BenchBlock benchBlock = commandBuffer.getComponent(ref, this.benchBlockComponentType); + BlockModule.BlockStateInfo blockStateInfo = commandBuffer.getComponent(ref, this.blockStateInfoComponentType); + if (processingBenchBlock != null && benchBlock != null && blockStateInfo != null) { + Ref chunkRef = blockStateInfo.getChunkRef(); + if (chunkRef.isValid()) { + BlockChunk blockChunk = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + if (blockChunk != null) { + int blockIndex = blockStateInfo.getIndex(); + int localX = ChunkUtil.xFromBlockInColumn(blockIndex); + int localY = ChunkUtil.yFromBlockInColumn(blockIndex); + int localZ = ChunkUtil.zFromBlockInColumn(blockIndex); + int blockX = ChunkUtil.worldCoordFromLocalCoord(blockChunk.getX(), localX); + int blockZ = ChunkUtil.worldCoordFromLocalCoord(blockChunk.getZ(), localZ); + BlockSection blockSection = blockChunk.getSectionAtBlockY(localY); + int blockId = blockSection.get(localX, localY, localZ); + BlockType blockType = BlockType.getAssetMap().getAsset(blockId); + if (blockType != null) { + int rotationIndex = blockSection.getRotationIndex(localX, localY, localZ); + if (processingBenchBlock.initializeBenchConfig(blockType)) { + World world = commandBuffer.getExternalData().getWorld(); + processingBenchBlock.setupSlots(world, benchBlock, blockStateInfo, blockX, localY, blockZ, blockType, rotationIndex); + } + } + } + } + } + } + + @Override + public void onEntityRemove( + @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + if (reason == RemoveReason.UNLOAD) { + BlockModule.BlockStateInfo blockStateInfo = commandBuffer.getComponent(ref, this.blockStateInfoComponentType); + if (blockStateInfo != null) { + blockStateInfo.markNeedsSaving(); + } + } else { + ProcessingBenchBlock processingBenchBlock = commandBuffer.getComponent(ref, this.componentType); + if (processingBenchBlock != null) { + BlockModule.BlockStateInfo blockStateInfo = commandBuffer.getComponent(ref, this.blockStateInfoComponentType); + if (blockStateInfo != null) { + Ref chunkRef = blockStateInfo.getChunkRef(); + if (chunkRef.isValid()) { + BlockChunk blockChunk = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + if (blockChunk != null) { + int blockIndex = blockStateInfo.getIndex(); + int localX = ChunkUtil.xFromBlockInColumn(blockIndex); + int localY = ChunkUtil.yFromBlockInColumn(blockIndex); + int localZ = ChunkUtil.zFromBlockInColumn(blockIndex); + int blockX = ChunkUtil.worldCoordFromLocalCoord(blockChunk.getX(), localX); + int blockZ = ChunkUtil.worldCoordFromLocalCoord(blockChunk.getZ(), localZ); + CombinedItemContainer combinedItemContainer = processingBenchBlock.getItemContainer(); + if (combinedItemContainer != null) { + List itemStacks = combinedItemContainer.dropAllItemStacks(); + processingBenchBlock.dropFuelItems(itemStacks); + World world = commandBuffer.getExternalData().getWorld(); + Store entityStore = world.getEntityStore().getStore(); + Vector3d dropPosition = new Vector3d(blockX + 0.5, localY, blockZ + 0.5); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Rotation3f.IDENTITY); + if (itemEntityHolders.length > 0) { + world.execute(() -> entityStore.addEntities(itemEntityHolders, AddReason.SPAWN)); + } + } + } + } + } + } + } + } + } + + public static class ProcessingBenchTick extends EntityTickingSystem { + private final ComponentType componentType; + private final ComponentType benchBlockComponentType; + private final ComponentType blockStateInfoComponentType = BlockModule.BlockStateInfo.getComponentType(); + + public ProcessingBenchTick( + @Nonnull ComponentType componentType, @Nonnull ComponentType benchBlockComponentType + ) { + this.componentType = componentType; + this.benchBlockComponentType = benchBlockComponentType; + } + + @Override + public Query getQuery() { + return this.componentType; + } + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + ProcessingBenchBlock processingBenchBlock = archetypeChunk.getComponent(index, this.componentType); + BenchBlock benchBlock = archetypeChunk.getComponent(index, this.benchBlockComponentType); + BlockModule.BlockStateInfo blockStateInfo = archetypeChunk.getComponent(index, this.blockStateInfoComponentType); + if (benchBlock != null && blockStateInfo != null) { + Ref chunkRef = blockStateInfo.getChunkRef(); + if (chunkRef.isValid()) { + BlockChunk blockChunk = store.getComponent(chunkRef, BlockChunk.getComponentType()); + if (blockChunk != null) { + int blockIndex = blockStateInfo.getIndex(); + int localX = ChunkUtil.xFromBlockInColumn(blockIndex); + int localY = ChunkUtil.yFromBlockInColumn(blockIndex); + int localZ = ChunkUtil.zFromBlockInColumn(blockIndex); + int blockX = ChunkUtil.worldCoordFromLocalCoord(blockChunk.getX(), localX); + int blockZ = ChunkUtil.worldCoordFromLocalCoord(blockChunk.getZ(), localZ); + BlockSection blockSection = blockChunk.getSectionAtBlockY(localY); + int blockId = blockSection.get(localX, localY, localZ); + BlockType blockType = BlockType.getAssetMap().getAsset(blockId); + if (blockType != null) { + int rotationIndex = blockSection.getRotationIndex(localX, localY, localZ); + World world = store.getExternalData().getWorld(); + Store entityStore = world.getEntityStore().getStore(); + ProcessingBench processingBench = processingBenchBlock.getProcessingBench(); + if (processingBench != null) { + Instant currentGameTime = entityStore.getResource(WorldTimeResource.getResourceType()).getGameTime(); + float effectiveDt; + if (processingBenchBlock.getLastTickGameTime() != null + && currentGameTime != null + && !currentGameTime.equals(processingBenchBlock.getLastTickGameTime())) { + float gameElapsedSeconds = (float)Math.max( + 0L, currentGameTime.toEpochMilli() - processingBenchBlock.getLastTickGameTime().toEpochMilli() + ) + / 1000.0F; + effectiveDt = (float)(gameElapsedSeconds / WorldTimeResource.getSecondsPerTick(world)); + } else { + effectiveDt = 0.0F; + } + + String currentState = BlockAccessor.getCurrentInteractionState(blockType); + Map windows = benchBlock.getWindows(); + processingBenchBlock.getProcessingSlots().clear(); + processingBenchBlock.checkForRecipeUpdate(benchBlock); + boolean hasFuelSlots = processingBench.getFuel() != null; + boolean canProcess = false; + CraftingRecipe recipe = processingBenchBlock.getRecipe(); + if (recipe != null) { + List outputItemStacks = CraftingManager.getOutputItemStacks(recipe); + if (!processingBenchBlock.getOutputContainer().canAddItemStacks(outputItemStacks, false, false)) { + if ("Processing".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getFailedSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + } else if ("ProcessCompleted".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getEndSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + } + + processingBenchBlock.setActive(false, benchBlock, blockStateInfo); + processingBenchBlock.setLastTickGameTime(currentGameTime); + return; + } + + List inputMaterials = CraftingManager.getInputMaterials(recipe); + List result = processingBenchBlock.getInputContainer() + .getSlotMaterialsToRemove(inputMaterials, true, true); + if (result.isEmpty()) { + if ("Processing".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getFailedSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + } else if ("ProcessCompleted".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getEndSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + } + + processingBenchBlock.setInputProgress(0.0F); + processingBenchBlock.setActive(false, benchBlock, blockStateInfo); + processingBenchBlock.clearCurrentRecipe(); + processingBenchBlock.setLastTickGameTime(currentGameTime); + return; + } + + for (TestRemoveItemSlotResult item : result) { + processingBenchBlock.getProcessingSlots().addAll(item.getPickedSlots()); + } + + processingBenchBlock.sendProcessingSlots(windows); + canProcess = true; + } else { + if (!hasFuelSlots) { + if ("Processing".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getFailedSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + } else if ("ProcessCompleted".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getEndSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + } + + processingBenchBlock.setLastTickGameTime(currentGameTime); + return; + } + + boolean allowNoInputProcessing = processingBench.shouldAllowNoInputProcessing(); + if (!allowNoInputProcessing && "Processing".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getFailedSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + } else if ("ProcessCompleted".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getEndSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + processingBenchBlock.setActive(false, benchBlock, blockStateInfo); + processingBenchBlock.sendProgress(0.0F, windows); + processingBenchBlock.setLastTickGameTime(currentGameTime); + return; + } + + processingBenchBlock.sendProgress(0.0F, windows); + if (!allowNoInputProcessing) { + processingBenchBlock.setActive(false, benchBlock, blockStateInfo); + processingBenchBlock.setLastTickGameTime(currentGameTime); + return; + } + } + + int completions = processingBenchBlock.advanceProcessing( + effectiveDt, entityStore, benchBlock, blockStateInfo, blockX, localY, blockZ, blockType, rotationIndex + ); + if (!canProcess && processingBenchBlock.isActive()) { + processingBenchBlock.consumeFuelForDuration(effectiveDt, entityStore, blockX, localY, blockZ, blockType, rotationIndex); + } + + processingBenchBlock.getProcessingFuelSlots().clear(); + if (hasFuelSlots) { + ProcessingBench.ProcessingSlot[] fuelSlots = processingBench.getFuel(); + if (processingBenchBlock.isActive() && processingBenchBlock.getFuelTime() > 0.0F && fuelSlots != null) { + for (int i = 0; i < fuelSlots.length; i++) { + if (processingBenchBlock.getFuelContainer().getItemStack((short)i) != null) { + processingBenchBlock.getProcessingFuelSlots().add((short)i); + break; + } + } + } + + if (!processingBenchBlock.isActive() || processingBenchBlock.getFuelTime() <= 0.0F) { + processingBenchBlock.setLastConsumedFuelTotal(0); + if ("Processing".equals(currentState) || "ProcessCompleted".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("default", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBench.getFailedSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + processingBenchBlock.setActive(false, benchBlock, blockStateInfo); + } + + processingBenchBlock.setLastTickGameTime(currentGameTime); + return; + } + + processingBenchBlock.updateFuelValues(windows); + } + + if (completions > 0) { + processingBenchBlock.setBlockInteractionState("ProcessCompleted", blockType, world, blockX, localY, blockZ); + processingBenchBlock.playSound( + processingBenchBlock.getBench().getCompletedSoundEventIndex(), entityStore, blockType, rotationIndex, blockX, localY, blockZ + ); + } else { + if (!"Processing".equals(currentState)) { + processingBenchBlock.setBlockInteractionState("Processing", blockType, world, blockX, localY, blockZ); + } + + if (canProcess) { + int tierLevel = benchBlock.getTierLevel(); + float recipeTime = processingBenchBlock.getRecipeTimeSeconds(tierLevel); + if (processingBenchBlock.getRecipe() != null && recipeTime > 0.0F) { + processingBenchBlock.sendProgress(processingBenchBlock.getInputProgress() / recipeTime, windows); + } else { + processingBenchBlock.sendProgress(0.0F, windows); + } + } + } + + processingBenchBlock.setLastTickGameTime(currentGameTime); + } + } + } + } + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/crafting/system/PlayerCraftingSystems.java b/src/com/hypixel/hytale/builtin/crafting/system/PlayerCraftingSystems.java index 572f65d9..3543975f 100644 --- a/src/com/hypixel/hytale/builtin/crafting/system/PlayerCraftingSystems.java +++ b/src/com/hypixel/hytale/builtin/crafting/system/PlayerCraftingSystems.java @@ -14,7 +14,6 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -39,14 +38,6 @@ public class PlayerCraftingSystems { @Override public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - World world = store.getExternalData().getWorld(); - if (world.getWorldConfig().isSavingPlayers()) { - Player playerComponent = holder.getComponent(this.playerComponentType); - - assert playerComponent != null; - - playerComponent.saveConfig(world, holder); - } } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/crafting/window/BenchWindow.java b/src/com/hypixel/hytale/builtin/crafting/window/BenchWindow.java index 7318dac6..f67f2aa4 100644 --- a/src/com/hypixel/hytale/builtin/crafting/window/BenchWindow.java +++ b/src/com/hypixel/hytale/builtin/crafting/window/BenchWindow.java @@ -2,8 +2,8 @@ package com.hypixel.hytale.builtin.crafting.window; import com.google.gson.JsonObject; import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; import com.hypixel.hytale.builtin.crafting.component.CraftingManager; -import com.hypixel.hytale.builtin.crafting.state.BenchState; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; @@ -17,6 +17,7 @@ import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.entities.player.windows.BlockWindow; import com.hypixel.hytale.server.core.entity.entities.player.windows.MaterialContainerWindow; import com.hypixel.hytale.server.core.entity.entities.player.windows.MaterialExtraResourcesSection; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; @@ -32,19 +33,19 @@ public abstract class BenchWindow extends BlockWindow implements MaterialContain private long lastUpdateTimeMs; protected final Bench bench; @Nonnull - protected final BenchState benchState; + protected final BenchBlock benchBlock; @Nonnull protected final JsonObject windowData = new JsonObject(); @Nonnull private final MaterialExtraResourcesSection extraResourcesSection = new MaterialExtraResourcesSection(); - public BenchWindow(@Nonnull WindowType windowType, @Nonnull BenchState benchState) { - super(windowType, benchState.getBlockX(), benchState.getBlockY(), benchState.getBlockZ(), benchState.getRotationIndex(), benchState.getBlockType()); - this.bench = this.blockType.getBench(); - this.benchState = benchState; - Item item = this.blockType.getItem(); + public BenchWindow(@Nonnull WindowType windowType, int x, int y, int z, int rotationIndex, @Nonnull BlockType blockType, @Nonnull BenchBlock benchBlock) { + super(windowType, x, y, z, rotationIndex, blockType); + this.bench = blockType.getBench(); + this.benchBlock = benchBlock; + Item item = blockType.getItem(); if (item == null) { - throw new IllegalStateException("Bench block type " + this.blockType.getId() + " does not have an associated item!"); + throw new IllegalStateException("Bench block type " + blockType.getId() + " does not have an associated item!"); } else { this.windowData.addProperty("type", this.bench.getType().ordinal()); this.windowData.addProperty("id", this.bench.getId()); @@ -70,7 +71,9 @@ public abstract class BenchWindow extends BlockWindow implements MaterialContain World world = store.getExternalData().getWorld(); int memoriesLevel = MemoriesPlugin.get().getMemoriesLevel(world.getGameplayConfig()); this.windowData.addProperty("worldMemoriesLevel", memoriesLevel); - int chestCount = CraftingManager.feedExtraResourcesSection(this.benchState, this.extraResourcesSection); + int chestCount = CraftingManager.feedExtraResourcesSection( + world, this.x, this.y, this.z, this.blockType, this.rotationIndex, this.bench, this.getBenchTierLevel(), this.extraResourcesSection + ); CraftingConfig craftingConfig = world.getGameplayConfig().getCraftingConfig(); int maxChestCount = craftingConfig.getBenchMaterialChestLimit(); int horizontalRadius = craftingConfig.getBenchMaterialHorizontalChestSearchRadius(); @@ -84,13 +87,13 @@ public abstract class BenchWindow extends BlockWindow implements MaterialContain } protected int getBenchTierLevel() { - return this.benchState != null ? this.benchState.getTierLevel() : 1; + return this.benchBlock != null ? this.benchBlock.getTierLevel() : 1; } @Override public void onClose0(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { World world = componentAccessor.getExternalData().getWorld(); - this.setBlockInteractionState(this.benchState.getTierStateName(), world); + this.setBlockInteractionState(this.benchBlock.getTierStateName(), world); CraftingManager craftingManagerComponent = componentAccessor.getComponent(ref, CraftingManager.getComponentType()); if (craftingManagerComponent != null) { if (craftingManagerComponent.clearBench(ref, componentAccessor) && this.bench.getFailedSoundEventIndex() != 0) { @@ -109,6 +112,10 @@ public abstract class BenchWindow extends BlockWindow implements MaterialContain } } + public void updateQueueSize(int size) { + this.windowData.addProperty("queueSize", size); + } + public void updateCraftingJob(float percent) { this.windowData.addProperty("progress", percent); this.checkProgressInvalidate(percent); @@ -145,7 +152,16 @@ public abstract class BenchWindow extends BlockWindow implements MaterialContain @Override public MaterialExtraResourcesSection getExtraResourcesSection() { if (!this.extraResourcesSection.isValid()) { - CraftingManager.feedExtraResourcesSection(this.benchState, this.extraResourcesSection); + PlayerRef playerRef = this.getPlayerRef(); + if (playerRef != null) { + Ref ref = playerRef.getReference(); + if (ref != null && ref.isValid()) { + World world = ref.getStore().getExternalData().getWorld(); + CraftingManager.feedExtraResourcesSection( + world, this.x, this.y, this.z, this.blockType, this.rotationIndex, this.bench, this.getBenchTierLevel(), this.extraResourcesSection + ); + } + } } return this.extraResourcesSection; diff --git a/src/com/hypixel/hytale/builtin/crafting/window/CraftingWindow.java b/src/com/hypixel/hytale/builtin/crafting/window/CraftingWindow.java index 2022ad43..737d6e25 100644 --- a/src/com/hypixel/hytale/builtin/crafting/window/CraftingWindow.java +++ b/src/com/hypixel/hytale/builtin/crafting/window/CraftingWindow.java @@ -4,18 +4,21 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.hypixel.hytale.builtin.adventure.memories.MemoriesGameplayConfig; import com.hypixel.hytale.builtin.crafting.CraftingPlugin; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; import com.hypixel.hytale.builtin.crafting.component.CraftingManager; -import com.hypixel.hytale.builtin.crafting.state.BenchState; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.packets.window.CraftRecipeAction; import com.hypixel.hytale.protocol.packets.window.WindowType; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.CraftingBench; import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -28,8 +31,8 @@ public abstract class CraftingWindow extends BenchWindow { @Nonnull protected static final String CRAFT_COMPLETED_INSTANT = "CraftCompletedInstant"; - public CraftingWindow(@Nonnull WindowType windowType, @Nonnull BenchState benchState) { - super(windowType, benchState); + public CraftingWindow(@Nonnull WindowType windowType, int x, int y, int z, int rotationIndex, @Nonnull BlockType blockType, @Nonnull BenchBlock benchBlock) { + super(windowType, x, y, z, rotationIndex, blockType, benchBlock); JsonArray categories = new JsonArray(); if (this.bench instanceof CraftingBench craftingBench) { for (CraftingBench.BenchCategory benchCategory : craftingBench.getCategories()) { @@ -117,14 +120,11 @@ public abstract class CraftingWindow extends BenchWindow { assert playerRef != null; - playerRef.getPacketHandler().disconnect("Attempted to craft unknown recipe!"); + playerRef.getPacketHandler().disconnect(Message.translation("server.general.disconnect.unknownRecipe")); return false; } else { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - craftingManager.craftItem(ref, store, recipe, quantity, playerComponent.getInventory().getCombinedBackpackStorageHotbar()); + CombinedItemContainer combinedBackpackStorageHotbar = InventoryComponent.getCombined(store, ref, InventoryComponent.BACKPACK_STORAGE_HOTBAR); + craftingManager.craftItem(ref, store, recipe, quantity, combinedBackpackStorageHotbar); return true; } } diff --git a/src/com/hypixel/hytale/builtin/crafting/window/DiagramCraftingWindow.java b/src/com/hypixel/hytale/builtin/crafting/window/DiagramCraftingWindow.java index b3510dfa..0fe13723 100644 --- a/src/com/hypixel/hytale/builtin/crafting/window/DiagramCraftingWindow.java +++ b/src/com/hypixel/hytale/builtin/crafting/window/DiagramCraftingWindow.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.crafting.window; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.hypixel.hytale.builtin.crafting.CraftingPlugin; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; import com.hypixel.hytale.builtin.crafting.component.CraftingManager; -import com.hypixel.hytale.builtin.crafting.state.BenchState; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; @@ -18,12 +18,13 @@ import com.hypixel.hytale.protocol.packets.window.UpdateCategoryAction; import com.hypixel.hytale.protocol.packets.window.WindowAction; import com.hypixel.hytale.protocol.packets.window.WindowType; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.CraftingBench; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.DiagramCraftingBench; import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.windows.ItemContainerWindow; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; @@ -57,8 +58,17 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain private CombinedItemContainer combinedItemContainer; private EventRegistration inventoryRegistration; - public DiagramCraftingWindow(@Nonnull Ref ref, @Nonnull ComponentAccessor store, @Nonnull BenchState benchState) { - super(WindowType.DiagramCrafting, benchState); + public DiagramCraftingWindow( + @Nonnull Ref ref, + @Nonnull ComponentAccessor store, + int x, + int y, + int z, + int rotationIndex, + @Nonnull BlockType blockType, + @Nonnull BenchBlock benchBlock + ) { + super(WindowType.DiagramCrafting, x, y, z, rotationIndex, blockType, benchBlock); DiagramCraftingBench bench = (DiagramCraftingBench)this.bench; if (bench.getCategories() != null && bench.getCategories().length > 0) { CraftingBench.BenchCategory benchCategory = bench.getCategories()[0]; @@ -86,15 +96,11 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain @Override public boolean onOpen0(@Nonnull Ref ref, @Nonnull Store store) { boolean result = super.onOpen0(ref, store); - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - Inventory inventory = playerComponent.getInventory(); + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(store, ref, InventoryComponent.HOTBAR_FIRST); this.updateInput(null, ref, store); - this.inventoryRegistration = inventory.getCombinedHotbarFirst().registerChangeEvent(event -> { + this.inventoryRegistration = combinedInventory.registerChangeEvent(event -> { ObjectList recipes = new ObjectArrayList<>(); - this.windowData.add("slots", this.generateSlots(inventory.getCombinedHotbarFirst(), recipes)); + this.windowData.add("slots", this.generateSlots(combinedInventory, recipes)); this.invalidate(); }); return result; @@ -102,12 +108,9 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain @Override public void onClose0(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - List itemStacks = this.combinedInputItemContainer.dropAllItemStacks(); - SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, playerComponent.getInventory().getCombinedHotbarFirst(), itemStacks); + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); + SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, combinedInventory, itemStacks); CraftingManager craftingManagerComponent = componentAccessor.getComponent(ref, CraftingManager.getComponentType()); assert craftingManagerComponent != null; @@ -144,7 +147,7 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain } break; case CraftItemAction ignoredx: - label59: { + label63: { ItemStack itemStack = this.outputContainer.getItemStack((short)0); if (itemStack == null || itemStack.isEmpty()) { playerRefComponent.sendMessage(Message.translation("server.ui.diagramcraftingwindow.noOutputItem")); @@ -159,7 +162,12 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain } CraftingRecipe recipe = recipes.getFirst(); - craftingManagerComponent.queueCraft(ref, store, this, 0, recipe, 1, this.combinedInputItemContainer, CraftingManager.InputRemovalType.ORDERED); + if (craftingManagerComponent.queueCraft( + ref, store, this, 0, recipe, 1, this.combinedInputItemContainer, CraftingManager.InputRemovalType.ORDERED + )) { + this.updateQueueSize(craftingManagerComponent.getRemainingQueueSize()); + } + String completedState = recipe.getTimeSeconds() > 0.0F ? "CraftCompleted" : "CraftCompletedInstant"; this.setBlockInteractionState(completedState, world); if (this.bench.getCompletedSoundEventIndex() != 0) { @@ -169,7 +177,7 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain if (CraftingPlugin.learnRecipe(ref, recipe.getId(), store)) { this.updateInput(this.outputContainer, ref, store); } - break label59; + break label63; } default: } @@ -206,12 +214,9 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain @Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor, @Nonnull CraftingBench.BenchItemCategory benchItemCategory ) { if (this.combinedInputItemContainer != null) { - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - List itemStacks = this.combinedInputItemContainer.dropAllItemStacks(); - SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, playerComponent.getInventory().getCombinedHotbarFirst(), itemStacks); + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); + SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, combinedInventory, itemStacks); } this.inputPrimaryContainer = new SimpleItemContainer((short)1); @@ -240,8 +245,8 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain assert playerComponent != null; + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(store, ref, InventoryComponent.HOTBAR_FIRST); ItemStack primaryItemStack = this.inputPrimaryContainer.getItemStack((short)0); - CombinedItemContainer combinedStorage = playerComponent.getInventory().getCombinedHotbarFirst(); if (primaryItemStack != null && !primaryItemStack.isEmpty()) { this.inputSecondaryContainer.setGlobalFilter(FilterType.ALLOW_ALL); boolean needsDropSlot = true; @@ -261,13 +266,13 @@ public class DiagramCraftingWindow extends CraftingWindow implements ItemContain this.inputSecondaryContainer.setGlobalFilter(FilterType.ALLOW_OUTPUT_ONLY); if (container != this.inputSecondaryContainer && !this.inputSecondaryContainer.isEmpty()) { List itemStacks = this.inputSecondaryContainer.dropAllItemStacks(); - SimpleItemContainer.addOrDropItemStacks(store, ref, combinedStorage, itemStacks); + SimpleItemContainer.addOrDropItemStacks(store, ref, combinedInventory, itemStacks); } } List recipes = new ObjectArrayList<>(); boolean allSlotsFull = this.collectRecipes(ref, recipes, store); - this.windowData.add("slots", this.generateSlots(combinedStorage, recipes)); + this.windowData.add("slots", this.generateSlots(combinedInventory, recipes)); if (recipes.size() == 1 && allSlotsFull) { CraftingRecipe recipe = recipes.getFirst(); ItemStack output = CraftingManager.getOutputItemStacks(recipe).getFirst(); diff --git a/src/com/hypixel/hytale/builtin/crafting/window/FieldCraftingWindow.java b/src/com/hypixel/hytale/builtin/crafting/window/FieldCraftingWindow.java index 40aabd10..e0955f2e 100644 --- a/src/com/hypixel/hytale/builtin/crafting/window/FieldCraftingWindow.java +++ b/src/com/hypixel/hytale/builtin/crafting/window/FieldCraftingWindow.java @@ -30,7 +30,7 @@ public class FieldCraftingWindow extends Window { super(WindowType.PocketCrafting); this.windowData.addProperty("type", BenchType.Crafting.ordinal()); this.windowData.addProperty("id", "Fieldcraft"); - this.windowData.addProperty("name", "server.ui.inventory.fieldcraft.title"); + this.windowData.addProperty("name", "client.inventory.fieldcraft.title"); JsonArray categories = new JsonArray(); for (FieldcraftCategory fieldcraftCategory : FieldcraftCategory.getAssetMap().getAssetMap().values()) { diff --git a/src/com/hypixel/hytale/builtin/crafting/window/ProcessingBenchWindow.java b/src/com/hypixel/hytale/builtin/crafting/window/ProcessingBenchWindow.java index 139ea46b..0f65569c 100644 --- a/src/com/hypixel/hytale/builtin/crafting/window/ProcessingBenchWindow.java +++ b/src/com/hypixel/hytale/builtin/crafting/window/ProcessingBenchWindow.java @@ -3,27 +3,26 @@ package com.hypixel.hytale.builtin.crafting.window; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.hypixel.hytale.builtin.crafting.CraftingPlugin; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; import com.hypixel.hytale.builtin.crafting.component.CraftingManager; -import com.hypixel.hytale.builtin.crafting.state.ProcessingBenchState; +import com.hypixel.hytale.builtin.crafting.component.ProcessingBenchBlock; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.event.EventRegistration; -import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.packets.window.SetActiveAction; import com.hypixel.hytale.protocol.packets.window.TierUpgradeAction; import com.hypixel.hytale.protocol.packets.window.WindowAction; import com.hypixel.hytale.protocol.packets.window.WindowType; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Bench; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.ProcessingBench; import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.windows.ItemContainerWindow; -import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; +import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.universe.world.SoundUtil; -import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.HashSet; import java.util.Set; @@ -31,6 +30,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class ProcessingBenchWindow extends BenchWindow implements ItemContainerWindow { + @Nonnull + private final ProcessingBenchBlock processingBenchState; + @Nullable + private final BlockModule.BlockStateInfo blockStateInfo; private CombinedItemContainer itemContainer; @Nullable private EventRegistration inventoryRegistration; @@ -43,9 +46,20 @@ public class ProcessingBenchWindow extends BenchWindow implements ItemContainerW @Nonnull private final Set processingFuelSlots = new HashSet<>(); - public ProcessingBenchWindow(@Nonnull ProcessingBenchState benchState) { - super(WindowType.Processing, benchState); - ProcessingBench processingBench = (ProcessingBench)this.blockType.getBench(); + public ProcessingBenchWindow( + @Nonnull ProcessingBenchBlock benchState, + @Nonnull BenchBlock benchBlock, + @Nullable BlockModule.BlockStateInfo blockStateInfo, + int x, + int y, + int z, + int rotationIndex, + @Nonnull BlockType blockType + ) { + super(WindowType.Processing, x, y, z, rotationIndex, blockType, benchBlock); + this.processingBenchState = benchState; + this.blockStateInfo = blockStateInfo; + ProcessingBench processingBench = (ProcessingBench)blockType.getBench(); CraftingRecipe recipe = benchState.getRecipe(); float inputProgress = benchState.getInputProgress(); float progress = recipe != null && recipe.getTimeSeconds() > 0.0F ? inputProgress / recipe.getTimeSeconds() : 0.0F; @@ -115,9 +129,11 @@ public class ProcessingBenchWindow extends BenchWindow implements ItemContainerW } public void setMaxFuel(int maxFuel) { - this.maxFuel = maxFuel; - this.windowData.addProperty("maxFuel", maxFuel); - this.invalidate(); + if (this.maxFuel != maxFuel) { + this.maxFuel = maxFuel; + this.windowData.addProperty("maxFuel", maxFuel); + this.invalidate(); + } } public void setProgress(float progress) { @@ -166,47 +182,29 @@ public class ProcessingBenchWindow extends BenchWindow implements ItemContainerW @Override public void handleAction(@Nonnull Ref ref, @Nonnull Store store, @Nonnull WindowAction action) { - World world = store.getExternalData().getWorld(); - if (world.getChunk(ChunkUtil.indexChunkFromBlock(this.x, this.z)).getState(this.x, this.y, this.z) instanceof ProcessingBenchState benchState) { - switch (action) { - case SetActiveAction setActiveAction: - if (!benchState.setActive(setActiveAction.state)) { - this.invalidate(); + ProcessingBenchBlock benchState = this.processingBenchState; + switch (action) { + case SetActiveAction setActiveAction: + if (!benchState.setActive(setActiveAction.state, this.benchBlock, this.blockStateInfo)) { + this.invalidate(); + } + break; + case TierUpgradeAction ignored: + label17: { + CraftingManager craftingManager = store.getComponent(ref, CraftingManager.getComponentType()); + if (craftingManager == null) { + return; } - break; - case TierUpgradeAction ignored: - label20: { - CraftingManager craftingManager = store.getComponent(ref, CraftingManager.getComponentType()); - if (craftingManager == null) { - return; - } - if (craftingManager.startTierUpgrade(ref, store, this) && this.bench.getBenchUpgradeSoundEventIndex() != 0) { - SoundUtil.playSoundEvent3d(this.bench.getBenchUpgradeSoundEventIndex(), SoundCategory.SFX, this.x + 0.5, this.y + 0.5, this.z + 0.5, store); - } - break label20; + if (craftingManager.startTierUpgrade(ref, store, this) && this.bench.getBenchUpgradeSoundEventIndex() != 0) { + SoundUtil.playSoundEvent3d(this.bench.getBenchUpgradeSoundEventIndex(), SoundCategory.SFX, this.x + 0.5, this.y + 0.5, this.z + 0.5, store); } - default: - } + break label17; + } + default: } } - @Override - protected boolean onOpen0(@Nonnull Ref ref, @Nonnull Store store) { - super.onOpen0(ref, store); - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - Inventory inventory = playerComponent.getInventory(); - this.inventoryRegistration = inventory.getCombinedHotbarFirst().registerChangeEvent(event -> { - this.windowData.add("inventoryHints", generateInventoryHints(this.bench, inventory.getCombinedHotbarFirst())); - this.invalidate(); - }); - this.windowData.add("inventoryHints", generateInventoryHints(this.bench, inventory.getCombinedHotbarFirst())); - return true; - } - private void updateOutputSlots(int tierLevel) { this.windowData.addProperty("outputSlotsCount", ((ProcessingBench)this.blockType.getBench()).getOutputSlotsCount(tierLevel)); } @@ -233,9 +231,7 @@ public class ProcessingBenchWindow extends BenchWindow implements ItemContainerW super.updateBenchTierLevel(newValue); this.updateInputSlots(newValue); this.updateOutputSlots(newValue); - if (this.benchState instanceof ProcessingBenchState processingBenchState) { - this.itemContainer = processingBenchState.getItemContainer(); - } + this.itemContainer = this.processingBenchState.getItemContainer(); } @Override diff --git a/src/com/hypixel/hytale/builtin/crafting/window/SimpleCraftingWindow.java b/src/com/hypixel/hytale/builtin/crafting/window/SimpleCraftingWindow.java index b3fe6aa8..71eb199a 100644 --- a/src/com/hypixel/hytale/builtin/crafting/window/SimpleCraftingWindow.java +++ b/src/com/hypixel/hytale/builtin/crafting/window/SimpleCraftingWindow.java @@ -1,29 +1,31 @@ package com.hypixel.hytale.builtin.crafting.window; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; import com.hypixel.hytale.builtin.crafting.component.CraftingManager; -import com.hypixel.hytale.builtin.crafting.state.BenchState; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.packets.window.CraftRecipeAction; import com.hypixel.hytale.protocol.packets.window.TierUpgradeAction; import com.hypixel.hytale.protocol.packets.window.WindowAction; import com.hypixel.hytale.protocol.packets.window.WindowType; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.windows.MaterialContainerWindow; import com.hypixel.hytale.server.core.entity.entities.player.windows.WindowManager; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SimpleCraftingWindow extends CraftingWindow implements MaterialContainerWindow { - public SimpleCraftingWindow(@Nonnull BenchState benchState) { - super(WindowType.BasicCrafting, benchState); + public SimpleCraftingWindow(int x, int y, int z, int rotationIndex, @Nonnull BlockType blockType, @Nonnull BenchBlock benchBlock) { + super(WindowType.BasicCrafting, x, y, z, rotationIndex, blockType, benchBlock); } @Override @@ -43,24 +45,24 @@ public class SimpleCraftingWindow extends CraftingWindow implements MaterialCont if (craftRecipe == null) { PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType()); if (playerRef != null) { - playerRef.getPacketHandler().disconnect("Attempted to craft unknown recipe!"); + playerRef.getPacketHandler().disconnect(Message.translation("server.general.disconnect.unknownRecipe")); } return; } - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - if (playerComponent == null) { - return; - } - - CombinedItemContainer combined = playerComponent.getInventory().getCombinedBackpackStorageHotbar(); - CombinedItemContainer playerAndContainerInventory = new CombinedItemContainer(combined, this.getExtraResourcesSection().getItemContainer()); + CombinedItemContainer combinedBackpackStorageHotbar = InventoryComponent.getCombined(store, ref, InventoryComponent.BACKPACK_STORAGE_HOTBAR); + CombinedItemContainer playerAndContainerInventory = new CombinedItemContainer( + combinedBackpackStorageHotbar, this.getExtraResourcesSection().getItemContainer() + ); boolean accepted; if (craftRecipe.getTimeSeconds() > 0.0F) { accepted = craftingManager.queueCraft( ref, store, this, 0, craftRecipe, quantity, playerAndContainerInventory, CraftingManager.InputRemovalType.NORMAL ); + if (accepted) { + this.updateQueueSize(craftingManager.getRemainingQueueSize()); + } } else { accepted = craftingManager.craftItem(ref, store, craftRecipe, quantity, playerAndContainerInventory); } diff --git a/src/com/hypixel/hytale/builtin/crafting/window/StructuralCraftingWindow.java b/src/com/hypixel/hytale/builtin/crafting/window/StructuralCraftingWindow.java index 62c2dd59..e9698673 100644 --- a/src/com/hypixel/hytale/builtin/crafting/window/StructuralCraftingWindow.java +++ b/src/com/hypixel/hytale/builtin/crafting/window/StructuralCraftingWindow.java @@ -2,8 +2,8 @@ package com.hypixel.hytale.builtin.crafting.window; import com.google.gson.JsonArray; import com.hypixel.hytale.builtin.crafting.CraftingPlugin; +import com.hypixel.hytale.builtin.crafting.component.BenchBlock; import com.hypixel.hytale.builtin.crafting.component.CraftingManager; -import com.hypixel.hytale.builtin.crafting.state.BenchState; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; @@ -16,13 +16,13 @@ import com.hypixel.hytale.protocol.packets.window.CraftRecipeAction; import com.hypixel.hytale.protocol.packets.window.SelectSlotAction; import com.hypixel.hytale.protocol.packets.window.WindowAction; import com.hypixel.hytale.protocol.packets.window.WindowType; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.StructuralCraftingBench; import com.hypixel.hytale.server.core.asset.type.item.config.BlockGroup; import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe; import com.hypixel.hytale.server.core.asset.type.item.config.Item; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.windows.ItemContainerWindow; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.MaterialQuantity; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; @@ -54,8 +54,8 @@ public class StructuralCraftingWindow extends CraftingWindow implements ItemCont @Nullable private EventRegistration inventoryRegistration; - public StructuralCraftingWindow(@Nonnull BenchState benchState) { - super(WindowType.StructuralCrafting, benchState); + public StructuralCraftingWindow(int x, int y, int z, int rotationIndex, @Nonnull BlockType blockType, @Nonnull BenchBlock benchBlock) { + super(WindowType.StructuralCrafting, x, y, z, rotationIndex, blockType, benchBlock); this.inputContainer = new SimpleItemContainer((short)1); this.inputContainer.registerChangeEvent(e -> this.updateRecipes()); this.inputContainer.setSlotFilter(FilterActionType.ADD, (short)0, this::isValidInput); @@ -149,7 +149,7 @@ public class StructuralCraftingWindow extends CraftingWindow implements ItemCont } MaterialQuantity primaryOutput = recipe.getPrimaryOutput(); - String primaryOutputItemId = primaryOutput.getItemId(); + String primaryOutputItemId = primaryOutput != null ? primaryOutput.getItemId() : null; if (primaryOutputItemId != null) { Item primaryOutputItem = Item.getAssetMap().getAsset(primaryOutputItemId); if (primaryOutputItem != null) { @@ -157,7 +157,12 @@ public class StructuralCraftingWindow extends CraftingWindow implements ItemCont } } - craftingManagerComponent.queueCraft(ref, store, this, 0, recipe, quantity, this.inputContainer, CraftingManager.InputRemovalType.ORDERED); + if (craftingManagerComponent.queueCraft(ref, store, this, 0, recipe, quantity, this.inputContainer, CraftingManager.InputRemovalType.ORDERED) + ) + { + this.updateQueueSize(craftingManagerComponent.getRemainingQueueSize()); + } + this.invalidate(); } break; @@ -213,36 +218,21 @@ public class StructuralCraftingWindow extends CraftingWindow implements ItemCont @Override public boolean onOpen0(@Nonnull Ref ref, @Nonnull Store store) { super.onOpen0(ref, store); - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - Inventory inventory = playerComponent.getInventory(); - this.inventoryRegistration = inventory.getCombinedHotbarFirst() - .registerChangeEvent( - event -> { - this.windowData - .add( - "inventoryHints", - CraftingManager.generateInventoryHints(CraftingPlugin.getBenchRecipes(this.bench), 0, inventory.getCombinedHotbarFirst()) - ); - this.invalidate(); - } - ); - this.windowData - .add("inventoryHints", CraftingManager.generateInventoryHints(CraftingPlugin.getBenchRecipes(this.bench), 0, inventory.getCombinedHotbarFirst())); + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(store, ref, InventoryComponent.HOTBAR_FIRST); + this.inventoryRegistration = combinedInventory.registerChangeEvent(event -> { + this.windowData.add("inventoryHints", CraftingManager.generateInventoryHints(CraftingPlugin.getBenchRecipes(this.bench), 0, combinedInventory)); + this.invalidate(); + }); + this.windowData.add("inventoryHints", CraftingManager.generateInventoryHints(CraftingPlugin.getBenchRecipes(this.bench), 0, combinedInventory)); return true; } @Override public void onClose0(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { super.onClose0(ref, componentAccessor); - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); List itemStacks = this.inputContainer.dropAllItemStacks(); - SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, playerComponent.getInventory().getCombinedHotbarFirst(), itemStacks); + SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, combinedInventory, itemStacks); CraftingManager craftingManagerComponent = componentAccessor.getComponent(ref, CraftingManager.getComponentType()); assert craftingManagerComponent != null; diff --git a/src/com/hypixel/hytale/builtin/creativehub/CreativeHubPlugin.java b/src/com/hypixel/hytale/builtin/creativehub/CreativeHubPlugin.java index 9ba90fa2..34528d0a 100644 --- a/src/com/hypixel/hytale/builtin/creativehub/CreativeHubPlugin.java +++ b/src/com/hypixel/hytale/builtin/creativehub/CreativeHubPlugin.java @@ -66,7 +66,9 @@ public class CreativeHubPlugin extends JavaPlugin { return (World)existingInstance; } else { try { - return InstancesPlugin.get().spawnInstance(hubConfig.getStartupInstance(), parentWorld, returnPoint).join(); + World hub = InstancesPlugin.get().spawnInstance(hubConfig.getStartupInstance(), parentWorld, returnPoint).join(); + hub.getWorldConfig().setDeleteOnRemove(true); + return hub; } catch (Exception var7) { this.getLogger().at(Level.SEVERE).withCause(var7).log("Failed to spawn hub instance"); throw new RuntimeException("Failed to spawn hub instance", var7); diff --git a/src/com/hypixel/hytale/builtin/creativehub/interactions/HubPortalInteraction.java b/src/com/hypixel/hytale/builtin/creativehub/interactions/HubPortalInteraction.java index 263314bb..0c1be302 100644 --- a/src/com/hypixel/hytale/builtin/creativehub/interactions/HubPortalInteraction.java +++ b/src/com/hypixel/hytale/builtin/creativehub/interactions/HubPortalInteraction.java @@ -15,6 +15,7 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.vector.Transform; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -147,7 +148,7 @@ public class HubPortalInteraction extends SimpleInstantInteraction { if (transformComponent == null) { LOGGER.at(Level.SEVERE).log("Cannot teleport player %s to permanent world - missing TransformComponent", playerRef); } else { - Transform originalPosition = transformComponent.getTransform().clone(); + Transform originalPosition = new Transform(transformComponent.getTransform()); PlayerRef playerRefComponent = componentAccessor.getComponent(playerRef, PlayerRef.getComponentType()); if (playerRefComponent == null) { LOGGER.at(Level.SEVERE).log("Cannot teleport player %s to permanent world - missing PlayerRef component", playerRef); @@ -197,7 +198,7 @@ public class HubPortalInteraction extends SimpleInstantInteraction { defaultWorld.addPlayer(playerRefComponent, null, Boolean.TRUE, Boolean.FALSE); } else { LOGGER.at(Level.SEVERE).log("No fallback world available for %s, disconnecting", playerRefComponent.getUsername()); - playerRefComponent.getPacketHandler().disconnect("Failed to teleport - no world available"); + playerRefComponent.getPacketHandler().disconnect(Message.translation("server.general.disconnect.teleportNoWorld")); } } } diff --git a/src/com/hypixel/hytale/builtin/deployables/DeployablesUtils.java b/src/com/hypixel/hytale/builtin/deployables/DeployablesUtils.java index 87c92397..3c6dc417 100644 --- a/src/com/hypixel/hytale/builtin/deployables/DeployablesUtils.java +++ b/src/com/hypixel/hytale/builtin/deployables/DeployablesUtils.java @@ -9,8 +9,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.packets.entities.PlayAnimation; @@ -45,6 +44,7 @@ import java.util.UUID; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class DeployablesUtils { @Nonnull @@ -56,8 +56,8 @@ public class DeployablesUtils { @Nonnull Store store, @Nonnull DeployableConfig config, @Nonnull Ref deployerRef, - @Nonnull Vector3f position, - @Nonnull Vector3f rotation, + @Nonnull Vector3d position, + @Nonnull Rotation3f rotation, @Nonnull String spawnFace ) { Holder holder = EntityStore.REGISTRY.newHolder(); @@ -70,7 +70,7 @@ public class DeployablesUtils { holder.addComponent(DeployableComponent.getComponentType(), new DeployableComponent()); holder.addComponent(TransformComponent.getComponentType(), new TransformComponent()); - holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(Vector3f.FORWARD)); + holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(Rotation3f.IDENTITY)); holder.addComponent(UUIDComponent.getComponentType(), new UUIDComponent(UUID.randomUUID())); holder.addComponent(EntityStatMap.getComponentType(), new EntityStatMap()); holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model)); diff --git a/src/com/hypixel/hytale/builtin/deployables/component/DeployableComponent.java b/src/com/hypixel/hytale/builtin/deployables/component/DeployableComponent.java index 98966570..049b496c 100644 --- a/src/com/hypixel/hytale/builtin/deployables/component/DeployableComponent.java +++ b/src/com/hypixel/hytale/builtin/deployables/component/DeployableComponent.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.time.Instant; @@ -18,6 +17,7 @@ import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3f; public class DeployableComponent implements Component { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/deployables/component/DeployableOwnerComponent.java b/src/com/hypixel/hytale/builtin/deployables/component/DeployableOwnerComponent.java index d48903a5..fcb425de 100644 --- a/src/com/hypixel/hytale/builtin/deployables/component/DeployableOwnerComponent.java +++ b/src/com/hypixel/hytale/builtin/deployables/component/DeployableOwnerComponent.java @@ -18,6 +18,7 @@ import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import javax.annotation.Nonnull; @@ -27,7 +28,7 @@ public class DeployableOwnerComponent implements Component { @Nonnull private final Object2IntMap deployableCountPerId = new Object2IntOpenHashMap<>(); @Nonnull - private final List> deployablesForDestruction = new ObjectArrayList<>(); + private final List> deployablesForDestruction = new ReferenceArrayList<>(); @Nonnull private final List>> tempDestructionList = new ObjectArrayList<>(); diff --git a/src/com/hypixel/hytale/builtin/deployables/component/DeployableProjectileComponent.java b/src/com/hypixel/hytale/builtin/deployables/component/DeployableProjectileComponent.java index e12edf68..41299219 100644 --- a/src/com/hypixel/hytale/builtin/deployables/component/DeployableProjectileComponent.java +++ b/src/com/hypixel/hytale/builtin/deployables/component/DeployableProjectileComponent.java @@ -3,16 +3,17 @@ package com.hypixel.hytale.builtin.deployables.component; import com.hypixel.hytale.builtin.deployables.DeployablesPlugin; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DeployableProjectileComponent implements Component { @Nonnull protected Vector3d previousTickPosition; public DeployableProjectileComponent() { - this(Vector3d.ZERO.clone()); + this(new Vector3d(Vector3dUtil.ZERO)); } public DeployableProjectileComponent(@Nonnull Vector3d previousTickPosition) { @@ -25,15 +26,15 @@ public class DeployableProjectileComponent implements Component { @Override public Component clone() { - return new DeployableProjectileComponent(this.previousTickPosition.clone()); + return new DeployableProjectileComponent(new Vector3d(this.previousTickPosition)); } @Nonnull public Vector3d getPreviousTickPosition() { - return this.previousTickPosition.clone(); + return new Vector3d(this.previousTickPosition); } public void setPreviousTickPosition(@Nonnull Vector3d pos) { - this.previousTickPosition = pos.clone(); + this.previousTickPosition = new Vector3d(pos); } } diff --git a/src/com/hypixel/hytale/builtin/deployables/component/DeployableProjectileShooterComponent.java b/src/com/hypixel/hytale/builtin/deployables/component/DeployableProjectileShooterComponent.java index 12659bde..c614b505 100644 --- a/src/com/hypixel/hytale/builtin/deployables/component/DeployableProjectileShooterComponent.java +++ b/src/com/hypixel/hytale/builtin/deployables/component/DeployableProjectileShooterComponent.java @@ -5,19 +5,19 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.projectile.config.ProjectileConfig; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DeployableProjectileShooterComponent implements Component { @Nonnull - protected final List> projectiles = new ObjectArrayList<>(); + protected final List> projectiles = new ReferenceArrayList<>(); @Nonnull - protected final List> projectilesForRemoval = new ObjectArrayList<>(); + protected final List> projectilesForRemoval = new ReferenceArrayList<>(); protected Ref activeTarget; public static ComponentType getComponentType() { diff --git a/src/com/hypixel/hytale/builtin/deployables/config/DeployableAoeConfig.java b/src/com/hypixel/hytale/builtin/deployables/config/DeployableAoeConfig.java index c0a23e9f..bebd3672 100644 --- a/src/com/hypixel/hytale/builtin/deployables/config/DeployableAoeConfig.java +++ b/src/com/hypixel/hytale/builtin/deployables/config/DeployableAoeConfig.java @@ -10,8 +10,6 @@ import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -24,9 +22,12 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import java.time.Duration; import java.time.Instant; +import java.util.Objects; import java.util.function.Consumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DeployableAoeConfig extends DeployableConfig { @Nonnull @@ -178,6 +179,10 @@ public class DeployableAoeConfig extends DeployableConfig { @Nonnull final DamageCause damageCause ) { var attackConsumer = new Consumer>() { + { + Objects.requireNonNull(DeployableAoeConfig.this); + } + public void accept(@Nonnull Ref entityStoreRef) { if (entityStoreRef != deployableRef) { DeployableAoeConfig.this.attackTarget(entityStoreRef, deployableRef, damageCause, commandBuffer); diff --git a/src/com/hypixel/hytale/builtin/deployables/config/DeployableSpawner.java b/src/com/hypixel/hytale/builtin/deployables/config/DeployableSpawner.java index e7a80d42..abce4314 100644 --- a/src/com/hypixel/hytale/builtin/deployables/config/DeployableSpawner.java +++ b/src/com/hypixel/hytale/builtin/deployables/config/DeployableSpawner.java @@ -9,8 +9,9 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DeployableSpawner implements JsonAssetWithMap> { @Nonnull @@ -20,7 +21,9 @@ public class DeployableSpawner implements JsonAssetWithMap("Config", DeployableConfig.CODEC), (i, s) -> i.config = s, i -> i.config) .addValidator(Validators.nonNull()) .add() - .append(new KeyedCodec<>("PositionOffsets", new ArrayCodec<>(Vector3d.CODEC, Vector3d[]::new)), (i, s) -> i.positionOffsets = s, i -> i.positionOffsets) + .append( + new KeyedCodec<>("PositionOffsets", new ArrayCodec<>(Vector3dUtil.CODEC, Vector3d[]::new)), (i, s) -> i.positionOffsets = s, i -> i.positionOffsets + ) .add() .build(); private static DefaultAssetMap ASSET_MAP; diff --git a/src/com/hypixel/hytale/builtin/deployables/config/DeployableTrapConfig.java b/src/com/hypixel/hytale/builtin/deployables/config/DeployableTrapConfig.java index ff63d7b4..bd18a10a 100644 --- a/src/com/hypixel/hytale/builtin/deployables/config/DeployableTrapConfig.java +++ b/src/com/hypixel/hytale/builtin/deployables/config/DeployableTrapConfig.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.DespawnComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; @@ -20,8 +19,10 @@ import com.hypixel.hytale.server.core.util.TargetUtil; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Objects; import java.util.function.Consumer; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DeployableTrapConfig extends DeployableAoeConfig { @Nonnull @@ -116,6 +117,10 @@ public class DeployableTrapConfig extends DeployableAoeConfig { ) { World world = store.getExternalData().getWorld(); var consumer = new Consumer>() { + { + Objects.requireNonNull(DeployableTrapConfig.this); + } + public void accept(@Nonnull Ref ref) { if (ref != deployableRef) { if (store.getComponent(ref, DeployableComponent.getComponentType()) == null) { diff --git a/src/com/hypixel/hytale/builtin/deployables/config/DeployableTrapSpawnerConfig.java b/src/com/hypixel/hytale/builtin/deployables/config/DeployableTrapSpawnerConfig.java index 11d9db74..58484f72 100644 --- a/src/com/hypixel/hytale/builtin/deployables/config/DeployableTrapSpawnerConfig.java +++ b/src/com/hypixel/hytale/builtin/deployables/config/DeployableTrapSpawnerConfig.java @@ -11,8 +11,7 @@ import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; import com.hypixel.hytale.server.core.modules.time.TimeResource; @@ -21,6 +20,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.time.Instant; import java.time.temporal.ChronoUnit; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DeployableTrapSpawnerConfig extends DeployableTrapConfig { @Nonnull @@ -137,8 +137,8 @@ public class DeployableTrapSpawnerConfig extends DeployableTrapConfig { Vector3d[] positionOffsets = spawner.getPositionOffsets(); for (Vector3d offset : positionOffsets) { - Vector3f childPosition = Vector3d.add(parentPosition, offset).toVector3f(); - world.execute(() -> DeployablesUtils.spawnDeployable(commandBuffer, store, config, parentOwner, childPosition, new Vector3f(), "UP")); + Vector3d childPosition = new Vector3d(parentPosition).add(offset); + world.execute(() -> DeployablesUtils.spawnDeployable(commandBuffer, store, config, parentOwner, childPosition, new Rotation3f(), "UP")); } } } diff --git a/src/com/hypixel/hytale/builtin/deployables/config/DeployableTurretConfig.java b/src/com/hypixel/hytale/builtin/deployables/config/DeployableTurretConfig.java index 11b5577b..ed5cc8c9 100644 --- a/src/com/hypixel/hytale/builtin/deployables/config/DeployableTurretConfig.java +++ b/src/com/hypixel/hytale/builtin/deployables/config/DeployableTurretConfig.java @@ -14,9 +14,9 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.Opacity; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -47,6 +47,10 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3f; +import org.joml.Vector3i; public class DeployableTurretConfig extends DeployableConfig { @Nonnull @@ -127,7 +131,7 @@ public class DeployableTurretConfig extends DeployableConfig { ) .add() .appendInherited( - new KeyedCodec<>("TargetOffset", Vector3d.CODEC), + new KeyedCodec<>("TargetOffset", Vector3dUtil.CODEC), (i, s) -> i.targetOffset = s, i -> i.targetOffset, (i, parent) -> i.targetOffset = parent.targetOffset @@ -161,7 +165,7 @@ public class DeployableTurretConfig extends DeployableConfig { ) .add() .appendInherited( - new KeyedCodec<>("ProjectileSpawnOffsets", new MapCodec<>(Vector3d.CODEC, Object2ObjectOpenHashMap::new, true)), + new KeyedCodec<>("ProjectileSpawnOffsets", new MapCodec<>(Vector3dUtil.CODEC, Object2ObjectOpenHashMap::new, true)), (o, i) -> o.projectileSpawnOffsets = i, o -> o.projectileSpawnOffsets, (o, p) -> o.projectileSpawnOffsets = p.projectileSpawnOffsets @@ -262,7 +266,7 @@ public class DeployableTurretConfig extends DeployableConfig { component.setTimeSinceLastAttack(component.getTimeSinceLastAttack() + dt); World world = commandBuffer.getExternalData().getWorld(); DeployableProjectileShooterComponent shooterComponent = store.getComponent(ref, DeployableProjectileShooterComponent.getComponentType()); - Vector3d spawnPos = Vector3d.ZERO.clone(); + Vector3d spawnPos = new Vector3d(Vector3dUtil.ZERO); if (this.projectileSpawnOffsets != null) { Vector3d spawnOffset = this.projectileSpawnOffsets.get(component.getSpawnFace()); if (spawnOffset != null) { @@ -287,7 +291,7 @@ public class DeployableTurretConfig extends DeployableConfig { assert headRotationComponent != null; - Vector3d pos = Vector3d.add(spawnPos, transformComponent.getPosition()); + Vector3d pos = new Vector3d(spawnPos).add(transformComponent.getPosition()); this.updateProjectiles(store, commandBuffer, shooterComponent); boolean hasTarget = false; Ref target = shooterComponent.getActiveTarget(); @@ -297,15 +301,15 @@ public class DeployableTurretConfig extends DeployableConfig { assert targetTransformComponent != null; Vector3d targetPos = this.calculatedTargetPosition(targetTransformComponent.getPosition()); - Vector3d direction = Vector3d.directionTo(pos, targetPos); - if (targetPos.distanceTo(pos) <= this.trackableRadius && this.testLineOfSight(pos, targetPos, direction, commandBuffer)) { + Vector3d direction = Vector3dUtil.directionTo(pos, targetPos); + if (targetPos.distance(pos) <= this.trackableRadius && this.testLineOfSight(pos, targetPos, direction, commandBuffer)) { hasTarget = true; } } if (!hasTarget) { Ref closestTarget = null; - Vector3d closestTargetPos = Vector3d.MAX; + Vector3d closestTargetPos = new Vector3d(Vector3dUtil.MAX); for (Ref potentialTargetRef : TargetUtil.getAllEntitiesInSphere(pos, this.detectionRadius, commandBuffer)) { if (potentialTargetRef != null && potentialTargetRef.isValid()) { @@ -314,11 +318,11 @@ public class DeployableTurretConfig extends DeployableConfig { assert targetTransformComponentx != null; Vector3d targetPosition = this.calculatedTargetPosition(targetTransformComponentx.getPosition()); - Vector3d direction = Vector3d.directionTo(pos, targetPosition); + Vector3d direction = Vector3dUtil.directionTo(pos, targetPosition); if (this.testLineOfSight(pos, targetPosition, direction, commandBuffer) && this.isValidTarget(ref, store, potentialTargetRef) - && pos.distanceTo(targetPosition) < pos.distanceTo(closestTargetPos)) { - closestTargetPos = targetPosition; + && pos.distance(targetPosition) < pos.distance(closestTargetPos)) { + closestTargetPos.set(targetPosition); closestTarget = potentialTargetRef; } } @@ -331,18 +335,18 @@ public class DeployableTurretConfig extends DeployableConfig { } } - Vector3d targetPos = Vector3d.ZERO; - Vector3f targetLookRotation = Vector3f.ZERO; - Vector3f lookRotation = Vector3f.ZERO; + Vector3dc targetPos = Vector3dUtil.ZERO; + Rotation3fc targetLookRotation = Rotation3f.IDENTITY; + Rotation3fc lookRotation = Rotation3f.IDENTITY; if (hasTarget) { TransformComponent targetTransformComponentxx = store.getComponent(target, TransformComponent.getComponentType()); assert targetTransformComponentxx != null; - targetPos = this.calculatedTargetPosition(targetTransformComponentxx.getPosition().clone()); - Vector3d relativeTargetOffset = new Vector3d(pos.x - targetPos.x, pos.y - targetPos.y, pos.z - targetPos.z); - targetLookRotation = Vector3f.lookAt(relativeTargetOffset.negate()); - lookRotation = Vector3f.lerpAngle(headRotationComponent.getRotation(), targetLookRotation, this.rotationSpeed * dt); + Vector3dc var28 = this.calculatedTargetPosition(new Vector3d(targetTransformComponentxx.getPosition())); + Vector3d relativeTargetOffset = pos.sub(var28, new Vector3d()); + Rotation3fc var31 = Rotation3f.lookAt(relativeTargetOffset.negate()); + lookRotation = Rotation3f.lerpAngle(headRotationComponent.getRotation(), var31, this.rotationSpeed * dt); } headRotationComponent.setRotation(lookRotation); @@ -358,19 +362,19 @@ public class DeployableTurretConfig extends DeployableConfig { } if (canFire && hasTarget) { - Vector3d fwdDirection = new Vector3d().assign(lookRotation.getYaw(), lookRotation.getPitch()); + Vector3d fwdDirection = Vector3dUtil.setYawPitch(lookRotation.yaw(), lookRotation.pitch(), new Vector3d()); Vector3d rootPos = transformComponent.getPosition(); - Vector3d projectileSpawnPos = Vector3d.ZERO.clone(); + Vector3d projectileSpawnPos = new Vector3d(Vector3dUtil.ZERO); if (this.projectileSpawnOffsets != null) { - projectileSpawnPos = this.projectileSpawnOffsets.getOrDefault(component.getSpawnFace(), Vector3d.ZERO).clone(); + projectileSpawnPos = new Vector3d(this.projectileSpawnOffsets.getOrDefault(component.getSpawnFace(), new Vector3d())); } - projectileSpawnPos.add(fwdDirection.clone().normalize()); + projectileSpawnPos.add(new Vector3d(fwdDirection).normalize()); projectileSpawnPos.add(rootPos); UUIDComponent uuidComponent = store.getComponent(ref, UUIDComponent.getComponentType()); if (uuidComponent != null) { UUID uuid = uuidComponent.getUuid(); - shooterComponent.spawnProjectile(ref, commandBuffer, this.projectileConfig, uuid, projectileSpawnPos, fwdDirection.clone()); + shooterComponent.spawnProjectile(ref, commandBuffer, this.projectileConfig, uuid, projectileSpawnPos, new Vector3d(fwdDirection)); } playAnimation(store, ref, this, "Shoot"); @@ -381,7 +385,7 @@ public class DeployableTurretConfig extends DeployableConfig { @Nonnull private Vector3d calculatedTargetPosition(@Nonnull Vector3d original) { - return Vector3d.add(original.clone(), this.targetOffset); + return new Vector3d(original).add(this.targetOffset); } private boolean isValidTarget(@Nonnull Ref ref, @Nonnull Store store, @Nonnull Ref targetRef) { @@ -399,17 +403,17 @@ public class DeployableTurretConfig extends DeployableConfig { if (!this.doLineOfSightTest) { return true; } else { - com.hypixel.hytale.protocol.Vector3f spawnOffset = this.projectileConfig.getSpawnOffset(); - Vector3d testFromPos = attackerPos.clone().add(spawnOffset.x, spawnOffset.y + this.generatedModel.getEyeHeight(), spawnOffset.z); - double distance = testFromPos.distanceTo(targetPos); + Vector3f spawnOffset = this.projectileConfig.getSpawnOffset(); + Vector3d testFromPos = new Vector3d(attackerPos).add(spawnOffset.x, spawnOffset.y + this.generatedModel.getEyeHeight(), spawnOffset.z); + double distance = testFromPos.distance(targetPos); World world = commandBuffer.getExternalData().getWorld(); Vector3f whiteColor = new Vector3f(1.0F, 1.0F, 1.0F); if (this.getDebugVisuals()) { - Vector3d increment = direction.scale(distance); + Vector3d increment = direction.mul(distance); for (int i = 0; i < 10; i++) { - Vector3d pos = testFromPos.clone(); - pos.addScaled(increment, i / 10.0F); + Vector3d pos = new Vector3d(testFromPos); + pos.fma((double)(i / 10.0F), increment); DebugUtils.addSphere(world, pos, whiteColor, 0.1F, 0.5F); } } @@ -430,8 +434,8 @@ public class DeployableTurretConfig extends DeployableConfig { if (blockPosition == null) { return true; } else { - double entityDistance = attackerPos.distanceSquaredTo(targetPos); - double blockDistance = attackerPos.distanceSquaredTo(blockPosition.x + 0.5, blockPosition.y + 0.5, blockPosition.z + 0.5); + double entityDistance = attackerPos.distanceSquared(targetPos); + double blockDistance = attackerPos.distanceSquared(blockPosition.x + 0.5, blockPosition.y + 0.5, blockPosition.z + 0.5); return entityDistance < blockDistance; } } @@ -483,7 +487,7 @@ public class DeployableTurretConfig extends DeployableConfig { for (int j = 0; j < 10; j++) { if (!hit.get()) { - Vector3d scanPos = deployableProjectileComponent.getPreviousTickPosition().clone(); + Vector3d scanPos = new Vector3d(deployableProjectileComponent.getPreviousTickPosition()); scanPos.x = scanPos.x + increment.x * j; scanPos.y = scanPos.y + increment.y * j; scanPos.z = scanPos.z + increment.z * j; @@ -527,9 +531,9 @@ public class DeployableTurretConfig extends DeployableConfig { assert projectileTransformComponent != null; - Vector3d projectilePosition = projectileTransformComponent.getPosition().clone(); + Vector3d projectilePosition = new Vector3d(projectileTransformComponent.getPosition()); if (this.projectileKnockback != null) { - float projectileRotationYaw = projectileTransformComponent.getRotation().getYaw(); + float projectileRotationYaw = projectileTransformComponent.getRotation().yaw(); store.getExternalData().getWorld().execute(() -> { if (ref.isValid()) { this.applyKnockback(ref, projectilePosition, projectileRotationYaw, store); diff --git a/src/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableAtHitLocationInteraction.java b/src/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableAtHitLocationInteraction.java index 1e196cb4..77ba74a8 100644 --- a/src/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableAtHitLocationInteraction.java +++ b/src/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableAtHitLocationInteraction.java @@ -10,13 +10,14 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.protocol.InteractionChainData; import com.hypixel.hytale.protocol.InteractionType; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3fc; public class SpawnDeployableAtHitLocationInteraction extends SimpleInstantInteraction { @Nonnull @@ -41,21 +42,21 @@ public class SpawnDeployableAtHitLocationInteraction extends SimpleInstantIntera assert contextChain != null; InteractionChainData chainData = contextChain.getChainData(); - Vector3f hitLocation = chainData.hitLocation; + Vector3fc hitLocation = chainData.hitLocation; if (hitLocation != null) { CommandBuffer commandBuffer = context.getCommandBuffer(); assert commandBuffer != null; Store store = commandBuffer.getStore(); - Vector3f hitNormal = chainData.hitNormal; - com.hypixel.hytale.math.vector.Vector3f hitNormalVec = new com.hypixel.hytale.math.vector.Vector3f(hitNormal.x, hitNormal.y, hitNormal.z); + Vector3fc hitNormal = chainData.hitNormal; + Vector3d hitNormalVec = new Vector3d(hitNormal.x(), hitNormal.y(), hitNormal.z()); DeployablesUtils.spawnDeployable( commandBuffer, store, this.config, context.getEntity(), - new com.hypixel.hytale.math.vector.Vector3f(hitLocation.x, hitLocation.y, hitLocation.z), + new Vector3d(hitLocation.x(), hitLocation.y(), hitLocation.z()), MathUtil.getRotationForHitNormal(hitNormalVec), MathUtil.getNameForHitNormal(hitNormalVec) ); diff --git a/src/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableFromRaycastInteraction.java b/src/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableFromRaycastInteraction.java index 214c031c..6980faab 100644 --- a/src/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableFromRaycastInteraction.java +++ b/src/com/hypixel/hytale/builtin/deployables/interaction/SpawnDeployableFromRaycastInteraction.java @@ -11,8 +11,7 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.Interaction; import com.hypixel.hytale.protocol.InteractionState; @@ -35,6 +34,8 @@ import it.unimi.dsi.fastutil.objects.Object2FloatMap; import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3fc; public class SpawnDeployableFromRaycastInteraction extends SimpleInstantInteraction { @Nonnull @@ -74,7 +75,7 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInstantInteract } } - private static boolean isSurface(@Nonnull Vector3f normal) { + private static boolean isSurface(@Nonnull Rotation3f normal) { return normal.x == 0.0F && normal.y - 1.0F < 0.01 && normal.z == 0.0F; } @@ -114,20 +115,16 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInstantInteract raycastHit = new Position((float)position.x, (float)position.y, (float)position.z); } - com.hypixel.hytale.protocol.Vector3f raycastNormal = clientState.raycastNormal; + Vector3fc raycastNormal = clientState.raycastNormal; float correctedRaycastDistance = clientState.raycastDistance; - com.hypixel.hytale.protocol.Vector3f spawnPosition = new com.hypixel.hytale.protocol.Vector3f( - (float)raycastHit.x, (float)raycastHit.y, (float)raycastHit.z - ); - Vector3f norm = new Vector3f(raycastNormal.x, raycastNormal.y, raycastNormal.z); + Vector3d spawnPosition = new Vector3d(raycastHit.x, raycastHit.y, raycastHit.z); + Rotation3f norm = new Rotation3f(raycastNormal.x(), raycastNormal.y(), raycastNormal.z()); if (correctedRaycastDistance > 0.0F && correctedRaycastDistance <= this.maxPlacementDistance && (this.config.getAllowPlaceOnWalls() || isSurface(norm))) { Direction attackerRot = clientState.attackerRot; - Vector3f rot = new Vector3f(0.0F, attackerRot.yaw, 0.0F); - DeployablesUtils.spawnDeployable( - commandBuffer, store, this.config, entityRef, new Vector3f(spawnPosition.x, spawnPosition.y, spawnPosition.z), rot, "UP" - ); + Rotation3f rot = new Rotation3f(0.0F, attackerRot.yaw, 0.0F); + DeployablesUtils.spawnDeployable(commandBuffer, store, this.config, entityRef, spawnPosition, rot, "UP"); } } } diff --git a/src/com/hypixel/hytale/builtin/deployables/system/DeployablesSystem.java b/src/com/hypixel/hytale/builtin/deployables/system/DeployablesSystem.java index 61928edf..b6e3640f 100644 --- a/src/com/hypixel/hytale/builtin/deployables/system/DeployablesSystem.java +++ b/src/com/hypixel/hytale/builtin/deployables/system/DeployablesSystem.java @@ -13,9 +13,7 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.Direction; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -25,8 +23,10 @@ import com.hypixel.hytale.server.core.modules.entitystats.asset.DefaultEntitySta import com.hypixel.hytale.server.core.universe.world.ParticleUtil; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DeployablesSystem { private static void spawnParticleEffect( @@ -45,7 +45,7 @@ public class DeployablesSystem { } SpatialResource, EntityStore> playerSpatialResource = commandBuffer.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(particlePosition, 75.0, results); ParticleUtil.spawnParticleEffect( particle.getSystemId(), diff --git a/src/com/hypixel/hytale/builtin/fluid/DisabledFluidResource.java b/src/com/hypixel/hytale/builtin/fluid/DisabledFluidResource.java new file mode 100644 index 00000000..111fbdb3 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/fluid/DisabledFluidResource.java @@ -0,0 +1,49 @@ +package com.hypixel.hytale.builtin.fluid; + +import com.hypixel.hytale.component.Resource; +import com.hypixel.hytale.component.ResourceType; +import com.hypixel.hytale.server.core.universe.world.WorldConfig; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntSets; +import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class DisabledFluidResource implements Resource { + @Nullable + private Set tags; + @Nonnull + private IntSet ids = IntSets.EMPTY_SET; + + @Nonnull + public static ResourceType getResourceType() { + return FluidPlugin.get().getDisabledFluidResourceType(); + } + + @Nonnull + public IntSet getDisabledFluidIds(@Nonnull WorldConfig worldConfig) { + Set disabledTickers = worldConfig.getDisabledFluidTickers(); + if (disabledTickers.isEmpty()) { + return IntSets.EMPTY_SET; + } else if (this.tags == disabledTickers) { + return this.ids; + } else { + IntSet resolved = FluidPlugin.resolveFluidIds(disabledTickers); + this.tags = disabledTickers; + this.ids = resolved; + return resolved; + } + } + + public void invalidate() { + this.tags = null; + this.ids = IntSets.EMPTY_SET; + } + + @Nonnull + @Override + public Resource clone() { + return new DisabledFluidResource(); + } +} diff --git a/src/com/hypixel/hytale/builtin/fluid/FluidCommand.java b/src/com/hypixel/hytale/builtin/fluid/FluidCommand.java index 04858212..c11bd5ce 100644 --- a/src/com/hypixel/hytale/builtin/fluid/FluidCommand.java +++ b/src/com/hypixel/hytale/builtin/fluid/FluidCommand.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -25,6 +25,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class FluidCommand extends AbstractCommandCollection { @Nonnull @@ -61,8 +62,8 @@ public class FluidCommand extends AbstractCommandCollection { playerRef.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK); } else { ChunkStore chunkStore = world.getChunkStore(); - Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore); - chunkStore.getChunkSectionReferenceAsync(ChunkUtil.chunkCoordinate(pos.x), ChunkUtil.chunkCoordinate(pos.y), ChunkUtil.chunkCoordinate(pos.z)) + Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(Vector3iUtil.toVector3d(blockTarget), chunkStore); + chunkStore.getChunkSectionReferenceAtBlockAsync(pos.x, pos.y, pos.z) .thenAcceptAsync( section -> { Store sectionStore = section.getStore(); @@ -119,7 +120,7 @@ public class FluidCommand extends AbstractCommandCollection { playerRef.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK); } else { ChunkStore chunkStore = world.getChunkStore(); - Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore); + Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(Vector3iUtil.toVector3d(blockTarget), chunkStore); Fluid fluid = this.fluid.get(context); if (fluid == null) { playerRef.sendMessage(MESSAGE_COMMANDS_SET_UNKNOWN_FLUID); @@ -131,7 +132,7 @@ public class FluidCommand extends AbstractCommandCollection { } Integer finalLevel = level; - chunkStore.getChunkSectionReferenceAsync(ChunkUtil.chunkCoordinate(pos.x), ChunkUtil.chunkCoordinate(pos.y), ChunkUtil.chunkCoordinate(pos.z)) + chunkStore.getChunkSectionReferenceAtBlockAsync(pos.x, pos.y, pos.z) .thenAcceptAsync( section -> { Store sectionStore = section.getStore(); @@ -192,7 +193,7 @@ public class FluidCommand extends AbstractCommandCollection { playerRef.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_LOOKING_AT_BLOCK); } else { ChunkStore chunkStore = world.getChunkStore(); - Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(blockTarget.toVector3d(), chunkStore); + Vector3i pos = offset == null ? blockTarget : offset.getBlockPosition(Vector3iUtil.toVector3d(blockTarget), chunkStore); Fluid fluid = this.fluid.get(context); if (fluid == null) { playerRef.sendMessage(MESSAGE_COMMANDS_SET_UNKNOWN_FLUID); diff --git a/src/com/hypixel/hytale/builtin/fluid/FluidPlugin.java b/src/com/hypixel/hytale/builtin/fluid/FluidPlugin.java index 693c9328..cc84e301 100644 --- a/src/com/hypixel/hytale/builtin/fluid/FluidPlugin.java +++ b/src/com/hypixel/hytale/builtin/fluid/FluidPlugin.java @@ -1,11 +1,14 @@ package com.hypixel.hytale.builtin.fluid; +import com.hypixel.hytale.assetstore.AssetRegistry; +import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent; import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; import com.hypixel.hytale.codec.lookup.Priority; import com.hypixel.hytale.component.ComponentRegistryProxy; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.event.EventPriority; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; @@ -18,6 +21,8 @@ import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.asset.type.fluid.FluidTicker; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; @@ -26,7 +31,11 @@ import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; import com.hypixel.hytale.server.core.universe.world.events.ChunkPreLoadProcessEvent; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntSets; import java.time.Instant; +import java.util.Set; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -35,11 +44,30 @@ public class FluidPlugin extends JavaPlugin { @Nonnull private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); private static FluidPlugin instance; + private ResourceType disabledFluidResourceType; public static FluidPlugin get() { return instance; } + @Nonnull + public ResourceType getDisabledFluidResourceType() { + return this.disabledFluidResourceType; + } + + @Nonnull + static IntSet resolveFluidIds(@Nonnull Set tags) { + IndexedLookupTableAssetMap assetMap = Fluid.getAssetMap(); + IntOpenHashSet result = new IntOpenHashSet(); + + for (String tag : tags) { + int tagIndex = AssetRegistry.getOrCreateTagIndex(tag); + result.addAll(assetMap.getIndexesForTag(tagIndex)); + } + + return (IntSet)(result.isEmpty() ? IntSets.EMPTY_SET : IntSets.unmodifiable(result)); + } + public FluidPlugin(@Nonnull JavaPluginInit init) { super(init); instance = this; @@ -48,6 +76,7 @@ public class FluidPlugin extends JavaPlugin { @Override protected void setup() { ComponentRegistryProxy chunkStoreRegistry = this.getChunkStoreRegistry(); + this.disabledFluidResourceType = chunkStoreRegistry.registerResource(DisabledFluidResource.class, DisabledFluidResource::new); FluidTicker.CODEC.register(Priority.DEFAULT, "Default", DefaultFluidTicker.class, DefaultFluidTicker.CODEC); FluidTicker.CODEC.register("Fire", FireFluidTicker.class, FireFluidTicker.CODEC); FluidTicker.CODEC.register("Finite", FiniteFluidTicker.class, FiniteFluidTicker.CODEC); @@ -57,15 +86,23 @@ public class FluidPlugin extends JavaPlugin { ComponentType blockChunkComponentType = BlockChunk.getComponentType(); ComponentType worldChunkComponentType = WorldChunk.getComponentType(); chunkStoreRegistry.registerSystem(new FluidSystems.EnsureFluidSection(chunkSectionComponentType, fluidSectionComponentType)); - chunkStoreRegistry.registerSystem(new FluidSystems.MigrateFromColumn(chunkColumnComponentType, blockChunkComponentType, fluidSectionComponentType)); chunkStoreRegistry.registerSystem(new FluidSystems.SetupSection(chunkSectionComponentType, fluidSectionComponentType)); chunkStoreRegistry.registerSystem(new FluidSystems.LoadPacketGenerator(chunkColumnComponentType, fluidSectionComponentType)); chunkStoreRegistry.registerSystem(new FluidSystems.ReplicateChanges(chunkSectionComponentType, fluidSectionComponentType, worldChunkComponentType)); chunkStoreRegistry.registerSystem(new FluidSystems.Ticking(chunkSectionComponentType, fluidSectionComponentType, blockChunkComponentType)); this.getEventRegistry().registerGlobal(EventPriority.FIRST, ChunkPreLoadProcessEvent.class, FluidPlugin::onChunkPreProcess); + this.getEventRegistry().register(LoadedAssetsEvent.class, Fluid.class, FluidPlugin::onFluidAssetsLoaded); this.getCommandRegistry().registerCommand(new FluidCommand()); } + private static void onFluidAssetsLoaded(@Nonnull LoadedAssetsEvent> event) { + ResourceType resourceType = DisabledFluidResource.getResourceType(); + + for (World world : Universe.get().getWorlds().values()) { + world.execute(() -> world.getChunkStore().getStore().getResource(resourceType).invalidate()); + } + } + private static void onChunkPreProcess(@Nonnull ChunkPreLoadProcessEvent event) { if (event.isNewlyGenerated()) { WorldChunk worldChunk = event.getChunk(); diff --git a/src/com/hypixel/hytale/builtin/fluid/FluidSystems.java b/src/com/hypixel/hytale/builtin/fluid/FluidSystems.java index ab45517c..8e0fd370 100644 --- a/src/com/hypixel/hytale/builtin/fluid/FluidSystems.java +++ b/src/com/hypixel/hytale/builtin/fluid/FluidSystems.java @@ -26,9 +26,7 @@ import com.hypixel.hytale.protocol.packets.world.SetFluidCmd; import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.asset.type.fluid.FluidTicker; -import com.hypixel.hytale.server.core.modules.LegacyModule; import com.hypixel.hytale.server.core.modules.entity.player.ChunkTracker; -import com.hypixel.hytale.server.core.modules.migrations.ChunkColumnMigrationSystem; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; @@ -41,6 +39,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Collection; import java.util.List; @@ -136,75 +135,6 @@ public class FluidSystems { } } - public static class MigrateFromColumn extends ChunkColumnMigrationSystem { - @Nonnull - private final ComponentType chunkColumnComponentType; - @Nonnull - private final ComponentType blockChunkComponentType; - @Nonnull - private final ComponentType fluidSectionComponentType; - @Nonnull - private final Query query; - @Nonnull - private final Set> dependencies = Set.of(new SystemDependency<>(Order.BEFORE, LegacyModule.MigrateLegacySections.class)); - - public MigrateFromColumn( - @Nonnull ComponentType chunkColumnComponentType, - @Nonnull ComponentType blockChunkComponentType, - @Nonnull ComponentType fluidSectionComponentType - ) { - this.chunkColumnComponentType = chunkColumnComponentType; - this.blockChunkComponentType = blockChunkComponentType; - this.fluidSectionComponentType = fluidSectionComponentType; - this.query = Query.and(chunkColumnComponentType, blockChunkComponentType); - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - ChunkColumn chunkColumnComponent = holder.getComponent(this.chunkColumnComponentType); - - assert chunkColumnComponent != null; - - BlockChunk blockChunkComponent = holder.getComponent(this.blockChunkComponentType); - - assert blockChunkComponent != null; - - Holder[] sections = chunkColumnComponent.getSectionHolders(); - if (sections != null) { - BlockSection[] legacySections = blockChunkComponent.getMigratedSections(); - if (legacySections != null) { - for (int i = 0; i < sections.length; i++) { - Holder section = sections[i]; - BlockSection paletteSection = legacySections[i]; - if (section != null && paletteSection != null) { - FluidSection fluid = paletteSection.takeMigratedFluid(); - if (fluid != null) { - section.putComponent(this.fluidSectionComponentType, fluid); - blockChunkComponent.markNeedsSaving(); - } - } - } - } - } - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return this.query; - } - - @Nonnull - @Override - public Set> getDependencies() { - return this.dependencies; - } - } - public static class ReplicateChanges extends EntityTickingSystem implements RunWhenPausedSystem { @Nonnull private final ComponentType chunkSectionComponentType; @@ -251,10 +181,12 @@ public class FluidSystems { World world = commandBuffer.getExternalData().getWorld(); WorldChunk worldChunkComponent = commandBuffer.getComponent(chunkSectionComponent.getChunkColumnReference(), this.worldChunkComponentType); + int sectionX = chunkSectionComponent.getX(); int sectionY = chunkSectionComponent.getY(); + int sectionZ = chunkSectionComponent.getZ(); world.execute(() -> { if (worldChunkComponent != null && worldChunkComponent.getWorld() != null) { - worldChunkComponent.getWorld().getChunkLighting().invalidateLightInChunkSection(worldChunkComponent, sectionY); + worldChunkComponent.getWorld().getChunkLighting().invalidateLightInChunkSection(store.getExternalData(), sectionX, sectionY, sectionZ); } }); Collection playerRefs = store.getExternalData().getWorld().getPlayerRefs(); @@ -352,8 +284,6 @@ public class FluidSystems { private final ComponentType fluidSectionComponentType; @Nonnull private final Query query; - @Nonnull - private final Set> dependencies = Set.of(new SystemDependency<>(Order.AFTER, FluidSystems.MigrateFromColumn.class)); public SetupSection( @Nonnull ComponentType chunkSectionComponentType, @Nonnull ComponentType fluidSectionComponentType @@ -385,12 +315,6 @@ public class FluidSystems { public Query getQuery() { return this.query; } - - @Nonnull - @Override - public Set> getDependencies() { - return this.dependencies; - } } public static class Ticking extends EntityTickingSystem { @@ -445,6 +369,9 @@ public class FluidSystems { BlockSection blockSection = blockChunkComponent.getSectionAtIndex(fluidSectionComponent.getY()); if (blockSection != null) { if (blockSection.getTickingBlocksCountCopy() != 0) { + World world = store.getExternalData().getWorld(); + DisabledFluidResource disabledFluidResource = store.getResource(DisabledFluidResource.getResourceType()); + IntSet disabledFluidIds = disabledFluidResource.getDisabledFluidIds(world.getWorldConfig()); FluidTicker.CachedAccessor accessor = FluidTicker.CachedAccessor.of(commandBuffer, fluidSectionComponent, blockSection, 5); blockSection.forEachTicking(accessor, commandBuffer, fluidSectionComponent.getY(), (accessor1, commandBuffer1, x, y, z, block) -> { FluidSection fluidSection1 = accessor1.selfFluidSection; @@ -452,6 +379,8 @@ public class FluidSystems { int fluidId = fluidSection1.getFluidId(x, y, z); if (fluidId == 0) { return BlockTickStrategy.IGNORED; + } else if (disabledFluidIds.contains(fluidId)) { + return BlockTickStrategy.IGNORED; } else { int blockX = fluidSection1.getX() << 5 | x; int blockZ = fluidSection1.getZ() << 5 | z; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/entity/EntityPlacementData.java b/src/com/hypixel/hytale/builtin/hytalegenerator/EntityPlacementData.java similarity index 84% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/entity/EntityPlacementData.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/EntityPlacementData.java index 52d2bd5a..cc9a1d36 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/entity/EntityPlacementData.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/EntityPlacementData.java @@ -1,11 +1,11 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.entity; +package com.hypixel.hytale.builtin.hytalegenerator; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class EntityPlacementData implements MemInstrument { private final Vector3i offset; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/GridUtils.java b/src/com/hypixel/hytale/builtin/hytalegenerator/GridUtils.java similarity index 59% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/GridUtils.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/GridUtils.java index 32b673a5..30379b50 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/GridUtils.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/GridUtils.java @@ -1,29 +1,30 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem; +package com.hypixel.hytale.builtin.hytalegenerator; -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.VoxelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class GridUtils { - public static final int BUFFER_COUNT_IN_CHUNK_Y = 320 / NVoxelBuffer.SIZE.y; + public static final int BUFFER_COUNT_IN_CHUNK_Y = 320 / VoxelBuffer.SIZE.y; public static void toBufferGrid_fromVoxelGridOverlap(@Nonnull Bounds3i bounds_voxelGrid) { assert bounds_voxelGrid.isCorrect(); VectorUtil.bitShiftRight(3, bounds_voxelGrid.min); - if (bounds_voxelGrid.max.x % NVoxelBuffer.SIZE.x == 0) { + if (bounds_voxelGrid.max.x % VoxelBuffer.SIZE.x == 0) { bounds_voxelGrid.max.x--; } - if (bounds_voxelGrid.max.y % NVoxelBuffer.SIZE.y == 0) { + if (bounds_voxelGrid.max.y % VoxelBuffer.SIZE.y == 0) { bounds_voxelGrid.max.y--; } - if (bounds_voxelGrid.max.z % NVoxelBuffer.SIZE.z == 0) { + if (bounds_voxelGrid.max.z % VoxelBuffer.SIZE.z == 0) { bounds_voxelGrid.max.z--; } @@ -36,9 +37,9 @@ public class GridUtils { public static Bounds3i createColumnBounds_voxelGrid(@Nonnull Vector3i position_bufferGrid, int minY_voxelSpace, int maxY_voxelSpace) { assert minY_voxelSpace <= maxY_voxelSpace; - Vector3i min = position_bufferGrid.clone(); + Vector3i min = new Vector3i(position_bufferGrid); VectorUtil.bitShiftLeft(3, min); - Vector3i max = min.clone().add(NVoxelBuffer.SIZE); + Vector3i max = new Vector3i(min).add(VoxelBuffer.SIZE); min.y = minY_voxelSpace; max.y = maxY_voxelSpace; return new Bounds3i(min, max); @@ -48,52 +49,39 @@ public class GridUtils { public static Bounds3i createBufferBoundsInclusive_fromVoxelBounds(@Nonnull Bounds3i bounds_voxelGrid) { assert bounds_voxelGrid.isCorrect(); - Vector3i min = bounds_voxelGrid.min.clone(); - Vector3i max = bounds_voxelGrid.max.clone(); - min.x = Calculator.floor(min.x, NVoxelBuffer.SIZE.x); - min.x >>= 3; - min.y = Calculator.floor(min.y, NVoxelBuffer.SIZE.y); - min.y >>= 3; - min.z = Calculator.floor(min.z, NVoxelBuffer.SIZE.z); - min.z >>= 3; - int mod = Calculator.wrap(max.x, NVoxelBuffer.SIZE.x); - max.x = Calculator.ceil(max.x, NVoxelBuffer.SIZE.x); - max.x >>= 3; - if (mod == 0) { - max.x += 2; + if (bounds_voxelGrid.isZeroVolume()) { + return new Bounds3i(); } else { + Vector3i min = new Vector3i(bounds_voxelGrid.min); + Vector3i max = new Vector3i(bounds_voxelGrid.max); + min.x = Calculator.floor(min.x, VoxelBuffer.SIZE.x); + min.x >>= 3; + min.y = Calculator.floor(min.y, VoxelBuffer.SIZE.y); + min.y >>= 3; + min.z = Calculator.floor(min.z, VoxelBuffer.SIZE.z); + min.z >>= 3; + max.x--; + max.x = Calculator.ceil(max.x, VoxelBuffer.SIZE.x); + max.x >>= 3; max.x++; - } - - mod = Calculator.wrap(max.y, NVoxelBuffer.SIZE.y); - max.y = Calculator.ceil(max.y, NVoxelBuffer.SIZE.y); - max.y >>= 3; - if (mod == 0) { - max.y += 2; - } else { + max.y--; + max.y = Calculator.ceil(max.y, VoxelBuffer.SIZE.y); + max.y >>= 3; max.y++; - } - - mod = Calculator.wrap(max.z, NVoxelBuffer.SIZE.z); - max.z = Calculator.ceil(max.z, NVoxelBuffer.SIZE.z); - max.z >>= 3; - if (mod == 0) { - max.z += 2; - } else { + max.z--; + max.z = Calculator.ceil(max.z, VoxelBuffer.SIZE.z); + max.z >>= 3; max.z++; + return new Bounds3i(min, max); } - - min.dropHash(); - max.dropHash(); - return new Bounds3i(min, max); } @Nonnull public static Bounds3i createColumnBounds_bufferGrid(@Nonnull Vector3i position_bufferGrid, int minY_bufferGrid, int maxY_bufferGrid) { assert minY_bufferGrid <= maxY_bufferGrid; - Vector3i min = position_bufferGrid.clone(); - Vector3i max = min.clone().add(Vector3i.ALL_ONES); + Vector3i min = new Vector3i(position_bufferGrid); + Vector3i max = new Vector3i(min).add(Vector3iUtil.ALL_ONES); min.y = minY_bufferGrid; max.y = maxY_bufferGrid; return new Bounds3i(min, max); @@ -102,13 +90,13 @@ public class GridUtils { @Nonnull public static Bounds3i createChunkBounds_voxelGrid(int x_chunkGrid, int z_chunkGrid) { Vector3i min = new Vector3i(x_chunkGrid << 5, 0, z_chunkGrid << 5); - Vector3i max = min.clone().add(32, 320, 32); + Vector3i max = new Vector3i(min).add(32, 320, 32); return new Bounds3i(min, max); } @Nonnull - public static Bounds3i createUnitBounds3i(@Nonnull Vector3i position) { - return new Bounds3i(position, position.clone().add(Vector3i.ALL_ONES)); + public static Bounds3i createUnitBounds3i(@Nonnull Vector3ic position) { + return new Bounds3i(position, new Vector3i(position).add(Vector3iUtil.ALL_ONES)); } @Nonnull @@ -120,8 +108,8 @@ public class GridUtils { @Nonnull public static Bounds3i createBounds_fromVector_originVoxelInclusive(@Nonnull Vector3i range) { - Vector3i min = new Vector3i(range).scale(-1); - Vector3i max = new Vector3i(range).add(Vector3i.ALL_ONES); + Vector3i min = new Vector3i(range).negate(); + Vector3i max = new Vector3i(range).add(Vector3iUtil.ALL_ONES); return new Bounds3i(min, max); } @@ -144,13 +132,17 @@ public class GridUtils { VectorUtil.bitShiftLeft(3, position_voxelGrid); } + public static int toBufferGrid_fromVoxelGrid(int worldPosition_voxelGrid) { + return worldPosition_voxelGrid >> 3; + } + public static void toBufferGrid_fromVoxelGrid(@Nonnull Vector3i worldPosition_voxelGrid) { VectorUtil.bitShiftRight(3, worldPosition_voxelGrid); } public static int toBufferDistanceInclusive_fromVoxelDistance(int distance_voxelGrid) { int distance_bufferGrid = distance_voxelGrid >> 3; - return Calculator.wrap(distance_voxelGrid, NVoxelBuffer.SIZE.x) == 0 ? distance_bufferGrid : distance_bufferGrid + 1; + return Calculator.wrap(distance_voxelGrid, VoxelBuffer.SIZE.x) == 0 ? distance_bufferGrid : distance_bufferGrid + 1; } @Nonnull @@ -162,10 +154,33 @@ public class GridUtils { return position; } + public static int toXVoxelGridInsideBuffer_fromWorldGrid(int x_voxelGrid) { + return Calculator.wrap(x_voxelGrid, VoxelBuffer.SIZE.x); + } + + public static int toYVoxelGridInsideBuffer_fromWorldGrid(int y_voxelGrid) { + return Calculator.wrap(y_voxelGrid, VoxelBuffer.SIZE.y); + } + + public static int toZVoxelGridInsideBuffer_fromWorldGrid(int z_voxelGrid) { + return Calculator.wrap(z_voxelGrid, VoxelBuffer.SIZE.z); + } + public static void toVoxelGridInsideBuffer_fromWorldGrid(@Nonnull Vector3i worldPosition_voxelGrid) { - worldPosition_voxelGrid.x = Calculator.wrap(worldPosition_voxelGrid.x, NVoxelBuffer.SIZE.x); - worldPosition_voxelGrid.y = Calculator.wrap(worldPosition_voxelGrid.y, NVoxelBuffer.SIZE.y); - worldPosition_voxelGrid.z = Calculator.wrap(worldPosition_voxelGrid.z, NVoxelBuffer.SIZE.z); + worldPosition_voxelGrid.x = Calculator.wrap(worldPosition_voxelGrid.x, VoxelBuffer.SIZE.x); + worldPosition_voxelGrid.y = Calculator.wrap(worldPosition_voxelGrid.y, VoxelBuffer.SIZE.y); + worldPosition_voxelGrid.z = Calculator.wrap(worldPosition_voxelGrid.z, VoxelBuffer.SIZE.z); + } + + public static int toIndexFromPositionYXZ(int x, int y, int z, @Nonnull Bounds3i bounds) { + assert bounds.contains(x, y, z); + + x -= bounds.min.x; + y -= bounds.min.y; + z -= bounds.min.z; + int sizeX = bounds.max.x - bounds.min.x; + int sizeY = bounds.max.y - bounds.min.y; + return y + x * sizeY + z * sizeY * sizeX; } public static int toIndexFromPositionYXZ(@Nonnull Vector3i position, @Nonnull Bounds3i bounds) { @@ -182,15 +197,15 @@ public class GridUtils { public static void setBoundsYToWorldHeight_bufferGrid(@Nonnull Bounds3i bounds_bufferGrid) { assert bounds_bufferGrid.isCorrect(); - bounds_bufferGrid.min.setY(0); - bounds_bufferGrid.max.setY(40); + bounds_bufferGrid.min.y = 0; + bounds_bufferGrid.max.y = 40; } public static void setBoundsYToWorldHeight_voxelGrid(@Nonnull Bounds3i bounds_voxelGrid) { assert bounds_voxelGrid.isCorrect(); - bounds_voxelGrid.min.setY(0); - bounds_voxelGrid.max.setY(320); + bounds_voxelGrid.min.y = 0; + bounds_voxelGrid.max.y = 320; } public static void toVoxelPosition_fromChunkPosition(@Nonnull Vector3i chunkPosition_voxelGrid) { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/PropField.java b/src/com/hypixel/hytale/builtin/hytalegenerator/PropField.java deleted file mode 100644 index 71874932..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/PropField.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator; - -import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; -import javax.annotation.Nonnull; - -public class PropField { - @Nonnull - private final Assignments assignments; - @Nonnull - private final PositionProvider positionProvider; - private final int runtime; - - public PropField(int runtime, @Nonnull Assignments assignments, @Nonnull PositionProvider positionProvider) { - this.runtime = runtime; - this.assignments = assignments; - this.positionProvider = positionProvider; - } - - @Nonnull - public PositionProvider getPositionProvider() { - return this.positionProvider; - } - - @Nonnull - public Assignments getPropDistribution() { - return this.assignments; - } - - public int getRuntime() { - return this.runtime; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/PropRuntime.java b/src/com/hypixel/hytale/builtin/hytalegenerator/PropRuntime.java new file mode 100644 index 00000000..04dfa5a2 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/PropRuntime.java @@ -0,0 +1,24 @@ +package com.hypixel.hytale.builtin.hytalegenerator; + +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import javax.annotation.Nonnull; + +public class PropRuntime { + @Nonnull + private final PropDistribution propDistribution; + private final int runtime; + + public PropRuntime(int runtime, @Nonnull PropDistribution propDistribution) { + this.runtime = runtime; + this.propDistribution = propDistribution; + } + + @Nonnull + public PropDistribution getPropDistribution() { + return this.propDistribution; + } + + public int getRuntimeIndex() { + return this.runtime; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/VectorUtil.java b/src/com/hypixel/hytale/builtin/hytalegenerator/VectorUtil.java index f129b8a3..2590bc00 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/VectorUtil.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/VectorUtil.java @@ -1,32 +1,39 @@ package com.hypixel.hytale.builtin.hytalegenerator; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.doubles.DoubleObjectPair; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector2i; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; public class VectorUtil { + public static void assignFloored(@Nonnull Vector3i to, @Nonnull Vector3d from) { + to.x = (int)Math.floor(from.x); + to.y = (int)Math.floor(from.y); + to.z = (int)Math.floor(from.z); + } + public static boolean areasOverlap(@Nonnull Vector3d minA, @Nonnull Vector3d maxA, @Nonnull Vector3d minB, @Nonnull Vector3d maxB) { return isAnyGreater(maxA, minB) && isAnySmaller(minA, maxB); } public static double distanceToSegment3d(@Nonnull Vector3d point, @Nonnull Vector3d p0, @Nonnull Vector3d p1) { - Vector3d lineVec = p1.clone().addScaled(p0, -1.0); - Vector3d pointVec = point.clone().addScaled(p0, -1.0); + Vector3d lineVec = new Vector3d(p1).fma(-1.0, p0); + Vector3d pointVec = new Vector3d(point).fma(-1.0, p0); double lineLength = lineVec.length(); - Vector3d lineUnitVec = lineVec.clone().setLength(1.0); - Vector3d pointVecScaled = pointVec.clone().scale(1.0 / lineLength); + Vector3d lineUnitVec = new Vector3d(lineVec).normalize(1.0); + Vector3d pointVecScaled = new Vector3d(pointVec).mul(1.0 / lineLength); double t = lineUnitVec.dot(pointVecScaled); t = Calculator.clamp(0.0, t, 1.0); - Vector3d nearestPoint = lineVec.clone().scale(t); - return nearestPoint.distanceTo(pointVec); + Vector3d nearestPoint = new Vector3d(lineVec).mul(t); + return nearestPoint.distance(pointVec); } public static double distanceToLine3d( @@ -39,26 +46,26 @@ public class VectorUtil { @Nonnull Vector3d rPointVecScaled, @Nonnull Vector3d rNearestPoint ) { - rLineVec.assign(p1).subtract(p0); - rPointVec.assign(point).subtract(p0); + rLineVec.set(p1).sub(p0); + rPointVec.set(point).sub(p0); double lineLength = rLineVec.length(); - rLineUnitVec.assign(rLineVec).setLength(1.0); - rPointVecScaled.assign(rPointVec).scale(1.0 / lineLength); + rLineUnitVec.set(rLineVec).normalize(1.0); + rPointVecScaled.set(rPointVec).mul(1.0 / lineLength); double t = rLineUnitVec.dot(rPointVecScaled); - rNearestPoint.assign(rLineVec).scale(t); - return rNearestPoint.distanceTo(rPointVec); + rNearestPoint.set(rLineVec).mul(t); + return rNearestPoint.distance(rPointVec); } @Nonnull public static Vector3d nearestPointOnSegment3d(@Nonnull Vector3d point, @Nonnull Vector3d p0, @Nonnull Vector3d p1) { - Vector3d lineVec = p1.clone().addScaled(p0, -1.0); - Vector3d pointVec = point.clone().addScaled(p0, -1.0); + Vector3d lineVec = new Vector3d(p1).fma(-1.0, p0); + Vector3d pointVec = new Vector3d(point).fma(-1.0, p0); double lineLength = lineVec.length(); - Vector3d lineUnitVec = lineVec.clone().setLength(1.0); - Vector3d pointVecScaled = pointVec.clone().scale(1.0 / lineLength); + Vector3d lineUnitVec = new Vector3d(lineVec).normalize(1.0); + Vector3d pointVecScaled = new Vector3d(pointVec).mul(1.0 / lineLength); double t = lineUnitVec.dot(pointVecScaled); t = Calculator.clamp(0.0, t, 1.0); - Vector3d nearestPoint = lineVec.clone().scale(t); + Vector3d nearestPoint = new Vector3d(lineVec).mul(t); return nearestPoint.add(p0); } @@ -73,13 +80,13 @@ public class VectorUtil { @Nonnull Vector3d rLineUnitVec, @Nonnull Vector3d rPointVecScaled ) { - rLineVec.assign(p1).subtract(p0); - rPointVec.assign(point).subtract(p0); + rLineVec.set(p1).sub(p0); + rPointVec.set(point).sub(p0); double lineLength = rLineVec.length(); - rLineUnitVec.assign(rLineVec).setLength(1.0); - rPointVecScaled.assign(rPointVec).scale(1.0 / lineLength); + rLineUnitVec.set(rLineVec).normalize(1.0); + rPointVecScaled.set(rPointVec).mul(1.0 / lineLength); double t = rLineUnitVec.dot(rPointVecScaled); - vector_out.assign(rLineVec).scale(t); + vector_out.set(rLineVec).mul(t); vector_out.add(p0); } @@ -87,43 +94,43 @@ public class VectorUtil { @Nonnull Vector3d a0, @Nonnull Vector3d a1, @Nonnull Vector3d b0, @Nonnull Vector3d b1, boolean clamp, @Nonnull Vector3d p0Out, @Nonnull Vector3d p1Out ) { boolean[] flags = new boolean[2]; - Vector3d A = a1.clone().addScaled(a0, -1.0); - Vector3d B = b1.clone().addScaled(b0, -1.0); + Vector3d A = new Vector3d(a1).fma(-1.0, a0); + Vector3d B = new Vector3d(b1).fma(-1.0, b0); double magA = A.length(); double magB = B.length(); - Vector3d _A = A.clone().scale(1.0 / magA); - Vector3d _B = B.clone().scale(1.0 / magB); - Vector3d cross = _A.cross(_B); + Vector3d _A = new Vector3d(A).mul(1.0 / magA); + Vector3d _B = new Vector3d(B).mul(1.0 / magB); + Vector3d cross = _A.cross(_B, new Vector3d()); double denom = Math.pow(cross.length(), 2.0); if (denom == 0.0) { flags[0] = true; - double d0 = _A.dot(b0.clone().addScaled(a0, -1.0)); + double d0 = _A.dot(new Vector3d(b0).fma(-1.0, a0)); if (clamp) { - double d1 = _A.dot(b1.clone().addScaled(a0, -1.0)); + double d1 = _A.dot(new Vector3d(b1).fma(-1.0, a0)); if (d0 <= 0.0 && d1 <= 0.0) { if (Math.abs(d0) < Math.abs(d1)) { - p0Out.assign(a0); - p1Out.assign(b0); + p0Out.set(a0); + p1Out.set(b0); flags[1] = true; return flags; } - p0Out.assign(a0); - p1Out.assign(b1); + p0Out.set(a0); + p1Out.set(b1); flags[1] = true; return flags; } if (d0 >= magA && d1 >= magA) { if (Math.abs(d0) < Math.abs(d1)) { - p0Out.assign(a1); - p1Out.assign(b0); + p0Out.set(a1); + p1Out.set(b0); flags[1] = true; return flags; } - p0Out.assign(a1); - p1Out.assign(b1); + p0Out.set(a1); + p1Out.set(b1); flags[1] = true; return flags; } @@ -131,58 +138,58 @@ public class VectorUtil { return flags; } else { - Vector3d t = b0.clone().addScaled(a0, -1.0); + Vector3d t = new Vector3d(b0).fma(-1.0, a0); double detA = determinant(t, _B, cross); double detB = determinant(t, _A, cross); double t0 = detA / denom; double t1 = detB / denom; - Vector3d pA = _A.clone().scale(t0).add(a0); - Vector3d pB = _B.clone().scale(t1).add(b0); + Vector3d pA = new Vector3d(_A).mul(t0).add(a0); + Vector3d pB = new Vector3d(_B).mul(t1).add(b0); if (clamp) { if (t0 < 0.0) { - pA = a0.clone(); + pA = new Vector3d(a0); } else if (t0 > magA) { - pA = a1.clone(); + pA = new Vector3d(a1); } if (t1 < 0.0) { - pB = b0.clone(); + pB = new Vector3d(b0); } else if (t1 > magB) { - pB = b1.clone(); + pB = new Vector3d(b1); } if (t0 < 0.0 || t0 > magA) { - double dot = _B.dot(pA.clone().addScaled(b0, -1.0)); + double dot = _B.dot(new Vector3d(pA).fma(-1.0, b0)); if (dot < 0.0) { dot = 0.0; } else if (dot > magB) { dot = magB; } - pB = b0.clone().add(_B.clone().scale(dot)); + pB = new Vector3d(b0).add(new Vector3d(_B).mul(dot)); } if (t1 < 0.0 || t1 > magA) { - double dot = _A.dot(pB.clone().addScaled(a0, -1.0)); + double dot = _A.dot(new Vector3d(pB).fma(-1.0, a0)); if (dot < 0.0) { dot = 0.0; } else if (dot > magA) { dot = magA; } - pA = a0.clone().add(_A.clone().scale(dot)); + pA = new Vector3d(a0).add(new Vector3d(_A).mul(dot)); } } - p0Out.assign(pA); - p1Out.assign(pB); + p0Out.set(pA); + p1Out.set(pB); flags[1] = true; return flags; } } - public static double determinant(@Nonnull Vector3d v1, @Nonnull Vector3d v2) { - Vector3d crossProduct = v1.cross(v2); + public static double determinant(@Nonnull Vector3dc v1, @Nonnull Vector3dc v2) { + Vector3d crossProduct = v1.cross(v2, new Vector3d()); return crossProduct.length(); } @@ -193,15 +200,15 @@ public class VectorUtil { @Nonnull public static DoubleObjectPair distanceAndNearestPointOnSegment3d(@Nonnull Vector3d point, @Nonnull Vector3d p0, @Nonnull Vector3d p1) { - Vector3d lineVec = p1.clone().addScaled(p0, -1.0); - Vector3d pointVec = point.clone().addScaled(p0, -1.0); + Vector3d lineVec = new Vector3d(p1).fma(-1.0, p0); + Vector3d pointVec = new Vector3d(point).fma(-1.0, p0); double lineLength = lineVec.length(); - Vector3d lineUnitVec = lineVec.clone().setLength(1.0); - Vector3d pointVecScaled = pointVec.clone().scale(1.0 / lineLength); + Vector3d lineUnitVec = new Vector3d(lineVec).normalize(1.0); + Vector3d pointVecScaled = new Vector3d(pointVec).mul(1.0 / lineLength); double t = lineUnitVec.dot(pointVecScaled); t = Calculator.clamp(0.0, t, 1.0); - Vector3d nearestPoint = lineVec.clone().scale(t); - return DoubleObjectPair.of(nearestPoint.distanceTo(pointVec), nearestPoint.add(p0)); + Vector3d nearestPoint = new Vector3d(lineVec).mul(t); + return DoubleObjectPair.of(nearestPoint.distance(pointVec), nearestPoint.add(p0)); } public static double angle(@Nonnull Vector3d a, @Nonnull Vector3d b) { @@ -229,7 +236,7 @@ public class VectorUtil { } public static void rotateVectorByAxisAngle(@Nonnull Vector3d vec, @Nonnull Vector3d axis, double angle) { - Vector3d crossProd = axis.cross(vec); + Vector3d crossProd = axis.cross(vec, new Vector3d()); double cosAngle = Math.cos(angle); double sinAngle = Math.sin(angle); double x = vec.x * cosAngle + crossProd.x * sinAngle + axis.x * axis.dot(vec) * (1.0 - cosAngle); @@ -319,7 +326,6 @@ public class VectorUtil { vector.x >>= shift; vector.y >>= shift; vector.z >>= shift; - vector.dropHash(); } } @@ -330,7 +336,6 @@ public class VectorUtil { vector.x <<= shift; vector.y <<= shift; vector.z <<= shift; - vector.dropHash(); } } @@ -376,6 +381,11 @@ public class VectorUtil { this.index = index; } + @Nonnull + public static VectorUtil.Retriever ofIndex(int index) { + return new VectorUtil.Retriever(index); + } + public int getIndex() { return this.index; } @@ -413,10 +423,5 @@ public class VectorUtil { default -> throw new IllegalArgumentException(); }; } - - @Nonnull - public static VectorUtil.Retriever ofIndex(int index) { - return new VectorUtil.Retriever(index); - } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/NViewport.java b/src/com/hypixel/hytale/builtin/hytalegenerator/Viewport.java similarity index 88% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/NViewport.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/Viewport.java index a060a2ff..fb1221f8 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/NViewport.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/Viewport.java @@ -1,6 +1,5 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem; +package com.hypixel.hytale.builtin.hytalegenerator; -import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.server.core.command.system.CommandSender; @@ -11,7 +10,7 @@ import it.unimi.dsi.fastutil.longs.LongSet; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; -public class NViewport { +public class Viewport { @Nonnull private final World world; @Nonnull @@ -19,7 +18,7 @@ public class NViewport { @Nonnull private final LongSet affectedChunkIndices; - public NViewport(@Nonnull Bounds3i viewportBounds_voxelGrid, @Nonnull World world, @Nonnull CommandSender sender) { + public Viewport(@Nonnull Bounds3i viewportBounds_voxelGrid, @Nonnull World world, @Nonnull CommandSender sender) { this.world = world; this.sender = sender; int minCX = ChunkUtil.chunkCoordinate(viewportBounds_voxelGrid.min.x); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/WeightedMap.java b/src/com/hypixel/hytale/builtin/hytalegenerator/WeightedMap.java similarity index 98% rename from src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/WeightedMap.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/WeightedMap.java index 4583fef6..76ed5ae9 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/WeightedMap.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/WeightedMap.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures; +package com.hypixel.hytale.builtin.hytalegenerator; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/AssetManager.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/AssetManager.java index 290e091b..a45fbfda 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/AssetManager.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/AssetManager.java @@ -4,6 +4,12 @@ import com.hypixel.hytale.assetstore.AssetRegistry; import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.AssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.ConstantAssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.FieldFunctionAssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.ImportedAssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.SandwichAssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.WeightedAssignmentsAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.biomes.BiomeAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.blockmask.BlockMaskAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.CeilingCurveAsset; @@ -15,6 +21,7 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.DistanceSCurveAs import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.FloorCurveAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.ImportedCurveAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.InverterCurveAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.MaxCurveAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.MinCurveAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.MultiplierCurveAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.NotCurveAsset; @@ -152,13 +159,13 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatter import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.CuboidPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.DensityPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.FloorPatternAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.GapPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ImportedPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.MaterialPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.NotPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.OffsetPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.OrPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.RotatorPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.SurfacePatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.WallPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.pointgenerators.MeshPointGeneratorAsset; @@ -167,32 +174,47 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.Ancho import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.BaseHeightPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.BoundPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.CachedPositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.ClustersPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.FieldFunctionOccurrencePositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.FieldFunctionPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.FrameworkPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.ImportedPositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.Jitter2dPositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.Jitter3dPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.ListPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.Mesh2DPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.Mesh3DPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.OffsetPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.PositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.ScalerPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.SimpleHorizontalPositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.SquareGrid2dPositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.SquareGrid3dPositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.TriangularGrid2dPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.UnionPositionProviderAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.AssignmentsAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.ConstantAssignmentsAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.FieldFunctionAssignmentsAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.ImportedAssignmentsAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.SandwichAssignmentsAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.WeightedAssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.AssignedPropDistributionAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.ConstantPropDistributionAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.ImportedPropDistributionAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.PositionsPropDistributionAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.PropDistributionAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.UnionPropDistributionAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.BoxPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.ClusterPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.ColumnPropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.CuboidPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.DensityPropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.DensitySelectorPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.ImportedPropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.LocatorPropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.ManualPropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.MaskPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.OffsetPropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.OrienterPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.PondFillerPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.PropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.QueuePropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.RandomRotatorPropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.StaticRotatorPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.UnionPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.WeightedPropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.PrefabPropAsset; @@ -204,8 +226,12 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.direct import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.AreaScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ColumnLinearScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ColumnRandomScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.DirectScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ImportedScannerAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.OriginScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.LinearScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.QueueScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.RadialScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.RandomScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.terrains.DensityTerrainAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.terrains.TerrainAsset; @@ -241,6 +267,12 @@ public class AssetManager { private final HashMap worldStructureAssets; @Nonnull private final HashMap blockMaskAssets; + @Nonnull + private final HashMap propDistributionAssets; + @Nonnull + private final HashMap positionProviderAssets; + @Nonnull + private final HashMap propAssets; private SettingsAsset settingsAsset; @Nonnull private final HytaleLogger logger; @@ -254,12 +286,51 @@ public class AssetManager { this.biomeAssets = new HashMap<>(1); this.worldStructureAssets = new HashMap<>(1); this.blockMaskAssets = new HashMap<>(1); + this.propDistributionAssets = new HashMap<>(1); + this.positionProviderAssets = new HashMap<>(1); + this.propAssets = new HashMap<>(1); eventRegistry.register(LoadedAssetsEvent.class, DensityAsset.class, this::loadDensityAssets); eventRegistry.register(LoadedAssetsEvent.class, AssignmentsAsset.class, this::loadAssignmentsAssets); eventRegistry.register(LoadedAssetsEvent.class, BiomeAsset.class, this::loadBiomeAssets); eventRegistry.register(LoadedAssetsEvent.class, WorldStructureAsset.class, this::loadWorldStructureAssets); eventRegistry.register(LoadedAssetsEvent.class, SettingsAsset.class, this::loadSettingsAssets); eventRegistry.register(LoadedAssetsEvent.class, BlockMaskAsset.class, this::loadBlockMaskAssets); + eventRegistry.register(LoadedAssetsEvent.class, PropDistributionAsset.class, this::loadPropDistributionAssets); + eventRegistry.register(LoadedAssetsEvent.class, PositionProviderAsset.class, this::loadPositionProviderAssets); + eventRegistry.register(LoadedAssetsEvent.class, PropAsset.class, this::loadPropAssets); + } + + private void loadPropAssets(@Nonnull LoadedAssetsEvent> event) { + this.blockMaskAssets.clear(); + + for (PropAsset value : event.getLoadedAssets().values()) { + this.propAssets.put(value.getId(), value); + this.logger.at(Level.FINE).log("Loaded Prop asset " + value); + } + + this.triggerReloadListeners(); + } + + private void loadPositionProviderAssets(@Nonnull LoadedAssetsEvent> event) { + this.blockMaskAssets.clear(); + + for (PositionProviderAsset value : event.getLoadedAssets().values()) { + this.positionProviderAssets.put(value.getId(), value); + this.logger.at(Level.FINE).log("Loaded PositionProvider asset " + value); + } + + this.triggerReloadListeners(); + } + + private void loadPropDistributionAssets(@Nonnull LoadedAssetsEvent> event) { + this.blockMaskAssets.clear(); + + for (PropDistributionAsset value : event.getLoadedAssets().values()) { + this.propDistributionAssets.put(value.getId(), value); + this.logger.at(Level.FINE).log("Loaded PropDistribution asset " + value); + } + + this.triggerReloadListeners(); } private void loadBlockMaskAssets(@Nonnull LoadedAssetsEvent> event) { @@ -391,6 +462,31 @@ public class AssetManager { .setCodec(AssignmentsAsset.CODEC)) .build() ); + AssetRegistry.register( + ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( + PropDistributionAsset.class, new DefaultAssetMap() + ) + .setPath("HytaleGenerator/PropDistributions")) + .setKeyFunction(PropDistributionAsset::getId)) + .setCodec(PropDistributionAsset.CODEC)) + .build() + ); + AssetRegistry.register( + ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( + PositionProviderAsset.class, new DefaultAssetMap() + ) + .setPath("HytaleGenerator/Positions")) + .setKeyFunction(PositionProviderAsset::getId)) + .setCodec(PositionProviderAsset.CODEC)) + .build() + ); + AssetRegistry.register( + ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(PropAsset.class, new DefaultAssetMap()) + .setPath("HytaleGenerator/Props")) + .setKeyFunction(PropAsset::getId)) + .setCodec(PropAsset.CODEC)) + .build() + ); AssetRegistry.register( ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(SettingsAsset.class, new DefaultAssetMap()) .setPath("HytaleGenerator/Settings")) @@ -512,6 +608,13 @@ public class AssetManager { PositionProviderAsset.CODEC.register("Anchor", AnchorPositionProviderAsset.class, AnchorPositionProviderAsset.CODEC); PositionProviderAsset.CODEC.register("Bound", BoundPositionProviderAsset.class, BoundPositionProviderAsset.CODEC); PositionProviderAsset.CODEC.register("Framework", FrameworkPositionProviderAsset.class, FrameworkPositionProviderAsset.CODEC); + PositionProviderAsset.CODEC.register("SquareGrid2d", SquareGrid2dPositionProviderAsset.class, SquareGrid2dPositionProviderAsset.CODEC); + PositionProviderAsset.CODEC.register("SquareGrid3d", SquareGrid3dPositionProviderAsset.class, SquareGrid3dPositionProviderAsset.CODEC); + PositionProviderAsset.CODEC.register("TriangularGrid2d", TriangularGrid2dPositionProviderAsset.class, TriangularGrid2dPositionProviderAsset.CODEC); + PositionProviderAsset.CODEC.register("Scaler", ScalerPositionProviderAsset.class, ScalerPositionProviderAsset.CODEC); + PositionProviderAsset.CODEC.register("Jitter2d", Jitter2dPositionProviderAsset.class, Jitter2dPositionProviderAsset.CODEC); + PositionProviderAsset.CODEC.register("Jitter3d", Jitter3dPositionProviderAsset.class, Jitter3dPositionProviderAsset.CODEC); + PositionProviderAsset.CODEC.register("Clusters", ClustersPositionProviderAsset.class, ClustersPositionProviderAsset.CODEC); PointGeneratorAsset.CODEC.register("Mesh", MeshPointGeneratorAsset.class, MeshPointGeneratorAsset.CODEC); AssignmentsAsset.CODEC.register("FieldFunction", FieldFunctionAssignmentsAsset.class, FieldFunctionAssignmentsAsset.CODEC); AssignmentsAsset.CODEC.register("Sandwich", SandwichAssignmentsAsset.class, SandwichAssignmentsAsset.CODEC); @@ -523,12 +626,25 @@ public class AssetManager { PropAsset.CODEC.register("Union", UnionPropAsset.class, UnionPropAsset.CODEC); PropAsset.CODEC.register("Column", ColumnPropAsset.class, ColumnPropAsset.CODEC); PropAsset.CODEC.register("Cluster", ClusterPropAsset.class, ClusterPropAsset.CODEC); - PropAsset.CODEC.register("Queue", QueuePropAsset.class, QueuePropAsset.CODEC); PropAsset.CODEC.register("Prefab", PrefabPropAsset.class, PrefabPropAsset.CODEC); PropAsset.CODEC.register("PondFiller", PondFillerPropAsset.class, PondFillerPropAsset.CODEC); PropAsset.CODEC.register("Density", DensityPropAsset.class, DensityPropAsset.CODEC); PropAsset.CODEC.register("Offset", OffsetPropAsset.class, OffsetPropAsset.CODEC); PropAsset.CODEC.register("Weighted", WeightedPropAsset.class, WeightedPropAsset.CODEC); + PropAsset.CODEC.register("Cuboid", CuboidPropAsset.class, CuboidPropAsset.CODEC); + PropAsset.CODEC.register("Manual", ManualPropAsset.class, ManualPropAsset.CODEC); + PropAsset.CODEC.register("Locator", LocatorPropAsset.class, LocatorPropAsset.CODEC); + PropAsset.CODEC.register("Queue", QueuePropAsset.class, QueuePropAsset.CODEC); + PropAsset.CODEC.register("Mask", MaskPropAsset.class, MaskPropAsset.CODEC); + PropAsset.CODEC.register("StaticRotator", StaticRotatorPropAsset.class, StaticRotatorPropAsset.CODEC); + PropAsset.CODEC.register("RandomRotator", RandomRotatorPropAsset.class, RandomRotatorPropAsset.CODEC); + PropAsset.CODEC.register("Orienter", OrienterPropAsset.class, OrienterPropAsset.CODEC); + PropAsset.CODEC.register("DensitySelector", DensitySelectorPropAsset.class, DensitySelectorPropAsset.CODEC); + PropDistributionAsset.CODEC.register("Constant", ConstantPropDistributionAsset.class, ConstantPropDistributionAsset.CODEC); + PropDistributionAsset.CODEC.register("Assigned", AssignedPropDistributionAsset.class, AssignedPropDistributionAsset.CODEC); + PropDistributionAsset.CODEC.register("Positions", PositionsPropDistributionAsset.class, PositionsPropDistributionAsset.CODEC); + PropDistributionAsset.CODEC.register("Union", UnionPropDistributionAsset.class, UnionPropDistributionAsset.CODEC); + PropDistributionAsset.CODEC.register("Imported", ImportedPropDistributionAsset.class, ImportedPropDistributionAsset.CODEC); DirectionalityAsset.CODEC.register("Imported", ImportedDirectionalityAsset.class, ImportedDirectionalityAsset.CODEC); DirectionalityAsset.CODEC.register("Static", StaticDirectionalityAsset.class, StaticDirectionalityAsset.CODEC); DirectionalityAsset.CODEC.register("Random", RandomDirectionalityAsset.class, RandomDirectionalityAsset.CODEC); @@ -544,15 +660,19 @@ public class AssetManager { PatternAsset.CODEC.register("Or", OrPatternAsset.class, OrPatternAsset.CODEC); PatternAsset.CODEC.register("Not", NotPatternAsset.class, NotPatternAsset.CODEC); PatternAsset.CODEC.register("Surface", SurfacePatternAsset.class, SurfacePatternAsset.CODEC); - PatternAsset.CODEC.register("Gap", GapPatternAsset.class, GapPatternAsset.CODEC); PatternAsset.CODEC.register("FieldFunction", DensityPatternAsset.class, DensityPatternAsset.CODEC); PatternAsset.CODEC.register("Imported", ImportedPatternAsset.class, ImportedPatternAsset.CODEC); PatternAsset.CODEC.register("Constant", ConstantPatternAsset.class, ConstantPatternAsset.CODEC); + PatternAsset.CODEC.register("Rotator", RotatorPatternAsset.class, RotatorPatternAsset.CODEC); ScannerAsset.CODEC.register("ColumnLinear", ColumnLinearScannerAsset.class, ColumnLinearScannerAsset.CODEC); ScannerAsset.CODEC.register("ColumnRandom", ColumnRandomScannerAsset.class, ColumnRandomScannerAsset.CODEC); - ScannerAsset.CODEC.register("Origin", OriginScannerAsset.class, OriginScannerAsset.CODEC); + ScannerAsset.CODEC.register("Origin", DirectScannerAsset.class, DirectScannerAsset.CODEC); ScannerAsset.CODEC.register("Area", AreaScannerAsset.class, AreaScannerAsset.CODEC); ScannerAsset.CODEC.register("Imported", ImportedScannerAsset.class, ImportedScannerAsset.CODEC); + ScannerAsset.CODEC.register("Linear", LinearScannerAsset.class, LinearScannerAsset.CODEC); + ScannerAsset.CODEC.register("Random", RandomScannerAsset.class, RandomScannerAsset.CODEC); + ScannerAsset.CODEC.register("Queue", QueueScannerAsset.class, QueueScannerAsset.CODEC); + ScannerAsset.CODEC.register("Radial", RadialScannerAsset.class, RadialScannerAsset.CODEC); CurveAsset.CODEC.register("Imported", ImportedCurveAsset.class, ImportedCurveAsset.CODEC); CurveAsset.CODEC.register("Manual", ManualCurveAsset.class, ManualCurveAsset.CODEC); CurveAsset.CODEC.register("DistanceExponential", DistanceExponentialCurveAsset.class, DistanceExponentialCurveAsset.CODEC); @@ -564,7 +684,7 @@ public class AssetManager { CurveAsset.CODEC.register("Clamp", ClampCurveAsset.class, ClampCurveAsset.CODEC); CurveAsset.CODEC.register("SmoothClamp", SmoothClampCurveAsset.class, SmoothClampCurveAsset.CODEC); CurveAsset.CODEC.register("Min", MinCurveAsset.class, MinCurveAsset.CODEC); - CurveAsset.CODEC.register("Max", MinCurveAsset.class, MinCurveAsset.CODEC); + CurveAsset.CODEC.register("Max", MaxCurveAsset.class, MaxCurveAsset.CODEC); CurveAsset.CODEC.register("SmoothMin", SmoothMinCurveAsset.class, SmoothMinCurveAsset.CODEC); CurveAsset.CODEC.register("SmoothMax", SmoothMaxCurveAsset.class, SmoothMaxCurveAsset.CODEC); CurveAsset.CODEC.register("SmoothFloor", SmoothFloorCurveAsset.class, SmoothFloorCurveAsset.CODEC); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/ValidatorUtil.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/ValidatorUtil.java index e688eccf..4fd8ad79 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/ValidatorUtil.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/ValidatorUtil.java @@ -1,13 +1,15 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets; -import com.hypixel.hytale.codec.validation.LegacyValidator; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; import javax.annotation.Nonnull; public class ValidatorUtil { @Nonnull - public static LegacyValidator validEnumValue(@Nonnull final T[] values) { - return new LegacyValidator() { + public static Validator validEnumValue(@Nonnull final T[] values) { + return new Validator() { public void accept(String providedValue, @Nonnull ValidationResults results) { for (T value : values) { if (value.toString().equals(providedValue)) { @@ -17,6 +19,10 @@ public class ValidatorUtil { results.fail("String not a valid enum value: " + providedValue); } + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } }; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/AssignmentsAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/AssignmentsAsset.java similarity index 82% rename from src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/AssignmentsAsset.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/AssignmentsAsset.java index d21485da..d1aca194 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/AssignmentsAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/AssignmentsAsset.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments; +package com.hypixel.hytale.builtin.hytalegenerator.assets.assignments; import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetCodecMapCodec; @@ -7,11 +7,12 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.PropDistributionAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.Assignments; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -78,20 +79,14 @@ public abstract class AssignmentsAsset implements Cleanable, JsonAssetWithMap("Prop", PropAsset.CODEC, true), (asset, v) -> asset.propAsset = v, asset -> asset.propAsset) .add() .build(); - private PropAsset propAsset = new NoPropAsset(); + private PropAsset propAsset = new EmptyPropAsset(); @Nonnull @Override public Assignments build(@Nonnull AssignmentsAsset.Argument argument) { if (super.skip()) { - return Assignments.noPropDistribution(argument.runtime); + return Assignments.noPropDistribution(); } else { Prop prop = this.propAsset.build(new PropAsset.Argument(argument.parentSeed, argument.materialCache, argument.referenceBundle, argument.workerId)); - return new ConstantAssignments(prop, argument.runtime); + return new ConstantAssignments(prop); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/FieldFunctionAssignmentsAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/FieldFunctionAssignmentsAsset.java similarity index 92% rename from src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/FieldFunctionAssignmentsAsset.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/FieldFunctionAssignmentsAsset.java index 2beebb65..a4a2b171 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/FieldFunctionAssignmentsAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/FieldFunctionAssignmentsAsset.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments; +package com.hypixel.hytale.builtin.hytalegenerator.assets.assignments; import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; @@ -7,9 +7,9 @@ import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.ConstantDensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.Assignments; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.FieldFunctionAssignments; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.FieldFunctionAssignments; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -40,7 +40,7 @@ public class FieldFunctionAssignmentsAsset extends AssignmentsAsset { @Override public Assignments build(@Nonnull AssignmentsAsset.Argument argument) { if (super.skip()) { - return Assignments.noPropDistribution(argument.runtime); + return Assignments.noPropDistribution(); } else { Density functionTree = this.densityAsset.build(DensityAsset.from(argument)); ArrayList delimiterList = new ArrayList<>(); @@ -51,7 +51,7 @@ public class FieldFunctionAssignmentsAsset extends AssignmentsAsset { delimiterList.add(delimiter); } - return new FieldFunctionAssignments(functionTree, delimiterList, argument.runtime); + return new FieldFunctionAssignments(functionTree, delimiterList); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/ImportedAssignmentsAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/ImportedAssignmentsAsset.java similarity index 80% rename from src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/ImportedAssignmentsAsset.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/ImportedAssignmentsAsset.java index 22434359..ebaa14c5 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/ImportedAssignmentsAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/ImportedAssignmentsAsset.java @@ -1,7 +1,7 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments; +package com.hypixel.hytale.builtin.hytalegenerator.assets.assignments; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.Assignments; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -20,12 +20,12 @@ public class ImportedAssignmentsAsset extends AssignmentsAsset { @Override public Assignments build(@Nonnull AssignmentsAsset.Argument argument) { if (super.skip()) { - return Assignments.noPropDistribution(argument.runtime); + return Assignments.noPropDistribution(); } else { AssignmentsAsset asset = getExportedAsset(this.name); if (asset == null) { LoggerUtil.getLogger().warning("Couldn't find Assignments asset exported with name: '" + this.name + "'."); - return Assignments.noPropDistribution(argument.runtime); + return Assignments.noPropDistribution(); } else { return asset.build(argument); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/SandwichAssignmentsAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/SandwichAssignmentsAsset.java similarity index 90% rename from src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/SandwichAssignmentsAsset.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/SandwichAssignmentsAsset.java index 160788f3..ca890708 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/SandwichAssignmentsAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/SandwichAssignmentsAsset.java @@ -1,12 +1,12 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments; +package com.hypixel.hytale.builtin.hytalegenerator.assets.assignments; import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.SandwichAssignments; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.Assignments; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.SandwichAssignments; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -32,7 +32,7 @@ public class SandwichAssignmentsAsset extends AssignmentsAsset { @Override public Assignments build(@Nonnull AssignmentsAsset.Argument argument) { if (super.skip()) { - return Assignments.noPropDistribution(argument.runtime); + return Assignments.noPropDistribution(); } else { ArrayList delimiterList = new ArrayList<>(); @@ -42,7 +42,7 @@ public class SandwichAssignmentsAsset extends AssignmentsAsset { delimiterList.add(delimiter); } - return new SandwichAssignments(delimiterList, argument.runtime); + return new SandwichAssignments(delimiterList); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/WeightedAssignmentsAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/WeightedAssignmentsAsset.java similarity index 87% rename from src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/WeightedAssignmentsAsset.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/WeightedAssignmentsAsset.java index b2a9a244..890afce7 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propassignments/WeightedAssignmentsAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/assignments/WeightedAssignmentsAsset.java @@ -1,14 +1,14 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments; +package com.hypixel.hytale.builtin.hytalegenerator.assets.assignments; import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.WeightedAssignments; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.Assignments; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.WeightedAssignments; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -41,7 +41,7 @@ public class WeightedAssignmentsAsset extends AssignmentsAsset { @Override public Assignments build(@Nonnull AssignmentsAsset.Argument argument) { if (super.skip()) { - return Assignments.noPropDistribution(argument.runtime); + return Assignments.noPropDistribution(); } else { WeightedMap weightMap = new WeightedMap<>(); @@ -50,7 +50,7 @@ public class WeightedAssignmentsAsset extends AssignmentsAsset { } SeedBox childSeed = argument.parentSeed.child(this.seed); - return new WeightedAssignments(weightMap, childSeed.createSupplier().get(), this.skipChance, argument.runtime); + return new WeightedAssignments(weightMap, childSeed.createSupplier().get(), this.skipChance); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/biomes/BiomeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/biomes/BiomeAsset.java index 50ababd0..4bca49e6 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/biomes/BiomeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/biomes/BiomeAsset.java @@ -7,14 +7,14 @@ import com.hypixel.hytale.assetstore.AssetStore; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; -import com.hypixel.hytale.builtin.hytalegenerator.PropField; +import com.hypixel.hytale.builtin.hytalegenerator.PropRuntime; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.environmentproviders.ConstantEnvironmentProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.environmentproviders.EnvironmentProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.ConstantMaterialProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.MaterialProviderAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propstageiterations.PropRuntimeAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propruntime.PropRuntimeAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.terrains.DensityTerrainAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.terrains.TerrainAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.tintproviders.ConstantTintProviderAsset; @@ -26,12 +26,10 @@ import com.hypixel.hytale.builtin.hytalegenerator.environmentproviders.Environme import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; -import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.builtin.hytalegenerator.tintproviders.TintProvider; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; @@ -147,10 +145,10 @@ public class BiomeAsset implements JsonAssetWithMap> { @Nonnull @@ -21,9 +22,9 @@ public class DecimalBounds3dAsset implements JsonAssetWithMap config.data = data, config -> config.data ) - .append(new KeyedCodec<>("PointA", Vector3d.CODEC, true), (t, value) -> t.pointA = value, t -> t.pointA) + .append(new KeyedCodec<>("PointA", Vector3dUtil.CODEC, true), (t, value) -> t.pointA = value, t -> t.pointA) .add() - .append(new KeyedCodec<>("PointB", Vector3d.CODEC, true), (t, value) -> t.pointB = value, t -> t.pointB) + .append(new KeyedCodec<>("PointB", Vector3dUtil.CODEC, true), (t, value) -> t.pointB = value, t -> t.pointB) .add() .build(); private String id; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/bounds/IntegerBounds3dAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/bounds/IntegerBounds3dAsset.java index a18251ee..e26e5fa4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/bounds/IntegerBounds3dAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/bounds/IntegerBounds3dAsset.java @@ -7,8 +7,9 @@ import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class IntegerBounds3dAsset implements JsonAssetWithMap> { @Nonnull @@ -21,9 +22,9 @@ public class IntegerBounds3dAsset implements JsonAssetWithMap config.data = data, config -> config.data ) - .append(new KeyedCodec<>("PointA", Vector3i.CODEC, true), (t, value) -> t.pointA = value, t -> t.pointA) + .append(new KeyedCodec<>("PointA", Vector3iUtil.CODEC, true), (t, value) -> t.pointA = value, t -> t.pointA) .add() - .append(new KeyedCodec<>("PointB", Vector3i.CODEC, true), (t, value) -> t.pointB = value, t -> t.pointB) + .append(new KeyedCodec<>("PointB", Vector3iUtil.CODEC, true), (t, value) -> t.pointB = value, t -> t.pointB) .add() .build(); private String id; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/ClampCurveAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/ClampCurveAsset.java index d2ef9c67..49c7c0e0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/ClampCurveAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/ClampCurveAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.curves; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/DistanceSCurveAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/DistanceSCurveAsset.java index db831709..3cd0d4a3 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/DistanceSCurveAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/DistanceSCurveAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.curves; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.InterpolatedCurve; +import com.hypixel.hytale.builtin.hytalegenerator.math.InterpolatedCurve; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothCeilingCurveAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothCeilingCurveAsset.java index 81dfe0a3..47d25076 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothCeilingCurveAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothCeilingCurveAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.curves; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothClampCurveAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothClampCurveAsset.java index 960e03b9..1979d5ff 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothClampCurveAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothClampCurveAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.curves; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothFloorCurveAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothFloorCurveAsset.java index 58df3e9d..835179be 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothFloorCurveAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothFloorCurveAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.curves; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothMaxCurveAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothMaxCurveAsset.java index d6970f63..8b475cb4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothMaxCurveAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothMaxCurveAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.curves; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothMinCurveAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothMinCurveAsset.java index 8f34fd38..9367d9ba 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothMinCurveAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/SmoothMinCurveAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.curves; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/legacy/NodeFunctionYOutAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/legacy/NodeFunctionYOutAsset.java index 5f793f87..5272dd0d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/legacy/NodeFunctionYOutAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/legacy/NodeFunctionYOutAsset.java @@ -5,14 +5,17 @@ import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.NodeFunction; +import com.hypixel.hytale.builtin.hytalegenerator.math.NodeFunction; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; -import com.hypixel.hytale.math.vector.Vector2d; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; import java.util.HashSet; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class NodeFunctionYOutAsset implements JsonAssetWithMap>, Cleanable { @Nonnull @@ -26,18 +29,24 @@ public class NodeFunctionYOutAsset implements JsonAssetWithMap config.data ) .append(new KeyedCodec<>("Points", new ArrayCodec<>(PointYOutAsset.CODEC, PointYOutAsset[]::new), true), (t, k) -> t.nodes = k, t -> t.nodes) - .addValidator((LegacyValidator)((v, r) -> { - HashSet ySet = new HashSet<>(v.length); + .addValidator(new Validator() { + public void accept(PointYOutAsset[] v, ValidationResults r) { + HashSet ySet = new HashSet<>(v.length); - for (PointYOutAsset point : v) { - if (ySet.contains(point.getY())) { - r.fail("More than one point with Y value: " + point.getY()); - return; + for (PointYOutAsset point : v) { + if (ySet.contains(point.getY())) { + r.fail("More than one point with Y value: " + point.getY()); + return; + } + + ySet.add(point.getY()); } - - ySet.add(point.getY()); } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() .build(); private String id; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/legacy/PointYOutAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/legacy/PointYOutAsset.java index 598376b8..9e62c48f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/legacy/PointYOutAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/legacy/PointYOutAsset.java @@ -6,8 +6,8 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.math.vector.Vector2d; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class PointYOutAsset implements JsonAssetWithMap> { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/manual/ManualCurveAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/manual/ManualCurveAsset.java index 1746e803..a90df479 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/manual/ManualCurveAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/manual/ManualCurveAsset.java @@ -2,14 +2,14 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.curves.manual; import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.CurveAsset; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.NodeFunction; +import com.hypixel.hytale.builtin.hytalegenerator.math.NodeFunction; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.LegacyValidator; -import com.hypixel.hytale.math.vector.Vector2d; import java.util.HashSet; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class ManualCurveAsset extends CurveAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/manual/PointInOutAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/manual/PointInOutAsset.java index 6c8f710b..7caeabcc 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/manual/PointInOutAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/curves/manual/PointInOutAsset.java @@ -6,8 +6,8 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.math.vector.Vector2d; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class PointInOutAsset implements JsonAssetWithMap> { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/AngleDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/AngleDensityAsset.java index a57ad559..3b5d8a5f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/AngleDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/AngleDensityAsset.java @@ -9,8 +9,9 @@ import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class AngleDensityAsset extends DensityAsset { @Nonnull @@ -23,7 +24,7 @@ public class AngleDensityAsset extends DensityAsset { value -> value.vectorProviderAsset ) .add() - .append(new KeyedCodec<>("Vector", Vector3d.CODEC, true), (asset, value) -> asset.vector = value, asset -> asset.vector) + .append(new KeyedCodec<>("Vector", Vector3dUtil.CODEC, true), (asset, value) -> asset.vector = value, asset -> asset.vector) .add() .append(new KeyedCodec<>("IsAxis", Codec.BOOLEAN, true), (asset, value) -> asset.isAxis = value, asset -> asset.isAxis) .add() diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/AxisDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/AxisDensityAsset.java index 2783ca2f..5b5a6d42 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/AxisDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/AxisDensityAsset.java @@ -8,9 +8,13 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.ConstantValueDen import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class AxisDensityAsset extends DensityAsset { @Nonnull @@ -19,12 +23,18 @@ public class AxisDensityAsset extends DensityAsset { .add() .append(new KeyedCodec<>("IsAnchored", Codec.BOOLEAN, false), (t, k) -> t.isAnchored = k, k -> k.isAnchored) .add() - .append(new KeyedCodec<>("Axis", Vector3d.CODEC, false), (t, k) -> t.axis = k, k -> k.axis) - .addValidator((LegacyValidator)((v, r) -> { - if (v.length() == 0.0) { - r.fail("Axis can't be a zero vector."); + .append(new KeyedCodec<>("Axis", Vector3dUtil.CODEC, false), (t, k) -> t.axis = k, k -> k.axis) + .addValidator(new Validator() { + public void accept(Vector3d v, ValidationResults r) { + if (v.length() == 0.0) { + r.fail("Axis can't be a zero vector."); + } } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() .build(); private CurveAsset distanceCurveAsset = new ConstantCurveAsset(); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CellNoise2DDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CellNoise2DDensityAsset.java index d464de50..79aaee99 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CellNoise2DDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CellNoise2DDensityAsset.java @@ -5,9 +5,9 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.ConstantValueDen import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.MultiCacheDensity; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.Noise2dDensity; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.YOverrideDensity; -import com.hypixel.hytale.builtin.hytalegenerator.fields.FastNoiseLite; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.CellNoiseField; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.CellNoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.noise.FastNoiseLite; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CellNoise3DDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CellNoise3DDensityAsset.java index e04d0825..853a6248 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CellNoise3DDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CellNoise3DDensityAsset.java @@ -3,9 +3,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.ConstantValueDensity; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.Noise3dDensity; -import com.hypixel.hytale.builtin.hytalegenerator.fields.FastNoiseLite; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.CellNoiseField; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.CellNoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.noise.FastNoiseLite; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CuboidDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CuboidDensityAsset.java index d190a8ea..c0994eb4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CuboidDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CuboidDensityAsset.java @@ -10,9 +10,13 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.ScaleDensity; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CuboidDensityAsset extends DensityAsset { @Nonnull @@ -21,14 +25,20 @@ public class CuboidDensityAsset extends DensityAsset { ) .append(new KeyedCodec<>("Curve", CurveAsset.CODEC, true), (t, k) -> t.densityCurveAsset = k, k -> k.densityCurveAsset) .add() - .append(new KeyedCodec<>("Scale", Vector3d.CODEC, false), (t, k) -> t.scaleVector = k, k -> k.scaleVector) - .addValidator((LegacyValidator)((v, r) -> { - if (v.x == 0.0 || v.y == 0.0 || v.z == 0.0) { - r.fail("scale vector contains 0.0"); + .append(new KeyedCodec<>("Scale", Vector3dUtil.CODEC, false), (t, k) -> t.scaleVector = k, k -> k.scaleVector) + .addValidator(new Validator() { + public void accept(Vector3d v, ValidationResults r) { + if (v.x == 0.0 || v.y == 0.0 || v.z == 0.0) { + r.fail("scale vector contains 0.0"); + } } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() - .append(new KeyedCodec<>("NewYAxis", Vector3d.CODEC, false), (t, k) -> { + .append(new KeyedCodec<>("NewYAxis", Vector3dUtil.CODEC, false), (t, k) -> { if (k.length() != 0.0) { t.newYAxis = k; } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CylinderDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CylinderDensityAsset.java index c1f6c731..a9f68e20 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CylinderDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/CylinderDensityAsset.java @@ -9,8 +9,9 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.RotatorDensity; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CylinderDensityAsset extends DensityAsset { @Nonnull @@ -21,7 +22,7 @@ public class CylinderDensityAsset extends DensityAsset { .add() .append(new KeyedCodec<>("AxialCurve", CurveAsset.CODEC, true), (t, k) -> t.axialCurveAsset = k, k -> k.axialCurveAsset) .add() - .append(new KeyedCodec<>("NewYAxis", Vector3d.CODEC, false), (t, k) -> { + .append(new KeyedCodec<>("NewYAxis", Vector3dUtil.CODEC, false), (t, k) -> { if (k.length() != 0.0) { t.newYAxis = k; } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/DensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/DensityAsset.java index c1f611d4..5b994eae 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/DensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/DensityAsset.java @@ -7,19 +7,19 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.AssignmentsAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.environmentproviders.EnvironmentProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.MaterialProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.PositionProviderAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.AssignmentsAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.PropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.tintproviders.TintProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.vectorproviders.VectorProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.worldstructures.WorldStructureAsset; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/EllipsoidDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/EllipsoidDensityAsset.java index 6afeb005..24033de9 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/EllipsoidDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/EllipsoidDensityAsset.java @@ -10,9 +10,13 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.ScaleDensity; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EllipsoidDensityAsset extends DensityAsset { @Nonnull @@ -21,14 +25,20 @@ public class EllipsoidDensityAsset extends DensityAsset { ) .append(new KeyedCodec<>("Curve", CurveAsset.CODEC, true), (t, k) -> t.densityCurveAsset = k, k -> k.densityCurveAsset) .add() - .append(new KeyedCodec<>("Scale", Vector3d.CODEC, false), (t, k) -> t.scaleVector = k, k -> k.scaleVector) - .addValidator((LegacyValidator)((v, r) -> { - if (v.x == 0.0 || v.y == 0.0 || v.z == 0.0) { - r.fail("scale vector contains 0.0"); + .append(new KeyedCodec<>("Scale", Vector3dUtil.CODEC, false), (t, k) -> t.scaleVector = k, k -> k.scaleVector) + .addValidator(new Validator() { + public void accept(Vector3d v, ValidationResults r) { + if (v.x == 0.0 || v.y == 0.0 || v.z == 0.0) { + r.fail("scale vector contains 0.0"); + } } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() - .append(new KeyedCodec<>("NewYAxis", Vector3d.CODEC, false), (t, k) -> { + .append(new KeyedCodec<>("NewYAxis", Vector3dUtil.CODEC, false), (t, k) -> { if (k.length() != 0.0) { t.newYAxis = k; } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/GradientDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/GradientDensityAsset.java index b5c2b666..4b03545b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/GradientDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/GradientDensityAsset.java @@ -6,22 +6,32 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.GradientDensity; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class GradientDensityAsset extends DensityAsset { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( GradientDensityAsset.class, GradientDensityAsset::new, DensityAsset.ABSTRACT_CODEC ) - .append(new KeyedCodec<>("Axis", Vector3d.CODEC, false), (t, k) -> t.axis = k, k -> k.axis) - .addValidator((LegacyValidator)((v, r) -> { - if (v.x == 0.0 && v.y == 0.0 && v.z == 0.0) { - r.fail("Axis can't be zero."); + .append(new KeyedCodec<>("Axis", Vector3dUtil.CODEC, false), (t, k) -> t.axis = k, k -> k.axis) + .addValidator(new Validator() { + public void accept(Vector3d v, ValidationResults r) { + if (v.x == 0.0 && v.y == 0.0 && v.z == 0.0) { + r.fail("Axis can't be zero."); + } } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() .append(new KeyedCodec<>("SampleRange", Codec.DOUBLE, false), (t, k) -> t.sampleRange = k, t -> t.sampleRange) .addValidator(Validators.greaterThan(0.0)) @@ -35,7 +45,7 @@ public class GradientDensityAsset extends DensityAsset { public Density build(@Nonnull DensityAsset.Argument argument) { return (Density)(this.isSkipped() ? new ConstantValueDensity(0.0) - : new GradientDensity(this.buildFirstInput(argument), this.sampleRange, this.axis.clone())); + : new GradientDensity(this.buildFirstInput(argument), this.sampleRange, new Vector3d(this.axis))); } @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/PlaneDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/PlaneDensityAsset.java index 4b969f15..ed6b6b8f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/PlaneDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/PlaneDensityAsset.java @@ -8,9 +8,13 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.PlaneDensity; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PlaneDensityAsset extends DensityAsset { @Nonnull @@ -21,12 +25,18 @@ public class PlaneDensityAsset extends DensityAsset { .add() .append(new KeyedCodec<>("IsAnchored", Codec.BOOLEAN, false), (t, k) -> t.isAnchored = k, k -> k.isAnchored) .add() - .append(new KeyedCodec<>("PlaneNormal", Vector3d.CODEC, false), (t, k) -> t.planeNormal = k, k -> k.planeNormal) - .addValidator((LegacyValidator)((v, r) -> { - if (v.length() == 0.0) { - r.fail("Plane normal can't be a zero vector."); + .append(new KeyedCodec<>("PlaneNormal", Vector3dUtil.CODEC, false), (t, k) -> t.planeNormal = k, k -> k.planeNormal) + .addValidator(new Validator() { + public void accept(Vector3d v, ValidationResults r) { + if (v.length() == 0.0) { + r.fail("Plane normal can't be a zero vector."); + } } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() .build(); private CurveAsset distanceCurveAsset = new ConstantCurveAsset(); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/PositionsTwistDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/PositionsTwistDensityAsset.java index 55930075..7a2596ab 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/PositionsTwistDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/PositionsTwistDensityAsset.java @@ -11,8 +11,9 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PositionsTwistDensityAsset extends DensityAsset { @Nonnull @@ -25,7 +26,7 @@ public class PositionsTwistDensityAsset extends DensityAsset { .add() .append(new KeyedCodec<>("TwistCurve", CurveAsset.CODEC, true), (asset, v) -> asset.pinchCurveAsset = v, asset -> asset.pinchCurveAsset) .add() - .append(new KeyedCodec<>("TwistAxis", Vector3d.CODEC, true), (asset, v) -> asset.twistAxis = v, asset -> asset.twistAxis) + .append(new KeyedCodec<>("TwistAxis", Vector3dUtil.CODEC, true), (asset, v) -> asset.twistAxis = v, asset -> asset.twistAxis) .add() .append(new KeyedCodec<>("MaxDistance", Codec.DOUBLE, true), (asset, v) -> asset.maxDistance = v, asset -> asset.maxDistance) .addValidator(Validators.greaterThanOrEqual(0.0)) diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/RotatorDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/RotatorDensityAsset.java index 64e6c303..52d28b94 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/RotatorDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/RotatorDensityAsset.java @@ -6,15 +6,16 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.RotatorDensity; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class RotatorDensityAsset extends DensityAsset { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( RotatorDensityAsset.class, RotatorDensityAsset::new, DensityAsset.ABSTRACT_CODEC ) - .append(new KeyedCodec<>("NewYAxis", Vector3d.CODEC, true), (t, k) -> t.newYAxis = k, t -> t.newYAxis) + .append(new KeyedCodec<>("NewYAxis", Vector3dUtil.CODEC, true), (t, k) -> t.newYAxis = k, t -> t.newYAxis) .add() .append(new KeyedCodec<>("SpinAngle", Codec.DOUBLE, true), (t, k) -> t.spinAngle = k, t -> t.spinAngle) .add() diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/ShellDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/ShellDensityAsset.java index 21ca23d8..a386f1c0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/ShellDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/ShellDensityAsset.java @@ -8,15 +8,16 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.ShellDensity; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ShellDensityAsset extends DensityAsset { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( ShellDensityAsset.class, ShellDensityAsset::new, DensityAsset.ABSTRACT_CODEC ) - .append(new KeyedCodec<>("Axis", Vector3d.CODEC, true), (t, k) -> t.axis = k, k -> k.axis) + .append(new KeyedCodec<>("Axis", Vector3dUtil.CODEC, true), (t, k) -> t.axis = k, k -> k.axis) .add() .append(new KeyedCodec<>("Mirror", Codec.BOOLEAN, false), (t, k) -> t.isMirrored = k, k -> k.isMirrored) .add() diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/SimplexNoise2dDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/SimplexNoise2dDensityAsset.java index 51a2b8c3..29db8334 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/SimplexNoise2dDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/SimplexNoise2dDensityAsset.java @@ -5,8 +5,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.ConstantValueDen import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.MultiCacheDensity; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.Noise2dDensity; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.YOverrideDensity; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.SimplexNoiseField; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.SimplexNoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/SimplexNoise3DDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/SimplexNoise3DDensityAsset.java index 1b43251b..fff46f3f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/SimplexNoise3DDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/SimplexNoise3DDensityAsset.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.ConstantValueDensity; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.Noise3dDensity; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.SimplexNoiseField; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.SimplexNoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/VectorWarpDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/VectorWarpDensityAsset.java index 9d01d1e7..88d983d9 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/VectorWarpDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/VectorWarpDensityAsset.java @@ -6,8 +6,9 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.VectorWarpDensit import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class VectorWarpDensityAsset extends DensityAsset { @Nonnull @@ -16,7 +17,7 @@ public class VectorWarpDensityAsset extends DensityAsset { ) .append(new KeyedCodec<>("WarpFactor", Codec.DOUBLE, true), (t, k) -> t.warpFactor = k, t -> t.warpFactor) .add() - .append(new KeyedCodec<>("WarpVector", Vector3d.CODEC, true), (t, k) -> t.warpVector = k, t -> t.warpVector) + .append(new KeyedCodec<>("WarpVector", Vector3dUtil.CODEC, true), (t, k) -> t.warpVector = k, t -> t.warpVector) .add() .build(); private double warpFactor = 1.0; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/YSampledDensityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/YSampledDensityAsset.java index 90a3ac08..abeede84 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/YSampledDensityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/YSampledDensityAsset.java @@ -19,16 +19,19 @@ public class YSampledDensityAsset extends DensityAsset { .add() .append(new KeyedCodec<>("SampleOffset", Codec.DOUBLE, true), (asset, value) -> asset.sampleOffset = value, asset -> asset.sampleOffset) .add() + .append(new KeyedCodec<>("Interpolate", Codec.BOOLEAN, true), (asset, value) -> asset.interpolate = value, asset -> asset.interpolate) + .add() .build(); private double sampleDistance = 4.0; private double sampleOffset = 0.0; + private boolean interpolate = true; @Nonnull @Override public Density build(@Nonnull DensityAsset.Argument argument) { return (Density)(this.sampleDistance <= 0.0 ? new ConstantValueDensity(0.0) - : new YSampledDensity(this.buildFirstInput(argument), this.sampleDistance, this.sampleOffset)); + : new YSampledDensity(this.buildFirstInput(argument), this.sampleDistance, this.sampleOffset, this.interpolate)); } @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/DistanceFunctionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/DistanceFunctionAsset.java index f17d8cc1..020d7a42 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/DistanceFunctionAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/DistanceFunctionAsset.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.assetstore.codec.ContainedAssetCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions.DistanceFunction; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/EuclideanDistanceFunctionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/EuclideanDistanceFunctionAsset.java index e41e59de..801d7e50 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/EuclideanDistanceFunctionAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/EuclideanDistanceFunctionAsset.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.dist import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions.DistanceFunction; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions.EuclideanDistanceFunction; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/ManhattanDistanceFunctionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/ManhattanDistanceFunctionAsset.java index 81d77357..ffacc6e9 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/ManhattanDistanceFunctionAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/distancefunctions/ManhattanDistanceFunctionAsset.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.dist import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions.DistanceFunction; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions.ManhattanDistanceFunction; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/CellValueReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/CellValueReturnTypeAsset.java index 4b5318df..05e2caed 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/CellValueReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/CellValueReturnTypeAsset.java @@ -8,8 +8,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.MultiCacheDensit import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.CellValueReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/CurveReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/CurveReturnTypeAsset.java index 36e13631..1d813539 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/CurveReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/CurveReturnTypeAsset.java @@ -5,8 +5,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.CurveAsset; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.CurveReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/DensityReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/DensityReturnTypeAsset.java index 528c4569..86eb4046 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/DensityReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/DensityReturnTypeAsset.java @@ -13,8 +13,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.MultiCacheDensit import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.DensityReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2AddReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2AddReturnTypeAsset.java index 857d568a..670d2f65 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2AddReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2AddReturnTypeAsset.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.retu import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.Distance2AddReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2DivReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2DivReturnTypeAsset.java index 1dfcc740..8dea2441 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2DivReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2DivReturnTypeAsset.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.retu import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.Distance2DivReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2MulReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2MulReturnTypeAsset.java index 57521d0b..7ede729a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2MulReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2MulReturnTypeAsset.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.retu import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.Distance2MulReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2ReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2ReturnTypeAsset.java index 7c150f35..619bd4e7 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2ReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2ReturnTypeAsset.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.retu import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.Distance2ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2SubReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2SubReturnTypeAsset.java index aae8a51c..d4abdb21 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2SubReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/Distance2SubReturnTypeAsset.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.retu import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.Distance2SubReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/DistanceReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/DistanceReturnTypeAsset.java index c0131633..b7980959 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/DistanceReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/DistanceReturnTypeAsset.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.retu import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.DistanceReturnType; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/ImportedReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/ImportedReturnTypeAsset.java index 078baf7d..639b02fa 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/ImportedReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/ImportedReturnTypeAsset.java @@ -3,15 +3,16 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.density.positions.retu import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import java.util.Objects; import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ImportedReturnTypeAsset extends ReturnTypeAsset { @Nonnull @@ -30,6 +31,10 @@ public class ImportedReturnTypeAsset extends ReturnTypeAsset { Logger.getLogger("Density") .warning("Couldn't find ReturnType asset exported with name: '" + this.importedAssetName + "'. Using a return type that only outputs 0 instead."); return new ReturnType() { + { + Objects.requireNonNull(ImportedReturnTypeAsset.this); + } + @Override public double get( double distance0, diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/ReturnTypeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/ReturnTypeAsset.java index ea56b30d..34b762fe 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/ReturnTypeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/density/positions/returntypes/ReturnTypeAsset.java @@ -8,8 +8,8 @@ import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/environmentproviders/EnvironmentProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/environmentproviders/EnvironmentProviderAsset.java index d303c2fd..89634392 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/environmentproviders/EnvironmentProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/environmentproviders/EnvironmentProviderAsset.java @@ -10,8 +10,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.environmentproviders.EnvironmentProvider; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/MaterialProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/MaterialProviderAsset.java index 8fa24e21..1864594c 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/MaterialProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/MaterialProviderAsset.java @@ -13,8 +13,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/WeightedMaterialProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/WeightedMaterialProviderAsset.java index b0447c6a..b130db1b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/WeightedMaterialProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/WeightedMaterialProviderAsset.java @@ -4,8 +4,8 @@ import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.WeightedMaterialProvider; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/spaceanddepth/layerassets/WeightedThicknessLayerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/spaceanddepth/layerassets/WeightedThicknessLayerAsset.java index c2be500b..40006e52 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/spaceanddepth/layerassets/WeightedThicknessLayerAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/materialproviders/spaceanddepth/layerassets/WeightedThicknessLayerAsset.java @@ -4,9 +4,9 @@ import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.ConstantMaterialProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.MaterialProviderAsset; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.spaceanddepth.SpaceAndDepthMaterialProvider; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.spaceanddepth.layers.WeightedThicknessLayer; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/CellNoiseAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/CellNoiseAsset.java index b805fc29..5d52429b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/CellNoiseAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/CellNoiseAsset.java @@ -1,12 +1,15 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.noisegenerators; -import com.hypixel.hytale.builtin.hytalegenerator.fields.FastNoiseLite; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.CellNoiseField; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.CellNoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.noise.FastNoiseLite; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; import com.hypixel.hytale.codec.validation.Validators; import java.util.HashSet; import java.util.Set; @@ -68,20 +71,26 @@ public class CellNoiseAsset extends NoiseAsset { (asset, cellType) -> asset.cellType = FastNoiseLite.CellularReturnType.valueOf(cellType), asset -> asset.cellType.name() ) - .addValidator((LegacyValidator)((v, r) -> { - try { - FastNoiseLite.CellularReturnType.valueOf(v); - } catch (IllegalArgumentException var6) { - String msg = "Invalid CellType: " + v + ". Valid choices: "; + .addValidator(new Validator() { + public void accept(String v, ValidationResults r) { + try { + FastNoiseLite.CellularReturnType.valueOf(v); + } catch (IllegalArgumentException var7) { + String msg = "Invalid CellType: " + v + ". Valid choices: "; - for (String t : validCellTypes) { - msg = msg + " "; - msg = msg + t; + for (String t : CellNoiseAsset.validCellTypes) { + msg = msg + " "; + msg = msg + t; + } + + r.fail(msg); } - - r.fail(msg); } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() .build(); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/NoiseAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/NoiseAsset.java index 81245460..0a548487 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/NoiseAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/NoiseAsset.java @@ -5,8 +5,8 @@ import com.hypixel.hytale.assetstore.codec.AssetCodecMapCodec; import com.hypixel.hytale.assetstore.codec.ContainedAssetCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.NoiseField; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.NoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/SimplexNoiseAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/SimplexNoiseAsset.java index 2fe1b7bd..0114c70d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/SimplexNoiseAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/noisegenerators/SimplexNoiseAsset.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.noisegenerators; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.SimplexNoiseField; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.SimplexNoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/AndPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/AndPatternAsset.java index 17bdc375..c0c1f70d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/AndPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/AndPatternAsset.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; import com.hypixel.hytale.builtin.hytalegenerator.patterns.AndPattern; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -22,7 +23,7 @@ public class AndPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { ArrayList patterns = new ArrayList<>(this.patternAssets.length); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/BlockSetPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/BlockSetPatternAsset.java index b0e74201..ea0796e1 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/BlockSetPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/BlockSetPatternAsset.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; import com.hypixel.hytale.builtin.hytalegenerator.MaterialSet; import com.hypixel.hytale.builtin.hytalegenerator.assets.blockset.MaterialSetAsset; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.MaterialSetPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.KeyedCodec; @@ -22,7 +23,7 @@ public class BlockSetPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { MaterialSet blockSet = this.materialSetAsset.build(argument.materialCache); return new MaterialSetPattern(blockSet); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/CeilingPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/CeilingPatternAsset.java index 44d2cc87..51c043be 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/CeilingPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/CeilingPatternAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.SurfacePattern; import com.hypixel.hytale.codec.KeyedCodec; @@ -23,7 +24,7 @@ public class CeilingPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { Pattern ceilingPattern = this.ceiling.build(argument); Pattern originPattern = this.origin.build(argument); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/ConstantPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/ConstantPatternAsset.java index 841382ed..0b162f77 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/ConstantPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/ConstantPatternAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -15,22 +15,15 @@ public class ConstantPatternAsset extends PatternAsset { .append(new KeyedCodec<>("Value", Codec.BOOLEAN, true), (asset, value) -> asset.value = value, value -> value.value) .add() .build(); - private boolean value; + private boolean value = false; @Nonnull @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { - return super.isSkipped() ? Pattern.noPattern() : new Pattern() { - @Override - public boolean matches(@Nonnull Pattern.Context context) { - return ConstantPatternAsset.this.value; - } - - @Nonnull - @Override - public SpaceSize readSpace() { - return SpaceSize.empty(); - } - }; + if (super.isSkipped()) { + return ConstantPattern.INSTANCE_FALSE; + } else { + return this.value ? ConstantPattern.INSTANCE_TRUE : ConstantPattern.INSTANCE_FALSE; + } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/CuboidPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/CuboidPatternAsset.java index e89c5530..e77ceb6f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/CuboidPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/CuboidPatternAsset.java @@ -1,11 +1,13 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.CuboidPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class CuboidPatternAsset extends PatternAsset { @Nonnull @@ -14,9 +16,9 @@ public class CuboidPatternAsset extends PatternAsset { ) .append(new KeyedCodec<>("SubPattern", PatternAsset.CODEC, true), (t, k) -> t.subPatternAsset = k, k -> k.subPatternAsset) .add() - .append(new KeyedCodec<>("Min", Vector3i.CODEC, true), (t, k) -> t.min = k, k -> k.min) + .append(new KeyedCodec<>("Min", Vector3iUtil.CODEC, true), (t, k) -> t.min = k, k -> k.min) .add() - .append(new KeyedCodec<>("Max", Vector3i.CODEC, true), (t, k) -> t.max = k, k -> k.max) + .append(new KeyedCodec<>("Max", Vector3iUtil.CODEC, true), (t, k) -> t.max = k, k -> k.max) .add() .build(); private PatternAsset subPatternAsset = new ConstantPatternAsset(); @@ -27,7 +29,7 @@ public class CuboidPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { Pattern subPattern = this.subPatternAsset.build(argument); return new CuboidPattern(subPattern, this.min, this.max); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/DensityPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/DensityPatternAsset.java index 9c4a6bab..4df84d7c 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/DensityPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/DensityPatternAsset.java @@ -7,6 +7,7 @@ import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.ConstantDensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.FieldFunctionPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.Codec; @@ -36,7 +37,7 @@ public class DensityPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { Density field = this.densityAsset.build(DensityAsset.from(argument)); FieldFunctionPattern out = new FieldFunctionPattern(field); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/FloorPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/FloorPatternAsset.java index d7493063..c91217a9 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/FloorPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/FloorPatternAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.SurfacePattern; import com.hypixel.hytale.codec.KeyedCodec; @@ -23,7 +24,7 @@ public class FloorPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { Pattern floorPattern = this.floor.build(argument); Pattern originPattern = this.origin.build(argument); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/GapPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/GapPatternAsset.java deleted file mode 100644 index d5869238..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/GapPatternAsset.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; - -import com.hypixel.hytale.builtin.hytalegenerator.patterns.GapPattern; -import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.codecs.array.ArrayCodec; -import com.hypixel.hytale.codec.validation.Validators; -import java.util.ArrayList; -import javax.annotation.Nonnull; - -public class GapPatternAsset extends PatternAsset { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(GapPatternAsset.class, GapPatternAsset::new, PatternAsset.ABSTRACT_CODEC) - .append(new KeyedCodec<>("GapPattern", PatternAsset.CODEC, true), (t, k) -> t.gapPatternAsset = k, k -> k.gapPatternAsset) - .add() - .append(new KeyedCodec<>("AnchorPattern", PatternAsset.CODEC, true), (t, k) -> t.anchorPatternAsset = k, k -> k.anchorPatternAsset) - .add() - .append(new KeyedCodec<>("GapSize", Codec.DOUBLE, true), (t, k) -> t.gapSize = k, k -> k.gapSize) - .addValidator(Validators.greaterThanOrEqual(0.0)) - .add() - .append(new KeyedCodec<>("AnchorSize", Codec.DOUBLE, true), (t, k) -> t.anchorSize = k, k -> k.anchorSize) - .addValidator(Validators.greaterThanOrEqual(0.0)) - .add() - .append(new KeyedCodec<>("AnchorRoughness", Codec.DOUBLE, true), (t, k) -> t.anchorRoughness = k, k -> k.anchorRoughness) - .addValidator(Validators.greaterThanOrEqual(0.0)) - .add() - .append(new KeyedCodec<>("DepthDown", Codec.INTEGER, true), (t, k) -> t.depthDown = k, k -> k.depthDown) - .addValidator(Validators.greaterThanOrEqual(0)) - .add() - .append(new KeyedCodec<>("DepthUp", Codec.INTEGER, true), (t, k) -> t.depthUp = k, k -> k.depthUp) - .addValidator(Validators.greaterThanOrEqual(0)) - .add() - .append(new KeyedCodec<>("Angles", new ArrayCodec<>(Codec.FLOAT, Float[]::new), true), (t, k) -> t.angles = k, k -> k.angles) - .add() - .build(); - private PatternAsset gapPatternAsset = new ConstantPatternAsset(); - private PatternAsset anchorPatternAsset = new ConstantPatternAsset(); - private double gapSize = 0.0; - private double anchorSize = 0.0; - private double anchorRoughness = 0.0; - private int depthDown = 0; - private int depthUp = 0; - private Float[] angles = new Float[0]; - - @Nonnull - @Override - public Pattern build(@Nonnull PatternAsset.Argument argument) { - if (super.isSkipped()) { - return Pattern.noPattern(); - } else { - Pattern gapPattern = this.gapPatternAsset.build(argument); - Pattern wallPattern = this.anchorPatternAsset.build(argument); - ArrayList angleList = new ArrayList<>(); - - for (Float a : this.angles) { - if (a != null && !Float.isNaN(a)) { - a = a * 180.0F; - angleList.add(a); - } - } - - return new GapPattern(angleList, this.gapSize, this.anchorSize, this.anchorRoughness, this.depthDown, this.depthUp, gapPattern, wallPattern); - } - } - - @Override - public void cleanUp() { - this.gapPatternAsset.cleanUp(); - this.anchorPatternAsset.cleanUp(); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/ImportedPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/ImportedPatternAsset.java index 2badbd1c..e9dcca89 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/ImportedPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/ImportedPatternAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -20,13 +21,13 @@ public class ImportedPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else if (this.name != null && !this.name.isEmpty()) { PatternAsset exportedAsset = PatternAsset.getExportedAsset(this.name); - return exportedAsset == null ? Pattern.noPattern() : exportedAsset.build(argument); + return (Pattern)(exportedAsset == null ? ConstantPattern.INSTANCE_FALSE : exportedAsset.build(argument)); } else { HytaleLogger.getLogger().atWarning().log("An exported Pattern with the name does not exist: " + this.name); - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/MaterialPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/MaterialPatternAsset.java index 8913676a..2c30cf7b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/MaterialPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/MaterialPatternAsset.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; import com.hypixel.hytale.builtin.hytalegenerator.assets.material.MaterialAsset; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.MaterialPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.KeyedCodec; @@ -22,7 +23,7 @@ public class MaterialPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { Material material = this.materialAsset.build(argument.materialCache); return new MaterialPattern(material); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/NotPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/NotPatternAsset.java index 309554e7..375c86f0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/NotPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/NotPatternAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.NotPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.KeyedCodec; @@ -17,7 +18,7 @@ public class NotPatternAsset extends PatternAsset { @Nonnull @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { - return (Pattern)(super.isSkipped() ? Pattern.noPattern() : new NotPattern(this.patternAsset.build(argument))); + return (Pattern)(super.isSkipped() ? ConstantPattern.INSTANCE_FALSE : new NotPattern(this.patternAsset.build(argument))); } @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/OffsetPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/OffsetPatternAsset.java index 4ea5bd07..3c6beb4a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/OffsetPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/OffsetPatternAsset.java @@ -1,11 +1,13 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.OffsetPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class OffsetPatternAsset extends PatternAsset { @Nonnull @@ -14,7 +16,7 @@ public class OffsetPatternAsset extends PatternAsset { ) .append(new KeyedCodec<>("Pattern", PatternAsset.CODEC, true), (t, k) -> t.patternAsset = k, k -> k.patternAsset) .add() - .append(new KeyedCodec<>("Offset", Vector3i.CODEC, true), (t, k) -> t.offset = k, k -> k.offset) + .append(new KeyedCodec<>("Offset", Vector3iUtil.CODEC, true), (t, k) -> t.offset = k, k -> k.offset) .add() .build(); private PatternAsset patternAsset = new ConstantPatternAsset(); @@ -24,10 +26,10 @@ public class OffsetPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { Pattern pattern = this.patternAsset.build(argument); - return new OffsetPattern(pattern, this.offset.clone()); + return new OffsetPattern(pattern, new Vector3i(this.offset)); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/OrPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/OrPatternAsset.java index fc2279ee..2b4ab37d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/OrPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/OrPatternAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.OrPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.codec.KeyedCodec; @@ -22,7 +23,7 @@ public class OrPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { ArrayList patterns = new ArrayList<>(this.patternAssets.length); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/PatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/PatternAsset.java index 62cfc4bb..ee532570 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/PatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/PatternAsset.java @@ -12,8 +12,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.direct import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/RotatorPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/RotatorPatternAsset.java new file mode 100644 index 00000000..69feab8c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/RotatorPatternAsset.java @@ -0,0 +1,38 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.material.OrthogonalRotationAsset; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.RotatorPattern; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class RotatorPatternAsset extends PatternAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + RotatorPatternAsset.class, RotatorPatternAsset::new, PatternAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Pattern", PatternAsset.CODEC, true), (asset, value) -> asset.patternAsset = value, asset -> asset.patternAsset) + .add() + .append(new KeyedCodec<>("Rotation", OrthogonalRotationAsset.CODEC, true), (asset, value) -> asset.rotationAsset = value, asset -> asset.rotationAsset) + .add() + .build(); + @Nonnull + private PatternAsset patternAsset = new ConstantPatternAsset(); + @Nonnull + private OrthogonalRotationAsset rotationAsset = new OrthogonalRotationAsset(); + + @Nonnull + @Override + public Pattern build(@Nonnull PatternAsset.Argument argument) { + return (Pattern)(super.isSkipped() + ? ConstantPattern.INSTANCE_FALSE + : new RotatorPattern(this.patternAsset.build(argument), this.rotationAsset.build(), argument.materialCache)); + } + + @Override + public void cleanUp() { + this.patternAsset.cleanUp(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/SurfacePatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/SurfacePatternAsset.java index e324b3f5..f88a31aa 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/SurfacePatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/SurfacePatternAsset.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; import com.hypixel.hytale.builtin.hytalegenerator.patterns.AndPattern; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.OrPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.SurfacePattern; @@ -55,7 +56,7 @@ public class SurfacePatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { Pattern floorPattern = this.surface.build(argument); Pattern originPattern = this.origin.build(argument); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/WallPatternAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/WallPatternAsset.java index 57bbb4e2..eddc2713 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/WallPatternAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/patterns/WallPatternAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.WallPattern; import com.hypixel.hytale.codec.Codec; @@ -34,7 +35,7 @@ public class WallPatternAsset extends PatternAsset { @Override public Pattern build(@Nonnull PatternAsset.Argument argument) { if (super.isSkipped()) { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } else { Pattern wallPattern = this.wall.build(argument); Pattern originPattern = this.origin.build(argument); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/MeshPointGeneratorAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/MeshPointGeneratorAsset.java index 7925ec40..a33a34a4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/MeshPointGeneratorAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/MeshPointGeneratorAsset.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.pointgenerators; -import com.hypixel.hytale.builtin.hytalegenerator.fields.points.JitterPointField; -import com.hypixel.hytale.builtin.hytalegenerator.fields.points.PointProvider; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider.JitterPointField; +import com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider.PointProvider; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/NoPointGeneratorAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/NoPointGeneratorAsset.java index b6cbce71..9d0ea1a4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/NoPointGeneratorAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/NoPointGeneratorAsset.java @@ -1,15 +1,16 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.pointgenerators; -import com.hypixel.hytale.builtin.hytalegenerator.fields.points.PointProvider; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider.PointProvider; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector2i; +import org.joml.Vector3d; +import org.joml.Vector3i; public class NoPointGeneratorAsset extends PointGeneratorAsset { @Nonnull @@ -22,6 +23,10 @@ public class NoPointGeneratorAsset extends PointGeneratorAsset { @Override public PointProvider build(@Nonnull SeedBox parentSeed) { return new PointProvider() { + { + Objects.requireNonNull(NoPointGeneratorAsset.this); + } + @Nonnull @Override public List points3i(@Nonnull Vector3i min, @Nonnull Vector3i max) { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/PointGeneratorAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/PointGeneratorAsset.java index 13913a4c..7c5a74e9 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/PointGeneratorAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/pointgenerators/PointGeneratorAsset.java @@ -6,8 +6,8 @@ import com.hypixel.hytale.assetstore.codec.ContainedAssetCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; -import com.hypixel.hytale.builtin.hytalegenerator.fields.points.PointProvider; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider.PointProvider; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/AnchorPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/AnchorPositionProviderAsset.java index bf470acc..36868b4b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/AnchorPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/AnchorPositionProviderAsset.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.AnchorPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -24,11 +25,11 @@ public class AnchorPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { - PositionProvider positionProvider = this.positionProviderAsset == null - ? PositionProvider.noPositionProvider() - : this.positionProviderAsset.build(argument); + PositionProvider positionProvider = (PositionProvider)(this.positionProviderAsset == null + ? EmptyPositionProvider.INSTANCE + : this.positionProviderAsset.build(argument)); return new AnchorPositionProvider(positionProvider, this.isReversed); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/BaseHeightPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/BaseHeightPositionProviderAsset.java index 8a3c6e56..ea79ce6e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/BaseHeightPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/BaseHeightPositionProviderAsset.java @@ -1,12 +1,14 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.assets.framework.DecimalConstantsFrameworkAsset; -import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.BaseHeightPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.OffsetPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BaseHeightPositionProviderAsset extends PositionProviderAsset { @Nonnull @@ -33,7 +35,7 @@ public class BaseHeightPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { PositionProvider positionProvider = this.positionProviderAsset.build(argument); Double baseHeight = DecimalConstantsFrameworkAsset.Entries.get(this.baseHeightName, argument.referenceBundle); @@ -41,7 +43,7 @@ public class BaseHeightPositionProviderAsset extends PositionProviderAsset { baseHeight = 0.0; } - return new BaseHeightPositionProvider(baseHeight, positionProvider, this.minYRead, this.maxYRead); + return new OffsetPositionProvider(new Vector3d(0.0, baseHeight, 0.0), positionProvider); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/BoundPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/BoundPositionProviderAsset.java index cca4f3db..28d47002 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/BoundPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/BoundPositionProviderAsset.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.assets.bounds.DecimalBounds3dAsset; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.BoundPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -28,7 +29,7 @@ public class BoundPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { return (PositionProvider)(super.skip() - ? PositionProvider.noPositionProvider() + ? EmptyPositionProvider.INSTANCE : new BoundPositionProvider(this.positionProviderAsset.build(argument), this.bounds.build())); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/CachedPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/CachedPositionProviderAsset.java index afd99f0e..0922ec8d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/CachedPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/CachedPositionProviderAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.cached.CachedPositionProvider; import com.hypixel.hytale.codec.Codec; @@ -30,10 +31,10 @@ public class CachedPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { PositionProvider childPositions = this.childAsset.build(argument); - return new CachedPositionProvider(childPositions, this.sectionSize, this.cacheSize, false); + return new CachedPositionProvider(childPositions, this.sectionSize, this.cacheSize); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ClustersPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ClustersPositionProviderAsset.java new file mode 100644 index 00000000..2f07171e --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ClustersPositionProviderAsset.java @@ -0,0 +1,64 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.bounds.DecimalBounds3dAsset; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.ClustersPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; +import org.joml.Vector3d; + +public class ClustersPositionProviderAsset extends PositionProviderAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + ClustersPositionProviderAsset.class, ClustersPositionProviderAsset::new, PositionProviderAsset.ABSTRACT_CODEC + ) + .append( + new KeyedCodec<>("Cluster", PositionProviderAsset.CODEC, true), + (asset, value) -> asset.clusterPositionProviderAsset = value, + asset -> asset.clusterPositionProviderAsset + ) + .add() + .append( + new KeyedCodec<>("Distributor", PositionProviderAsset.CODEC, true), + (asset, value) -> asset.distributorPositionProviderAsset = value, + asset -> asset.distributorPositionProviderAsset + ) + .add() + .append( + new KeyedCodec<>("ClusterBounds", DecimalBounds3dAsset.CODEC, true), (asset, v) -> asset.clusterBoundsAsset = v, asset -> asset.clusterBoundsAsset + ) + .add() + .build(); + @Nonnull + private PositionProviderAsset clusterPositionProviderAsset = new ListPositionProviderAsset(); + @Nonnull + private PositionProviderAsset distributorPositionProviderAsset = new ListPositionProviderAsset(); + @Nonnull + private DecimalBounds3dAsset clusterBoundsAsset = new DecimalBounds3dAsset(); + + @Nonnull + @Override + public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { + if (super.skip()) { + return EmptyPositionProvider.INSTANCE; + } else { + PositionProvider clusterPositionProvider = this.clusterPositionProviderAsset.build(argument); + PositionProvider distributorPositionProvider = this.distributorPositionProviderAsset.build(argument); + Bounds3d clusterBounds = this.clusterBoundsAsset.build(); + return new ClustersPositionProvider(clusterPositionProvider, distributorPositionProvider, clusterBounds); + } + } + + @Override + public void cleanUp() { + this.clusterPositionProviderAsset.cleanUp(); + this.distributorPositionProviderAsset.cleanUp(); + } + + private static boolean isValidScale(@Nonnull Vector3d vector) { + return vector.x != 0.0 && vector.y != 0.0 && vector.z != 0.0; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/EmptyPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/EmptyPositionProviderAsset.java new file mode 100644 index 00000000..6d02d367 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/EmptyPositionProviderAsset.java @@ -0,0 +1,14 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class EmptyPositionProviderAsset extends PositionProviderAsset { + public static final EmptyPositionProviderAsset INSTANCE = new EmptyPositionProviderAsset(); + + @Override + public PositionProvider build(@NonNullDecl PositionProviderAsset.Argument argument) { + return EmptyPositionProvider.INSTANCE; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FieldFunctionOccurrencePositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FieldFunctionOccurrencePositionProviderAsset.java index 013e70a4..736e8edb 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FieldFunctionOccurrencePositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FieldFunctionOccurrencePositionProviderAsset.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.ConstantDensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.FieldFunctionOccurrencePositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.Codec; @@ -32,7 +33,7 @@ public class FieldFunctionOccurrencePositionProviderAsset extends PositionProvid @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { Density functionTree = this.densityAsset.build(DensityAsset.from(argument)); PositionProvider positionProvider = this.positionProviderAsset.build(argument); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FieldFunctionPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FieldFunctionPositionProviderAsset.java index f0636e66..87df0979 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FieldFunctionPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FieldFunctionPositionProviderAsset.java @@ -7,6 +7,7 @@ import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.ConstantDensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.FieldFunctionPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.Codec; @@ -47,7 +48,7 @@ public class FieldFunctionPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { Density density = this.densityAsset.build(DensityAsset.from(argument)); PositionProvider positionProvider = this.positionProviderAsset.build(argument); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FrameworkPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FrameworkPositionProviderAsset.java index c1549ed4..78edaff0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FrameworkPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/FrameworkPositionProviderAsset.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.framework.PositionsFrameworkAsset; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -22,12 +23,12 @@ public class FrameworkPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { PositionProviderAsset baseAsset = PositionsFrameworkAsset.Entries.get(this.name, argument.referenceBundle); if (baseAsset == null) { LoggerUtil.getLogger().log(Level.WARNING, "Couldn't find WorldFramework Positions with name " + this.name); - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { return baseAsset.build(argument); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ImportedPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ImportedPositionProviderAsset.java index 8fcc12fd..acc09dc7 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ImportedPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ImportedPositionProviderAsset.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -20,12 +21,12 @@ public class ImportedPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { PositionProviderAsset asset = getExportedAsset(this.name); if (asset == null) { LoggerUtil.getLogger().warning("Couldn't find Positions asset exported with name: '" + this.name + "'."); - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { return asset.build(argument); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Jitter2dPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Jitter2dPositionProviderAsset.java new file mode 100644 index 00000000..35a89f46 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Jitter2dPositionProviderAsset.java @@ -0,0 +1,55 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.Jitter2dPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; +import org.joml.Vector3d; + +public class Jitter2dPositionProviderAsset extends PositionProviderAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + Jitter2dPositionProviderAsset.class, Jitter2dPositionProviderAsset::new, PositionProviderAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Magnitude", Codec.DOUBLE, true), (asset, value) -> asset.magnitude = value, asset -> asset.magnitude) + .addValidator(Validators.greaterThanOrEqual(0.0)) + .add() + .append(new KeyedCodec<>("Seed", Codec.STRING, true), (asset, value) -> asset.seed = value, asset -> asset.seed) + .add() + .append( + new KeyedCodec<>("Positions", PositionProviderAsset.CODEC, true), (asset, v) -> asset.positionProviderAsset = v, asset -> asset.positionProviderAsset + ) + .add() + .build(); + private double magnitude = 0.0; + @Nonnull + private String seed = ""; + @Nonnull + private PositionProviderAsset positionProviderAsset = new ListPositionProviderAsset(); + + @Nonnull + @Override + public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { + if (super.skip()) { + return EmptyPositionProvider.INSTANCE; + } else { + SeedBox seedBox = argument.parentSeed.child(this.seed); + PositionProvider positionProvider = this.positionProviderAsset.build(argument); + return new Jitter2dPositionProvider(this.magnitude, seedBox.createSupplier().get(), positionProvider); + } + } + + @Override + public void cleanUp() { + this.positionProviderAsset.cleanUp(); + } + + private static boolean isValidScale(@Nonnull Vector3d vector) { + return vector.x != 0.0 && vector.y != 0.0 && vector.z != 0.0; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Jitter3dPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Jitter3dPositionProviderAsset.java new file mode 100644 index 00000000..1c116f9c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Jitter3dPositionProviderAsset.java @@ -0,0 +1,55 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.Jitter3dPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; +import org.joml.Vector3d; + +public class Jitter3dPositionProviderAsset extends PositionProviderAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + Jitter3dPositionProviderAsset.class, Jitter3dPositionProviderAsset::new, PositionProviderAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Magnitude", Codec.DOUBLE, true), (asset, value) -> asset.magnitude = value, asset -> asset.magnitude) + .addValidator(Validators.greaterThanOrEqual(0.0)) + .add() + .append(new KeyedCodec<>("Seed", Codec.STRING, true), (asset, value) -> asset.seed = value, asset -> asset.seed) + .add() + .append( + new KeyedCodec<>("Positions", PositionProviderAsset.CODEC, true), (asset, v) -> asset.positionProviderAsset = v, asset -> asset.positionProviderAsset + ) + .add() + .build(); + private double magnitude = 0.0; + @Nonnull + private String seed = ""; + @Nonnull + private PositionProviderAsset positionProviderAsset = new ListPositionProviderAsset(); + + @Nonnull + @Override + public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { + if (super.skip()) { + return EmptyPositionProvider.INSTANCE; + } else { + SeedBox seedBox = argument.parentSeed.child(this.seed); + PositionProvider positionProvider = this.positionProviderAsset.build(argument); + return new Jitter3dPositionProvider(this.magnitude, seedBox.createSupplier().get(), positionProvider); + } + } + + @Override + public void cleanUp() { + this.positionProviderAsset.cleanUp(); + } + + private static boolean isValidScale(@Nonnull Vector3d vector) { + return vector.x != 0.0 && vector.y != 0.0 && vector.z != 0.0; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ListPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ListPositionProviderAsset.java index 611a9081..3da26ae4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ListPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ListPositionProviderAsset.java @@ -4,15 +4,16 @@ import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.ListPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.ArrayList; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ListPositionProviderAsset extends PositionProviderAsset { @Nonnull @@ -32,16 +33,16 @@ public class ListPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { - ArrayList list = new ArrayList<>(); + ArrayList list = new ArrayList<>(); for (ListPositionProviderAsset.PositionAsset asset : this.positions) { - Vector3i position = new Vector3i(asset.x, asset.y, asset.z); + Vector3d position = new Vector3d(asset.x, asset.y, asset.z); list.add(position); } - return ListPositionProvider.from3i(list); + return new ListPositionProvider(list); } } @@ -56,18 +57,18 @@ public class ListPositionProviderAsset extends PositionProviderAsset { (config, data) -> config.data = data, config -> config.data ) - .append(new KeyedCodec<>("X", Codec.INTEGER, true), (t, x) -> t.x = x, t -> t.x) + .append(new KeyedCodec<>("X", Codec.DOUBLE, true), (t, x) -> t.x = x, t -> t.x) .add() - .append(new KeyedCodec<>("Y", Codec.INTEGER, true), (t, y) -> t.y = y, t -> t.y) + .append(new KeyedCodec<>("Y", Codec.DOUBLE, true), (t, y) -> t.y = y, t -> t.y) .add() - .append(new KeyedCodec<>("Z", Codec.INTEGER, true), (t, z) -> t.z = z, t -> t.z) + .append(new KeyedCodec<>("Z", Codec.DOUBLE, true), (t, z) -> t.z = z, t -> t.z) .add() .build(); private String id; private AssetExtraInfo.Data data; - private int x; - private int y; - private int z; + private double x; + private double y; + private double z; public String getId() { return this.id; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Mesh2DPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Mesh2DPositionProviderAsset.java index 18cea4e5..94aeb345 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Mesh2DPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Mesh2DPositionProviderAsset.java @@ -2,8 +2,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.assets.pointgenerators.NoPointGeneratorAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.pointgenerators.PointGeneratorAsset; -import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.Mesh2DPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.deprecated.Mesh2DPositionProvider; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -28,7 +29,7 @@ public class Mesh2DPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { return (PositionProvider)(super.skip() - ? PositionProvider.noPositionProvider() + ? EmptyPositionProvider.INSTANCE : new Mesh2DPositionProvider(this.pointGeneratorAsset.build(argument.parentSeed), this.y)); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Mesh3DPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Mesh3DPositionProviderAsset.java index e8c5f30f..65abfd5e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Mesh3DPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/Mesh3DPositionProviderAsset.java @@ -2,8 +2,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.assets.pointgenerators.NoPointGeneratorAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.pointgenerators.PointGeneratorAsset; -import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.Mesh3DPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.deprecated.Mesh3DPositionProvider; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; @@ -23,8 +24,6 @@ public class Mesh3DPositionProviderAsset extends PositionProviderAsset { @Nonnull @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { - return (PositionProvider)(super.skip() - ? PositionProvider.noPositionProvider() - : new Mesh3DPositionProvider(this.pointGeneratorAsset.build(argument.parentSeed))); + return (PositionProvider)(super.skip() ? EmptyPositionProvider.INSTANCE : new Mesh3DPositionProvider(this.pointGeneratorAsset.build(argument.parentSeed))); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/OffsetPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/OffsetPositionProviderAsset.java index 0c75cc78..e73adaf8 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/OffsetPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/OffsetPositionProviderAsset.java @@ -1,42 +1,55 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.OffsetPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class OffsetPositionProviderAsset extends PositionProviderAsset { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( OffsetPositionProviderAsset.class, OffsetPositionProviderAsset::new, PositionProviderAsset.ABSTRACT_CODEC ) - .append(new KeyedCodec<>("OffsetX", Codec.INTEGER, true), (asset, v) -> asset.offsetX = v, asset -> asset.offsetX) - .add() - .append(new KeyedCodec<>("OffsetY", Codec.INTEGER, true), (asset, v) -> asset.offsetY = v, asset -> asset.offsetY) - .add() - .append(new KeyedCodec<>("OffsetZ", Codec.INTEGER, true), (asset, v) -> asset.offsetZ = v, asset -> asset.offsetZ) - .add() .append( - new KeyedCodec<>("Positions", PositionProviderAsset.CODEC, true), (asset, v) -> asset.positionProviderAsset = v, asset -> asset.positionProviderAsset + new KeyedCodec<>("Positions", PositionProviderAsset.CODEC, true), + (asset, value) -> asset.positionProviderAsset = value, + asset -> asset.positionProviderAsset ) .add() + .append(new KeyedCodec<>("Offset", Vector3dUtil.CODEC, true), (asset, value) -> asset.offset = value, asset -> asset.offset) + .add() + .append(new KeyedCodec<>("OffsetX", Codec.INTEGER, true), (asset, value) -> asset.offsetX = value, asset -> asset.offsetX) + .add() + .append(new KeyedCodec<>("OffsetY", Codec.INTEGER, true), (asset, value) -> asset.offsetY = value, asset -> asset.offsetY) + .add() + .append(new KeyedCodec<>("OffsetZ", Codec.INTEGER, true), (asset, value) -> asset.offsetZ = value, asset -> asset.offsetZ) + .add() .build(); - private int offsetX; - private int offsetY; - private int offsetZ; + private static final Vector3d DEFAULT_OFFSET = new Vector3d(); + @Nonnull + private Vector3d offset = DEFAULT_OFFSET; + @Nonnull private PositionProviderAsset positionProviderAsset = new ListPositionProviderAsset(); + private int offsetX = 0; + private int offsetY = 0; + private int offsetZ = 0; @Nonnull @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; + } else if (DEFAULT_OFFSET == this.offset) { + PositionProvider positionProvider = this.positionProviderAsset.build(argument); + return new OffsetPositionProvider(new Vector3d(this.offsetX, this.offsetY, this.offsetZ), positionProvider); } else { PositionProvider positionProvider = this.positionProviderAsset.build(argument); - return new OffsetPositionProvider(new Vector3i(this.offsetX, this.offsetY, this.offsetZ), positionProvider); + return new OffsetPositionProvider(this.offset, positionProvider); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/PositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/PositionProviderAsset.java index 248d1a88..93679765 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/PositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/PositionProviderAsset.java @@ -7,10 +7,11 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.PropDistributionAsset; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -89,5 +90,11 @@ public abstract class PositionProviderAsset implements Cleanable, JsonAssetWithM this.referenceBundle = argument.referenceBundle; this.workerId = argument.workerId; } + + public Argument(@Nonnull PropDistributionAsset.Argument argument) { + this.parentSeed = argument.parentSeed; + this.referenceBundle = argument.referenceBundle; + this.workerId = argument.workerId; + } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ScalerPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ScalerPositionProviderAsset.java new file mode 100644 index 00000000..9c9f7cec --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/ScalerPositionProviderAsset.java @@ -0,0 +1,64 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.ScalerPositionProvider; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import javax.annotation.Nonnull; +import org.joml.Vector3d; + +public class ScalerPositionProviderAsset extends PositionProviderAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + ScalerPositionProviderAsset.class, ScalerPositionProviderAsset::new, PositionProviderAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Scale", Vector3dUtil.CODEC, true), (asset, v) -> asset.scale = v, asset -> asset.scale) + .addValidator(new Validator() { + public void accept(Vector3d vector, ValidationResults results) { + if (!ScalerPositionProviderAsset.isValidScale(vector)) { + String msg = "Scale Vector " + vector.toString() + " has one or more zero members."; + results.fail(msg); + } + } + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) + .add() + .append( + new KeyedCodec<>("Positions", PositionProviderAsset.CODEC, true), (asset, v) -> asset.positionProviderAsset = v, asset -> asset.positionProviderAsset + ) + .add() + .build(); + @Nonnull + private Vector3d scale = new Vector3d(); + @Nonnull + private PositionProviderAsset positionProviderAsset = new ListPositionProviderAsset(); + + @Nonnull + @Override + public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { + if (!super.skip() && isValidScale(this.scale)) { + PositionProvider positionProvider = this.positionProviderAsset.build(argument); + return new ScalerPositionProvider(this.scale, positionProvider); + } else { + return EmptyPositionProvider.INSTANCE; + } + } + + @Override + public void cleanUp() { + this.positionProviderAsset.cleanUp(); + } + + private static boolean isValidScale(@Nonnull Vector3d vector) { + return vector.x != 0.0 && vector.y != 0.0 && vector.z != 0.0; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SimpleHorizontalPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SimpleHorizontalPositionProviderAsset.java index 70fee1b2..0aaf1120 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SimpleHorizontalPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SimpleHorizontalPositionProviderAsset.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.assets.delimiters.RangeDoubleAsset; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.SimpleHorizontalPositionProvider; import com.hypixel.hytale.codec.KeyedCodec; @@ -26,7 +27,7 @@ public class SimpleHorizontalPositionProviderAsset extends PositionProviderAsset @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { PositionProvider positionProvider = this.positionProviderAsset.build(argument); return new SimpleHorizontalPositionProvider(this.rangeYAsset.build(), positionProvider); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SquareGrid2dPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SquareGrid2dPositionProviderAsset.java new file mode 100644 index 00000000..812d06d1 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SquareGrid2dPositionProviderAsset.java @@ -0,0 +1,21 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.SquareGrid2dPositionProvider; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class SquareGrid2dPositionProviderAsset extends PositionProviderAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + SquareGrid2dPositionProviderAsset.class, SquareGrid2dPositionProviderAsset::new, PositionProviderAsset.ABSTRACT_CODEC + ) + .build(); + + @Nonnull + @Override + public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { + return (PositionProvider)(super.skip() ? EmptyPositionProvider.INSTANCE : new SquareGrid2dPositionProvider()); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SquareGrid3dPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SquareGrid3dPositionProviderAsset.java new file mode 100644 index 00000000..42372af5 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/SquareGrid3dPositionProviderAsset.java @@ -0,0 +1,21 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.SquareGrid3dPositionProvider; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class SquareGrid3dPositionProviderAsset extends PositionProviderAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + SquareGrid3dPositionProviderAsset.class, SquareGrid3dPositionProviderAsset::new, PositionProviderAsset.ABSTRACT_CODEC + ) + .build(); + + @Nonnull + @Override + public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { + return (PositionProvider)(super.skip() ? EmptyPositionProvider.INSTANCE : new SquareGrid3dPositionProvider()); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/TriangularGrid2dPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/TriangularGrid2dPositionProviderAsset.java new file mode 100644 index 00000000..5d409cc0 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/TriangularGrid2dPositionProviderAsset.java @@ -0,0 +1,21 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.TriangularGrid2dPositionProvider; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class TriangularGrid2dPositionProviderAsset extends PositionProviderAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + TriangularGrid2dPositionProviderAsset.class, TriangularGrid2dPositionProviderAsset::new, PositionProviderAsset.ABSTRACT_CODEC + ) + .build(); + + @Nonnull + @Override + public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { + return (PositionProvider)(super.skip() ? EmptyPositionProvider.INSTANCE : new TriangularGrid2dPositionProvider()); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/UnionPositionProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/UnionPositionProviderAsset.java index 24c4d820..05e31411 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/UnionPositionProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/positionproviders/UnionPositionProviderAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.UnionPositionProvider; import com.hypixel.hytale.codec.KeyedCodec; @@ -26,7 +27,7 @@ public class UnionPositionProviderAsset extends PositionProviderAsset { @Override public PositionProvider build(@Nonnull PositionProviderAsset.Argument argument) { if (super.skip()) { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } else { ArrayList list = new ArrayList<>(); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/AssignedPropDistributionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/AssignedPropDistributionAsset.java new file mode 100644 index 00000000..fbfc16bb --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/AssignedPropDistributionAsset.java @@ -0,0 +1,47 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.AssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.ConstantAssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.Assignments; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.AssignedPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.NoPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class AssignedPropDistributionAsset extends PropDistributionAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + AssignedPropDistributionAsset.class, AssignedPropDistributionAsset::new, PropDistributionAsset.ABSTRACT_CODEC + ) + .append( + new KeyedCodec<>("PropDistribution", PropDistributionAsset.CODEC, true), + (asset, value) -> asset.propDistributionAsset = value, + asset -> asset.propDistributionAsset + ) + .add() + .append(new KeyedCodec<>("Assignments", AssignmentsAsset.CODEC, true), (asset, value) -> asset.assignmentsAsset = value, asset -> asset.assignmentsAsset) + .add() + .append(new KeyedCodec<>("OverrideAllProps", Codec.BOOLEAN, true), (asset, value) -> asset.isOverrideAllProps = value, asset -> asset.isOverrideAllProps) + .add() + .build(); + @Nonnull + private PropDistributionAsset propDistributionAsset = NoPropDistributionAsset.INSTANCE; + @Nonnull + private AssignmentsAsset assignmentsAsset = new ConstantAssignmentsAsset(); + private boolean isOverrideAllProps = false; + + @Nonnull + @Override + public PropDistribution build(@Nonnull PropDistributionAsset.Argument argument) { + if (super.isSkipped()) { + return NoPropDistribution.INSTANCE; + } else { + PropDistribution propDistribution = this.propDistributionAsset.build(argument); + Assignments assignments = this.assignmentsAsset.build(new AssignmentsAsset.Argument(argument)); + return new AssignedPropDistribution(propDistribution, assignments, this.isOverrideAllProps); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/ConstantPropDistributionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/ConstantPropDistributionAsset.java new file mode 100644 index 00000000..12e7a15c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/ConstantPropDistributionAsset.java @@ -0,0 +1,40 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.ListPositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.PositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.EmptyPropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.props.PropAsset; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.ConstantPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.NoPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class ConstantPropDistributionAsset extends PropDistributionAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + ConstantPropDistributionAsset.class, ConstantPropDistributionAsset::new, PropDistributionAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Positions", PositionProviderAsset.CODEC, true), (t, k) -> t.positionProviderAsset = k, k -> k.positionProviderAsset) + .add() + .append(new KeyedCodec<>("Prop", PropAsset.CODEC, true), (t, k) -> t.propAsset = k, k -> k.propAsset) + .add() + .build(); + private PositionProviderAsset positionProviderAsset = new ListPositionProviderAsset(); + private PropAsset propAsset = new EmptyPropAsset(); + + @Nonnull + @Override + public PropDistribution build(@Nonnull PropDistributionAsset.Argument argument) { + if (super.isSkipped()) { + return NoPropDistribution.INSTANCE; + } else { + PositionProvider positionProvider = this.positionProviderAsset.build(new PositionProviderAsset.Argument(argument)); + Prop prop = this.propAsset.build(new PropAsset.Argument(argument)); + return new ConstantPropDistribution(positionProvider, prop); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/ImportedPropDistributionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/ImportedPropDistributionAsset.java new file mode 100644 index 00000000..f8eb9010 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/ImportedPropDistributionAsset.java @@ -0,0 +1,36 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution; + +import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.NoPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class ImportedPropDistributionAsset extends PropDistributionAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + ImportedPropDistributionAsset.class, ImportedPropDistributionAsset::new, PropDistributionAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Name", Codec.STRING, true), (asset, v) -> asset.name = v, asset -> asset.name) + .add() + .build(); + private String name = ""; + + @Nonnull + @Override + public PropDistribution build(@Nonnull PropDistributionAsset.Argument argument) { + if (super.isSkipped()) { + return NoPropDistribution.INSTANCE; + } else { + PropDistributionAsset asset = getExportedAsset(this.name); + if (asset == null) { + LoggerUtil.getLogger().warning("Couldn't find Positions asset exported with name: '" + this.name + "'."); + return NoPropDistribution.INSTANCE; + } else { + return asset.build(argument); + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/NoPropDistributionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/NoPropDistributionAsset.java new file mode 100644 index 00000000..caaf4cab --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/NoPropDistributionAsset.java @@ -0,0 +1,18 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution; + +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.NoPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import javax.annotation.Nonnull; + +public class NoPropDistributionAsset extends PropDistributionAsset { + public static final NoPropDistributionAsset INSTANCE = new NoPropDistributionAsset(); + + private NoPropDistributionAsset() { + } + + @Nonnull + @Override + public PropDistribution build(@Nonnull PropDistributionAsset.Argument argument) { + return NoPropDistribution.INSTANCE; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/PositionsPropDistributionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/PositionsPropDistributionAsset.java new file mode 100644 index 00000000..84620ece --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/PositionsPropDistributionAsset.java @@ -0,0 +1,43 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.EmptyPositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.PositionProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.NoPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PositionsPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class PositionsPropDistributionAsset extends PropDistributionAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + PositionsPropDistributionAsset.class, PositionsPropDistributionAsset::new, PropDistributionAsset.ABSTRACT_CODEC + ) + .append( + new KeyedCodec<>("Positions", PositionProviderAsset.CODEC, true), + (asset, value) -> asset.positionProviderAsset = value, + asset -> asset.positionProviderAsset + ) + .add() + .build(); + @Nonnull + private PositionProviderAsset positionProviderAsset = EmptyPositionProviderAsset.INSTANCE; + + @Nonnull + @Override + public PropDistribution build(@Nonnull PropDistributionAsset.Argument argument) { + if (super.isSkipped()) { + return NoPropDistribution.INSTANCE; + } else { + PositionProvider positionProvider = this.positionProviderAsset.build(new PositionProviderAsset.Argument(argument)); + return new PositionsPropDistribution(positionProvider); + } + } + + @Override + public void cleanUp() { + this.positionProviderAsset.cleanUp(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/PropDistributionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/PropDistributionAsset.java new file mode 100644 index 00000000..8b0648ca --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/PropDistributionAsset.java @@ -0,0 +1,104 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution; + +import com.hypixel.hytale.assetstore.AssetExtraInfo; +import com.hypixel.hytale.assetstore.codec.AssetCodecMapCodec; +import com.hypixel.hytale.assetstore.codec.ContainedAssetCodec; +import com.hypixel.hytale.assetstore.map.DefaultAssetMap; +import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; +import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; +import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nonnull; + +public abstract class PropDistributionAsset implements Cleanable, JsonAssetWithMap> { + @Nonnull + public static final AssetCodecMapCodec CODEC = new AssetCodecMapCodec<>( + Codec.STRING, (t, k) -> t.id = k, t -> t.id, (t, data) -> t.data = data, t -> t.data + ); + @Nonnull + private static final Map exportedNodes = new ConcurrentHashMap<>(); + @Nonnull + public static final Codec CHILD_ASSET_CODEC = new ContainedAssetCodec<>(PropDistributionAsset.class, CODEC); + @Nonnull + public static final Codec CHILD_ASSET_CODEC_ARRAY = new ArrayCodec<>(CHILD_ASSET_CODEC, String[]::new); + @Nonnull + public static final BuilderCodec ABSTRACT_CODEC = BuilderCodec.abstractBuilder(PropDistributionAsset.class) + .append(new KeyedCodec<>("Skip", Codec.BOOLEAN, false), (t, k) -> t.skip = k, t -> t.skip) + .add() + .append(new KeyedCodec<>("ExportAs", Codec.STRING, false), (t, k) -> t.exportName = k, t -> t.exportName) + .add() + .afterDecode(asset -> { + if (asset.exportName != null && !asset.exportName.isEmpty()) { + if (exportedNodes.containsKey(asset.exportName)) { + LoggerUtil.getLogger().warning("Duplicate export name for asset: " + asset.exportName); + } + + exportedNodes.put(asset.exportName, asset); + LoggerUtil.getLogger().fine("Registered imported node asset with name '" + asset.exportName + "' with asset id '" + asset.id); + } + }) + .build(); + private String id; + private AssetExtraInfo.Data data; + private boolean skip = false; + private String exportName = ""; + + protected PropDistributionAsset() { + } + + public abstract PropDistribution build(@Nonnull PropDistributionAsset.Argument var1); + + @Nonnull + public static PropDistributionAsset getFallbackAsset() { + return new ConstantPropDistributionAsset(); + } + + public boolean isSkipped() { + return this.skip; + } + + public static PropDistributionAsset getExportedAsset(@Nonnull String name) { + return exportedNodes.get(name); + } + + public String getId() { + return this.id; + } + + @Override + public void cleanUp() { + } + + public static class Argument { + public SeedBox parentSeed; + public MaterialCache materialCache; + public ReferenceBundle referenceBundle; + public WorkerIndexer.Id workerId; + + public Argument( + @Nonnull SeedBox parentSeed, @Nonnull MaterialCache materialCache, @Nonnull ReferenceBundle referenceBundle, @Nonnull WorkerIndexer.Id workerId + ) { + this.parentSeed = parentSeed; + this.materialCache = materialCache; + this.referenceBundle = referenceBundle; + this.workerId = workerId; + } + + public Argument(@Nonnull PropDistributionAsset.Argument argument) { + this.parentSeed = argument.parentSeed; + this.materialCache = argument.materialCache; + this.referenceBundle = argument.referenceBundle; + this.workerId = argument.workerId; + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/UnionPropDistributionAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/UnionPropDistributionAsset.java new file mode 100644 index 00000000..d84ec854 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propdistribution/UnionPropDistributionAsset.java @@ -0,0 +1,43 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution; + +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.NoPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.UnionPropDistribution; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; + +public class UnionPropDistributionAsset extends PropDistributionAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + UnionPropDistributionAsset.class, UnionPropDistributionAsset::new, PropDistributionAsset.ABSTRACT_CODEC + ) + .append( + new KeyedCodec<>("PropDistributions", new ArrayCodec<>(PropDistributionAsset.CODEC, PropDistributionAsset[]::new), true), + (asset, value) -> asset.propDistributionAssets = value, + asset -> asset.propDistributionAssets + ) + .add() + .build(); + @Nonnull + private PropDistributionAsset[] propDistributionAssets = new PropDistributionAsset[0]; + + @Nonnull + @Override + public PropDistribution build(@Nonnull PropDistributionAsset.Argument argument) { + if (super.isSkipped()) { + return NoPropDistribution.INSTANCE; + } else { + List propDistributions = new ArrayList<>(this.propDistributionAssets.length); + + for (PropDistributionAsset asset : this.propDistributionAssets) { + propDistributions.add(asset.build(argument)); + } + + return new UnionPropDistribution(propDistributions); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propstageiterations/PropRuntimeAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propruntime/PropRuntimeAsset.java similarity index 56% rename from src/com/hypixel/hytale/builtin/hytalegenerator/assets/propstageiterations/PropRuntimeAsset.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assets/propruntime/PropRuntimeAsset.java index 471bee58..01b4d8f4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propstageiterations/PropRuntimeAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/propruntime/PropRuntimeAsset.java @@ -1,20 +1,25 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.propstageiterations; +package com.hypixel.hytale.builtin.hytalegenerator.assets.propruntime; import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.AssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.assignments.ConstantAssignmentsAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.ListPositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.PositionProviderAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.AssignmentsAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.propassignments.ConstantAssignmentsAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.NoPropDistributionAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.PropDistributionAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assignments.Assignments; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.AssignedPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PositionsPropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import javax.annotation.Nonnull; @@ -36,6 +41,8 @@ public class PropRuntimeAsset implements Cleanable, JsonAssetWithMap("Assignments", AssignmentsAsset.CODEC, true), (t, k) -> t.assignmentsAsset = k, t -> t.assignmentsAsset) .add() + .append(new KeyedCodec<>("PropDistribution", PropDistributionAsset.CODEC, true), (t, k) -> t.propDistributionAsset = k, t -> t.propDistributionAsset) + .add() .append(new KeyedCodec<>("Skip", Codec.BOOLEAN, false), (t, k) -> t.skip = k, t -> t.skip) .add() .build(); @@ -45,6 +52,7 @@ public class PropRuntimeAsset implements Cleanable, JsonAssetWithMap CODEC = BuilderCodec.builder(BoxPropAsset.class, BoxPropAsset::new, PropAsset.ABSTRACT_CODEC) - .append(new KeyedCodec<>("Range", Vector3i.CODEC, true), (asset, v) -> asset.range = v, asset -> asset.range) + .append(new KeyedCodec<>("Range", Vector3iUtil.CODEC, true), (asset, v) -> asset.range = v, asset -> asset.range) .add() .append(new KeyedCodec<>("Material", MaterialAsset.CODEC, true), (asset, value) -> asset.materialAsset = value, asset -> asset.materialAsset) .add() @@ -28,20 +30,20 @@ public class BoxPropAsset extends PropAsset { private Vector3i range = new Vector3i(); private MaterialAsset materialAsset = new MaterialAsset(); private PatternAsset patternAsset = new ConstantPatternAsset(); - private ScannerAsset scannerAsset = new OriginScannerAsset(); + private ScannerAsset scannerAsset = new DirectScannerAsset(); @Nonnull @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (super.skip()) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else { Material material = this.materialAsset.build(argument.materialCache); return (Prop)(this.scannerAsset != null && this.patternAsset != null ? new BoxProp( this.range, material, this.scannerAsset.build(ScannerAsset.argumentFrom(argument)), this.patternAsset.build(PatternAsset.argumentFrom(argument)) ) - : Prop.noProp()); + : EmptyProp.INSTANCE); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ClusterPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ClusterPropAsset.java index af390dcb..8ee31140 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ClusterPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ClusterPropAsset.java @@ -5,27 +5,29 @@ import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.ConstantCurveAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.curves.CurveAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.OriginScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.DirectScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ScannerAsset; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.builtin.hytalegenerator.props.ClusterProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.OriginScanner; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.ClusterProp; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.DirectScanner; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ClusterPropAsset extends PropAsset { @Nonnull @@ -38,9 +40,9 @@ public class ClusterPropAsset extends PropAsset { .append(new KeyedCodec<>("Seed", Codec.STRING, false), (asset, v) -> asset.seed = v, asset -> asset.seed) .add() .append( - new KeyedCodec<>("WeightedProps", new ArrayCodec<>(ClusterPropAsset.WeightedPropAsset.CODEC, ClusterPropAsset.WeightedPropAsset[]::new), true), - (asset, v) -> asset.weightedPropAssets = v, - asset -> asset.weightedPropAssets + new KeyedCodec<>("WeightedProps", new ArrayCodec<>(ClusterPropAsset.WeightedEntryAsset.CODEC, ClusterPropAsset.WeightedEntryAsset[]::new), true), + (asset, v) -> asset.weightedEntryAssets = v, + asset -> asset.weightedEntryAssets ) .add() .append(new KeyedCodec<>("Pattern", PatternAsset.CODEC, false), (asset, v) -> asset.patternAsset = v, asset -> asset.patternAsset) @@ -51,19 +53,19 @@ public class ClusterPropAsset extends PropAsset { private int range = 0; private CurveAsset distanceCurve = new ConstantCurveAsset(); private String seed = "A"; - private ClusterPropAsset.WeightedPropAsset[] weightedPropAssets = new ClusterPropAsset.WeightedPropAsset[0]; + private ClusterPropAsset.WeightedEntryAsset[] weightedEntryAssets = new ClusterPropAsset.WeightedEntryAsset[0]; private PatternAsset patternAsset = new ConstantPatternAsset(); - private ScannerAsset scannerAsset = new OriginScannerAsset(); + private ScannerAsset scannerAsset = new DirectScannerAsset(); @Nonnull @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (super.skip()) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else { WeightedMap weightedMap = new WeightedMap<>(); - for (ClusterPropAsset.WeightedPropAsset entry : this.weightedPropAssets) { + for (ClusterPropAsset.WeightedEntryAsset entry : this.weightedEntryAssets) { Prop columnProp = entry.propAsset.build(argument); Vector3i readSize = columnProp.getReadBounds_voxelGrid().getSize(); Vector3i writeSize = columnProp.getWriteBounds_voxelGrid().getSize(); @@ -76,8 +78,8 @@ public class ClusterPropAsset extends PropAsset { } } - Pattern pattern = this.patternAsset == null ? Pattern.yesPattern() : this.patternAsset.build(PatternAsset.argumentFrom(argument)); - Scanner scanner = (Scanner)(this.scannerAsset == null ? OriginScanner.getInstance() : this.scannerAsset.build(ScannerAsset.argumentFrom(argument))); + Pattern pattern = (Pattern)(this.patternAsset == null ? ConstantPattern.INSTANCE_TRUE : this.patternAsset.build(PatternAsset.argumentFrom(argument))); + Scanner scanner = (Scanner)(this.scannerAsset == null ? new DirectScanner() : this.scannerAsset.build(ScannerAsset.argumentFrom(argument))); int intSeed = argument.parentSeed.child(this.seed).createSupplier().get(); return new ClusterProp(this.range, this.distanceCurve.build(), intSeed, weightedMap, pattern, scanner); } @@ -87,19 +89,19 @@ public class ClusterPropAsset extends PropAsset { public void cleanUp() { this.distanceCurve.cleanUp(); - for (ClusterPropAsset.WeightedPropAsset weightedPropAsset : this.weightedPropAssets) { - weightedPropAsset.cleanUp(); + for (ClusterPropAsset.WeightedEntryAsset weightedEntryAsset : this.weightedEntryAssets) { + weightedEntryAsset.cleanUp(); } this.patternAsset.cleanUp(); this.scannerAsset.cleanUp(); } - public static class WeightedPropAsset implements Cleanable, JsonAssetWithMap> { + public static class WeightedEntryAsset implements Cleanable, JsonAssetWithMap> { @Nonnull - public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( - ClusterPropAsset.WeightedPropAsset.class, - ClusterPropAsset.WeightedPropAsset::new, + public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( + ClusterPropAsset.WeightedEntryAsset.class, + ClusterPropAsset.WeightedEntryAsset::new, Codec.STRING, (asset, id) -> asset.id = id, config -> config.id, diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ColumnPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ColumnPropAsset.java index 5650772d..72b1a28a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ColumnPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ColumnPropAsset.java @@ -9,12 +9,13 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.blockmask.BlockMaskAsse import com.hypixel.hytale.builtin.hytalegenerator.assets.material.MaterialAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.directionality.DirectionalityAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.directionality.StaticDirectionalityAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.OriginScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.DirectScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.props.ColumnProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.ColumnProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -44,15 +45,15 @@ public class ColumnPropAsset extends PropAsset { private ColumnPropAsset.ColumnBlock[] columnBlocks = new ColumnPropAsset.ColumnBlock[0]; private BlockMaskAsset blockMaskAsset = new BlockMaskAsset(); private DirectionalityAsset directionalityAsset = new StaticDirectionalityAsset(); - private ScannerAsset scannerAsset = new OriginScannerAsset(); + private ScannerAsset scannerAsset = new DirectScannerAsset(); @Nonnull @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (super.skip()) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else if (this.directionalityAsset == null) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else { ArrayList blockPositions = new ArrayList<>(); ArrayList blockTypes = new ArrayList<>(); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/CuboidPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/CuboidPropAsset.java new file mode 100644 index 00000000..9ef1abad --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/CuboidPropAsset.java @@ -0,0 +1,48 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.bounds.IntegerBounds3dAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.ConstantMaterialProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.MaterialProviderAsset; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; +import com.hypixel.hytale.builtin.hytalegenerator.props.CuboidProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class CuboidPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(CuboidPropAsset.class, CuboidPropAsset::new, PropAsset.ABSTRACT_CODEC) + .append(new KeyedCodec<>("Bounds", IntegerBounds3dAsset.CODEC, true), (asset, value) -> asset.boundsAsset = value, asset -> asset.boundsAsset) + .add() + .append( + new KeyedCodec<>("Material", MaterialProviderAsset.CODEC, true), + (asset, value) -> asset.materialProviderAsset = value, + asset -> asset.materialProviderAsset + ) + .add() + .build(); + private IntegerBounds3dAsset boundsAsset = new IntegerBounds3dAsset(); + private MaterialProviderAsset materialProviderAsset = new ConstantMaterialProviderAsset(); + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + if (super.skip()) { + return EmptyProp.INSTANCE; + } else { + Bounds3i bounds = this.boundsAsset.build(); + bounds.correct(); + MaterialProvider materialProvider = this.materialProviderAsset.build(MaterialProviderAsset.argumentFrom(argument)); + return new CuboidProp(bounds, materialProvider); + } + } + + @Override + public void cleanUp() { + this.materialProviderAsset.cleanUp(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/DensityPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/DensityPropAsset.java index ba34a7fd..7be37487 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/DensityPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/DensityPropAsset.java @@ -2,32 +2,52 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props; import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; import com.hypixel.hytale.builtin.hytalegenerator.assets.blockmask.BlockMaskAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.bounds.IntegerBounds3dAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.ConstantDensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.ConstantMaterialProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.MaterialProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.OriginScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.DirectScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.props.DensityProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class DensityPropAsset extends PropAsset { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder(DensityPropAsset.class, DensityPropAsset::new, PropAsset.ABSTRACT_CODEC) - .append(new KeyedCodec<>("Range", Vector3i.CODEC, true), (asset, v) -> asset.range = v, asset -> asset.range) - .addValidator((LegacyValidator)((v, r) -> { - if (v.x < 0 || v.y < 0 || v.z < 0) { - r.fail("Range has a value smaller than 0"); + .append(new KeyedCodec<>("Density", DensityAsset.CODEC, true), (asset, v) -> asset.densityAsset = v, asset -> asset.densityAsset) + .add() + .append( + new KeyedCodec<>("Material", MaterialProviderAsset.CODEC, true), (asset, v) -> asset.materialProviderAsset = v, asset -> asset.materialProviderAsset + ) + .add() + .append(new KeyedCodec<>("Bounds", IntegerBounds3dAsset.CODEC, true), (asset, v) -> asset.boundsAsset = v, asset -> asset.boundsAsset) + .add() + .append(new KeyedCodec<>("Range", Vector3iUtil.CODEC, true), (asset, v) -> asset.range = v, asset -> asset.range) + .addValidator(new Validator() { + public void accept(Vector3i v, ValidationResults r) { + if (v.x < 0 || v.y < 0 || v.z < 0) { + r.fail("Range has a value smaller than 0"); + } } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() .append(new KeyedCodec<>("PlacementMask", BlockMaskAsset.CODEC, true), (asset, v) -> asset.placementMaskAsset = v, asset -> asset.placementMaskAsset) .add() @@ -35,31 +55,46 @@ public class DensityPropAsset extends PropAsset { .add() .append(new KeyedCodec<>("Scanner", ScannerAsset.CODEC, true), (asset, v) -> asset.scannerAsset = v, asset -> asset.scannerAsset) .add() - .append(new KeyedCodec<>("Density", DensityAsset.CODEC, true), (asset, v) -> asset.densityAsset = v, asset -> asset.densityAsset) - .add() - .append( - new KeyedCodec<>("Material", MaterialProviderAsset.CODEC, true), (asset, v) -> asset.materialProviderAsset = v, asset -> asset.materialProviderAsset - ) - .add() .build(); - private Vector3i range = new Vector3i(); - private BlockMaskAsset placementMaskAsset = new BlockMaskAsset(); - private PatternAsset patternAsset = new ConstantPatternAsset(); - private ScannerAsset scannerAsset = new OriginScannerAsset(); - private MaterialProviderAsset materialProviderAsset = new ConstantMaterialProviderAsset(); + @Nonnull private DensityAsset densityAsset = new ConstantDensityAsset(); + @Nonnull + private MaterialProviderAsset materialProviderAsset = new ConstantMaterialProviderAsset(); + @Nonnull + private IntegerBounds3dAsset boundsAsset = new IntegerBounds3dAsset(); + private static final PatternAsset DEFAULT_PATTERN_ASSET = new ConstantPatternAsset(); + private static final ScannerAsset DEFAULT_SCANNER_ASSET = new DirectScannerAsset(); + private static final BlockMaskAsset DEFAULT_MASK_ASSET = new BlockMaskAsset(); + private static final Vector3i DEFAULT_RANGE_ASSET = new Vector3i(); + @Nonnull + private Vector3i range = DEFAULT_RANGE_ASSET; + @Nonnull + private BlockMaskAsset placementMaskAsset = DEFAULT_MASK_ASSET; + @Nonnull + private PatternAsset patternAsset = DEFAULT_PATTERN_ASSET; + @Nonnull + private ScannerAsset scannerAsset = DEFAULT_SCANNER_ASSET; @Nonnull @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (super.skip()) { - return Prop.noProp(); + return EmptyProp.INSTANCE; + } else if (this.patternAsset == DEFAULT_PATTERN_ASSET + && this.scannerAsset == DEFAULT_SCANNER_ASSET + && this.range == DEFAULT_RANGE_ASSET + && this.placementMaskAsset == DEFAULT_MASK_ASSET) { + return new DensityProp( + this.densityAsset.build(DensityAsset.from(argument)), + this.materialProviderAsset.build(MaterialProviderAsset.argumentFrom(argument)), + this.boundsAsset.build() + ); } else if (this.placementMaskAsset == null) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else { BlockMask placementMask = this.placementMaskAsset.build(argument.materialCache); return (Prop)(this.scannerAsset != null && this.patternAsset != null && this.densityAsset != null && this.materialProviderAsset != null - ? new DensityProp( + ? new com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.DensityProp( this.range, this.densityAsset.build(DensityAsset.from(argument)), this.materialProviderAsset.build(MaterialProviderAsset.argumentFrom(argument)), @@ -68,7 +103,7 @@ public class DensityPropAsset extends PropAsset { placementMask, new Material(argument.materialCache.EMPTY_AIR, argument.materialCache.EMPTY_FLUID) ) - : Prop.noProp()); + : EmptyProp.INSTANCE); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/DensitySelectorPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/DensitySelectorPropAsset.java new file mode 100644 index 00000000..76e46973 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/DensitySelectorPropAsset.java @@ -0,0 +1,105 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.assetstore.AssetExtraInfo; +import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; +import com.hypixel.hytale.assetstore.map.DefaultAssetMap; +import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; +import com.hypixel.hytale.builtin.hytalegenerator.assets.delimiters.RangeDoubleAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.density.ConstantDensityAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; +import com.hypixel.hytale.builtin.hytalegenerator.delimiters.DelimiterDouble; +import com.hypixel.hytale.builtin.hytalegenerator.delimiters.RangeDouble; +import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.props.DensitySelectorProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; + +public class DensitySelectorPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + DensitySelectorPropAsset.class, DensitySelectorPropAsset::new, PropAsset.ABSTRACT_CODEC + ) + .append( + new KeyedCodec<>("Delimiters", new ArrayCodec<>(DensitySelectorPropAsset.DelimiterAsset.CODEC, DensitySelectorPropAsset.DelimiterAsset[]::new), true), + (asset, value) -> asset.delimiterAssets = value, + asset -> asset.delimiterAssets + ) + .add() + .append(new KeyedCodec<>("Density", DensityAsset.CODEC, true), (asset, value) -> asset.densityAsset = value, asset -> asset.densityAsset) + .add() + .build(); + @Nonnull + private DensitySelectorPropAsset.DelimiterAsset[] delimiterAssets = new DensitySelectorPropAsset.DelimiterAsset[0]; + @Nonnull + private DensityAsset densityAsset = new ConstantDensityAsset(); + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + if (super.skip()) { + return EmptyProp.INSTANCE; + } else { + List> delimiters = new ArrayList<>(this.delimiterAssets.length); + + for (DensitySelectorPropAsset.DelimiterAsset delimiterAsset : this.delimiterAssets) { + RangeDouble range = delimiterAsset.rangeAsset.build(); + Prop prop = delimiterAsset.propAsset.build(argument); + DelimiterDouble delimiter = new DelimiterDouble<>(range, prop); + delimiters.add(delimiter); + } + + Density density = this.densityAsset.build(DensityAsset.from(argument)); + return new DensitySelectorProp(delimiters, density); + } + } + + @Override + public void cleanUp() { + for (DensitySelectorPropAsset.DelimiterAsset delimiterAsset : this.delimiterAssets) { + delimiterAsset.propAsset.cleanUp(); + } + + this.densityAsset.cleanUp(); + } + + public static class DelimiterAsset implements Cleanable, JsonAssetWithMap> { + @Nonnull + public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( + DensitySelectorPropAsset.DelimiterAsset.class, + DensitySelectorPropAsset.DelimiterAsset::new, + Codec.STRING, + (asset, id) -> asset.id = id, + config -> config.id, + (config, data) -> config.data = data, + config -> config.data + ) + .append(new KeyedCodec<>("Range", RangeDoubleAsset.CODEC, true), (asset, value) -> asset.rangeAsset = value, asset -> asset.rangeAsset) + .add() + .append(new KeyedCodec<>("Prop", PropAsset.CODEC, true), (asset, value) -> asset.propAsset = value, asset -> asset.propAsset) + .add() + .build(); + private String id; + private AssetExtraInfo.Data data; + @Nonnull + private RangeDoubleAsset rangeAsset = new RangeDoubleAsset(); + @Nonnull + private PropAsset propAsset = new EmptyPropAsset(); + + public String getId() { + return this.id; + } + + @Override + public void cleanUp() { + this.propAsset.cleanUp(); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/EmptyPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/EmptyPropAsset.java new file mode 100644 index 00000000..d2c3d63c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/EmptyPropAsset.java @@ -0,0 +1,21 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class EmptyPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(EmptyPropAsset.class, EmptyPropAsset::new, PropAsset.ABSTRACT_CODEC).build(); + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + return EmptyProp.INSTANCE; + } + + @Override + public void cleanUp() { + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ImportedPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ImportedPropAsset.java index 6c9e6376..ecf5c342 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ImportedPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ImportedPropAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -18,13 +19,13 @@ public class ImportedPropAsset extends PropAsset { @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (super.skip()) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else if (this.name != null && !this.name.isEmpty()) { PropAsset exportedAsset = PropAsset.getExportedAsset(this.name); - return exportedAsset == null ? Prop.noProp() : exportedAsset.build(argument); + return (Prop)(exportedAsset == null ? EmptyProp.INSTANCE : exportedAsset.build(argument)); } else { HytaleLogger.getLogger().atWarning().log("An exported Pattern with the name does not exist: " + this.name); - return Prop.noProp(); + return EmptyProp.INSTANCE; } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/LocatorPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/LocatorPropAsset.java new file mode 100644 index 00000000..dd363f29 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/LocatorPropAsset.java @@ -0,0 +1,58 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.DirectScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.LocatorProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; + +public class LocatorPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(LocatorPropAsset.class, LocatorPropAsset::new, PropAsset.ABSTRACT_CODEC) + .append(new KeyedCodec<>("Prop", PropAsset.CODEC, true), (asset, value) -> asset.propAsset = value, asset -> asset.propAsset) + .add() + .append(new KeyedCodec<>("Pattern", PatternAsset.CODEC, true), (asset, value) -> asset.patternAsset = value, asset -> asset.patternAsset) + .add() + .append(new KeyedCodec<>("Scanner", ScannerAsset.CODEC, true), (asset, value) -> asset.scannerAsset = value, asset -> asset.scannerAsset) + .add() + .append(new KeyedCodec<>("PlacementCap", Codec.INTEGER, true), (asset, value) -> asset.placementCap = value, asset -> asset.placementCap) + .addValidator(Validators.greaterThanOrEqual(0)) + .add() + .build(); + @Nonnull + private PropAsset propAsset = new EmptyPropAsset(); + @Nonnull + private PatternAsset patternAsset = new ConstantPatternAsset(); + @Nonnull + private ScannerAsset scannerAsset = new DirectScannerAsset(); + private int placementCap = 1; + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + if (super.skip()) { + return EmptyProp.INSTANCE; + } else { + Prop prop = this.propAsset.build(argument); + Pattern pattern = this.patternAsset.build(PatternAsset.argumentFrom(argument)); + Scanner scanner = this.scannerAsset.build(ScannerAsset.argumentFrom(argument)); + return new LocatorProp(prop, pattern, scanner, this.placementCap); + } + } + + @Override + public void cleanUp() { + this.propAsset.cleanUp(); + this.patternAsset.cleanUp(); + this.scannerAsset.cleanUp(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ManualPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ManualPropAsset.java new file mode 100644 index 00000000..0ca9cb88 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/ManualPropAsset.java @@ -0,0 +1,75 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.assetstore.AssetExtraInfo; +import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; +import com.hypixel.hytale.assetstore.map.DefaultAssetMap; +import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.hytalegenerator.assets.material.MaterialAsset; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.ManualProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.joml.Vector3i; + +public class ManualPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(ManualPropAsset.class, ManualPropAsset::new, PropAsset.ABSTRACT_CODEC) + .append( + new KeyedCodec<>("Blocks", new ArrayCodec<>(ManualPropAsset.BlockAsset.CODEC, ManualPropAsset.BlockAsset[]::new), true), + (asset, value) -> asset.blockAssets = value, + asset -> asset.blockAssets + ) + .add() + .build(); + @Nonnull + private ManualPropAsset.BlockAsset[] blockAssets = new ManualPropAsset.BlockAsset[0]; + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + if (super.skip()) { + return EmptyProp.INSTANCE; + } else { + List blocks = new ArrayList<>(this.blockAssets.length); + + for (ManualPropAsset.BlockAsset blockAsset : this.blockAssets) { + blocks.add(new ManualProp.Block(blockAsset.materialAsset.build(argument.materialCache), blockAsset.position)); + } + + return new ManualProp(blocks); + } + } + + public static class BlockAsset implements JsonAssetWithMap> { + @Nonnull + public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( + ManualPropAsset.BlockAsset.class, + ManualPropAsset.BlockAsset::new, + Codec.STRING, + (asset, id) -> asset.id = id, + config -> config.id, + (config, data) -> config.data = data, + config -> config.data + ) + .append(new KeyedCodec<>("Position", Vector3iUtil.CODEC, true), (asset, value) -> asset.position = value, asset -> asset.position) + .add() + .append(new KeyedCodec<>("Material", MaterialAsset.CODEC, true), (asset, value) -> asset.materialAsset = value, asset -> asset.materialAsset) + .add() + .build(); + private String id; + private AssetExtraInfo.Data data; + private Vector3i position = new Vector3i(); + private MaterialAsset materialAsset = new MaterialAsset(); + + public String getId() { + return this.id; + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/MaskPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/MaskPropAsset.java new file mode 100644 index 00000000..475104d2 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/MaskPropAsset.java @@ -0,0 +1,42 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; +import com.hypixel.hytale.builtin.hytalegenerator.assets.blockmask.BlockMaskAsset; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.MaskProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class MaskPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(MaskPropAsset.class, MaskPropAsset::new, PropAsset.ABSTRACT_CODEC) + .append(new KeyedCodec<>("Prop", PropAsset.CODEC, true), (asset, value) -> asset.propAsset = value, asset -> asset.propAsset) + .add() + .append(new KeyedCodec<>("Mask", BlockMaskAsset.CODEC, true), (asset, value) -> asset.blockMaskAsset = value, asset -> asset.blockMaskAsset) + .add() + .build(); + @Nonnull + private PropAsset propAsset = new EmptyPropAsset(); + @Nonnull + private BlockMaskAsset blockMaskAsset = new BlockMaskAsset(); + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + if (super.skip()) { + return EmptyProp.INSTANCE; + } else { + Prop prop = this.propAsset.build(argument); + BlockMask mask = this.blockMaskAsset.build(argument.materialCache); + return new MaskProp(prop, mask); + } + } + + @Override + public void cleanUp() { + this.propAsset.cleanUp(); + this.blockMaskAsset.cleanUp(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/NoPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/NoPropAsset.java deleted file mode 100644 index 2f19fb2c..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/NoPropAsset.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.props; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.props.ScanResult; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3i; -import javax.annotation.Nonnull; -import org.checkerframework.checker.nullness.compatqual.NonNullDecl; - -public class NoPropAsset extends PropAsset { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(NoPropAsset.class, NoPropAsset::new, PropAsset.ABSTRACT_CODEC).build(); - - @Nonnull - @Override - public Prop build(@Nonnull PropAsset.Argument argument) { - return new Prop() { - @Nonnull - final Bounds3i emptyBounds_voxelGrid = new Bounds3i(); - - @Nonnull - @Override - public ScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - return ScanResult.noScanResult(); - } - - @Override - public void place(@Nonnull Prop.Context context) { - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return ContextDependency.EMPTY; - } - - @NonNullDecl - @Override - public Bounds3i getReadBounds_voxelGrid() { - return this.emptyBounds_voxelGrid; - } - - @Nonnull - @Override - public Bounds3i getWriteBounds_voxelGrid() { - return this.emptyBounds_voxelGrid; - } - }; - } - - @Override - public void cleanUp() { - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/OffsetPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/OffsetPropAsset.java index 19345d22..b8535f3c 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/OffsetPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/OffsetPropAsset.java @@ -1,27 +1,29 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.OffsetProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class OffsetPropAsset extends PropAsset { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder(OffsetPropAsset.class, OffsetPropAsset::new, PropAsset.ABSTRACT_CODEC) - .append(new KeyedCodec<>("Offset", Vector3i.CODEC, true), (asset, value) -> asset.offset_voxelGrid = value, asset -> asset.offset_voxelGrid) + .append(new KeyedCodec<>("Offset", Vector3iUtil.CODEC, true), (asset, value) -> asset.offset_voxelGrid = value, asset -> asset.offset_voxelGrid) .add() .append(new KeyedCodec<>("Prop", PropAsset.CODEC, true), (asset, value) -> asset.propAsset = value, asset -> asset.propAsset) .add() .build(); private Vector3i offset_voxelGrid = new Vector3i(); - private PropAsset propAsset = new NoPropAsset(); + private PropAsset propAsset = new EmptyPropAsset(); @Nonnull @Override public Prop build(@Nonnull PropAsset.Argument argument) { - return (Prop)(super.skip() ? Prop.noProp() : new OffsetProp(this.offset_voxelGrid, this.propAsset.build(argument))); + return (Prop)(super.skip() ? EmptyProp.INSTANCE : new OffsetProp(this.offset_voxelGrid, this.propAsset.build(argument))); } @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/OrienterPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/OrienterPropAsset.java new file mode 100644 index 00000000..1686f79b --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/OrienterPropAsset.java @@ -0,0 +1,86 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.material.OrthogonalRotationAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.DirectScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.OrienterProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; + +public class OrienterPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(OrienterPropAsset.class, OrienterPropAsset::new, PropAsset.ABSTRACT_CODEC) + .append(new KeyedCodec<>("Prop", PropAsset.CODEC, true), (asset, value) -> asset.propAsset = value, asset -> asset.propAsset) + .add() + .append( + new KeyedCodec<>("Rotations", new ArrayCodec<>(OrthogonalRotationAsset.CODEC, OrthogonalRotationAsset[]::new), true), + (asset, value) -> asset.rotationAssets = value, + asset -> asset.rotationAssets + ) + .add() + .append(new KeyedCodec<>("Pattern", PatternAsset.CODEC, true), (asset, value) -> asset.patternAsset = value, asset -> asset.patternAsset) + .add() + .append(new KeyedCodec<>("Scanner", ScannerAsset.CODEC, true), (asset, value) -> asset.scannerAsset = value, asset -> asset.scannerAsset) + .add() + .append( + new KeyedCodec<>("SelectionMode", new EnumCodec<>(OrienterProp.SelectionMode.class), true), + (asset, value) -> asset.selectionMode = value, + asset -> asset.selectionMode + ) + .add() + .append(new KeyedCodec<>("Seed", Codec.STRING, false), (asset, value) -> asset.seed = value, asset -> asset.seed) + .add() + .build(); + @Nonnull + private PropAsset propAsset = new EmptyPropAsset(); + @Nonnull + private PatternAsset patternAsset = new ConstantPatternAsset(); + @Nonnull + private ScannerAsset scannerAsset = new DirectScannerAsset(); + @Nonnull + private OrthogonalRotationAsset[] rotationAssets = new OrthogonalRotationAsset[0]; + @Nonnull + private OrienterProp.SelectionMode selectionMode = OrienterProp.SelectionMode.FIRST_VALID; + @Nonnull + private String seed = ""; + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + if (super.skip()) { + return EmptyProp.INSTANCE; + } else { + SeedBox seedBox = argument.parentSeed.child(this.seed); + Prop prop = this.propAsset.build(argument); + Pattern pattern = this.patternAsset.build(PatternAsset.argumentFrom(argument)); + Scanner scanner = this.scannerAsset.build(ScannerAsset.argumentFrom(argument)); + List rotations = new ArrayList<>(this.rotationAssets.length); + + for (int i = 0; i < this.rotationAssets.length; i++) { + RotationTuple rotation = this.rotationAssets[i].build(); + rotations.add(rotation); + } + + return new OrienterProp(rotations, prop, pattern, scanner, argument.materialCache, this.selectionMode, seedBox.createSupplier().get()); + } + } + + @Override + public void cleanUp() { + this.propAsset.cleanUp(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/PondFillerPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/PondFillerPropAsset.java index 724f0e81..7a2568c2 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/PondFillerPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/PondFillerPropAsset.java @@ -2,32 +2,31 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props; import com.hypixel.hytale.builtin.hytalegenerator.MaterialSet; import com.hypixel.hytale.builtin.hytalegenerator.assets.blockset.MaterialSetAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.bounds.IntegerBounds3dAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.ConstantMaterialProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.materialproviders.MaterialProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.OriginScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.DirectScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.PondFillerProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.props.filler.PondFillerProp; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class PondFillerPropAsset extends PropAsset { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( PondFillerPropAsset.class, PondFillerPropAsset::new, PropAsset.ABSTRACT_CODEC ) - .append(new KeyedCodec<>("BoundingMin", Vector3i.CODEC, true), (asset, v) -> asset.boundingMin = v, asset -> asset.boundingMin) - .add() - .append(new KeyedCodec<>("BoundingMax", Vector3i.CODEC, true), (asset, v) -> asset.boundingMax = v, asset -> asset.boundingMax) - .add() .append( new KeyedCodec<>("FillMaterial", MaterialProviderAsset.CODEC, true), (asset, v) -> asset.fluidMaterialProviderAsset = v, @@ -36,31 +35,60 @@ public class PondFillerPropAsset extends PropAsset { .add() .append(new KeyedCodec<>("BarrierBlockSet", MaterialSetAsset.CODEC, true), (asset, v) -> asset.solidSetAsset = v, asset -> asset.solidSetAsset) .add() + .append(new KeyedCodec<>("Bounds", IntegerBounds3dAsset.CODEC, true), (asset, v) -> asset.boundsAsset = v, asset -> asset.boundsAsset) + .add() + .append(new KeyedCodec<>("BoundingMin", Vector3iUtil.CODEC, true), (asset, v) -> asset.boundingMin = v, asset -> asset.boundingMin) + .add() + .append(new KeyedCodec<>("BoundingMax", Vector3iUtil.CODEC, true), (asset, v) -> asset.boundingMax = v, asset -> asset.boundingMax) + .add() .append(new KeyedCodec<>("Pattern", PatternAsset.CODEC, true), (asset, v) -> asset.patternAsset = v, asset -> asset.patternAsset) .add() .append(new KeyedCodec<>("Scanner", ScannerAsset.CODEC, true), (asset, v) -> asset.scannerAsset = v, asset -> asset.scannerAsset) .add() .build(); - private Vector3i boundingMin = new Vector3i(-10, -10, -10); - private Vector3i boundingMax = new Vector3i(10, 10, 10); + private static final PatternAsset DEFAULT_PATTERN_ASSET = new ConstantPatternAsset(); + private static final ScannerAsset DEFAULT_SCANNER_ASSET = new DirectScannerAsset(); + private static final Vector3i DEFAULT_MIN_ASSET = new Vector3i(-10, -10, -10); + private static final Vector3i DEFAULT_MAX_ASSET = new Vector3i(10, 10, 10); + @Nonnull + private IntegerBounds3dAsset boundsAsset = new IntegerBounds3dAsset(); + @Nonnull private MaterialProviderAsset fluidMaterialProviderAsset = new ConstantMaterialProviderAsset(); + @Nonnull private MaterialSetAsset solidSetAsset = new MaterialSetAsset(); - private PatternAsset patternAsset = new ConstantPatternAsset(); - private ScannerAsset scannerAsset = new OriginScannerAsset(); + @Nonnull + private Vector3i boundingMin = DEFAULT_MIN_ASSET; + @Nonnull + private Vector3i boundingMax = DEFAULT_MAX_ASSET; + @Nonnull + private PatternAsset patternAsset = DEFAULT_PATTERN_ASSET; + @Nonnull + private ScannerAsset scannerAsset = DEFAULT_SCANNER_ASSET; @Nonnull @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (super.skip()) { - return Prop.noProp(); + return EmptyProp.INSTANCE; + } else if (this.patternAsset == DEFAULT_PATTERN_ASSET + && this.scannerAsset == DEFAULT_SCANNER_ASSET + && this.boundingMin == DEFAULT_MIN_ASSET + && this.boundingMax == DEFAULT_MAX_ASSET) { + return new PondFillerProp( + this.boundsAsset.build(), + this.fluidMaterialProviderAsset.build(MaterialProviderAsset.argumentFrom(argument)), + this.solidSetAsset.build(argument.materialCache) + ); } else if (this.scannerAsset != null && this.patternAsset != null && this.fluidMaterialProviderAsset != null && this.solidSetAsset != null) { MaterialProvider materialProvider = this.fluidMaterialProviderAsset.build(MaterialProviderAsset.argumentFrom(argument)); MaterialSet solidSet = this.solidSetAsset.build(argument.materialCache); Pattern pattern = this.patternAsset.build(PatternAsset.argumentFrom(argument)); Scanner scanner = this.scannerAsset.build(ScannerAsset.argumentFrom(argument)); - return new PondFillerProp(this.boundingMin, this.boundingMax, solidSet, materialProvider, scanner, pattern); + return new com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.filler.PondFillerProp( + this.boundingMin, this.boundingMax, solidSet, materialProvider, scanner, pattern + ); } else { - return Prop.noProp(); + return EmptyProp.INSTANCE; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/PropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/PropAsset.java index 6c9f750f..05398a10 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/PropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/PropAsset.java @@ -7,11 +7,12 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; +import com.hypixel.hytale.builtin.hytalegenerator.assets.propdistribution.PropDistributionAsset; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -95,5 +96,12 @@ public abstract class PropAsset implements Cleanable, JsonAssetWithMap CODEC = BuilderCodec.builder(QueuePropAsset.class, QueuePropAsset::new, PropAsset.ABSTRACT_CODEC) .append( - new KeyedCodec<>("Queue", new ArrayCodec<>(PropAsset.CODEC, PropAsset[]::new), true), (asset, v) -> asset.propAssets = v, asset -> asset.propAssets + new KeyedCodec<>("Props", new ArrayCodec<>(PropAsset.CODEC, PropAsset[]::new), true), + (asset, value) -> asset.propAssets = value, + asset -> asset.propAssets ) .add() .build(); @@ -22,15 +25,15 @@ public class QueuePropAsset extends PropAsset { @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (super.skip()) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else { - ArrayList propsQueue = new ArrayList<>(this.propAssets.length); + ArrayList props = new ArrayList<>(this.propAssets.length); for (PropAsset asset : this.propAssets) { - propsQueue.add(asset.build(argument)); + props.add(asset.build(argument)); } - return new QueueProp(propsQueue); + return new QueueProp(props); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/RandomRotatorPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/RandomRotatorPropAsset.java new file mode 100644 index 00000000..8f141801 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/RandomRotatorPropAsset.java @@ -0,0 +1,75 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; +import com.hypixel.hytale.builtin.hytalegenerator.assets.material.OrthogonalRotationAsset; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.props.StaticRotatorProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.WeightedProp; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import javax.annotation.Nonnull; + +public class RandomRotatorPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + RandomRotatorPropAsset.class, RandomRotatorPropAsset::new, PropAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Prop", PropAsset.CODEC, true), (asset, value) -> asset.propAsset = value, asset -> asset.propAsset) + .add() + .append( + new KeyedCodec<>("Rotations", new ArrayCodec<>(OrthogonalRotationAsset.CODEC, OrthogonalRotationAsset[]::new), true), + (asset, value) -> asset.rotationAssets = value, + asset -> asset.rotationAssets + ) + .add() + .append(new KeyedCodec<>("HorizontalRotations", BuilderCodec.BOOLEAN, false), (asset, value) -> asset.allHorizontal = value, asset -> asset.allHorizontal) + .add() + .append(new KeyedCodec<>("Seed", BuilderCodec.STRING, true), (asset, value) -> asset.seed = value, asset -> asset.seed) + .add() + .build(); + @Nonnull + private PropAsset propAsset = new EmptyPropAsset(); + @Nonnull + private OrthogonalRotationAsset[] rotationAssets = new OrthogonalRotationAsset[0]; + private boolean allHorizontal = false; + @Nonnull + private String seed = ""; + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + if (super.skip()) { + return EmptyProp.INSTANCE; + } else if (!this.allHorizontal && this.rotationAssets.length == 0) { + return EmptyProp.INSTANCE; + } else { + Prop childProp = this.propAsset.build(argument); + WeightedMap rotatedProps = new WeightedMap<>(); + if (this.allHorizontal) { + Prop ninety = new StaticRotatorProp(childProp, RotationTuple.of(Rotation.Ninety, Rotation.None, Rotation.None), argument.materialCache); + Prop oneEighty = new StaticRotatorProp(childProp, RotationTuple.of(Rotation.OneEighty, Rotation.None, Rotation.None), argument.materialCache); + Prop twoSeventy = new StaticRotatorProp(childProp, RotationTuple.of(Rotation.TwoSeventy, Rotation.None, Rotation.None), argument.materialCache); + rotatedProps.add(childProp, 1.0); + rotatedProps.add(ninety, 1.0); + rotatedProps.add(oneEighty, 1.0); + rotatedProps.add(twoSeventy, 1.0); + } else { + for (OrthogonalRotationAsset rotationAsset : this.rotationAssets) { + Prop rotatedProp = new StaticRotatorProp(childProp, rotationAsset.build(), argument.materialCache); + rotatedProps.add(rotatedProp, 1.0); + } + } + + return new WeightedProp(rotatedProps, argument.parentSeed.child(this.seed).createSupplier().get()); + } + } + + @Override + public void cleanUp() { + this.propAsset.cleanUp(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/StaticRotatorPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/StaticRotatorPropAsset.java new file mode 100644 index 00000000..64c38ae6 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/StaticRotatorPropAsset.java @@ -0,0 +1,43 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.props; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.material.OrthogonalRotationAsset; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.props.StaticRotatorProp; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import javax.annotation.Nonnull; + +public class StaticRotatorPropAsset extends PropAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + StaticRotatorPropAsset.class, StaticRotatorPropAsset::new, PropAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Prop", PropAsset.CODEC, true), (asset, value) -> asset.propAsset = value, asset -> asset.propAsset) + .add() + .append(new KeyedCodec<>("Rotation", OrthogonalRotationAsset.CODEC, true), (asset, value) -> asset.rotationAsset = value, asset -> asset.rotationAsset) + .add() + .build(); + @Nonnull + private PropAsset propAsset = new EmptyPropAsset(); + @Nonnull + private OrthogonalRotationAsset rotationAsset = new OrthogonalRotationAsset(); + + @Nonnull + @Override + public Prop build(@Nonnull PropAsset.Argument argument) { + if (super.skip()) { + return EmptyProp.INSTANCE; + } else { + Prop prop = this.propAsset.build(argument); + RotationTuple rotation = this.rotationAsset.build(); + return new StaticRotatorProp(prop, rotation, argument.materialCache); + } + } + + @Override + public void cleanUp() { + this.propAsset.cleanUp(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/UnionPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/UnionPropAsset.java index 256af177..07512ece 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/UnionPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/UnionPropAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; import com.hypixel.hytale.builtin.hytalegenerator.props.UnionProp; import com.hypixel.hytale.codec.KeyedCodec; @@ -22,15 +23,15 @@ public class UnionPropAsset extends PropAsset { @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (super.skip()) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else { - ArrayList chainedProps = new ArrayList<>(this.propAssets.length); + ArrayList props = new ArrayList<>(this.propAssets.length); for (PropAsset asset : this.propAssets) { - chainedProps.add(asset.build(argument)); + props.add(asset.build(argument)); } - return new UnionProp(chainedProps); + return new UnionProp(props); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/WeightedPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/WeightedPropAsset.java index cb950637..7c6f3107 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/WeightedPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/WeightedPropAsset.java @@ -4,8 +4,9 @@ import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; import com.hypixel.hytale.builtin.hytalegenerator.props.WeightedProp; import com.hypixel.hytale.codec.Codec; @@ -44,7 +45,7 @@ public class WeightedPropAsset extends PropAsset { return new WeightedProp(weightedProps, childArgument.parentSeed.createSupplier().get()); } else { - return Prop.noProp(); + return EmptyProp.INSTANCE; } } @@ -75,7 +76,7 @@ public class WeightedPropAsset extends PropAsset { private String id; private AssetExtraInfo.Data data; private double weight = 1.0; - private PropAsset propAsset = new NoPropAsset(); + private PropAsset propAsset = new EmptyPropAsset(); public String getId() { return this.id; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabFileVisitor.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabFileVisitor.java index 06265a37..74e8fd76 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabFileVisitor.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabFileVisitor.java @@ -1,34 +1,41 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop; -import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; +import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import java.io.IOException; +import java.nio.file.AccessDeniedException; import java.nio.file.FileVisitResult; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.util.List; +import java.util.function.BiConsumer; import javax.annotation.Nonnull; public class PrefabFileVisitor extends SimpleFileVisitor { @Nonnull - private final List prefabBuffers; + private final BiConsumer consumer; - public PrefabFileVisitor(@Nonnull List prefabBuffers) { - this.prefabBuffers = prefabBuffers; + public PrefabFileVisitor(@Nonnull BiConsumer consumer) { + this.consumer = consumer; } @Nonnull - public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) throws IOException { + public FileVisitResult visitFile(@Nonnull Path path, @Nonnull BasicFileAttributes attrs) throws IOException { if (!attrs.isRegularFile()) { return FileVisitResult.CONTINUE; } else { - PrefabBuffer loadedPrefab = PrefabLoader.loadPrefabBufferAt(file); + IPrefabBuffer loadedPrefab = PrefabLoader.loadPrefabBufferAt(path); if (loadedPrefab == null) { return FileVisitResult.CONTINUE; } else { - this.prefabBuffers.add(loadedPrefab); + this.consumer.accept(path, loadedPrefab); return FileVisitResult.CONTINUE; } } } + + @Nonnull + public FileVisitResult visitFileFailed(@Nonnull Path file, @Nonnull IOException exc) throws IOException { + return !(exc instanceof NoSuchFileException) && !(exc instanceof AccessDeniedException) ? super.visitFileFailed(file, exc) : FileVisitResult.CONTINUE; + } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabLoader.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabLoader.java index 8adc590e..d43bdc29 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabLoader.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabLoader.java @@ -2,27 +2,25 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.common.util.ExceptionUtil; -import com.hypixel.hytale.server.core.prefab.selection.buffer.BsonPrefabBufferDeserializer; -import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; -import com.hypixel.hytale.server.core.util.BsonUtil; +import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferUtil; +import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; +import java.util.function.BiConsumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.bson.BsonDocument; public class PrefabLoader { - public static void loadAllPrefabBuffersUnder(@Nonnull Path dirPath, @Nonnull List pathPrefabs) { - if (!Files.isDirectory(dirPath)) { - PrefabBuffer prefab = loadPrefabBufferAt(dirPath); + public static void traverseAllPrefabBuffersUnder(@Nonnull Path path, @Nonnull BiConsumer prefabsOut) { + if (!Files.isDirectory(path)) { + IPrefabBuffer prefab = loadPrefabBufferAt(path); if (prefab != null) { - pathPrefabs.add(prefab); + prefabsOut.accept(path, prefab); } } else { try { - Files.walkFileTree(dirPath, new PrefabFileVisitor(pathPrefabs)); + Files.walkFileTree(path, new PrefabFileVisitor(prefabsOut)); } catch (IOException var4) { String msg = "Exception thrown by HytaleGenerator while loading a Prefab:\n"; msg = msg + ExceptionUtil.toStringWithStack(var4); @@ -32,17 +30,16 @@ public class PrefabLoader { } @Nullable - public static PrefabBuffer loadPrefabBufferAt(@Nonnull Path filePath) { + public static IPrefabBuffer loadPrefabBufferAt(@Nonnull Path filePath) { if (!hasJsonExtension(filePath)) { return null; + } else if (!Files.exists(filePath)) { + LoggerUtil.getLogger().info("Didn't find a prefab with path: " + filePath); + return null; } else { try { - BsonDocument prefabAsBson = BsonUtil.readDocumentNow(filePath); - return prefabAsBson == null ? null : BsonPrefabBufferDeserializer.INSTANCE.deserialize(filePath, prefabAsBson); - } catch (Exception var3) { - String msg = "Exception thrown by HytaleGenerator while loading a PrefabBuffer for " + filePath + ":\n"; - msg = msg + ExceptionUtil.toStringWithStack(var3); - LoggerUtil.getLogger().severe(msg); + return PrefabBufferUtil.getCached(filePath); + } catch (Error var2) { return null; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabPropAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabPropAsset.java index a919a244..fdc32452 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabPropAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/PrefabPropAsset.java @@ -7,22 +7,23 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.blockmask.BlockMaskAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.PropAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.directionality.DirectionalityAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.directionality.StaticDirectionalityAsset; -import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.OriginScannerAsset; +import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.DirectScannerAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.scanners.ScannerAsset; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.prefab.MoldingDirection; -import com.hypixel.hytale.builtin.hytalegenerator.props.prefab.PrefabMoldingConfiguration; -import com.hypixel.hytale.builtin.hytalegenerator.props.prefab.PrefabProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab.MoldingDirection; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab.PrefabMoldingConfiguration; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab.PrefabProp; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -31,12 +32,13 @@ import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.common.util.ExceptionUtil; import com.hypixel.hytale.common.util.PathUtil; -import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.server.core.asset.AssetModule; -import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; +import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -71,49 +73,33 @@ public class PrefabPropAsset extends PropAsset { .add() .build(); private PrefabPropAsset.WeightedPathAsset[] weightedPrefabPathAssets = new PrefabPropAsset.WeightedPathAsset[0]; + private DirectionalityAsset directionalityAsset = null; + private ScannerAsset scannerAsset = null; private boolean legacyPath = false; private boolean loadEntities = true; - private DirectionalityAsset directionalityAsset = new StaticDirectionalityAsset(); - private ScannerAsset scannerAsset = new OriginScannerAsset(); private BlockMaskAsset blockMaskAsset = new BlockMaskAsset(); private MoldingDirection moldingDirectionName = MoldingDirection.NONE; - private ScannerAsset moldingScannerAsset = new OriginScannerAsset(); + private ScannerAsset moldingScannerAsset = new DirectScannerAsset(); private PatternAsset moldingPatternAsset = new ConstantPatternAsset(); private boolean moldChildren = false; - @Override - public void cleanUp() { - this.directionalityAsset.cleanUp(); - this.scannerAsset.cleanUp(); - this.blockMaskAsset.cleanUp(); - this.moldingScannerAsset.cleanUp(); - this.moldingPatternAsset.cleanUp(); - } - @Nonnull @Override public Prop build(@Nonnull PropAsset.Argument argument) { if (!super.skip() && this.weightedPrefabPathAssets.length != 0) { - WeightedMap> prefabWeightedMap = new WeightedMap<>(); + WeightedMap> prefabWeightedMap = new WeightedMap<>(); for (PrefabPropAsset.WeightedPathAsset pathAsset : this.weightedPrefabPathAssets) { - List pathPrefabs = this.loadPrefabBuffersFrom(pathAsset.path); - if (pathPrefabs != null) { + List pathPrefabs = this.loadPrefabBuffersFrom(pathAsset.path); + if (pathPrefabs != null && !pathPrefabs.isEmpty()) { prefabWeightedMap.add(pathPrefabs, pathAsset.weight); } } if (prefabWeightedMap.size() == 0) { - return Prop.noProp(); - } else { - MaterialCache voxelCache = argument.materialCache; - BlockMask blockMask; - if (this.blockMaskAsset == null) { - blockMask = new BlockMask(); - } else { - blockMask = this.blockMaskAsset.build(voxelCache); - } - + return EmptyProp.INSTANCE; + } else if (this.scannerAsset != null && this.directionalityAsset != null) { + BlockMask blockMask = this.blockMaskAsset.build(argument.materialCache); Scanner scanner = this.scannerAsset.build(ScannerAsset.argumentFrom(argument)); Directionality directionality = this.directionalityAsset.build(DirectionalityAsset.argumentFrom(argument)); MoldingDirection moldingDirection = this.moldingDirectionName; @@ -121,12 +107,12 @@ public class PrefabPropAsset extends PropAsset { if (moldingDirection != MoldingDirection.DOWN && moldingDirection != MoldingDirection.UP) { moldingConfiguration = PrefabMoldingConfiguration.none(); } else { - Scanner moldingScanner = this.moldingScannerAsset == null - ? Scanner.noScanner() - : this.moldingScannerAsset.build(ScannerAsset.argumentFrom(argument)); - Pattern moldingPattern = this.moldingPatternAsset == null - ? Pattern.noPattern() - : this.moldingPatternAsset.build(PatternAsset.argumentFrom(argument)); + Scanner moldingScanner = (Scanner)(this.moldingScannerAsset == null + ? EmptyScanner.INSTANCE + : this.moldingScannerAsset.build(ScannerAsset.argumentFrom(argument))); + Pattern moldingPattern = (Pattern)(this.moldingPatternAsset == null + ? ConstantPattern.INSTANCE_FALSE + : this.moldingPatternAsset.build(PatternAsset.argumentFrom(argument))); moldingConfiguration = new PrefabMoldingConfiguration(moldingScanner, moldingPattern, moldingDirection, this.moldChildren); } @@ -134,25 +120,32 @@ public class PrefabPropAsset extends PropAsset { prefabWeightedMap, scanner, directionality, - voxelCache, + argument.materialCache, blockMask, moldingConfiguration, this::loadPrefabBuffersFrom, argument.parentSeed, this.loadEntities ); + } else { + return new com.hypixel.hytale.builtin.hytalegenerator.props.PrefabProp( + prefabWeightedMap, argument.materialCache, argument.parentSeed, this::loadPrefabBuffersFrom + ); } } else { - return Prop.noProp(); + return EmptyProp.INSTANCE; } } @Nullable - private List loadPrefabBuffersFrom(@Nonnull String path) { - List pathPrefabs = new ArrayList<>(); + private List loadPrefabBuffersFrom(@Nonnull String path) { + List loadedPrefabs = new ArrayList<>(); + Set traversedPaths = new HashSet<>(); + List packs = AssetModule.get().getAssetPacks(); - for (AssetPack pack : AssetModule.get().getAssetPacks()) { - Path prefabsDir = pack.getRoot().resolve("Server"); + for (int i = packs.size() - 1; i >= 0; i--) { + Path packRootPath = packs.get(i).getRoot(); + Path prefabsDir = packRootPath.resolve("Server"); if (this.legacyPath) { prefabsDir = prefabsDir.resolve("World").resolve("Default").resolve("Prefabs"); } else { @@ -160,28 +153,41 @@ public class PrefabPropAsset extends PropAsset { } Path fullPath = PathUtil.resolvePathWithinDir(prefabsDir, path); - if (fullPath == null) { - LoggerUtil.getLogger().severe("Invalid prefab path: " + path); - return null; - } - - try { - PrefabLoader.loadAllPrefabBuffersUnder(fullPath, pathPrefabs); - } catch (Exception var9) { - String msg = "Couldn't load prefab with path: " + path; - msg = msg + "\n"; - msg = msg + ExceptionUtil.toStringWithStack(var9); - LoggerUtil.getLogger().severe(msg); - return null; + if (fullPath != null) { + try { + PrefabLoader.traverseAllPrefabBuffersUnder(fullPath, (fullPrefabPath, prefab) -> { + Path relativePrefabPath = fullPrefabPath.subpath(packRootPath.getNameCount(), fullPrefabPath.getNameCount()); + if (!traversedPaths.contains(relativePrefabPath)) { + traversedPaths.add(relativePrefabPath); + loadedPrefabs.add(prefab); + } + }); + } catch (Exception var11) { + String msg = "Couldn't load prefab with path: " + path; + msg = msg + "\n"; + msg = msg + ExceptionUtil.toStringWithStack(var11); + LoggerUtil.getLogger().severe(msg); + return null; + } } } - if (pathPrefabs.isEmpty()) { - HytaleLogger.getLogger().atWarning().log("This prefab path contains no prefabs: " + path); - return null; - } else { - return pathPrefabs; + return loadedPrefabs; + } + + @Override + public void cleanUp() { + if (this.directionalityAsset != null) { + this.directionalityAsset.cleanUp(); } + + if (this.scannerAsset != null) { + this.scannerAsset.cleanUp(); + } + + this.blockMaskAsset.cleanUp(); + this.moldingScannerAsset.cleanUp(); + this.moldingPatternAsset.cleanUp(); } public static class WeightedPathAsset implements JsonAssetWithMap> { @@ -204,7 +210,7 @@ public class PrefabPropAsset extends PropAsset { private String id; private AssetExtraInfo.Data data; private double weight = 1.0; - private String path; + private String path = ""; public String getId() { return this.id; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/DirectionalityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/DirectionalityAsset.java index f2077dc6..aeffbe3c 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/DirectionalityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/DirectionalityAsset.java @@ -9,10 +9,10 @@ import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.PropAsset; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/ImportedDirectionalityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/ImportedDirectionalityAsset.java index 4084414a..2a340608 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/ImportedDirectionalityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/ImportedDirectionalityAsset.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.directionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/PatternDirectionalityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/PatternDirectionalityAsset.java index f289af82..ddca61eb 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/PatternDirectionalityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/PatternDirectionalityAsset.java @@ -2,10 +2,11 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.direc import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.OrthogonalDirection; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.PatternDirectionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.OrthogonalDirection; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.PatternDirectionality; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -41,10 +42,18 @@ public class PatternDirectionalityAsset extends DirectionalityAsset { public Directionality build(@Nonnull DirectionalityAsset.Argument argument) { int intSeed = argument.parentSeed.child(this.seed).createSupplier().get(); OrthogonalDirection direction = this.prefabDirection; - Pattern northPattern = this.northPatternAsset == null ? Pattern.noPattern() : this.northPatternAsset.build(PatternAsset.argumentFrom(argument)); - Pattern southPattern = this.southPatternAsset == null ? Pattern.noPattern() : this.southPatternAsset.build(PatternAsset.argumentFrom(argument)); - Pattern eastPattern = this.eastPatternAsset == null ? Pattern.noPattern() : this.eastPatternAsset.build(PatternAsset.argumentFrom(argument)); - Pattern westPattern = this.westPatternAsset == null ? Pattern.noPattern() : this.westPatternAsset.build(PatternAsset.argumentFrom(argument)); + Pattern northPattern = (Pattern)(this.northPatternAsset == null + ? ConstantPattern.INSTANCE_FALSE + : this.northPatternAsset.build(PatternAsset.argumentFrom(argument))); + Pattern southPattern = (Pattern)(this.southPatternAsset == null + ? ConstantPattern.INSTANCE_FALSE + : this.southPatternAsset.build(PatternAsset.argumentFrom(argument))); + Pattern eastPattern = (Pattern)(this.eastPatternAsset == null + ? ConstantPattern.INSTANCE_FALSE + : this.eastPatternAsset.build(PatternAsset.argumentFrom(argument))); + Pattern westPattern = (Pattern)(this.westPatternAsset == null + ? ConstantPattern.INSTANCE_FALSE + : this.westPatternAsset.build(PatternAsset.argumentFrom(argument))); return new PatternDirectionality(direction, southPattern, northPattern, eastPattern, westPattern, intSeed); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/RandomDirectionalityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/RandomDirectionalityAsset.java index 4b6b4cfd..a80101ea 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/RandomDirectionalityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/RandomDirectionalityAsset.java @@ -2,8 +2,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.direc import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.RandomDirectionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.RandomDirectionality; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/StaticDirectionalityAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/StaticDirectionalityAsset.java index 9f7ae2f3..9b42f338 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/StaticDirectionalityAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/props/prefabprop/directionality/StaticDirectionalityAsset.java @@ -2,12 +2,15 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.props.prefabprop.direc import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.ConstantPatternAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.patterns.PatternAsset; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.StaticDirectionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.StaticDirectionality; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.LegacyValidator; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import javax.annotation.Nonnull; @@ -17,11 +20,17 @@ public class StaticDirectionalityAsset extends DirectionalityAsset { StaticDirectionalityAsset.class, StaticDirectionalityAsset::new, DirectionalityAsset.ABSTRACT_CODEC ) .append(new KeyedCodec<>("Rotation", Codec.INTEGER, false), (asset, v) -> asset.rotation = v, asset -> asset.rotation) - .addValidator((LegacyValidator)((v, r) -> { - if (v != 0 && v != 90 && v != 180 && v != 270) { - r.fail("Rotation can only have the values: 0, 90, 180, 270"); + .addValidator(new Validator() { + public void accept(Integer v, ValidationResults r) { + if (v != 0 && v != 90 && v != 180 && v != 270) { + r.fail("Rotation can only have the values: 0, 90, 180, 270"); + } } - })) + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }) .add() .append(new KeyedCodec<>("Pattern", PatternAsset.CODEC, true), (asset, v) -> asset.patternAsset = v, asset -> asset.patternAsset) .add() diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/AreaScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/AreaScannerAsset.java index 9892e793..3d84bcab 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/AreaScannerAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/AreaScannerAsset.java @@ -1,7 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.AreaScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.deprecated.AreaScanner; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -25,14 +26,14 @@ public class AreaScannerAsset extends ScannerAsset { private int resultCap = 1; private AreaScanner.ScanShape scanShape = AreaScanner.ScanShape.CIRCLE; private int scanRange = 0; - private ScannerAsset childScannerAsset = new OriginScannerAsset(); + private ScannerAsset childScannerAsset = new DirectScannerAsset(); @Nonnull @Override public Scanner build(@Nonnull ScannerAsset.Argument argument) { return (Scanner)(!super.skip() && this.childScannerAsset != null ? new AreaScanner(this.resultCap, this.scanShape, this.scanRange, this.childScannerAsset.build(argument)) - : Scanner.noScanner()); + : EmptyScanner.INSTANCE); } @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ColumnLinearScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ColumnLinearScannerAsset.java index 35cd6371..b3e0487e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ColumnLinearScannerAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ColumnLinearScannerAsset.java @@ -1,8 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; import com.hypixel.hytale.builtin.hytalegenerator.assets.framework.DecimalConstantsFrameworkAsset; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.ColumnLinearScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.deprecated.ColumnLinearScanner; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -39,7 +40,7 @@ public class ColumnLinearScannerAsset extends ScannerAsset { @Override public Scanner build(@Nonnull ScannerAsset.Argument argument) { if (super.skip()) { - return Scanner.noScanner(); + return EmptyScanner.INSTANCE; } else if (this.isRelativeToPosition) { return new ColumnLinearScanner(this.minY, this.maxY, this.resultCap, this.topDownOrder, true, 0.0); } else { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ColumnRandomScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ColumnRandomScannerAsset.java index d6df6200..c43f0816 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ColumnRandomScannerAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ColumnRandomScannerAsset.java @@ -2,9 +2,10 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; import com.hypixel.hytale.builtin.hytalegenerator.assets.ValidatorUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.framework.DecimalConstantsFrameworkAsset; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.ColumnRandomScanner; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.deprecated.ColumnRandomScanner; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; @@ -45,7 +46,7 @@ public class ColumnRandomScannerAsset extends ScannerAsset { @Override public Scanner build(@Nonnull ScannerAsset.Argument argument) { if (super.skip()) { - return Scanner.noScanner(); + return EmptyScanner.INSTANCE; } else { SeedBox childSeed = argument.parentSeed.child(this.seed); ColumnRandomScanner.Strategy strategy = ColumnRandomScanner.Strategy.valueOf(this.strategyName); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/DirectScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/DirectScannerAsset.java new file mode 100644 index 00000000..cba1cf6b --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/DirectScannerAsset.java @@ -0,0 +1,21 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.scanners.DirectScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class DirectScannerAsset extends ScannerAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + DirectScannerAsset.class, DirectScannerAsset::new, ScannerAsset.ABSTRACT_CODEC + ) + .build(); + + @Nonnull + @Override + public Scanner build(@Nonnull ScannerAsset.Argument argument) { + return (Scanner)(super.skip() ? EmptyScanner.INSTANCE : new DirectScanner()); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ImportedScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ImportedScannerAsset.java index 1dcaac24..a95ddc0f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ImportedScannerAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ImportedScannerAsset.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -20,13 +21,13 @@ public class ImportedScannerAsset extends ScannerAsset { @Override public Scanner build(@Nonnull ScannerAsset.Argument argument) { if (super.skip()) { - return Scanner.noScanner(); + return EmptyScanner.INSTANCE; } else if (this.name != null && !this.name.isEmpty()) { ScannerAsset exportedAsset = ScannerAsset.getExportedAsset(this.name); - return exportedAsset == null ? Scanner.noScanner() : exportedAsset.build(argument); + return (Scanner)(exportedAsset == null ? EmptyScanner.INSTANCE : exportedAsset.build(argument)); } else { HytaleLogger.getLogger().atWarning().log("An exported Pattern with the name does not exist: " + this.name); - return Scanner.noScanner(); + return EmptyScanner.INSTANCE; } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/LinearScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/LinearScannerAsset.java new file mode 100644 index 00000000..d271d09a --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/LinearScannerAsset.java @@ -0,0 +1,46 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.delimiters.RangeIntAsset; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.LinearScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.math.Axis; +import javax.annotation.Nonnull; + +public class LinearScannerAsset extends ScannerAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + LinearScannerAsset.class, LinearScannerAsset::new, ScannerAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Axis", new EnumCodec<>(Axis.class), true), (asset, value) -> asset.axis = value, asset -> asset.axis) + .add() + .append(new KeyedCodec<>("Range", RangeIntAsset.CODEC, true), (asset, value) -> asset.rangeAsset = value, asset -> asset.rangeAsset) + .add() + .append(new KeyedCodec<>("Scanner", ScannerAsset.CODEC, false), (asset, value) -> asset.scannerAsset = value, asset -> asset.scannerAsset) + .add() + .append(new KeyedCodec<>("AscendingOrder", Codec.BOOLEAN, false), (asset, value) -> asset.isAscendingOrder = value, asset -> asset.isAscendingOrder) + .add() + .build(); + @Nonnull + private Axis axis = Axis.Y; + @Nonnull + private RangeIntAsset rangeAsset = new RangeIntAsset(); + @Nonnull + private ScannerAsset scannerAsset = new DirectScannerAsset(); + private boolean isAscendingOrder = false; + + @Nonnull + @Override + public Scanner build(@Nonnull ScannerAsset.Argument argument) { + if (super.skip()) { + return EmptyScanner.INSTANCE; + } else { + Scanner childScanner = this.scannerAsset.build(argument); + return new LinearScanner(this.axis, this.rangeAsset.build(), childScanner, this.isAscendingOrder); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/OriginScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/OriginScannerAsset.java deleted file mode 100644 index 600b9878..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/OriginScannerAsset.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; - -import com.hypixel.hytale.builtin.hytalegenerator.scanners.OriginScanner; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import javax.annotation.Nonnull; - -public class OriginScannerAsset extends ScannerAsset { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder( - OriginScannerAsset.class, OriginScannerAsset::new, ScannerAsset.ABSTRACT_CODEC - ) - .build(); - - @Nonnull - @Override - public Scanner build(@Nonnull ScannerAsset.Argument argument) { - return (Scanner)(super.skip() ? Scanner.noScanner() : OriginScanner.getInstance()); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/QueueScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/QueueScannerAsset.java new file mode 100644 index 00000000..5d3da628 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/QueueScannerAsset.java @@ -0,0 +1,43 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.QueueScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; + +public class QueueScannerAsset extends ScannerAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + QueueScannerAsset.class, QueueScannerAsset::new, ScannerAsset.ABSTRACT_CODEC + ) + .append( + new KeyedCodec<>("Scanners", new ArrayCodec<>(ScannerAsset.CODEC, ScannerAsset[]::new), true), + (asset, value) -> asset.scannerAssets = value, + asset -> asset.scannerAssets + ) + .add() + .build(); + @Nonnull + private ScannerAsset[] scannerAssets = new ScannerAsset[0]; + + @Nonnull + @Override + public Scanner build(@Nonnull ScannerAsset.Argument argument) { + if (super.skip()) { + return EmptyScanner.INSTANCE; + } else { + List scanners = new ArrayList<>(this.scannerAssets.length); + + for (ScannerAsset scannerAsset : this.scannerAssets) { + scanners.add(scannerAsset.build(argument)); + } + + return new QueueScanner(scanners); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/RadialScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/RadialScannerAsset.java new file mode 100644 index 00000000..67a3aea8 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/RadialScannerAsset.java @@ -0,0 +1,36 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.bounds.IntegerBounds3dAsset; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.RadialScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public class RadialScannerAsset extends ScannerAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + RadialScannerAsset.class, RadialScannerAsset::new, ScannerAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Bounds", IntegerBounds3dAsset.CODEC, true), (asset, value) -> asset.boundsAsset = value, asset -> asset.boundsAsset) + .add() + .append(new KeyedCodec<>("Scanner", ScannerAsset.CODEC, false), (asset, value) -> asset.scannerAsset = value, asset -> asset.scannerAsset) + .add() + .build(); + @Nonnull + private IntegerBounds3dAsset boundsAsset = new IntegerBounds3dAsset(); + @Nonnull + private ScannerAsset scannerAsset = new DirectScannerAsset(); + + @Nonnull + @Override + public Scanner build(@Nonnull ScannerAsset.Argument argument) { + if (super.skip()) { + return EmptyScanner.INSTANCE; + } else { + Scanner childScanner = this.scannerAsset.build(argument); + return new RadialScanner(this.boundsAsset.build(), childScanner); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/RandomScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/RandomScannerAsset.java new file mode 100644 index 00000000..e21d883d --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/RandomScannerAsset.java @@ -0,0 +1,49 @@ +package com.hypixel.hytale.builtin.hytalegenerator.assets.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.assets.delimiters.RangeIntAsset; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.EmptyScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.RandomScanner; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.math.Axis; +import javax.annotation.Nonnull; + +public class RandomScannerAsset extends ScannerAsset { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + RandomScannerAsset.class, RandomScannerAsset::new, ScannerAsset.ABSTRACT_CODEC + ) + .append(new KeyedCodec<>("Axis", new EnumCodec<>(Axis.class), true), (asset, value) -> asset.axis = value, asset -> asset.axis) + .add() + .append(new KeyedCodec<>("Range", RangeIntAsset.CODEC, true), (asset, value) -> asset.rangeAsset = value, asset -> asset.rangeAsset) + .add() + .append(new KeyedCodec<>("Scanner", ScannerAsset.CODEC, false), (asset, value) -> asset.scannerAsset = value, asset -> asset.scannerAsset) + .add() + .append(new KeyedCodec<>("Seed", Codec.STRING, true), (asset, value) -> asset.seed = value, asset -> asset.seed) + .add() + .build(); + @Nonnull + private Axis axis = Axis.Y; + @Nonnull + private RangeIntAsset rangeAsset = new RangeIntAsset(); + @Nonnull + private ScannerAsset scannerAsset = new DirectScannerAsset(); + @Nonnull + private String seed = ""; + + @Nonnull + @Override + public Scanner build(@Nonnull ScannerAsset.Argument argument) { + if (super.skip()) { + return EmptyScanner.INSTANCE; + } else { + Scanner childScanner = this.scannerAsset.build(argument); + SeedBox seedBox = argument.parentSeed.child(this.seed); + return new RandomScanner(this.axis, this.rangeAsset.build(), childScanner, seedBox.createSupplier().get()); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ScannerAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ScannerAsset.java index 212f826d..4b479559 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ScannerAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/scanners/ScannerAsset.java @@ -9,8 +9,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.assets.props.PropAsset; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/terrains/DensityTerrainAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/terrains/DensityTerrainAsset.java index 2a64f3a4..e0d69ea1 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/terrains/DensityTerrainAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/terrains/DensityTerrainAsset.java @@ -4,8 +4,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.assets.density.ConstantDensity import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/terrains/TerrainAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/terrains/TerrainAsset.java index f4cb7a1b..49b0c8a6 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/terrains/TerrainAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/terrains/TerrainAsset.java @@ -8,8 +8,8 @@ import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/tintproviders/TintProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/tintproviders/TintProviderAsset.java index 74f6f9b7..6503edef 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/tintproviders/TintProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/tintproviders/TintProviderAsset.java @@ -9,9 +9,9 @@ import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.builtin.hytalegenerator.tintproviders.TintProvider; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/CacheVectorProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/CacheVectorProviderAsset.java index a482d9ef..a314b791 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/CacheVectorProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/CacheVectorProviderAsset.java @@ -5,8 +5,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.ConstantVector import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CacheVectorProviderAsset extends VectorProviderAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ConstantVectorProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ConstantVectorProviderAsset.java index e69d4d73..30c5cafa 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ConstantVectorProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ConstantVectorProviderAsset.java @@ -4,15 +4,16 @@ import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.ConstantVector import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ConstantVectorProviderAsset extends VectorProviderAsset { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( ConstantVectorProviderAsset.class, ConstantVectorProviderAsset::new, ABSTRACT_CODEC ) - .append(new KeyedCodec<>("Value", Vector3d.CODEC, true), (asset, value) -> asset.value = value, asset -> asset.value) + .append(new KeyedCodec<>("Value", Vector3dUtil.CODEC, true), (asset, value) -> asset.value = value, asset -> asset.value) .add() .build(); private Vector3d value = new Vector3d(); @@ -21,7 +22,7 @@ public class ConstantVectorProviderAsset extends VectorProviderAsset { } public ConstantVectorProviderAsset(@Nonnull Vector3d vector) { - this.value.assign(vector); + this.value.set(vector); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/DensityGradientVectorProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/DensityGradientVectorProviderAsset.java index 17047208..1770b6b1 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/DensityGradientVectorProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/DensityGradientVectorProviderAsset.java @@ -9,8 +9,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DensityGradientVectorProviderAsset extends VectorProviderAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ExportedVectorProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ExportedVectorProviderAsset.java index 038ceb07..625504d2 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ExportedVectorProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ExportedVectorProviderAsset.java @@ -6,8 +6,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ExportedVectorProviderAsset extends VectorProviderAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ImportedVectorProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ImportedVectorProviderAsset.java index 237a3ff9..15c0b74c 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ImportedVectorProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/ImportedVectorProviderAsset.java @@ -6,8 +6,8 @@ import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ImportedVectorProviderAsset extends VectorProviderAsset { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/VectorProviderAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/VectorProviderAsset.java index 36af173b..b59446b4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/VectorProviderAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/vectorproviders/VectorProviderAsset.java @@ -9,9 +9,9 @@ import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.assets.density.DensityAsset; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/worldstructures/WorldStructureAsset.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/worldstructures/WorldStructureAsset.java index 2ef58d1b..f6bb63ff 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/assets/worldstructures/WorldStructureAsset.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assets/worldstructures/WorldStructureAsset.java @@ -8,8 +8,8 @@ import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.builtin.hytalegenerator.assets.Cleanable; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.PositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/Assignments.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/Assignments.java similarity index 58% rename from src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/Assignments.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assignments/Assignments.java index 96050fcf..60113bf0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/Assignments.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/Assignments.java @@ -1,37 +1,31 @@ -package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; +package com.hypixel.hytale.builtin.hytalegenerator.assignments; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class Assignments { public abstract Prop propAt(@Nonnull Vector3d var1, @Nonnull WorkerIndexer.Id var2, double var3); - public abstract int getRuntime(); - public abstract List getAllPossibleProps(); @Nonnull - public static Assignments noPropDistribution(final int runtime) { + public static Assignments noPropDistribution() { return new Assignments() { @Nonnull @Override public Prop propAt(@Nonnull Vector3d position, @Nonnull WorkerIndexer.Id id, double distanceTOBiomeEdge) { - return Prop.noProp(); - } - - @Override - public int getRuntime() { - return runtime; + return EmptyProp.INSTANCE; } @Nonnull @Override public List getAllPossibleProps() { - return Collections.singletonList(Prop.noProp()); + return Collections.singletonList(EmptyProp.INSTANCE); } }; } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/ConstantAssignments.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/ConstantAssignments.java similarity index 60% rename from src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/ConstantAssignments.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assignments/ConstantAssignments.java index 852da5e6..694e832d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/ConstantAssignments.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/ConstantAssignments.java @@ -1,20 +1,18 @@ -package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; +package com.hypixel.hytale.builtin.hytalegenerator.assignments; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ConstantAssignments extends Assignments { @Nonnull private final Prop prop; - private final int runtime; - public ConstantAssignments(@Nonnull Prop prop, int runtime) { + public ConstantAssignments(@Nonnull Prop prop) { this.prop = prop; - this.runtime = runtime; } @Nonnull @@ -23,11 +21,6 @@ public class ConstantAssignments extends Assignments { return this.prop; } - @Override - public int getRuntime() { - return this.runtime; - } - @Nonnull @Override public List getAllPossibleProps() { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/FieldFunctionAssignments.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/FieldFunctionAssignments.java similarity index 50% rename from src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/FieldFunctionAssignments.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assignments/FieldFunctionAssignments.java index 0b85a8d6..2f996ccd 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/FieldFunctionAssignments.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/FieldFunctionAssignments.java @@ -1,57 +1,53 @@ -package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; +package com.hypixel.hytale.builtin.hytalegenerator.assignments; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class FieldFunctionAssignments extends Assignments { @Nonnull private final Density density; @Nonnull - private final List fieldDelimiters; - private final int runtime; + private final List delimiters; + @Nonnull + private final Density.Context rDensityContext; - public FieldFunctionAssignments(@Nonnull Density functionTree, @Nonnull List fieldDelimiters, int runtime) { - this.runtime = runtime; + public FieldFunctionAssignments(@Nonnull Density functionTree, @Nonnull List delimiters) { this.density = functionTree; - this.fieldDelimiters = new ArrayList<>(fieldDelimiters); + this.delimiters = new ArrayList<>(delimiters); + this.rDensityContext = new Density.Context(); } @Override - public Prop propAt(@Nonnull Vector3d position, @Nonnull WorkerIndexer.Id id, double distanceTOBiomeEdge) { - if (this.fieldDelimiters.isEmpty()) { - return Prop.noProp(); + public Prop propAt(@Nonnull Vector3d position, @Nonnull WorkerIndexer.Id id, double distanceFromBiomeEdge) { + if (this.delimiters.isEmpty()) { + return EmptyProp.INSTANCE; } else { - Density.Context context = new Density.Context(); - context.position = position; - context.distanceToBiomeEdge = distanceTOBiomeEdge; - double fieldValue = this.density.process(context); + this.rDensityContext.position.set(position); + this.rDensityContext.distanceToBiomeEdge = distanceFromBiomeEdge; + double fieldValue = this.density.process(this.rDensityContext); - for (FieldFunctionAssignments.FieldDelimiter fd : this.fieldDelimiters) { - if (fd.isInside(fieldValue)) { - return fd.assignments.propAt(position, id, distanceTOBiomeEdge); + for (FieldFunctionAssignments.FieldDelimiter delimiter : this.delimiters) { + if (delimiter.isInside(fieldValue)) { + return delimiter.assignments.propAt(position, id, distanceFromBiomeEdge); } } - return Prop.noProp(); + return EmptyProp.INSTANCE; } } - @Override - public int getRuntime() { - return this.runtime; - } - @Nonnull @Override public List getAllPossibleProps() { ArrayList list = new ArrayList<>(); - for (FieldFunctionAssignments.FieldDelimiter f : this.fieldDelimiters) { + for (FieldFunctionAssignments.FieldDelimiter f : this.delimiters) { list.addAll(f.assignments.getAllPossibleProps()); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/SandwichAssignments.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/SandwichAssignments.java similarity index 55% rename from src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/SandwichAssignments.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assignments/SandwichAssignments.java index 58e28253..2ad59295 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/SandwichAssignments.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/SandwichAssignments.java @@ -1,48 +1,42 @@ -package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; +package com.hypixel.hytale.builtin.hytalegenerator.assignments; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SandwichAssignments extends Assignments { @Nonnull - private final List verticalDelimiters; - private final int runtime; + private final List delimiters; - public SandwichAssignments(@Nonnull List verticalDelimiters, int runtime) { - this.runtime = runtime; - this.verticalDelimiters = new ArrayList<>(verticalDelimiters); + public SandwichAssignments(@Nonnull List delimiters) { + this.delimiters = new ArrayList<>(delimiters); } @Override public Prop propAt(@Nonnull Vector3d position, @Nonnull WorkerIndexer.Id id, double distanceTOBiomeEdge) { - if (this.verticalDelimiters.isEmpty()) { - return Prop.noProp(); + if (this.delimiters.isEmpty()) { + return EmptyProp.INSTANCE; } else { - for (SandwichAssignments.VerticalDelimiter fd : this.verticalDelimiters) { - if (fd.isInside(position.y)) { - return fd.assignments.propAt(position, id, distanceTOBiomeEdge); + for (SandwichAssignments.VerticalDelimiter delimiter : this.delimiters) { + if (delimiter.isInside(position.y)) { + return delimiter.assignments.propAt(position, id, distanceTOBiomeEdge); } } - return Prop.noProp(); + return EmptyProp.INSTANCE; } } - @Override - public int getRuntime() { - return this.runtime; - } - @Nonnull @Override public List getAllPossibleProps() { ArrayList list = new ArrayList<>(); - for (SandwichAssignments.VerticalDelimiter f : this.verticalDelimiters) { + for (SandwichAssignments.VerticalDelimiter f : this.delimiters) { list.addAll(f.assignments.getAllPossibleProps()); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/WeightedAssignments.java b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/WeightedAssignments.java similarity index 50% rename from src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/WeightedAssignments.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/assignments/WeightedAssignments.java index 93ccac8c..f3c91515 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/WeightedAssignments.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/assignments/WeightedAssignments.java @@ -1,48 +1,44 @@ -package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; +package com.hypixel.hytale.builtin.hytalegenerator.assignments; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector3d; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class WeightedAssignments extends Assignments { @Nonnull private final WeightedMap weightedDistributions; @Nonnull - private final SeedGenerator seedGenerator; - private final int runtime; + private final RngField rngField; private final double noneProbability; + @Nonnull + private final FastRandom rRandom; - public WeightedAssignments(@Nonnull WeightedMap props, int seed, double noneProbability, int runtime) { + public WeightedAssignments(@Nonnull WeightedMap props, int seed, double noneProbability) { this.weightedDistributions = new WeightedMap<>(props); - this.runtime = runtime; - this.seedGenerator = new SeedGenerator(seed); + this.rngField = new RngField(seed); this.noneProbability = noneProbability; + this.rRandom = new FastRandom(); } @Override public Prop propAt(@Nonnull Vector3d position, @Nonnull WorkerIndexer.Id id, double distanceTOBiomeEdge) { if (this.weightedDistributions.size() == 0) { - return Prop.noProp(); + return EmptyProp.INSTANCE; } else { - long x = (long)(position.x * 10000.0); - long y = (long)(position.y * 10000.0); - long z = (long)(position.z * 10000.0); - FastRandom rand = new FastRandom(this.seedGenerator.seedAt(x, y, z)); - return rand.nextDouble() < this.noneProbability ? Prop.noProp() : this.weightedDistributions.pick(rand).propAt(position, id, distanceTOBiomeEdge); + this.rRandom.setSeed(this.rngField.get(position.x, position.y, position.z)); + return (Prop)(this.rRandom.nextDouble() < this.noneProbability + ? EmptyProp.INSTANCE + : this.weightedDistributions.pick(this.rRandom).propAt(position, id, distanceTOBiomeEdge)); } } - @Override - public int getRuntime() { - return this.runtime; - } - @Nonnull @Override public List getAllPossibleProps() { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/biome/Biome.java b/src/com/hypixel/hytale/builtin/hytalegenerator/biome/Biome.java index 68caea26..daadaf5b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/biome/Biome.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/biome/Biome.java @@ -4,8 +4,6 @@ import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import javax.annotation.Nonnull; public interface Biome extends MaterialSource, PropsSource, EnvironmentSource, TintSource { - String getBiomeName(); - @Nonnull Density getTerrainDensity(); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/biome/PropsSource.java b/src/com/hypixel/hytale/builtin/hytalegenerator/biome/PropsSource.java index 36b22198..42821381 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/biome/PropsSource.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/biome/PropsSource.java @@ -1,11 +1,12 @@ package com.hypixel.hytale.builtin.hytalegenerator.biome; -import com.hypixel.hytale.builtin.hytalegenerator.PropField; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; +import com.hypixel.hytale.builtin.hytalegenerator.PropRuntime; import java.util.List; +import java.util.function.Consumer; +import javax.annotation.Nonnull; public interface PropsSource { - List getPropFields(); + void getRuntimesWithIndex(int var1, @Nonnull Consumer var2); - List getAllPropDistributions(); + List getPropRuntimes(); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/biome/SimpleBiome.java b/src/com/hypixel/hytale/builtin/hytalegenerator/biome/SimpleBiome.java index a37faaa5..ea115bb4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/biome/SimpleBiome.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/biome/SimpleBiome.java @@ -1,15 +1,16 @@ package com.hypixel.hytale.builtin.hytalegenerator.biome; -import com.hypixel.hytale.builtin.hytalegenerator.PropField; +import com.hypixel.hytale.builtin.hytalegenerator.PropRuntime; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.environmentproviders.EnvironmentProvider; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; -import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.Assignments; import com.hypixel.hytale.builtin.hytalegenerator.tintproviders.TintProvider; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class SimpleBiome implements Biome { @Nonnull @@ -17,7 +18,7 @@ public class SimpleBiome implements Biome { @Nonnull private final MaterialProvider materialProvider; @Nonnull - private final List propFields; + private final List propRuntimes; @Nonnull private final EnvironmentProvider environmentProvider; @Nonnull @@ -35,13 +36,13 @@ public class SimpleBiome implements Biome { this.terrainDensity = terrainDensity; this.materialProvider = materialProvider; this.biomeName = biomeName; - this.propFields = new ArrayList<>(); + this.propRuntimes = new ArrayList<>(); this.environmentProvider = environmentProvider; this.tintProvider = tintProvider; } - public void addPropFieldTo(@Nonnull PropField propField) { - this.propFields.add(propField); + public void addPropFieldTo(@Nonnull PropRuntime propRuntime) { + this.propRuntimes.add(propRuntime); } @Nonnull @@ -56,16 +57,19 @@ public class SimpleBiome implements Biome { return this.terrainDensity; } - @Nonnull @Override - public String getBiomeName() { - return this.biomeName; + public void getRuntimesWithIndex(int runtimeIndex, @NonNullDecl Consumer out) { + for (PropRuntime runtime : this.propRuntimes) { + if (runtime.getRuntimeIndex() == runtimeIndex) { + out.accept(runtime); + } + } } @Nonnull @Override - public List getPropFields() { - return this.propFields; + public List getPropRuntimes() { + return this.propRuntimes; } @Nonnull @@ -79,16 +83,4 @@ public class SimpleBiome implements Biome { public TintProvider getTintProvider() { return this.tintProvider; } - - @Nonnull - @Override - public List getAllPropDistributions() { - ArrayList list = new ArrayList<>(); - - for (PropField f : this.propFields) { - list.add(f.getPropDistribution()); - } - - return list; - } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/Bounds3d.java b/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/Bounds3d.java index de189a44..167142f7 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/Bounds3d.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/Bounds3d.java @@ -1,25 +1,48 @@ package com.hypixel.hytale.builtin.hytalegenerator.bounds; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; public class Bounds3d implements MemInstrument { + @Nonnull + public static final Bounds3d ZERO = new Bounds3d(); @Nonnull public final Vector3d min; @Nonnull public final Vector3d max; public Bounds3d() { - this(Vector3d.ZERO, Vector3d.ZERO); + this(Vector3dUtil.ZERO, Vector3dUtil.ZERO); } - public Bounds3d(@Nonnull Vector3d min, @Nonnull Vector3d max) { - this.min = min.clone(); - this.max = max.clone(); + public Bounds3d(@Nonnull Vector3dc min, @Nonnull Vector3dc max) { + this.min = new Vector3d(min); + this.max = new Vector3d(max); this.correct(); } + public boolean contains(int x, int y, int z) { + return x >= this.min.x && y >= this.min.y && z >= this.min.z && x < this.max.x && y < this.max.y && z < this.max.z; + } + + public boolean contains(double x, double y, double z) { + return x >= this.min.x && y >= this.min.y && z >= this.min.z && x < this.max.x && y < this.max.y && z < this.max.z; + } + + public boolean contains(@Nonnull Vector3i position) { + return position.x >= this.min.x + && position.y >= this.min.y + && position.z >= this.min.z + && position.x < this.max.x + && position.y < this.max.y + && position.z < this.max.z; + } + public boolean contains(@Nonnull Vector3d position) { return position.x >= this.min.x && position.y >= this.min.y @@ -53,21 +76,29 @@ public class Bounds3d implements MemInstrument { @Nonnull public Vector3d getSize() { - return this.max.clone().subtract(this.min); + return new Vector3d(this.max).sub(this.min); } @Nonnull public Bounds3d assign(@Nonnull Bounds3d other) { - this.min.assign(other.min); - this.max.assign(other.max); + this.min.set(other.min); + this.max.set(other.max); + this.correct(); + return this; + } + + @Nonnull + public Bounds3d assign(@Nonnull Bounds3i other) { + this.min.set(other.min); + this.max.set(other.max); this.correct(); return this; } @Nonnull public Bounds3d assign(@Nonnull Vector3d min, @Nonnull Vector3d max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); this.correct(); return this; } @@ -79,15 +110,22 @@ public class Bounds3d implements MemInstrument { return this; } + @Nonnull + public Bounds3d offsetOpposite(@Nonnull Vector3d vector) { + this.min.sub(vector); + this.max.sub(vector); + return this; + } + @Nonnull public Bounds3d intersect(@Nonnull Bounds3d other) { if (!this.intersects(other)) { - this.min.assign(Vector3d.ZERO); - this.max.assign(Vector3d.ZERO); + this.min.zero(); + this.max.zero(); } - this.min.assign(Math.max(this.min.x, other.min.x), Math.max(this.min.y, other.min.y), Math.max(this.min.z, other.min.z)); - this.max.assign(Math.min(this.max.x, other.max.x), Math.min(this.max.y, other.max.y), Math.min(this.max.z, other.max.z)); + this.min.set(Math.max(this.min.x, other.min.x), Math.max(this.min.y, other.min.y), Math.max(this.min.z, other.min.z)); + this.max.set(Math.min(this.max.x, other.max.x), Math.min(this.max.y, other.max.y), Math.min(this.max.z, other.max.z)); return this; } @@ -96,27 +134,27 @@ public class Bounds3d implements MemInstrument { if (other.isZeroVolume()) { return this; } else if (this.isZeroVolume()) { - this.min.assign(other.min); - this.max.assign(other.max); + this.min.set(other.min); + this.max.set(other.max); return this; } else { - this.min.assign(Math.min(this.min.x, other.min.x), Math.min(this.min.y, other.min.y), Math.min(this.min.z, other.min.z)); - this.max.assign(Math.max(this.max.x, other.max.x), Math.max(this.max.y, other.max.y), Math.max(this.max.z, other.max.z)); + this.min.set(Math.min(this.min.x, other.min.x), Math.min(this.min.y, other.min.y), Math.min(this.min.z, other.min.z)); + this.max.set(Math.max(this.max.x, other.max.x), Math.max(this.max.y, other.max.y), Math.max(this.max.z, other.max.z)); return this; } } @Nonnull public Bounds3d encompass(@Nonnull Vector3d position) { - this.min.assign(Math.min(this.min.x, position.x), Math.min(this.min.y, position.y), Math.min(this.min.z, position.z)); - this.max.assign(Math.max(this.max.x, position.x), Math.max(this.max.y, position.y), Math.max(this.max.z, position.z)); + this.min.set(Math.min(this.min.x, position.x), Math.min(this.min.y, position.y), Math.min(this.min.z, position.z)); + this.max.set(Math.max(this.max.x, position.x), Math.max(this.max.y, position.y), Math.max(this.max.z, position.z)); return this; } @Nonnull public Bounds3d stack(@Nonnull Bounds3d other) { if (!this.isZeroVolume() && !other.isZeroVolume()) { - Vector3d initialMax = this.max.clone(); + Vector3d initialMax = new Vector3d(this.max); Bounds3d stamp = other.clone(); stamp.offset(this.min); this.encompass(stamp); @@ -131,27 +169,65 @@ public class Bounds3d implements MemInstrument { @Nonnull public Bounds3d flipOnOriginPoint() { - Vector3d swap = this.min.clone(); - this.min.assign(this.max); - this.min.scale(-1.0); - this.max.assign(swap); - this.max.scale(-1.0); - return this; + if (this.isZeroVolume()) { + return this; + } else { + Vector3d swap = new Vector3d(this.min); + this.min.set(this.max); + this.min.mul(-1.0); + this.max.set(swap); + this.max.mul(-1.0); + return this; + } } @Nonnull public Bounds3d flipOnOriginVoxel() { - Vector3d swap = this.min.clone(); - this.min.assign(Vector3d.ALL_ONES); - this.min.subtract(this.max); - this.max.assign(Vector3d.ALL_ONES); - this.max.subtract(swap); - return this; + if (this.isZeroVolume()) { + return this; + } else { + Vector3d swap = new Vector3d(this.min); + this.min.set(Vector3dUtil.ALL_ONES); + this.min.sub(this.max); + this.max.set(Vector3dUtil.ALL_ONES); + this.max.sub(swap); + return this; + } + } + + public Bounds3d applyRotation(@Nonnull RotationTuple rotationTuple, @Nonnull Vector3d anchor) { + if (this.isZeroVolume()) { + return this; + } else { + this.min.sub(anchor); + rotationTuple.applyRotationTo(this.min); + this.min.add(anchor); + this.max.sub(anchor); + rotationTuple.applyRotationTo(this.max); + this.max.add(anchor); + this.correct(); + return this; + } + } + + public Bounds3d undoRotation(@Nonnull RotationTuple rotationTuple, @Nonnull Vector3d anchor) { + if (this.isZeroVolume()) { + return this; + } else { + this.min.sub(anchor); + rotationTuple.undoRotationTo(this.min); + this.min.add(anchor); + this.max.sub(anchor); + rotationTuple.undoRotationTo(this.max); + this.max.add(anchor); + this.correct(); + return this; + } } @Nonnull public Bounds3d clone() { - return new Bounds3d(this.min.clone(), this.max.clone()); + return new Bounds3d(new Vector3d(this.min), new Vector3d(this.max)); } public boolean isCorrect() { @@ -159,9 +235,9 @@ public class Bounds3d implements MemInstrument { } public void correct() { - Vector3d swap = this.min.clone(); - this.min.assign(Math.min(this.max.x, this.min.x), Math.min(this.max.y, this.min.y), Math.min(this.max.z, this.min.z)); - this.max.assign(Math.max(swap.x, this.max.x), Math.max(swap.y, this.max.y), Math.max(swap.z, this.max.z)); + Vector3d swap = new Vector3d(this.min); + this.min.set(Math.min(this.max.x, this.min.x), Math.min(this.max.y, this.min.y), Math.min(this.max.z, this.min.z)); + this.max.set(Math.max(swap.x, this.max.x), Math.max(swap.y, this.max.y), Math.max(swap.z, this.max.z)); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/Bounds3i.java b/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/Bounds3i.java index 3363836a..ba3398fd 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/Bounds3i.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/Bounds3i.java @@ -1,25 +1,39 @@ package com.hypixel.hytale.builtin.hytalegenerator.bounds; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class Bounds3i implements MemInstrument { + @Nonnull + public static final Bounds3i ZERO = new Bounds3i(); @Nonnull public final Vector3i min; @Nonnull public final Vector3i max; public Bounds3i() { - this(Vector3i.ZERO, Vector3i.ZERO); + this(Vector3iUtil.ZERO, Vector3iUtil.ZERO); } - public Bounds3i(@Nonnull Vector3i min, @Nonnull Vector3i max) { - this.min = min.clone(); - this.max = max.clone(); + public Bounds3i(@Nonnull Vector3ic min, @Nonnull Vector3ic max) { + this.min = new Vector3i(min); + this.max = new Vector3i(max); this.correct(); } + public boolean contains(int x, int y, int z) { + return x >= this.min.x && y >= this.min.y && z >= this.min.z && x < this.max.x && y < this.max.y && z < this.max.z; + } + + public boolean contains(double x, double y, double z) { + return x >= this.min.x && y >= this.min.y && z >= this.min.z && x < this.max.x && y < this.max.y && z < this.max.z; + } + public boolean contains(@Nonnull Vector3i position) { return position.x >= this.min.x && position.y >= this.min.y @@ -29,6 +43,15 @@ public class Bounds3i implements MemInstrument { && position.z < this.max.z; } + public boolean contains(@Nonnull Vector3d position) { + return position.x >= this.min.x + && position.y >= this.min.y + && position.z >= this.min.z + && position.x < this.max.x + && position.y < this.max.y + && position.z < this.max.z; + } + public boolean contains(@Nonnull Bounds3i other) { return other.min.x >= this.min.x && other.min.y >= this.min.y @@ -53,25 +76,32 @@ public class Bounds3i implements MemInstrument { @Nonnull public Vector3i getSize() { - return this.max.clone().subtract(this.min); + return new Vector3i(this.max).sub(this.min); } @Nonnull public Bounds3i assign(@Nonnull Bounds3i other) { - this.min.assign(other.min); - this.max.assign(other.max); + this.min.set(other.min); + this.max.set(other.max); this.correct(); return this; } @Nonnull public Bounds3i assign(@Nonnull Vector3i min, @Nonnull Vector3i max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); this.correct(); return this; } + @Nonnull + public Bounds3i offset(int x, int y, int z) { + this.min.add(x, y, z); + this.max.add(x, y, z); + return this; + } + @Nonnull public Bounds3i offset(@Nonnull Vector3i vector) { this.min.add(vector); @@ -79,15 +109,22 @@ public class Bounds3i implements MemInstrument { return this; } + @Nonnull + public Bounds3i offsetOpposite(@Nonnull Vector3i vector) { + this.min.sub(vector); + this.max.sub(vector); + return this; + } + @Nonnull public Bounds3i intersect(@Nonnull Bounds3i other) { if (!this.intersects(other)) { - this.min.assign(Vector3i.ZERO); - this.max.assign(Vector3i.ZERO); + this.min.set(Vector3iUtil.ZERO); + this.max.set(Vector3iUtil.ZERO); } - this.min.assign(Math.max(this.min.x, other.min.x), Math.max(this.min.y, other.min.y), Math.max(this.min.z, other.min.z)); - this.max.assign(Math.min(this.max.x, other.max.x), Math.min(this.max.y, other.max.y), Math.min(this.max.z, other.max.z)); + this.min.set(Math.max(this.min.x, other.min.x), Math.max(this.min.y, other.min.y), Math.max(this.min.z, other.min.z)); + this.max.set(Math.min(this.max.x, other.max.x), Math.min(this.max.y, other.max.y), Math.min(this.max.z, other.max.z)); return this; } @@ -96,32 +133,32 @@ public class Bounds3i implements MemInstrument { if (other.isZeroVolume()) { return this; } else if (this.isZeroVolume()) { - this.min.assign(other.min); - this.max.assign(other.max); + this.min.set(other.min); + this.max.set(other.max); return this; } else { - this.min.assign(Math.min(this.min.x, other.min.x), Math.min(this.min.y, other.min.y), Math.min(this.min.z, other.min.z)); - this.max.assign(Math.max(this.max.x, other.max.x), Math.max(this.max.y, other.max.y), Math.max(this.max.z, other.max.z)); + this.min.set(Math.min(this.min.x, other.min.x), Math.min(this.min.y, other.min.y), Math.min(this.min.z, other.min.z)); + this.max.set(Math.max(this.max.x, other.max.x), Math.max(this.max.y, other.max.y), Math.max(this.max.z, other.max.z)); return this; } } @Nonnull - public Bounds3i encompass(@Nonnull Vector3i position) { - this.min.assign(Math.min(this.min.x, position.x), Math.min(this.min.y, position.y), Math.min(this.min.z, position.z)); - this.max.assign(Math.max(this.max.x, position.x), Math.max(this.max.y, position.y), Math.max(this.max.z, position.z)); + public Bounds3i encompass(@Nonnull Vector3ic position) { + this.min.set(Math.min(this.min.x, position.x()), Math.min(this.min.y, position.y()), Math.min(this.min.z, position.z())); + this.max.set(Math.max(this.max.x, position.x() + 1), Math.max(this.max.y, position.y() + 1), Math.max(this.max.z, position.z() + 1)); return this; } @Nonnull public Bounds3i stack(@Nonnull Bounds3i other) { if (!this.isZeroVolume() && !other.isZeroVolume()) { - Vector3i initialMax = this.max.clone(); + Vector3i initialMax = new Vector3i(this.max); Bounds3i stamp = other.clone(); stamp.offset(this.min); this.encompass(stamp); stamp = other.clone(); - stamp.offset(initialMax.clone().subtract(Vector3i.ALL_ONES)); + stamp.offset(new Vector3i(initialMax).sub(Vector3iUtil.ALL_ONES)); this.encompass(stamp); return this; } else { @@ -131,32 +168,74 @@ public class Bounds3i implements MemInstrument { @Nonnull public Bounds3i flipOnOriginPoint() { - Vector3i swap = this.min.clone(); - this.min.assign(this.max); - this.min.scale(-1); - this.max.assign(swap); - this.max.scale(-1); - return this; + if (this.isZeroVolume()) { + return this; + } else { + Vector3i swap = new Vector3i(this.min); + this.min.set(this.max); + this.min.mul(-1); + this.max.set(swap); + this.max.mul(-1); + return this; + } } @Nonnull public Bounds3i flipOnOriginVoxel() { - Vector3i swap = this.min.clone(); - this.min.assign(Vector3i.ALL_ONES); - this.min.subtract(this.max); - this.max.assign(Vector3i.ALL_ONES); - this.max.subtract(swap); - return this; + if (this.isZeroVolume()) { + return this; + } else { + Vector3i swap = new Vector3i(this.min); + this.min.set(Vector3iUtil.ALL_ONES); + this.min.sub(this.max); + this.max.set(Vector3iUtil.ALL_ONES); + this.max.sub(swap); + return this; + } + } + + public Bounds3i applyRotationAroundVoxel(@Nonnull RotationTuple rotationTuple, @Nonnull Vector3ic anchor) { + if (this.isZeroVolume()) { + return this; + } else { + this.max.sub(Vector3iUtil.ALL_ONES); + this.min.sub(anchor); + rotationTuple.applyRotationTo(this.min); + this.min.add(anchor); + this.max.sub(anchor); + rotationTuple.applyRotationTo(this.max); + this.max.add(anchor); + this.correct(); + this.max.add(Vector3iUtil.ALL_ONES); + return this; + } + } + + public Bounds3i undoRotationAroundVoxel(@Nonnull RotationTuple rotationTuple, @Nonnull Vector3ic anchor) { + if (this.isZeroVolume()) { + return this; + } else { + this.max.sub(Vector3iUtil.ALL_ONES); + this.min.sub(anchor); + rotationTuple.undoRotationTo(this.min); + this.min.add(anchor); + this.max.sub(anchor); + rotationTuple.undoRotationTo(this.max); + this.max.add(anchor); + this.correct(); + this.max.add(Vector3iUtil.ALL_ONES); + return this; + } } @Nonnull public Bounds3d toBounds3d() { - return new Bounds3d(this.min.toVector3d(), this.max.toVector3d()); + return new Bounds3d(Vector3iUtil.toVector3d(this.min), Vector3iUtil.toVector3d(this.max)); } @Nonnull public Bounds3i clone() { - return new Bounds3i(this.min.clone(), this.max.clone()); + return new Bounds3i(new Vector3i(this.min), new Vector3i(this.max)); } public boolean isCorrect() { @@ -164,9 +243,9 @@ public class Bounds3i implements MemInstrument { } public void correct() { - Vector3i swap = this.min.clone(); - this.min.assign(Math.min(this.max.x, this.min.x), Math.min(this.max.y, this.min.y), Math.min(this.max.z, this.min.z)); - this.max.assign(Math.max(swap.x, this.max.x), Math.max(swap.y, this.max.y), Math.max(swap.z, this.max.z)); + Vector3i swap = new Vector3i(this.min); + this.min.set(Math.min(this.max.x, this.min.x), Math.min(this.max.y, this.min.y), Math.min(this.max.z, this.min.z)); + this.max.set(Math.max(swap.x, this.max.x), Math.max(swap.y, this.max.y), Math.max(swap.z, this.max.z)); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/SpaceSize.java b/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/SpaceSize.java deleted file mode 100644 index 407504f7..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/bounds/SpaceSize.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.bounds; - -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; -import com.hypixel.hytale.math.vector.Vector3i; -import javax.annotation.Nonnull; - -@Deprecated -public class SpaceSize { - @Nonnull - private final Vector3i minInclusive; - @Nonnull - private final Vector3i maxExclusive; - @Nonnull - private final Vector3i maxInclusive; - - public SpaceSize(@Nonnull Vector3i minInclusive, @Nonnull Vector3i maxExclusive) { - this.minInclusive = minInclusive.clone(); - this.maxExclusive = maxExclusive.clone(); - this.maxInclusive = maxExclusive.clone().add(-1, -1, -1); - } - - public SpaceSize(@Nonnull Vector3i voxel) { - this(voxel.clone(), voxel.clone().add(1, 1, 1)); - } - - public SpaceSize() { - this(new Vector3i(), new Vector3i()); - } - - @Nonnull - public SpaceSize moveBy(@Nonnull Vector3i delta) { - this.minInclusive.add(delta); - this.maxExclusive.add(delta); - this.maxInclusive.add(delta); - return this; - } - - @Nonnull - public Vector3i getMinInclusive() { - return this.minInclusive.clone(); - } - - @Nonnull - public Vector3i getMaxExclusive() { - return this.maxExclusive.clone(); - } - - @Nonnull - public Vector3i getMaxInclusive() { - return this.maxInclusive.clone(); - } - - @Nonnull - public Vector3i getRange() { - Vector3i absMin = VectorUtil.fromOperation(value -> Math.abs(value.from(this.minInclusive))); - Vector3i absMax = VectorUtil.fromOperation(value -> Math.abs(value.from(this.maxInclusive))); - return Vector3i.max(absMin, absMax); - } - - @Nonnull - public Bounds3i toBounds3i() { - return new Bounds3i(this.minInclusive, this.maxExclusive); - } - - @Nonnull - public SpaceSize clone() { - return new SpaceSize(this.minInclusive, this.maxExclusive); - } - - @Nonnull - public static SpaceSize merge(@Nonnull SpaceSize a, @Nonnull SpaceSize b) { - return new SpaceSize(Vector3i.min(a.minInclusive, b.minInclusive), Vector3i.max(a.maxExclusive, b.maxExclusive)); - } - - @Nonnull - public static SpaceSize stack(@Nonnull SpaceSize a, @Nonnull SpaceSize b) { - SpaceSize aMovedToMin = a.clone().moveBy(b.minInclusive); - SpaceSize aMovedToMax = a.clone().moveBy(b.maxInclusive); - Vector3i stackedMin = Vector3i.min(aMovedToMin.minInclusive, b.minInclusive); - Vector3i stackedMax = Vector3i.max(aMovedToMax.maxExclusive, b.maxExclusive); - return new SpaceSize(stackedMin, stackedMax); - } - - @Nonnull - public static SpaceSize empty() { - return new SpaceSize(new Vector3i(), new Vector3i()); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/cartas/SimpleNoiseCarta.java b/src/com/hypixel/hytale/builtin/hytalegenerator/cartas/SimpleNoiseCarta.java index 381befe1..cc9f71b3 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/cartas/SimpleNoiseCarta.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/cartas/SimpleNoiseCarta.java @@ -1,13 +1,13 @@ package com.hypixel.hytale.builtin.hytalegenerator.cartas; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.BiCarta; import com.hypixel.hytale.builtin.hytalegenerator.rangemaps.DoubleRange; import com.hypixel.hytale.builtin.hytalegenerator.rangemaps.DoubleRangeMap; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.BiCarta; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SimpleNoiseCarta extends BiCarta { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/commands/ViewportCommand.java b/src/com/hypixel/hytale/builtin/hytalegenerator/commands/ViewportCommand.java index 74d6832b..45d69080 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/commands/ViewportCommand.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/commands/ViewportCommand.java @@ -2,14 +2,14 @@ package com.hypixel.hytale.builtin.hytalegenerator.commands; import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; +import com.hypixel.hytale.builtin.hytalegenerator.Viewport; import com.hypixel.hytale.builtin.hytalegenerator.assets.AssetManager; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.NViewport; import com.hypixel.hytale.common.util.ExceptionUtil; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -24,6 +24,8 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class ViewportCommand extends AbstractPlayerCommand { @Nonnull @@ -56,12 +58,18 @@ public class ViewportCommand extends AbstractPlayerCommand { if (context.get(this.deleteFlag)) { playerRef.sendMessage(Message.translation("server.commands.viewport.removed")); } else { - Integer radius = context.get(this.radiusArg) << 5; + Integer radius = context.get(this.radiusArg); Bounds3i viewportBounds_voxelGrid; if (radius != null) { + radius = radius << 5; Vector3d playerPosition_voxelGrid = store.getComponent(ref, TransformComponent.getComponentType()).getPosition(); - Vector3i min_voxelGrid = playerPosition_voxelGrid.clone().subtract(radius.intValue()).toVector3i(); - Vector3i max_voxelGrid = playerPosition_voxelGrid.clone().add(radius.intValue()).toVector3i().add(Vector3i.ALL_ONES); + Vector3i min_voxelGrid = Vector3dUtil.toVector3i( + new Vector3d(playerPosition_voxelGrid).sub(radius.intValue(), radius.intValue(), radius.intValue()) + ); + Vector3i max_voxelGrid = Vector3dUtil.toVector3i( + new Vector3d(playerPosition_voxelGrid).add(radius.intValue(), radius.intValue(), radius.intValue()) + ) + .add(Vector3iUtil.ALL_ONES); viewportBounds_voxelGrid = new Bounds3i(min_voxelGrid, max_voxelGrid); } else { BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef); @@ -73,7 +81,7 @@ public class ViewportCommand extends AbstractPlayerCommand { viewportBounds_voxelGrid = new Bounds3i(selection.getSelectionMin(), selection.getSelectionMax()); } - NViewport viewport = new NViewport(viewportBounds_voxelGrid, world, context.sender()); + Viewport viewport = new Viewport(viewportBounds_voxelGrid, world, context.sender()); this.activeTask = () -> world.execute(() -> { try { viewport.refresh(); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/conveyor/stagedconveyor/ContextDependency.java b/src/com/hypixel/hytale/builtin/hytalegenerator/conveyor/stagedconveyor/ContextDependency.java deleted file mode 100644 index c84653ec..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/conveyor/stagedconveyor/ContextDependency.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor; - -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import javax.annotation.Nonnull; - -public class ContextDependency { - @Nonnull - public static ContextDependency EMPTY = new ContextDependency(new Vector3i(), new Vector3i()); - @Nonnull - private final Vector3i readRange; - @Nonnull - private final Vector3i writeRange; - private Vector3i trashRange; - private Vector3i externalDependencyRange; - private Vector3i positioningRange; - - public ContextDependency(@Nonnull Vector3i readRange, @Nonnull Vector3i writeRange) { - this.readRange = readRange.clone(); - this.writeRange = writeRange.clone(); - this.update(); - } - - public ContextDependency() { - this(new Vector3i(), new Vector3i()); - } - - @Nonnull - public Bounds3i getReadBounds_voxelGrid() { - return new Bounds3i(this.getReadRange().clone().scale(-1), this.getReadRange().clone().add(Vector3i.ALL_ONES)); - } - - @Nonnull - public Bounds3i getWriteBounds_voxelGrid() { - Vector3i readMin_voxelGrid = this.getReadRange().scale(-1); - Vector3i readMax_voxelGrid = this.getReadRange().add(Vector3i.ALL_ONES); - Vector3i writeMin_voxelGrid = this.getWriteRange().scale(-1); - Vector3i writeMax_voxelGrid = this.getWriteRange().add(Vector3i.ALL_ONES); - Bounds3i readBounds_voxelGrid = new Bounds3i(readMin_voxelGrid, readMax_voxelGrid); - Bounds3i writeBounds_voxelGrid = new Bounds3i(writeMin_voxelGrid, writeMax_voxelGrid); - writeBounds_voxelGrid.stack(readBounds_voxelGrid); - return writeBounds_voxelGrid; - } - - private void update() { - this.trashRange = VectorUtil.fromOperation(this.readRange, this.writeRange, (r, w, retriever) -> r >= 0 && w >= 0 ? r + w : 0); - this.externalDependencyRange = VectorUtil.fromOperation( - this.readRange, this.writeRange, (r, w, retriever) -> r < 0 ? -w : Math.max(r, retriever.from(this.trashRange)) - ); - this.positioningRange = VectorUtil.fromOperation(this.readRange, this.writeRange, (r, w, retriever) -> r < 0 ? -w : r); - this.trashRange.y = 0; - this.externalDependencyRange.y = 0; - this.positioningRange.y = 0; - this.readRange.y = 0; - this.writeRange.y = 0; - } - - @Nonnull - public ContextDependency stackOver(@Nonnull ContextDependency other) { - new Vector3i(); - new Vector3i(); - Vector3i r1 = this.getReadRange(); - Vector3i w1 = this.getWriteRange(); - Vector3i r2 = other.getReadRange(); - Vector3i w2 = other.getWriteRange(); - Vector3i totalRead = VectorUtil.fromOperation(value -> { - if (value.from(r1) < 0 && value.from(r2) < 0) { - return -1; - } else if (value.from(r1) < 0) { - return value.from(r2); - } else { - return value.from(r2) < 0 ? value.from(r1) : value.from(r1) + value.from(w1) + value.from(r2); - } - }); - Vector3i totalWrite = VectorUtil.fromOperation(value -> { - if (value.from(r1) < 0 && value.from(r2) < 0) { - return -Math.min(value.from(w1), value.from(w2)); - } else if (value.from(r1) < 0) { - return value.from(w2); - } else { - return value.from(r2) < 0 ? value.from(w1) : value.from(w2); - } - }); - return new ContextDependency(totalRead, totalWrite); - } - - @Nonnull - public Vector3i getReadRange() { - return this.readRange.clone(); - } - - @Nonnull - public Vector3i getWriteRange() { - return this.writeRange.clone(); - } - - @Nonnull - public Vector3i getTrashRange() { - return this.trashRange.clone(); - } - - @Nonnull - public Vector3i getExternalDependencyRange() { - return this.externalDependencyRange.clone(); - } - - @Nonnull - public Vector3i getPositioningRange() { - return this.positioningRange.clone(); - } - - @Nonnull - public static Vector3i getRequiredPadOf(@Nonnull List dependencies) { - Vector3i pad = new Vector3i(); - - for (ContextDependency dependency : dependencies) { - pad.add(dependency.getExternalDependencyRange()); - } - - return pad; - } - - @Nonnull - public static Map cloneMap(@Nonnull Map map) { - HashMap out = new HashMap<>(map.size()); - map.forEach((k, v) -> out.put(k, v.clone())); - return out; - } - - @Nonnull - public static Map stackMaps(@Nonnull Map under, @Nonnull Map over) { - Map out = new HashMap<>(); - - for (Entry entry : over.entrySet()) { - if (!under.containsKey(entry.getKey())) { - out.put(entry.getKey(), entry.getValue()); - } else { - out.put(entry.getKey(), entry.getValue().stackOver(under.get(entry.getKey()))); - } - } - - for (Entry entryx : under.entrySet()) { - if (!over.containsKey(entryx.getKey())) { - out.put(entryx.getKey(), entryx.getValue()); - } - } - - return out; - } - - @Nonnull - public static ContextDependency mostOf(@Nonnull List dependencies) { - ContextDependency out = EMPTY; - - for (ContextDependency d : dependencies) { - out = mostOf(out, d); - } - - return out; - } - - @Nonnull - public static ContextDependency mostOf(@Nonnull ContextDependency a, @Nonnull ContextDependency b) { - Vector3i read = Vector3i.max(a.readRange, b.readRange); - Vector3i write = Vector3i.max(a.writeRange, b.writeRange); - return new ContextDependency(read, write); - } - - @Nonnull - public static ContextDependency from(@Nonnull Bounds3i readBounds, @Nonnull Bounds3i writeBounds) { - return new ContextDependency(rangeFromBounds(readBounds), rangeFromBounds(writeBounds)); - } - - @Nonnull - private static Vector3i rangeFromBounds(@Nonnull Bounds3i readBounds) { - Vector3i readRange = new Vector3i(); - readRange.x = Math.max(Math.abs(readBounds.min.x), Math.abs(readBounds.max.x - 1)); - readRange.y = Math.max(Math.abs(readBounds.min.y), Math.abs(readBounds.max.y - 1)); - readRange.z = Math.max(Math.abs(readBounds.min.z), Math.abs(readBounds.max.z - 1)); - return readRange; - } - - @Nonnull - public ContextDependency clone() { - return new ContextDependency(this.readRange, this.writeRange); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/CollectionFactory.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/CollectionFactory.java deleted file mode 100644 index 939874a7..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/CollectionFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures; - -import java.util.HashSet; -import java.util.Set; -import javax.annotation.Nonnull; - -public class CollectionFactory { - @Nonnull - public static Set hashSetOf(@Nonnull T... elements) { - Set set = new HashSet<>(elements.length); - - for (T element : elements) { - if (element == null) { - throw new NullPointerException("elements can't be null"); - } - - set.add(element); - } - - return set; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/TieredList.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/TieredList.java deleted file mode 100644 index 3990b983..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/TieredList.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import javax.annotation.Nonnull; - -public class TieredList { - @Nonnull - private final Map> elements; - private final int tiers; - private List sortedTierList; - - public TieredList() { - this(0); - } - - public TieredList(int tiers) { - if (tiers < 0) { - throw new IllegalArgumentException("negative number of tiers"); - } else { - this.tiers = tiers; - this.elements = new HashMap<>(); - - for (int tier = 0; tier < tiers; tier++) { - this.elements.put(tier, new ArrayList<>()); - } - - this.updateSortedTierList(); - } - } - - @Nonnull - public TieredList addTier(int tier) { - if (this.tierExists(tier)) { - throw new IllegalArgumentException("tier already exists " + tier); - } else { - this.elements.put(tier, new ArrayList<>()); - this.updateSortedTierList(); - return this; - } - } - - @Nonnull - public TieredList removeTier(int tier) { - if (!this.tierExists(tier)) { - return this; - } else { - this.elements.remove(tier); - this.updateSortedTierList(); - return this; - } - } - - public void add(@Nonnull E element, int tier) { - if (element == null) { - throw new NullPointerException(); - } else { - if (!this.tierExists(tier)) { - this.addTier(tier); - } - - this.elements.get(tier).add(element); - } - } - - public boolean isEmpty() { - for (List list : this.elements.values()) { - if (!list.isEmpty()) { - return false; - } - } - - return true; - } - - public E peek() { - for (int tier = 0; tier < this.tiers; tier++) { - List tierElements = this.elements.get(tier); - if (!tierElements.isEmpty()) { - return tierElements.getFirst(); - } - } - - throw new IllegalStateException("queue is empty"); - } - - public E remove() { - for (int tier = 0; tier < this.tiers; tier++) { - List tierElements = this.elements.get(tier); - if (!tierElements.isEmpty()) { - return tierElements.removeFirst(); - } - } - - throw new IllegalStateException("queue is empty"); - } - - public int size() { - int size = 0; - - for (List list : this.elements.values()) { - size += list.size(); - } - - return size; - } - - public int size(int tier) { - return !this.tierExists(tier) ? 0 : this.elements.get(tier).size(); - } - - @Nonnull - public TieredList forEach(int tier, @Nonnull Consumer consumer) { - if (!this.tierExists(tier)) { - return this; - } else { - this.elements.get(tier).forEach(consumer); - return this; - } - } - - @Nonnull - public TieredList removeEach(int tier, @Nonnull Consumer consumer) { - if (!this.tierExists(tier)) { - return this; - } else { - for (E e : this.elements.get(tier)) { - consumer.accept(e); - } - - new ArrayList(); - return this; - } - } - - @Nonnull - public TieredList forEach(@Nonnull Consumer consumer) { - ArrayList tiers = new ArrayList<>(this.getTiers()); - tiers.sort(Comparator.naturalOrder()); - - for (int tier : tiers) { - this.forEach(tier, consumer); - } - - return this; - } - - @Nonnull - public TieredList removeEach(@Nonnull Consumer consumer) { - for (int tier : this.getTiers()) { - this.removeEach(tier, consumer); - } - - return this; - } - - @Nonnull - public Iterator iterator(int tier) { - if (!this.tierExists(tier)) { - throw new IllegalArgumentException("tier doesn't exist"); - } else { - return this.elements.get(tier).iterator(); - } - } - - @Nonnull - public List listOf(int tier) { - if (!this.tierExists(tier)) { - throw new IllegalArgumentException("tier doesn't exist"); - } else { - return Collections.unmodifiableList(this.elements.get(tier)); - } - } - - public boolean tierExists(int tier) { - return this.elements.containsKey(tier); - } - - public List getTiers() { - return this.sortedTierList; - } - - private void updateSortedTierList() { - List tierList = new ArrayList<>(this.elements.keySet()); - tierList.sort(Comparator.naturalOrder()); - tierList = Collections.unmodifiableList(tierList); - this.sortedTierList = tierList; - } - - @Nonnull - @Override - public String toString() { - return "TieredList{elements=" + this.elements + ", tiers=" + this.tiers + ", sortedTierList=" + this.sortedTierList + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/BiCoordinateCache.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/BiCoordinateCache.java deleted file mode 100644 index 359750df..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/BiCoordinateCache.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.bicoordinatecache; - -public interface BiCoordinateCache { - T get(int var1, int var2); - - boolean isCached(int var1, int var2); - - T save(int var1, int var2, T var3); - - void flush(int var1, int var2); - - void flush(); - - int size(); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/HashedBiCoordinateCache.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/HashedBiCoordinateCache.java deleted file mode 100644 index c1c9199f..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/HashedBiCoordinateCache.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.bicoordinatecache; - -import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nonnull; - -public class HashedBiCoordinateCache implements BiCoordinateCache { - @Nonnull - private final ConcurrentHashMap values = new ConcurrentHashMap<>(); - - public static long hash(int x, int z) { - long hash = x; - hash <<= 32; - return hash + z; - } - - @Override - public T get(int x, int z) { - long key = hash(x, z); - if (!this.values.containsKey(key)) { - throw new IllegalStateException("doesn't contain coordinates"); - } else { - return this.values.get(key); - } - } - - @Override - public boolean isCached(int x, int z) { - return this.values.containsKey(hash(x, z)); - } - - @Nonnull - @Override - public T save(int x, int z, @Nonnull T value) { - long key = hash(x, z); - this.values.put(key, value); - return value; - } - - @Override - public void flush(int x, int z) { - long key = hash(x, z); - if (this.values.containsKey(key)) { - this.values.remove(key); - } - } - - @Override - public void flush() { - for (long key : this.values.keySet()) { - this.values.remove(key); - } - } - - @Override - public int size() { - return this.values.size(); - } - - @Nonnull - @Override - public String toString() { - return "HashedBiCoordinateCache{values=" + this.values + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/WrappedBiCoordinateCache.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/WrappedBiCoordinateCache.java deleted file mode 100644 index 67394917..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/WrappedBiCoordinateCache.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.bicoordinatecache; - -import java.util.Arrays; -import javax.annotation.Nonnull; - -public class WrappedBiCoordinateCache implements BiCoordinateCache { - private final int sizeX; - private final int sizeZ; - @Nonnull - private final T[][] values; - @Nonnull - private final boolean[][] populated; - private int size; - - public WrappedBiCoordinateCache(int sizeX, int sizeZ) { - if (sizeX >= 0 && sizeZ >= 0) { - this.sizeX = sizeX; - this.sizeZ = sizeZ; - this.values = (T[][])(new Object[sizeX][sizeZ]); - this.populated = new boolean[sizeX][sizeZ]; - this.size = 0; - } else { - throw new IllegalArgumentException("negative size"); - } - } - - public int localXFrom(int x) { - return x < 0 ? (x % this.sizeX + this.sizeX - 1) % this.sizeX : x % this.sizeX; - } - - public int localZFrom(int z) { - return z < 0 ? (z % this.sizeZ + this.sizeZ - 1) % this.sizeZ : z % this.sizeZ; - } - - @Override - public T get(int x, int z) { - x = this.localXFrom(x); - z = this.localZFrom(z); - if (!this.isCached(x, z)) { - throw new IllegalStateException("accessing coordinates that are not cached: " + x + " " + z); - } else { - return this.values[x][z]; - } - } - - @Override - public boolean isCached(int x, int z) { - return this.populated[this.localXFrom(x)][this.localZFrom(z)]; - } - - @Override - public T save(int x, int z, T value) { - x = this.localXFrom(x); - z = this.localZFrom(z); - this.values[x][z] = value; - this.populated[x][z] = true; - this.size++; - return value; - } - - @Override - public void flush(int x, int z) { - x = this.localXFrom(x); - z = this.localZFrom(z); - if (this.populated[x][z]) { - this.values[x][z] = null; - this.populated[x][z] = false; - this.size--; - } - } - - @Override - public void flush() { - for (int x = 0; x < this.sizeX; x++) { - for (int z = 0; z < this.sizeZ; z++) { - this.values[x][z] = null; - this.populated[x][z] = false; - } - } - - this.size = 0; - } - - @Override - public int size() { - return this.size; - } - - @Nonnull - @Override - public String toString() { - return "WrappedBiCoordinateCache{sizeX=" - + this.sizeX - + ", sizeZ=" - + this.sizeZ - + ", values=" - + Arrays.toString((Object[])this.values) - + ", populated=" - + Arrays.toString((Object[])this.populated) - + ", size=" - + this.size - + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/WrappedBiCoordinateDoubleCache.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/WrappedBiCoordinateDoubleCache.java deleted file mode 100644 index 3f4278f0..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/bicoordinatecache/WrappedBiCoordinateDoubleCache.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.bicoordinatecache; - -import java.util.Arrays; -import javax.annotation.Nonnull; - -public class WrappedBiCoordinateDoubleCache implements BiCoordinateCache { - private final int sizeX; - private final int sizeZ; - @Nonnull - private final double[][] values; - @Nonnull - private final boolean[][] populated; - private int size; - - public WrappedBiCoordinateDoubleCache(int sizeX, int sizeZ) { - if (sizeX >= 0 && sizeZ >= 0) { - this.sizeX = sizeX; - this.sizeZ = sizeZ; - this.values = new double[sizeX][sizeZ]; - this.populated = new boolean[sizeX][sizeZ]; - this.size = 0; - } else { - throw new IllegalArgumentException("negative size"); - } - } - - public int localXFrom(int x) { - return x < 0 ? (x % this.sizeX + this.sizeX - 1) % this.sizeX : x % this.sizeX; - } - - public int localZFrom(int z) { - return z < 0 ? (z % this.sizeZ + this.sizeZ - 1) % this.sizeZ : z % this.sizeZ; - } - - @Nonnull - public Double get(int x, int z) { - x = this.localXFrom(x); - z = this.localZFrom(z); - if (!this.isCached(x, z)) { - throw new IllegalStateException("accessing coordinates that are not cached: " + x + " " + z); - } else { - return this.values[x][z]; - } - } - - @Override - public boolean isCached(int x, int z) { - return this.populated[this.localXFrom(x)][this.localZFrom(z)]; - } - - @Nonnull - public Double save(int x, int z, @Nonnull Double value) { - x = this.localXFrom(x); - z = this.localZFrom(z); - this.values[x][z] = value; - this.populated[x][z] = true; - this.size++; - return value; - } - - @Override - public void flush(int x, int z) { - if (this.populated[this.localXFrom(x)][this.localZFrom(z)]) { - this.populated[this.localXFrom(x)][this.localZFrom(z)] = false; - this.size--; - } - } - - @Override - public void flush() { - for (int x = 0; x < this.sizeX; x++) { - for (int z = 0; z < this.sizeZ; z++) { - this.populated[x][z] = false; - } - } - - this.size = 0; - } - - @Override - public int size() { - return this.size; - } - - @Nonnull - @Override - public String toString() { - return "WrappedBiCoordinateDoubleCache{sizeX=" - + this.sizeX - + ", sizeZ=" - + this.sizeZ - + ", values=" - + Arrays.toString((Object[])this.values) - + ", populated=" - + Arrays.toString((Object[])this.populated) - + ", size=" - + this.size - + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/compression/Compressor.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/compression/Compressor.java deleted file mode 100644 index 751eb038..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/compression/Compressor.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.compression; - -import javax.annotation.Nonnull; - -public class Compressor { - private final int MIN_RUN = 7; - - @Nonnull - public Compressor.CompressedArray compressOnReference(@Nonnull T[] in) { - int currentRun = 0; - int resultIndex = 0; - Object runObj = null; - Object[] result = new Object[in.length]; - - for (int i = 0; i < result.length; i++) { - if (in[i] != runObj && currentRun >= 7) { - result[resultIndex] = new Compressor.Run(runObj, currentRun); - currentRun = 0; - resultIndex++; - runObj = in[i]; - } else if (in[i] != runObj && currentRun < 7) { - while (currentRun > 0) { - result[resultIndex] = runObj; - resultIndex++; - currentRun--; - } - - currentRun = 0; - runObj = in[i]; - } else { - currentRun++; - } - } - - if (currentRun >= 7) { - result[resultIndex] = new Compressor.Run(runObj, currentRun); - } else { - while (currentRun > 0) { - result[resultIndex] = runObj; - resultIndex++; - currentRun--; - } - } - - Object[] trimmedResult = new Object[resultIndex]; - System.arraycopy(result, 0, trimmedResult, 0, trimmedResult.length); - return new Compressor.CompressedArray<>(trimmedResult, in.length); - } - - @Nonnull - public T[] decompress(@Nonnull Compressor.CompressedArray compressedArray) { - int caIndex = 0; - int runIndex = 0; - int outIndex = 0; - Object[] ca = compressedArray.data; - - Object[] out; - for (out = new Object[compressedArray.initialLength]; caIndex < ca.length; caIndex++) { - if (ca[caIndex] instanceof Compressor.Run run) { - for (int var8 = 0; var8 < run.length; var8++) { - out[outIndex++] = run.obj; - } - } else { - out[outIndex++] = ca[caIndex]; - } - } - - return (T[])out; - } - - public static class CompressedArray { - private final Object[] data; - private final int initialLength; - - private CompressedArray(Object[] data, int initialLength) { - this.data = data; - this.initialLength = initialLength; - } - } - - public static class Run { - Object obj; - int length; - - private Run(Object obj, int length) { - this.obj = obj; - this.length = length; - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/ArrayVoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/ArrayVoxelSpace.java deleted file mode 100644 index f3a11a3b..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/ArrayVoxelSpace.java +++ /dev/null @@ -1,330 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.function.Predicate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class ArrayVoxelSpace implements VoxelSpace { - protected final int sizeX; - protected final int sizeY; - protected final int sizeZ; - @Nonnull - protected final T[] contents; - @Nonnull - protected String name = "schematic"; - @Nullable - protected T[] fastReset = null; - protected VoxelCoordinate origin; - - public ArrayVoxelSpace(@Nonnull Bounds3i bounds) { - int var10005 = -bounds.min.x; - this("", bounds.getSize().x, bounds.getSize().y, bounds.getSize().z, var10005, bounds.min.y, -bounds.min.z); - } - - public ArrayVoxelSpace(@Nonnull String name, int sizeX, int sizeY, int sizeZ, int originX, int originY, int originZ) { - if (name == null) { - throw new NullPointerException(); - } else if (sizeX >= 1 && sizeY >= 1 && sizeZ >= 1) { - this.name = name; - this.sizeX = sizeX; - this.sizeY = sizeY; - this.sizeZ = sizeZ; - this.contents = (T[])(new Object[sizeX * sizeY * sizeZ]); - this.origin = new VoxelCoordinate(originX, originY, originZ); - } else { - throw new IllegalArgumentException("invalid size " + sizeX + " " + sizeY + " " + sizeZ); - } - } - - public ArrayVoxelSpace(int sizeX, int sizeY, int sizeZ) { - if (sizeX >= 1 && sizeY >= 1 && sizeZ >= 1) { - this.name = this.getClass().getName(); - this.sizeX = sizeX; - this.sizeY = sizeY; - this.sizeZ = sizeZ; - this.contents = (T[])(new Object[sizeX * sizeY * sizeZ]); - this.origin = new VoxelCoordinate(0, 0, 0); - } else { - throw new IllegalArgumentException("invalid size " + sizeX + " " + sizeY + " " + sizeZ); - } - } - - public ArrayVoxelSpace(@Nonnull VoxelSpace voxelSpace) { - this( - voxelSpace.getName(), - voxelSpace.sizeX(), - voxelSpace.sizeY(), - voxelSpace.sizeZ(), - voxelSpace.getOriginX(), - voxelSpace.getOriginY(), - voxelSpace.getOriginZ() - ); - voxelSpace.forEach((v, x, y, z) -> this.set((T)v, x, y, z)); - } - - public void setFastResetTo(T e) { - this.fastReset = (T[])(new Object[this.contents.length]); - - for (int i = 0; i < this.fastReset.length; i++) { - this.fastReset[i] = e; - } - } - - public void disableFastReset() { - this.fastReset = null; - } - - public boolean hasFastReset() { - return this.fastReset != null; - } - - public void fastReset() { - if (this.fastReset == null) { - throw new IllegalStateException("no fast-reset"); - } else { - System.arraycopy(this.fastReset, 0, this.contents, 0, this.fastReset.length); - } - } - - @Override - public int sizeX() { - return this.sizeX; - } - - @Override - public int sizeY() { - return this.sizeY; - } - - @Override - public int sizeZ() { - return this.sizeZ; - } - - @Override - public void pasteFrom(@Nonnull VoxelSpace source) { - if (source == null) { - throw new NullPointerException(); - } else { - for (int x = source.minX(); x < source.maxX(); x++) { - for (int y = source.minY(); y < source.maxY(); y++) { - for (int z = source.minZ(); z < source.maxZ(); z++) { - this.set(source.getContent(x, y, z), x, y, z); - } - } - } - } - } - - @Override - public boolean set(T content, int x, int y, int z) { - if (!this.isInsideSpace(x, y, z)) { - return false; - } else { - this.contents[this.arrayIndex(x + this.origin.x, y + this.origin.y, z + this.origin.z)] = content; - return true; - } - } - - @Override - public boolean set(T content, @Nonnull Vector3i position) { - return this.set(content, position.x, position.y, position.z); - } - - @Override - public void set(T content) { - for (int x = this.minX(); x < this.maxX(); x++) { - for (int y = this.minY(); y < this.maxY(); y++) { - for (int z = this.minZ(); z < this.maxZ(); z++) { - this.set(content, x, y, z); - } - } - } - } - - @Override - public void setOrigin(int x, int y, int z) { - this.origin.x = x; - this.origin.y = y; - this.origin.z = z; - } - - @Override - public T getContent(int x, int y, int z) { - if (!this.isInsideSpace(x, y, z)) { - throw new IndexOutOfBoundsException( - "Coordinates outside VoxelSpace: " - + x - + " " - + y - + " " - + z - + " constraints " - + this.minX() - + " -> " - + this.maxX() - + " " - + this.minY() - + " -> " - + this.maxY() - + " " - + this.minZ() - + " -> " - + this.maxZ() - + "\n" - + this.toString() - ); - } else { - return this.contents[this.arrayIndex(x + this.origin.x, y + this.origin.y, z + this.origin.z)]; - } - } - - @Nullable - @Override - public T getContent(@Nonnull Vector3i position) { - return this.getContent(position.x, position.y, position.z); - } - - @Override - public boolean replace(T replacement, int x, int y, int z, @Nonnull Predicate mask) { - if (!this.isInsideSpace(x, y, z)) { - throw new IllegalArgumentException("outside schematic"); - } else if (!mask.test(this.getContent(x, y, z))) { - return false; - } else { - this.set(replacement, x, y, z); - return true; - } - } - - public T[] toArray() { - return (T[])((Object[])this.contents.clone()); - } - - @Nonnull - VoxelCoordinate getOrigin() { - return this.origin.clone(); - } - - @Override - public int getOriginX() { - return this.origin.x; - } - - @Override - public int getOriginY() { - return this.origin.y; - } - - @Override - public int getOriginZ() { - return this.origin.z; - } - - @Nonnull - @Override - public String getName() { - return this.name; - } - - @Override - public boolean isInsideSpace(int x, int y, int z) { - return x + this.origin.x >= 0 - && x + this.origin.x < this.sizeX - && y + this.origin.y >= 0 - && y + this.origin.y < this.sizeY - && z + this.origin.z >= 0 - && z + this.origin.z < this.sizeZ; - } - - @Override - public boolean isInsideSpace(@Nonnull Vector3i position) { - return this.isInsideSpace(position.x, position.y, position.z); - } - - @Override - public void forEach(@Nonnull VoxelConsumer action) { - if (action == null) { - throw new NullPointerException(); - } else { - for (int x = this.minX(); x < this.maxX(); x++) { - for (int y = this.minY(); y < this.maxY(); y++) { - for (int z = this.minZ(); z < this.maxZ(); z++) { - action.accept(this.getContent(x, y, z), x, y, z); - } - } - } - } - } - - @Override - public int minX() { - return -this.origin.x; - } - - @Override - public int maxX() { - return this.sizeX - this.origin.x; - } - - @Override - public int minY() { - return -this.origin.y; - } - - @Override - public int maxY() { - return this.sizeY - this.origin.y; - } - - @Override - public int minZ() { - return -this.origin.z; - } - - @Override - public int maxZ() { - return this.sizeZ - this.origin.z; - } - - @Nonnull - public ArrayVoxelSpace clone() { - ArrayVoxelSpace clone = new ArrayVoxelSpace<>(this.name, this.sizeX, this.sizeY, this.sizeZ, this.origin.x, this.origin.y, this.origin.z); - this.forEach((v, x, y, z) -> clone.set((T)v, x, y, z)); - return clone; - } - - private int arrayIndex(int x, int y, int z) { - return y + x * this.sizeY + z * this.sizeY * this.sizeX; - } - - @Nonnull - @Override - public String toString() { - return "ArrayVoxelSpace{sizeX=" - + this.sizeX - + ", sizeY=" - + this.sizeY - + ", sizeZ=" - + this.sizeZ - + ", minX=" - + this.minX() - + ", minY=" - + this.minY() - + ", minZ=" - + this.minZ() - + ", maxX=" - + this.maxX() - + ", maxY=" - + this.maxY() - + ", maxZ=" - + this.maxZ() - + ", name='" - + this.name - + "', origin=" - + this.origin - + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/BooleanVoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/BooleanVoxelSpace.java deleted file mode 100644 index 1387d36b..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/BooleanVoxelSpace.java +++ /dev/null @@ -1,382 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace; - -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.function.Predicate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class BooleanVoxelSpace implements VoxelSpace { - protected final int sizeX; - protected final int sizeY; - protected final int sizeZ; - @Nonnull - protected final int[][] cells; - protected VoxelCoordinate origin; - private boolean alignedOriginZ; - private int originZOffset; - - public BooleanVoxelSpace(int sizeX, int sizeY, int sizeZ, int originX, int originY, int originZ, boolean alignedOriginZ) { - if (sizeX < 1 || sizeY < 1 || sizeZ < 1) { - throw new IllegalArgumentException("invalid size " + sizeX + " " + sizeY + " " + sizeZ); - } else if (alignedOriginZ && !isAlignedOriginZ(originZ)) { - throw new IllegalArgumentException("unaligned originZ: " + originZ); - } else { - this.sizeX = sizeX; - this.sizeY = sizeY; - this.sizeZ = sizeZ; - this.alignedOriginZ = alignedOriginZ; - int primaryDepth = sizeX * sizeY; - int secondaryDepth = (sizeZ - 1 >> 5) + 1; - if (!alignedOriginZ) { - secondaryDepth++; - } - - this.cells = new int[primaryDepth][secondaryDepth]; - this.origin = new VoxelCoordinate(originX, originY, originZ); - this.setOrigin(originX, originY, originZ); - } - } - - public BooleanVoxelSpace(int sizeX, int sizeY, int sizeZ, int originX, int originY, int originZ) { - this(sizeX, sizeY, sizeZ, originX, originY, originZ, false); - } - - public BooleanVoxelSpace(int sizeX, int sizeY, int sizeZ) { - this(sizeX, sizeY, sizeZ, 0, 0, 0); - } - - public BooleanVoxelSpace(int sizeX, int sizeY, int sizeZ, boolean forceAlignOriginZ) { - this(sizeX, sizeY, sizeZ, 0, 0, 0, forceAlignOriginZ); - } - - @Override - public int sizeX() { - return this.sizeX; - } - - @Override - public int sizeY() { - return this.sizeY; - } - - @Override - public int sizeZ() { - return this.sizeZ; - } - - @Override - public void pasteFrom(@Nonnull VoxelSpace source) { - if (source == null) { - throw new NullPointerException(); - } else { - for (int x = source.minX(); x < source.maxX(); x++) { - for (int y = source.minY(); y < source.maxY(); y++) { - for (int z = source.minZ(); z < source.maxZ(); z++) { - this.set(source.getContent(x, y, z), x, y, z); - } - } - } - } - } - - private int primaryAddressIndex(int x, int y) { - return x * this.sizeY + y; - } - - private int secondaryAddressIndex(int z) { - z += this.originZOffset; - return z >> 5; - } - - private static int setBit(int bits, int index, boolean value) { - int mask = 1 << index; - if (!value) { - bits &= ~mask; - } else { - bits |= mask; - } - - return bits; - } - - private static boolean getBit(int bits, int index) { - return (bits >> index & 1) == 1; - } - - public boolean set(@Nullable Boolean value, int x, int y, int z) { - if (!this.isInsideSpace(x, y, z)) { - return false; - } else { - if (value == null) { - value = false; - } - - int localX = x + this.origin.x; - int localY = y + this.origin.y; - int localZ = z + this.origin.z; - int i = this.primaryAddressIndex(localX, localY); - int j = this.secondaryAddressIndex(localZ); - int bitIndex = localZ - j * 32 + this.originZOffset; - int cell = setBit(this.cells[i][j], bitIndex, value); - this.cells[i][j] = cell; - return true; - } - } - - public boolean set(Boolean content, @Nonnull Vector3i position) { - return this.set(content, position.x, position.y, position.z); - } - - @Nonnull - public Boolean getContent(int x, int y, int z) { - if (!this.isInsideSpace(x, y, z)) { - throw new IndexOutOfBoundsException( - "Coordinates outside VoxelSpace: " - + x - + " " - + y - + " " - + z - + " constraints " - + this.minX() - + " -> " - + this.maxX() - + " " - + this.minY() - + " -> " - + this.maxY() - + " " - + this.minZ() - + " -> " - + this.maxZ() - + "\n" - + this.toString() - ); - } else { - int localX = x + this.origin.x; - int localY = y + this.origin.y; - int localZ = z + this.origin.z; - int i = this.primaryAddressIndex(localX, localY); - int j = this.secondaryAddressIndex(localZ); - int bitIndex = localZ - j * 32 + this.originZOffset; - return getBit(this.cells[i][j], bitIndex); - } - } - - @Nonnull - public Boolean getContent(@Nonnull Vector3i position) { - return this.getContent(position.x, position.y, position.z); - } - - private int globalJ(int globalZ) { - return globalZ >> 5; - } - - private int localJ(int globalJ) { - return globalJ - this.globalJ(-this.origin.z); - } - - public void deepCopyFrom(@Nonnull BooleanVoxelSpace other) { - if (other.cells.length != 0) { - if (other.cells[0].length != 0) { - if (this.cells.length != 0) { - if (this.cells[0].length != 0) { - int thisGlobalJ = this.globalJ(-this.origin.z); - int otherGlobalJ = other.globalJ(-other.origin.z); - int minGlobalJ = Math.max(otherGlobalJ, thisGlobalJ); - int minThisJ = this.localJ(minGlobalJ); - int minOtherJ = other.localJ(minGlobalJ); - int maxIterations = Math.min(other.cells[0].length - minOtherJ, this.cells[0].length - minThisJ); - int minX = Math.max(this.minX(), other.minX()); - int minY = Math.max(this.minY(), other.minY()); - int maxX = Math.min(this.maxX(), other.maxX()); - int maxY = Math.min(this.maxY(), other.maxY()); - - for (int x = minX; x < maxX; x++) { - for (int y = minY; y < maxY; y++) { - int thisLocalX = x + this.origin.x; - int thisLocalY = y + this.origin.y; - int otherLocalX = x + other.origin.x; - int otherLocalY = y + other.origin.y; - int thisI = this.primaryAddressIndex(thisLocalX, thisLocalY); - int otherI = other.primaryAddressIndex(otherLocalX, otherLocalY); - int thisJ = minThisJ; - int otherJ = minOtherJ; - - for (int c = 0; c < maxIterations; c++) { - this.cells[thisI][thisJ] = other.cells[otherI][otherJ]; - otherJ++; - thisJ++; - } - } - } - } - } - } - } - } - - public void set(Boolean content) { - for (int x = this.minX(); x < this.maxX(); x++) { - for (int y = this.minY(); y < this.maxY(); y++) { - for (int z = this.minZ(); z < this.maxZ(); z++) { - this.set(content, x, y, z); - } - } - } - } - - @Override - public void setOrigin(int x, int y, int z) { - if (this.alignedOriginZ && z % 32 != 0) { - throw new IllegalArgumentException("z isn't aligned to 32 bit integer grid: " + z); - } else { - this.origin.x = x; - this.origin.y = y; - this.origin.z = z; - this.originZOffset = -this.origin.z - getAlignedZ(-this.origin.z); - } - } - - public boolean replace(Boolean replacement, int x, int y, int z, @Nonnull Predicate mask) { - if (!this.isInsideSpace(x, y, z)) { - throw new IllegalArgumentException("outside schematic"); - } else if (!mask.test(this.getContent(x, y, z))) { - return false; - } else { - this.set(replacement, x, y, z); - return true; - } - } - - @Nonnull - VoxelCoordinate getOrigin() { - return this.origin.clone(); - } - - @Override - public int getOriginX() { - return this.origin.x; - } - - @Override - public int getOriginY() { - return this.origin.y; - } - - @Override - public int getOriginZ() { - return this.origin.z; - } - - @Nonnull - @Override - public String getName() { - return ""; - } - - @Override - public boolean isInsideSpace(int x, int y, int z) { - return x + this.origin.x >= 0 - && x + this.origin.x < this.sizeX - && y + this.origin.y >= 0 - && y + this.origin.y < this.sizeY - && z + this.origin.z >= 0 - && z + this.origin.z < this.sizeZ; - } - - @Override - public boolean isInsideSpace(@Nonnull Vector3i position) { - return this.isInsideSpace(position.x, position.y, position.z); - } - - @Override - public void forEach(@Nonnull VoxelConsumer action) { - if (action == null) { - throw new NullPointerException(); - } else { - for (int x = this.minX(); x < this.maxX(); x++) { - for (int y = this.minY(); y < this.maxY(); y++) { - for (int z = this.minZ(); z < this.maxZ(); z++) { - action.accept(this.getContent(x, y, z), x, y, z); - } - } - } - } - } - - @Override - public int minX() { - return -this.origin.x; - } - - @Override - public int maxX() { - return this.sizeX - this.origin.x; - } - - @Override - public int minY() { - return -this.origin.y; - } - - @Override - public int maxY() { - return this.sizeY - this.origin.y; - } - - @Override - public int minZ() { - return -this.origin.z; - } - - @Override - public int maxZ() { - return this.sizeZ - this.origin.z; - } - - @Nonnull - public BooleanVoxelSpace clone() { - BooleanVoxelSpace clone = new BooleanVoxelSpace(this.sizeX, this.sizeY, this.sizeZ, this.origin.x, this.origin.y, this.origin.z); - this.forEach(clone::set); - return clone; - } - - private int arrayIndex(int x, int y, int z) { - return y + x * this.sizeY + z * this.sizeY * this.sizeX; - } - - @Nonnull - @Override - public String toString() { - return "ArrayVoxelSpace{sizeX=" - + this.sizeX - + ", sizeY=" - + this.sizeY - + ", sizeZ=" - + this.sizeZ - + ", minX=" - + this.minX() - + ", minY=" - + this.minY() - + ", minZ=" - + this.minZ() - + ", maxX=" - + this.maxX() - + ", maxY=" - + this.maxY() - + ", maxZ=" - + this.maxZ() - + ", origin=" - + this.origin - + "}"; - } - - public static boolean isAlignedOriginZ(int z) { - return z % 32 == 0; - } - - public static int getAlignedZ(int z) { - return z >> 5 << 5; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/NullSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/NullSpace.java deleted file mode 100644 index b0b098b3..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/NullSpace.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace; - -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.function.Predicate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class NullSpace implements VoxelSpace { - @Nonnull - private static final NullSpace INSTANCE = new NullSpace(); - - @Nonnull - public static NullSpace instance() { - return INSTANCE; - } - - @Nonnull - public static NullSpace instance(@Nonnull Class clazz) { - return INSTANCE; - } - - private NullSpace() { - } - - @Override - public boolean set(V content, int x, int y, int z) { - return false; - } - - @Override - public boolean set(V content, @Nonnull Vector3i position) { - return this.set(content, position.x, position.y, position.z); - } - - @Override - public void set(V content) { - } - - @Override - public void setOrigin(int x, int y, int z) { - } - - @Nullable - @Override - public V getContent(int x, int y, int z) { - return null; - } - - @Nullable - @Override - public V getContent(@Nonnull Vector3i position) { - return this.getContent(position.x, position.y, position.z); - } - - @Override - public boolean replace(V replacement, int x, int y, int z, @Nonnull Predicate mask) { - return false; - } - - @Override - public void pasteFrom(@Nonnull VoxelSpace source) { - } - - @Override - public int getOriginX() { - return 0; - } - - @Override - public int getOriginY() { - return 0; - } - - @Override - public int getOriginZ() { - return 0; - } - - @Nonnull - @Override - public String getName() { - return "null_space"; - } - - @Override - public boolean isInsideSpace(int x, int y, int z) { - return false; - } - - @Override - public boolean isInsideSpace(@Nonnull Vector3i position) { - return this.isInsideSpace(position.x, position.y, position.z); - } - - @Override - public void forEach(VoxelConsumer action) { - } - - @Override - public int minX() { - return 0; - } - - @Override - public int maxX() { - return 0; - } - - @Override - public int minY() { - return 0; - } - - @Override - public int maxY() { - return 0; - } - - @Override - public int minZ() { - return 0; - } - - @Override - public int maxZ() { - return 0; - } - - @Override - public int sizeX() { - return 0; - } - - @Override - public int sizeY() { - return 0; - } - - @Override - public int sizeZ() { - return 0; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelConsumer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelConsumer.java deleted file mode 100644 index 62b78a83..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelConsumer.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace; - -@FunctionalInterface -public interface VoxelConsumer { - void accept(V var1, int var2, int var3, int var4); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelCoordinate.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelCoordinate.java deleted file mode 100644 index 6067d642..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelCoordinate.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace; - -import javax.annotation.Nonnull; - -public class VoxelCoordinate { - int x; - int y; - int z; - - public VoxelCoordinate(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals(Object other) { - return !(other instanceof VoxelCoordinate otherVoxelCoordinate) - ? false - : this == otherVoxelCoordinate || this.x == otherVoxelCoordinate.x && this.y == otherVoxelCoordinate.y && this.z == otherVoxelCoordinate.z; - } - - @Nonnull - public VoxelCoordinate clone() { - return new VoxelCoordinate(this.x, this.y, this.z); - } - - @Nonnull - @Override - public String toString() { - return "VoxelCoordinate{x=" + this.x + ", y=" + this.y + ", z=" + this.z + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelSpace.java deleted file mode 100644 index 8a38781e..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelSpace.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.function.Predicate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public interface VoxelSpace { - boolean set(T var1, int var2, int var3, int var4); - - boolean set(T var1, @Nonnull Vector3i var2); - - void set(T var1); - - void setOrigin(int var1, int var2, int var3); - - @Nullable - T getContent(int var1, int var2, int var3); - - @Nullable - T getContent(@Nonnull Vector3i var1); - - boolean replace(T var1, int var2, int var3, int var4, @Nonnull Predicate var5); - - void pasteFrom(@Nonnull VoxelSpace var1); - - int getOriginX(); - - int getOriginY(); - - int getOriginZ(); - - String getName(); - - boolean isInsideSpace(int var1, int var2, int var3); - - boolean isInsideSpace(@Nonnull Vector3i var1); - - void forEach(VoxelConsumer var1); - - @Nonnull - default Bounds3i getBounds() { - return new Bounds3i(new Vector3i(this.minX(), this.minY(), this.minZ()), new Vector3i(this.maxX(), this.maxY(), this.maxZ())); - } - - int minX(); - - int maxX(); - - int minY(); - - int maxY(); - - int minZ(); - - int maxZ(); - - int sizeX(); - - int sizeY(); - - int sizeZ(); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelSpaceUtil.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelSpaceUtil.java deleted file mode 100644 index 86779fcc..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/VoxelSpaceUtil.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace; - -import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; -import com.hypixel.hytale.common.util.ExceptionUtil; -import java.util.LinkedList; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import javax.annotation.Nonnull; - -public class VoxelSpaceUtil { - public static void parallelCopy(@Nonnull VoxelSpace source, @Nonnull VoxelSpace destination, int concurrency) { - if (concurrency < 1) { - throw new IllegalArgumentException("negative concurrency"); - } else { - int minX = source.minX(); - int minY = source.minY(); - int minZ = source.minZ(); - int sizeX = source.sizeX(); - int sizeY = source.sizeY(); - int sizeZ = source.sizeZ(); - LinkedList> tasks = new LinkedList<>(); - int bSize = source.sizeX() * source.sizeY() * source.sizeZ() / concurrency; - - for (int b = 0; b < concurrency; b++) { - tasks.add(CompletableFuture.runAsync(() -> { - for (int i = b * bSize; i < (b + 1) * bSize; i++) { - int x = i % sizeX + minX; - int y = i / sizeX % sizeY + minY; - int z = i / (sizeX * sizeY) % sizeZ + minZ; - if (source.isInsideSpace(x, y, z) && destination.isInsideSpace(x, y, z)) { - destination.set(source.getContent(x, y, z), x, y, z); - } - } - }).handle((r, ex) -> { - if (ex == null) { - return (Void)r; - } else { - LoggerUtil.logException("a VoxelSpace async process", ex, LoggerUtil.getLogger()); - return null; - } - })); - } - - try { - while (!tasks.isEmpty()) { - tasks.removeFirst().get(); - } - } catch (ExecutionException | InterruptedException var13) { - Thread.currentThread().interrupt(); - String msg = "Exception thrown by HytaleGenerator while attempting an asynchronous copy of a VoxelSpace:\n"; - msg = msg + ExceptionUtil.toStringWithStack(var13); - LoggerUtil.getLogger().severe(msg); - } - } - } - - private static class BatchTransfer implements Runnable { - private final VoxelSpace source; - private final VoxelSpace destination; - private final int minX; - private final int minY; - private final int minZ; - private final int maxX; - private final int maxY; - private final int maxZ; - - private BatchTransfer(VoxelSpace source, VoxelSpace destination, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { - this.source = source; - this.destination = destination; - this.minX = minX; - this.minY = minY; - this.minZ = minZ; - this.maxX = maxX; - this.maxY = maxY; - this.maxZ = maxZ; - } - - @Override - public void run() { - try { - for (int x = this.minX; x < this.maxX; x++) { - for (int y = this.minY; y < this.maxY; y++) { - for (int z = this.minZ; z < this.maxZ; z++) { - if (this.destination.isInsideSpace(x, y, z)) { - this.destination.set(this.source.getContent(x, y, z), x, y, z); - } - } - } - } - } catch (Exception var4) { - String msg = "Exception thrown by HytaleGenerator while attempting a BatchTransfer operation:\n"; - msg = msg + ExceptionUtil.toStringWithStack(var4); - LoggerUtil.getLogger().severe(msg); - } - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/WindowVoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/WindowVoxelSpace.java deleted file mode 100644 index 24ad65a5..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/datastructures/voxelspace/WindowVoxelSpace.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace; - -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.function.Predicate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class WindowVoxelSpace implements VoxelSpace { - @Nonnull - private final VoxelSpace wrappedVoxelSpace; - @Nonnull - private final VoxelCoordinate min; - @Nonnull - private final VoxelCoordinate max; - - public WindowVoxelSpace(@Nonnull VoxelSpace voxelSpace) { - this.wrappedVoxelSpace = voxelSpace; - this.min = new VoxelCoordinate(voxelSpace.minX(), voxelSpace.minY(), voxelSpace.minZ()); - this.max = new VoxelCoordinate(voxelSpace.maxX(), voxelSpace.maxY(), voxelSpace.maxZ()); - } - - @Nonnull - public WindowVoxelSpace setWindow(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { - if (minX >= this.wrappedVoxelSpace.minX() - && minY >= this.wrappedVoxelSpace.minY() - && minZ >= this.wrappedVoxelSpace.minZ() - && maxX >= minX - && maxY >= minY - && maxZ >= minZ - && maxX <= this.wrappedVoxelSpace.maxX() - && maxY <= this.wrappedVoxelSpace.maxY() - && maxZ <= this.wrappedVoxelSpace.maxZ()) { - this.min.x = minX; - this.min.y = minY; - this.min.z = minZ; - this.max.x = maxX; - this.max.y = maxY; - this.max.z = maxZ; - return this; - } else { - throw new IllegalArgumentException("invalid values"); - } - } - - @Nonnull - public VoxelSpace getWrappedSchematic() { - return this.wrappedVoxelSpace; - } - - @Override - public boolean set(T content, int x, int y, int z) { - if (!this.isInsideSpace(x, y, z)) { - return false; - } else { - return !this.wrappedVoxelSpace.isInsideSpace(x, y, z) ? false : this.wrappedVoxelSpace.set(content, x, y, z); - } - } - - @Override - public boolean set(T content, @Nonnull Vector3i position) { - return this.set(content, position.x, position.y, position.z); - } - - @Override - public void set(T content) { - for (int x = this.minX(); x < this.maxX(); x++) { - for (int y = this.minY(); y < this.maxY(); y++) { - for (int z = this.minZ(); z < this.maxZ(); z++) { - this.set(content, x, y, z); - } - } - } - } - - @Override - public void setOrigin(int x, int y, int z) { - throw new UnsupportedOperationException("can't set origin of window"); - } - - @Override - public T getContent(int x, int y, int z) { - if (!this.isInsideSpace(x, y, z)) { - throw new IllegalArgumentException("outside schematic"); - } else { - return this.wrappedVoxelSpace.getContent(x, y, z); - } - } - - @Nullable - @Override - public T getContent(@Nonnull Vector3i position) { - return this.getContent(position.x, position.y, position.z); - } - - @Override - public boolean replace(T replacement, int x, int y, int z, @Nonnull Predicate mask) { - if (!this.isInsideSpace(x, y, z)) { - throw new IllegalArgumentException("outside schematic"); - } else { - return this.wrappedVoxelSpace.replace(replacement, x, y, z, mask); - } - } - - @Override - public void pasteFrom(@Nonnull VoxelSpace source) { - for (int x = source.minX(); x < source.maxX(); x++) { - for (int y = source.minY(); y < source.maxY(); y++) { - for (int z = source.minZ(); z < source.maxZ(); z++) { - this.set(source.getContent(x, y, z), x, y, z); - } - } - } - } - - @Override - public int getOriginX() { - int offset = this.min.x - this.wrappedVoxelSpace.minX(); - return this.wrappedVoxelSpace.getOriginX() - offset; - } - - @Override - public int getOriginY() { - int offset = this.min.y - this.wrappedVoxelSpace.minY(); - return this.wrappedVoxelSpace.getOriginY() - offset; - } - - @Override - public int getOriginZ() { - int offset = this.min.z - this.wrappedVoxelSpace.minZ(); - return this.wrappedVoxelSpace.getOriginZ() - offset; - } - - @Nonnull - @Override - public String getName() { - return "window_to_" + this.wrappedVoxelSpace.getName(); - } - - @Override - public boolean isInsideSpace(int x, int y, int z) { - return x >= this.minX() && x < this.maxX() && y >= this.minY() && y < this.maxY() && z >= this.minZ() && z < this.maxZ(); - } - - @Override - public boolean isInsideSpace(@Nonnull Vector3i position) { - return this.isInsideSpace(position.x, position.y, position.z); - } - - @Override - public void forEach(@Nonnull VoxelConsumer action) { - for (int x = this.minX(); x < this.maxX(); x++) { - for (int y = this.minY(); y < this.maxY(); y++) { - for (int z = this.minZ(); z < this.maxZ(); z++) { - action.accept(this.getContent(x, y, z), x, y, z); - } - } - } - } - - @Override - public int minX() { - return this.min.x; - } - - @Override - public int maxX() { - return this.max.x; - } - - @Override - public int minY() { - return this.min.y; - } - - @Override - public int maxY() { - return this.max.y; - } - - @Override - public int minZ() { - return this.min.z; - } - - @Override - public int maxZ() { - return this.max.z; - } - - @Override - public int sizeX() { - return this.max.x - this.min.x; - } - - @Override - public int sizeY() { - return this.max.y - this.min.y; - } - - @Override - public int sizeZ() { - return this.max.z - this.min.z; - } - - @Nonnull - @Override - public String toString() { - return "WindowVoxelSpace{wrappedVoxelSpace=" + this.wrappedVoxelSpace + ", min=" + this.min + ", max=" + this.max + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/Density.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/Density.java index fb107bd6..ad4ad486 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/Density.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/Density.java @@ -1,15 +1,17 @@ package com.hypixel.hytale.builtin.hytalegenerator.density; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.TerrainDensityProvider; import com.hypixel.hytale.builtin.hytalegenerator.environmentproviders.EnvironmentProvider; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.TerrainDensityProvider; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; import com.hypixel.hytale.builtin.hytalegenerator.tintproviders.TintProvider; import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class Density { @Nonnull @@ -78,21 +80,21 @@ public abstract class Density { } public Context(@Nonnull TintProvider.Context context) { - this.position = context.position.toVector3d(); + this.position = Vector3iUtil.toVector3d(context.position); } public Context(@Nonnull EnvironmentProvider.Context context) { - this.position = context.position.toVector3d(); + this.position = Vector3iUtil.toVector3d(context.position); } public Context(@Nonnull MaterialProvider.Context context) { - this.position = context.position.toVector3d(); + this.position = Vector3iUtil.toVector3d(context.position); this.terrainDensityProvider = context.terrainDensityProvider; this.distanceToBiomeEdge = context.distanceToBiomeEdge; } public Context(@Nonnull Pattern.Context context) { - this.position = context.position.toVector3d(); + this.position = Vector3iUtil.toVector3d(context.position); } public void assign(@Nonnull Density.Context other) { @@ -111,17 +113,27 @@ public abstract class Density { } public void assign(@Nonnull MaterialProvider.Context context) { - this.position.assign(context.position.x, context.position.y, context.position.z); + this.position.set(context.position.x, context.position.y, context.position.z); this.terrainDensityProvider = context.terrainDensityProvider; this.distanceToBiomeEdge = context.distanceToBiomeEdge; } public void assign(@Nonnull EnvironmentProvider.Context context) { - this.position.assign(context.position.x, context.position.y, context.position.z); + this.position.set(context.position.x, context.position.y, context.position.z); } public void assign(@Nonnull Pattern.Context context) { - this.position.assign(context.position.x, context.position.y, context.position.z); + this.position.set(context.position.x, context.position.y, context.position.z); + } + + public void assign(@Nonnull Prop.Context other) { + this.position.set(other.position); + this.distanceToBiomeEdge = other.distanceToBiomeEdge; + this.densityAnchor = null; + this.positionsAnchor = null; + this.switchState = 0; + this.distanceFromCellWall = Double.MAX_VALUE; + this.terrainDensityProvider = null; } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AmplitudeDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AmplitudeDensity.java index 5ad90af8..9d011648 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AmplitudeDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AmplitudeDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.NodeFunction; +import com.hypixel.hytale.builtin.hytalegenerator.math.NodeFunction; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AnchorDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AnchorDensity.java index be2cf3de..24b3d32a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AnchorDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AnchorDensity.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class AnchorDensity extends Density { @Nullable @@ -28,10 +28,10 @@ public class AnchorDensity extends Density { } else { if (this.isReversed) { this.rChildPosition - .assign(context.position.x + context.densityAnchor.x, context.position.y + context.densityAnchor.y, context.position.z + context.densityAnchor.z); + .set(context.position.x + context.densityAnchor.x, context.position.y + context.densityAnchor.y, context.position.z + context.densityAnchor.z); } else { this.rChildPosition - .assign(context.position.x - context.densityAnchor.x, context.position.y - context.densityAnchor.y, context.position.z - context.densityAnchor.z); + .set(context.position.x - context.densityAnchor.x, context.position.y - context.densityAnchor.y, context.position.z - context.densityAnchor.z); } this.rChildContext.assign(context); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AngleDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AngleDensity.java index 6be879e8..e6cdb029 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AngleDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AngleDensity.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.vectorproviders.VectorProvider; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class AngleDensity extends Density { private static final double HALF_PI = Math.PI / 2; @@ -19,7 +19,7 @@ public class AngleDensity extends Density { private final VectorProvider.Context rVectorProviderContext; public AngleDensity(@Nonnull VectorProvider vectorProvider, @Nonnull Vector3d vector, boolean toAxis) { - this.vector = vector.clone(); + this.vector = new Vector3d(vector); this.vectorProvider = vectorProvider; this.toAxis = toAxis; this.rOtherVector = new Vector3d(); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AxisDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AxisDensity.java index bfc31dae..e1284878 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AxisDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/AxisDensity.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class AxisDensity extends Density { public static final double ZERO_DELTA = 1.0E-9; @@ -60,7 +60,7 @@ public class AxisDensity extends Density { if (anchor == null) { return 0.0; } else { - this.rPosition.assign(context.position).subtract(anchor); + this.rPosition.set(context.position).sub(anchor); double distance = VectorUtil.distanceToLine3d(this.rPosition, ZERO_VECTOR, this.axis, this.r0, this.r1, this.r2, this.r3, this.r4); return this.distanceCurve.get(distance); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/CacheDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/CacheDensity.java index f4a8ee82..689001c6 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/CacheDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/CacheDensity.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CacheDensity extends Density { @Nonnull @@ -27,7 +27,7 @@ public class CacheDensity extends Density { this.cache.position = new Vector3d(); } - this.cache.position.assign(context.position); + this.cache.position.set(context.position); this.cache.value = this.input.process(context); return this.cache.value; } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ClampDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ClampDensity.java index cf86b3f8..d43f9e8b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ClampDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ClampDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/CylinderDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/CylinderDensity.java index 8ccfa9ef..9ee9c7dc 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/CylinderDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/CylinderDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/DistanceDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/DistanceDensity.java index 387407b9..7c6ff7d7 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/DistanceDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/DistanceDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/FastGradientWarpDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/FastGradientWarpDensity.java index 88fe8612..6c4d781b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/FastGradientWarpDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/FastGradientWarpDensity.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.fields.FastNoiseLite; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.noise.FastNoiseLite; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class FastGradientWarpDensity extends Density { private static final double HALF_PI = Math.PI / 2; @@ -51,10 +51,10 @@ public class FastGradientWarpDensity extends Density { this.rWarpedPosition.y = context.position.y; this.rWarpedPosition.z = context.position.z; this.warper.DomainWarpFractalProgressive(this.rWarpedPosition); - this.rPosition.assign(context.position); + this.rPosition.set(context.position); this.rChildContext.assign(context); this.rChildContext.position = this.rPosition; - this.rChildContext.position.assign(this.rWarpedPosition.x, this.rWarpedPosition.y, this.rWarpedPosition.z); + this.rChildContext.position.set(this.rWarpedPosition.x, this.rWarpedPosition.y, this.rWarpedPosition.z); return this.input.process(this.rChildContext); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/GradientDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/GradientDensity.java index 033dcb58..61a95759 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/GradientDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/GradientDensity.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class GradientDensity extends Density { private static final double HALF_PI = Math.PI / 2; @@ -24,7 +24,7 @@ public class GradientDensity extends Density { if (slopeRange <= 0.0) { throw new IllegalArgumentException(); } else { - this.axis = axis.clone(); + this.axis = new Vector3d(axis); this.slopeRange = slopeRange; this.input = input; this.rChildContext = new Density.Context(); @@ -38,20 +38,20 @@ public class GradientDensity extends Density { if (this.input == null) { return 0.0; } else { - this.rPosition.assign(context.position); + this.rPosition.set(context.position); this.rChildContext.assign(context); this.rChildContext.position = this.rPosition; double valueAtOrigin = this.input.process(this.rChildContext); double maxX = context.position.x + this.slopeRange; double maxY = context.position.y + this.slopeRange; double maxZ = context.position.z + this.slopeRange; - this.rChildContext.position.assign(maxX, context.position.y, context.position.z); + this.rChildContext.position.set(maxX, context.position.y, context.position.z); double deltaX = Math.abs(this.input.process(this.rChildContext) - valueAtOrigin); - this.rChildContext.position.assign(context.position.x, maxY, context.position.z); + this.rChildContext.position.set(context.position.x, maxY, context.position.z); double deltaY = Math.abs(this.input.process(this.rChildContext) - valueAtOrigin); - this.rChildContext.position.assign(context.position.x, context.position.y, maxZ); + this.rChildContext.position.set(context.position.x, context.position.y, maxZ); double deltaZ = Math.abs(this.input.process(this.rChildContext) - valueAtOrigin); - this.rSlopeDirection.assign(deltaX, deltaY, deltaZ); + this.rSlopeDirection.set(deltaX, deltaY, deltaZ); double slopeAngle = VectorUtil.angle(this.axis, this.rSlopeDirection); if (slopeAngle > Math.PI / 2) { slopeAngle = Math.PI - slopeAngle; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/GradientWarpDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/GradientWarpDensity.java index 9bf4d647..bf06c022 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/GradientWarpDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/GradientWarpDensity.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class GradientWarpDensity extends Density { private static final double HALF_PI = Math.PI / 2; @@ -45,18 +45,18 @@ public class GradientWarpDensity extends Density { double maxX = context.position.x + this.slopeRange; double maxY = context.position.y + this.slopeRange; double maxZ = context.position.z + this.slopeRange; - this.rPosition.assign(context.position); + this.rPosition.set(context.position); this.rChildContext.assign(context); this.rChildContext.position = this.rPosition; - this.rChildContext.position.assign(maxX, context.position.y, context.position.z); + this.rChildContext.position.set(maxX, context.position.y, context.position.z); double deltaX = this.warpInput.process(this.rChildContext) - valueAtOrigin; - this.rChildContext.position.assign(context.position.x, maxY, context.position.z); + this.rChildContext.position.set(context.position.x, maxY, context.position.z); double deltaY = this.warpInput.process(this.rChildContext) - valueAtOrigin; - this.rChildContext.position.assign(context.position.x, context.position.z, maxZ); + this.rChildContext.position.set(context.position.x, context.position.z, maxZ); double deltaZ = this.warpInput.process(this.rChildContext) - valueAtOrigin; - this.rGradient.assign(deltaX, deltaY, deltaZ); - this.rGradient.scale(1.0 / this.slopeRange); - this.rGradient.scale(this.warpFactor); + this.rGradient.set(deltaX, deltaY, deltaZ); + this.rGradient.mul(1.0 / this.slopeRange); + this.rGradient.mul(this.warpFactor); this.rGradient.add(context.position.x, context.position.y, context.position.z); this.rChildContext.position = this.rGradient; return this.input.process(this.rChildContext); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/MultiCacheDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/MultiCacheDensity.java index 6b364495..fe184984 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/MultiCacheDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/MultiCacheDensity.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class MultiCacheDensity extends Density { @Nonnull @@ -27,7 +27,7 @@ public class MultiCacheDensity extends Density { matchingEntry.position = new Vector3d(); } - matchingEntry.position.assign(context.position); + matchingEntry.position.set(context.position); matchingEntry.value = this.input.process(context); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/MultiMixDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/MultiMixDensity.java index 42c1d744..eb59f539 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/MultiMixDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/MultiMixDensity.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.ArrayUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Interpolation; +import com.hypixel.hytale.builtin.hytalegenerator.math.Interpolation; import java.util.ArrayList; import java.util.Comparator; import java.util.List; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/Noise2dDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/Noise2dDensity.java index f753a4dc..046fc191 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/Noise2dDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/Noise2dDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.NoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.noise.NoiseField; import javax.annotation.Nonnull; public class Noise2dDensity extends Density { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/Noise3dDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/Noise3dDensity.java index 3881e339..700d8488 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/Noise3dDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/Noise3dDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.fields.noise.NoiseField; +import com.hypixel.hytale.builtin.hytalegenerator.noise.NoiseField; import javax.annotation.Nonnull; public class Noise3dDensity extends Density { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/NormalizerDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/NormalizerDensity.java index b10b2142..f43708da 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/NormalizerDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/NormalizerDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Normalizer; +import com.hypixel.hytale.builtin.hytalegenerator.math.Normalizer; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PlaneDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PlaneDensity.java index d5cc0d73..7897218b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PlaneDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PlaneDensity.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PlaneDensity extends Density { public static final double ZERO_DELTA = 1.0E-9; @@ -69,7 +69,7 @@ public class PlaneDensity extends Density { if (context == null) { return 0.0; } else { - this.rPosition.assign(x, y, z); + this.rPosition.set(x, y, z); Vector3d p0 = context.densityAnchor; if (p0 == null) { return 0.0; @@ -79,7 +79,7 @@ public class PlaneDensity extends Density { distance = Math.abs(p0.y - this.rPosition.y); } - this.rPosition.subtract(p0); + this.rPosition.sub(p0); VectorUtil.nearestPointOnLine3d(this.rPosition, ZERO_VECTOR, this.planeNormal, this.rVectorFromPlane, this.r0, this.r1, this.r2, this.r3); distance = this.rVectorFromPlane.length(); return this.distanceCurve.get(distance); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsHorizontalPinchDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsHorizontalPinchDensity.java index 891876d2..2535fdde 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsHorizontalPinchDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsHorizontalPinchDensity.java @@ -2,11 +2,12 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.ReusableList; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PositionsHorizontalPinchDensity extends Density { @Nonnull @@ -90,13 +91,13 @@ public class PositionsHorizontalPinchDensity extends Density { return this.input.process(context); } else { if (this.cache.x == context.position.x && this.cache.z == context.position.z && !this.cache.hasValue) { - this.rWarpVector.assign(this.cache.warpVector); + this.rWarpVector.set(this.cache.warpVector); } else { this.calculateWarpVector(context, this.rWarpVector); this.cache.warpVector = this.rWarpVector; } - this.rPosition.assign(this.rWarpVector.x + context.position.x, this.rWarpVector.y + context.position.y, this.rWarpVector.z + context.position.z); + this.rPosition.set(this.rWarpVector.x + context.position.x, this.rWarpVector.y + context.position.y, this.rWarpVector.z + context.position.z); this.rChildContext.assign(context); this.rChildContext.position = this.rPosition; return this.input.process(this.rChildContext); @@ -112,12 +113,12 @@ public class PositionsHorizontalPinchDensity extends Density { this.input = inputs[0]; } - private void consumer(@Nonnull Vector3d iteratedPosition) { + private void consumer(@Nonnull Vector3d iteratedPosition, @Nonnull Control control) { double distance = Calculator.distance(iteratedPosition.x, iteratedPosition.z, this.rSamplePoint.x, this.rSamplePoint.z); if (!(distance > this.maxDistance)) { double normalizedDistance = distance / this.maxDistance; - this.rConsumerResult.assign(iteratedPosition).subtract(this.rSamplePoint); - this.rConsumerResult.setY(0.0); + this.rConsumerResult.set(iteratedPosition).sub(this.rSamplePoint); + this.rConsumerResult.y = 0.0; double radialDistance; if (this.distanceNormalized) { radialDistance = this.pinchCurve.applyAsDouble(normalizedDistance); @@ -127,13 +128,13 @@ public class PositionsHorizontalPinchDensity extends Density { } if (!(Math.abs(this.rConsumerResult.length()) < 1.0E-9)) { - this.rConsumerResult.setLength(radialDistance); + this.rConsumerResult.normalize(radialDistance); } if (this.rWarpVectors.isAtHardCapacity()) { - this.rWarpVectors.expandAndSet(this.rConsumerResult.clone()); + this.rWarpVectors.expandAndSet(new Vector3d(this.rConsumerResult)); } else { - this.rWarpVectors.expandAndGet().assign(this.rConsumerResult); + this.rWarpVectors.expandAndGet().set(this.rConsumerResult); } this.rWarpDistances.expandAndSet(normalizedDistance); @@ -141,19 +142,19 @@ public class PositionsHorizontalPinchDensity extends Density { } public void calculateWarpVector(@Nonnull Density.Context context, @Nonnull Vector3d vector_out) { - this.rMin.assign(context.position.x - this.maxDistance, this.positionsMinY, context.position.z - this.maxDistance); - this.rMax.assign(context.position.x + this.maxDistance, this.positionsMaxY, context.position.z + this.maxDistance); - this.rSamplePoint.assign(context.position); + this.rMin.set(context.position.x - this.maxDistance, this.positionsMinY, context.position.z - this.maxDistance); + this.rMax.set(context.position.x + this.maxDistance, this.positionsMaxY, context.position.z + this.maxDistance); + this.rSamplePoint.set(context.position); this.rWarpVectors.clear(); this.rWarpDistances.clear(); - this.rPositionsContext.minInclusive = this.rMin; - this.rPositionsContext.maxExclusive = this.rMax; - this.rPositionsContext.consumer = this::consumer; - this.positions.positionsIn(this.rPositionsContext); + this.rPositionsContext.bounds.min.set(this.rMin); + this.rPositionsContext.bounds.max.set(this.rMax); + this.rPositionsContext.pipe = this::consumer; + this.positions.generate(this.rPositionsContext); if (this.rWarpVectors.getSoftSize() == 0) { - vector_out.assign(0.0, 0.0, 0.0); + vector_out.set(0.0, 0.0, 0.0); } else if (this.rWarpVectors.getSoftSize() == 1) { - vector_out.assign(this.rWarpVectors.get(0)); + vector_out.set(this.rWarpVectors.get(0)); } else { int possiblePointsSize = this.rWarpVectors.getSoftSize(); this.rWeights.clear(); @@ -169,7 +170,7 @@ public class PositionsHorizontalPinchDensity extends Density { for (int i = 0; i < possiblePointsSize; i++) { double weight = this.rWeights.get(i) / totalWeight; Vector3d warpVector = this.rWarpVectors.get(i); - warpVector.scale(weight); + warpVector.mul(weight); vector_out.add(warpVector); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsPinchDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsPinchDensity.java index 98b2d0b8..6cbb0232 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsPinchDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsPinchDensity.java @@ -2,11 +2,12 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.ReusableList; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PositionsPinchDensity extends Density { @Nullable @@ -55,11 +56,11 @@ public class PositionsPinchDensity extends Density { } } - private void consumer(@Nonnull Vector3d p) { - double distance = p.distanceTo(this.rSamplePoint); + private void pipe(@Nonnull Vector3d p, @Nonnull Control control) { + double distance = p.distance(this.rSamplePoint); if (!(distance > this.maxDistance)) { double normalizedDistance = distance / this.maxDistance; - this.rWarpVector.assign(p).subtract(this.rSamplePoint); + this.rWarpVector.set(p).sub(this.rSamplePoint); double radialDistance; if (this.distanceNormalized) { radialDistance = this.pinchCurve.applyAsDouble(normalizedDistance); @@ -69,13 +70,13 @@ public class PositionsPinchDensity extends Density { } if (!(Math.abs(this.rWarpVector.length()) < 1.0E-9)) { - this.rWarpVector.setLength(radialDistance); + this.rWarpVector.normalize(radialDistance); } if (this.rWarpVectors.isAtHardCapacity()) { - this.rWarpVectors.expandAndSet(this.rWarpVector.clone()); + this.rWarpVectors.expandAndSet(new Vector3d(this.rWarpVector)); } else { - this.rWarpVectors.expandAndGet().assign(this.rWarpVector); + this.rWarpVectors.expandAndGet().set(this.rWarpVector); } this.rWarpDistances.expandAndSet(normalizedDistance); @@ -89,16 +90,16 @@ public class PositionsPinchDensity extends Density { } else if (this.positions == null) { return this.input.process(context); } else { - this.rMin.assign(context.position.x - this.maxDistance, context.position.y - this.maxDistance, context.position.z - this.maxDistance); - this.rMax.assign(context.position.x + this.maxDistance, context.position.y + this.maxDistance, context.position.z + this.maxDistance); - this.rSamplePoint.assign(context.position); + this.rMin.set(context.position.x - this.maxDistance, context.position.y - this.maxDistance, context.position.z - this.maxDistance); + this.rMax.set(context.position.x + this.maxDistance, context.position.y + this.maxDistance, context.position.z + this.maxDistance); + this.rSamplePoint.set(context.position); this.rWarpVectors.clear(); this.rWarpDistances.clear(); PositionProvider.Context positionsContext = new PositionProvider.Context(); - positionsContext.minInclusive = this.rMin; - positionsContext.maxExclusive = this.rMax; - positionsContext.consumer = this::consumer; - this.positions.positionsIn(positionsContext); + positionsContext.bounds.min.set(this.rMin); + positionsContext.bounds.max.set(this.rMax); + positionsContext.pipe = this::pipe; + this.positions.generate(positionsContext); if (this.rWarpVectors.getSoftSize() == 0) { return this.input.process(context); } else if (this.rWarpVectors.getSoftSize() == 1) { @@ -122,7 +123,7 @@ public class PositionsPinchDensity extends Density { for (int i = 0; i < possiblePointsSize; i++) { double weight = this.rWeights.get(i) / totalWeight; Vector3d warpVector = this.rWarpVectors.get(i); - warpVector.scale(weight); + warpVector.mul(weight); this.rSamplePoint.add(warpVector); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsTwistDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsTwistDensity.java index c56870d8..d6a100aa 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsTwistDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/PositionsTwistDensity.java @@ -3,11 +3,12 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.ReusableList; import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PositionsTwistDensity extends Density { @Nullable @@ -76,11 +77,11 @@ public class PositionsTwistDensity extends Density { } } - public void consumer(@Nonnull Vector3d p) { - double distance = p.distanceTo(this.rQueryPosition); + public void consumer(@Nonnull Vector3d p, @Nonnull Control control) { + double distance = p.distance(this.rQueryPosition); if (!(distance > this.maxDistance)) { double normalizedDistance = distance / this.maxDistance; - this.rWarpVector.assign(this.rSamplePoint); + this.rWarpVector.set(this.rSamplePoint); double twistAngle; if (this.distanceNormalized) { twistAngle = this.twistCurve.applyAsDouble(normalizedDistance); @@ -90,14 +91,14 @@ public class PositionsTwistDensity extends Density { twistAngle /= 180.0; twistAngle *= Math.PI; - this.rWarpVector.subtract(p); + this.rWarpVector.sub(p); VectorUtil.rotateAroundAxis(this.rWarpVector, this.twistAxis, twistAngle); this.rWarpVector.add(p); - this.rWarpVector.subtract(this.rSamplePoint); + this.rWarpVector.sub(this.rSamplePoint); if (this.rWarpVectors.isAtHardCapacity()) { - this.rWarpVectors.expandAndSet(this.rWarpVector.clone()); + this.rWarpVectors.expandAndSet(new Vector3d(this.rWarpVector)); } else { - this.rWarpVectors.expandAndGet().assign(this.rWarpVector); + this.rWarpVectors.expandAndGet().set(this.rWarpVector); } if (this.distanceNormalized) { @@ -115,10 +116,10 @@ public class PositionsTwistDensity extends Density { } else if (this.positions == null) { return this.input.process(context); } else { - this.rMin.assign(context.position.x - this.maxDistance, context.position.y - this.maxDistance, context.position.z - this.maxDistance); - this.rMax.assign(context.position.x + this.maxDistance, context.position.y + this.maxDistance, context.position.z + this.maxDistance); - this.rSamplePoint.assign(context.position); - this.rQueryPosition.assign(context.position); + this.rMin.set(context.position.x - this.maxDistance, context.position.y - this.maxDistance, context.position.z - this.maxDistance); + this.rMax.set(context.position.x + this.maxDistance, context.position.y + this.maxDistance, context.position.z + this.maxDistance); + this.rSamplePoint.set(context.position); + this.rQueryPosition.set(context.position); if (this.zeroPositionsY) { this.rQueryPosition.y = 0.0; this.rMin.y = -1.0; @@ -127,10 +128,10 @@ public class PositionsTwistDensity extends Density { this.rWarpVectors.clear(); this.rWarpDistances.clear(); - this.rPositionsContext.minInclusive = this.rMin; - this.rPositionsContext.maxExclusive = this.rMax; - this.rPositionsContext.consumer = this::consumer; - this.positions.positionsIn(this.rPositionsContext); + this.rPositionsContext.bounds.min.set(this.rMin); + this.rPositionsContext.bounds.max.set(this.rMax); + this.rPositionsContext.pipe = this::consumer; + this.positions.generate(this.rPositionsContext); if (this.rWarpVectors.getSoftSize() == 0) { return this.input.process(context); } else if (this.rWarpVectors.getSoftSize() == 1) { @@ -154,7 +155,7 @@ public class PositionsTwistDensity extends Density { for (int i = 0; i < possiblePointsSize; i++) { double weight = this.rWeights.get(i) / totalWeight; Vector3d warpVector = this.rWarpVectors.get(i); - warpVector.scale(weight); + warpVector.mul(weight); this.rSamplePoint.add(warpVector); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/RotatorDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/RotatorDensity.java index a619b074..6e61dd33 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/RotatorDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/RotatorDensity.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class RotatorDensity extends Density { @Nonnull @@ -26,7 +26,7 @@ public class RotatorDensity extends Density { this.input = input; this.spinAngle = spinAngle * Math.PI / 180.0; Vector3d yAxis = new Vector3d(0.0, 1.0, 0.0); - this.rotationAxis = newYAxis.cross(yAxis); + this.rotationAxis = newYAxis.cross(yAxis, new Vector3d()); if (this.rotationAxis.length() < 1.0E-8) { this.rotationAxis = yAxis; if (newYAxis.dot(yAxis) < 0.0) { @@ -44,7 +44,7 @@ public class RotatorDensity extends Density { this.tiltAngle = 0.0; } - this.tiltAxis = yAxis.cross(newYAxis); + this.tiltAxis = yAxis.cross(newYAxis, new Vector3d()); this.tiltAngle = Math.acos(newYAxis.dot(yAxis) / (newYAxis.length() * yAxis.length())); this.rChildPosition = new Vector3d(); this.rChildContext = new Density.Context(); @@ -55,10 +55,10 @@ public class RotatorDensity extends Density { if (this.input == null) { return 0.0; } else { - this.rChildPosition.assign(context.position); + this.rChildPosition.set(context.position); switch (this.axisSpecialCase) { case INVERTED_Y_AXIS: - this.rChildPosition.scale(-1.0); + this.rChildPosition.mul(-1.0); case NONE: VectorUtil.rotateAroundAxis(this.rChildPosition, this.tiltAxis, this.tiltAngle); case Y_AXIS: diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ScaleDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ScaleDensity.java index a59e1861..25cffc08 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ScaleDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ScaleDensity.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ScaleDensity extends Density { @Nonnull @@ -31,8 +31,8 @@ public class ScaleDensity extends Density { } else if (this.isInvalid) { return 0.0; } else { - this.rChildPosition.assign(context.position); - this.rChildPosition.scale(this.scale); + this.rChildPosition.set(context.position); + this.rChildPosition.mul(this.scale); this.rChildContext.assign(context); this.rChildContext.position = this.rChildPosition; return this.input.process(this.rChildContext); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SelectorDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SelectorDensity.java index ecbace15..3816ac32 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SelectorDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SelectorDensity.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Normalizer; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Normalizer; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ShellDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ShellDensity.java index 7c4e47ac..03397d0b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ShellDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ShellDensity.java @@ -2,10 +2,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ShellDensity extends Density { public static final double ZERO_DELTA = 1.0E-9; @@ -29,11 +28,11 @@ public class ShellDensity extends Density { @Override public double process(@Nonnull Density.Context context) { - double distance = Calculator.distance(context.position, Vector3d.ZERO); + double distance = context.position.length(); if (this.axis.length() == 0.0) { return 0.0; } else { - this.rRadialVector.assign(context.position); + this.rRadialVector.set(context.position); double amplitude = this.distanceCurve.applyAsDouble(distance); if (amplitude == 0.0) { return 0.0; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SliderDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SliderDensity.java index 43805139..df70f227 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SliderDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SliderDensity.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SliderDensity extends Density { private final double slideX; @@ -30,7 +30,7 @@ public class SliderDensity extends Density { if (this.input == null) { return 0.0; } else { - this.rChildPosition.assign(context.position.x - this.slideX, context.position.y - this.slideY, context.position.z - this.slideZ); + this.rChildPosition.set(context.position.x - this.slideX, context.position.y - this.slideY, context.position.z - this.slideZ); this.rChildContext.assign(context); this.rChildContext.position = this.rChildPosition; return this.input.process(this.rChildContext); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothCeilingDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothCeilingDensity.java index dcdcefd5..9d4ee7bb 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothCeilingDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothCeilingDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothClampDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothClampDensity.java index efd3c1c0..ba397c38 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothClampDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothClampDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothFloorDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothFloorDensity.java index 2cb20850..9b14cbaa 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothFloorDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothFloorDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothMaxDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothMaxDensity.java index 45823f06..1eb89506 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothMaxDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothMaxDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothMinDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothMinDensity.java index 23348d28..1ef5879e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothMinDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/SmoothMinDensity.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/TerrainDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/TerrainDensity.java index df0eb8e7..611e3eb3 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/TerrainDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/TerrainDensity.java @@ -1,11 +1,12 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; public class TerrainDensity extends Density { @Override public double process(@Nonnull Density.Context context) { - return context.terrainDensityProvider == null ? 0.0 : context.terrainDensityProvider.get(context.position.toVector3i()); + return context.terrainDensityProvider == null ? 0.0 : context.terrainDensityProvider.get(Vector3dUtil.toVector3i(context.position)); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/VectorWarpDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/VectorWarpDensity.java index 5768e2a2..69e5554c 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/VectorWarpDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/VectorWarpDensity.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class VectorWarpDensity extends Density { @Nullable @@ -36,9 +36,9 @@ public class VectorWarpDensity extends Density { } else { double warp = this.warpInput.process(context); warp *= this.warpFactor; - this.rSamplePoint.assign(this.warpVector); - this.rSamplePoint.setLength(1.0); - this.rSamplePoint.scale(warp); + this.rSamplePoint.set(this.warpVector); + this.rSamplePoint.normalize(1.0); + this.rSamplePoint.mul(warp); this.rSamplePoint.add(context.position); this.rChildContext.assign(context); this.rChildContext.position = this.rSamplePoint; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/XOverrideDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/XOverrideDensity.java index f56b829b..acb095ba 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/XOverrideDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/XOverrideDensity.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class XOverrideDensity extends Density { @Nonnull @@ -22,7 +22,7 @@ public class XOverrideDensity extends Density { @Override public double process(@Nonnull Density.Context context) { - this.rChildPosition.assign(this.value, context.position.y, context.position.z); + this.rChildPosition.set(this.value, context.position.y, context.position.z); this.rChildContext.assign(context); this.rChildContext.position = this.rChildPosition; return this.input.process(this.rChildContext); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/YOverrideDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/YOverrideDensity.java index e9ab8223..3cf4ea00 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/YOverrideDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/YOverrideDensity.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class YOverrideDensity extends Density { @Nonnull @@ -22,7 +22,7 @@ public class YOverrideDensity extends Density { @Override public double process(@Nonnull Density.Context context) { - this.rChildPosition.assign(context.position.x, this.value, context.position.z); + this.rChildPosition.set(context.position.x, this.value, context.position.z); this.rChildContext.assign(context); this.rChildContext.position = this.rChildPosition; return this.input.process(this.rChildContext); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/YSampledDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/YSampledDensity.java index e76befe4..eec15416 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/YSampledDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/YSampledDensity.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Interpolation; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.math.Interpolation; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class YSampledDensity extends Density { @Nonnull @@ -11,6 +11,7 @@ public class YSampledDensity extends Density { private final double sampleDistance; private final double sampleDistanceInverse; private final double sampleOffset; + private final boolean isInterpolated; private double value0; private double value1; private double y0; @@ -21,13 +22,14 @@ public class YSampledDensity extends Density { private final Vector3d rChildPosition; private final Density.Context rChildContext; - public YSampledDensity(@Nonnull Density input, double sampleDistance, double sampleOffset) { + public YSampledDensity(@Nonnull Density input, double sampleDistance, double sampleOffset, boolean isInterpolated) { assert sampleDistance > 0.0; this.input = input; this.sampleDistance = sampleDistance; this.sampleDistanceInverse = 1.0 / sampleDistance; this.sampleOffset = sampleOffset; + this.isInterpolated = isInterpolated; this.isEmpty = true; this.rChildPosition = new Vector3d(); this.rChildContext = new Density.Context(); @@ -42,9 +44,9 @@ public class YSampledDensity extends Density { this.rChildContext.position = this.rChildPosition; this.y0 = newY0; this.y1 = newY1; - this.rChildPosition.assign(context.position.x, this.y0, context.position.z); + this.rChildPosition.set(context.position.x, this.y0, context.position.z); this.value0 = this.input.process(this.rChildContext); - this.rChildPosition.assign(context.position.x, this.y1, context.position.z); + this.rChildPosition.set(context.position.x, this.y1, context.position.z); this.value1 = this.input.process(this.rChildContext); this.isEmpty = false; this.x = context.position.x; @@ -59,7 +61,7 @@ public class YSampledDensity extends Density { this.value0 = this.value1; } else { this.y0 = newY0; - this.rChildPosition.assign(context.position.x, this.y0, context.position.z); + this.rChildPosition.set(context.position.x, this.y0, context.position.z); this.value0 = this.input.process(this.rChildContext); } @@ -68,13 +70,17 @@ public class YSampledDensity extends Density { this.value1 = this.value0; } else { this.y1 = newY1; - this.rChildPosition.assign(context.position.x, this.y1, context.position.z); + this.rChildPosition.set(context.position.x, this.y1, context.position.z); this.value1 = this.input.process(this.rChildContext); } } double ratio = (context.position.y - this.y0) * this.sampleDistanceInverse; - return Interpolation.linear(this.value0, this.value1, ratio); + if (this.isInterpolated) { + return Interpolation.linear(this.value0, this.value1, ratio); + } else { + return ratio < 0.5 ? this.value0 : this.value1; + } } private double toY0(double position) { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ZOverrideDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ZOverrideDensity.java index 91bf732b..19d348ec 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ZOverrideDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/ZOverrideDensity.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ZOverrideDensity extends Density { @Nonnull @@ -22,7 +22,7 @@ public class ZOverrideDensity extends Density { @Override public double process(@Nonnull Density.Context context) { - this.rChildPosition.assign(context.position.x, context.position.y, this.value); + this.rChildPosition.set(context.position.x, context.position.y, this.value); this.rChildContext.assign(context); this.rChildContext.position = this.rChildPosition; return this.input.process(this.rChildContext); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/PositionsDensity.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/PositionsDensity.java index e83d9bbe..60258e10 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/PositionsDensity.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/PositionsDensity.java @@ -3,11 +3,11 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions.DistanceFunction; import com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes.ReturnType; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; -import java.util.function.Consumer; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PositionsDensity extends Density { @Nonnull @@ -61,16 +61,16 @@ public class PositionsDensity extends Density { @Override public double process(@Nonnull Density.Context context) { - this.rMin.assign(context.position).subtract(this.maxDistance); - this.rMax.assign(context.position).add(this.maxDistance); + this.rMin.set(context.position).sub(this.maxDistance, this.maxDistance, this.maxDistance); + this.rMax.set(context.position).add(this.maxDistance, this.maxDistance, this.maxDistance); this.rDistance[0] = Double.MAX_VALUE; this.rDistance[1] = Double.MAX_VALUE; this.rHasClosestPoint[0] = false; this.rHasClosestPoint[1] = false; - this.rClosestPoint.assign(0.0, 0.0, 0.0); - this.rPreviousClosestPoint.assign(0.0, 0.0, 0.0); - this.rLocalPoint.assign(0.0, 0.0, 0.0); - Consumer positionsConsumer = providedPoint -> { + this.rClosestPoint.set(0.0, 0.0, 0.0); + this.rPreviousClosestPoint.set(0.0, 0.0, 0.0); + this.rLocalPoint.set(0.0, 0.0, 0.0); + Pipe.One positionsPipe = (providedPoint, control) -> { this.rLocalPoint.x = providedPoint.x - context.position.x; this.rLocalPoint.y = providedPoint.y - context.position.y; this.rLocalPoint.z = providedPoint.z - context.position.z; @@ -79,25 +79,25 @@ public class PositionsDensity extends Density { this.rDistance[1] = Math.max(Math.min(this.rDistance[1], newDistance), this.rDistance[0]); if (newDistance < this.rDistance[0]) { this.rDistance[0] = newDistance; - this.rPreviousClosestPoint.assign(this.rClosestPoint); - this.rClosestPoint.assign(providedPoint); + this.rPreviousClosestPoint.set(this.rClosestPoint); + this.rClosestPoint.set(providedPoint); this.rHasClosestPoint[1] = this.rHasClosestPoint[0]; this.rHasClosestPoint[0] = true; } } }; PositionProvider.Context positionsContext = new PositionProvider.Context(); - positionsContext.minInclusive = this.rMin; - positionsContext.maxExclusive = this.rMax; - positionsContext.consumer = positionsConsumer; - this.positionProvider.positionsIn(positionsContext); + positionsContext.bounds.min.set(this.rMin); + positionsContext.bounds.max.set(this.rMax); + positionsContext.pipe = positionsPipe; + this.positionProvider.generate(positionsContext); this.rDistance[0] = Math.sqrt(this.rDistance[0]); this.rDistance[1] = Math.sqrt(this.rDistance[1]); return this.returnType .get( this.rDistance[0], this.rDistance[1], - context.position.clone(), + new Vector3d(context.position), this.rHasClosestPoint[0] ? this.rClosestPoint : null, this.rHasClosestPoint[1] ? this.rPreviousClosestPoint : null, context diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/DistanceFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/DistanceFunction.java index 328497c4..69786cc0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/DistanceFunction.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/DistanceFunction.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class DistanceFunction { public abstract double getDistance(@Nonnull Vector3d var1); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/EuclideanDistanceFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/EuclideanDistanceFunction.java index 57008315..9274abe7 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/EuclideanDistanceFunction.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/EuclideanDistanceFunction.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EuclideanDistanceFunction extends DistanceFunction { @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/ManhattanDistanceFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/ManhattanDistanceFunction.java index cb2a4076..5366f390 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/ManhattanDistanceFunction.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/distancefunctions/ManhattanDistanceFunction.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.distancefunctions; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ManhattanDistanceFunction extends DistanceFunction { @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/CellValueReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/CellValueReturnType.java index 900e3f52..7f3b4368 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/CellValueReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/CellValueReturnType.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CellValueReturnType extends ReturnType { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/CurveReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/CurveReturnType.java index 275e2c05..bcfd86f2 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/CurveReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/CurveReturnType.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CurveReturnType extends ReturnType { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/DensityReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/DensityReturnType.java index 2f498d1c..3ce15f7a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/DensityReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/DensityReturnType.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.retur import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.math.Range; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.objects.Object2DoubleAVLTreeMap; import it.unimi.dsi.fastutil.objects.Object2DoubleMap; import java.util.Comparator; @@ -10,6 +9,7 @@ import java.util.LinkedList; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class DensityReturnType extends ReturnType { @Nonnull @@ -57,12 +57,12 @@ public class DensityReturnType extends ReturnType { ) { double distanceFromWall = Double.MAX_VALUE; if (closestPoint0 != null && this.calculateDistanceFromWall) { - distance0 = this.rScaledSamplePointClone.assign(samplePoint).subtract(closestPoint0).length(); + distance0 = this.rScaledSamplePointClone.set(samplePoint).sub(closestPoint0).length(); double fromMaxDistance = Math.abs(super.maxDistance - distance0); if (closestPoint1 == null) { distanceFromWall = fromMaxDistance; } else { - distance1 = this.rScaledSamplePointClone.assign(samplePoint).subtract(closestPoint1).length(); + distance1 = this.rScaledSamplePointClone.set(samplePoint).sub(closestPoint1).length(); double l = distance1 / this.maxDistance; double fromOtherCell = Math.abs(distance1 - distance0) / 2.0; distanceFromWall = fromOtherCell; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2AddReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2AddReturnType.java index fa588dc6..0f4a6576 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2AddReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2AddReturnType.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Distance2AddReturnType extends ReturnType { @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2DivReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2DivReturnType.java index 8303c0db..68f3d770 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2DivReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2DivReturnType.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Distance2DivReturnType extends ReturnType { @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2MulReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2MulReturnType.java index e88b2579..e5a4e597 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2MulReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2MulReturnType.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Distance2MulReturnType extends ReturnType { @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2ReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2ReturnType.java index 6d17e8cc..fc57f395 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2ReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2ReturnType.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Distance2ReturnType extends ReturnType { @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2SubReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2SubReturnType.java index fb8eea12..a30f073c 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2SubReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/Distance2SubReturnType.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Distance2SubReturnType extends ReturnType { @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/DistanceReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/DistanceReturnType.java index ea862f34..c6246fb9 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/DistanceReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/DistanceReturnType.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class DistanceReturnType extends ReturnType { @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/ReturnType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/ReturnType.java index 90b02ed8..2fb97fb3 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/ReturnType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/density/nodes/positions/returntypes/ReturnType.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.density.nodes.positions.returntypes; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class ReturnType { protected double maxDistance = Double.MAX_VALUE; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/TerrainDensityProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/TerrainDensityProvider.java similarity index 55% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/TerrainDensityProvider.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/TerrainDensityProvider.java index a74cce29..da736e8e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/TerrainDensityProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/TerrainDensityProvider.java @@ -1,7 +1,7 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem; +package com.hypixel.hytale.builtin.hytalegenerator.engine; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; @FunctionalInterface public interface TerrainDensityProvider { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/NBufferBundle.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/BufferBundle.java similarity index 67% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/NBufferBundle.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/BufferBundle.java index 26e8a53b..65d3c15f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/NBufferBundle.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/BufferBundle.java @@ -1,11 +1,10 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle; +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle; +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.Buffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -15,40 +14,41 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; +import org.joml.Vector3i; -public class NBufferBundle implements MemInstrument { +public class BufferBundle implements MemInstrument { @Nonnull - private final Map grids = new HashMap<>(); + private final Map grids = new HashMap<>(); @Nonnull - public NBufferBundle.Grid createGrid(@Nonnull NBufferType bufferType, int capacity) { + public BufferBundle.Grid createGrid(@Nonnull BufferType bufferType, int capacity) { assert capacity >= 0; assert !this.grids.containsKey(bufferType); assert !this.existingGridHasBufferTypeIndex(bufferType.index); - NBufferBundle.Grid grid = new NBufferBundle.Grid(bufferType, capacity); + BufferBundle.Grid grid = new BufferBundle.Grid(bufferType, capacity); this.grids.put(bufferType, grid); return grid; } @Nonnull - public NBufferBundle.Access createBufferAccess(@Nonnull NBufferType bufferType, @Nonnull Bounds3i bounds_bufferGrid) { + public BufferBundle.Access createBufferAccess(@Nonnull BufferType bufferType, @Nonnull Bounds3i bounds_bufferGrid) { assert bounds_bufferGrid.isCorrect(); return this.getGrid(bufferType).openAccess(bounds_bufferGrid); } public void closeALlAccesses() { - for (NBufferBundle.Grid grid : this.grids.values()) { + for (BufferBundle.Grid grid : this.grids.values()) { grid.closeAllAccesses(); } } @Nonnull - public NBufferBundle.Grid getGrid(@Nonnull NBufferType contentType) { - NBufferBundle.Grid grid = this.grids.get(contentType); + public BufferBundle.Grid getGrid(@Nonnull BufferType contentType) { + BufferBundle.Grid grid = this.grids.get(contentType); assert grid != null; @@ -60,7 +60,7 @@ public class NBufferBundle implements MemInstrument { public MemInstrument.Report getMemoryUsage() { long size_bytes = 16L; - for (Entry entry : this.grids.entrySet()) { + for (Entry entry : this.grids.entrySet()) { size_bytes += entry.getValue().getMemoryUsage().size_bytes(); } @@ -68,7 +68,7 @@ public class NBufferBundle implements MemInstrument { } private boolean existingGridHasBufferTypeIndex(int bufferTypeIndex) { - for (NBufferBundle.Grid grid : this.grids.values()) { + for (BufferBundle.Grid grid : this.grids.values()) { if (grid.bufferType.index == bufferTypeIndex) { return true; } @@ -78,13 +78,13 @@ public class NBufferBundle implements MemInstrument { } @Nonnull - public NBufferBundle.MemoryReport createMemoryReport() { - NBufferBundle.MemoryReport memoryReport = new NBufferBundle.MemoryReport(); + public BufferBundle.MemoryReport createMemoryReport() { + BufferBundle.MemoryReport memoryReport = new BufferBundle.MemoryReport(); - for (NBufferBundle.Grid grid : this.grids.values()) { + for (BufferBundle.Grid grid : this.grids.values()) { MemInstrument.Report gridUsage = grid.getMemoryUsage(); int gridBufferCount = grid.buffers.size(); - memoryReport.gridEntries.add(new NBufferBundle.MemoryReport.GridEntry(gridUsage, gridBufferCount, grid.bufferType)); + memoryReport.gridEntries.add(new BufferBundle.MemoryReport.GridEntry(gridUsage, gridBufferCount, grid.bufferType)); } return memoryReport; @@ -92,14 +92,14 @@ public class NBufferBundle implements MemInstrument { public static class Access implements MemInstrument { @Nonnull - private final NBufferBundle.Grid grid; + private final BufferBundle.Grid grid; @Nonnull private final Bounds3i bounds_bufferGrid; @Nonnull - private final NBufferBundle.Grid.TrackedBuffer[] buffers; + private final BufferBundle.Grid.TrackedBuffer[] buffers; private boolean isClosed; - private Access(@Nonnull NBufferBundle.Grid grid, @Nonnull Bounds3i bounds_bufferGrid) { + private Access(@Nonnull BufferBundle.Grid grid, @Nonnull Bounds3i bounds_bufferGrid) { assert bounds_bufferGrid.isCorrect(); this.grid = grid; @@ -108,24 +108,34 @@ public class NBufferBundle implements MemInstrument { this.bounds_bufferGrid.max.y = 40; Vector3i boundsSize_bufferGrid = this.bounds_bufferGrid.getSize(); int bufferCount = boundsSize_bufferGrid.x * boundsSize_bufferGrid.y * boundsSize_bufferGrid.z; - this.buffers = new NBufferBundle.Grid.TrackedBuffer[bufferCount]; + this.buffers = new BufferBundle.Grid.TrackedBuffer[bufferCount]; this.isClosed = false; } @Nonnull - public NBufferBundle.Access.View createView(@Nonnull Bounds3i viewBounds_bufferGrid) { + public BufferBundle.Access.View createView(@Nonnull Bounds3i viewBounds_bufferGrid) { assert this.bounds_bufferGrid.contains(viewBounds_bufferGrid); - return new NBufferBundle.Access.View(this, viewBounds_bufferGrid); + return new BufferBundle.Access.View(this, viewBounds_bufferGrid); } @Nonnull - public NBufferBundle.Access.View createView() { - return new NBufferBundle.Access.View(this, this.bounds_bufferGrid); + public BufferBundle.Access.View createView() { + return new BufferBundle.Access.View(this, this.bounds_bufferGrid); } @Nonnull - public NBufferBundle.Grid.TrackedBuffer getBuffer(@Nonnull Vector3i position_bufferGrid) { + public BufferBundle.Grid.TrackedBuffer getBuffer(int x_bufferGrid, int y_bufferGrid, int z_bufferGrid) { + assert !this.isClosed; + + assert this.bounds_bufferGrid.contains(x_bufferGrid, y_bufferGrid, z_bufferGrid); + + int index = GridUtils.toIndexFromPositionYXZ(x_bufferGrid, y_bufferGrid, z_bufferGrid, this.bounds_bufferGrid); + return this.buffers[index]; + } + + @Nonnull + public BufferBundle.Grid.TrackedBuffer getBuffer(@Nonnull Vector3i position_bufferGrid) { assert !this.isClosed; assert this.bounds_bufferGrid.contains(position_bufferGrid); @@ -157,18 +167,17 @@ public class NBufferBundle implements MemInstrument { assert this.bounds_bufferGrid.min.y == 0 && this.bounds_bufferGrid.max.y == 40; - Vector3i position_bufferGrid = this.bounds_bufferGrid.min.clone(); - position_bufferGrid.setY(0); - NBufferBundle.Grid.TrackedBuffer[] trackedBuffersOutput = new NBufferBundle.Grid.TrackedBuffer[40]; + Vector3i position_bufferGrid = new Vector3i(this.bounds_bufferGrid.min); + position_bufferGrid.y = 0; + BufferBundle.Grid.TrackedBuffer[] trackedBuffersOutput = new BufferBundle.Grid.TrackedBuffer[40]; for (position_bufferGrid.z = this.bounds_bufferGrid.min.z; position_bufferGrid.z < this.bounds_bufferGrid.max.z; position_bufferGrid.z++) { for (position_bufferGrid.x = this.bounds_bufferGrid.min.x; position_bufferGrid.x < this.bounds_bufferGrid.max.x; position_bufferGrid.x++) { - position_bufferGrid.setY(0); + position_bufferGrid.y = 0; this.grid.ensureBufferColumnExists(position_bufferGrid, trackedBuffersOutput); int i = 0; for (position_bufferGrid.y = 0; position_bufferGrid.y < 40; position_bufferGrid.y++) { - position_bufferGrid.dropHash(); int index = GridUtils.toIndexFromPositionYXZ(position_bufferGrid, this.bounds_bufferGrid); this.buffers[index] = trackedBuffersOutput[i]; i++; @@ -179,11 +188,11 @@ public class NBufferBundle implements MemInstrument { public static class View { @Nonnull - private final NBufferBundle.Access access; + private final BufferBundle.Access access; @Nonnull private final Bounds3i bounds_bufferGrid; - private View(@Nonnull NBufferBundle.Access access, @Nonnull Bounds3i bounds_bufferGrid) { + private View(@Nonnull BufferBundle.Access access, @Nonnull Bounds3i bounds_bufferGrid) { assert access.bounds_bufferGrid.contains(bounds_bufferGrid); this.access = access; @@ -191,7 +200,16 @@ public class NBufferBundle implements MemInstrument { } @Nonnull - public NBufferBundle.Grid.TrackedBuffer getBuffer(@Nonnull Vector3i position_bufferGrid) { + public BufferBundle.Grid.TrackedBuffer getBuffer(int x_bufferGrid, int y_bufferGrid, int z_bufferGrid) { + assert !this.access.isClosed; + + assert this.bounds_bufferGrid.contains(x_bufferGrid, y_bufferGrid, z_bufferGrid); + + return this.access.getBuffer(x_bufferGrid, y_bufferGrid, z_bufferGrid); + } + + @Nonnull + public BufferBundle.Grid.TrackedBuffer getBuffer(@Nonnull Vector3i position_bufferGrid) { assert !this.access.isClosed; assert this.bounds_bufferGrid.contains(position_bufferGrid); @@ -208,16 +226,16 @@ public class NBufferBundle implements MemInstrument { public static class Grid implements MemInstrument { @Nonnull - private final NBufferType bufferType; + private final BufferType bufferType; @Nonnull - private final Map buffers; + private final Map buffers; @Nonnull private final Deque oldestColumnEntryDeque_bufferGrid; private final int capacity; @Nonnull - private final List accessors; + private final List accessors; - private Grid(@Nonnull NBufferType bufferType, int capacity) { + private Grid(@Nonnull BufferType bufferType, int capacity) { this.bufferType = bufferType; this.buffers = new HashMap<>(); this.oldestColumnEntryDeque_bufferGrid = new ArrayDeque<>(); @@ -226,13 +244,13 @@ public class NBufferBundle implements MemInstrument { } @Nonnull - public NBufferType getBufferType() { + public BufferType getBufferType() { return this.bufferType; } @Nonnull - public NBufferBundle.Access openAccess(@Nonnull Bounds3i bounds_bufferGrid) { - NBufferBundle.Access access = new NBufferBundle.Access(this, bounds_bufferGrid); + public BufferBundle.Access openAccess(@Nonnull Bounds3i bounds_bufferGrid) { + BufferBundle.Access access = new BufferBundle.Access(this, bounds_bufferGrid); this.accessors.add(access); access.loadGrid(); return access; @@ -240,7 +258,7 @@ public class NBufferBundle implements MemInstrument { public void closeAllAccesses() { for (int i = this.accessors.size() - 1; i >= 0; i--) { - NBufferBundle.Access access = this.accessors.get(i); + BufferBundle.Access access = this.accessors.get(i); access.close(); } } @@ -253,32 +271,32 @@ public class NBufferBundle implements MemInstrument { size_bytes += 4L * this.buffers.size(); size_bytes += 32L * this.buffers.size(); - for (NBufferBundle.Grid.TrackedBuffer buffer : this.buffers.values()) { + for (BufferBundle.Grid.TrackedBuffer buffer : this.buffers.values()) { size_bytes += buffer.getMemoryUsage().size_bytes(); } size_bytes += 8L * this.accessors.size(); - for (NBufferBundle.Access access : this.accessors) { + for (BufferBundle.Access access : this.accessors) { size_bytes += access.getMemoryUsage().size_bytes(); } return new MemInstrument.Report(size_bytes); } - private void ensureBufferColumnExists(@Nonnull Vector3i position_bufferGrid, @Nonnull NBufferBundle.Grid.TrackedBuffer[] trackedBuffersOut) { + private void ensureBufferColumnExists(@Nonnull Vector3i position_bufferGrid, @Nonnull BufferBundle.Grid.TrackedBuffer[] trackedBuffersOut) { assert position_bufferGrid.y == 0; assert trackedBuffersOut.length == 40; - NBufferBundle.Grid.TrackedBuffer buffer = this.buffers.get(position_bufferGrid); + BufferBundle.Grid.TrackedBuffer buffer = this.buffers.get(position_bufferGrid); if (buffer == null) { this.createBufferColumn(position_bufferGrid, trackedBuffersOut); } else { Vector3i positionClone_bufferGrid = new Vector3i(position_bufferGrid); for (int i = 0; i < trackedBuffersOut.length; i++) { - positionClone_bufferGrid.setY(i + 0); + positionClone_bufferGrid.y = i + 0; trackedBuffersOut[i] = this.buffers.get(positionClone_bufferGrid); assert trackedBuffersOut[i] != null; @@ -286,7 +304,7 @@ public class NBufferBundle implements MemInstrument { } } - private void createBufferColumn(@Nonnull Vector3i position_bufferGrid, @Nonnull NBufferBundle.Grid.TrackedBuffer[] trackedBuffersOut) { + private void createBufferColumn(@Nonnull Vector3i position_bufferGrid, @Nonnull BufferBundle.Grid.TrackedBuffer[] trackedBuffersOut) { assert !this.buffers.containsKey(position_bufferGrid); assert trackedBuffersOut.length == 40; @@ -296,12 +314,12 @@ public class NBufferBundle implements MemInstrument { for (int y = 0; y < 40; y++) { Vector3i finalPosition_bufferGrid = new Vector3i(position_bufferGrid.x, y, position_bufferGrid.z); - NBufferBundle.Tracker tracker = new NBufferBundle.Tracker(); - NBuffer buffer = this.bufferType.bufferSupplier.get(); + BufferBundle.Tracker tracker = new BufferBundle.Tracker(); + Buffer buffer = this.bufferType.bufferSupplier.get(); assert this.bufferType.isValid(buffer); - trackedBuffersOut[i] = new NBufferBundle.Grid.TrackedBuffer(tracker, buffer); + trackedBuffersOut[i] = new BufferBundle.Grid.TrackedBuffer(tracker, buffer); this.buffers.put(finalPosition_bufferGrid, trackedBuffersOut[i]); i++; } @@ -343,7 +361,7 @@ public class NBufferBundle implements MemInstrument { Vector3i removalPosition_bufferGrid = new Vector3i(position_bufferGrid); for (int y = 0; y < 40; y++) { - removalPosition_bufferGrid.setY(y); + removalPosition_bufferGrid.y = y; this.buffers.remove(removalPosition_bufferGrid); } } @@ -351,7 +369,7 @@ public class NBufferBundle implements MemInstrument { private boolean isBufferColumnInAccess(@Nonnull Vector3i position_bufferGrid) { assert position_bufferGrid.y == 0; - for (NBufferBundle.Access access : this.accessors) { + for (BufferBundle.Access access : this.accessors) { if (access.bounds_bufferGrid.contains(position_bufferGrid)) { return true; } @@ -360,7 +378,7 @@ public class NBufferBundle implements MemInstrument { return false; } - public record TrackedBuffer(@Nonnull NBufferBundle.Tracker tracker, @Nonnull NBuffer buffer) implements MemInstrument { + public record TrackedBuffer(@Nonnull BufferBundle.Tracker tracker, @Nonnull Buffer buffer) implements MemInstrument { @Nonnull @Override public MemInstrument.Report getMemoryUsage() { @@ -372,7 +390,7 @@ public class NBufferBundle implements MemInstrument { public static class MemoryReport { @Nonnull - public final List gridEntries = new ArrayList<>(); + public final List gridEntries = new ArrayList<>(); @Nonnull @Override @@ -387,7 +405,7 @@ public class NBufferBundle implements MemInstrument { StringBuilder builder = new StringBuilder(); long total_mb = 0L; - for (NBufferBundle.MemoryReport.GridEntry entry : this.gridEntries) { + for (BufferBundle.MemoryReport.GridEntry entry : this.gridEntries) { total_mb += entry.report.size_bytes(); } @@ -395,14 +413,14 @@ public class NBufferBundle implements MemInstrument { builder.append("Memory Usage Report\n"); builder.append("Buffers Memory Usage: ").append(total_mb).append(" mb\n"); - for (NBufferBundle.MemoryReport.GridEntry entry : this.gridEntries) { + for (BufferBundle.MemoryReport.GridEntry entry : this.gridEntries) { builder.append(entry.toString(1)); } return builder.toString(); } - public record GridEntry(MemInstrument.Report report, int bufferCount, @Nonnull NBufferType bufferType) { + public record GridEntry(MemInstrument.Report report, int bufferCount, @Nonnull BufferType bufferType) { @Nonnull public String toString(int indentation) { long size_mb = this.report.size_bytes() / 1000000L; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/Buffer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/Buffer.java new file mode 100644 index 00000000..7aa0c6ec --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/Buffer.java @@ -0,0 +1,6 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers; + +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; + +public abstract class Buffer implements MemInstrument { +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NCountedPixelBuffer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/CountedPixelBuffer.java similarity index 75% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NCountedPixelBuffer.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/CountedPixelBuffer.java index 25eeafd0..ef1a7cb6 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NCountedPixelBuffer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/CountedPixelBuffer.java @@ -1,37 +1,42 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers; +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers; import com.hypixel.hytale.builtin.hytalegenerator.ArrayUtil; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; -public class NCountedPixelBuffer extends NPixelBuffer { +public class CountedPixelBuffer extends PixelBuffer { public static final int BUFFER_SIZE_BITS = 3; @Nonnull public static final Vector3i SIZE_VOXEL_GRID = new Vector3i(8, 1, 8); @Nonnull - public static final Bounds3i BOUNDS_VOXEL_GRID = new Bounds3i(Vector3i.ZERO, SIZE_VOXEL_GRID); + public static final Bounds3i BOUNDS_VOXEL_GRID = new Bounds3i(Vector3iUtil.ZERO, SIZE_VOXEL_GRID); @Nonnull private final Class pixelType; @Nonnull - private NCountedPixelBuffer.State state; + private CountedPixelBuffer.State state; @Nullable - private NCountedPixelBuffer.CountedArrayContents countedArrayContents; + private CountedPixelBuffer.CountedArrayContents countedArrayContents; @Nullable private T singleValue; - public NCountedPixelBuffer(@Nonnull Class voxelType) { + public CountedPixelBuffer(@Nonnull Class voxelType) { this.pixelType = voxelType; - this.state = NCountedPixelBuffer.State.EMPTY; + this.state = CountedPixelBuffer.State.EMPTY; this.countedArrayContents = null; this.singleValue = null; } + private static int index(@Nonnull Vector3i position) { + return position.y + position.x * SIZE_VOXEL_GRID.y + position.z * SIZE_VOXEL_GRID.y * SIZE_VOXEL_GRID.x; + } + @Nullable @Override public T getPixelContent(@Nonnull Vector3i position) { @@ -64,7 +69,7 @@ public class NCountedPixelBuffer extends NPixelBuffer { } break; default: - this.state = NCountedPixelBuffer.State.SINGLE_VALUE; + this.state = CountedPixelBuffer.State.SINGLE_VALUE; this.singleValue = value; } } @@ -89,14 +94,14 @@ public class NCountedPixelBuffer extends NPixelBuffer { } } - public void copyFrom(@Nonnull NCountedPixelBuffer sourceBuffer) { + public void copyFrom(@Nonnull CountedPixelBuffer sourceBuffer) { this.state = sourceBuffer.state; switch (this.state) { case SINGLE_VALUE: this.singleValue = sourceBuffer.singleValue; break; case ARRAY: - this.countedArrayContents = new NCountedPixelBuffer.CountedArrayContents<>(); + this.countedArrayContents = new CountedPixelBuffer.CountedArrayContents<>(); this.countedArrayContents.copyFrom(sourceBuffer.countedArrayContents); break; default: @@ -116,28 +121,24 @@ public class NCountedPixelBuffer extends NPixelBuffer { } private void switchFromSingleValueToArray() { - assert this.state == NCountedPixelBuffer.State.SINGLE_VALUE; + assert this.state == CountedPixelBuffer.State.SINGLE_VALUE; - this.state = NCountedPixelBuffer.State.ARRAY; - this.countedArrayContents = new NCountedPixelBuffer.CountedArrayContents<>(); + this.state = CountedPixelBuffer.State.ARRAY; + this.countedArrayContents = new CountedPixelBuffer.CountedArrayContents<>(); Arrays.fill(this.countedArrayContents.array, this.singleValue); this.countedArrayContents.allBiomes.add(this.singleValue); this.singleValue = null; } - private static int index(@Nonnull Vector3i position) { - return position.y + position.x * SIZE_VOXEL_GRID.y + position.z * SIZE_VOXEL_GRID.y * SIZE_VOXEL_GRID.x; - } - public static class CountedArrayContents implements MemInstrument { @Nonnull - private final T[] array = (T[])(new Object[NCountedPixelBuffer.SIZE_VOXEL_GRID.x - * NCountedPixelBuffer.SIZE_VOXEL_GRID.y - * NCountedPixelBuffer.SIZE_VOXEL_GRID.z]); + private final T[] array = (T[])(new Object[CountedPixelBuffer.SIZE_VOXEL_GRID.x + * CountedPixelBuffer.SIZE_VOXEL_GRID.y + * CountedPixelBuffer.SIZE_VOXEL_GRID.z]); @Nonnull private final List allBiomes = new ArrayList<>(1); - public void copyFrom(@Nonnull NCountedPixelBuffer.CountedArrayContents countedArrayContents) { + public void copyFrom(@Nonnull CountedPixelBuffer.CountedArrayContents countedArrayContents) { ArrayUtil.copy(countedArrayContents.array, this.array); this.allBiomes.clear(); this.allBiomes.addAll(countedArrayContents.allBiomes); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NEntityBuffer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/EntityBuffer.java similarity index 76% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NEntityBuffer.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/EntityBuffer.java index ac600406..3781341c 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NEntityBuffer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/EntityBuffer.java @@ -1,14 +1,14 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers; +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; -import com.hypixel.hytale.builtin.hytalegenerator.props.entity.EntityPlacementData; +import com.hypixel.hytale.builtin.hytalegenerator.EntityPlacementData; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class NEntityBuffer extends NBuffer { +public class EntityBuffer extends Buffer { @Nullable private List entities = null; private boolean isReference = false; @@ -44,7 +44,7 @@ public class NEntityBuffer extends NBuffer { return new MemInstrument.Report(size_bytes); } - public void copyFrom(@Nonnull NEntityBuffer sourceBuffer) { + public void copyFrom(@Nonnull EntityBuffer sourceBuffer) { this.entities = sourceBuffer.entities; this.isReference = true; } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NPixelBuffer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/PixelBuffer.java similarity index 68% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NPixelBuffer.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/PixelBuffer.java index c1c4338a..bb992688 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NPixelBuffer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/PixelBuffer.java @@ -1,10 +1,10 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers; +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; -public abstract class NPixelBuffer extends NBuffer { +public abstract class PixelBuffer extends Buffer { public static final int BUFFER_SIZE_BITS = 3; @Nonnull public static final Vector3i SIZE = new Vector3i(8, 1, 8); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NSimplePixelBuffer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/SimplePixelBuffer.java similarity index 70% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NSimplePixelBuffer.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/SimplePixelBuffer.java index c3f59839..b36b8a1b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NSimplePixelBuffer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/SimplePixelBuffer.java @@ -1,32 +1,37 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers; +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers; import com.hypixel.hytale.builtin.hytalegenerator.ArrayUtil; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; -public class NSimplePixelBuffer extends NPixelBuffer { +public class SimplePixelBuffer extends PixelBuffer { @Nonnull - private static final Bounds3i bounds = new Bounds3i(Vector3i.ZERO, SIZE); + private static final Bounds3i bounds = new Bounds3i(Vector3iUtil.ZERO, SIZE); @Nonnull private final Class pixelType; @Nonnull - private NSimplePixelBuffer.State state; + private SimplePixelBuffer.State state; @Nullable - private NSimplePixelBuffer.ArrayContents arrayContents; + private SimplePixelBuffer.ArrayContents arrayContents; @Nullable private T singleValue; - public NSimplePixelBuffer(@Nonnull Class pixelType) { + public SimplePixelBuffer(@Nonnull Class pixelType) { this.pixelType = pixelType; - this.state = NSimplePixelBuffer.State.EMPTY; + this.state = SimplePixelBuffer.State.EMPTY; this.arrayContents = null; this.singleValue = null; } + private static int index(@Nonnull Vector3i position) { + return position.y + position.x * SIZE.y + position.z * SIZE.y * SIZE.x; + } + @Nullable @Override public T getPixelContent(@Nonnull Vector3i position) { @@ -56,7 +61,7 @@ public class NSimplePixelBuffer extends NPixelBuffer { this.arrayContents.array[index(position)] = value; break; default: - this.state = NSimplePixelBuffer.State.SINGLE_VALUE; + this.state = SimplePixelBuffer.State.SINGLE_VALUE; this.singleValue = value; } } @@ -67,14 +72,14 @@ public class NSimplePixelBuffer extends NPixelBuffer { return this.pixelType; } - public void copyFrom(@Nonnull NSimplePixelBuffer sourceBuffer) { + public void copyFrom(@Nonnull SimplePixelBuffer sourceBuffer) { this.state = sourceBuffer.state; switch (this.state) { case SINGLE_VALUE: this.singleValue = sourceBuffer.singleValue; break; case ARRAY: - this.arrayContents = new NSimplePixelBuffer.ArrayContents<>(); + this.arrayContents = new SimplePixelBuffer.ArrayContents<>(); ArrayUtil.copy(sourceBuffer.arrayContents.array, this.arrayContents.array); break; default: @@ -95,26 +100,22 @@ public class NSimplePixelBuffer extends NPixelBuffer { private void ensureContents() { if (this.arrayContents == null) { - this.arrayContents = new NSimplePixelBuffer.ArrayContents<>(); + this.arrayContents = new SimplePixelBuffer.ArrayContents<>(); } } private void switchFromSingleValueToArray() { - assert this.state == NSimplePixelBuffer.State.SINGLE_VALUE; + assert this.state == SimplePixelBuffer.State.SINGLE_VALUE; - this.state = NSimplePixelBuffer.State.ARRAY; - this.arrayContents = new NSimplePixelBuffer.ArrayContents<>(); + this.state = SimplePixelBuffer.State.ARRAY; + this.arrayContents = new SimplePixelBuffer.ArrayContents<>(); Arrays.fill(this.arrayContents.array, this.singleValue); this.singleValue = null; } - private static int index(@Nonnull Vector3i position) { - return position.y + position.x * SIZE.y + position.z * SIZE.y * SIZE.x; - } - public static class ArrayContents implements MemInstrument { @Nonnull - private final T[] array = (T[])(new Object[NPixelBuffer.SIZE.x * NPixelBuffer.SIZE.y * NPixelBuffer.SIZE.z]); + private final T[] array = (T[])(new Object[PixelBuffer.SIZE.x * PixelBuffer.SIZE.y * PixelBuffer.SIZE.z]); @Nonnull @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NVoxelBuffer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/VoxelBuffer.java similarity index 59% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NVoxelBuffer.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/VoxelBuffer.java index ec8e3461..2d5e0c06 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NVoxelBuffer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/VoxelBuffer.java @@ -1,38 +1,51 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers; +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers; import com.hypixel.hytale.builtin.hytalegenerator.ArrayUtil; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.MemInstrument; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; -public class NVoxelBuffer extends NBuffer { +public class VoxelBuffer extends Buffer { public static final int BUFFER_SIZE_BITS = 3; @Nonnull public static final Vector3i SIZE = new Vector3i(8, 8, 8); @Nonnull - private static final Bounds3i bounds = new Bounds3i(Vector3i.ZERO, SIZE); + private static final Bounds3i bounds = new Bounds3i(Vector3iUtil.ZERO, SIZE); @Nonnull private final Class voxelType; @Nonnull - private NVoxelBuffer.State state; + private VoxelBuffer.State state; @Nullable - private NVoxelBuffer.ArrayContents arrayContents; + private VoxelBuffer.ArrayContents arrayContents; @Nullable private T singleValue; @Nullable - private NVoxelBuffer referenceBuffer; + private VoxelBuffer referenceBuffer; - public NVoxelBuffer(@Nonnull Class voxelType) { + public VoxelBuffer(@Nonnull Class voxelType) { this.voxelType = voxelType; - this.state = NVoxelBuffer.State.EMPTY; + this.state = VoxelBuffer.State.EMPTY; this.arrayContents = null; this.singleValue = null; this.referenceBuffer = null; } + @Nullable + public T getVoxelContent(int x, int y, int z) { + assert bounds.contains(x, y, z); + + return (T)(switch (this.state) { + case SINGLE_VALUE -> this.singleValue; + case ARRAY -> this.arrayContents.array[index(x, y, z)]; + case REFERENCE -> this.referenceBuffer.getVoxelContent(x, y, z); + default -> null; + }); + } + @Nullable public T getVoxelContent(@Nonnull Vector3i position) { assert bounds.contains(position); @@ -50,8 +63,8 @@ public class NVoxelBuffer extends NBuffer { return this.voxelType; } - public void setVoxelContent(@Nonnull Vector3i position, @Nullable T value) { - assert bounds.contains(position); + public void setVoxelContent(int x, int y, int z, @Nullable T value) { + assert bounds.contains(x, y, z); switch (this.state) { case SINGLE_VALUE: @@ -60,31 +73,35 @@ public class NVoxelBuffer extends NBuffer { } this.switchFromSingleValueToArray(); - this.setVoxelContent(position, value); + this.setVoxelContent(x, y, z, value); break; case ARRAY: - this.arrayContents.array[index(position)] = value; + this.arrayContents.array[index(x, y, z)] = value; break; case REFERENCE: this.dereference(); - this.setVoxelContent(position, value); + this.setVoxelContent(x, y, z, value); break; default: - this.state = NVoxelBuffer.State.SINGLE_VALUE; + this.state = VoxelBuffer.State.SINGLE_VALUE; this.singleValue = value; } } - public void reference(@Nonnull NVoxelBuffer sourceBuffer) { - this.state = NVoxelBuffer.State.REFERENCE; + public void setVoxelContent(@Nonnull Vector3i position, @Nullable T value) { + this.setVoxelContent(position.x, position.y, position.z, value); + } + + public void reference(@Nonnull VoxelBuffer sourceBuffer) { + this.state = VoxelBuffer.State.REFERENCE; this.referenceBuffer = this.lastReference(sourceBuffer); this.singleValue = null; this.arrayContents = null; } @Nonnull - private NVoxelBuffer lastReference(@Nonnull NVoxelBuffer sourceBuffer) { - while (sourceBuffer.state == NVoxelBuffer.State.REFERENCE) { + private VoxelBuffer lastReference(@Nonnull VoxelBuffer sourceBuffer) { + while (sourceBuffer.state == VoxelBuffer.State.REFERENCE) { sourceBuffer = sourceBuffer.referenceBuffer; } @@ -96,7 +113,7 @@ public class NVoxelBuffer extends NBuffer { public MemInstrument.Report getMemoryUsage() { long size_bytes = 128L; size_bytes += 40L; - if (this.state == NVoxelBuffer.State.ARRAY) { + if (this.state == VoxelBuffer.State.ARRAY) { size_bytes += this.arrayContents.getMemoryUsage().size_bytes(); } @@ -104,16 +121,16 @@ public class NVoxelBuffer extends NBuffer { } private void switchFromSingleValueToArray() { - assert this.state == NVoxelBuffer.State.SINGLE_VALUE; + assert this.state == VoxelBuffer.State.SINGLE_VALUE; - this.state = NVoxelBuffer.State.ARRAY; - this.arrayContents = new NVoxelBuffer.ArrayContents<>(); + this.state = VoxelBuffer.State.ARRAY; + this.arrayContents = new VoxelBuffer.ArrayContents<>(); Arrays.fill(this.arrayContents.array, this.singleValue); this.singleValue = null; } private void dereference() { - assert this.state == NVoxelBuffer.State.REFERENCE; + assert this.state == VoxelBuffer.State.REFERENCE; this.state = this.referenceBuffer.state; switch (this.state) { @@ -121,7 +138,7 @@ public class NVoxelBuffer extends NBuffer { this.singleValue = this.referenceBuffer.singleValue; break; case ARRAY: - this.arrayContents = new NVoxelBuffer.ArrayContents<>(); + this.arrayContents = new VoxelBuffer.ArrayContents<>(); ArrayUtil.copy(this.referenceBuffer.arrayContents.array, this.arrayContents.array); break; case REFERENCE: @@ -132,13 +149,17 @@ public class NVoxelBuffer extends NBuffer { } } + private static int index(int x, int y, int z) { + return y + x * SIZE.y + z * SIZE.y * SIZE.x; + } + private static int index(@Nonnull Vector3i position) { return position.y + position.x * SIZE.y + position.z * SIZE.y * SIZE.x; } public static class ArrayContents implements MemInstrument { @Nonnull - private final T[] array = (T[])(new Object[NVoxelBuffer.SIZE.x * NVoxelBuffer.SIZE.y * NVoxelBuffer.SIZE.z]); + private final T[] array = (T[])(new Object[VoxelBuffer.SIZE.x * VoxelBuffer.SIZE.y * VoxelBuffer.SIZE.z]); @Nonnull @Override diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/type/NBufferType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/type/BufferType.java similarity index 65% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/type/NBufferType.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/type/BufferType.java index 89a71566..d4dfafc1 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/type/NBufferType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/type/BufferType.java @@ -1,19 +1,19 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type; +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.Buffer; import java.util.function.Supplier; import javax.annotation.Nonnull; -public class NBufferType { +public class BufferType { @Nonnull public final Class bufferClass; public final int index; @Nonnull - public final Supplier bufferSupplier; + public final Supplier bufferSupplier; @Nonnull public final String name; - public NBufferType(@Nonnull String name, int index, @Nonnull Class bufferClass, @Nonnull Supplier bufferSupplier) { + public BufferType(@Nonnull String name, int index, @Nonnull Class bufferClass, @Nonnull Supplier bufferSupplier) { this.name = name; this.index = index; this.bufferClass = bufferClass; @@ -22,7 +22,7 @@ public class NBufferType { @Override public boolean equals(Object o) { - return !(o instanceof NBufferType that) + return !(o instanceof BufferType that) ? false : this.index == that.index && this.bufferClass.equals(that.bufferClass) && this.bufferSupplier.equals(that.bufferSupplier); } @@ -31,7 +31,7 @@ public class NBufferType { return this.bufferClass.equals(bufferClass); } - public boolean isValid(@Nonnull NBuffer buffer) { + public boolean isValid(@Nonnull Buffer buffer) { return this.bufferClass.isInstance(buffer); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/type/NParametrizedBufferType.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/type/ParametrizedBufferType.java similarity index 66% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/type/NParametrizedBufferType.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/type/ParametrizedBufferType.java index 61e4350b..3f0fa8db 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/type/NParametrizedBufferType.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/bufferbundle/buffers/type/ParametrizedBufferType.java @@ -1,16 +1,16 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type; +package com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.Buffer; import java.util.Objects; import java.util.function.Supplier; import javax.annotation.Nonnull; -public class NParametrizedBufferType extends NBufferType { +public class ParametrizedBufferType extends BufferType { @Nonnull public final Class parameterClass; - public NParametrizedBufferType( - @Nonnull String name, int index, @Nonnull Class bufferClass, @Nonnull Class parameterClass, @Nonnull Supplier bufferSupplier + public ParametrizedBufferType( + @Nonnull String name, int index, @Nonnull Class bufferClass, @Nonnull Class parameterClass, @Nonnull Supplier bufferSupplier ) { super(name, index, bufferClass, bufferSupplier); this.parameterClass = parameterClass; @@ -21,13 +21,13 @@ public class NParametrizedBufferType extends NBufferType { } @Override - public boolean isValid(@Nonnull NBuffer buffer) { + public boolean isValid(@Nonnull Buffer buffer) { return this.bufferClass.isInstance(buffer); } @Override public boolean equals(Object o) { - if (o instanceof NParametrizedBufferType that) { + if (o instanceof ParametrizedBufferType that) { return !super.equals(o) ? false : Objects.equals(this.parameterClass, that.parameterClass); } else { return false; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/ChunkGenerator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/ChunkGenerator.java similarity index 74% rename from src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/ChunkGenerator.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/ChunkGenerator.java index e0b8c99a..51406b59 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/ChunkGenerator.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/ChunkGenerator.java @@ -1,11 +1,12 @@ -package com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator; +package com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedChunk; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public interface ChunkGenerator { - @Nonnull + @Nullable GeneratedChunk generate(@Nonnull ChunkRequest.Arguments var1); @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/ChunkRequest.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/ChunkRequest.java similarity index 96% rename from src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/ChunkRequest.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/ChunkRequest.java index e0cb3ca4..e117630e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/ChunkRequest.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/ChunkRequest.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator; +package com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator; import java.util.Objects; import java.util.function.LongPredicate; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/FallbackGenerator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/FallbackGenerator.java similarity index 81% rename from src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/FallbackGenerator.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/FallbackGenerator.java index a54ecae7..74c3e1fc 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/chunkgenerator/FallbackGenerator.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/FallbackGenerator.java @@ -1,18 +1,20 @@ -package com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator; +package com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.EmptyPositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedBlockChunk; import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedBlockStateChunk; import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedChunk; import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedEntityChunk; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class FallbackGenerator implements ChunkGenerator { @Nonnull public static final FallbackGenerator INSTANCE = new FallbackGenerator(); - @NonNullDecl + @Nullable @Override public GeneratedChunk generate(@Nonnull ChunkRequest.Arguments arguments) { return new GeneratedChunk( @@ -26,6 +28,6 @@ public class FallbackGenerator implements ChunkGenerator { @NonNullDecl @Override public PositionProvider getSpawnPositions() { - return PositionProvider.noPositionProvider(); + return EmptyPositionProvider.INSTANCE; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/NStagedChunkGenerator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/StagedChunkGenerator.java similarity index 77% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/NStagedChunkGenerator.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/StagedChunkGenerator.java index 950d898d..b30ea17f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/NStagedChunkGenerator.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/chunkgenerator/StagedChunkGenerator.java @@ -1,32 +1,30 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem; +package com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator; import com.hypixel.hytale.builtin.hytalegenerator.ArrayUtil; import com.hypixel.hytale.builtin.hytalegenerator.FutureUtils; +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.ChunkGenerator; -import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.ChunkRequest; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.EntityBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.SimplePixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.VoxelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.ParametrizedBufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.TimeInstrument; +import com.hypixel.hytale.builtin.hytalegenerator.engine.stages.Stage; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.EntityBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.PixelBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.VoxelBufferView; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.material.SolidMaterial; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NEntityBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NSimplePixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.TimeInstrument; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NStage; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NEntityBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NPixelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NVoxelBufferView; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; import com.hypixel.hytale.server.core.universe.world.chunk.environment.EnvironmentChunk; @@ -47,25 +45,28 @@ import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; +import org.joml.Vector3i; -public class NStagedChunkGenerator implements ChunkGenerator { +public class StagedChunkGenerator implements ChunkGenerator { public static final int WORLD_MIN_Y_BUFFER_GRID = 0; public static final int WORLD_MAX_Y_BUFFER_GRID = 40; public static final int WORLD_HEIGHT_BUFFER_GRID = 40; @Nonnull - public static final Bounds3i CHUNK_BOUNDS_BUFFER_GRID = new Bounds3i(Vector3i.ZERO, new Vector3i(4, 40, 4)); + public static final Bounds3i CHUNK_BOUNDS_BUFFER_GRID = new Bounds3i(Vector3iUtil.ZERO, new Vector3i(4, 40, 4)); @Nonnull public static final Bounds3i SINGLE_BUFFER_TILE_BOUNDS_BUFFER_GRID = new Bounds3i( - new Vector3i(0, 0, 0), new Vector3i(NVoxelBuffer.SIZE.x, 320, NVoxelBuffer.SIZE.x) + new Vector3i(0, 0, 0), new Vector3i(VoxelBuffer.SIZE.x, 320, VoxelBuffer.SIZE.x) ); - private NBufferType materialOutput_bufferType; - private NBufferType tintOutput_bufferType; - private NBufferType environmentOutput_bufferType; - private NBufferType entityOutput_bufferType; - private NStage[] stages; + private BufferType materialOutput_bufferType; + private BufferType tintOutput_bufferType; + private BufferType environmentOutput_bufferType; + private BufferType entityOutput_bufferType; + private Stage[] stages; private Bounds3i[] stagesOutputBounds_bufferGrid; - private NBufferBundle bufferBundle; + private BufferBundle bufferBundle; private ExecutorService concurrentExecutor; private MaterialCache materialCache; private WorkerIndexer workerIndexer; @@ -76,10 +77,10 @@ public class NStagedChunkGenerator implements ChunkGenerator { private long totalCacheBufferRequests; private long missedCacheBufferRequests; - private NStagedChunkGenerator() { + private StagedChunkGenerator() { } - @NonNullDecl + @Nullable @Override public GeneratedChunk generate(@Nonnull ChunkRequest.Arguments arguments) { if (arguments.stillNeeded() != null && !arguments.stillNeeded().test(arguments.index())) { @@ -90,7 +91,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { TimeInstrument.Probe contentGeneration_timeProbe = total_timeProbe.createProbe("Content Generation").start(); TimeInstrument.Probe accessInit_timeProbe = contentGeneration_timeProbe.createProbe("Access Initialization").start(); Bounds3i localChunkBounds_bufferGrid = GridUtils.createChunkBounds_bufferGrid(arguments.x(), arguments.z()); - Map accessMap = this.createAccesses(localChunkBounds_bufferGrid); + Map accessMap = this.createAccesses(localChunkBounds_bufferGrid); accessInit_timeProbe.stop(); for (int stageIndex = 0; stageIndex < this.stages.length; stageIndex++) { @@ -98,12 +99,12 @@ public class NStagedChunkGenerator implements ChunkGenerator { .start(); TimeInstrument.Probe stagePrep_timeProbe = stage_timeProbe.createProbe("Preparation").start(); int stageIndexConst = stageIndex; - NStage stage = this.stages[stageIndex]; - List outputTypes = stage.getOutputTypes(); - List outputGrids = new ArrayList<>(outputTypes.size()); + Stage stage = this.stages[stageIndex]; + List outputTypes = stage.getOutputTypes(); + List outputGrids = new ArrayList<>(outputTypes.size()); - for (NBufferType type : outputTypes) { - NBufferBundle.Grid grid = this.bufferBundle.getGrid(type); + for (BufferType type : outputTypes) { + BufferBundle.Grid grid = this.bufferBundle.getGrid(type); outputGrids.add(grid); } @@ -121,12 +122,11 @@ public class NStagedChunkGenerator implements ChunkGenerator { tilePos_bufferGrid.z < stageChunkOutputBounds_bufferGrid.max.z; tilePos_bufferGrid.z++ ) { - tilePos_bufferGrid.dropHash(); this.totalCacheBufferRequests++; boolean isOutputCached = true; - for (NBufferBundle.Grid grid : outputGrids) { - NBufferBundle.Access access = accessMap.get(grid.getBufferType()); + for (BufferBundle.Grid grid : outputGrids) { + BufferBundle.Access access = accessMap.get(grid.getBufferType()); if (!isColumnCached(access, tilePos_bufferGrid, stageIndex)) { isOutputCached = false; break; @@ -135,7 +135,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { if (!isOutputCached) { this.missedCacheBufferRequests++; - positions_bufferGrid.add(tilePos_bufferGrid.clone()); + positions_bufferGrid.add(new Vector3i(tilePos_bufferGrid)); } } } @@ -204,7 +204,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { total_timeProbe.stop(); this.timeInstrument.takeSample(total_timeProbe); if (this.statsCheckpoints.contains(this.generatedChunkCount)) { - NBufferBundle.MemoryReport bufferMemoryReport = this.bufferBundle.createMemoryReport(); + BufferBundle.MemoryReport bufferMemoryReport = this.bufferBundle.createMemoryReport(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(this.timeInstrument.toString()) .append(bufferMemoryReport.toString()) @@ -225,18 +225,18 @@ public class NStagedChunkGenerator implements ChunkGenerator { } @Nonnull - private Map createAccesses(@Nonnull Bounds3i localChunkBounds_bufferGrid) { - Map accessMap = new HashMap<>(); + private Map createAccesses(@Nonnull Bounds3i localChunkBounds_bufferGrid) { + Map accessMap = new HashMap<>(); for (int stageIndex = 0; stageIndex < this.stages.length; stageIndex++) { - NStage stage = this.stages[stageIndex]; - List outputTypes = stage.getOutputTypes(); + Stage stage = this.stages[stageIndex]; + List outputTypes = stage.getOutputTypes(); Bounds3i bounds_bufferGrid = this.stagesOutputBounds_bufferGrid[stageIndex].clone(); bounds_bufferGrid.stack(CHUNK_BOUNDS_BUFFER_GRID); bounds_bufferGrid.offset(localChunkBounds_bufferGrid.min); - for (NBufferType bufferType : outputTypes) { - NBufferBundle.Access access = this.bufferBundle.createBufferAccess(bufferType, bounds_bufferGrid); + for (BufferType bufferType : outputTypes) { + BufferBundle.Access access = this.bufferBundle.createBufferAccess(bufferType, bounds_bufferGrid); accessMap.put(bufferType, access); } } @@ -246,35 +246,37 @@ public class NStagedChunkGenerator implements ChunkGenerator { @Nonnull private Runnable createTileTask( - int stageIndex, @Nonnull Vector3i position_bufferTileGrid, @Nonnull WorkerIndexer.Id workerId, @Nonnull Map accessMap + int stageIndex, @Nonnull Vector3i position_bufferTileGrid, @Nonnull WorkerIndexer.Id workerId, @Nonnull Map accessMap ) { - NStage stage = this.stages[stageIndex]; - Map inputTypesAndBounds_tileGrid = stage.getInputTypesAndBounds_bufferGrid(); - List outputTypes = stage.getOutputTypes(); + Stage stage = this.stages[stageIndex]; + Map inputTypesAndBounds_tileGrid = stage.getInputTypesAndBounds_bufferGrid(); + List outputTypes = stage.getOutputTypes(); int bufferAccessCount = inputTypesAndBounds_tileGrid.size() + outputTypes.size(); - NStage.Context context = new NStage.Context(new HashMap<>(bufferAccessCount), workerId); + Stage.Context context = new Stage.Context(new HashMap<>(bufferAccessCount), workerId); - for (Entry entry : inputTypesAndBounds_tileGrid.entrySet()) { - NBufferType bufferType = entry.getKey(); + for (Entry entry : inputTypesAndBounds_tileGrid.entrySet()) { + BufferType bufferType = entry.getKey(); Bounds3i localInputBounds_bufferGrid = entry.getValue().clone().offset(position_bufferTileGrid); - NBufferBundle.Access.View bufferAccess = accessMap.get(bufferType).createView(localInputBounds_bufferGrid); + BufferBundle.Access.View bufferAccess = accessMap.get(bufferType).createView(localInputBounds_bufferGrid); context.bufferAccess.put(bufferType, bufferAccess); } - for (NBufferType bufferType : stage.getOutputTypes()) { + for (BufferType bufferType : stage.getOutputTypes()) { assert !context.bufferAccess.containsKey(bufferType); Bounds3i columnBounds_bufferGrid = GridUtils.createColumnBounds_bufferGrid(position_bufferTileGrid, 0, 40); - NBufferBundle.Access.View bufferAccess = accessMap.get(bufferType).createView(columnBounds_bufferGrid); + BufferBundle.Access.View bufferAccess = accessMap.get(bufferType).createView(columnBounds_bufferGrid); context.bufferAccess.put(bufferType, bufferAccess); } - Vector3i bufferPositionClone_bufferTileGrid = position_bufferTileGrid.clone(); + Vector3i bufferPositionClone_bufferTileGrid = new Vector3i(position_bufferTileGrid); return () -> { - stage.run(context); - - for (NBufferType outputType : stage.getOutputTypes()) { - updateTrackersForColumn(stageIndex, accessMap.get(outputType).createView(), bufferPositionClone_bufferTileGrid); + try { + stage.run(context); + } finally { + for (BufferType outputType : stage.getOutputTypes()) { + updateTrackersForColumn(stageIndex, accessMap.get(outputType).createView(), bufferPositionClone_bufferTileGrid); + } } }; } @@ -285,8 +287,8 @@ public class NStagedChunkGenerator implements ChunkGenerator { ) { Bounds3i chunkBounds_voxelGrid = GridUtils.createChunkBounds_voxelGrid(arguments.x(), arguments.z()); Bounds3i chunkBounds_bufferGrid = GridUtils.createChunkBounds_bufferGrid(arguments.x(), arguments.z()); - NBufferBundle.Access materialBufferAccess = this.bufferBundle.createBufferAccess(this.materialOutput_bufferType, chunkBounds_bufferGrid); - VoxelSpace materialVoxelSpace = new NVoxelBufferView<>(materialBufferAccess.createView(), Material.class); + BufferBundle.Access materialBufferAccess = this.bufferBundle.createBufferAccess(this.materialOutput_bufferType, chunkBounds_bufferGrid); + VoxelSpace materialVoxelSpace = new VoxelBufferView<>(materialBufferAccess.createView(), Material.class); TimeInstrument.Probe timeProbe = transfer_timeProbe.createProbe("Block States"); return CompletableFuture.runAsync(() -> { timeProbe.start(); @@ -295,7 +297,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { for (position_voxelGrid.x = chunkBounds_voxelGrid.min.x; position_voxelGrid.x < chunkBounds_voxelGrid.max.x; position_voxelGrid.x++) { for (position_voxelGrid.z = chunkBounds_voxelGrid.min.z; position_voxelGrid.z < chunkBounds_voxelGrid.max.z; position_voxelGrid.z++) { for (position_voxelGrid.y = chunkBounds_voxelGrid.min.y; position_voxelGrid.y < chunkBounds_voxelGrid.max.y; position_voxelGrid.y++) { - SolidMaterial solidMaterial = materialVoxelSpace.getContent(position_voxelGrid).solid(); + SolidMaterial solidMaterial = materialVoxelSpace.get(position_voxelGrid).solid(); if (solidMaterial != null && solidMaterial.holder != null) { blockStateChunk.setState(position_voxelGrid.x, position_voxelGrid.y, position_voxelGrid.z, solidMaterial.holder); } @@ -320,8 +322,8 @@ public class NStagedChunkGenerator implements ChunkGenerator { ) { Bounds3i chunkBounds_voxelGrid = GridUtils.createChunkBounds_voxelGrid(arguments.x(), arguments.z()); Bounds3i chunkBounds_bufferGrid = GridUtils.createChunkBounds_bufferGrid(arguments.x(), arguments.z()); - NBufferBundle.Access materialBufferAccess = this.bufferBundle.createBufferAccess(this.materialOutput_bufferType, chunkBounds_bufferGrid); - VoxelSpace materialVoxelSpace = new NVoxelBufferView<>(materialBufferAccess.createView(), Material.class); + BufferBundle.Access materialBufferAccess = this.bufferBundle.createBufferAccess(this.materialOutput_bufferType, chunkBounds_bufferGrid); + VoxelSpace materialVoxelSpace = new VoxelBufferView<>(materialBufferAccess.createView(), Material.class); GeneratedBlockChunk blockChunk = generatedChunk.getBlockChunk(); Holder[] sections = generatedChunk.getSections(); FluidSection[] fluidSections = new FluidSection[sections.length]; @@ -354,7 +356,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { world_voxelGrid.x = x_voxelGrid + chunkBounds_voxelGrid.min.x; world_voxelGrid.y = y_voxelGrid + chunkBounds_voxelGrid.min.y; world_voxelGrid.z = z_voxelGrid + chunkBounds_voxelGrid.min.z; - Material material = materialVoxelSpace.getContent(world_voxelGrid); + Material material = materialVoxelSpace.get(world_voxelGrid); if (material != null && !material.equals(this.materialCache.EMPTY)) { blockChunk.setBlock( x_voxelGrid, y_voxelGrid, z_voxelGrid, material.solid().blockId, material.solid().rotation, material.solid().filler @@ -390,8 +392,8 @@ public class NStagedChunkGenerator implements ChunkGenerator { ) { Bounds3i chunkBounds_voxelGrid = GridUtils.createChunkBounds_voxelGrid(arguments.x(), arguments.z()); Bounds3i chunkBounds_bufferGrid = GridUtils.createChunkBounds_bufferGrid(arguments.x(), arguments.z()); - NBufferBundle.Access tintBufferAccess = this.bufferBundle.createBufferAccess(this.tintOutput_bufferType, chunkBounds_bufferGrid); - VoxelSpace tintVoxelSpace = new NPixelBufferView<>(tintBufferAccess.createView(), Integer.class); + BufferBundle.Access tintBufferAccess = this.bufferBundle.createBufferAccess(this.tintOutput_bufferType, chunkBounds_bufferGrid); + VoxelSpace tintVoxelSpace = new PixelBufferView<>(tintBufferAccess.createView(), Integer.class); GeneratedBlockChunk blockChunk = generatedChunk.getBlockChunk(); TimeInstrument.Probe tintsTransfer_timeProbe = transfer_timeProbe.createProbe("Tints"); return CompletableFuture.runAsync(() -> { @@ -403,7 +405,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { for (int z_voxelGrid = 0; z_voxelGrid < 32; z_voxelGrid++) { worldPosition_voxelGrid.x = x_voxelGrid + chunkBounds_voxelGrid.min.x; worldPosition_voxelGrid.z = z_voxelGrid + chunkBounds_voxelGrid.min.z; - Integer tint = tintVoxelSpace.getContent(worldPosition_voxelGrid); + Integer tint = tintVoxelSpace.get(worldPosition_voxelGrid); if (tint == null) { blockChunk.setTint(x_voxelGrid, z_voxelGrid, 0); } else { @@ -429,8 +431,8 @@ public class NStagedChunkGenerator implements ChunkGenerator { ) { Bounds3i chunkBounds_voxelGrid = GridUtils.createChunkBounds_voxelGrid(arguments.x(), arguments.z()); Bounds3i chunkBounds_bufferGrid = GridUtils.createChunkBounds_bufferGrid(arguments.x(), arguments.z()); - NBufferBundle.Access environmentBufferAccess = this.bufferBundle.createBufferAccess(this.environmentOutput_bufferType, chunkBounds_bufferGrid); - VoxelSpace environmentVoxelSpace = new NVoxelBufferView<>(environmentBufferAccess.createView(), Integer.class); + BufferBundle.Access environmentBufferAccess = this.bufferBundle.createBufferAccess(this.environmentOutput_bufferType, chunkBounds_bufferGrid); + VoxelSpace environmentVoxelSpace = new VoxelBufferView<>(environmentBufferAccess.createView(), Integer.class); EnvironmentChunk.BulkWriter bulkWriter = new EnvironmentChunk.BulkWriter(); int sectionCounter = 0; Vector3i taskSize = new Vector3i(8, 0, 8); @@ -457,8 +459,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { world_voxelGrid.x = x_voxelGrid_final + chunkBounds_voxelGrid.min.x; world_voxelGrid.y = y_voxelGrid + chunkBounds_voxelGrid.min.y; world_voxelGrid.z = z_voxelGrid_final + chunkBounds_voxelGrid.min.z; - world_voxelGrid.dropHash(); - Integer environment = environmentVoxelSpace.getContent(world_voxelGrid); + Integer environment = environmentVoxelSpace.get(world_voxelGrid); assert environment != null; @@ -477,7 +478,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { return FutureUtils.allOf(futures).thenRun(() -> { timeProbe.start(); bulkWriter.write(generatedChunk.getBlockChunk().getEnvironmentChunk()); - timeProbe.start(); + timeProbe.stop(); }).handle((r, e) -> { if (e == null) { return (Void)r; @@ -493,8 +494,8 @@ public class NStagedChunkGenerator implements ChunkGenerator { @Nonnull ChunkRequest.Arguments arguments, @Nonnull GeneratedChunk generatedChunk, @Nonnull TimeInstrument.Probe transfer_timeProbe ) { Bounds3i chunkBounds_bufferGrid = GridUtils.createChunkBounds_bufferGrid(arguments.x(), arguments.z()); - NBufferBundle.Access entityBufferAccess = this.bufferBundle.createBufferAccess(this.entityOutput_bufferType, chunkBounds_bufferGrid); - NEntityBufferView entityView = new NEntityBufferView(entityBufferAccess.createView()); + BufferBundle.Access entityBufferAccess = this.bufferBundle.createBufferAccess(this.entityOutput_bufferType, chunkBounds_bufferGrid); + EntityBufferView entityView = new EntityBufferView(entityBufferAccess.createView()); GeneratedEntityChunk entityChunk = generatedChunk.getEntityChunk(); TimeInstrument.Probe entitesTransfer_timeProbe = transfer_timeProbe.createProbe("Entities"); return CompletableFuture.runAsync(() -> { @@ -531,7 +532,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { Bounds3i bounds_bufferGrid = this.stagesOutputBounds_bufferGrid[stageIndex]; Vector3i size_bufferGrid = bounds_bufferGrid.getSize().add(3, 0, 3); Vector3d size_chunkGrid = new Vector3d(size_bufferGrid); - size_chunkGrid.scale(0.25); + size_chunkGrid.mul(0.25); builder.append("\t".repeat(indentation)).append(this.stages[stageIndex].getName()).append(" (Stage ").append(stageIndex).append("):\n"); builder.append("\t".repeat(indentation + 1)) .append("Output Size (Buffer Column): {x=") @@ -566,41 +567,40 @@ public class NStagedChunkGenerator implements ChunkGenerator { } private static void setBoundsToWorldHeight_bufferGrid(@Nonnull Bounds3i bounds_bufferGrid) { - bounds_bufferGrid.min.setY(0); - bounds_bufferGrid.max.setY(40); + bounds_bufferGrid.min.y = 0; + bounds_bufferGrid.max.y = 40; } - private static boolean isColumnCached(@Nonnull NBufferBundle.Access access, @Nonnull Vector3i position_bufferGrid, int stageIndex) { + private static boolean isColumnCached(@Nonnull BufferBundle.Access access, @Nonnull Vector3i position_bufferGrid, int stageIndex) { assert position_bufferGrid.y == 0; - NBufferBundle.Tracker tracker = access.getBuffer(position_bufferGrid).tracker(); + BufferBundle.Tracker tracker = access.getBuffer(position_bufferGrid).tracker(); return tracker.stageIndex == stageIndex; } - private static void updateTrackersForColumn(int stageIndex, @Nonnull NBufferBundle.Access.View access, @Nonnull Vector3i position_bufferGrid) { + private static void updateTrackersForColumn(int stageIndex, @Nonnull BufferBundle.Access.View access, @Nonnull Vector3i position_bufferGrid) { for (position_bufferGrid.y = 0; position_bufferGrid.y < 40; position_bufferGrid.y++) { - position_bufferGrid.dropHash(); - NBufferBundle.Tracker tracker = access.getBuffer(position_bufferGrid).tracker(); + BufferBundle.Tracker tracker = access.getBuffer(position_bufferGrid).tracker(); tracker.stageIndex = stageIndex; } } public static class Builder { @Nonnull - public final NParametrizedBufferType MATERIAL_OUTPUT_BUFFER_TYPE = new NParametrizedBufferType( - "MaterialResult", -1, NVoxelBuffer.class, Material.class, () -> new NVoxelBuffer<>(Material.class) + public final ParametrizedBufferType MATERIAL_OUTPUT_BUFFER_TYPE = new ParametrizedBufferType( + "MaterialResult", -1, VoxelBuffer.class, Material.class, () -> new VoxelBuffer<>(Material.class) ); @Nonnull - public final NParametrizedBufferType TINT_OUTPUT_BUFFER_TYPE = new NParametrizedBufferType( - "TintResult", -3, NSimplePixelBuffer.class, Integer.class, () -> new NSimplePixelBuffer<>(Integer.class) + public final ParametrizedBufferType TINT_OUTPUT_BUFFER_TYPE = new ParametrizedBufferType( + "TintResult", -3, SimplePixelBuffer.class, Integer.class, () -> new SimplePixelBuffer<>(Integer.class) ); @Nonnull - public final NParametrizedBufferType ENVIRONMENT_OUTPUT_BUFFER_TYPE = new NParametrizedBufferType( - "EnvironmentResult", -4, NVoxelBuffer.class, Integer.class, () -> new NVoxelBuffer<>(Integer.class) + public final ParametrizedBufferType ENVIRONMENT_OUTPUT_BUFFER_TYPE = new ParametrizedBufferType( + "EnvironmentResult", -4, VoxelBuffer.class, Integer.class, () -> new VoxelBuffer<>(Integer.class) ); @Nonnull - public final NBufferType ENTITY_OUTPUT_BUFFER_TYPE = new NBufferType("EntityResult", -5, NEntityBuffer.class, NEntityBuffer::new); - private List stages = new ArrayList<>(); + public final BufferType ENTITY_OUTPUT_BUFFER_TYPE = new BufferType("EntityResult", -5, EntityBuffer.class, EntityBuffer::new); + private List stages = new ArrayList<>(); private ExecutorService concurrentExecutor; private MaterialCache materialCache; private WorkerIndexer workerIndexer; @@ -612,7 +612,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { private double targetPlayerCount; @Nonnull - public NStagedChunkGenerator build() { + public StagedChunkGenerator build() { assert this.concurrentExecutor != null; assert this.materialCache != null; @@ -625,17 +625,17 @@ public class NStagedChunkGenerator implements ChunkGenerator { assert this.spawnPositions != null; - NStagedChunkGenerator instance = new NStagedChunkGenerator(); + StagedChunkGenerator instance = new StagedChunkGenerator(); instance.materialOutput_bufferType = this.MATERIAL_OUTPUT_BUFFER_TYPE; instance.tintOutput_bufferType = this.TINT_OUTPUT_BUFFER_TYPE; instance.environmentOutput_bufferType = this.ENVIRONMENT_OUTPUT_BUFFER_TYPE; instance.entityOutput_bufferType = this.ENTITY_OUTPUT_BUFFER_TYPE; - instance.stages = new NStage[this.stages.size()]; + instance.stages = new Stage[this.stages.size()]; this.stages.toArray(instance.stages); - Set allUsedBufferTypes = this.createListOfAllBufferTypes(); + Set allUsedBufferTypes = this.createListOfAllBufferTypes(); Map> laterToEalierStageMap = this.createStageDependencyMap(); instance.stagesOutputBounds_bufferGrid = this.createTotalOutputBoundsArray(laterToEalierStageMap); - instance.bufferBundle = new NBufferBundle(); + instance.bufferBundle = new BufferBundle(); instance.bufferBundle .createGrid(this.MATERIAL_OUTPUT_BUFFER_TYPE, this.resolveBufferCapacity(this.MATERIAL_OUTPUT_BUFFER_TYPE, instance.stagesOutputBounds_bufferGrid)); instance.bufferBundle @@ -647,7 +647,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { instance.bufferBundle .createGrid(this.ENTITY_OUTPUT_BUFFER_TYPE, this.resolveBufferCapacity(this.ENTITY_OUTPUT_BUFFER_TYPE, instance.stagesOutputBounds_bufferGrid)); - for (NBufferType bufferType : allUsedBufferTypes) { + for (BufferType bufferType : allUsedBufferTypes) { if (!this.isGeneratorOutputBufferType(bufferType)) { instance.bufferBundle.createGrid(bufferType, this.resolveBufferCapacity(bufferType, instance.stagesOutputBounds_bufferGrid)); } @@ -664,33 +664,33 @@ public class NStagedChunkGenerator implements ChunkGenerator { } @Nonnull - public NStagedChunkGenerator.Builder withStats(@Nonnull String statsHeader, @Nonnull Set statsCheckpoints) { + public StagedChunkGenerator.Builder withStats(@Nonnull String statsHeader, @Nonnull Set statsCheckpoints) { this.statsHeader = statsHeader; this.statsCheckpoints = new HashSet<>(statsCheckpoints); return this; } @Nonnull - public NStagedChunkGenerator.Builder withSpawnPositions(@Nonnull PositionProvider spawnPositions) { + public StagedChunkGenerator.Builder withSpawnPositions(@Nonnull PositionProvider spawnPositions) { this.spawnPositions = spawnPositions; return this; } @Nonnull - public NStagedChunkGenerator.Builder withConcurrentExecutor(@Nonnull ExecutorService executor, @Nonnull WorkerIndexer workerIndexer) { + public StagedChunkGenerator.Builder withConcurrentExecutor(@Nonnull ExecutorService executor, @Nonnull WorkerIndexer workerIndexer) { this.concurrentExecutor = executor; this.workerIndexer = workerIndexer; return this; } @Nonnull - public NStagedChunkGenerator.Builder withMaterialCache(@Nonnull MaterialCache materialCache) { + public StagedChunkGenerator.Builder withMaterialCache(@Nonnull MaterialCache materialCache) { this.materialCache = materialCache; return this; } @Nonnull - public NStagedChunkGenerator.Builder withBufferCapacity(double factor, double targetViewDistance, double targetPlayerCount) { + public StagedChunkGenerator.Builder withBufferCapacity(double factor, double targetViewDistance, double targetPlayerCount) { assert factor >= 0.0; assert targetViewDistance >= 0.0; @@ -704,22 +704,22 @@ public class NStagedChunkGenerator implements ChunkGenerator { } @Nonnull - public NStagedChunkGenerator.Builder appendStage(@Nonnull NStage stage) { + public StagedChunkGenerator.Builder appendStage(@Nonnull Stage stage) { this.stages.add(stage); return this; } @Nonnull private List createStagesThatReadFrom(int stageIndex) { - NStage stage = this.stages.get(stageIndex); + Stage stage = this.stages.get(stageIndex); List stagesThatReadFromThis = new ArrayList<>(); - List outputTypes = stage.getOutputTypes(); + List outputTypes = stage.getOutputTypes(); for (int i = 0; i < outputTypes.size(); i++) { - NBufferType outputType = outputTypes.get(i); + BufferType outputType = outputTypes.get(i); for (int j = 0; j < this.stages.size(); j++) { - NStage dependentStage = this.stages.get(j); + Stage dependentStage = this.stages.get(j); if (dependentStage.getInputTypesAndBounds_bufferGrid().containsKey(outputType)) { stagesThatReadFromThis.add(j); } @@ -746,7 +746,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { return dependencyMap; } - private int resolveBufferCapacity(@Nonnull NBufferType bufferType, @Nonnull Bounds3i[] stagesOutputBounds) { + private int resolveBufferCapacity(@Nonnull BufferType bufferType, @Nonnull Bounds3i[] stagesOutputBounds) { int stageIndex = 0; while (stageIndex < stagesOutputBounds.length && !this.stages.get(stageIndex).getOutputTypes().contains(bufferType)) { @@ -772,7 +772,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { if (size.x == 1 && size.z == 1) { return 0; } else { - double viewDistance_bufferGrid = viewDistance_voxelGrid / NVoxelBuffer.SIZE.x; + double viewDistance_bufferGrid = viewDistance_voxelGrid / VoxelBuffer.SIZE.x; double entireArea = size.x + viewDistance_bufferGrid * 2.0; entireArea *= size.z + viewDistance_bufferGrid * 2.0; double holeArea; @@ -796,16 +796,16 @@ public class NStagedChunkGenerator implements ChunkGenerator { private void createTotalOutputBoundsForStage( int stageIndex, @Nonnull Map> stageDependencyMap, @Nonnull Bounds3i[] totalOutputBoundsPerStage_bufferGrid ) { - Bounds3i initialOutputBounds_bufferGrid = new Bounds3i(Vector3i.ZERO, Vector3i.ALL_ONES); - NStage stage = this.stages.get(stageIndex); + Bounds3i initialOutputBounds_bufferGrid = new Bounds3i(Vector3iUtil.ZERO, Vector3iUtil.ALL_ONES); + Stage stage = this.stages.get(stageIndex); List allOutputBounds = new ArrayList<>(); for (int dependentStageIndex = this.stages.size() - 1; dependentStageIndex >= stageIndex + 1; dependentStageIndex--) { if (stageDependencyMap.get(dependentStageIndex).contains(stageIndex)) { - NStage dependentStage = this.stages.get(dependentStageIndex); - Map dependentInputTypesAndBounds_bufferGrid = dependentStage.getInputTypesAndBounds_bufferGrid(); + Stage dependentStage = this.stages.get(dependentStageIndex); + Map dependentInputTypesAndBounds_bufferGrid = dependentStage.getInputTypesAndBounds_bufferGrid(); - for (NBufferType thisStageOutputTypes : stage.getOutputTypes()) { + for (BufferType thisStageOutputTypes : stage.getOutputTypes()) { Bounds3i dependentStageInputBounds_bufferGrid = dependentInputTypesAndBounds_bufferGrid.get(thisStageOutputTypes); if (dependentStageInputBounds_bufferGrid != null) { Bounds3i totalDependentStageOutputBounds_bufferGrid = totalOutputBoundsPerStage_bufferGrid[dependentStageIndex]; @@ -818,7 +818,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { } if (allOutputBounds.isEmpty()) { - NStagedChunkGenerator.setBoundsToWorldHeight_bufferGrid(initialOutputBounds_bufferGrid); + StagedChunkGenerator.setBoundsToWorldHeight_bufferGrid(initialOutputBounds_bufferGrid); totalOutputBoundsPerStage_bufferGrid[stageIndex] = initialOutputBounds_bufferGrid; } else { Bounds3i totalOutputBounds_bufferGrid = allOutputBounds.getFirst().clone(); @@ -827,7 +827,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { totalOutputBounds_bufferGrid.encompass(allOutputBounds.get(i)); } - NStagedChunkGenerator.setBoundsToWorldHeight_bufferGrid(totalOutputBounds_bufferGrid); + StagedChunkGenerator.setBoundsToWorldHeight_bufferGrid(totalOutputBounds_bufferGrid); totalOutputBoundsPerStage_bufferGrid[stageIndex] = totalOutputBounds_bufferGrid; } } @@ -844,11 +844,11 @@ public class NStagedChunkGenerator implements ChunkGenerator { } @Nonnull - private Set createListOfAllBufferTypes() { - Set allBufferTypes = new HashSet<>(); + private Set createListOfAllBufferTypes() { + Set allBufferTypes = new HashSet<>(); for (int stageIndex = 0; stageIndex < this.stages.size(); stageIndex++) { - NStage stage = this.stages.get(stageIndex); + Stage stage = this.stages.get(stageIndex); allBufferTypes.addAll(stage.getInputTypesAndBounds_bufferGrid().keySet()); allBufferTypes.addAll(stage.getOutputTypes()); } @@ -867,7 +867,7 @@ public class NStagedChunkGenerator implements ChunkGenerator { return out; } - private boolean isGeneratorOutputBufferType(@Nonnull NBufferType bufferType) { + private boolean isGeneratorOutputBufferType(@Nonnull BufferType bufferType) { return bufferType.equals(this.MATERIAL_OUTPUT_BUFFER_TYPE) || bufferType.equals(this.TINT_OUTPUT_BUFFER_TYPE) || bufferType.equals(this.ENVIRONMENT_OUTPUT_BUFFER_TYPE) diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/containers/FloatContainer3d.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/containers/FloatContainer3d.java similarity index 85% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/containers/FloatContainer3d.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/containers/FloatContainer3d.java index 2a616e06..1a2d46ab 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/containers/FloatContainer3d.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/containers/FloatContainer3d.java @@ -1,9 +1,9 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.containers; +package com.hypixel.hytale.builtin.hytalegenerator.engine.containers; +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class FloatContainer3d { @Nonnull @@ -43,7 +43,7 @@ public class FloatContainer3d { } public void moveMinTo(@Nonnull Vector3i min_voxelGrid) { - Vector3i oldMin_voxelGrid = this.bounds_voxelGrid.min.clone().scale(-1); + Vector3i oldMin_voxelGrid = new Vector3i(this.bounds_voxelGrid.min).negate(); this.bounds_voxelGrid.offset(oldMin_voxelGrid); this.bounds_voxelGrid.offset(min_voxelGrid); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/entityfunnel/EntityFunnel.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/entityfunnel/EntityFunnel.java new file mode 100644 index 00000000..cedfed6c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/entityfunnel/EntityFunnel.java @@ -0,0 +1,25 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.entityfunnel; + +import com.hypixel.hytale.builtin.hytalegenerator.EntityPlacementData; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public interface EntityFunnel { + EntityFunnel NULL = new EntityFunnel() { + @Override + public void addEntity(@NonNullDecl EntityPlacementData entityPlacementData) { + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return Bounds3i.ZERO; + } + }; + + void addEntity(@Nonnull EntityPlacementData var1); + + @Nonnull + Bounds3i getBounds(); +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/entityfunnel/RotationEntityFunnel.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/entityfunnel/RotationEntityFunnel.java new file mode 100644 index 00000000..84eebbc6 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/entityfunnel/RotationEntityFunnel.java @@ -0,0 +1,60 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.entityfunnel; + +import com.hypixel.hytale.builtin.hytalegenerator.EntityPlacementData; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.joml.Vector3ic; + +public class RotationEntityFunnel implements EntityFunnel { + @Nonnull + private final RotationTuple rotation_fromViewToSource; + @Nonnull + private Bounds3i viewBounds; + @Nonnull + private EntityFunnel source; + @Nonnull + private final Vector3i anchor; + + public RotationEntityFunnel(@Nonnull RotationTuple rotation) { + this.rotation_fromViewToSource = rotation; + this.anchor = new Vector3i(); + this.setSource(EntityFunnel.NULL, Vector3iUtil.ZERO); + } + + public void setSource(@Nonnull EntityFunnel source, @Nonnull Vector3ic anchor) { + this.source = source; + this.anchor.set(anchor); + this.viewBounds = source.getBounds().clone(); + this.viewBounds.undoRotationAroundVoxel(this.rotation_fromViewToSource, anchor); + } + + @Override + public void addEntity(@NonNullDecl EntityPlacementData entityPlacementData) { + Vector3i offset = entityPlacementData.getOffset(); + offset.sub(this.anchor); + this.rotation_fromViewToSource.applyRotationTo(offset); + offset.add(this.anchor); + TransformComponent entityTransform = entityPlacementData.getEntityHolder().getComponent(TransformComponent.getComponentType()); + if (entityTransform != null) { + Vector3d entityPosition = entityTransform.getPosition(); + Rotation3f entityRotation = entityTransform.getRotation(); + this.rotation_fromViewToSource.applyRotationTo(entityPosition); + this.rotation_fromViewToSource.applyRotationTo(entityRotation); + } + + this.source.addEntity(entityPlacementData); + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return this.viewBounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/performanceinstruments/MemInstrument.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/performanceinstruments/MemInstrument.java similarity index 88% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/performanceinstruments/MemInstrument.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/performanceinstruments/MemInstrument.java index 07cde36e..028bc1f0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/performanceinstruments/MemInstrument.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/performanceinstruments/MemInstrument.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments; +package com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/performanceinstruments/TimeInstrument.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/performanceinstruments/TimeInstrument.java similarity index 91% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/performanceinstruments/TimeInstrument.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/performanceinstruments/TimeInstrument.java index 81033daf..4e42f921 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/performanceinstruments/TimeInstrument.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/performanceinstruments/TimeInstrument.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments; +package com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; import java.util.ArrayList; @@ -44,7 +44,7 @@ public class TimeInstrument { @Nonnull private String toString(int indentation, @Nonnull TimeInstrument.Probe probe) { - long ns = probe.getTotalTime() / this.sampleCount; + long ns = probe.getTotalTime_ns() / this.sampleCount; StringBuilder s = new StringBuilder(); s.append("\t".repeat(indentation)); s.append(probe.getName()).append(": "); @@ -63,7 +63,7 @@ public class TimeInstrument { @Nonnull private final String name; private long startTime; - private long totalTime; + private long totalTime_ns; private TimeInstrument.Probe.State state; private List probes; @@ -85,14 +85,14 @@ public class TimeInstrument { assert this.state == TimeInstrument.Probe.State.STARTED; this.state = TimeInstrument.Probe.State.COMPLETED; - this.totalTime = System.nanoTime() - this.startTime; + this.totalTime_ns = System.nanoTime() - this.startTime; return this; } - public long getTotalTime() { + public long getTotalTime_ns() { assert this.state == TimeInstrument.Probe.State.COMPLETED; - return this.totalTime; + return this.totalTime_ns; } @Nonnull @@ -131,7 +131,7 @@ public class TimeInstrument { assert this.isCompatibleForAddition(probe); - this.totalTime = this.totalTime + probe.getTotalTime(); + this.totalTime_ns = this.totalTime_ns + probe.getTotalTime_ns(); for (int i = 0; i < this.probes.size(); i++) { this.probes.get(i).add(probe.probes.get(i)); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NBiomeDistanceStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/BiomeDistanceStage.java similarity index 65% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NBiomeDistanceStage.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/BiomeDistanceStage.java index 149dfe0b..7eb4c894 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NBiomeDistanceStage.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/BiomeDistanceStage.java @@ -1,38 +1,38 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; +package com.hypixel.hytale.builtin.hytalegenerator.engine.stages; +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NCountedPixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NPixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NSimplePixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NPixelBufferView; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.CountedPixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.PixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.SimplePixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.ParametrizedBufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.PixelBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; -public class NBiomeDistanceStage implements NStage { +public class BiomeDistanceStage implements Stage { private static final double ORIGIN_REACH = 1.0; - private static final double BUFFER_DIAGONAL_VOXEL_GRID = Math.sqrt(NPixelBuffer.SIZE.x * NPixelBuffer.SIZE.x + NPixelBuffer.SIZE.z * NPixelBuffer.SIZE.z); + private static final double BUFFER_DIAGONAL_VOXEL_GRID = Math.sqrt(PixelBuffer.SIZE.x * PixelBuffer.SIZE.x + PixelBuffer.SIZE.z * PixelBuffer.SIZE.z); public static final double DEFAULT_DISTANCE_TO_BIOME_EDGE = Double.MAX_VALUE; @Nonnull - public static final Class biomeBufferClass = NCountedPixelBuffer.class; + public static final Class biomeBufferClass = CountedPixelBuffer.class; @Nonnull public static final Class biomeClass = Integer.class; @Nonnull - public static final Class biomeDistanceBufferClass = NSimplePixelBuffer.class; + public static final Class biomeDistanceBufferClass = SimplePixelBuffer.class; @Nonnull - public static final Class biomeDistanceClass = NBiomeDistanceStage.BiomeDistanceEntries.class; + public static final Class biomeDistanceClass = BiomeDistanceStage.BiomeDistanceEntries.class; @Nonnull - private final NParametrizedBufferType biomeInputBufferType; + private final ParametrizedBufferType biomeInputBufferType; @Nonnull - private final NParametrizedBufferType biomeDistanceOutputBufferType; + private final ParametrizedBufferType biomeDistanceOutputBufferType; @Nonnull private final String stageName; private final double maxDistance_voxelGrid; @@ -40,10 +40,10 @@ public class NBiomeDistanceStage implements NStage { @Nonnull private final Bounds3i inputBounds_bufferGrid; - public NBiomeDistanceStage( + public BiomeDistanceStage( @Nonnull String stageName, - @Nonnull NParametrizedBufferType biomeInputBufferType, - @Nonnull NParametrizedBufferType biomeDistanceOutputBufferType, + @Nonnull ParametrizedBufferType biomeInputBufferType, + @Nonnull ParametrizedBufferType biomeDistanceOutputBufferType, double maxDistance_voxelGrid ) { assert maxDistance_voxelGrid >= 0.0; @@ -61,26 +61,27 @@ public class NBiomeDistanceStage implements NStage { } @Override - public void run(@Nonnull NStage.Context context) { - NBufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); - NPixelBufferView biomeSpace = new NPixelBufferView<>(biomeAccess, biomeClass); - NBufferBundle.Access.View biomeDistanceAccess = context.bufferAccess.get(this.biomeDistanceOutputBufferType); - NPixelBufferView biomeDistanceSpace = new NPixelBufferView<>(biomeDistanceAccess, biomeDistanceClass); + public void run(@Nonnull Stage.Context context) { + BufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); + PixelBufferView biomeSpace = new PixelBufferView<>(biomeAccess, biomeClass); + BufferBundle.Access.View biomeDistanceAccess = context.bufferAccess.get(this.biomeDistanceOutputBufferType); + PixelBufferView biomeDistanceSpace = new PixelBufferView<>(biomeDistanceAccess, biomeDistanceClass); + Bounds3i bounds_voxelGrid = biomeDistanceSpace.getBounds(); Vector3i position_voxelGrid = new Vector3i(); - for (position_voxelGrid.x = biomeDistanceSpace.minX(); position_voxelGrid.x < biomeDistanceSpace.maxX(); position_voxelGrid.x++) { - for (position_voxelGrid.z = biomeDistanceSpace.minZ(); position_voxelGrid.z < biomeDistanceSpace.maxZ(); position_voxelGrid.z++) { - NBiomeDistanceStage.BiomeDistanceEntries distanceEntries = this.createDistanceTracker(biomeAccess, biomeSpace, position_voxelGrid); + for (position_voxelGrid.x = bounds_voxelGrid.min.x; position_voxelGrid.x < bounds_voxelGrid.max.x; position_voxelGrid.x++) { + for (position_voxelGrid.z = bounds_voxelGrid.min.z; position_voxelGrid.z < bounds_voxelGrid.max.z; position_voxelGrid.z++) { + BiomeDistanceStage.BiomeDistanceEntries distanceEntries = this.createDistanceTracker(biomeAccess, biomeSpace, position_voxelGrid); biomeDistanceSpace.set(distanceEntries, position_voxelGrid); } } } @Nonnull - private NBiomeDistanceStage.BiomeDistanceEntries createDistanceTracker( - @Nonnull NBufferBundle.Access.View biomeAccess, @Nonnull NPixelBufferView biomeSpace, @Nonnull Vector3i targetPosition_voxelGrid + private BiomeDistanceStage.BiomeDistanceEntries createDistanceTracker( + @Nonnull BufferBundle.Access.View biomeAccess, @Nonnull PixelBufferView biomeSpace, @Nonnull Vector3i targetPosition_voxelGrid ) { - NBiomeDistanceStage.BiomeDistanceCounter counter = new NBiomeDistanceStage.BiomeDistanceCounter(); + BiomeDistanceStage.BiomeDistanceCounter counter = new BiomeDistanceStage.BiomeDistanceCounter(); Vector3i position_bufferGrid = new Vector3i(); Bounds3i scanBounds_voxelGrid = GridUtils.createBounds_fromRadius_originVoxelInclusive((int)Math.ceil(this.maxDistance_voxelGrid)); scanBounds_voxelGrid.offset(targetPosition_voxelGrid); @@ -91,7 +92,7 @@ public class NBiomeDistanceStage implements NStage { double distanceToBuffer_voxelGrid = distanceToBuffer_voxelGrid(targetPosition_voxelGrid, position_bufferGrid); distanceToBuffer_voxelGrid = Math.max(distanceToBuffer_voxelGrid - 1.0, 0.0); if (!(distanceToBuffer_voxelGrid > this.maxDistance_voxelGrid)) { - NCountedPixelBuffer biomeBuffer = (NCountedPixelBuffer)biomeAccess.getBuffer(position_bufferGrid).buffer(); + CountedPixelBuffer biomeBuffer = (CountedPixelBuffer)biomeAccess.getBuffer(position_bufferGrid).buffer(); List uniqueBiomeIds = biomeBuffer.getUniqueEntries(); assert !uniqueBiomeIds.isEmpty(); @@ -118,7 +119,7 @@ public class NBiomeDistanceStage implements NStage { ); distanceToColumn_voxelGrid = Math.max(distanceToColumn_voxelGrid - 1.0, 0.0); if (!(distanceToColumn_voxelGrid > this.maxDistance_voxelGrid)) { - Integer biomeId = biomeSpace.getContent(columnPosition_voxelGrid); + Integer biomeId = biomeSpace.get(columnPosition_voxelGrid); assert biomeId != null; @@ -132,18 +133,18 @@ public class NBiomeDistanceStage implements NStage { } } - return new NBiomeDistanceStage.BiomeDistanceEntries(counter.entries); + return new BiomeDistanceStage.BiomeDistanceEntries(counter.entries); } @Nonnull @Override - public Map getInputTypesAndBounds_bufferGrid() { + public Map getInputTypesAndBounds_bufferGrid() { return Map.of(this.biomeInputBufferType, this.inputBounds_bufferGrid); } @Nonnull @Override - public List getOutputTypes() { + public List getOutputTypes() { return List.of(this.biomeDistanceOutputBufferType); } @@ -158,13 +159,13 @@ public class NBiomeDistanceStage implements NStage { assert (double)position_bufferGrid.y == 0.0; - Vector3i bufferAtPosition_bufferGrid = position_voxelGrid.clone(); + Vector3i bufferAtPosition_bufferGrid = new Vector3i(position_voxelGrid); GridUtils.toBufferGrid_fromVoxelGrid(bufferAtPosition_bufferGrid); if (bufferAtPosition_bufferGrid.x == position_bufferGrid.x && bufferAtPosition_bufferGrid.z == position_bufferGrid.z) { return 0.0; } else { - int cornerShift = NCountedPixelBuffer.SIZE_VOXEL_GRID.x - 1; - Vector3i corner00 = position_bufferGrid.clone(); + int cornerShift = CountedPixelBuffer.SIZE_VOXEL_GRID.x - 1; + Vector3i corner00 = new Vector3i(position_bufferGrid); GridUtils.toVoxelGrid_fromBufferGrid(corner00); Vector3i corner01 = new Vector3i(corner00); corner01.z += cornerShift; @@ -176,21 +177,21 @@ public class NBiomeDistanceStage implements NStage { if (position_voxelGrid.z >= corner00.z && position_voxelGrid.z <= corner00.z + cornerShift) { return Math.min(Math.abs(position_voxelGrid.x - corner00.x), Math.abs(position_voxelGrid.x - corner10.x)); } else if (position_voxelGrid.x < corner00.x && position_voxelGrid.z < corner00.z) { - return position_voxelGrid.distanceTo(corner00); + return position_voxelGrid.distance(corner00); } else if (position_voxelGrid.x < corner01.x && position_voxelGrid.z > corner01.z) { - return position_voxelGrid.distanceTo(corner01); + return position_voxelGrid.distance(corner01); } else if (position_voxelGrid.x > corner10.x && position_voxelGrid.z < corner10.z) { - return position_voxelGrid.distanceTo(corner10); + return position_voxelGrid.distance(corner10); } else { Vector3i corner11 = new Vector3i(corner10.x, 0, corner01.z); - return position_voxelGrid.distanceTo(corner11); + return position_voxelGrid.distance(corner11); } } } } private static boolean allBiomesAreCountedAndFarther( - @Nonnull NBiomeDistanceStage.BiomeDistanceCounter counter, @Nonnull List uniqueBiomes, double distanceToBuffer_voxelGrid + @Nonnull BiomeDistanceStage.BiomeDistanceCounter counter, @Nonnull List uniqueBiomes, double distanceToBuffer_voxelGrid ) { for (Integer biomeId : uniqueBiomes) { if (counter.isCloserThanCounted(biomeId, distanceToBuffer_voxelGrid)) { @@ -203,15 +204,15 @@ public class NBiomeDistanceStage implements NStage { private static class BiomeDistanceCounter { @Nonnull - final List entries = new ArrayList<>(3); + final List entries = new ArrayList<>(3); @Nullable - NBiomeDistanceStage.BiomeDistanceEntry cachedEntry = null; + BiomeDistanceStage.BiomeDistanceEntry cachedEntry = null; BiomeDistanceCounter() { } boolean isCloserThanCounted(int biomeId, double distance_voxelGrid) { - for (NBiomeDistanceStage.BiomeDistanceEntry entry : this.entries) { + for (BiomeDistanceStage.BiomeDistanceEntry entry : this.entries) { if (entry.biomeId == biomeId) { return distance_voxelGrid < entry.distance_voxelGrid; } @@ -226,7 +227,7 @@ public class NBiomeDistanceStage implements NStage { this.cachedEntry.distance_voxelGrid = distance_voxelGrid; } } else { - for (NBiomeDistanceStage.BiomeDistanceEntry entry : this.entries) { + for (BiomeDistanceStage.BiomeDistanceEntry entry : this.entries) { if (entry.biomeId == biomeId) { this.cachedEntry = entry; if (entry.distance_voxelGrid <= distance_voxelGrid) { @@ -238,7 +239,7 @@ public class NBiomeDistanceStage implements NStage { } } - NBiomeDistanceStage.BiomeDistanceEntry entryx = new NBiomeDistanceStage.BiomeDistanceEntry(); + BiomeDistanceStage.BiomeDistanceEntry entryx = new BiomeDistanceStage.BiomeDistanceEntry(); entryx.biomeId = biomeId; entryx.distance_voxelGrid = distance_voxelGrid; this.entries.add(entryx); @@ -249,16 +250,16 @@ public class NBiomeDistanceStage implements NStage { public static class BiomeDistanceEntries { @Nonnull - public final List entries; + public final List entries; - public BiomeDistanceEntries(@Nonnull List entries) { + public BiomeDistanceEntries(@Nonnull List entries) { this.entries = entries; } public double distanceToClosestOtherBiome(int thisBiomeId) { double smallestDistance = Double.MAX_VALUE; - for (NBiomeDistanceStage.BiomeDistanceEntry entry : this.entries) { + for (BiomeDistanceStage.BiomeDistanceEntry entry : this.entries) { if (entry.biomeId != thisBiomeId) { smallestDistance = Math.min(smallestDistance, entry.distance_voxelGrid); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/BiomeStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/BiomeStage.java new file mode 100644 index 00000000..e5c75d4f --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/BiomeStage.java @@ -0,0 +1,68 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.stages; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.CountedPixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.ParametrizedBufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.PixelBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.BiCarta; +import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; + +public class BiomeStage implements Stage { + @Nonnull + public static final Class bufferClass = CountedPixelBuffer.class; + @Nonnull + public static final Class biomeClass = Integer.class; + @Nonnull + private final ParametrizedBufferType biomeOutputBufferType; + @Nonnull + private final String stageName; + @Nonnull + private final WorkerIndexer.Data worldStructure_workerData; + + public BiomeStage( + @Nonnull String stageName, @Nonnull ParametrizedBufferType biomeOutputBufferType, @Nonnull WorkerIndexer.Data worldStructure_workerData + ) { + this.stageName = stageName; + this.biomeOutputBufferType = biomeOutputBufferType; + this.worldStructure_workerData = worldStructure_workerData; + } + + @Override + public void run(@Nonnull Stage.Context context) { + BufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeOutputBufferType); + PixelBufferView biomeSpace = new PixelBufferView<>(biomeAccess, biomeClass); + BiCarta biomeMap = this.worldStructure_workerData.get(context.workerId).getBiomeMap(); + Bounds3i bounds_voxelSpace = biomeSpace.getBounds(); + + for (int x = bounds_voxelSpace.min.x; x < bounds_voxelSpace.max.x; x++) { + for (int z = bounds_voxelSpace.min.z; z < bounds_voxelSpace.max.z; z++) { + Integer biomeId = biomeMap.apply(x, z, context.workerId); + biomeSpace.set(biomeId, x, 0, z); + } + } + } + + @Nonnull + @Override + public Map getInputTypesAndBounds_bufferGrid() { + return Map.of(); + } + + @Nonnull + @Override + public List getOutputTypes() { + return List.of(this.biomeOutputBufferType); + } + + @Nonnull + @Override + public String getName() { + return this.stageName; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NEnvironmentStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/EnvironmentStage.java similarity index 57% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NEnvironmentStage.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/EnvironmentStage.java index 281abb1d..516c3ff4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NEnvironmentStage.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/EnvironmentStage.java @@ -1,37 +1,38 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; +package com.hypixel.hytale.builtin.hytalegenerator.engine.stages; +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; import com.hypixel.hytale.builtin.hytalegenerator.Registry; import com.hypixel.hytale.builtin.hytalegenerator.biome.Biome; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.CountedPixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.VoxelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.ParametrizedBufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.PixelBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.VoxelBufferView; import com.hypixel.hytale.builtin.hytalegenerator.environmentproviders.EnvironmentProvider; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NCountedPixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NPixelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NVoxelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; +import org.joml.Vector3i; -public class NEnvironmentStage implements NStage { +public class EnvironmentStage implements Stage { @Nonnull - public static final Class biomeBufferClass = NCountedPixelBuffer.class; + public static final Class biomeBufferClass = CountedPixelBuffer.class; @Nonnull public static final Class biomeTypeClass = Integer.class; @Nonnull - public static final Class environmentBufferClass = NVoxelBuffer.class; + public static final Class environmentBufferClass = VoxelBuffer.class; @Nonnull public static final Class environmentClass = Integer.class; @Nonnull - private final NParametrizedBufferType biomeInputBufferType; + private final ParametrizedBufferType biomeInputBufferType; @Nonnull - private final NParametrizedBufferType environmentOutputBufferType; + private final ParametrizedBufferType environmentOutputBufferType; @Nonnull private final Bounds3i inputBounds_bufferGrid; @Nonnull @@ -39,10 +40,10 @@ public class NEnvironmentStage implements NStage { @Nonnull private final WorkerIndexer.Data worldStructure_workerData; - public NEnvironmentStage( + public EnvironmentStage( @Nonnull String stageName, - @Nonnull NParametrizedBufferType biomeInputBufferType, - @Nonnull NParametrizedBufferType environmentOutputBufferType, + @Nonnull ParametrizedBufferType biomeInputBufferType, + @Nonnull ParametrizedBufferType environmentOutputBufferType, @Nonnull WorkerIndexer.Data worldStructure_workerData ) { assert biomeInputBufferType.isValidType(biomeBufferClass, biomeTypeClass); @@ -53,15 +54,15 @@ public class NEnvironmentStage implements NStage { this.environmentOutputBufferType = environmentOutputBufferType; this.stageName = stageName; this.worldStructure_workerData = worldStructure_workerData; - this.inputBounds_bufferGrid = GridUtils.createUnitBounds3i(Vector3i.ZERO); + this.inputBounds_bufferGrid = GridUtils.createUnitBounds3i(Vector3iUtil.ZERO); } @Override - public void run(@Nonnull NStage.Context context) { - NBufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); - NPixelBufferView biomeSpace = new NPixelBufferView<>(biomeAccess, biomeTypeClass); - NBufferBundle.Access.View environmentAccess = context.bufferAccess.get(this.environmentOutputBufferType); - NVoxelBufferView environmentSpace = new NVoxelBufferView<>(environmentAccess, environmentClass); + public void run(@Nonnull Stage.Context context) { + BufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); + PixelBufferView biomeSpace = new PixelBufferView<>(biomeAccess, biomeTypeClass); + BufferBundle.Access.View environmentAccess = context.bufferAccess.get(this.environmentOutputBufferType); + VoxelBufferView environmentSpace = new VoxelBufferView<>(environmentAccess, environmentClass); Bounds3i outputBounds_voxelGrid = environmentSpace.getBounds(); Vector3i position_voxelGrid = new Vector3i(outputBounds_voxelGrid.min); EnvironmentProvider.Context environmentContext = new EnvironmentProvider.Context(position_voxelGrid); @@ -69,7 +70,7 @@ public class NEnvironmentStage implements NStage { for (position_voxelGrid.x = outputBounds_voxelGrid.min.x; position_voxelGrid.x < outputBounds_voxelGrid.max.x; position_voxelGrid.x++) { for (position_voxelGrid.z = outputBounds_voxelGrid.min.z; position_voxelGrid.z < outputBounds_voxelGrid.max.z; position_voxelGrid.z++) { - Integer biomeId = biomeSpace.getContent(position_voxelGrid.x, 0, position_voxelGrid.z); + Integer biomeId = biomeSpace.get(position_voxelGrid.x, 0, position_voxelGrid.z); assert biomeId != null; @@ -80,7 +81,6 @@ public class NEnvironmentStage implements NStage { EnvironmentProvider environmentProvider = biome.getEnvironmentProvider(); for (position_voxelGrid.y = outputBounds_voxelGrid.min.y; position_voxelGrid.y < outputBounds_voxelGrid.max.y; position_voxelGrid.y++) { - position_voxelGrid.dropHash(); int environment = environmentProvider.getValue(environmentContext); environmentSpace.set(environment, position_voxelGrid); } @@ -90,13 +90,13 @@ public class NEnvironmentStage implements NStage { @Nonnull @Override - public Map getInputTypesAndBounds_bufferGrid() { + public Map getInputTypesAndBounds_bufferGrid() { return Map.of(this.biomeInputBufferType, this.inputBounds_bufferGrid); } @Nonnull @Override - public List getOutputTypes() { + public List getOutputTypes() { return List.of(this.environmentOutputBufferType); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/PropStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/PropStage.java new file mode 100644 index 00000000..1fad6e78 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/PropStage.java @@ -0,0 +1,277 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.stages; + +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; +import com.hypixel.hytale.builtin.hytalegenerator.PropRuntime; +import com.hypixel.hytale.builtin.hytalegenerator.Registry; +import com.hypixel.hytale.builtin.hytalegenerator.biome.Biome; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.CountedPixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.EntityBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.SimplePixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.VoxelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.ParametrizedBufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.EntityBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.PixelBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.VoxelBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import it.unimi.dsi.fastutil.Pair; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; + +public class PropStage implements Stage { + public static final double DEFAULT_BACKGROUND_DENSITY = 0.0; + @Nonnull + public static final Class biomeBufferClass = CountedPixelBuffer.class; + @Nonnull + public static final Class biomeClass = Integer.class; + @Nonnull + public static final Class biomeDistanceBufferClass = SimplePixelBuffer.class; + @Nonnull + public static final Class biomeDistanceClass = BiomeDistanceStage.BiomeDistanceEntries.class; + @Nonnull + public static final Class materialBufferClass = VoxelBuffer.class; + @Nonnull + public static final Class materialClass = Material.class; + @Nonnull + public static final Class entityBufferClass = EntityBuffer.class; + @Nonnull + private final ParametrizedBufferType biomeInputBufferType; + @Nonnull + private final ParametrizedBufferType biomeDistanceInputBufferType; + @Nonnull + private final ParametrizedBufferType materialInputBufferType; + @Nullable + private final BufferType entityInputBufferType; + @Nonnull + private final ParametrizedBufferType materialOutputBufferType; + @Nonnull + private final BufferType entityOutputBufferType; + @Nonnull + private final Bounds3i materialInputBounds_bufferGrid; + @Nonnull + private final Bounds3i materialInputBounds_voxelGrid; + @Nonnull + private final Bounds3i biomeInputBounds_bufferGrid; + @Nonnull + private final Bounds3i positionsBounds_voxelGrid; + @Nonnull + private final Bounds3i positionsBounds_bufferGrid; + @Nonnull + private final String stageName; + @Nonnull + private final MaterialCache materialCache; + @Nonnull + private final WorkerIndexer.Data worldStructure_workerData; + private final int runtimeIndex; + + public PropStage( + @Nonnull String stageName, + @Nonnull ParametrizedBufferType biomeInputBufferType, + @Nonnull ParametrizedBufferType biomeDistanceInputBufferType, + @Nonnull ParametrizedBufferType materialInputBufferType, + @Nullable BufferType entityInputBufferType, + @Nonnull ParametrizedBufferType materialOutputBufferType, + @Nonnull BufferType entityOutputBufferType, + @Nonnull MaterialCache materialCache, + @Nonnull WorkerIndexer.Data worldStructure_workerData, + int runtimeIndex + ) { + assert biomeInputBufferType.isValidType(biomeBufferClass, biomeClass); + + assert biomeDistanceInputBufferType.isValidType(biomeDistanceBufferClass, biomeDistanceClass); + + assert materialInputBufferType.isValidType(materialBufferClass, materialClass); + + assert entityInputBufferType == null || entityInputBufferType.isValidType(entityBufferClass); + + assert materialOutputBufferType.isValidType(materialBufferClass, materialClass); + + assert entityOutputBufferType.isValidType(entityBufferClass); + + this.biomeInputBufferType = biomeInputBufferType; + this.biomeDistanceInputBufferType = biomeDistanceInputBufferType; + this.materialInputBufferType = materialInputBufferType; + this.entityInputBufferType = entityInputBufferType; + this.materialOutputBufferType = materialOutputBufferType; + this.entityOutputBufferType = entityOutputBufferType; + this.worldStructure_workerData = worldStructure_workerData; + this.stageName = stageName; + this.materialCache = materialCache; + this.runtimeIndex = runtimeIndex; + List allBiomes = new ArrayList<>(); + this.worldStructure_workerData.get(WorkerIndexer.Id.MAIN).getBiomeRegistry().forEach((biomeId, biomex) -> allBiomes.add(biomex)); + this.materialInputBounds_voxelGrid = new Bounds3i(); + this.positionsBounds_voxelGrid = new Bounds3i(); + + for (Biome biome : allBiomes) { + for (PropRuntime propRuntime : biome.getPropRuntimes()) { + if (propRuntime.getRuntimeIndex() == this.runtimeIndex) { + propRuntime.getPropDistribution().forEachPossibleProp(prop -> { + Bounds3i readBounds_voxelGrid = prop.getReadBounds_voxelGrid(); + Bounds3i writeBounds_voxelGrid = prop.getWriteBounds_voxelGrid(); + Bounds3i propInputBounds_voxelGrid = toInputBounds_voxelGrid(readBounds_voxelGrid, writeBounds_voxelGrid); + this.materialInputBounds_voxelGrid.encompass(propInputBounds_voxelGrid); + Bounds3i positionsBounds_voxelGrid = toPositionsBounds_voxelGrid(writeBounds_voxelGrid); + this.positionsBounds_voxelGrid.encompass(positionsBounds_voxelGrid); + }); + } + } + } + + this.materialInputBounds_voxelGrid.min.y = 0; + this.materialInputBounds_voxelGrid.max.y = 320; + this.materialInputBounds_bufferGrid = GridUtils.createBufferBoundsInclusive_fromVoxelBounds(this.materialInputBounds_voxelGrid); + if (this.materialInputBounds_bufferGrid.isZeroVolume()) { + this.materialInputBounds_bufferGrid.encompass(Vector3iUtil.ZERO); + } + + GridUtils.setBoundsYToWorldHeight_bufferGrid(this.materialInputBounds_bufferGrid); + this.biomeInputBounds_bufferGrid = GridUtils.createBufferBoundsInclusive_fromVoxelBounds(this.positionsBounds_voxelGrid); + GridUtils.setBoundsYToWorldHeight_bufferGrid(this.biomeInputBounds_bufferGrid); + this.positionsBounds_bufferGrid = GridUtils.createBufferBoundsInclusive_fromVoxelBounds(this.positionsBounds_voxelGrid); + GridUtils.setBoundsYToWorldHeight_bufferGrid(this.positionsBounds_bufferGrid); + } + + @Nonnull + private static Bounds3i toInputBounds_voxelGrid(@Nonnull Bounds3i readBounds_voxelGrid, @Nonnull Bounds3i writeBounds_voxelGrid) { + if (readBounds_voxelGrid.isZeroVolume()) { + return new Bounds3i(); + } else { + Bounds3i out = writeBounds_voxelGrid.clone().flipOnOriginVoxel(); + out.stack(readBounds_voxelGrid); + return out; + } + } + + @Nonnull + private static Bounds3i toPositionsBounds_voxelGrid(@Nonnull Bounds3i writeBounds_voxelGrid) { + return writeBounds_voxelGrid.clone().flipOnOriginVoxel(); + } + + @Override + public void run(@Nonnull Stage.Context context) { + BufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); + PixelBufferView biomeInputSpace = new PixelBufferView<>(biomeAccess, biomeClass); + BufferBundle.Access.View biomeDistanceAccess = context.bufferAccess.get(this.biomeDistanceInputBufferType); + PixelBufferView biomeDistanceSpace = new PixelBufferView<>(biomeDistanceAccess, biomeDistanceClass); + BufferBundle.Access.View materialInputAccess = context.bufferAccess.get(this.materialInputBufferType); + VoxelBufferView materialInputSpace = new VoxelBufferView<>(materialInputAccess, materialClass); + BufferBundle.Access.View materialOutputAccess = context.bufferAccess.get(this.materialOutputBufferType); + VoxelBufferView materialOutputSpace = new VoxelBufferView<>(materialOutputAccess, materialClass); + BufferBundle.Access.View entityOutputAccess = context.bufferAccess.get(this.entityOutputBufferType); + EntityBufferView entityOutputSpace = new EntityBufferView(entityOutputAccess); + Bounds3i localOutputBounds_voxelGrid = materialOutputSpace.getBounds(); + Bounds3i localMaterialInputBounds_voxelGrid = materialInputSpace.getBounds(); + Bounds3i localPositionsBounds_voxelGrid = localOutputBounds_voxelGrid.clone(); + localPositionsBounds_voxelGrid.stack(this.positionsBounds_voxelGrid.clone()); + localPositionsBounds_voxelGrid.min.y = 0; + localPositionsBounds_voxelGrid.max.y = 320; + materialOutputSpace.copyFrom(materialInputSpace); + if (this.entityInputBufferType != null) { + BufferBundle.Access.View entityInputAccess = context.bufferAccess.get(this.entityInputBufferType); + EntityBufferView entityInputSpace = new EntityBufferView(entityInputAccess); + entityOutputSpace.copyFrom(entityInputSpace); + } + + Registry biomeRegistry = this.worldStructure_workerData.get(context.workerId).getBiomeRegistry(); + HashSet traversedBiomes = new HashSet<>(); + List biomesInBuffer = new ArrayList<>(); + + for (int x = localPositionsBounds_voxelGrid.min.x; x < localPositionsBounds_voxelGrid.max.x; x++) { + for (int z = localPositionsBounds_voxelGrid.min.z; z < localPositionsBounds_voxelGrid.max.z; z++) { + Integer biomeId = biomeInputSpace.get(x, 0, z); + if (!traversedBiomes.contains(biomeId)) { + traversedBiomes.add(biomeId); + Biome biome = biomeRegistry.getObject(biomeId); + biomesInBuffer.add(biome); + } + } + } + + List> propRuntimeBiomeMap = new ArrayList<>(10); + + for (Biome biome : biomesInBuffer) { + biome.getRuntimesWithIndex(this.runtimeIndex, propRuntimex -> propRuntimeBiomeMap.add(Pair.of(propRuntimex, biome))); + } + + Prop.Context propContext = new Prop.Context(new Vector3i(), materialInputSpace, materialOutputSpace, entityOutputSpace, Double.MAX_VALUE); + Bounds3i propReadBounds_voxelGrid = new Bounds3i(); + Bounds3i propWriteBounds_voxelGrid = new Bounds3i(); + Vector3i position2d_voxelGrid = new Vector3i(); + PropDistribution.Context distributionContext = new PropDistribution.Context(new Bounds3d(), Pipe.getEmptyTwo(), Double.MAX_VALUE); + + for (Pair entry : propRuntimeBiomeMap) { + PropRuntime propRuntime = entry.left(); + Biome biome = entry.right(); + PropDistribution propDistribution = propRuntime.getPropDistribution(); + Pipe.Two propDistributionPipe = (position_voxelGrid, prop, control) -> { + int positionX_voxelGrid = (int)Math.floor(position_voxelGrid.x); + int positionY_voxelGrid = (int)Math.floor(position_voxelGrid.y); + int positionZ_voxelGrid = (int)Math.floor(position_voxelGrid.z); + propWriteBounds_voxelGrid.assign(prop.getWriteBounds_voxelGrid()); + propWriteBounds_voxelGrid.offset(positionX_voxelGrid, positionY_voxelGrid, positionZ_voxelGrid); + if (propWriteBounds_voxelGrid.intersects(localOutputBounds_voxelGrid)) { + propReadBounds_voxelGrid.assign(prop.getReadBounds_voxelGrid()); + propReadBounds_voxelGrid.offset(positionX_voxelGrid, positionY_voxelGrid, positionZ_voxelGrid); + if (propReadBounds_voxelGrid.isZeroVolume() || localMaterialInputBounds_voxelGrid.intersects(propReadBounds_voxelGrid)) { + Integer biomeIdAtPosition = biomeInputSpace.get(positionX_voxelGrid, 0, positionZ_voxelGrid); + Biome biomeAtPosition = biomeRegistry.getObject(biomeIdAtPosition); + if (biomeAtPosition == biome) { + position2d_voxelGrid.set(positionX_voxelGrid, 0, positionZ_voxelGrid); + double distanceToBiomeEdge = biomeDistanceSpace.get(position2d_voxelGrid).distanceToClosestOtherBiome(biomeIdAtPosition); + propContext.distanceToBiomeEdge = distanceToBiomeEdge; + propContext.position.set(positionX_voxelGrid, positionY_voxelGrid, positionZ_voxelGrid); + prop.generate(propContext); + } + } + } + }; + distributionContext.bounds.assign(localPositionsBounds_voxelGrid); + distributionContext.pipe = propDistributionPipe; + propDistribution.distribute(distributionContext); + } + } + + @Nonnull + @Override + public Map getInputTypesAndBounds_bufferGrid() { + Map map = new HashMap<>(); + map.put(this.biomeInputBufferType, this.biomeInputBounds_bufferGrid); + map.put(this.biomeDistanceInputBufferType, this.positionsBounds_bufferGrid); + map.put(this.materialInputBufferType, this.materialInputBounds_bufferGrid); + if (this.entityInputBufferType != null) { + map.put(this.entityInputBufferType, this.materialInputBounds_bufferGrid); + } + + return map; + } + + @Nonnull + @Override + public List getOutputTypes() { + return List.of(this.materialOutputBufferType, this.entityOutputBufferType); + } + + @Nonnull + @Override + public String getName() { + return this.stageName; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/Stage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/Stage.java new file mode 100644 index 00000000..e681dc6b --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/Stage.java @@ -0,0 +1,34 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.stages; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; + +public interface Stage { + void run(@Nonnull Stage.Context var1); + + @Nonnull + Map getInputTypesAndBounds_bufferGrid(); + + @Nonnull + List getOutputTypes(); + + @Nonnull + String getName(); + + public static final class Context { + @Nonnull + public Map bufferAccess; + @Nonnull + public WorkerIndexer.Id workerId; + + public Context(@Nonnull Map bufferAccess, @Nonnull WorkerIndexer.Id workerId) { + this.bufferAccess = bufferAccess; + this.workerId = workerId; + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTerrainStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/TerrainStage.java similarity index 71% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTerrainStage.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/TerrainStage.java index bb1921ed..50c9a4da 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTerrainStage.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/TerrainStage.java @@ -1,55 +1,57 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; +package com.hypixel.hytale.builtin.hytalegenerator.engine.stages; +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; import com.hypixel.hytale.builtin.hytalegenerator.Registry; import com.hypixel.hytale.builtin.hytalegenerator.biome.Biome; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.CountedPixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.SimplePixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.VoxelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.ParametrizedBufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator.StagedChunkGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.engine.containers.FloatContainer3d; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.PixelBufferView; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.VoxelBufferView; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.NStagedChunkGenerator; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NCountedPixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NSimplePixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.containers.FloatContainer3d; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NPixelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NVoxelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import javax.annotation.Nonnull; +import org.joml.Vector3i; -public class NTerrainStage implements NStage { +public class TerrainStage implements Stage { public static final double DEFAULT_BACKGROUND_DENSITY = 0.0; public static final double ORIGIN_REACH = 1.0; public static final double ORIGIN_REACH_HALF = 0.5; public static final double QUARTER_PI = Math.PI / 4; @Nonnull - public static final Class biomeBufferClass = NCountedPixelBuffer.class; + public static final Class biomeBufferClass = CountedPixelBuffer.class; @Nonnull public static final Class biomeClass = Integer.class; @Nonnull - public static final Class biomeDistanceBufferClass = NSimplePixelBuffer.class; + public static final Class biomeDistanceBufferClass = SimplePixelBuffer.class; @Nonnull - public static final Class biomeDistanceClass = NBiomeDistanceStage.BiomeDistanceEntries.class; + public static final Class biomeDistanceClass = BiomeDistanceStage.BiomeDistanceEntries.class; @Nonnull - public static final Class materialBufferClass = NVoxelBuffer.class; + public static final Class materialBufferClass = VoxelBuffer.class; @Nonnull public static final Class materialClass = Material.class; @Nonnull - private final NParametrizedBufferType biomeInputBufferType; + private final ParametrizedBufferType biomeInputBufferType; @Nonnull - private final NParametrizedBufferType biomeDistanceInputBufferType; + private final ParametrizedBufferType biomeDistanceInputBufferType; @Nonnull - private final NParametrizedBufferType materialOutputBufferType; + private final ParametrizedBufferType materialOutputBufferType; @Nonnull private final Bounds3i inputBounds_bufferGrid; @Nonnull @@ -62,11 +64,11 @@ public class NTerrainStage implements NStage { @Nonnull private final WorkerIndexer.Data worldStructure_workerdata; - public NTerrainStage( + public TerrainStage( @Nonnull String stageName, - @Nonnull NParametrizedBufferType biomeInputBufferType, - @Nonnull NParametrizedBufferType biomeDistanceInputBufferType, - @Nonnull NParametrizedBufferType materialOutputBufferType, + @Nonnull ParametrizedBufferType biomeInputBufferType, + @Nonnull ParametrizedBufferType biomeDistanceInputBufferType, + @Nonnull ParametrizedBufferType materialOutputBufferType, int maxInterpolationRadius_voxelGrid, @Nonnull MaterialCache materialCache, @Nonnull WorkerIndexer workerIndexer, @@ -88,22 +90,22 @@ public class NTerrainStage implements NStage { this.maxInterpolationRadius_voxelGrid = maxInterpolationRadius_voxelGrid; this.materialCache = materialCache; this.densityContainers = new WorkerIndexer.Data<>( - workerIndexer.getWorkerCount(), () -> new FloatContainer3d(NStagedChunkGenerator.SINGLE_BUFFER_TILE_BOUNDS_BUFFER_GRID, 0.0F) + workerIndexer.getWorkerCount(), () -> new FloatContainer3d(StagedChunkGenerator.SINGLE_BUFFER_TILE_BOUNDS_BUFFER_GRID, 0.0F) ); this.inputBounds_bufferGrid = GridUtils.createColumnBounds_bufferGrid(new Vector3i(), 0, 40); - this.inputBounds_bufferGrid.min.subtract(Vector3i.ALL_ONES); - this.inputBounds_bufferGrid.max.add(Vector3i.ALL_ONES); + this.inputBounds_bufferGrid.min.sub(Vector3iUtil.ALL_ONES); + this.inputBounds_bufferGrid.max.add(Vector3iUtil.ALL_ONES); GridUtils.setBoundsYToWorldHeight_bufferGrid(this.inputBounds_bufferGrid); } @Override - public void run(@Nonnull NStage.Context context) { - NBufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); - NPixelBufferView biomeSpace = new NPixelBufferView<>(biomeAccess, biomeClass); - NBufferBundle.Access.View biomeDistanceAccess = context.bufferAccess.get(this.biomeDistanceInputBufferType); - NPixelBufferView biomeDistanceSpace = new NPixelBufferView<>(biomeDistanceAccess, biomeDistanceClass); - NBufferBundle.Access.View materialAccess = context.bufferAccess.get(this.materialOutputBufferType); - NVoxelBufferView materialSpace = new NVoxelBufferView<>(materialAccess, materialClass); + public void run(@Nonnull Stage.Context context) { + BufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); + PixelBufferView biomeSpace = new PixelBufferView<>(biomeAccess, biomeClass); + BufferBundle.Access.View biomeDistanceAccess = context.bufferAccess.get(this.biomeDistanceInputBufferType); + PixelBufferView biomeDistanceSpace = new PixelBufferView<>(biomeDistanceAccess, biomeDistanceClass); + BufferBundle.Access.View materialAccess = context.bufferAccess.get(this.materialOutputBufferType); + VoxelBufferView materialSpace = new VoxelBufferView<>(materialAccess, materialClass); Bounds3i outputBounds_voxelGrid = materialSpace.getBounds(); FloatContainer3d densityContainer = this.densityContainers.get(context.workerId); densityContainer.moveMinTo(outputBounds_voxelGrid.min); @@ -114,8 +116,8 @@ public class NTerrainStage implements NStage { @Nonnull @Override - public Map getInputTypesAndBounds_bufferGrid() { - Map map = new HashMap<>(); + public Map getInputTypesAndBounds_bufferGrid() { + Map map = new HashMap<>(); map.put(this.biomeInputBufferType, this.inputBounds_bufferGrid); map.put(this.biomeDistanceInputBufferType, this.inputBounds_bufferGrid); return map; @@ -123,7 +125,7 @@ public class NTerrainStage implements NStage { @Nonnull @Override - public List getOutputTypes() { + public List getOutputTypes() { return List.of(this.materialOutputBufferType); } @@ -135,14 +137,14 @@ public class NTerrainStage implements NStage { private void generateDensity( @Nonnull FloatContainer3d densityBuffer, - @Nonnull NPixelBufferView biomeSpace, - @Nonnull NPixelBufferView distanceSpace, + @Nonnull PixelBufferView biomeSpace, + @Nonnull PixelBufferView distanceSpace, @Nonnull Registry biomeRegistry ) { Bounds3i bounds_voxelGrid = densityBuffer.getBounds_voxelGrid(); Vector3i position_voxelGrid = new Vector3i(bounds_voxelGrid.min); Density.Context densityContext = new Density.Context(); - densityContext.position = position_voxelGrid.toVector3d(); + densityContext.position = Vector3iUtil.toVector3d(position_voxelGrid); for (position_voxelGrid.x = bounds_voxelGrid.min.x; position_voxelGrid.x < bounds_voxelGrid.max.x; position_voxelGrid.x++) { densityContext.position.x = position_voxelGrid.x; @@ -150,8 +152,7 @@ public class NTerrainStage implements NStage { for (position_voxelGrid.z = bounds_voxelGrid.min.z; position_voxelGrid.z < bounds_voxelGrid.max.z; position_voxelGrid.z++) { densityContext.position.z = position_voxelGrid.z; position_voxelGrid.y = 0; - position_voxelGrid.dropHash(); - Integer biomeIdAtOrigin = biomeSpace.getContent(position_voxelGrid); + Integer biomeIdAtOrigin = biomeSpace.get(position_voxelGrid); assert biomeIdAtOrigin != null; @@ -159,17 +160,16 @@ public class NTerrainStage implements NStage { assert biomeAtOrigin != null; - NBiomeDistanceStage.BiomeDistanceEntries biomeDistances = distanceSpace.getContent(position_voxelGrid); - NTerrainStage.BiomeWeights biomeWeights = createWeights(biomeDistances, biomeIdAtOrigin, this.maxInterpolationRadius_voxelGrid); + BiomeDistanceStage.BiomeDistanceEntries biomeDistances = distanceSpace.get(position_voxelGrid); + TerrainStage.BiomeWeights biomeWeights = createWeights(biomeDistances, biomeIdAtOrigin, this.maxInterpolationRadius_voxelGrid); densityContext.distanceToBiomeEdge = biomeDistances.distanceToClosestOtherBiome(biomeIdAtOrigin); boolean isFirstBiome = true; - for (NTerrainStage.BiomeWeights.Entry biomeWeight : biomeWeights.entries) { + for (TerrainStage.BiomeWeights.Entry biomeWeight : biomeWeights.entries) { Biome biome = biomeRegistry.getObject(biomeWeight.biomeId); Density density = biome.getTerrainDensity(); if (isFirstBiome) { for (position_voxelGrid.y = bounds_voxelGrid.min.y; position_voxelGrid.y < bounds_voxelGrid.max.y; position_voxelGrid.y++) { - position_voxelGrid.dropHash(); densityContext.position.y = position_voxelGrid.y; float densityValue = (float)density.process(densityContext); float scaledDensityValue = densityValue * biomeWeight.weight; @@ -179,7 +179,6 @@ public class NTerrainStage implements NStage { if (!isFirstBiome) { for (position_voxelGrid.y = bounds_voxelGrid.min.y; position_voxelGrid.y < bounds_voxelGrid.max.y; position_voxelGrid.y++) { - position_voxelGrid.dropHash(); densityContext.position.y = position_voxelGrid.y; float bufferDensityValue = densityBuffer.get(position_voxelGrid); float densityValue = (float)density.process(densityContext); @@ -197,8 +196,8 @@ public class NTerrainStage implements NStage { private float getOrGenerateDensity( @Nonnull Vector3i position_voxelGrid, @Nonnull FloatContainer3d densityBuffer, - @Nonnull NPixelBufferView biomeSpace, - @Nonnull NPixelBufferView distanceSpace, + @Nonnull PixelBufferView biomeSpace, + @Nonnull PixelBufferView distanceSpace, @Nonnull Registry biomeRegistry ) { return densityBuffer.getBounds_voxelGrid().contains(position_voxelGrid) @@ -208,24 +207,24 @@ public class NTerrainStage implements NStage { private float generateDensity( @Nonnull Vector3i position_voxelGrid, - @Nonnull NPixelBufferView biomeSpace, - @Nonnull NPixelBufferView distanceSpace, + @Nonnull PixelBufferView biomeSpace, + @Nonnull PixelBufferView distanceSpace, @Nonnull Registry biomeRegistry ) { - if (!distanceSpace.isInsideSpace(position_voxelGrid.x, 0, position_voxelGrid.z)) { + if (!distanceSpace.getBounds().contains(position_voxelGrid.x, 0, position_voxelGrid.z)) { return 0.0F; } else { Density.Context densityContext = new Density.Context(); - densityContext.position = position_voxelGrid.toVector3d(); - Integer biomeIdAtOrigin = biomeSpace.getContent(position_voxelGrid.x, 0, position_voxelGrid.z); + densityContext.position = Vector3iUtil.toVector3d(position_voxelGrid); + Integer biomeIdAtOrigin = biomeSpace.get(position_voxelGrid.x, 0, position_voxelGrid.z); assert biomeIdAtOrigin != null; - NBiomeDistanceStage.BiomeDistanceEntries biomeDistances = distanceSpace.getContent(position_voxelGrid.x, 0, position_voxelGrid.z); - NTerrainStage.BiomeWeights biomeWeights = createWeights(biomeDistances, biomeIdAtOrigin, this.maxInterpolationRadius_voxelGrid); + BiomeDistanceStage.BiomeDistanceEntries biomeDistances = distanceSpace.get(position_voxelGrid.x, 0, position_voxelGrid.z); + TerrainStage.BiomeWeights biomeWeights = createWeights(biomeDistances, biomeIdAtOrigin, this.maxInterpolationRadius_voxelGrid); float densityResult = 0.0F; - for (NTerrainStage.BiomeWeights.Entry biomeWeight : biomeWeights.entries) { + for (TerrainStage.BiomeWeights.Entry biomeWeight : biomeWeights.entries) { Biome biome = biomeRegistry.getObject(biomeWeight.biomeId); Density density = biome.getTerrainDensity(); float densityValue = (float)density.process(densityContext); @@ -238,31 +237,31 @@ public class NTerrainStage implements NStage { } private void generateMaterials( - @Nonnull NPixelBufferView biomeSpace, - @Nonnull NPixelBufferView distanceSpace, + @Nonnull PixelBufferView biomeSpace, + @Nonnull PixelBufferView distanceSpace, @Nonnull FloatContainer3d densityBuffer, - @Nonnull NVoxelBufferView materialSpace, + @Nonnull VoxelBufferView materialSpace, @Nonnull Registry biomeRegistry ) { Bounds3i bounds_voxelGrid = materialSpace.getBounds(); MaterialProvider.Context context = new MaterialProvider.Context( new Vector3i(), 0.0, 0, 0, 0, 0, position -> this.getOrGenerateDensity(position, densityBuffer, biomeSpace, distanceSpace, biomeRegistry), 0.0 ); - NTerrainStage.ColumnData columnData = new NTerrainStage.ColumnData(bounds_voxelGrid.min.y, bounds_voxelGrid.max.y, densityBuffer); + TerrainStage.ColumnData columnData = new TerrainStage.ColumnData(bounds_voxelGrid.min.y, bounds_voxelGrid.max.y, densityBuffer); Vector3i position_voxelGrid = new Vector3i(); for (position_voxelGrid.x = bounds_voxelGrid.min.x; position_voxelGrid.x < bounds_voxelGrid.max.x; position_voxelGrid.x++) { for (position_voxelGrid.z = bounds_voxelGrid.min.z; position_voxelGrid.z < bounds_voxelGrid.max.z; position_voxelGrid.z++) { position_voxelGrid.y = bounds_voxelGrid.min.y; - Integer biomeId = biomeSpace.getContent(position_voxelGrid.x, 0, position_voxelGrid.z); + Integer biomeId = biomeSpace.get(position_voxelGrid.x, 0, position_voxelGrid.z); Biome biome = biomeRegistry.getObject(biomeId); MaterialProvider materialProvider = biome.getMaterialProvider(); columnData.resolve(position_voxelGrid.x, position_voxelGrid.z, materialProvider); - double distanceToOtherBiome_voxelGrid = distanceSpace.getContent(position_voxelGrid).distanceToClosestOtherBiome(biomeId); + double distanceToOtherBiome_voxelGrid = distanceSpace.get(position_voxelGrid).distanceToClosestOtherBiome(biomeId); for (position_voxelGrid.y = bounds_voxelGrid.min.y; position_voxelGrid.y < bounds_voxelGrid.max.y; position_voxelGrid.y++) { int i = position_voxelGrid.y - bounds_voxelGrid.min.y; - context.position.assign(position_voxelGrid); + context.position.set(position_voxelGrid); context.density = densityBuffer.get(position_voxelGrid); context.depthIntoFloor = columnData.depthIntoFloor[i]; context.depthIntoCeiling = columnData.depthIntoCeiling[i]; @@ -281,18 +280,18 @@ public class NTerrainStage implements NStage { } @Nonnull - private static NTerrainStage.BiomeWeights createWeights( - @Nonnull NBiomeDistanceStage.BiomeDistanceEntries distances, int biomeIdAtOrigin, double interpolationRange + private static TerrainStage.BiomeWeights createWeights( + @Nonnull BiomeDistanceStage.BiomeDistanceEntries distances, int biomeIdAtOrigin, double interpolationRange ) { double circleRadius = interpolationRange + 0.5; - NTerrainStage.BiomeWeights biomeWeights = new NTerrainStage.BiomeWeights(); + TerrainStage.BiomeWeights biomeWeights = new TerrainStage.BiomeWeights(); int originIndex = 0; double smallestNonOriginDistance = Double.MAX_VALUE; double total = 0.0; for (int i = 0; i < distances.entries.size(); i++) { - NBiomeDistanceStage.BiomeDistanceEntry distanceEntry = distances.entries.get(i); - NTerrainStage.BiomeWeights.Entry weightEntry = new NTerrainStage.BiomeWeights.Entry(); + BiomeDistanceStage.BiomeDistanceEntry distanceEntry = distances.entries.get(i); + TerrainStage.BiomeWeights.Entry weightEntry = new TerrainStage.BiomeWeights.Entry(); if (!(distanceEntry.distance_voxelGrid >= interpolationRange)) { if (distanceEntry.biomeId == biomeIdAtOrigin) { originIndex = biomeWeights.entries.size(); @@ -308,14 +307,14 @@ public class NTerrainStage implements NStage { } if (biomeWeights.entries.size() > 0) { - NTerrainStage.BiomeWeights.Entry originWeightEntry = biomeWeights.entries.get(originIndex); + TerrainStage.BiomeWeights.Entry originWeightEntry = biomeWeights.entries.get(originIndex); double maxX = 0.5 + smallestNonOriginDistance; double originExtraWeight = areaUnderCircleCurve(0.0, maxX, circleRadius); originWeightEntry.weight = (float)(originWeightEntry.weight + originExtraWeight); total += originExtraWeight; } - for (NTerrainStage.BiomeWeights.Entry entry : biomeWeights.entries) { + for (TerrainStage.BiomeWeights.Entry entry : biomeWeights.entries) { entry.weight /= (float)total; } @@ -341,7 +340,7 @@ public class NTerrainStage implements NStage { } private static class BiomeWeights { - List entries = new ArrayList<>(3); + List entries = new ArrayList<>(3); BiomeWeights() { } @@ -367,6 +366,8 @@ public class NTerrainStage implements NStage { FloatContainer3d densityBuffer; ColumnData(int bottom, int topExclusive, @Nonnull FloatContainer3d densityBuffer) { + Objects.requireNonNull(TerrainStage.this); + super(); this.topExclusive = topExclusive; this.bottom = bottom; this.densityBuffer = densityBuffer; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTintStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/TintStage.java similarity index 58% rename from src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTintStage.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/TintStage.java index 138a8ffe..ba86ebe5 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTintStage.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/stages/TintStage.java @@ -1,36 +1,37 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; +package com.hypixel.hytale.builtin.hytalegenerator.engine.stages; +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; import com.hypixel.hytale.builtin.hytalegenerator.Registry; import com.hypixel.hytale.builtin.hytalegenerator.biome.Biome; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NCountedPixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NSimplePixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NPixelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.CountedPixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.SimplePixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.ParametrizedBufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.views.PixelBufferView; import com.hypixel.hytale.builtin.hytalegenerator.tintproviders.TintProvider; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; +import org.joml.Vector3i; -public class NTintStage implements NStage { +public class TintStage implements Stage { @Nonnull - public static final Class biomeBufferClass = NCountedPixelBuffer.class; + public static final Class biomeBufferClass = CountedPixelBuffer.class; @Nonnull public static final Class biomeClass = Integer.class; @Nonnull - public static final Class tintBufferClass = NSimplePixelBuffer.class; + public static final Class tintBufferClass = SimplePixelBuffer.class; @Nonnull public static final Class tintClass = Integer.class; @Nonnull - private final NParametrizedBufferType biomeInputBufferType; + private final ParametrizedBufferType biomeInputBufferType; @Nonnull - private final NParametrizedBufferType tintOutputBufferType; + private final ParametrizedBufferType tintOutputBufferType; @Nonnull private final Bounds3i inputBounds_bufferGrid; @Nonnull @@ -38,10 +39,10 @@ public class NTintStage implements NStage { @Nonnull private final WorkerIndexer.Data worldStructure_workerData; - public NTintStage( + public TintStage( @Nonnull String stageName, - @Nonnull NParametrizedBufferType biomeInputBufferType, - @Nonnull NParametrizedBufferType tintOutputBufferType, + @Nonnull ParametrizedBufferType biomeInputBufferType, + @Nonnull ParametrizedBufferType tintOutputBufferType, @Nonnull WorkerIndexer.Data worldStructure_workerData ) { assert biomeInputBufferType.isValidType(biomeBufferClass, biomeClass); @@ -52,24 +53,24 @@ public class NTintStage implements NStage { this.tintOutputBufferType = tintOutputBufferType; this.stageName = stageName; this.worldStructure_workerData = worldStructure_workerData; - this.inputBounds_bufferGrid = GridUtils.createUnitBounds3i(Vector3i.ZERO); + this.inputBounds_bufferGrid = GridUtils.createUnitBounds3i(Vector3iUtil.ZERO); } @Override - public void run(@Nonnull NStage.Context context) { - NBufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); - NPixelBufferView biomeSpace = new NPixelBufferView<>(biomeAccess, biomeClass); - NBufferBundle.Access.View tintAccess = context.bufferAccess.get(this.tintOutputBufferType); - NPixelBufferView tintSpace = new NPixelBufferView<>(tintAccess, tintClass); + public void run(@Nonnull Stage.Context context) { + BufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); + PixelBufferView biomeSpace = new PixelBufferView<>(biomeAccess, biomeClass); + BufferBundle.Access.View tintAccess = context.bufferAccess.get(this.tintOutputBufferType); + PixelBufferView tintSpace = new PixelBufferView<>(tintAccess, tintClass); Bounds3i outputBounds_voxelGrid = tintSpace.getBounds(); Registry biomeRegistry = this.worldStructure_workerData.get(context.workerId).getBiomeRegistry(); Vector3i position_voxelGrid = new Vector3i(outputBounds_voxelGrid.min); - position_voxelGrid.setY(0); + position_voxelGrid.y = 0; TintProvider.Context tintContext = new TintProvider.Context(position_voxelGrid, context.workerId); for (position_voxelGrid.x = outputBounds_voxelGrid.min.x; position_voxelGrid.x < outputBounds_voxelGrid.max.x; position_voxelGrid.x++) { for (position_voxelGrid.z = outputBounds_voxelGrid.min.z; position_voxelGrid.z < outputBounds_voxelGrid.max.z; position_voxelGrid.z++) { - Integer biomeId = biomeSpace.getContent(position_voxelGrid.x, 0, position_voxelGrid.z); + Integer biomeId = biomeSpace.get(position_voxelGrid.x, 0, position_voxelGrid.z); assert biomeId != null; @@ -90,13 +91,13 @@ public class NTintStage implements NStage { @Nonnull @Override - public Map getInputTypesAndBounds_bufferGrid() { + public Map getInputTypesAndBounds_bufferGrid() { return Map.of(this.biomeInputBufferType, this.inputBounds_bufferGrid); } @Nonnull @Override - public List getOutputTypes() { + public List getOutputTypes() { return List.of(this.tintOutputBufferType); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/EntityBufferView.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/EntityBufferView.java new file mode 100644 index 00000000..ba31e443 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/EntityBufferView.java @@ -0,0 +1,90 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.views; + +import com.hypixel.hytale.builtin.hytalegenerator.EntityPlacementData; +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.EntityBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.entityfunnel.EntityFunnel; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import java.util.function.Consumer; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; +import org.joml.Vector3i; + +public class EntityBufferView implements EntityFunnel { + @Nonnull + private final BufferBundle.Access.View access; + @Nonnull + private final Bounds3i bounds_voxelGrid; + @Nonnull + private final Bounds3i bounds_bufferGrid; + + public EntityBufferView(@Nonnull BufferBundle.Access.View bufferAccess) { + this.access = bufferAccess; + this.bounds_bufferGrid = bufferAccess.getBounds_bufferGrid(); + this.bounds_voxelGrid = bufferAccess.getBounds_bufferGrid(); + GridUtils.toVoxelGrid_fromBufferGrid(this.bounds_voxelGrid); + } + + public void forEach(@Nonnull Consumer consumer) { + Vector3i position_bufferGrid = new Vector3i(this.bounds_voxelGrid.min); + + for (position_bufferGrid.x = this.bounds_bufferGrid.min.x; position_bufferGrid.x < this.bounds_bufferGrid.max.x; position_bufferGrid.x++) { + for (position_bufferGrid.z = this.bounds_bufferGrid.min.z; position_bufferGrid.z < this.bounds_bufferGrid.max.z; position_bufferGrid.z++) { + for (position_bufferGrid.y = this.bounds_bufferGrid.min.y; position_bufferGrid.y < this.bounds_bufferGrid.max.y; position_bufferGrid.y++) { + EntityBuffer buffer = this.getBuffer_fromBufferGrid(position_bufferGrid); + buffer.forEach(consumer); + } + } + } + } + + @Nonnull + private EntityBuffer getBuffer_fromBufferGrid(@Nonnull Vector3i position_bufferGrid) { + return (EntityBuffer)this.access.getBuffer(position_bufferGrid).buffer(); + } + + public void copyFrom(@Nonnull EntityBufferView source) { + assert source.bounds_voxelGrid.contains(this.bounds_voxelGrid); + + Bounds3i thisBounds_bufferGrid = this.access.getBounds_bufferGrid(); + Vector3i pos_bufferGrid = new Vector3i(); + + for (pos_bufferGrid.x = thisBounds_bufferGrid.min.x; pos_bufferGrid.x < thisBounds_bufferGrid.max.x; pos_bufferGrid.x++) { + for (pos_bufferGrid.y = thisBounds_bufferGrid.min.y; pos_bufferGrid.y < thisBounds_bufferGrid.max.y; pos_bufferGrid.y++) { + for (pos_bufferGrid.z = thisBounds_bufferGrid.min.z; pos_bufferGrid.z < thisBounds_bufferGrid.max.z; pos_bufferGrid.z++) { + EntityBuffer sourceBuffer = source.getBuffer_fromBufferGrid(pos_bufferGrid); + EntityBuffer destinationBuffer = this.getBuffer_fromBufferGrid(pos_bufferGrid); + destinationBuffer.copyFrom(sourceBuffer); + } + } + } + } + + @Override + public void addEntity(@Nonnull EntityPlacementData entityPlacementData) { + Vector3d entityPosition_voxelGrid = Vector3iUtil.toVector3d(entityPlacementData.getOffset()); + TransformComponent transform = entityPlacementData.getEntityHolder().getComponent(TransformComponent.getComponentType()); + + assert transform != null; + + Vector3d holderPosition_voxelGrid = transform.getPosition(); + entityPosition_voxelGrid.add(holderPosition_voxelGrid); + Vector3i position_bufferGrid = GridUtils.toIntegerGrid_fromDecimalGrid(entityPosition_voxelGrid); + + assert this.bounds_voxelGrid.contains(position_bufferGrid); + + GridUtils.toBufferGrid_fromVoxelGrid(position_bufferGrid); + EntityBuffer buffer = (EntityBuffer)this.access.getBuffer(position_bufferGrid).buffer(); + buffer.addEntity(entityPlacementData); + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return this.bounds_voxelGrid; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/PixelBufferView.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/PixelBufferView.java new file mode 100644 index 00000000..6dfba723 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/PixelBufferView.java @@ -0,0 +1,88 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.views; + +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.PixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class PixelBufferView implements VoxelSpace { + public static final int Y_LEVEL_BUFFER_GRID = 0; + public static final int Y_LEVEL_VOXEL_GRID = 0; + @Nonnull + private final Class voxelType; + @Nonnull + private final BufferBundle.Access.View bufferAccess; + @Nonnull + private final Bounds3i bounds_voxelGrid; + @Nonnull + private final Vector3i size_voxelGrid; + + public PixelBufferView(@Nonnull BufferBundle.Access.View bufferAccess, @Nonnull Class pixelType) { + assert bufferAccess.getBounds_bufferGrid().min.y <= 0 && bufferAccess.getBounds_bufferGrid().max.y > 0; + + this.bufferAccess = bufferAccess; + this.voxelType = pixelType; + this.bounds_voxelGrid = bufferAccess.getBounds_bufferGrid(); + GridUtils.toVoxelGrid_fromBufferGrid(this.bounds_voxelGrid); + this.bounds_voxelGrid.min.y = 0; + this.bounds_voxelGrid.max.y = 1; + this.size_voxelGrid = this.bounds_voxelGrid.getSize(); + } + + @Override + public void set(T content, int x, int y, int z) { + this.set(content, new Vector3i(x, y, z)); + } + + @Override + public void set(T value, @Nonnull Vector3i position_voxelGrid) { + assert this.bounds_voxelGrid.contains(position_voxelGrid); + + PixelBuffer buffer = this.getBuffer(position_voxelGrid); + Vector3i positionInBuffer_voxelGrid = new Vector3i(position_voxelGrid); + GridUtils.toVoxelGridInsideBuffer_fromWorldGrid(positionInBuffer_voxelGrid); + buffer.setPixelContent(positionInBuffer_voxelGrid, value); + } + + @Override + public void setAll(T content) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public T get(int x, int y, int z) { + return this.get(new Vector3i(x, y, z)); + } + + @Nullable + @Override + public T get(@Nonnull Vector3i position_voxelGrid) { + assert this.bounds_voxelGrid.contains(position_voxelGrid); + + PixelBuffer buffer = this.getBuffer(position_voxelGrid); + Vector3i positionInBuffer_voxelGrid = new Vector3i(position_voxelGrid); + GridUtils.toVoxelGridInsideBuffer_fromWorldGrid(positionInBuffer_voxelGrid); + return buffer.getPixelContent(positionInBuffer_voxelGrid); + } + + @Nonnull + private PixelBuffer getBuffer(@Nonnull Vector3i position_voxelGrid) { + assert this.bounds_voxelGrid.contains(position_voxelGrid); + + Vector3i localBufferPosition_bufferGrid = new Vector3i(position_voxelGrid); + GridUtils.toBufferGrid_fromVoxelGrid(localBufferPosition_bufferGrid); + return (PixelBuffer)this.bufferAccess.getBuffer(localBufferPosition_bufferGrid).buffer(); + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return this.bounds_voxelGrid; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/VoxelBufferView.java b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/VoxelBufferView.java new file mode 100644 index 00000000..00c3f6dd --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/engine/views/VoxelBufferView.java @@ -0,0 +1,134 @@ +package com.hypixel.hytale.builtin.hytalegenerator.engine.views; + +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.BufferBundle; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.VoxelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class VoxelBufferView implements VoxelSpace { + @Nonnull + private final Class voxelType; + @Nonnull + private final BufferBundle.Access.View bufferAccess; + @Nonnull + private final Bounds3i bounds_voxelGrid; + @Nonnull + private final Vector3i size_voxelGrid; + + public VoxelBufferView(@Nonnull BufferBundle.Access.View bufferAccess, @Nonnull Class voxelType) { + this.bufferAccess = bufferAccess; + this.voxelType = voxelType; + this.bounds_voxelGrid = bufferAccess.getBounds_bufferGrid(); + GridUtils.toVoxelGrid_fromBufferGrid(this.bounds_voxelGrid); + this.size_voxelGrid = this.bounds_voxelGrid.getSize(); + } + + public void copyFrom(@Nonnull VoxelBufferView source) { + assert source.bounds_voxelGrid.contains(this.bounds_voxelGrid); + + Bounds3i thisBounds_bufferGrid = this.bufferAccess.getBounds_bufferGrid(); + Vector3i pos_bufferGrid = new Vector3i(); + + for (pos_bufferGrid.x = thisBounds_bufferGrid.min.x; pos_bufferGrid.x < thisBounds_bufferGrid.max.x; pos_bufferGrid.x++) { + for (pos_bufferGrid.y = thisBounds_bufferGrid.min.y; pos_bufferGrid.y < thisBounds_bufferGrid.max.y; pos_bufferGrid.y++) { + for (pos_bufferGrid.z = thisBounds_bufferGrid.min.z; pos_bufferGrid.z < thisBounds_bufferGrid.max.z; pos_bufferGrid.z++) { + VoxelBuffer sourceBuffer = source.getBuffer_fromBufferGrid(pos_bufferGrid); + VoxelBuffer destinationBuffer = this.getBuffer_fromBufferGrid(pos_bufferGrid); + destinationBuffer.reference(sourceBuffer); + } + } + } + } + + @Override + public void set(T content, int x, int y, int z) { + assert this.bounds_voxelGrid.contains(x, y, z); + + VoxelBuffer buffer = this.getBuffer_fromVoxelGrid(x, y, z); + int x_internal = GridUtils.toXVoxelGridInsideBuffer_fromWorldGrid(x); + int y_internal = GridUtils.toYVoxelGridInsideBuffer_fromWorldGrid(y); + int z_internal = GridUtils.toZVoxelGridInsideBuffer_fromWorldGrid(z); + buffer.setVoxelContent(x_internal, y_internal, z_internal, content); + } + + @Override + public void set(T content, @Nonnull Vector3i position_voxelGrid) { + assert this.bounds_voxelGrid.contains(position_voxelGrid); + + int initialX = position_voxelGrid.x; + int initialY = position_voxelGrid.y; + int initialZ = position_voxelGrid.z; + VoxelBuffer buffer = this.getBuffer_fromVoxelGrid(position_voxelGrid); + GridUtils.toVoxelGridInsideBuffer_fromWorldGrid(position_voxelGrid); + buffer.setVoxelContent(position_voxelGrid, content); + position_voxelGrid.set(initialX, initialY, initialZ); + } + + @Override + public void setAll(T content) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public T get(int x, int y, int z) { + assert this.bounds_voxelGrid.contains(x, y, z); + + VoxelBuffer buffer = this.getBuffer_fromVoxelGrid(x, y, z); + int x_internal = GridUtils.toXVoxelGridInsideBuffer_fromWorldGrid(x); + int y_internal = GridUtils.toYVoxelGridInsideBuffer_fromWorldGrid(y); + int z_internal = GridUtils.toZVoxelGridInsideBuffer_fromWorldGrid(z); + return buffer.getVoxelContent(x_internal, y_internal, z_internal); + } + + @Nullable + @Override + public T get(@Nonnull Vector3i position_voxelGrid) { + assert this.bounds_voxelGrid.contains(position_voxelGrid); + + int initialX = position_voxelGrid.x; + int initialY = position_voxelGrid.y; + int initialZ = position_voxelGrid.z; + VoxelBuffer buffer = this.getBuffer_fromVoxelGrid(position_voxelGrid); + GridUtils.toVoxelGridInsideBuffer_fromWorldGrid(position_voxelGrid); + T content = buffer.getVoxelContent(position_voxelGrid); + position_voxelGrid.set(initialX, initialY, initialZ); + return content; + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return this.bounds_voxelGrid; + } + + @Nonnull + private VoxelBuffer getBuffer_fromVoxelGrid(int x_voxelGrid, int y_voxelGrid, int z_voxelGrid) { + int x_bufferGrid = GridUtils.toBufferGrid_fromVoxelGrid(x_voxelGrid); + int y_bufferGrid = GridUtils.toBufferGrid_fromVoxelGrid(y_voxelGrid); + int z_bufferGrid = GridUtils.toBufferGrid_fromVoxelGrid(z_voxelGrid); + return this.getBuffer_fromBufferGrid(x_bufferGrid, y_bufferGrid, z_bufferGrid); + } + + @Nonnull + private VoxelBuffer getBuffer_fromVoxelGrid(@Nonnull Vector3i position_voxelGrid) { + Vector3i localBufferPosition_bufferGrid = new Vector3i(position_voxelGrid); + GridUtils.toBufferGrid_fromVoxelGrid(localBufferPosition_bufferGrid); + return this.getBuffer_fromBufferGrid(localBufferPosition_bufferGrid); + } + + @Nonnull + private VoxelBuffer getBuffer_fromBufferGrid(int x_bufferGrid, int y_bufferGrid, int z_bufferGrid) { + return (VoxelBuffer)this.bufferAccess.getBuffer(x_bufferGrid, y_bufferGrid, z_bufferGrid).buffer(); + } + + @Nonnull + private VoxelBuffer getBuffer_fromBufferGrid(@Nonnull Vector3i position_bufferGrid) { + return (VoxelBuffer)this.bufferAccess.getBuffer(position_bufferGrid).buffer(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/environmentproviders/EnvironmentProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/environmentproviders/EnvironmentProvider.java index 85c6cb6a..023ac327 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/environmentproviders/EnvironmentProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/environmentproviders/EnvironmentProvider.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.environmentproviders; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public abstract class EnvironmentProvider { public abstract int getValue(@Nonnull EnvironmentProvider.Context var1); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/ImageCarta.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/ImageCarta.java deleted file mode 100644 index 5ba4c613..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/ImageCarta.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.cartas; - -import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.TriCarta; -import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.TriDoubleFunction; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class ImageCarta extends TriCarta { - private int[] rgbArray; - private int width; - private int height; - private TriDoubleFunction functionX; - private TriDoubleFunction functionY; - private Map rgbToTerrainMap; - private List allPossibleValues; - - private ImageCarta() { - } - - @Nullable - @Override - public R apply(int x, int y, int z, @Nonnull WorkerIndexer.Id tHreadId) { - Objects.requireNonNull(x); - Objects.requireNonNull(y); - Objects.requireNonNull(z); - int sampleX = Calculator.toNearestInt(this.functionX.apply(x, y, z) * this.width); - sampleX = sampleX < 0 ? 0 : Math.min(sampleX, this.width - 1); - int sampleY = Calculator.toNearestInt(this.functionY.apply(x, y, z) * this.height); - sampleY = sampleY < 0 ? 0 : Math.min(sampleY, this.height - 1); - int rgb = this.rgbArray[sampleX + sampleY * this.width]; - return !this.rgbToTerrainMap.containsKey(rgb) ? null : this.rgbToTerrainMap.get(rgb); - } - - @Override - public List allPossibleValues() { - return this.allPossibleValues; - } - - public static int greenFromRgb(int rgb) { - return (rgb & 0xFF00) >> 8; - } - - public static int redFromRgb(int rgb) { - return (rgb & 0xFF0000) >> 16; - } - - public static int blueFromRgb(int rgb) { - return rgb & 0xFF; - } - - public static int coloursToRgb(int red, int green, int blue) { - int rgb = red << 16; - rgb += green << 8; - return rgb + blue; - } - - @Nonnull - @Override - public String toString() { - return "ImageCarta{rgbArray=" - + Arrays.toString(this.rgbArray) - + ", width=" - + this.width - + ", height=" - + this.height - + ", functionX=" - + this.functionX - + ", functionY=" - + this.functionY - + ", rgbToTerrainMap=" - + this.rgbToTerrainMap - + ", allPossibleValues=" - + this.allPossibleValues - + "}"; - } - - public static class Builder { - @Nonnull - private final Map rgbToTerrainMap = new HashMap<>(); - private BufferedImage bufferedImage; - private boolean bufferedImageCheck; - private TriDoubleFunction noiseX; - private TriDoubleFunction noiseY; - private boolean noiseCheck; - - @Nonnull - public ImageCarta build() { - if (this.bufferedImageCheck && this.noiseCheck) { - ImageCarta instance = new ImageCarta<>(); - instance.rgbToTerrainMap = this.rgbToTerrainMap; - instance.functionX = this.noiseX; - instance.functionY = this.noiseY; - instance.allPossibleValues = new ArrayList<>(1); - instance.allPossibleValues.addAll(this.rgbToTerrainMap.values()); - instance.width = this.bufferedImage.getWidth(); - instance.height = this.bufferedImage.getHeight(); - instance.rgbArray = new int[instance.width * instance.height]; - - for (int x = 0; x < instance.width; x++) { - for (int y = 0; y < instance.height; y++) { - instance.rgbArray[x + y * instance.width] = 16777215 & this.bufferedImage.getRGB(x, y); - } - } - - return instance; - } else { - throw new IllegalStateException("incomplete builder"); - } - } - - @Nonnull - public ImageCarta.Builder withImage(BufferedImage image) { - Objects.requireNonNull(image); - this.bufferedImage = image; - this.bufferedImageCheck = true; - return this; - } - - @Nonnull - public ImageCarta.Builder withNoiseFunctions(TriDoubleFunction noiseX, TriDoubleFunction noiseY) { - Objects.requireNonNull(noiseX); - Objects.requireNonNull(noiseY); - this.noiseX = noiseX; - this.noiseY = noiseY; - this.noiseCheck = true; - return this; - } - - @Nonnull - public ImageCarta.Builder addTerrainRgb(int rgb, @Nonnull R terrain) { - this.rgbToTerrainMap.put(rgb, terrain); - return this; - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/LayeredCarta.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/LayeredCarta.java deleted file mode 100644 index 191f72ff..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/LayeredCarta.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.cartas; - -import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.TriCarta; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import javax.annotation.Nonnull; - -public class LayeredCarta extends TriCarta { - @Nonnull - private final List> layers; - @Nonnull - private final List allValues; - @Nonnull - private final R defaultValue; - - public LayeredCarta(@Nonnull R defaultValue) { - Objects.requireNonNull(defaultValue); - this.layers = new ArrayList<>(1); - this.allValues = new ArrayList<>(1); - this.defaultValue = defaultValue; - this.allValues.add(defaultValue); - } - - @Override - public R apply(int x, int y, int z, @Nonnull WorkerIndexer.Id id) { - R result = this.defaultValue; - - for (TriCarta layer : this.layers) { - R value = layer.apply(x, y, z, id); - if (value != null) { - result = value; - } - } - - return result; - } - - @Nonnull - @Override - public List allPossibleValues() { - return Collections.unmodifiableList(this.allValues); - } - - @Nonnull - public LayeredCarta addLayer(@Nonnull TriCarta layer) { - Objects.requireNonNull(layer); - this.layers.add(layer); - this.allValues.addAll(layer.allPossibleValues()); - return this; - } - - @Nonnull - @Override - public String toString() { - return "LayeredCarta{layers=" + this.layers + ", allValues=" + this.allValues + ", defaultValue=" + this.defaultValue + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/SingleElementCarta.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/SingleElementCarta.java deleted file mode 100644 index d7356846..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/cartas/SingleElementCarta.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.cartas; - -import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.BiCarta; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; - -public class SingleElementCarta extends BiCarta { - private R element; - - private SingleElementCarta() { - } - - @Nonnull - public static SingleElementCarta of(@Nonnull R element) { - SingleElementCarta c = new SingleElementCarta<>(); - c.element = element; - return c; - } - - @Override - public R apply(int x, int z, @Nonnull WorkerIndexer.Id id) { - return this.element; - } - - @Nonnull - @Override - public List allPossibleValues() { - return Collections.singletonList(this.element); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/BiDouble2DoubleFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/BiDouble2DoubleFunction.java deleted file mode 100644 index 02c9aedb..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/BiDouble2DoubleFunction.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions; - -@FunctionalInterface -public interface BiDouble2DoubleFunction { - double apply(double var1, double var3); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/QuadDoubleFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/QuadDoubleFunction.java deleted file mode 100644 index dbdd1293..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/QuadDoubleFunction.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions; - -@FunctionalInterface -public interface QuadDoubleFunction { - R apply(double var1, double var3, double var5, double var7); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/QuintoDoubleFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/QuintoDoubleFunction.java deleted file mode 100644 index 9e0fbbce..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/QuintoDoubleFunction.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions; - -@FunctionalInterface -public interface QuintoDoubleFunction { - R apply(double var1, double var3, double var5, double var7, double var9); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriCarta.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriCarta.java deleted file mode 100644 index 8c3709fa..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriCarta.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions; - -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public abstract class TriCarta { - @Nullable - public abstract R apply(int var1, int var2, int var3, @Nonnull WorkerIndexer.Id var4); - - public abstract List allPossibleValues(); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriDoubleFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriDoubleFunction.java deleted file mode 100644 index 0f97a5bc..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriDoubleFunction.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions; - -@FunctionalInterface -public interface TriDoubleFunction { - R apply(double var1, double var3, double var5); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriFunction.java deleted file mode 100644 index ae644d3e..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/TriFunction.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions; - -@FunctionalInterface -public interface TriFunction { - R apply(A var1, B var2, C var3); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/BitConverter.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/BitConverter.java deleted file mode 100644 index 24370874..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/BitConverter.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -import javax.annotation.Nonnull; - -public class BitConverter { - public static void main(String[] args) { - System.out.println("LONG TEST:"); - - for (int i = -4; i < 10; i++) { - System.out.println(); - System.out.print("INPUT [" + i + "] -> BINARY -> ["); - boolean[] output = toBitArray((long)i); - - for (boolean bit : output) { - System.out.print(bit ? "1" : "0"); - } - - System.out.print("] -> DECIMAL -> [" + toLong(output) + "]"); - } - - System.out.println(); - System.out.println("INT TEST:"); - - for (int i = -4; i < 10; i++) { - System.out.println(); - System.out.print("INPUT [" + i + "] -> BINARY -> ["); - boolean[] output = toBitArray(i); - - for (boolean bit : output) { - System.out.print(bit ? "1" : "0"); - } - - System.out.print("] -> DECIMAL -> [" + toInt(output) + "]"); - } - - System.out.println(); - System.out.println("BYTE TEST:"); - - for (int i = -4; i < 10; i++) { - System.out.println(); - System.out.print("INPUT [" + i + "] -> BINARY -> ["); - boolean[] output = toBitArray((byte)i); - - for (boolean bit : output) { - System.out.print(bit ? "1" : "0"); - } - - System.out.print("] -> DECIMAL -> [" + toByte(output) + "]"); - } - - System.out.println(); - } - - public static boolean[] toBitArray(long number) { - byte PRECISION = 64; - boolean[] bits = new boolean[64]; - long position = 1L; - - for (byte i = 63; i >= 0; i--) { - bits[i] = (number & position) != 0L; - position <<= 1; - } - - return bits; - } - - public static boolean[] toBitArray(int number) { - byte PRECISION = 32; - boolean[] bits = new boolean[32]; - int position = 1; - - for (byte i = 31; i >= 0; i--) { - bits[i] = (number & position) != 0; - position <<= 1; - } - - return bits; - } - - public static boolean[] toBitArray(byte number) { - byte PRECISION = 8; - boolean[] bits = new boolean[8]; - byte position = 1; - - for (byte i = 7; i >= 0; i--) { - bits[i] = (number & position) != 0; - position = (byte)(position << 1); - } - - return bits; - } - - public static long toLong(@Nonnull boolean[] bits) { - byte PRECISION = 64; - if (bits.length != 64) { - throw new IllegalArgumentException("array must have length 64"); - } else { - long position = 1L; - long number = 0L; - - for (byte i = 63; i >= 0; i--) { - if (bits[i]) { - number += position; - } - - position <<= 1; - } - - return number; - } - } - - public static int toInt(@Nonnull boolean[] bits) { - byte PRECISION = 32; - if (bits.length != 32) { - throw new IllegalArgumentException("array must have length 32"); - } else { - int position = 1; - int number = 0; - - for (byte i = 31; i >= 0; i--) { - if (bits[i]) { - number += position; - } - - position <<= 1; - } - - return number; - } - } - - public static int toByte(@Nonnull boolean[] bits) { - byte PRECISION = 8; - if (bits.length != 8) { - throw new IllegalArgumentException("array must have length 8"); - } else { - byte position = 1; - byte number = 0; - - for (byte i = 7; i >= 0; i--) { - if (bits[i]) { - number += position; - } - - position = (byte)(position << 1); - } - - return number; - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/CoPrimeGenerator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/CoPrimeGenerator.java deleted file mode 100644 index 2fff977f..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/CoPrimeGenerator.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -import java.util.Random; -import java.util.stream.IntStream; -import javax.annotation.Nonnull; - -public class CoPrimeGenerator { - public static long[] generateCoPrimes(long seed, int bucketSize, int numberOfBuckets, long floor) { - if (bucketSize >= 1 && numberOfBuckets >= 1) { - Random rand = new Random(seed); - int[] primes = new int[bucketSize * numberOfBuckets]; - fillWithPrimes(primes); - int[][] buckets = new int[numberOfBuckets][bucketSize]; - long[] output = new long[numberOfBuckets]; - IntStream.range(0, output.length).forEach(ix -> output[ix] = 1L); - int indexOfBucket = 0; - int indexOfPrime = 0; - - for (int indexInsideBucket = 0; indexOfPrime < primes.length; indexOfPrime++) { - buckets[indexOfBucket][indexInsideBucket] = primes[indexOfPrime]; - if (indexOfBucket == numberOfBuckets - 1) { - indexInsideBucket++; - } - - indexOfBucket = (indexOfBucket + 1) % numberOfBuckets; - } - - for (int i = 0; i < numberOfBuckets; i++) { - while (output[i] < floor) { - output[i] *= buckets[i][rand.nextInt(bucketSize)]; - } - } - - return output; - } else { - throw new IllegalArgumentException("invalid sizes"); - } - } - - public static void fillWithPrimes(@Nonnull int[] bucket) { - int number = 2; - - for (int index = 0; index < bucket.length; number++) { - if (isPrime(number)) { - bucket[index] = number; - index++; - } - } - } - - public static boolean isPrime(int number) { - for (int i = 2; i < number; i++) { - if (number % i == 0) { - return false; - } - } - - return true; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Combiner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Combiner.java deleted file mode 100644 index d8bf34d1..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Combiner.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -import javax.annotation.Nonnull; - -public class Combiner { - private final double y; - private double value; - - public Combiner(double background, double y) { - this.value = background; - this.y = y; - } - - @Nonnull - public Combiner.Layer addLayer(double density) { - return new Combiner.Layer(this, density); - } - - public double getValue() { - return this.value; - } - - public static enum IntersectionPolicy { - MAX_POLICY, - MIN_POLICY; - } - - public class Layer { - @Nonnull - private final Combiner parent; - private double value; - private double floor; - private double ceiling; - private double paddingFloor; - private double paddingCeiling; - private Combiner.IntersectionPolicy intersectionPolicy; - private double intersectionSmoothingRange; - private boolean withLimitsCheck; - private boolean withPaddingCheck; - private boolean withIntersectionPolicyCheck; - private boolean isFinished = false; - - private Layer(@Nonnull Combiner combiner, double value) { - if (combiner == null) { - throw new NullPointerException(); - } else { - this.parent = combiner; - this.value = value; - } - } - - @Nonnull - public Combiner finishLayer() { - if (!this.withPaddingCheck || !this.withIntersectionPolicyCheck || !this.withLimitsCheck) { - throw new IllegalStateException("incomplete"); - } else if (this.isFinished) { - throw new IllegalStateException("method was already called"); - } else { - this.isFinished = true; - if (this.intersectionPolicy == Combiner.IntersectionPolicy.MAX_POLICY) { - this.ceiling = Calculator.smoothMax(this.intersectionSmoothingRange, this.floor, this.ceiling); - } else if (this.intersectionPolicy == Combiner.IntersectionPolicy.MIN_POLICY) { - this.floor = Calculator.smoothMin(this.intersectionSmoothingRange, this.floor, this.ceiling); - } else { - this.ceiling = this.floor; - } - - if (!(Combiner.this.y < this.floor) && !(Combiner.this.y >= this.ceiling)) { - double floorPaddingMultiplier; - if (this.paddingFloor == 0.0) { - floorPaddingMultiplier = 1.0; - } else { - floorPaddingMultiplier = (Combiner.this.y - this.floor) / this.paddingFloor; - floorPaddingMultiplier = Calculator.clamp(0.0, floorPaddingMultiplier, 1.0); - } - - double ceilingPaddingMultiplier; - if (this.paddingCeiling == 0.0) { - ceilingPaddingMultiplier = 1.0; - } else { - ceilingPaddingMultiplier = (this.ceiling - Combiner.this.y) / this.paddingCeiling; - ceilingPaddingMultiplier = Calculator.clamp(0.0, ceilingPaddingMultiplier, 1.0); - } - - double paddingMultiplier = Calculator.smoothMin(0.2, floorPaddingMultiplier, ceilingPaddingMultiplier); - this.value *= paddingMultiplier; - this.parent.value = this.parent.value + this.value; - return this.parent; - } else { - return this.parent; - } - } - } - - @Nonnull - public Combiner.Layer withLimits(double floor, double ceiling) { - this.withLimitsCheck = true; - this.floor = floor; - this.ceiling = ceiling; - return this; - } - - @Nonnull - public Combiner.Layer withPadding(double paddingFloor, double paddingCeiling) { - if (!(paddingFloor < 0.0) && !(paddingCeiling < 0.0)) { - this.withPaddingCheck = true; - this.paddingFloor = paddingFloor; - this.paddingCeiling = paddingCeiling; - return this; - } else { - throw new IllegalArgumentException("negative padding values"); - } - } - - @Nonnull - public Combiner.Layer withIntersectionPolicy(@Nonnull Combiner.IntersectionPolicy policy, double smoothRange) { - if (policy == null) { - throw new NullPointerException(); - } else { - this.withIntersectionPolicyCheck = true; - this.intersectionPolicy = policy; - this.intersectionSmoothingRange = smoothRange; - return this; - } - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/MultipliedIteration.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/MultipliedIteration.java deleted file mode 100644 index 54447809..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/MultipliedIteration.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -public class MultipliedIteration { - public static double calculateMultiplier(double startValue, double endValue, int numberOfIterations, double precision) { - if (startValue < endValue) { - throw new IllegalArgumentException("start smaller than end"); - } else if (numberOfIterations <= 0) { - throw new IllegalArgumentException("number of iterations must be greater than 0"); - } else if (precision <= 0.0) { - throw new IllegalArgumentException("precision must be greater than 0"); - } else { - double candidate = 0.0; - - for (int result = 0; candidate < 1.0; candidate += precision) { - result = calculateIterations(candidate, startValue, endValue); - if (result >= numberOfIterations) { - break; - } - } - - return Math.min(candidate, 0.99999); - } - } - - public static int calculateIterations(double multiplier, double startValue, double endValue) { - double currentSize = startValue; - - int iterations; - for (iterations = 0; currentSize > endValue; iterations++) { - currentSize *= multiplier; - } - - return iterations; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Probability.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Probability.java deleted file mode 100644 index 0bab9cd4..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Probability.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -import java.util.Random; - -public class Probability { - public static boolean of(double chance, long seed) { - Random rand = new Random(seed); - return rand.nextDouble() < chance; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/RegionGrid.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/RegionGrid.java deleted file mode 100644 index e33b5607..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/RegionGrid.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -public class RegionGrid { - private int regionSizeX; - private int regionSizeZ; - - public RegionGrid(int regionSizeX, int regionSizeZ) { - this.regionSizeX = regionSizeX; - this.regionSizeZ = regionSizeZ; - } - - public int regionMinX(int chunkX) { - return chunkX >= 0 ? chunkX / this.regionSizeX * this.regionSizeX : (chunkX - (this.regionSizeZ - 1)) / this.regionSizeX * this.regionSizeX; - } - - public int regionMinZ(int chunkZ) { - return chunkZ >= 0 ? chunkZ / this.regionSizeZ * this.regionSizeZ : (chunkZ - (this.regionSizeX - 1)) / this.regionSizeZ * this.regionSizeZ; - } - - public int regionMaxX(int chunkX) { - return this.regionMinX(chunkX) + this.regionSizeX; - } - - public int regionMaxZ(int chunkZ) { - return this.regionMinZ(chunkZ) + this.regionSizeZ; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/SeedGenerator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/SeedGenerator.java deleted file mode 100644 index cc91da56..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/SeedGenerator.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -import java.util.Arrays; -import javax.annotation.Nonnull; - -public class SeedGenerator { - @Nonnull - private final long[] coPrimes; - private static final long FLOOR = 10000000L; - - public SeedGenerator(long seed) { - this.coPrimes = CoPrimeGenerator.generateCoPrimes(seed, 100, 7, 10000000L); - } - - public long seedAt(long x, long y, long z, long w, long k, long t) { - return (x * this.coPrimes[0] + y * this.coPrimes[1] + z * this.coPrimes[2] + w * this.coPrimes[3] + k * this.coPrimes[4] + t * this.coPrimes[5]) - % this.coPrimes[6]; - } - - public long seedAt(long x, long y, long z, long w, long k) { - return (x * this.coPrimes[0] + y * this.coPrimes[1] + z * this.coPrimes[2] + w * this.coPrimes[3] + k * this.coPrimes[4]) % this.coPrimes[6]; - } - - public long seedAt(long x, long y, long z, long w) { - return (x * this.coPrimes[0] + y * this.coPrimes[1] + z * this.coPrimes[2] + w * this.coPrimes[3]) % this.coPrimes[6]; - } - - public long seedAt(long x, long y, long z) { - return (x * this.coPrimes[0] + y * this.coPrimes[1] + z * this.coPrimes[2]) % this.coPrimes[6]; - } - - public long seedAt(long x, long y) { - return (x * this.coPrimes[0] + y * this.coPrimes[1]) % this.coPrimes[6]; - } - - public long seedAt(double xd, double yd, double zd, double wd, double kd, double td, double resolution) { - int x = (int)(xd * resolution); - int y = (int)(yd * resolution); - int z = (int)(zd * resolution); - int w = (int)(wd * resolution); - int k = (int)(kd * resolution); - int t = (int)(td * resolution); - return (x * this.coPrimes[0] + y * this.coPrimes[1] + z * this.coPrimes[2] + w * this.coPrimes[3] + k * this.coPrimes[4] + t * this.coPrimes[5]) - % this.coPrimes[6]; - } - - public long seedAt(double xd, double yd, double zd, double wd, double kd, double resolution) { - int x = (int)(xd * resolution); - int y = (int)(yd * resolution); - int z = (int)(zd * resolution); - int w = (int)(wd * resolution); - int k = (int)(kd * resolution); - return (x * this.coPrimes[0] + y * this.coPrimes[1] + z * this.coPrimes[2] + w * this.coPrimes[3] + k * this.coPrimes[4]) % this.coPrimes[6]; - } - - public long seedAt(double xd, double yd, double zd, double wd, double resolution) { - int x = (int)(xd * resolution); - int y = (int)(yd * resolution); - int z = (int)(zd * resolution); - int w = (int)(wd * resolution); - return (x * this.coPrimes[0] + y * this.coPrimes[1] + z * this.coPrimes[2] + w * this.coPrimes[3]) % this.coPrimes[6]; - } - - public long seedAt(double xd, double yd, double zd, double resolution) { - int x = (int)(xd * resolution); - int y = (int)(yd * resolution); - int z = (int)(zd * resolution); - return (x * this.coPrimes[0] + y * this.coPrimes[1] + z * this.coPrimes[2]) % this.coPrimes[6]; - } - - public long seedAt(double xd, double yd, double resolution) { - int x = (int)(xd * resolution); - int y = (int)(yd * resolution); - return (x * this.coPrimes[0] + y * this.coPrimes[1]) % this.coPrimes[6]; - } - - @Nonnull - @Override - public String toString() { - return "SeedGenerator{coPrimes=" + Arrays.toString(this.coPrimes) + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Splitter.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Splitter.java deleted file mode 100644 index ac5a4e9f..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Splitter.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -import javax.annotation.Nonnull; - -public class Splitter { - @Nonnull - public static Splitter.Range[] split(@Nonnull Splitter.Range range, int pieces) { - if (pieces < 0) { - throw new IllegalArgumentException("negative number of pieces"); - } else { - int size = range.max - range.min; - int pieceSize = size / pieces; - if (size % pieces > 0) { - pieceSize++; - } - - Splitter.Range[] output = new Splitter.Range[pieces]; - - for (int i = 0; i < output.length; i++) { - int min = Math.min(i * pieceSize + range.min, range.max); - int max = Math.min(min + pieceSize, range.max); - output[i] = new Splitter.Range(min, max); - } - - return output; - } - } - - @Nonnull - public static Splitter.Area[] split(@Nonnull Splitter.Area area, int pieces) { - if (pieces < 1) { - throw new IllegalArgumentException("negative number of pieces"); - } else if (pieces == 1) { - return new Splitter.Area[]{area}; - } else { - int sizeX = area.maxX - area.minX; - int sizeZ = area.maxZ - area.minZ; - if (pieces > sizeX) { - pieces = sizeX; - } - - Splitter.Area[] output = new Splitter.Area[pieces]; - if (pieces % 3 == 0) { - Splitter.Range[] rangesX = split(new Splitter.Range(area.minX, area.maxX), 3); - Splitter.Range[] rangesZ = split(new Splitter.Range(area.minZ, area.maxZ), pieces / 3); - int o = 0; - - for (Splitter.Range x : rangesX) { - for (Splitter.Range range : rangesZ) { - output[o++] = new Splitter.Area(x.min, range.min, x.max, range.max); - } - } - } else if (pieces % 2 == 0) { - Splitter.Range[] rangesX = split(new Splitter.Range(area.minX, area.maxX), 2); - Splitter.Range[] rangesZ = split(new Splitter.Range(area.minZ, area.maxZ), pieces / 2); - int o = 0; - - for (Splitter.Range x : rangesX) { - for (Splitter.Range range : rangesZ) { - output[o++] = new Splitter.Area(x.min, range.min, x.max, range.max); - } - } - } else { - Splitter.Range[] ranges = split(new Splitter.Range(area.minX, area.maxX), pieces); - - for (int i = 0; i < ranges.length; i++) { - output[i] = new Splitter.Area(ranges[i].min, area.minZ, ranges[i].max, area.maxZ); - } - } - - return output; - } - } - - @Nonnull - public static Splitter.Area[] splitX(@Nonnull Splitter.Area area, int pieces) { - if (pieces < 1) { - throw new IllegalArgumentException("negative number of pieces"); - } else if (pieces == 1) { - return new Splitter.Area[]{area}; - } else { - int sizeX = area.maxX - area.minX; - int sizeZ = area.maxZ - area.minZ; - if (pieces > sizeX) { - pieces = sizeX; - } - - Splitter.Area[] output = new Splitter.Area[pieces]; - Splitter.Range[] ranges = split(new Splitter.Range(area.minX, area.maxX), pieces); - - for (int i = 0; i < ranges.length; i++) { - output[i] = new Splitter.Area(ranges[i].min, area.minZ, ranges[i].max, area.maxZ); - } - - return output; - } - } - - public static class Area { - public final int minX; - public final int minZ; - public final int maxX; - public final int maxZ; - - public Area(int minX, int minZ, int maxX, int maxZ) { - if (maxX >= minX && maxZ >= minZ) { - this.minX = minX; - this.minZ = minZ; - this.maxX = maxX; - this.maxZ = maxZ; - } else { - throw new IllegalArgumentException("max smaller than min"); - } - } - - @Nonnull - @Override - public String toString() { - return "Area{minX=" + this.minX + ", minZ=" + this.minZ + ", maxX=" + this.maxX + ", maxZ=" + this.maxZ + "}"; - } - } - - public static class Range { - public final int min; - public final int max; - - public Range(int min, int max) { - if (max < min) { - throw new IllegalArgumentException("max smaller than min"); - } else { - this.min = min; - this.max = max; - } - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Stepinizer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Stepinizer.java deleted file mode 100644 index 06612d00..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Stepinizer.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; - -import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; -import java.util.function.Function; -import javax.annotation.Nonnull; - -public class Stepinizer implements Function, Double2DoubleFunction { - private double stepSize; - private double stepSizeHalf; - private double slope; - private double topSmooth; - private double bottomSmooth; - - public Stepinizer() { - this.setStep(1.0); - this.setEdgeSlope(1.0); - this.setSmooth(1.0, 1.0); - } - - @Nonnull - public Stepinizer setSmooth(double top, double bottom) { - if (!(top <= 0.0) && !(bottom <= 0.0)) { - this.topSmooth = top; - this.bottomSmooth = bottom; - return this; - } else { - throw new IllegalArgumentException("invalid values provided"); - } - } - - @Nonnull - public Stepinizer setEdgeSlope(double slope) { - if (slope < 0.0) { - throw new IllegalArgumentException("negative slope"); - } else { - this.slope = slope; - return this; - } - } - - @Nonnull - public Stepinizer setStep(double size) { - if (size < 0.0) { - throw new IllegalArgumentException("negative size"); - } else { - this.stepSize = size; - this.stepSizeHalf = size / 2.0; - return this; - } - } - - public double apply(double x) { - return this.get(x); - } - - @Override - public double get(double x) { - double polarity = this.polarity(x); - double steepness = this.steepness(polarity); - double bottomStep = this.bottomStep(x); - double topStep = this.topStep(x); - double result; - if (polarity < 0.0) { - result = Calculator.smoothMax(this.bottomSmooth, steepness, -1.0); - } else { - result = Calculator.smoothMin(this.topSmooth, steepness, 1.0); - } - - return Normalizer.normalize(-1.0, 1.0, bottomStep, topStep, result); - } - - private double closestStep(double x) { - double remainder = x % this.stepSize; - return remainder < this.stepSizeHalf ? x - remainder : x - remainder + this.stepSize; - } - - private double topStep(double x) { - return x - x % this.stepSize + this.stepSize; - } - - private double bottomStep(double x) { - return x - x % this.stepSize; - } - - private double polarity(double x) { - double midPoint = this.bottomStep(x) + this.stepSizeHalf; - return (x - midPoint) / this.stepSizeHalf; - } - - private double steepness(double x) { - return this.slope * x; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/MaskShader.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/MaskShader.java deleted file mode 100644 index 27fb8cf3..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/MaskShader.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.shaders; - -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; -import java.util.function.Predicate; -import javax.annotation.Nonnull; - -public class MaskShader implements Shader { - private final Shader childShader; - private final Predicate mask; - private SeedGenerator seedGenerator; - - private MaskShader(Predicate mask, Shader childShader, long seed) { - this.mask = mask; - this.childShader = childShader; - this.seedGenerator = new SeedGenerator(seed); - } - - @Nonnull - public static MaskShader.Builder builder(@Nonnull Class dataType) { - return new MaskShader.Builder<>(); - } - - @Override - public T shade(T current, long seed) { - return !this.mask.test(current) ? current : this.childShader.shade(current, seed); - } - - @Override - public T shade(T current, long seedA, long seedB) { - return this.shade(current, 0L); - } - - @Override - public T shade(T current, long seedA, long seedB, long seedC) { - return this.shade(current, 0L); - } - - @Nonnull - @Override - public String toString() { - return "MaskShader{childShader=" + this.childShader + ", mask=" + this.mask + ", seedGenerator=" + this.seedGenerator + "}"; - } - - public static class Builder { - private Shader childShader; - private Predicate mask; - private long seed = System.nanoTime(); - - private Builder() { - } - - @Nonnull - public MaskShader build() { - if (this.childShader != null && this.mask != null) { - return new MaskShader<>(this.mask, this.childShader, this.seed); - } else { - throw new IllegalStateException("incomplete builder"); - } - } - - @Nonnull - public MaskShader.Builder withSeed(long seed) { - this.seed = seed; - return this; - } - - @Nonnull - public MaskShader.Builder withMask(@Nonnull Predicate mask) { - this.mask = mask; - return this; - } - - @Nonnull - public MaskShader.Builder withChildShader(@Nonnull Shader shader) { - this.childShader = shader; - return this; - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/RelationalShader.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/RelationalShader.java deleted file mode 100644 index aa91b7f0..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/RelationalShader.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.shaders; - -import java.util.HashMap; -import java.util.Map; -import javax.annotation.Nonnull; - -public class RelationalShader implements Shader { - @Nonnull - private final Map> relations; - @Nonnull - private final Shader onMissingKey; - - public RelationalShader(@Nonnull Shader onMissingKey) { - this.onMissingKey = onMissingKey; - this.relations = new HashMap<>(1); - } - - @Nonnull - public RelationalShader addRelation(@Nonnull T key, @Nonnull Shader value) { - this.relations.put(key, value); - return this; - } - - @Override - public T shade(T current, long seed) { - return !this.relations.containsKey(current) ? this.onMissingKey.shade(current, seed) : this.relations.get(current).shade(current, seed); - } - - @Override - public T shade(T current, long seedA, long seedB) { - return !this.relations.containsKey(current) ? this.onMissingKey.shade(current, seedA, seedB) : this.relations.get(current).shade(current, seedA, seedB); - } - - @Override - public T shade(T current, long seedA, long seedB, long seedC) { - return !this.relations.containsKey(current) - ? this.onMissingKey.shade(current, seedA, seedB, seedC) - : this.relations.get(current).shade(current, seedA, seedB, seedC); - } - - @Nonnull - @Override - public String toString() { - return "RelationalShader{relations=" + this.relations + ", onMissingKey=" + this.onMissingKey + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/Shader.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/Shader.java deleted file mode 100644 index aca7ab55..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/Shader.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.shaders; - -public interface Shader { - T shade(T var1, long var2); - - T shade(T var1, long var2, long var4); - - T shade(T var1, long var2, long var4, long var6); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/SimpleShader.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/SimpleShader.java deleted file mode 100644 index aaa1d26c..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/SimpleShader.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.shaders; - -import javax.annotation.Nonnull; - -public class SimpleShader implements Shader { - @Nonnull - private final T value; - - private SimpleShader(@Nonnull T value) { - this.value = value; - } - - @Nonnull - public static SimpleShader of(@Nonnull T value) { - return new SimpleShader<>(value); - } - - @Nonnull - @Override - public T shade(T current, long seed) { - return this.value; - } - - @Nonnull - @Override - public T shade(T current, long seedA, long seedB) { - return this.value; - } - - @Nonnull - @Override - public T shade(T current, long seedA, long seedB, long seedC) { - return this.value; - } - - @Nonnull - @Override - public String toString() { - return "SimpleShader{value=" + this.value + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/WeighedShader.java b/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/WeighedShader.java deleted file mode 100644 index cdfd1903..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/shaders/WeighedShader.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.shaders; - -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; -import java.util.Random; -import javax.annotation.Nonnull; - -public class WeighedShader implements Shader { - @Nonnull - private final WeightedMap> childrenWeightedMap = new WeightedMap<>(1); - private SeedGenerator seedGenerator = new SeedGenerator(System.nanoTime()); - - public WeighedShader(@Nonnull Shader initialChild, double weight) { - this.add(initialChild, weight); - } - - @Nonnull - public WeighedShader add(@Nonnull Shader child, double weight) { - if (weight <= 0.0) { - throw new IllegalArgumentException("invalid weight"); - } else { - this.childrenWeightedMap.add(child, weight); - return this; - } - } - - @Nonnull - public WeighedShader setSeed(long seed) { - this.seedGenerator = new SeedGenerator(seed); - return this; - } - - @Override - public T shade(T current, long seed) { - Random r = new Random(seed); - return this.childrenWeightedMap.pick(r).shade(current, seed); - } - - @Override - public T shade(T current, long seedA, long seedB) { - return this.shade(current, this.seedGenerator.seedAt(seedA, seedB)); - } - - @Override - public T shade(T current, long seedA, long seedB, long seedC) { - return this.shade(current, this.seedGenerator.seedAt(seedA, seedB, seedC)); - } - - @Nonnull - @Override - public String toString() { - return "WeighedShader{childrenWeighedMap=" + this.childrenWeightedMap + ", seedGenerator=" + this.seedGenerator + "}"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/BackwardIntIterator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/BackwardIntIterator.java deleted file mode 100644 index b03c0a20..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/BackwardIntIterator.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.iterators; - -import it.unimi.dsi.fastutil.ints.IntIterator; -import java.util.Iterator; -import javax.annotation.Nonnull; - -public class BackwardIntIterator implements IntIterator, Iterator { - private int min; - private int current; - - public BackwardIntIterator(int min, int maxExclusive) { - if (min > maxExclusive) { - throw new IllegalArgumentException("Start greater than end."); - } else { - this.min = min; - this.current = maxExclusive; - } - } - - private BackwardIntIterator() { - } - - @Override - public boolean hasNext() { - return this.current > this.min; - } - - @Override - public int nextInt() { - return --this.current; - } - - @Nonnull - @Override - public Integer next() { - return --this.current; - } - - @Nonnull - public Integer getCurrent() { - return this.current; - } - - @Nonnull - public BackwardIntIterator clone() { - BackwardIntIterator clone = new BackwardIntIterator(); - clone.current = this.current; - clone.min = this.min; - return clone; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/ForwardIntIterator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/ForwardIntIterator.java deleted file mode 100644 index c27efe5f..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/ForwardIntIterator.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.iterators; - -import it.unimi.dsi.fastutil.ints.IntIterator; -import java.util.Iterator; -import javax.annotation.Nonnull; - -public class ForwardIntIterator implements IntIterator, Iterator { - private int max; - private int current; - - public ForwardIntIterator(int min, int maxExclusive) { - if (min > maxExclusive) { - throw new IllegalArgumentException("Start greater than end."); - } else { - this.max = maxExclusive - 1; - this.current = min - 1; - } - } - - private ForwardIntIterator() { - } - - @Override - public boolean hasNext() { - return this.current < this.max; - } - - @Override - public int nextInt() { - return ++this.current; - } - - @Nonnull - @Override - public Integer next() { - return ++this.current; - } - - @Nonnull - public Integer getCurrent() { - return this.current; - } - - @Nonnull - public ForwardIntIterator clone() { - ForwardIntIterator clone = new ForwardIntIterator(); - clone.current = this.current; - clone.max = this.max; - return clone; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/IntIterators.java b/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/IntIterators.java deleted file mode 100644 index 478769b6..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/iterators/IntIterators.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.iterators; - -import it.unimi.dsi.fastutil.ints.IntIterator; -import javax.annotation.Nonnull; - -public class IntIterators { - @Nonnull - public static IntIterator range(int start, int end) { - return (IntIterator)(start <= end ? new ForwardIntIterator(start, end) : new BackwardIntIterator(end, start)); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/material/Material.java b/src/com/hypixel/hytale/builtin/hytalegenerator/material/Material.java index 0611c4c8..ec3bbfdd 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/material/Material.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/material/Material.java @@ -45,8 +45,7 @@ public final class Material { } public static int hashCode(@Nonnull SolidMaterial solid, @Nonnull FluidMaterial fluid) { - int result = solid.hashCode(); - return 31 * result + fluid.hashCode(); + return Objects.hash(solid.hashCode(), fluid.hashCode()); } public static int hashMaterialIds(@Nonnull SolidMaterial solid, @Nonnull FluidMaterial fluid) { @@ -70,7 +69,14 @@ public final class Material { } private class Hash { - int value = 0; - boolean isCalculated = false; + int value; + boolean isCalculated; + + private Hash() { + Objects.requireNonNull(Material.this); + super(); + this.value = 0; + this.isCalculated = false; + } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/material/MaterialCache.java b/src/com/hypixel/hytale/builtin/hytalegenerator/material/MaterialCache.java index d5036d2f..747ed14d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/material/MaterialCache.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/material/MaterialCache.java @@ -7,6 +7,7 @@ import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.prefab.PrefabRotation; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockRotationUtil; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; @@ -63,6 +64,19 @@ public class MaterialCache { } } + @Nonnull + public Material getMaterialRotated(@Nonnull Material material, @Nonnull RotationTuple rotation) { + SolidMaterial solid = material.solid(); + RotationTuple newMaterialRotation = RotationTuple.compose(rotation, RotationTuple.get(solid.rotation)); + int rotationIndex = newMaterialRotation.index(); + int rotatedFiller = BlockRotationUtil.getRotatedFiller(solid.filler, rotation); + SolidMaterial rotatedSolid = this.getSolidMaterial(solid.blockId, solid.support, rotationIndex, rotatedFiller, solid.holder); + + assert rotatedSolid != null; + + return rotatedSolid == null ? material : this.getMaterial(rotatedSolid, material.fluid()); + } + @Nullable public FluidMaterial getFluidMaterial(@Nonnull String fluidString) { int fluidId = 0; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/MaterialProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/MaterialProvider.java index 2be21f1b..c8f74952 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/MaterialProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/MaterialProvider.java @@ -1,19 +1,19 @@ package com.hypixel.hytale.builtin.hytalegenerator.materialproviders; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.TerrainDensityProvider; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.TerrainDensityProvider; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public abstract class MaterialProvider { - @Nullable - public abstract V getVoxelTypeAt(@Nonnull MaterialProvider.Context var1); - @Nonnull public static MaterialProvider noMaterialProvider() { return new ConstantMaterialProvider<>(null); } + @Nullable + public abstract V getVoxelTypeAt(@Nonnull MaterialProvider.Context var1); + public static class Context { @Nonnull public Vector3i position; @@ -26,6 +26,17 @@ public abstract class MaterialProvider { public TerrainDensityProvider terrainDensityProvider; public double distanceToBiomeEdge; + public Context() { + this.position = new Vector3i(); + this.density = 0.0; + this.depthIntoFloor = 0; + this.depthIntoCeiling = 0; + this.spaceAboveFloor = 0; + this.spaceBelowCeiling = 0; + this.terrainDensityProvider = p -> 0.0; + this.distanceToBiomeEdge = Double.MAX_VALUE; + } + public Context( @Nonnull Vector3i position, double density, diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/WeightedMaterialProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/WeightedMaterialProvider.java index 2205cdf8..551472a7 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/WeightedMaterialProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/WeightedMaterialProvider.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.materialproviders; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.math.util.FastRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -11,22 +11,25 @@ public class WeightedMaterialProvider extends MaterialProvider { @Nonnull private final WeightedMap> weightedMap; @Nonnull - private final SeedGenerator seedGenerator; + private final RngField rngField; + @Nonnull + private final FastRandom random; private final double noneProbability; public WeightedMaterialProvider(@Nonnull WeightedMap> weightedMap, @Nonnull SeedBox seedBox, double noneProbability) { this.weightedMap = weightedMap; - this.seedGenerator = new SeedGenerator(seedBox.createSupplier().get().intValue()); + this.rngField = new RngField(seedBox.createSupplier().get()); this.noneProbability = noneProbability; + this.random = new FastRandom(); } @Nullable @Override public V getVoxelTypeAt(@Nonnull MaterialProvider.Context context) { - long seed = this.seedGenerator.seedAt((long)context.position.x, (long)context.position.y, (long)context.position.z); - FastRandom random = new FastRandom(seed); - if (this.weightedMap.size() != 0 && !(random.nextDouble() < this.noneProbability)) { - MaterialProvider pick = this.weightedMap.pick(random); + int localSeed = this.rngField.get(context.position.x, context.position.y, context.position.z); + this.random.setSeed(localSeed); + if (this.weightedMap.size() != 0 && !(this.random.nextDouble() < this.noneProbability)) { + MaterialProvider pick = this.weightedMap.pick(this.random); return pick.getVoxelTypeAt(context); } else { return null; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/NoiseThickness.java b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/NoiseThickness.java index dd806b37..1ad24541 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/NoiseThickness.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/NoiseThickness.java @@ -24,7 +24,7 @@ public class NoiseThickness extends SpaceAndDepthMaterialProvider.Layer { public int getThicknessAt( int x, int y, int z, int depthIntoFloor, int depthIntoCeiling, int spaceAboveFloor, int spaceBelowCeiling, double distanceToBiomeEdge ) { - this.rDensityContext.position.assign(x, y, z); + this.rDensityContext.position.set(x, y, z); this.rDensityContext.distanceToBiomeEdge = distanceToBiomeEdge; return (int)this.density.process(this.rDensityContext); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/RangedThicknessLayer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/RangedThicknessLayer.java index 2b44fb4e..c7df1201 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/RangedThicknessLayer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/RangedThicknessLayer.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.materialproviders.spaceanddepth.layers; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.spaceanddepth.SpaceAndDepthMaterialProvider; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.math.util.FastRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -13,7 +13,7 @@ public class RangedThicknessLayer extends SpaceAndDepthMaterialProvider.Layer private final int max; private final int delta; @Nonnull - private final SeedGenerator seedGenerator; + private final RngField rngField; @Nullable private final MaterialProvider materialProvider; @@ -24,7 +24,7 @@ public class RangedThicknessLayer extends SpaceAndDepthMaterialProvider.Layer if (this.delta < 0) { throw new IllegalArgumentException("min greater than max"); } else { - this.seedGenerator = new SeedGenerator(seedBox.createSupplier().get().intValue()); + this.rngField = new RngField(seedBox.createSupplier().get()); this.materialProvider = materialProvider; } } @@ -36,7 +36,7 @@ public class RangedThicknessLayer extends SpaceAndDepthMaterialProvider.Layer if (this.delta <= 0) { return this.min; } else { - FastRandom random = new FastRandom(this.seedGenerator.seedAt(x, z)); + FastRandom random = new FastRandom(this.rngField.get(x, z)); return random.nextInt(this.delta + 1) + this.min; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/WeightedThicknessLayer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/WeightedThicknessLayer.java index 939e98bb..f1f04a3e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/WeightedThicknessLayer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/materialproviders/spaceanddepth/layers/WeightedThicknessLayer.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.builtin.hytalegenerator.materialproviders.spaceanddepth.layers; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.spaceanddepth.SpaceAndDepthMaterialProvider; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; import com.hypixel.hytale.math.util.FastRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -13,12 +13,12 @@ public class WeightedThicknessLayer extends SpaceAndDepthMaterialProvider.Lay @Nonnull private final WeightedMap thicknessPool; @Nonnull - private final SeedGenerator seedGenerator; + private final RngField rngField; @Nullable private final MaterialProvider materialProvider; public WeightedThicknessLayer(@Nonnull WeightedMap thicknessPool, @Nullable MaterialProvider materialProvider, @Nonnull SeedBox seedBox) { - this.seedGenerator = new SeedGenerator(seedBox.createSupplier().get().intValue()); + this.rngField = new RngField(seedBox.createSupplier().get()); this.materialProvider = materialProvider; this.thicknessPool = thicknessPool; } @@ -30,7 +30,7 @@ public class WeightedThicknessLayer extends SpaceAndDepthMaterialProvider.Lay if (this.thicknessPool.size() == 0) { return 0; } else { - FastRandom random = new FastRandom(this.seedGenerator.seedAt(x, z)); + FastRandom random = new FastRandom(this.rngField.get(x, z)); return this.thicknessPool.pick(random); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Calculator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/math/Calculator.java similarity index 93% rename from src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Calculator.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/math/Calculator.java index 91008f87..01ff48cd 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Calculator.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/math/Calculator.java @@ -1,8 +1,8 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; +package com.hypixel.hytale.builtin.hytalegenerator.math; -import com.hypixel.hytale.math.vector.Vector3d; import java.util.Objects; import javax.annotation.Nonnull; +import org.joml.Vector3dc; public class Calculator { public static int toIntFloored(double d) { @@ -104,8 +104,8 @@ public class Calculator { return Math.sqrt(Math.pow(x2 - x1, 2.0) + Math.pow(y2 - y1, 2.0) + Math.pow(z2 - z1, 2.0)); } - public static double distance(@Nonnull Vector3d a, @Nonnull Vector3d b) { - return Math.sqrt(Math.pow(b.x - a.x, 2.0) + Math.pow(b.y - a.y, 2.0) + Math.pow(b.z - a.z, 2.0)); + public static double distance(@Nonnull Vector3dc a, @Nonnull Vector3dc b) { + return Math.sqrt(Math.pow(b.x() - a.x(), 2.0) + Math.pow(b.y() - a.y(), 2.0) + Math.pow(b.z() - a.z(), 2.0)); } public static double distance(double x1, double y1, double x2, double y2) { @@ -216,6 +216,6 @@ public class Calculator { } public static int ceil(int value, int gridSize) { - return value >= 0 ? (value + gridSize - 1) / gridSize * gridSize : value / gridSize * gridSize; + return value >= 0 ? (value + gridSize) / gridSize * gridSize : value / gridSize * gridSize; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/InterpolatedCurve.java b/src/com/hypixel/hytale/builtin/hytalegenerator/math/InterpolatedCurve.java similarity index 96% rename from src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/InterpolatedCurve.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/math/InterpolatedCurve.java index a6e74c4b..22fe774d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/InterpolatedCurve.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/math/InterpolatedCurve.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; +package com.hypixel.hytale.builtin.hytalegenerator.math; import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Interpolation.java b/src/com/hypixel/hytale/builtin/hytalegenerator/math/Interpolation.java similarity index 80% rename from src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Interpolation.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/math/Interpolation.java index 7ba5bdfa..e12fd194 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Interpolation.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/math/Interpolation.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; +package com.hypixel.hytale.builtin.hytalegenerator.math; public class Interpolation { public static double linear(double value0, double value1, double weight) { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/NodeFunction.java b/src/com/hypixel/hytale/builtin/hytalegenerator/math/NodeFunction.java similarity index 97% rename from src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/NodeFunction.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/math/NodeFunction.java index cb2e6e01..0d55fd3a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/NodeFunction.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/math/NodeFunction.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; +package com.hypixel.hytale.builtin.hytalegenerator.math; import com.hypixel.hytale.builtin.hytalegenerator.ArrayUtil; import com.hypixel.hytale.builtin.hytalegenerator.delimiters.RangeDouble; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Normalizer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/math/Normalizer.java similarity index 88% rename from src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Normalizer.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/math/Normalizer.java index 78d64d19..958396e3 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/math/Normalizer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/math/Normalizer.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.math; +package com.hypixel.hytale.builtin.hytalegenerator.math; public class Normalizer { public static double normalizeNoise(double input) { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NBuffer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NBuffer.java deleted file mode 100644 index 0a45ffc6..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/bufferbundle/buffers/NBuffer.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers; - -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument; - -public abstract class NBuffer implements MemInstrument { -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NBiomeStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NBiomeStage.java deleted file mode 100644 index 26959713..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NBiomeStage.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.BiCarta; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NCountedPixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NPixelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; -import java.util.List; -import java.util.Map; -import javax.annotation.Nonnull; - -public class NBiomeStage implements NStage { - @Nonnull - public static final Class bufferClass = NCountedPixelBuffer.class; - @Nonnull - public static final Class biomeClass = Integer.class; - @Nonnull - private final NParametrizedBufferType biomeOutputBufferType; - @Nonnull - private final String stageName; - @Nonnull - private final WorkerIndexer.Data worldStructure_workerData; - - public NBiomeStage( - @Nonnull String stageName, @Nonnull NParametrizedBufferType biomeOutputBufferType, @Nonnull WorkerIndexer.Data worldStructure_workerData - ) { - this.stageName = stageName; - this.biomeOutputBufferType = biomeOutputBufferType; - this.worldStructure_workerData = worldStructure_workerData; - } - - @Override - public void run(@Nonnull NStage.Context context) { - NBufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeOutputBufferType); - NPixelBufferView biomeSpace = new NPixelBufferView<>(biomeAccess, biomeClass); - BiCarta biomeMap = this.worldStructure_workerData.get(context.workerId).getBiomeMap(); - - for (int x = biomeSpace.minX(); x < biomeSpace.maxX(); x++) { - for (int z = biomeSpace.minZ(); z < biomeSpace.maxZ(); z++) { - Integer biomeId = biomeMap.apply(x, z, context.workerId); - biomeSpace.set(biomeId, x, 0, z); - } - } - } - - @Nonnull - @Override - public Map getInputTypesAndBounds_bufferGrid() { - return Map.of(); - } - - @Nonnull - @Override - public List getOutputTypes() { - return List.of(this.biomeOutputBufferType); - } - - @Nonnull - @Override - public String getName() { - return this.stageName; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NPropStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NPropStage.java deleted file mode 100644 index bfb28f23..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NPropStage.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; - -import com.hypixel.hytale.builtin.hytalegenerator.PropField; -import com.hypixel.hytale.builtin.hytalegenerator.Registry; -import com.hypixel.hytale.builtin.hytalegenerator.biome.Biome; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NCountedPixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NEntityBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NSimplePixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NEntityBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NPixelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NVoxelBufferView; -import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; -import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.props.ScanResult; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.function.Consumer; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class NPropStage implements NStage { - public static final double DEFAULT_BACKGROUND_DENSITY = 0.0; - @Nonnull - public static final Class biomeBufferClass = NCountedPixelBuffer.class; - @Nonnull - public static final Class biomeClass = Integer.class; - @Nonnull - public static final Class biomeDistanceBufferClass = NSimplePixelBuffer.class; - @Nonnull - public static final Class biomeDistanceClass = NBiomeDistanceStage.BiomeDistanceEntries.class; - @Nonnull - public static final Class materialBufferClass = NVoxelBuffer.class; - @Nonnull - public static final Class materialClass = Material.class; - @Nonnull - public static final Class entityBufferClass = NEntityBuffer.class; - @Nonnull - private final NParametrizedBufferType biomeInputBufferType; - @Nonnull - private final NParametrizedBufferType biomeDistanceInputBufferType; - @Nonnull - private final NParametrizedBufferType materialInputBufferType; - @Nullable - private final NBufferType entityInputBufferType; - @Nonnull - private final NParametrizedBufferType materialOutputBufferType; - @Nonnull - private final NBufferType entityOutputBufferType; - @Nonnull - private final Bounds3i inputBounds_bufferGrid; - @Nonnull - private final Bounds3i inputBounds_voxelGrid; - @Nonnull - private final String stageName; - @Nonnull - private final MaterialCache materialCache; - @Nonnull - private final WorkerIndexer.Data worldStructure_workerData; - private final int runtimeIndex; - - public NPropStage( - @Nonnull String stageName, - @Nonnull NParametrizedBufferType biomeInputBufferType, - @Nonnull NParametrizedBufferType biomeDistanceInputBufferType, - @Nonnull NParametrizedBufferType materialInputBufferType, - @Nullable NBufferType entityInputBufferType, - @Nonnull NParametrizedBufferType materialOutputBufferType, - @Nonnull NBufferType entityOutputBufferType, - @Nonnull MaterialCache materialCache, - @Nonnull WorkerIndexer.Data worldStructure_workerData, - int runtimeIndex - ) { - assert biomeInputBufferType.isValidType(biomeBufferClass, biomeClass); - - assert biomeDistanceInputBufferType.isValidType(biomeDistanceBufferClass, biomeDistanceClass); - - assert materialInputBufferType.isValidType(materialBufferClass, materialClass); - - assert entityInputBufferType == null || entityInputBufferType.isValidType(entityBufferClass); - - assert materialOutputBufferType.isValidType(materialBufferClass, materialClass); - - assert entityOutputBufferType.isValidType(entityBufferClass); - - this.biomeInputBufferType = biomeInputBufferType; - this.biomeDistanceInputBufferType = biomeDistanceInputBufferType; - this.materialInputBufferType = materialInputBufferType; - this.entityInputBufferType = entityInputBufferType; - this.materialOutputBufferType = materialOutputBufferType; - this.entityOutputBufferType = entityOutputBufferType; - this.worldStructure_workerData = worldStructure_workerData; - this.stageName = stageName; - this.materialCache = materialCache; - this.runtimeIndex = runtimeIndex; - List allBiomes = new ArrayList<>(); - this.worldStructure_workerData - .forEach((workerId, worldStructure) -> worldStructure.getBiomeRegistry().forEach((biomeId, biomex) -> allBiomes.add(biomex))); - this.inputBounds_voxelGrid = new Bounds3i(); - Vector3i range = new Vector3i(); - - for (Biome biome : allBiomes) { - for (PropField propField : biome.getPropFields()) { - if (propField.getRuntime() == this.runtimeIndex) { - for (Prop prop : propField.getPropDistribution().getAllPossibleProps()) { - Vector3i readRange_voxelGrid = prop.getContextDependency().getReadRange(); - Vector3i writeRange_voxelGrid = prop.getContextDependency().getWriteRange(); - range.x = readRange_voxelGrid.x + writeRange_voxelGrid.x; - range.y = readRange_voxelGrid.y + writeRange_voxelGrid.y; - range.z = readRange_voxelGrid.z + writeRange_voxelGrid.z; - this.inputBounds_voxelGrid.encompass(range.clone().add(Vector3i.ALL_ONES)); - range.scale(-1); - this.inputBounds_voxelGrid.encompass(range); - } - } - } - } - - this.inputBounds_voxelGrid.min.y = 0; - this.inputBounds_voxelGrid.max.y = 320; - this.inputBounds_bufferGrid = GridUtils.createBufferBoundsInclusive_fromVoxelBounds(this.inputBounds_voxelGrid); - GridUtils.setBoundsYToWorldHeight_bufferGrid(this.inputBounds_bufferGrid); - } - - @Override - public void run(@Nonnull NStage.Context context) { - NBufferBundle.Access.View biomeAccess = context.bufferAccess.get(this.biomeInputBufferType); - NPixelBufferView biomeInputSpace = new NPixelBufferView<>(biomeAccess, biomeClass); - NBufferBundle.Access.View biomeDistanceAccess = context.bufferAccess.get(this.biomeDistanceInputBufferType); - NPixelBufferView biomeDistanceSpace = new NPixelBufferView<>(biomeDistanceAccess, biomeDistanceClass); - NBufferBundle.Access.View materialInputAccess = context.bufferAccess.get(this.materialInputBufferType); - NVoxelBufferView materialInputSpace = new NVoxelBufferView<>(materialInputAccess, materialClass); - NBufferBundle.Access.View materialOutputAccess = context.bufferAccess.get(this.materialOutputBufferType); - NVoxelBufferView materialOutputSpace = new NVoxelBufferView<>(materialOutputAccess, materialClass); - NBufferBundle.Access.View entityOutputAccess = context.bufferAccess.get(this.entityOutputBufferType); - NEntityBufferView entityOutputSpace = new NEntityBufferView(entityOutputAccess); - Bounds3i localOutputBounds_voxelGrid = materialOutputSpace.getBounds(); - Bounds3i localInputBounds_voxelGrid = this.inputBounds_voxelGrid.clone(); - Bounds3i absoluteOutputBounds_voxelGrid = localOutputBounds_voxelGrid.clone(); - absoluteOutputBounds_voxelGrid.offset(localOutputBounds_voxelGrid.min.clone().scale(-1)); - localInputBounds_voxelGrid.stack(absoluteOutputBounds_voxelGrid); - localInputBounds_voxelGrid.offset(localOutputBounds_voxelGrid.min); - localInputBounds_voxelGrid.min.y = 0; - localInputBounds_voxelGrid.max.y = 320; - Bounds3d localInputBoundsDouble_voxelGrid = localInputBounds_voxelGrid.toBounds3d(); - materialOutputSpace.copyFrom(materialInputSpace); - if (this.entityInputBufferType != null) { - NBufferBundle.Access.View entityInputAccess = context.bufferAccess.get(this.entityInputBufferType); - NEntityBufferView entityInputSpace = new NEntityBufferView(entityInputAccess); - entityOutputSpace.copyFrom(entityInputSpace); - } - - Registry biomeRegistry = this.worldStructure_workerData.get(context.workerId).getBiomeRegistry(); - HashSet biomesInBuffer = new HashSet<>(); - - for (int x = localInputBounds_voxelGrid.min.x; x < localInputBounds_voxelGrid.max.x; x++) { - for (int z = localInputBounds_voxelGrid.min.z; z < localInputBounds_voxelGrid.max.z; z++) { - Integer biomeId = biomeInputSpace.getContent(x, 0, z); - Biome biome = biomeRegistry.getObject(biomeId); - biomesInBuffer.add(biome); - } - } - - Map propFieldBiomeMap = new HashMap<>(); - - for (Biome biome : biomesInBuffer) { - for (PropField propField : biome.getPropFields()) { - if (propField.getRuntime() == this.runtimeIndex) { - propFieldBiomeMap.put(propField, biome); - } - } - } - - for (Entry entry : propFieldBiomeMap.entrySet()) { - PropField propFieldx = entry.getKey(); - Biome biome = entry.getValue(); - PositionProvider positionProvider = propFieldx.getPositionProvider(); - Consumer positionsConsumer = position -> { - if (localInputBoundsDouble_voxelGrid.contains(position)) { - Vector3i positionInt_voxelGrid = position.toVector3i(); - Integer biomeIdAtPosition = biomeInputSpace.getContent(positionInt_voxelGrid.x, 0, positionInt_voxelGrid.z); - Biome biomeAtPosition = biomeRegistry.getObject(biomeIdAtPosition); - if (biomeAtPosition == biome) { - Vector3i position2d_voxelGrid = positionInt_voxelGrid.clone(); - position2d_voxelGrid.setY(0); - double distanceToBiomeEdge = biomeDistanceSpace.getContent(position2d_voxelGrid).distanceToClosestOtherBiome(biomeIdAtPosition); - Prop prop = propField.getPropDistribution().propAt(position, context.workerId, distanceToBiomeEdge); - Bounds3i propWriteBounds = prop.getWriteBounds_voxelGrid().clone(); - propWriteBounds.offset(positionInt_voxelGrid); - if (propWriteBounds.intersects(localOutputBounds_voxelGrid)) { - ScanResult scanResult = prop.scan(positionInt_voxelGrid, materialInputSpace, context.workerId); - Prop.Context propContext = new Prop.Context(scanResult, materialOutputSpace, entityOutputSpace, context.workerId, distanceToBiomeEdge); - prop.place(propContext); - } - } - } - }; - PositionProvider.Context positionsContext = new PositionProvider.Context( - localInputBoundsDouble_voxelGrid.min, localInputBoundsDouble_voxelGrid.max, positionsConsumer, null - ); - positionProvider.positionsIn(positionsContext); - } - } - - @Nonnull - @Override - public Map getInputTypesAndBounds_bufferGrid() { - Map map = new HashMap<>(); - map.put(this.biomeInputBufferType, this.inputBounds_bufferGrid); - map.put(this.biomeDistanceInputBufferType, this.inputBounds_bufferGrid); - map.put(this.materialInputBufferType, this.inputBounds_bufferGrid); - if (this.entityInputBufferType != null) { - map.put(this.entityInputBufferType, this.inputBounds_bufferGrid); - } - - return map; - } - - @Nonnull - @Override - public List getOutputTypes() { - return List.of(this.materialOutputBufferType, this.entityOutputBufferType); - } - - @Nonnull - @Override - public String getName() { - return this.stageName; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NStage.java deleted file mode 100644 index 172bf812..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NStage.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import java.util.List; -import java.util.Map; -import javax.annotation.Nonnull; - -public interface NStage { - void run(@Nonnull NStage.Context var1); - - @Nonnull - Map getInputTypesAndBounds_bufferGrid(); - - @Nonnull - List getOutputTypes(); - - @Nonnull - String getName(); - - public static final class Context { - @Nonnull - public Map bufferAccess; - @Nonnull - public WorkerIndexer.Id workerId; - - public Context(@Nonnull Map bufferAccess, @Nonnull WorkerIndexer.Id workerId) { - this.bufferAccess = bufferAccess; - this.workerId = workerId; - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTestPropStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTestPropStage.java deleted file mode 100644 index fa6e87c3..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTestPropStage.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.material.SolidMaterial; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NVoxelBufferView; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Random; -import javax.annotation.Nonnull; - -public class NTestPropStage implements NStage { - @Nonnull - private static final Class bufferClass = NVoxelBuffer.class; - @Nonnull - private static final Class solidMaterialClass = SolidMaterial.class; - private final int CONTEXT_DEPENDENCY_RANGE_BUFFER_GRID = 0; - @Nonnull - private final Bounds3i inputBounds_bufferGrid = new Bounds3i(new Vector3i(0, 0, 0), new Vector3i(1, 40, 1)); - @Nonnull - private final NParametrizedBufferType inputBufferType; - @Nonnull - private final NParametrizedBufferType outputBufferType; - @Nonnull - private final SolidMaterial floorMaterial; - @Nonnull - private final SolidMaterial anchorMaterial; - @Nonnull - private final SolidMaterial propMaterial; - - public NTestPropStage( - @Nonnull NBufferType inputBufferType, - @Nonnull NBufferType outputBufferType, - @Nonnull SolidMaterial floorMaterial, - @Nonnull SolidMaterial anchorMaterial, - @Nonnull SolidMaterial propMaterial - ) { - assert inputBufferType instanceof NParametrizedBufferType; - - assert outputBufferType instanceof NParametrizedBufferType; - - this.inputBufferType = (NParametrizedBufferType)inputBufferType; - this.outputBufferType = (NParametrizedBufferType)outputBufferType; - - assert this.outputBufferType.isValidType(bufferClass, solidMaterialClass); - - this.floorMaterial = floorMaterial; - this.anchorMaterial = anchorMaterial; - this.propMaterial = propMaterial; - } - - @Override - public void run(@Nonnull NStage.Context context) { - NBufferBundle.Access.View inputAccess = context.bufferAccess.get(this.inputBufferType); - NVoxelBufferView inputView = new NVoxelBufferView<>(inputAccess, solidMaterialClass); - NBufferBundle.Access.View outputAccess = context.bufferAccess.get(this.outputBufferType); - NVoxelBufferView outputView = new NVoxelBufferView<>(outputAccess, solidMaterialClass); - outputView.copyFrom(inputView); - Vector3i scanPosition = new Vector3i(0, 316, 0); - Random rand = new Random(Objects.hash(outputView.minX() * 1000, outputView.minZ())); - scanPosition.setX(rand.nextInt(NVoxelBuffer.SIZE.x) + outputView.minX()); - scanPosition.setZ(rand.nextInt(NVoxelBuffer.SIZE.z) + outputView.minZ()); - - for (; scanPosition.y >= 10; scanPosition.setY(scanPosition.y - 1)) { - SolidMaterial floor = inputView.getContent(scanPosition.clone().add(0, -1, 0)); - SolidMaterial anchor = inputView.getContent(scanPosition); - if (this.floorMaterial.equals(floor) && this.anchorMaterial.equals(anchor)) { - this.placeProp(scanPosition, outputView); - } - } - } - - private void placeProp(@Nonnull Vector3i position, @Nonnull NVoxelBufferView view) { - int height = 5; - Vector3i placePosition = position.clone(); - - for (int i = 0; i < 5; i++) { - view.set(this.propMaterial, placePosition); - placePosition.setY(placePosition.getY() + 1); - } - } - - @Nonnull - @Override - public Map getInputTypesAndBounds_bufferGrid() { - return Map.of(this.inputBufferType, this.inputBounds_bufferGrid.clone()); - } - - @Nonnull - @Override - public List getOutputTypes() { - return List.of(this.outputBufferType); - } - - @Nonnull - @Override - public String getName() { - return "TestPropStage"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTestTerrainStage.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTestTerrainStage.java deleted file mode 100644 index 02523cc2..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/stages/NTestTerrainStage.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.material.SolidMaterial; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.NVoxelBufferView; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.procedurallib.logic.SimplexNoise; -import java.util.List; -import java.util.Map; -import javax.annotation.Nonnull; - -public class NTestTerrainStage implements NStage { - @Nonnull - private static final Class bufferClass = NVoxelBuffer.class; - @Nonnull - private static final Class solidMaterialClass = SolidMaterial.class; - @Nonnull - private final NParametrizedBufferType outputBufferType; - @Nonnull - private final SolidMaterial ground; - @Nonnull - private final SolidMaterial empty; - - public NTestTerrainStage(@Nonnull NBufferType outputBufferType, @Nonnull SolidMaterial groundMaterial, @Nonnull SolidMaterial emptyMaterial) { - assert outputBufferType instanceof NParametrizedBufferType; - - this.outputBufferType = (NParametrizedBufferType)outputBufferType; - - assert this.outputBufferType.isValidType(bufferClass, solidMaterialClass); - - this.ground = groundMaterial; - this.empty = emptyMaterial; - } - - @Override - public void run(@Nonnull NStage.Context context) { - NBufferBundle.Access.View access = context.bufferAccess.get(this.outputBufferType); - NVoxelBufferView materialBuffer = new NVoxelBufferView<>(access, solidMaterialClass); - SimplexNoise noise = SimplexNoise.INSTANCE; - Vector3i position = new Vector3i(); - - for (position.x = materialBuffer.minX(); position.x < materialBuffer.maxX(); position.x++) { - for (position.z = materialBuffer.minZ(); position.z < materialBuffer.maxZ(); position.z++) { - for (position.y = materialBuffer.minY(); position.y < materialBuffer.maxY(); position.y++) { - Vector3d noisePosition = position.toVector3d(); - noisePosition.scale(0.05); - double noiseValue = noise.get(1, 0, noisePosition.x, noisePosition.y, noisePosition.z); - if (position.y >= 130 && (!(noiseValue > 0.0) || position.y >= 150)) { - materialBuffer.set(this.empty, position); - } else { - materialBuffer.set(this.ground, position); - } - } - } - } - } - - @Nonnull - @Override - public Map getInputTypesAndBounds_bufferGrid() { - return Map.of(); - } - - @Nonnull - @Override - public List getOutputTypes() { - return List.of(this.outputBufferType); - } - - @Nonnull - @Override - public String getName() { - return "TestTerrainStage"; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/EntityContainer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/EntityContainer.java deleted file mode 100644 index e5af4a26..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/EntityContainer.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.views; - -import com.hypixel.hytale.builtin.hytalegenerator.props.entity.EntityPlacementData; -import javax.annotation.Nonnull; - -public interface EntityContainer { - void addEntity(@Nonnull EntityPlacementData var1); - - boolean isInsideBuffer(int var1, int var2, int var3); -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NEntityBufferView.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NEntityBufferView.java deleted file mode 100644 index dc55636c..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NEntityBufferView.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.views; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NEntityBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.props.entity.EntityPlacementData; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; -import java.util.function.Consumer; -import javax.annotation.Nonnull; - -public class NEntityBufferView implements EntityContainer { - @Nonnull - private final NBufferBundle.Access.View access; - @Nonnull - private final Bounds3i bounds_voxelGrid; - @Nonnull - private final Bounds3i bounds_bufferGrid; - - public NEntityBufferView(@Nonnull NBufferBundle.Access.View bufferAccess) { - this.access = bufferAccess; - this.bounds_bufferGrid = bufferAccess.getBounds_bufferGrid(); - this.bounds_voxelGrid = bufferAccess.getBounds_bufferGrid(); - GridUtils.toVoxelGrid_fromBufferGrid(this.bounds_voxelGrid); - } - - public void forEach(@Nonnull Consumer consumer) { - Vector3i position_bufferGrid = this.bounds_voxelGrid.min.clone(); - position_bufferGrid.setX(this.bounds_bufferGrid.min.x); - - while (position_bufferGrid.x < this.bounds_bufferGrid.max.x) { - position_bufferGrid.setZ(this.bounds_bufferGrid.min.z); - - while (position_bufferGrid.z < this.bounds_bufferGrid.max.z) { - position_bufferGrid.setY(this.bounds_bufferGrid.min.y); - - while (position_bufferGrid.y < this.bounds_bufferGrid.max.y) { - NEntityBuffer buffer = this.getBuffer_fromBufferGrid(position_bufferGrid); - buffer.forEach(consumer); - position_bufferGrid.setY(position_bufferGrid.y + 1); - } - - position_bufferGrid.setZ(position_bufferGrid.z + 1); - } - - position_bufferGrid.setX(position_bufferGrid.x + 1); - } - } - - @Nonnull - private NEntityBuffer getBuffer_fromBufferGrid(@Nonnull Vector3i position_bufferGrid) { - return (NEntityBuffer)this.access.getBuffer(position_bufferGrid).buffer(); - } - - public void copyFrom(@Nonnull NEntityBufferView source) { - assert source.bounds_voxelGrid.contains(this.bounds_voxelGrid); - - Bounds3i thisBounds_bufferGrid = this.access.getBounds_bufferGrid(); - Vector3i pos_bufferGrid = new Vector3i(); - pos_bufferGrid.setX(thisBounds_bufferGrid.min.x); - - while (pos_bufferGrid.x < thisBounds_bufferGrid.max.x) { - pos_bufferGrid.setY(thisBounds_bufferGrid.min.y); - - while (pos_bufferGrid.y < thisBounds_bufferGrid.max.y) { - pos_bufferGrid.setZ(thisBounds_bufferGrid.min.z); - - while (pos_bufferGrid.z < thisBounds_bufferGrid.max.z) { - NEntityBuffer sourceBuffer = source.getBuffer_fromBufferGrid(pos_bufferGrid); - NEntityBuffer destinationBuffer = this.getBuffer_fromBufferGrid(pos_bufferGrid); - destinationBuffer.copyFrom(sourceBuffer); - pos_bufferGrid.setZ(pos_bufferGrid.z + 1); - } - - pos_bufferGrid.setY(pos_bufferGrid.y + 1); - } - - pos_bufferGrid.setX(pos_bufferGrid.x + 1); - } - } - - @Override - public void addEntity(@Nonnull EntityPlacementData entityPlacementData) { - Vector3d entityPosition_voxelGrid = entityPlacementData.getOffset().toVector3d(); - TransformComponent transform = entityPlacementData.getEntityHolder().getComponent(TransformComponent.getComponentType()); - - assert transform != null; - - Vector3d holderPosition_voxelGrid = transform.getPosition(); - entityPosition_voxelGrid.add(holderPosition_voxelGrid); - Vector3i position_bufferGrid = GridUtils.toIntegerGrid_fromDecimalGrid(entityPosition_voxelGrid); - - assert this.bounds_voxelGrid.contains(position_bufferGrid); - - GridUtils.toBufferGrid_fromVoxelGrid(position_bufferGrid); - NEntityBuffer buffer = (NEntityBuffer)this.access.getBuffer(position_bufferGrid).buffer(); - buffer.addEntity(entityPlacementData); - } - - @Override - public boolean isInsideBuffer(int x, int y, int z) { - return this.bounds_voxelGrid.contains(new Vector3i(x, y, z)); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NPixelBufferView.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NPixelBufferView.java deleted file mode 100644 index fb93425e..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NPixelBufferView.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.views; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelConsumer; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NPixelBuffer; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.function.Predicate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class NPixelBufferView implements VoxelSpace { - public static final int Y_LEVEL_BUFFER_GRID = 0; - public static final int Y_LEVEL_VOXEL_GRID = 0; - @Nonnull - private final Class voxelType; - @Nonnull - private final NBufferBundle.Access.View bufferAccess; - @Nonnull - private final Bounds3i bounds_voxelGrid; - @Nonnull - private final Vector3i size_voxelGrid; - - public NPixelBufferView(@Nonnull NBufferBundle.Access.View bufferAccess, @Nonnull Class pixelType) { - assert bufferAccess.getBounds_bufferGrid().min.y <= 0 && bufferAccess.getBounds_bufferGrid().max.y > 0; - - this.bufferAccess = bufferAccess; - this.voxelType = pixelType; - this.bounds_voxelGrid = bufferAccess.getBounds_bufferGrid(); - GridUtils.toVoxelGrid_fromBufferGrid(this.bounds_voxelGrid); - this.bounds_voxelGrid.min.y = 0; - this.bounds_voxelGrid.max.y = 1; - this.size_voxelGrid = this.bounds_voxelGrid.getSize(); - } - - @Override - public boolean set(T content, int x, int y, int z) { - return this.set(content, new Vector3i(x, y, z)); - } - - @Override - public boolean set(T value, @Nonnull Vector3i position_voxelGrid) { - assert this.bounds_voxelGrid.contains(position_voxelGrid); - - NPixelBuffer buffer = this.getBuffer(position_voxelGrid); - Vector3i positionInBuffer_voxelGrid = position_voxelGrid.clone(); - GridUtils.toVoxelGridInsideBuffer_fromWorldGrid(positionInBuffer_voxelGrid); - buffer.setPixelContent(positionInBuffer_voxelGrid, value); - return true; - } - - @Override - public void set(T content) { - throw new UnsupportedOperationException(); - } - - @Override - public void setOrigin(int x, int y, int z) { - throw new UnsupportedOperationException(); - } - - @Nullable - @Override - public T getContent(int x, int y, int z) { - return this.getContent(new Vector3i(x, y, z)); - } - - @Nullable - @Override - public T getContent(@Nonnull Vector3i position_voxelGrid) { - assert this.bounds_voxelGrid.contains(position_voxelGrid); - - NPixelBuffer buffer = this.getBuffer(position_voxelGrid); - Vector3i positionInBuffer_voxelGrid = position_voxelGrid.clone(); - GridUtils.toVoxelGridInsideBuffer_fromWorldGrid(positionInBuffer_voxelGrid); - return buffer.getPixelContent(positionInBuffer_voxelGrid); - } - - @Nonnull - private NPixelBuffer getBuffer(@Nonnull Vector3i position_voxelGrid) { - assert this.bounds_voxelGrid.contains(position_voxelGrid); - - Vector3i localBufferPosition_bufferGrid = position_voxelGrid.clone(); - GridUtils.toBufferGrid_fromVoxelGrid(localBufferPosition_bufferGrid); - return (NPixelBuffer)this.bufferAccess.getBuffer(localBufferPosition_bufferGrid).buffer(); - } - - @Override - public boolean replace(T replacement, int x, int y, int z, @Nonnull Predicate mask) { - return false; - } - - @Override - public void pasteFrom(@Nonnull VoxelSpace source) { - throw new UnsupportedOperationException(); - } - - @Override - public int getOriginX() { - return 0; - } - - @Override - public int getOriginY() { - return 0; - } - - @Override - public int getOriginZ() { - return 0; - } - - @Override - public String getName() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isInsideSpace(int x, int y, int z) { - return this.isInsideSpace(new Vector3i(x, y, z)); - } - - @Override - public boolean isInsideSpace(@Nonnull Vector3i position) { - return this.bounds_voxelGrid.contains(position); - } - - @Override - public void forEach(VoxelConsumer action) { - throw new UnsupportedOperationException(); - } - - @Override - public int minX() { - return this.bounds_voxelGrid.min.x; - } - - @Override - public int maxX() { - return this.bounds_voxelGrid.max.x; - } - - @Override - public int minY() { - return this.bounds_voxelGrid.min.y; - } - - @Override - public int maxY() { - return this.bounds_voxelGrid.max.y; - } - - @Override - public int minZ() { - return this.bounds_voxelGrid.min.z; - } - - @Override - public int maxZ() { - return this.bounds_voxelGrid.max.z; - } - - @Override - public int sizeX() { - return this.size_voxelGrid.x; - } - - @Override - public int sizeY() { - return this.size_voxelGrid.y; - } - - @Override - public int sizeZ() { - return this.size_voxelGrid.z; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NVoxelBufferView.java b/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NVoxelBufferView.java deleted file mode 100644 index e65ead86..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/newsystem/views/NVoxelBufferView.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.newsystem.views; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelConsumer; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.NBufferBundle; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.function.Predicate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class NVoxelBufferView implements VoxelSpace { - @Nonnull - private final Class voxelType; - @Nonnull - private final NBufferBundle.Access.View bufferAccess; - @Nonnull - private final Bounds3i bounds_voxelGrid; - @Nonnull - private final Vector3i size_voxelGrid; - - public NVoxelBufferView(@Nonnull NBufferBundle.Access.View bufferAccess, @Nonnull Class voxelType) { - this.bufferAccess = bufferAccess; - this.voxelType = voxelType; - this.bounds_voxelGrid = bufferAccess.getBounds_bufferGrid(); - GridUtils.toVoxelGrid_fromBufferGrid(this.bounds_voxelGrid); - this.size_voxelGrid = this.bounds_voxelGrid.getSize(); - } - - public void copyFrom(@Nonnull NVoxelBufferView source) { - assert source.bounds_voxelGrid.contains(this.bounds_voxelGrid); - - Bounds3i thisBounds_bufferGrid = this.bufferAccess.getBounds_bufferGrid(); - Vector3i pos_bufferGrid = new Vector3i(); - pos_bufferGrid.setX(thisBounds_bufferGrid.min.x); - - while (pos_bufferGrid.x < thisBounds_bufferGrid.max.x) { - pos_bufferGrid.setY(thisBounds_bufferGrid.min.y); - - while (pos_bufferGrid.y < thisBounds_bufferGrid.max.y) { - pos_bufferGrid.setZ(thisBounds_bufferGrid.min.z); - - while (pos_bufferGrid.z < thisBounds_bufferGrid.max.z) { - NVoxelBuffer sourceBuffer = source.getBuffer_fromBufferGrid(pos_bufferGrid); - NVoxelBuffer destinationBuffer = this.getBuffer_fromBufferGrid(pos_bufferGrid); - destinationBuffer.reference(sourceBuffer); - pos_bufferGrid.setZ(pos_bufferGrid.z + 1); - } - - pos_bufferGrid.setY(pos_bufferGrid.y + 1); - } - - pos_bufferGrid.setX(pos_bufferGrid.x + 1); - } - } - - @Override - public boolean set(T content, int x, int y, int z) { - return this.set(content, new Vector3i(x, y, z)); - } - - @Override - public boolean set(T content, @Nonnull Vector3i position_voxelGrid) { - assert this.bounds_voxelGrid.contains(position_voxelGrid); - - int initialX = position_voxelGrid.x; - int initialY = position_voxelGrid.y; - int initialZ = position_voxelGrid.z; - NVoxelBuffer buffer = this.getBuffer_fromVoxelGrid(position_voxelGrid); - GridUtils.toVoxelGridInsideBuffer_fromWorldGrid(position_voxelGrid); - buffer.setVoxelContent(position_voxelGrid, content); - position_voxelGrid.assign(initialX, initialY, initialZ); - return true; - } - - @Override - public void set(T content) { - throw new UnsupportedOperationException(); - } - - @Override - public void setOrigin(int x, int y, int z) { - throw new UnsupportedOperationException(); - } - - @Nullable - @Override - public T getContent(int x, int y, int z) { - return this.getContent(new Vector3i(x, y, z)); - } - - @Nullable - @Override - public T getContent(@Nonnull Vector3i position_voxelGrid) { - assert this.bounds_voxelGrid.contains(position_voxelGrid); - - int initialX = position_voxelGrid.x; - int initialY = position_voxelGrid.y; - int initialZ = position_voxelGrid.z; - NVoxelBuffer buffer = this.getBuffer_fromVoxelGrid(position_voxelGrid); - GridUtils.toVoxelGridInsideBuffer_fromWorldGrid(position_voxelGrid); - T content = buffer.getVoxelContent(position_voxelGrid); - position_voxelGrid.assign(initialX, initialY, initialZ); - return content; - } - - @Override - public boolean replace(T replacement, int x, int y, int z, @Nonnull Predicate mask) { - return false; - } - - @Override - public void pasteFrom(@Nonnull VoxelSpace source) { - throw new UnsupportedOperationException(); - } - - @Override - public int getOriginX() { - return 0; - } - - @Override - public int getOriginY() { - return 0; - } - - @Override - public int getOriginZ() { - return 0; - } - - @Override - public String getName() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isInsideSpace(int x, int y, int z) { - return this.isInsideSpace(new Vector3i(x, y, z)); - } - - @Override - public boolean isInsideSpace(@Nonnull Vector3i position) { - return this.bounds_voxelGrid.contains(position); - } - - @Override - public void forEach(VoxelConsumer action) { - throw new UnsupportedOperationException(); - } - - @Override - public int minX() { - return this.bounds_voxelGrid.min.x; - } - - @Override - public int maxX() { - return this.bounds_voxelGrid.max.x; - } - - @Override - public int minY() { - return this.bounds_voxelGrid.min.y; - } - - @Override - public int maxY() { - return this.bounds_voxelGrid.max.y; - } - - @Override - public int minZ() { - return this.bounds_voxelGrid.min.z; - } - - @Override - public int maxZ() { - return this.bounds_voxelGrid.max.z; - } - - @Override - public int sizeX() { - return this.size_voxelGrid.x; - } - - @Override - public int sizeY() { - return this.size_voxelGrid.y; - } - - @Override - public int sizeZ() { - return this.size_voxelGrid.z; - } - - @Nonnull - private NVoxelBuffer getBuffer_fromVoxelGrid(@Nonnull Vector3i position_voxelGrid) { - Vector3i localBufferPosition_bufferGrid = position_voxelGrid.clone(); - GridUtils.toBufferGrid_fromVoxelGrid(localBufferPosition_bufferGrid); - return this.getBuffer_fromBufferGrid(localBufferPosition_bufferGrid); - } - - @Nonnull - private NVoxelBuffer getBuffer_fromBufferGrid(@Nonnull Vector3i position_bufferGrid) { - return (NVoxelBuffer)this.bufferAccess.getBuffer(position_bufferGrid).buffer(); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/CellNoiseField.java b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/CellNoiseField.java similarity index 96% rename from src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/CellNoiseField.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/noise/CellNoiseField.java index c6afecfa..27d48af8 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/CellNoiseField.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/CellNoiseField.java @@ -1,6 +1,5 @@ -package com.hypixel.hytale.builtin.hytalegenerator.fields.noise; +package com.hypixel.hytale.builtin.hytalegenerator.noise; -import com.hypixel.hytale.builtin.hytalegenerator.fields.FastNoiseLite; import javax.annotation.Nonnull; public class CellNoiseField extends NoiseField { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/FastNoiseLite.java b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/FastNoiseLite.java similarity index 99% rename from src/com/hypixel/hytale/builtin/hytalegenerator/fields/FastNoiseLite.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/noise/FastNoiseLite.java index a999fd86..50a1eb3f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/FastNoiseLite.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/FastNoiseLite.java @@ -1,10 +1,10 @@ -package com.hypixel.hytale.builtin.hytalegenerator.fields; +package com.hypixel.hytale.builtin.hytalegenerator.noise; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.codecs.EnumCodec; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector3d; public class FastNoiseLite { private int mSeed = 1337; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/NoiseField.java b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/NoiseField.java similarity index 93% rename from src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/NoiseField.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/noise/NoiseField.java index 127fc2eb..f58f3b6e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/NoiseField.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/NoiseField.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.fields.noise; +package com.hypixel.hytale.builtin.hytalegenerator.noise; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/Simplex.java b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/Simplex.java similarity index 99% rename from src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/Simplex.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/noise/Simplex.java index 2cebcf84..86238ac2 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/Simplex.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/Simplex.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.fields.noise; +package com.hypixel.hytale.builtin.hytalegenerator.noise; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/SimplexNoiseField.java b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/SimplexNoiseField.java similarity index 98% rename from src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/SimplexNoiseField.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/noise/SimplexNoiseField.java index 15ed4760..aa1cbc41 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/noise/SimplexNoiseField.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/SimplexNoiseField.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.fields.noise; +package com.hypixel.hytale.builtin.hytalegenerator.noise; import java.util.Random; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/JitterPointField.java b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/JitterPointField.java similarity index 82% rename from src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/JitterPointField.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/JitterPointField.java index f1d9a6b4..fbf1e56f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/JitterPointField.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/JitterPointField.java @@ -1,13 +1,15 @@ -package com.hypixel.hytale.builtin.hytalegenerator.fields.points; +package com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider; import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; -import com.hypixel.hytale.builtin.hytalegenerator.fields.FastNoiseLite; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.noise.FastNoiseLite; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.function.Consumer; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector2i; +import org.joml.Vector3d; +import org.joml.Vector3i; public class JitterPointField extends PointField { @Nonnull @@ -50,7 +52,7 @@ public class JitterPointField extends PointField { @Override public void points3i(@Nonnull Vector3i min, @Nonnull Vector3i max, @Nonnull Consumer pointsOut) { - this.points3d(min.toVector3d(), max.toVector3d(), p -> pointsOut.accept(p.toVector3i())); + this.points3d(Vector3iUtil.toVector3d(min), Vector3iUtil.toVector3d(max), p -> pointsOut.accept(Vector3dUtil.toVector3i(p))); } @Override @@ -65,8 +67,8 @@ public class JitterPointField extends PointField { @Override public void points3d(@Nonnull Vector3d min, @Nonnull Vector3d max, @Nonnull Consumer pointsOut) { - Vector3d cellMin = min.clone().scale(this.scaleDown3d); - Vector3d cellMax = max.clone().scale(this.scaleDown3d); + Vector3d cellMin = new Vector3d(min).mul(this.scaleDown3d); + Vector3d cellMax = new Vector3d(max).mul(this.scaleDown3d); cellMin.x = FastNoiseLite.fastRound(cellMin.x); cellMin.y = FastNoiseLite.fastRound(cellMin.y); cellMin.z = FastNoiseLite.fastRound(cellMin.z); @@ -78,7 +80,7 @@ public class JitterPointField extends PointField { for (double y = cellMin.y; y <= cellMax.y + 0.25; y++) { for (double z = cellMin.z; z <= cellMax.z + 0.25; z++) { Vector3d point = this.noise.pointFor(this.seed, this.jitter, x, y, z); - point.scale(this.scaleUp3d); + point.mul(this.scaleUp3d); if (VectorUtil.isInside(point, min, max)) { pointsOut.accept(point); } @@ -89,8 +91,8 @@ public class JitterPointField extends PointField { @Override public void points2d(@Nonnull Vector2d min, @Nonnull Vector2d max, @Nonnull Consumer pointsOut) { - Vector2d cellMin = min.clone().scale(this.scaleDown2d); - Vector2d cellMax = max.clone().scale(this.scaleDown2d); + Vector2d cellMin = new Vector2d(min).mul(this.scaleDown2d); + Vector2d cellMax = new Vector2d(max).mul(this.scaleDown2d); cellMin.x = FastNoiseLite.fastRound(cellMin.x); cellMin.y = FastNoiseLite.fastRound(cellMin.y); cellMax.x = FastNoiseLite.fastRound(cellMax.x); @@ -99,7 +101,7 @@ public class JitterPointField extends PointField { for (double x = cellMin.x; x <= cellMax.x + 0.25; x++) { for (double z = cellMin.y; z <= cellMax.y + 0.25; z++) { Vector2d point = this.noise.pointFor(this.seed, this.jitter, x, z); - point.scale(this.scaleUp2d); + point.mul(this.scaleUp2d); if (VectorUtil.isInside(point, min, max)) { pointsOut.accept(point); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/PointField.java b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/PointField.java similarity index 88% rename from src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/PointField.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/PointField.java index 22b4fd09..060a3e85 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/PointField.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/PointField.java @@ -1,12 +1,12 @@ -package com.hypixel.hytale.builtin.hytalegenerator.fields.points; +package com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector2i; +import org.joml.Vector3d; +import org.joml.Vector3i; public abstract class PointField implements PointProvider { protected double scaleX = 1.0; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/PointProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/PointProvider.java similarity index 80% rename from src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/PointProvider.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/PointProvider.java index 0f4af45c..99c71c8e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/fields/points/PointProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/noise/pointprovider/PointProvider.java @@ -1,12 +1,12 @@ -package com.hypixel.hytale.builtin.hytalegenerator.fields.points; +package com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.List; import java.util.function.Consumer; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector2i; +import org.joml.Vector3d; +import org.joml.Vector3i; public interface PointProvider { List points3i(@Nonnull Vector3i var1, @Nonnull Vector3i var2); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/AndPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/AndPattern.java index 11505562..8915371b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/AndPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/AndPattern.java @@ -1,29 +1,29 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import java.util.List; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class AndPattern extends Pattern { @Nonnull private final Pattern[] patterns; - private final SpaceSize readSpaceSize; + @Nonnull + private final Bounds3i bounds_voxelGrid; public AndPattern(@Nonnull List patterns) { if (patterns.isEmpty()) { this.patterns = new Pattern[0]; - this.readSpaceSize = SpaceSize.empty(); + this.bounds_voxelGrid = Bounds3i.ZERO; } else { this.patterns = new Pattern[patterns.size()]; - SpaceSize spaceAcc = patterns.getFirst().readSpace(); + this.bounds_voxelGrid = patterns.getFirst().getBounds_voxelGrid().clone(); for (int i = 0; i < patterns.size(); i++) { Pattern pattern = patterns.get(i); this.patterns[i] = pattern; - spaceAcc = SpaceSize.merge(spaceAcc, pattern.readSpace()); + this.bounds_voxelGrid.encompass(pattern.getBounds_voxelGrid()); } - - this.readSpaceSize = spaceAcc; } } @@ -38,9 +38,9 @@ public class AndPattern extends Pattern { return true; } - @Nonnull + @NonNullDecl @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/ConstantPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/ConstantPattern.java new file mode 100644 index 00000000..35206571 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/ConstantPattern.java @@ -0,0 +1,25 @@ +package com.hypixel.hytale.builtin.hytalegenerator.patterns; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class ConstantPattern extends Pattern { + public static final ConstantPattern INSTANCE_TRUE = new ConstantPattern(true); + public static final ConstantPattern INSTANCE_FALSE = new ConstantPattern(false); + private final boolean value; + + private ConstantPattern(boolean value) { + this.value = value; + } + + @Override + public boolean matches(@NonNullDecl Pattern.Context context) { + return this.value; + } + + @NonNullDecl + @Override + public Bounds3i getBounds_voxelGrid() { + return Bounds3i.ZERO; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/CuboidPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/CuboidPattern.java index 70ba36b7..046d06f2 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/CuboidPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/CuboidPattern.java @@ -1,8 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class CuboidPattern extends Pattern { @Nonnull @@ -12,7 +13,7 @@ public class CuboidPattern extends Pattern { @Nonnull private final Vector3i max; @Nonnull - private final SpaceSize readSpaceSize; + private final Bounds3i bounds_voxelGrid; @Nonnull private final Vector3i rScanMin; @Nonnull @@ -26,7 +27,8 @@ public class CuboidPattern extends Pattern { this.subPattern = subPattern; this.min = min; this.max = max; - this.readSpaceSize = new SpaceSize(min, max.clone().add(1, 1, 1)); + this.bounds_voxelGrid = new Bounds3i(min, new Vector3i(max).add(Vector3iUtil.ALL_ONES)); + this.bounds_voxelGrid.stack(subPattern.getBounds_voxelGrid()); this.rScanMin = new Vector3i(); this.rScanMax = new Vector3i(); this.rChildPosition = new Vector3i(); @@ -35,16 +37,16 @@ public class CuboidPattern extends Pattern { @Override public boolean matches(@Nonnull Pattern.Context context) { - this.rScanMin.assign(this.min).add(context.position); - this.rScanMax.assign(this.max).add(context.position); - this.rChildPosition.assign(context.position); + this.rScanMin.set(this.min).add(context.position); + this.rScanMax.set(this.max).add(context.position); + this.rChildPosition.set(context.position); this.rChildContext.assign(context); this.rChildContext.position = this.rChildPosition; for (this.rChildPosition.x = this.rScanMin.x; this.rChildPosition.x <= this.rScanMax.x; this.rChildPosition.x++) { for (this.rChildPosition.z = this.rScanMin.z; this.rChildPosition.z <= this.rScanMax.z; this.rChildPosition.z++) { for (this.rChildPosition.y = this.rScanMin.y; this.rChildPosition.y <= this.rScanMax.y; this.rChildPosition.y++) { - if (!context.materialSpace.isInsideSpace(this.rChildPosition)) { + if (!context.materialSpace.getBounds().contains(this.rChildPosition)) { return false; } @@ -60,7 +62,7 @@ public class CuboidPattern extends Pattern { @Nonnull @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/FieldFunctionPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/FieldFunctionPattern.java index 316f5d7c..a11f94fe 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/FieldFunctionPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/FieldFunctionPattern.java @@ -1,24 +1,23 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.delimiters.RangeDouble; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class FieldFunctionPattern extends Pattern { @Nonnull private final Density field; @Nonnull - private final SpaceSize readSpaceSize; - @Nonnull - private final List delimiters; + private final List delimiters; @Nonnull private final Density.Context rDensityContext; public FieldFunctionPattern(@Nonnull Density field) { this.field = field; - this.readSpaceSize = SpaceSize.empty(); this.delimiters = new ArrayList<>(1); this.rDensityContext = new Density.Context(); } @@ -28,8 +27,8 @@ public class FieldFunctionPattern extends Pattern { this.rDensityContext.assign(context); double density = this.field.process(this.rDensityContext); - for (FieldFunctionPattern.Delimiter d : this.delimiters) { - if (d.isInside(density)) { + for (RangeDouble delimiter : this.delimiters) { + if (delimiter.contains(density)) { return true; } } @@ -37,25 +36,13 @@ public class FieldFunctionPattern extends Pattern { return false; } - @Nonnull + @NonNullDecl @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); + public Bounds3i getBounds_voxelGrid() { + return Bounds3i.ZERO; } public void addDelimiter(double min, double max) { - FieldFunctionPattern.Delimiter d = new FieldFunctionPattern.Delimiter(); - d.min = min; - d.max = max; - this.delimiters.add(d); - } - - private static class Delimiter { - double min; - double max; - - boolean isInside(double v) { - return v >= this.min && v < this.max; - } + this.delimiters.add(new RangeDouble(min, max)); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/GapPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/GapPattern.java deleted file mode 100644 index 496228e0..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/GapPattern.java +++ /dev/null @@ -1,238 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.patterns; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import javax.annotation.Nonnull; - -public class GapPattern extends Pattern { - private List> axisPositionedPatterns; - private List depthPositionedPatterns; - private double gapSize; - private double anchorSize; - private double anchorRoughness; - private int depthDown; - private int depthUp; - private Pattern gapPattern; - private Pattern anchorPattern; - private SpaceSize readSpaceSize; - @Nonnull - private final Vector3i rChildPosition; - @Nonnull - private final Pattern.Context rChildContext; - - public GapPattern( - @Nonnull List angles, - double gapSize, - double anchorSize, - double anchorRoughness, - int depthDown, - int depthUp, - @Nonnull Pattern gapPattern, - @Nonnull Pattern anchorPattern - ) { - if (!(gapSize < 0.0) && !(anchorSize < 0.0) && !(anchorRoughness < 0.0) && depthDown >= 0 && depthUp >= 0) { - this.gapSize = gapSize; - this.anchorSize = anchorSize; - this.gapPattern = gapPattern; - this.anchorPattern = anchorPattern; - this.anchorRoughness = anchorRoughness; - this.depthDown = depthDown; - this.depthUp = depthUp; - this.rChildPosition = new Vector3i(); - this.rChildContext = new Pattern.Context(); - this.depthPositionedPatterns = this.renderDepths(); - this.axisPositionedPatterns = new ArrayList<>(angles.size()); - - for (float angle : angles) { - List positions = this.renderPositions(angle); - this.axisPositionedPatterns.add(positions); - } - - Vector3i min = null; - Vector3i max = null; - - for (List direction : this.axisPositionedPatterns) { - for (GapPattern.PositionedPattern pos : direction) { - if (min == null) { - min = pos.position.clone(); - max = pos.position.clone(); - } else { - min = Vector3i.min(min, pos.position); - max = Vector3i.max(max, pos.position); - } - } - } - - if (max == null) { - this.readSpaceSize = new SpaceSize(new Vector3i(), new Vector3i()); - } else { - max.add(1, 1, 1); - this.readSpaceSize = new SpaceSize(min, max); - } - } else { - throw new IllegalArgumentException("negative sizes"); - } - } - - @Override - public boolean matches(@Nonnull Pattern.Context context) { - this.rChildPosition.assign(0, 0, 0); - this.rChildContext.assign(context); - this.rChildContext.position = this.rChildPosition; - - for (GapPattern.PositionedPattern entry : this.depthPositionedPatterns) { - this.rChildPosition.assign(entry.position).add(context.position); - if (!entry.pattern.matches(this.rChildContext)) { - return false; - } - } - - for (List patternsInDirection : this.axisPositionedPatterns) { - boolean matchesDirection = true; - - for (GapPattern.PositionedPattern entryx : patternsInDirection) { - this.rChildPosition.assign(entryx.position).add(context.position); - if (!entryx.pattern.matches(context)) { - matchesDirection = false; - break; - } - } - - if (matchesDirection) { - return true; - } - } - - return false; - } - - @Nonnull - @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); - } - - @Nonnull - private List renderDepths() { - ArrayList positions = new ArrayList<>(); - Vector3i pointer = new Vector3i(); - int stepsDown = this.depthDown - 1; - - for (int i = 0; i < this.depthDown; i++) { - pointer.add(0, -1, 0); - positions.add(new GapPattern.PositionedPattern(this.gapPattern, pointer.clone())); - } - - pointer = new Vector3i(); - int stepsUp = this.depthUp - 1; - - for (int i = 0; i < this.depthUp; i++) { - pointer.add(0, 1, 0); - positions.add(new GapPattern.PositionedPattern(this.gapPattern, pointer.clone())); - } - - return positions; - } - - @Nonnull - private List renderPositions(float angle) { - ArrayList positions = new ArrayList<>(); - positions.addAll(this.renderHalfPositions(angle)); - positions.addAll(this.renderHalfPositions((float) Math.PI + angle)); - ArrayList uniquePositions = new ArrayList<>(positions.size()); - HashSet positionsSet = new HashSet<>(); - - for (GapPattern.PositionedPattern e : positions) { - if (!positionsSet.contains(e.position)) { - uniquePositions.add(e); - positionsSet.add(e.position); - } - } - - return uniquePositions; - } - - @Nonnull - private List renderHalfPositions(float angle) { - ArrayList positions = new ArrayList<>(); - double halfGap = this.gapSize / 2.0 - 1.0 - this.anchorRoughness; - halfGap = Math.max(0.0, halfGap); - double halfWall = this.anchorSize / 2.0; - Vector3d pointer = new Vector3d(0.5, 0.5, 0.5); - Vector3d mov = new Vector3d(0.0, 0.0, -1.0); - mov.rotateY(angle); - double stepSize = 0.5; - mov.setLength(stepSize); - int steps = (int)(halfGap / stepSize); - - for (int s = 0; s < steps; s++) { - pointer.add(mov); - positions.add(new GapPattern.PositionedPattern(this.gapPattern, pointer.toVector3i())); - } - - positions.add(new GapPattern.PositionedPattern(this.gapPattern, new Vector3i())); - pointer = mov.clone().setLength(halfGap).add(0.5, 0.5, 0.5); - positions.add(new GapPattern.PositionedPattern(this.gapPattern, pointer.toVector3i())); - Vector3d anchor = mov.clone().setLength(this.gapSize / 2.0); - pointer = anchor.clone().add(0.5, 0.5, 0.5); - positions.add(new GapPattern.PositionedPattern(this.anchorPattern, anchor.toVector3i())); - mov.rotateY((float) (Math.PI / 2)); - steps = (int)(halfWall / stepSize); - - for (int s = 0; s < steps; s++) { - pointer.add(mov); - positions.add(new GapPattern.PositionedPattern(this.anchorPattern, pointer.toVector3i())); - } - - Vector3d wallTip = anchor.clone().add(0.5, 0.5, 0.5); - wallTip.add(mov.clone().setLength(halfWall)); - positions.add(new GapPattern.PositionedPattern(this.anchorPattern, wallTip.toVector3i())); - mov.scale(-1.0); - pointer = anchor.clone().add(0.5, 0.5, 0.5); - - for (int s = 0; s < steps; s++) { - pointer.add(mov); - positions.add(new GapPattern.PositionedPattern(this.anchorPattern, pointer.toVector3i())); - } - - wallTip = anchor.clone().add(0.5, 0.5, 0.5); - wallTip.add(mov.clone().setLength(halfWall)); - positions.add(new GapPattern.PositionedPattern(this.anchorPattern, wallTip.toVector3i())); - return positions; - } - - public static class PositionedPattern { - private Vector3i position; - private Pattern pattern; - - public PositionedPattern(@Nonnull Pattern pattern, @Nonnull Vector3i position) { - this.pattern = pattern; - this.position = position.clone(); - } - - public int getX() { - return this.position.x; - } - - public int getY() { - return this.position.y; - } - - public int getZ() { - return this.position.z; - } - - public Pattern getPattern() { - return this.pattern; - } - - @Nonnull - protected GapPattern.PositionedPattern clone() { - return new GapPattern.PositionedPattern(this.pattern, this.position.clone()); - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/MaterialPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/MaterialPattern.java index 05fe09ff..b0df2e9b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/MaterialPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/MaterialPattern.java @@ -1,13 +1,14 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class MaterialPattern extends Pattern { @Nonnull - private static final SpaceSize READ_SPACE_SIZE = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(1, 0, 1)); + private static final Bounds3i BOUNDS = new Bounds3i(Vector3iUtil.ZERO, Vector3iUtil.ALL_ONES); @Nonnull private final Material material; @@ -17,17 +18,17 @@ public class MaterialPattern extends Pattern { @Override public boolean matches(@Nonnull Pattern.Context context) { - if (!context.materialSpace.isInsideSpace(context.position)) { + if (!context.materialSpace.getBounds().contains(context.position)) { return false; } else { - Material material = context.materialSpace.getContent(context.position); + Material material = context.materialSpace.get(context.position); return this.material.solid().blockId == material.solid().blockId && this.material.fluid().fluidId == material.fluid().fluidId; } } - @Nonnull + @NonNullDecl @Override - public SpaceSize readSpace() { - return READ_SPACE_SIZE.clone(); + public Bounds3i getBounds_voxelGrid() { + return BOUNDS; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/MaterialSetPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/MaterialSetPattern.java index 87f90b9a..332aa614 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/MaterialSetPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/MaterialSetPattern.java @@ -1,14 +1,16 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; import com.hypixel.hytale.builtin.hytalegenerator.MaterialSet; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; public class MaterialSetPattern extends Pattern { @Nonnull - private static final SpaceSize READ_SPACE_SIZE = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(1, 0, 1)); + private static final Bounds3i BOUNDS_VOXEL_GRID = new Bounds3i(new Vector3i(), Vector3iUtil.ALL_ONES); @Nonnull private final MaterialSet materialSet; @@ -18,18 +20,18 @@ public class MaterialSetPattern extends Pattern { @Override public boolean matches(@Nonnull Pattern.Context context) { - if (!context.materialSpace.isInsideSpace(context.position)) { + if (!context.materialSpace.getBounds().contains(context.position)) { return false; } else { - Material material = context.materialSpace.getContent(context.position); + Material material = context.materialSpace.get(context.position); int hash = material.hashMaterialIds(); return this.materialSet.test(hash); } } - @Nonnull + @NonNullDecl @Override - public SpaceSize readSpace() { - return READ_SPACE_SIZE.clone(); + public Bounds3i getBounds_voxelGrid() { + return BOUNDS_VOXEL_GRID; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/NotPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/NotPattern.java index 76f7ec98..80db7853 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/NotPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/NotPattern.java @@ -1,16 +1,15 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class NotPattern extends Pattern { @Nonnull private final Pattern pattern; - private final SpaceSize readSpaceSize; public NotPattern(@Nonnull Pattern pattern) { this.pattern = pattern; - this.readSpaceSize = pattern.readSpace(); } @Override @@ -18,9 +17,9 @@ public class NotPattern extends Pattern { return !this.pattern.matches(context); } - @Nonnull + @NonNullDecl @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); + public Bounds3i getBounds_voxelGrid() { + return this.pattern.getBounds_voxelGrid(); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/OffsetPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/OffsetPattern.java index 352bde64..2dc59a40 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/OffsetPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/OffsetPattern.java @@ -1,8 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; public class OffsetPattern extends Pattern { @Nonnull @@ -10,7 +11,7 @@ public class OffsetPattern extends Pattern { @Nonnull private final Vector3i offset; @Nonnull - private final SpaceSize readSpaceSize; + private final Bounds3i bounds_voxelGrid; @Nonnull private final Vector3i rChildPosition; @Nonnull @@ -19,22 +20,22 @@ public class OffsetPattern extends Pattern { public OffsetPattern(@Nonnull Pattern pattern, @Nonnull Vector3i offset) { this.pattern = pattern; this.offset = offset; - this.readSpaceSize = pattern.readSpace().moveBy(offset); + this.bounds_voxelGrid = pattern.getBounds_voxelGrid().clone().offset(offset); this.rChildPosition = new Vector3i(); this.rChildContext = new Pattern.Context(); } @Override public boolean matches(@Nonnull Pattern.Context context) { - this.rChildPosition.assign(context.position).add(this.offset); + this.rChildPosition.set(context.position).add(this.offset); this.rChildContext.assign(context); this.rChildContext.position = this.rChildPosition; return this.pattern.matches(this.rChildContext); } - @Nonnull + @NonNullDecl @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/OrPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/OrPattern.java index dca7e846..e1fcaf3e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/OrPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/OrPattern.java @@ -1,29 +1,29 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import java.util.List; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class OrPattern extends Pattern { @Nonnull private final Pattern[] patterns; - private final SpaceSize readSpaceSize; + @Nonnull + private final Bounds3i bounds_voxelGrid; public OrPattern(@Nonnull List patterns) { if (patterns.isEmpty()) { this.patterns = new Pattern[0]; - this.readSpaceSize = SpaceSize.empty(); + this.bounds_voxelGrid = Bounds3i.ZERO; } else { this.patterns = new Pattern[patterns.size()]; - SpaceSize spaceAcc = patterns.getFirst().readSpace(); + this.bounds_voxelGrid = patterns.getFirst().getBounds_voxelGrid().clone(); for (int i = 0; i < patterns.size(); i++) { Pattern pattern = patterns.get(i); this.patterns[i] = pattern; - spaceAcc = SpaceSize.merge(spaceAcc, pattern.readSpace()); + this.bounds_voxelGrid.encompass(pattern.getBounds_voxelGrid()); } - - this.readSpaceSize = spaceAcc; } } @@ -38,9 +38,9 @@ public class OrPattern extends Pattern { return false; } - @Nonnull + @NonNullDecl @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/Pattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/Pattern.java index a034cb15..54fedf71 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/Pattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/Pattern.java @@ -1,51 +1,19 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.NullSpace; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.NullSpace; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public abstract class Pattern { public abstract boolean matches(@Nonnull Pattern.Context var1); - public abstract SpaceSize readSpace(); - @Nonnull - public static Pattern noPattern() { - final SpaceSize space = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(0, 0, 0)); - return new Pattern() { - @Override - public boolean matches(@Nonnull Pattern.Context context) { - return false; - } - - @Nonnull - @Override - public SpaceSize readSpace() { - return space; - } - }; - } - - @Nonnull - public static Pattern yesPattern() { - final SpaceSize space = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(0, 0, 0)); - return new Pattern() { - @Override - public boolean matches(@Nonnull Pattern.Context context) { - return true; - } - - @Nonnull - @Override - public SpaceSize readSpace() { - return space; - } - }; - } + public abstract Bounds3i getBounds_voxelGrid(); public static class Context { @Nonnull @@ -72,5 +40,10 @@ public abstract class Pattern { this.position = other.position; this.materialSpace = other.materialSpace; } + + public void assign(@Nonnull Prop.Context other) { + this.position = other.position; + this.materialSpace = other.materialReadSpace; + } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/RotatorPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/RotatorPattern.java new file mode 100644 index 00000000..a98049a0 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/RotatorPattern.java @@ -0,0 +1,41 @@ +package com.hypixel.hytale.builtin.hytalegenerator.patterns; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.RotationVoxelSpace; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class RotatorPattern extends Pattern { + @Nonnull + private final Pattern pattern; + @Nonnull + private final Bounds3i bounds; + @Nonnull + private final RotationVoxelSpace readRotationVoxelSpace; + @Nonnull + private final Pattern.Context rChildContext; + + public RotatorPattern(@Nonnull Pattern pattern, @Nonnull RotationTuple rotation, @Nonnull MaterialCache materialCache) { + this.pattern = pattern; + this.bounds = pattern.getBounds_voxelGrid().clone().applyRotationAroundVoxel(rotation, Vector3iUtil.ZERO); + this.readRotationVoxelSpace = new RotationVoxelSpace(rotation, materialCache); + this.rChildContext = new Pattern.Context(); + } + + @Override + public boolean matches(@NonNullDecl Pattern.Context context) { + this.readRotationVoxelSpace.setSource(context.materialSpace, context.position); + this.rChildContext.assign(context); + this.rChildContext.materialSpace = this.readRotationVoxelSpace; + return this.pattern.matches(this.rChildContext); + } + + @NonNullDecl + @Override + public Bounds3i getBounds_voxelGrid() { + return this.bounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/SurfacePattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/SurfacePattern.java index f6f38c7e..a58319ed 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/SurfacePattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/SurfacePattern.java @@ -1,13 +1,14 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.codecs.EnumCodec; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; public class SurfacePattern extends Pattern { @Nonnull @@ -15,12 +16,12 @@ public class SurfacePattern extends Pattern { @Nonnull private final Pattern originPattern; @Nonnull - private final SpaceSize readSpaceSize; - @Nonnull private final List surfacePositions; @Nonnull private final List originPositions; @Nonnull + private final Bounds3i bounds_voxelGrid; + @Nonnull private final Vector3i rChildPosition; @Nonnull private final Pattern.Context rChildContext; @@ -70,38 +71,39 @@ public class SurfacePattern extends Pattern { this.applyFacing(pos, facing); } - SpaceSize floorSpace = surfacePattern.readSpace(); + Bounds3i stampBounds_voxelGrid = surfacePattern.getBounds_voxelGrid().clone(); + if (!this.surfacePositions.isEmpty()) { + this.bounds_voxelGrid = stampBounds_voxelGrid.clone().offset(this.surfacePositions.getFirst()); + } else { + this.bounds_voxelGrid = new Bounds3i(); + } for (Vector3i pos : this.surfacePositions) { - floorSpace = SpaceSize.merge(floorSpace, new SpaceSize(pos)); + this.bounds_voxelGrid.encompass(stampBounds_voxelGrid.clone().offset(pos)); } - floorSpace = SpaceSize.stack(floorSpace, surfacePattern.readSpace()); - SpaceSize originSpace = originPattern.readSpace(); + stampBounds_voxelGrid.assign(originPattern.getBounds_voxelGrid()); for (Vector3i pos : this.originPositions) { - originSpace = SpaceSize.merge(originSpace, new SpaceSize(pos)); + this.bounds_voxelGrid.encompass(stampBounds_voxelGrid.clone().offset(pos)); } - - originSpace = SpaceSize.stack(originSpace, originPattern.readSpace()); - this.readSpaceSize = SpaceSize.merge(floorSpace, originSpace); } @Override public boolean matches(@Nonnull Pattern.Context context) { - this.rChildPosition.assign(context.position); + this.rChildPosition.set(context.position); this.rChildContext.assign(context); this.rChildContext.position = this.rChildPosition; for (Vector3i pos : this.originPositions) { - this.rChildPosition.assign(pos).add(context.position); + this.rChildPosition.set(pos).add(context.position); if (!this.originPattern.matches(this.rChildContext)) { return false; } } for (Vector3i posx : this.surfacePositions) { - this.rChildPosition.assign(posx).add(context.position); + this.rChildPosition.set(posx).add(context.position); if (!this.wallPattern.matches(this.rChildContext)) { return false; } @@ -155,10 +157,10 @@ public class SurfacePattern extends Pattern { pos.x = -pos.x; } - @Nonnull + @NonNullDecl @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; } public static enum Facing { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/WallPattern.java b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/WallPattern.java index d50f54e2..b8960130 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/WallPattern.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/patterns/WallPattern.java @@ -1,12 +1,12 @@ package com.hypixel.hytale.builtin.hytalegenerator.patterns; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.codecs.EnumCodec; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class WallPattern extends Pattern { @Nonnull @@ -16,7 +16,8 @@ public class WallPattern extends Pattern { @Nonnull private final List directions; private final boolean matchAll; - private final SpaceSize readSpaceSize; + @Nonnull + private final Bounds3i bounds_voxelGrid; @Nonnull private final Vector3i rWallPosition; @Nonnull @@ -29,21 +30,26 @@ public class WallPattern extends Pattern { this.matchAll = matchAll; this.rWallPosition = new Vector3i(); this.rWallContext = new Pattern.Context(); - SpaceSize originSpace = originPattern.readSpace(); - SpaceSize wallSpace = wallPattern.readSpace(); - SpaceSize totalSpace = originSpace; + this.bounds_voxelGrid = originPattern.getBounds_voxelGrid().clone(); + Bounds3i wallBounds_voxelGrid = wallPattern.getBounds_voxelGrid().clone(); for (WallPattern.WallDirection d : this.directions) { - SpaceSize directionedWallSpace = switch (d) { - case N -> wallSpace.clone().moveBy(new Vector3i(0, 0, -1)); - case S -> wallSpace.clone().moveBy(new Vector3i(0, 0, 1)); - case E -> wallSpace.clone().moveBy(new Vector3i(1, 0, 0)); - case W -> wallSpace.clone().moveBy(new Vector3i(-1, 0, 0)); - }; - totalSpace = SpaceSize.merge(totalSpace, directionedWallSpace); - } + switch (d) { + case N: + wallBounds_voxelGrid.clone().offset(new Vector3i(0, 0, -1)); + break; + case S: + wallBounds_voxelGrid.clone().offset(new Vector3i(0, 0, 1)); + break; + case E: + wallBounds_voxelGrid.clone().offset(new Vector3i(1, 0, 0)); + break; + case W: + wallBounds_voxelGrid.clone().offset(new Vector3i(-1, 0, 0)); + } - this.readSpaceSize = totalSpace; + this.bounds_voxelGrid.encompass(wallBounds_voxelGrid); + } } @Override @@ -63,7 +69,7 @@ public class WallPattern extends Pattern { } private boolean matches(@Nonnull Pattern.Context context, @Nonnull WallPattern.WallDirection direction) { - this.rWallPosition.assign(context.position); + this.rWallPosition.set(context.position); switch (direction) { case N: this.rWallPosition.z--; @@ -85,8 +91,8 @@ public class WallPattern extends Pattern { @Nonnull @Override - public SpaceSize readSpace() { - return this.readSpaceSize.clone(); + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; } public static enum WallDirection { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/pipe/Control.java b/src/com/hypixel/hytale/builtin/hytalegenerator/pipe/Control.java new file mode 100644 index 00000000..f9845981 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/pipe/Control.java @@ -0,0 +1,9 @@ +package com.hypixel.hytale.builtin.hytalegenerator.pipe; + +public class Control { + public boolean stop = false; + + public void reset() { + this.stop = false; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/pipe/Pipe.java b/src/com/hypixel/hytale/builtin/hytalegenerator/pipe/Pipe.java new file mode 100644 index 00000000..c8dddb95 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/pipe/Pipe.java @@ -0,0 +1,26 @@ +package com.hypixel.hytale.builtin.hytalegenerator.pipe; + +import javax.annotation.Nonnull; + +public class Pipe { + public static final Pipe.One EMPTY_ONE = (a, c) -> {}; + public static final Pipe.Two EMPTY_TWO = (a, b, c) -> {}; + + public static Pipe.One getEmptyOne() { + return (Pipe.One)EMPTY_ONE; + } + + public static Pipe.Two getEmptyTwo() { + return (Pipe.Two)EMPTY_TWO; + } + + @FunctionalInterface + public interface One { + void accept(@Nonnull Input var1, @Nonnull Control var2); + } + + @FunctionalInterface + public interface Two { + void accept(@Nonnull InputA var1, @Nonnull InputB var2, @Nonnull Control var3); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/Handle.java b/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/Handle.java index e4a0d058..fafdb5fe 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/Handle.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/Handle.java @@ -1,18 +1,17 @@ package com.hypixel.hytale.builtin.hytalegenerator.plugin; -import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.ChunkRequest; +import com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator.ChunkRequest; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider; import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedChunk; import com.hypixel.hytale.server.core.universe.world.worldgen.IWorldGen; import com.hypixel.hytale.server.core.universe.world.worldgen.WorldGenTimingsCollector; import java.util.List; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.LongPredicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Handle implements IWorldGen { @Nonnull @@ -33,7 +32,7 @@ public class Handle implements IWorldGen { public CompletableFuture generate(int seed, long index, int x, int z, LongPredicate stillNeeded) { ChunkRequest.Arguments arguments = new ChunkRequest.Arguments(seed, index, x, z, stillNeeded); if (this.seedOverride != null) { - seed = Objects.hash(this.seedOverride); + seed = this.seedOverride.hashCode(); } this.profile.setSeed(seed); @@ -50,6 +49,10 @@ public class Handle implements IWorldGen { @Override public Transform[] getSpawnPoints(int seed) { ChunkRequest.GeneratorProfile seededProfile = this.profile.clone(); + if (this.seedOverride != null) { + seed = this.seedOverride.hashCode(); + } + seededProfile.setSeed(seed); int MAX_SPAWN_POINTS = 1000000; List positions = this.plugin.getSpawnPositions(seededProfile, 1000000); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/HandleProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/HandleProvider.java index 0656623c..5cb25624 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/HandleProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/HandleProvider.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.plugin; -import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.ChunkRequest; +import com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator.ChunkRequest; import com.hypixel.hytale.server.core.universe.world.worldgen.IWorldGen; import com.hypixel.hytale.server.core.universe.world.worldgen.WorldGenLoadException; import com.hypixel.hytale.server.core.universe.world.worldgen.provider.IWorldGenProvider; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/HytaleGenerator.java b/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/HytaleGenerator.java index ef14bc91..68814336 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/HytaleGenerator.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/plugin/HytaleGenerator.java @@ -2,40 +2,41 @@ package com.hypixel.hytale.builtin.hytalegenerator.plugin; import com.hypixel.hytale.builtin.hytalegenerator.FutureUtils; import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; -import com.hypixel.hytale.builtin.hytalegenerator.PropField; +import com.hypixel.hytale.builtin.hytalegenerator.PropRuntime; import com.hypixel.hytale.builtin.hytalegenerator.assets.AssetManager; import com.hypixel.hytale.builtin.hytalegenerator.assets.SettingsAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.positionproviders.PositionProviderAsset; import com.hypixel.hytale.builtin.hytalegenerator.assets.worldstructures.WorldStructureAsset; import com.hypixel.hytale.builtin.hytalegenerator.biome.Biome; -import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.ChunkGenerator; -import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.ChunkRequest; -import com.hypixel.hytale.builtin.hytalegenerator.chunkgenerator.FallbackGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; import com.hypixel.hytale.builtin.hytalegenerator.commands.ViewportCommand; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.CountedPixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.EntityBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.SimplePixelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.VoxelBuffer; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.BufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.bufferbundle.buffers.type.ParametrizedBufferType; +import com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator.ChunkGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator.ChunkRequest; +import com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator.FallbackGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.engine.chunkgenerator.StagedChunkGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.engine.performanceinstruments.TimeInstrument; +import com.hypixel.hytale.builtin.hytalegenerator.engine.stages.BiomeDistanceStage; +import com.hypixel.hytale.builtin.hytalegenerator.engine.stages.BiomeStage; +import com.hypixel.hytale.builtin.hytalegenerator.engine.stages.EnvironmentStage; +import com.hypixel.hytale.builtin.hytalegenerator.engine.stages.PropStage; +import com.hypixel.hytale.builtin.hytalegenerator.engine.stages.Stage; +import com.hypixel.hytale.builtin.hytalegenerator.engine.stages.TerrainStage; +import com.hypixel.hytale.builtin.hytalegenerator.engine.stages.TintStage; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.NStagedChunkGenerator; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NCountedPixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NEntityBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NSimplePixelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.NVoxelBuffer; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers.type.NParametrizedBufferType; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NBiomeDistanceStage; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NBiomeStage; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NEnvironmentStage; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NPropStage; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NStage; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NTerrainStage; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.stages.NTintStage; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.builtin.hytalegenerator.referencebundle.ReferenceBundle; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.builtin.hytalegenerator.worldstructure.WorldStructure; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.world.events.RemoveWorldEvent; @@ -56,6 +57,7 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class HytaleGenerator extends JavaPlugin { private AssetManager assetManager; @@ -101,17 +103,18 @@ public class HytaleGenerator extends JavaPlugin { PositionProvider spawnPositionProvider = worldStructureAsset.getSpawnPositionsAsset() .build(new PositionProviderAsset.Argument(seed, new ReferenceBundle(), WorkerIndexer.Id.MAIN)); List positions = new ArrayList<>(maxPositionsCount); - PositionProvider.Context context = new PositionProvider.Context( + Bounds3d bounds = new Bounds3d( new Vector3d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY), - new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY), - position -> { - if (positions.size() < maxPositionsCount) { - positions.add(position); - } - }, - null + new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY) ); - spawnPositionProvider.positionsIn(context); + PositionProvider.Context context = new PositionProvider.Context(bounds, (position, control) -> { + if (positions.size() >= maxPositionsCount) { + control.stop = true; + } else { + positions.add(position); + } + }, null); + spawnPositionProvider.generate(context); return positions; } } @@ -162,7 +165,7 @@ public class HytaleGenerator extends JavaPlugin { } @Nonnull - public NStagedChunkGenerator createStagedChunkGenerator( + public StagedChunkGenerator createStagedChunkGenerator( @Nonnull ChunkRequest.GeneratorProfile generatorProfile, @Nonnull WorldStructureAsset worldStructureAsset, @Nonnull SettingsAsset settingsAsset ) { WorkerIndexer workerIndexer = new WorkerIndexer(this.concurrency); @@ -170,6 +173,7 @@ public class HytaleGenerator extends JavaPlugin { MaterialCache materialCache = new MaterialCache(); WorkerIndexer.Session workerSession = workerIndexer.createSession(); WorkerIndexer.Data worldStructure_workerData = new WorkerIndexer.Data<>(workerIndexer.getWorkerCount(), () -> null); + TimeInstrument.Probe assetLoad_timeProbe = new TimeInstrument.Probe("Assets Loading").start(); List> futures = new ArrayList<>(); while (workerSession.hasNext()) { @@ -190,44 +194,47 @@ public class HytaleGenerator extends JavaPlugin { FutureUtils.allOf(futures).join(); worldStructureAsset.cleanUp(); - NStagedChunkGenerator.Builder generatorBuilder = new NStagedChunkGenerator.Builder(); + assetLoad_timeProbe.stop(); + String assetLoadingTime_ms = LoggerUtil.nsToMsDecimal(assetLoad_timeProbe.getTotalTime_ns()); + LoggerUtil.getLogger().info("Loaded World Structure " + generatorProfile.worldStructureName() + ": " + assetLoadingTime_ms + " ms"); + StagedChunkGenerator.Builder generatorBuilder = new StagedChunkGenerator.Builder(); WorldStructure worldStructure_worker0 = worldStructure_workerData.get(workerIndexer.createSession().next()); List allBiomes = worldStructure_worker0.getBiomeRegistry().getAllValues(); List allRuntimes = new ArrayList<>(getAllPossibleRuntimeIndices(allBiomes)); allRuntimes.sort(Comparator.naturalOrder()); int bufferTypeIndexCounter = 0; - NParametrizedBufferType biome_bufferType = new NParametrizedBufferType( - "Biome", bufferTypeIndexCounter++, NBiomeStage.bufferClass, NBiomeStage.biomeClass, () -> new NCountedPixelBuffer<>(NBiomeStage.biomeClass) + ParametrizedBufferType biome_bufferType = new ParametrizedBufferType( + "Biome", bufferTypeIndexCounter++, BiomeStage.bufferClass, BiomeStage.biomeClass, () -> new CountedPixelBuffer<>(BiomeStage.biomeClass) ); - NStage biomeStage = new NBiomeStage("BiomeStage", biome_bufferType, worldStructure_workerData); + Stage biomeStage = new BiomeStage("BiomeStage", biome_bufferType, worldStructure_workerData); generatorBuilder.appendStage(biomeStage); - NParametrizedBufferType biomeDistance_bufferType = new NParametrizedBufferType( + ParametrizedBufferType biomeDistance_bufferType = new ParametrizedBufferType( "BiomeDistance", bufferTypeIndexCounter++, - NBiomeDistanceStage.biomeDistanceBufferClass, - NBiomeDistanceStage.biomeDistanceClass, - () -> new NSimplePixelBuffer<>(NBiomeDistanceStage.biomeDistanceClass) + BiomeDistanceStage.biomeDistanceBufferClass, + BiomeDistanceStage.biomeDistanceClass, + () -> new SimplePixelBuffer<>(BiomeDistanceStage.biomeDistanceClass) ); int MAX_BIOME_DISTANCE_RADIUS = 512; int interpolationRadius = Math.clamp((long)(worldStructure_worker0.getBiomeTransitionDistance() / 2), 0, 512); int biomeEdgeRadius = Math.clamp((long)worldStructure_worker0.getMaxBiomeEdgeDistance(), 0, 512); int maxDistance = Math.max(interpolationRadius, biomeEdgeRadius); - NStage biomeDistanceStage = new NBiomeDistanceStage("BiomeDistanceStage", biome_bufferType, biomeDistance_bufferType, maxDistance); + Stage biomeDistanceStage = new BiomeDistanceStage("BiomeDistanceStage", biome_bufferType, biomeDistance_bufferType, maxDistance); generatorBuilder.appendStage(biomeDistanceStage); int materialBufferIndexCounter = 0; - NParametrizedBufferType material0_bufferType = generatorBuilder.MATERIAL_OUTPUT_BUFFER_TYPE; + ParametrizedBufferType material0_bufferType = generatorBuilder.MATERIAL_OUTPUT_BUFFER_TYPE; if (!allRuntimes.isEmpty()) { - material0_bufferType = new NParametrizedBufferType( + material0_bufferType = new ParametrizedBufferType( "Material" + materialBufferIndexCounter, bufferTypeIndexCounter++, - NTerrainStage.materialBufferClass, - NTerrainStage.materialClass, - () -> new NVoxelBuffer<>(NTerrainStage.materialClass) + TerrainStage.materialBufferClass, + TerrainStage.materialClass, + () -> new VoxelBuffer<>(TerrainStage.materialClass) ); materialBufferIndexCounter++; } - NStage terrainStage = new NTerrainStage( + Stage terrainStage = new TerrainStage( "TerrainStage", biome_bufferType, biomeDistance_bufferType, @@ -238,23 +245,23 @@ public class HytaleGenerator extends JavaPlugin { worldStructure_workerData ); generatorBuilder.appendStage(terrainStage); - NParametrizedBufferType materialInput_bufferType = material0_bufferType; - NBufferType entityInput_bufferType = null; + ParametrizedBufferType materialInput_bufferType = material0_bufferType; + BufferType entityInput_bufferType = null; for (int i = 0; i < allRuntimes.size() - 1; i++) { int runtime = allRuntimes.get(i); String runtimeString = Integer.toString(runtime); - NParametrizedBufferType materialOutput_bufferType = new NParametrizedBufferType( + ParametrizedBufferType materialOutput_bufferType = new ParametrizedBufferType( "Material" + materialBufferIndexCounter, bufferTypeIndexCounter++, - NTerrainStage.materialBufferClass, - NTerrainStage.materialClass, - () -> new NVoxelBuffer<>(NTerrainStage.materialClass) + TerrainStage.materialBufferClass, + TerrainStage.materialClass, + () -> new VoxelBuffer<>(TerrainStage.materialClass) ); - NBufferType entityOutput_bufferType = new NBufferType( - "Entity" + materialBufferIndexCounter, bufferTypeIndexCounter++, NEntityBuffer.class, NEntityBuffer::new + BufferType entityOutput_bufferType = new BufferType( + "Entity" + materialBufferIndexCounter, bufferTypeIndexCounter++, EntityBuffer.class, EntityBuffer::new ); - NStage propStage = new NPropStage( + Stage propStage = new PropStage( "PropStage" + runtimeString, biome_bufferType, biomeDistance_bufferType, @@ -275,7 +282,7 @@ public class HytaleGenerator extends JavaPlugin { if (!allRuntimes.isEmpty()) { int runtime = allRuntimes.getLast(); String runtimeString = Integer.toString(runtime); - NStage propStage = new NPropStage( + Stage propStage = new PropStage( "PropStage" + runtimeString, biome_bufferType, biomeDistance_bufferType, @@ -290,9 +297,9 @@ public class HytaleGenerator extends JavaPlugin { generatorBuilder.appendStage(propStage); } - NStage tintStage = new NTintStage("TintStage", biome_bufferType, generatorBuilder.TINT_OUTPUT_BUFFER_TYPE, worldStructure_workerData); + Stage tintStage = new TintStage("TintStage", biome_bufferType, generatorBuilder.TINT_OUTPUT_BUFFER_TYPE, worldStructure_workerData); generatorBuilder.appendStage(tintStage); - NStage environmentStage = new NEnvironmentStage( + Stage environmentStage = new EnvironmentStage( "EnvironmentStage", biome_bufferType, generatorBuilder.ENVIRONMENT_OUTPUT_BUFFER_TYPE, worldStructure_workerData ); generatorBuilder.appendStage(environmentStage); @@ -313,8 +320,8 @@ public class HytaleGenerator extends JavaPlugin { Set allRuntimes = new HashSet<>(); for (Biome biome : biomes) { - for (PropField propField : biome.getPropFields()) { - allRuntimes.add(propField.getRuntime()); + for (PropRuntime propRuntime : biome.getPropRuntimes()) { + allRuntimes.add(propRuntime.getRuntimeIndex()); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/AnchorPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/AnchorPositionProvider.java index 456ce259..dc801c19 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/AnchorPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/AnchorPositionProvider.java @@ -1,39 +1,75 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import java.util.Objects; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; public class AnchorPositionProvider extends PositionProvider { @Nonnull private final PositionProvider positionProvider; private final boolean isReversed; + @Nonnull + private final Bounds3d rOffsetBounds; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private final Vector3d rNewPosition; + @Nonnull + private final Vector3d rAnchor; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(AnchorPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + Vector3d newPoint = new Vector3d(position); + AnchorPositionProvider.this.rNewPosition.set(position); + if (AnchorPositionProvider.this.isReversed) { + AnchorPositionProvider.this.rNewPosition.set(AnchorPositionProvider.this.rAnchor); + } else { + AnchorPositionProvider.this.rNewPosition.add(AnchorPositionProvider.this.rAnchor); + } + + if (AnchorPositionProvider.this.rContext.bounds.contains(newPoint)) { + AnchorPositionProvider.this.rContext.pipe.accept(newPoint, control); + } + } + }; public AnchorPositionProvider(@Nonnull PositionProvider positionProvider, boolean isReversed) { this.positionProvider = positionProvider; this.isReversed = isReversed; + this.rOffsetBounds = new Bounds3d(); + this.rChildContext = new PositionProvider.Context(); + this.rNewPosition = new Vector3d(); + this.rAnchor = new Vector3d(); + this.rContext = new PositionProvider.Context(); } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { + public void generate(@Nonnull PositionProvider.Context context) { + this.rContext = context; if (context != null) { Vector3d anchor = context.anchor; if (anchor != null) { - Vector3d offsetMin = this.isReversed ? context.minInclusive.clone().add(anchor) : context.minInclusive.clone().addScaled(anchor, -1.0); - Vector3d offsetMax = this.isReversed ? context.maxExclusive.clone().add(anchor) : context.maxExclusive.clone().addScaled(anchor, -1.0); - PositionProvider.Context childContext = new PositionProvider.Context(offsetMin, offsetMax, p -> { - Vector3d newPoint = p.clone(); - if (this.isReversed) { - newPoint.addScaled(anchor, -1.0); - } else { - newPoint.add(anchor); - } + this.rOffsetBounds.assign(context.bounds); + if (this.isReversed) { + this.rOffsetBounds.offset(anchor); + } else { + this.rOffsetBounds.offsetOpposite(anchor); + } - if (VectorUtil.isInside(newPoint, context.minInclusive, context.maxExclusive)) { - context.consumer.accept(newPoint); - } - }, context.anchor); - this.positionProvider.positionsIn(childContext); + this.rChildContext.assign(context); + this.rChildContext.bounds = this.rOffsetBounds; + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/BaseHeightPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/BaseHeightPositionProvider.java index 390bbb09..62289ba3 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/BaseHeightPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/BaseHeightPositionProvider.java @@ -1,8 +1,11 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import java.util.Objects; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; public class BaseHeightPositionProvider extends PositionProvider { @Nonnull @@ -11,6 +14,26 @@ public class BaseHeightPositionProvider extends PositionProvider { private final double minYInput; @Nonnull private final PositionProvider positionProvider; + @Nonnull + private final Vector3d rOffsetPosition; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(BaseHeightPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + BaseHeightPositionProvider.this.rOffsetPosition.set(position); + BaseHeightPositionProvider.this.rOffsetPosition.y = BaseHeightPositionProvider.this.rOffsetPosition.y + BaseHeightPositionProvider.this.baseHeight; + if (BaseHeightPositionProvider.this.rContext.bounds.contains(BaseHeightPositionProvider.this.rOffsetPosition)) { + BaseHeightPositionProvider.this.rContext.pipe.accept(BaseHeightPositionProvider.this.rOffsetPosition, control); + } + } + }; public BaseHeightPositionProvider(double baseHeight, @Nonnull PositionProvider positionProvider, double minYInput, double maxYInput) { maxYInput = Math.max(minYInput, maxYInput); @@ -18,18 +41,16 @@ public class BaseHeightPositionProvider extends PositionProvider { this.positionProvider = positionProvider; this.maxYInput = maxYInput; this.minYInput = minYInput; + this.rOffsetPosition = new Vector3d(); + this.rContext = new PositionProvider.Context(); + this.rChildContext = new PositionProvider.Context(); } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - PositionProvider.Context childContext = new PositionProvider.Context(context); - childContext.consumer = position -> { - Vector3d offsetP = position.clone(); - offsetP.y = offsetP.y + this.baseHeight; - if (VectorUtil.isInside(offsetP, context.minInclusive, context.maxExclusive)) { - context.consumer.accept(offsetP); - } - }; - this.positionProvider.positionsIn(childContext); + public void generate(@Nonnull PositionProvider.Context context) { + this.rContext = context; + this.rChildContext.assign(context); + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/BoundPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/BoundPositionProvider.java index 3ce37b28..f03a0589 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/BoundPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/BoundPositionProvider.java @@ -7,17 +7,19 @@ public class BoundPositionProvider extends PositionProvider { @Nonnull private final PositionProvider positionProvider; private final Bounds3d bounds; + @Nonnull + private final PositionProvider.Context rChildContext; public BoundPositionProvider(@Nonnull PositionProvider positionProvider, @Nonnull Bounds3d bounds) { this.positionProvider = positionProvider; this.bounds = bounds; + this.rChildContext = new PositionProvider.Context(); } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - PositionProvider.Context childContext = new PositionProvider.Context(context); - childContext.minInclusive = this.bounds.min; - childContext.maxExclusive = this.bounds.max; - this.positionProvider.positionsIn(childContext); + public void generate(@Nonnull PositionProvider.Context context) { + this.rChildContext.assign(context); + this.rChildContext.bounds.assign(this.bounds); + this.positionProvider.generate(this.rChildContext); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ClustersPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ClustersPositionProvider.java new file mode 100644 index 00000000..e4a2999c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ClustersPositionProvider.java @@ -0,0 +1,88 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class ClustersPositionProvider extends PositionProvider { + @Nonnull + private final PositionProvider clusterPositions; + @Nonnull + private final PositionProvider distributorPositions; + @Nonnull + private final Bounds3d clusterBounds; + private final Bounds3d clusterBoundsFlipped; + @Nonnull + private final Bounds3d rDistributionBounds; + @Nonnull + private final PositionProvider.Context rDistributionContext; + @Nonnull + private final Bounds3d rClusterBounds; + @Nonnull + private final PositionProvider.Context rClusterContext; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private Control rControl; + @Nonnull + private final Pipe.One rDistributionPipe = new Pipe.One() { + { + Objects.requireNonNull(ClustersPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d clusterAnchor, @NonNullDecl Control control) { + ClustersPositionProvider.this.rClusterBounds.assign(ClustersPositionProvider.this.clusterBounds); + ClustersPositionProvider.this.rClusterBounds.offset(clusterAnchor); + ClustersPositionProvider.this.rClusterBounds.intersect(ClustersPositionProvider.this.rContext.bounds); + ClustersPositionProvider.this.rClusterContext.assign(ClustersPositionProvider.this.rContext); + ClustersPositionProvider.this.rClusterContext.bounds = ClustersPositionProvider.this.rClusterBounds; + ClustersPositionProvider.this.rClusterContext.anchor = clusterAnchor; + ClustersPositionProvider.this.rControl = control; + ClustersPositionProvider.this.rClusterContext.pipe = ClustersPositionProvider.this.rClusterPipe; + ClustersPositionProvider.this.clusterPositions.generate(ClustersPositionProvider.this.rClusterContext); + } + }; + @Nonnull + private final Pipe.One rClusterPipe = new Pipe.One() { + { + Objects.requireNonNull(ClustersPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + if (control.stop) { + ClustersPositionProvider.this.rControl.stop = true; + } else { + ClustersPositionProvider.this.rContext.pipe.accept(position, control); + } + } + }; + + public ClustersPositionProvider(@Nonnull PositionProvider clusterPositions, @Nonnull PositionProvider distributorPositions, @Nonnull Bounds3d clusterBounds) { + this.clusterPositions = clusterPositions; + this.distributorPositions = distributorPositions; + this.clusterBounds = clusterBounds.clone(); + this.clusterBoundsFlipped = this.clusterBounds.clone(); + this.clusterBoundsFlipped.flipOnOriginPoint(); + this.rDistributionBounds = new Bounds3d(); + this.rDistributionContext = new PositionProvider.Context(); + this.rClusterBounds = new Bounds3d(); + this.rClusterContext = new PositionProvider.Context(); + this.rContext = new PositionProvider.Context(); + this.rControl = new Control(); + } + + @Override + public void generate(@NonNullDecl PositionProvider.Context context) { + this.rContext = context; + this.rDistributionBounds.assign(context.bounds); + this.rDistributionBounds.stack(this.clusterBoundsFlipped); + this.rDistributionContext.assign(context); + this.rDistributionContext.bounds = this.rDistributionBounds; + this.rDistributionContext.pipe = this.rDistributionPipe; + this.distributorPositions.generate(this.rDistributionContext); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/EmptyPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/EmptyPositionProvider.java new file mode 100644 index 00000000..c164b295 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/EmptyPositionProvider.java @@ -0,0 +1,11 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; + +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class EmptyPositionProvider extends PositionProvider { + public static final EmptyPositionProvider INSTANCE = new EmptyPositionProvider(); + + @Override + public void generate(@NonNullDecl PositionProvider.Context context) { + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/FieldFunctionOccurrencePositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/FieldFunctionOccurrencePositionProvider.java index b3325011..6f17154e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/FieldFunctionOccurrencePositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/FieldFunctionOccurrencePositionProvider.java @@ -1,9 +1,14 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; import com.hypixel.hytale.math.util.FastRandom; +import java.util.Objects; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; public class FieldFunctionOccurrencePositionProvider extends PositionProvider { public static final double FP_RESOLUTION = 100.0; @@ -12,27 +17,49 @@ public class FieldFunctionOccurrencePositionProvider extends PositionProvider { @Nonnull private final PositionProvider positionProvider; @Nonnull - private final SeedGenerator seedGenerator; + private final RngField rngField; + @Nonnull + private final FastRandom rRandom; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private final Density.Context rDensityContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(FieldFunctionOccurrencePositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + FieldFunctionOccurrencePositionProvider.this.rDensityContext.position = position; + FieldFunctionOccurrencePositionProvider.this.rDensityContext.positionsAnchor = FieldFunctionOccurrencePositionProvider.this.rContext.anchor; + FieldFunctionOccurrencePositionProvider.this.rDensityContext.densityAnchor = FieldFunctionOccurrencePositionProvider.this.rContext.anchor; + double discardChance = 1.0 - FieldFunctionOccurrencePositionProvider.this.field.process(FieldFunctionOccurrencePositionProvider.this.rDensityContext); + FieldFunctionOccurrencePositionProvider.this.rRandom + .setSeed(FieldFunctionOccurrencePositionProvider.this.rngField.get(position.x, position.y, position.z)); + if (!(discardChance > FieldFunctionOccurrencePositionProvider.this.rRandom.nextDouble())) { + FieldFunctionOccurrencePositionProvider.this.rContext.pipe.accept(position, control); + } + } + }; public FieldFunctionOccurrencePositionProvider(@Nonnull Density field, @Nonnull PositionProvider positionProvider, int seed) { this.field = field; this.positionProvider = positionProvider; - this.seedGenerator = new SeedGenerator(seed); + this.rngField = new RngField(seed); + this.rChildContext = new PositionProvider.Context(); + this.rRandom = new FastRandom(); + this.rContext = new PositionProvider.Context(); + this.rDensityContext = new Density.Context(); } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - PositionProvider.Context childContext = new PositionProvider.Context(context); - childContext.consumer = position -> { - Density.Context densityContext = new Density.Context(); - densityContext.position = position; - densityContext.positionsAnchor = context.anchor; - double discardChance = 1.0 - this.field.process(densityContext); - FastRandom random = new FastRandom(this.seedGenerator.seedAt(position.x, position.y, position.z, 100.0)); - if (!(discardChance > random.nextDouble())) { - context.consumer.accept(position); - } - }; - this.positionProvider.positionsIn(childContext); + public void generate(@Nonnull PositionProvider.Context context) { + this.rContext = context; + this.rChildContext.assign(context); + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/FieldFunctionPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/FieldFunctionPositionProvider.java index 8eb8d2b9..efbd50e4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/FieldFunctionPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/FieldFunctionPositionProvider.java @@ -1,9 +1,14 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; public class FieldFunctionPositionProvider extends PositionProvider { @Nonnull @@ -12,11 +17,47 @@ public class FieldFunctionPositionProvider extends PositionProvider { private final List delimiters; @Nonnull private final PositionProvider positionProvider; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private final Density.Context rDensityContext; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(FieldFunctionPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + FieldFunctionPositionProvider.this.rDensityContext.position = position; + FieldFunctionPositionProvider.this.rDensityContext.positionsAnchor = FieldFunctionPositionProvider.this.rContext.anchor; + double value = FieldFunctionPositionProvider.this.field.process(FieldFunctionPositionProvider.this.rDensityContext); + + for (FieldFunctionPositionProvider.Delimiter delimiter : FieldFunctionPositionProvider.this.delimiters) { + if (delimiter.isInside(value)) { + FieldFunctionPositionProvider.this.rContext.pipe.accept(position, control); + return; + } + } + } + }; public FieldFunctionPositionProvider(@Nonnull Density field, @Nonnull PositionProvider positionProvider) { this.field = field; this.positionProvider = positionProvider; this.delimiters = new ArrayList<>(); + this.rChildContext = new PositionProvider.Context(); + this.rDensityContext = new Density.Context(); + this.rContext = new PositionProvider.Context(); + } + + @Override + public void generate(@Nonnull PositionProvider.Context context) { + this.rContext = context; + this.rChildContext.assign(context); + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); } public void addDelimiter(double min, double max) { @@ -26,25 +67,6 @@ public class FieldFunctionPositionProvider extends PositionProvider { this.delimiters.add(d); } - @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - PositionProvider.Context childContext = new PositionProvider.Context(context); - childContext.consumer = p -> { - Density.Context densityContext = new Density.Context(); - densityContext.position = p; - densityContext.positionsAnchor = context.anchor; - double value = this.field.process(densityContext); - - for (FieldFunctionPositionProvider.Delimiter d : this.delimiters) { - if (d.isInside(value)) { - context.consumer.accept(p); - return; - } - } - }; - this.positionProvider.positionsIn(childContext); - } - private static class Delimiter { double min; double max; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Jitter2dPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Jitter2dPositionProvider.java new file mode 100644 index 00000000..1e934dda --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Jitter2dPositionProvider.java @@ -0,0 +1,71 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.math.util.FastRandom; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class Jitter2dPositionProvider extends PositionProvider { + private static final double SEED_GENERATOR_RESOLUTION = 10.0; + private final double magnitude; + @Nonnull + private final PositionProvider positionProvider; + @Nonnull + private final RngField rngField; + @Nonnull + private final FastRandom random; + @Nonnull + private final Vector3d rVector; + @Nonnull + private final Bounds3d rBounds; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(Jitter2dPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + int localSeed = Jitter2dPositionProvider.this.rngField.get(position.x, position.y, position.z); + Jitter2dPositionProvider.this.random.setSeed(localSeed); + double radius = Jitter2dPositionProvider.this.magnitude * Math.sqrt(Jitter2dPositionProvider.this.random.nextDouble()); + double theta = Jitter2dPositionProvider.this.random.nextDouble() * 2.0 * Math.PI; + Jitter2dPositionProvider.this.rVector.set(radius * Math.cos(theta), 0.0, radius * Math.sin(theta)); + position.add(Jitter2dPositionProvider.this.rVector); + if (Jitter2dPositionProvider.this.rContext.bounds.contains(position)) { + Jitter2dPositionProvider.this.rContext.pipe.accept(position, control); + } + } + }; + + public Jitter2dPositionProvider(double magnitude, int seed, @Nonnull PositionProvider positionProvider) { + this.magnitude = Math.abs(magnitude); + this.positionProvider = positionProvider; + this.rngField = new RngField(seed); + this.random = new FastRandom(); + this.rVector = new Vector3d(); + this.rBounds = new Bounds3d(); + this.rChildContext = new PositionProvider.Context(); + this.rContext = new PositionProvider.Context(); + } + + @Override + public void generate(@Nonnull PositionProvider.Context context) { + this.rContext = context; + this.rBounds.assign(context.bounds); + this.rBounds.min.add(-this.magnitude, 0.0, -this.magnitude); + this.rBounds.max.add(this.magnitude, 0.0, this.magnitude); + this.rChildContext.assign(context); + this.rChildContext.bounds = this.rBounds; + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Jitter3dPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Jitter3dPositionProvider.java new file mode 100644 index 00000000..96a3572f --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Jitter3dPositionProvider.java @@ -0,0 +1,76 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.math.util.FastRandom; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class Jitter3dPositionProvider extends PositionProvider { + private static final float PI = (float) Math.PI; + private static final double SEED_GENERATOR_RESOLUTION = 10.0; + private final double magnitude; + @Nonnull + private final PositionProvider positionProvider; + @Nonnull + private final RngField rngField; + @Nonnull + private final FastRandom random; + @Nonnull + private final Vector3d rVector; + @Nonnull + private final Bounds3d rBounds; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(Jitter3dPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + Jitter3dPositionProvider.this.random.setSeed(Jitter3dPositionProvider.this.rngField.get(position.x, position.y, position.z)); + double radius = Jitter3dPositionProvider.this.magnitude * Math.sqrt(Jitter3dPositionProvider.this.random.nextDouble()); + float rotationX = Jitter3dPositionProvider.this.random.nextFloat() * 2.0F * (float) Math.PI; + float rotationY = Jitter3dPositionProvider.this.random.nextFloat() * 2.0F * (float) Math.PI; + float rotationZ = Jitter3dPositionProvider.this.random.nextFloat() * 2.0F * (float) Math.PI; + Jitter3dPositionProvider.this.rVector.set(radius, 0.0, 0.0); + Jitter3dPositionProvider.this.rVector.rotateX(rotationX); + Jitter3dPositionProvider.this.rVector.rotateY(rotationY); + Jitter3dPositionProvider.this.rVector.rotateZ(rotationZ); + position.add(Jitter3dPositionProvider.this.rVector); + if (Jitter3dPositionProvider.this.rContext.bounds.contains(position)) { + Jitter3dPositionProvider.this.rContext.pipe.accept(position, control); + } + } + }; + + public Jitter3dPositionProvider(double magnitude, int seed, @Nonnull PositionProvider positionProvider) { + this.magnitude = Math.abs(magnitude); + this.positionProvider = positionProvider; + this.rngField = new RngField(seed); + this.random = new FastRandom(); + this.rVector = new Vector3d(); + this.rBounds = new Bounds3d(); + this.rChildContext = new PositionProvider.Context(); + this.rContext = new PositionProvider.Context(); + } + + @Override + public void generate(@Nonnull PositionProvider.Context context) { + this.rContext = context; + this.rBounds.assign(context.bounds); + this.rBounds.min.add(-this.magnitude, -this.magnitude, -this.magnitude); + this.rBounds.max.add(this.magnitude, this.magnitude, this.magnitude); + this.rChildContext.assign(context); + this.rChildContext.bounds = this.rBounds; + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ListPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ListPositionProvider.java index 4f2ed345..fe8985f3 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ListPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ListPositionProvider.java @@ -1,46 +1,33 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ListPositionProvider extends PositionProvider { - private List positions3i; - private List positions3d; - - private ListPositionProvider() { - } - + private List positions = new ArrayList<>(); @Nonnull - public static ListPositionProvider from3i(@Nonnull List positions3i) { - ListPositionProvider instance = new ListPositionProvider(); - instance.positions3i = new ArrayList<>(); - instance.positions3i.addAll(positions3i); - instance.positions3d = new ArrayList<>(positions3i.size()); - instance.positions3i.forEach(p -> instance.positions3d.add(p.toVector3d())); - return instance; - } + private final Control rControl; - @Nonnull - public static ListPositionProvider from3d(@Nonnull List positions3d) { - ListPositionProvider instance = new ListPositionProvider(); - instance.positions3d = new ArrayList<>(); - instance.positions3d.addAll(positions3d); - instance.positions3i = new ArrayList<>(positions3d.size()); - instance.positions3d.forEach(p -> instance.positions3i.add(p.toVector3i())); - return instance; + public ListPositionProvider(@Nonnull List positions) { + positions.forEach(p -> this.positions.add(new Vector3d(p))); + this.rControl = new Control(); } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - for (Vector3d p : this.positions3d) { - if (VectorUtil.isInside(p, context.minInclusive, context.maxExclusive)) { + public void generate(@Nonnull PositionProvider.Context context) { + this.rControl.reset(); + + for (Vector3d p : this.positions) { + if (this.rControl.stop) { + return; } - context.consumer.accept(p); + if (context.bounds.contains(p)) { + context.pipe.accept(p, this.rControl); + } } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Mesh2DPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Mesh2DPositionProvider.java deleted file mode 100644 index e138277a..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Mesh2DPositionProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; - -import com.hypixel.hytale.builtin.hytalegenerator.fields.points.PointProvider; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; -import javax.annotation.Nonnull; - -public class Mesh2DPositionProvider extends PositionProvider { - @Nonnull - private final PointProvider pointGenerator; - private final int y; - - public Mesh2DPositionProvider(@Nonnull PointProvider positionProvider, int y) { - this.pointGenerator = positionProvider; - this.y = y; - } - - @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - if (!(context.minInclusive.y > this.y) && !(context.maxExclusive.y <= this.y)) { - Vector2d min2d = new Vector2d(context.minInclusive.x, context.minInclusive.z); - Vector2d max2d = new Vector2d(context.maxExclusive.x, context.maxExclusive.z); - this.pointGenerator.points2d(min2d, max2d, point -> context.consumer.accept(new Vector3d(point.x, this.y, point.y))); - } - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Mesh3DPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Mesh3DPositionProvider.java deleted file mode 100644 index a9a98d42..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/Mesh3DPositionProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; - -import com.hypixel.hytale.builtin.hytalegenerator.fields.points.PointProvider; -import javax.annotation.Nonnull; - -public class Mesh3DPositionProvider extends PositionProvider { - @Nonnull - private final PointProvider pointGenerator; - - public Mesh3DPositionProvider(@Nonnull PointProvider positionProvider) { - this.pointGenerator = positionProvider; - } - - @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - this.pointGenerator.points3d(context.minInclusive, context.maxExclusive, context.consumer); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/OffsetPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/OffsetPositionProvider.java index 4509923e..ac972160 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/OffsetPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/OffsetPositionProvider.java @@ -1,43 +1,52 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import java.util.Objects; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; public class OffsetPositionProvider extends PositionProvider { @Nonnull - private final Vector3i offset3i; - @Nonnull - private final Vector3d offset3d; + private final Vector3d vector; @Nonnull private final PositionProvider positionProvider; + @Nonnull + private final Bounds3d rBounds; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(OffsetPositionProvider.this); + } - public OffsetPositionProvider(@Nonnull Vector3i offset, @Nonnull PositionProvider positionProvider) { - this.offset3i = offset.clone(); - this.positionProvider = positionProvider; - this.offset3d = this.offset3i.toVector3d(); - } + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + position.add(OffsetPositionProvider.this.vector); + OffsetPositionProvider.this.rContext.pipe.accept(position, control); + } + }; - public OffsetPositionProvider(@Nonnull Vector3d offset, @Nonnull PositionProvider positionProvider) { - this.offset3d = offset.clone(); + public OffsetPositionProvider(@Nonnull Vector3d vector, @Nonnull PositionProvider positionProvider) { + this.vector = new Vector3d(vector); this.positionProvider = positionProvider; - this.offset3i = this.offset3d.toVector3i(); + this.rBounds = new Bounds3d(); + this.rChildContext = new PositionProvider.Context(); + this.rContext = new PositionProvider.Context(); } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - Vector3d windowMin = context.minInclusive.clone(); - Vector3d windowMax = context.maxExclusive.clone(); - windowMin.subtract(this.offset3d); - windowMax.subtract(this.offset3d); - PositionProvider.Context childContext = new PositionProvider.Context(); - childContext.minInclusive = windowMin; - childContext.maxExclusive = windowMax; - childContext.consumer = p -> { - Vector3d offsetP = p.clone(); - offsetP.add(this.offset3d); - context.consumer.accept(offsetP); - }; - this.positionProvider.positionsIn(childContext); + public void generate(@Nonnull PositionProvider.Context context) { + this.rContext = context; + this.rBounds.assign(context.bounds); + this.rBounds.offsetOpposite(this.vector); + this.rChildContext.assign(context); + this.rChildContext.bounds = this.rBounds; + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/PositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/PositionProvider.java index c60f344a..a7f62c51 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/PositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/PositionProvider.java @@ -1,57 +1,49 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; -import com.hypixel.hytale.math.vector.Vector3d; -import java.util.function.Consumer; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.propdistributions.PropDistribution; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class PositionProvider { - public abstract void positionsIn(@Nonnull PositionProvider.Context var1); - - @Nonnull - public static PositionProvider noPositionProvider() { - return new PositionProvider() { - @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - } - }; - } + public abstract void generate(@Nonnull PositionProvider.Context var1); public static class Context { - @Nonnull - public static final Consumer EMPTY_CONSUMER = p -> {}; - public Vector3d minInclusive; - public Vector3d maxExclusive; - public Consumer consumer; + public Bounds3d bounds; + public Pipe.One pipe; @Nullable public Vector3d anchor; public Context() { - this.minInclusive = Vector3d.ZERO; - this.maxExclusive = Vector3d.ZERO; - this.consumer = EMPTY_CONSUMER; + this.bounds = new Bounds3d(); + this.pipe = Pipe.getEmptyOne(); this.anchor = null; } - public Context(@Nonnull Vector3d minInclusive, @Nonnull Vector3d maxExclusive, @Nonnull Consumer consumer, @Nullable Vector3d anchor) { - this.minInclusive = minInclusive; - this.maxExclusive = maxExclusive; - this.consumer = consumer; + public Context(@Nonnull Bounds3d bounds, @Nonnull Pipe.One pipe, @Nullable Vector3d anchor) { + this.bounds = bounds; + this.pipe = pipe; this.anchor = anchor; } public Context(@Nonnull PositionProvider.Context other) { - this.minInclusive = other.minInclusive; - this.maxExclusive = other.maxExclusive; - this.consumer = other.consumer; + this.bounds = other.bounds; + this.pipe = other.pipe; this.anchor = other.anchor; } public void assign(@Nonnull PositionProvider.Context other) { - this.minInclusive = other.minInclusive; - this.maxExclusive = other.maxExclusive; - this.consumer = other.consumer; + this.bounds = other.bounds; + this.pipe = other.pipe; this.anchor = other.anchor; } + + public void assign(@Nonnull PropDistribution.Context other) { + this.bounds.assign(other.bounds); + this.pipe = Pipe.getEmptyOne(); + this.anchor = null; + } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ScalerPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ScalerPositionProvider.java new file mode 100644 index 00000000..d8bb643b --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/ScalerPositionProvider.java @@ -0,0 +1,56 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class ScalerPositionProvider extends PositionProvider { + @Nonnull + private final Vector3d scale; + @Nonnull + private final Vector3d inverseScale; + @Nonnull + private final PositionProvider positionProvider; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private final Bounds3d rChildBounds; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(ScalerPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + position.mul(ScalerPositionProvider.this.scale); + ScalerPositionProvider.this.rContext.pipe.accept(position, control); + } + }; + + public ScalerPositionProvider(@Nonnull Vector3d scale, @Nonnull PositionProvider positionProvider) { + this.scale = new Vector3d(scale); + this.inverseScale = new Vector3d(1.0 / scale.x, 1.0 / scale.y, 1.0 / scale.z); + this.positionProvider = positionProvider; + this.rChildContext = new PositionProvider.Context(); + this.rChildBounds = new Bounds3d(); + this.rContext = new PositionProvider.Context(); + } + + @Override + public void generate(@NonNullDecl PositionProvider.Context context) { + this.rContext = context; + this.rChildBounds.assign(context.bounds); + this.rChildBounds.min.mul(this.inverseScale); + this.rChildBounds.max.mul(this.inverseScale); + this.rChildContext.assign(context); + this.rChildContext.bounds = this.rChildBounds; + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SimpleHorizontalPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SimpleHorizontalPositionProvider.java index 2623bfc6..eca821bb 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SimpleHorizontalPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SimpleHorizontalPositionProvider.java @@ -1,27 +1,47 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; import com.hypixel.hytale.builtin.hytalegenerator.delimiters.RangeDouble; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import java.util.Objects; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; public class SimpleHorizontalPositionProvider extends PositionProvider { @Nonnull private final RangeDouble rangeY; @Nonnull private final PositionProvider positionProvider; + @Nonnull + private final PositionProvider.Context rChildContext; + @Nonnull + private PositionProvider.Context rContext; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(SimpleHorizontalPositionProvider.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + if (SimpleHorizontalPositionProvider.this.rangeY.contains(position.y)) { + SimpleHorizontalPositionProvider.this.rContext.pipe.accept(position, control); + } + } + }; public SimpleHorizontalPositionProvider(@Nonnull RangeDouble rangeY, @Nonnull PositionProvider positionProvider) { this.rangeY = rangeY; this.positionProvider = positionProvider; + this.rContext = new PositionProvider.Context(); + this.rChildContext = new PositionProvider.Context(); } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { - PositionProvider.Context childContext = new PositionProvider.Context(context); - childContext.consumer = positions -> { - if (this.rangeY.contains(positions.y)) { - context.consumer.accept(positions); - } - }; - this.positionProvider.positionsIn(childContext); + public void generate(@Nonnull PositionProvider.Context context) { + this.rContext = context; + this.rChildContext.assign(context); + this.rChildContext.pipe = this.rChildPipe; + this.positionProvider.generate(this.rChildContext); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SquareGrid2dPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SquareGrid2dPositionProvider.java new file mode 100644 index 00000000..fadc5325 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SquareGrid2dPositionProvider.java @@ -0,0 +1,47 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class SquareGrid2dPositionProvider extends PositionProvider { + private static final double Y = 0.0; + @Nonnull + private final Vector3d rPosition = new Vector3d(); + @Nonnull + private final Bounds3d rGridBounds = new Bounds3d(); + @Nonnull + private final Control rControl = new Control(); + + @Override + public void generate(@NonNullDecl PositionProvider.Context context) { + if (!(context.bounds.min.y > 0.0) && !(context.bounds.max.y <= 0.0)) { + this.rGridBounds.min.set(Math.floor(context.bounds.min.x), 0.0, Math.floor(context.bounds.min.z)); + this.rGridBounds.max.set(Math.ceil(context.bounds.max.x), 1.0, Math.ceil(context.bounds.max.z)); + if (this.rGridBounds.min.x < context.bounds.min.x) { + this.rGridBounds.min.x++; + } + + if (this.rGridBounds.min.z < context.bounds.min.z) { + this.rGridBounds.min.z++; + } + + this.rControl.reset(); + + for (double x = this.rGridBounds.min.x; x < this.rGridBounds.max.x; x++) { + for (double z = this.rGridBounds.min.z; z < this.rGridBounds.max.z; z++) { + assert context.bounds.contains(x, 0.0, z); + + if (this.rControl.stop) { + return; + } + + this.rPosition.set(x, 0.0, z); + context.pipe.accept(this.rPosition, this.rControl); + } + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SquareGrid3dPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SquareGrid3dPositionProvider.java new file mode 100644 index 00000000..3b2d839d --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/SquareGrid3dPositionProvider.java @@ -0,0 +1,50 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class SquareGrid3dPositionProvider extends PositionProvider { + @Nonnull + private final Vector3d rPosition = new Vector3d(); + @Nonnull + private final Bounds3d rGridBounds = new Bounds3d(); + @Nonnull + private final Control rControl = new Control(); + + @Override + public void generate(@NonNullDecl PositionProvider.Context context) { + this.rGridBounds.min.set(Math.floor(context.bounds.min.x), Math.floor(context.bounds.min.y), Math.floor(context.bounds.min.z)); + this.rGridBounds.max.set(Math.ceil(context.bounds.max.x), Math.ceil(context.bounds.max.y), Math.ceil(context.bounds.max.z)); + if (this.rGridBounds.min.x < context.bounds.min.x) { + this.rGridBounds.min.x++; + } + + if (this.rGridBounds.min.y < context.bounds.min.y) { + this.rGridBounds.min.y++; + } + + if (this.rGridBounds.min.z < context.bounds.min.z) { + this.rGridBounds.min.z++; + } + + this.rControl.reset(); + + for (double x = this.rGridBounds.min.x; x < this.rGridBounds.max.x; x++) { + for (double y = this.rGridBounds.min.y; y < this.rGridBounds.max.y; y++) { + for (double z = this.rGridBounds.min.z; z < this.rGridBounds.max.z; z++) { + assert context.bounds.contains(x, y, z); + + if (this.rControl.stop) { + return; + } + + this.rPosition.set(x, y, z); + context.pipe.accept(this.rPosition, this.rControl); + } + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/TriangularGrid2dPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/TriangularGrid2dPositionProvider.java new file mode 100644 index 00000000..82b5a298 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/TriangularGrid2dPositionProvider.java @@ -0,0 +1,63 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class TriangularGrid2dPositionProvider extends PositionProvider { + private static final double Y = 0.0; + private static final double SPACING = 1.0; + private static final double HALF_SPACING = 0.5; + private static final double X_HEIGHT = Math.sqrt(0.75); + private static final double X_HEIGHT_INVERSE = 1.0 / X_HEIGHT; + @Nonnull + private final Vector3d rPosition = new Vector3d(); + @Nonnull + private final Bounds3d rGridBounds = new Bounds3d(); + @Nonnull + private final Control rControl = new Control(); + + @Override + public void generate(@NonNullDecl PositionProvider.Context context) { + if (!(context.bounds.min.y > 0.0) && !(context.bounds.max.y <= 0.0)) { + this.rGridBounds.min.set(Math.floor(context.bounds.min.x), 0.0, Math.floor(context.bounds.min.z)); + this.rGridBounds.max.set(Math.ceil(context.bounds.max.x), 1.0, Math.ceil(context.bounds.max.z)); + if (this.rGridBounds.min.x < context.bounds.min.x) { + this.rGridBounds.min.x++; + } + + if (this.rGridBounds.min.z < context.bounds.min.z) { + this.rGridBounds.min.z++; + } + + this.rGridBounds.min.x = this.rGridBounds.min.x * X_HEIGHT_INVERSE; + this.rGridBounds.max.x = this.rGridBounds.max.x * X_HEIGHT_INVERSE; + this.rControl.reset(); + + for (int x = (int)Math.floor(this.rGridBounds.min.x); x < this.rGridBounds.max.x; x++) { + double zOffset = x % 2 == 0 ? 0.5 : 0.0; + + for (double z = this.rGridBounds.min.z - zOffset; z < this.rGridBounds.max.z; z++) { + this.rPosition.set(x * X_HEIGHT, 0.0, z); + if (context.bounds.contains(this.rPosition)) { + if (this.rControl.stop) { + return; + } + + context.pipe.accept(this.rPosition, this.rControl); + } + } + } + } + } + + private static double toX0(double position) { + return toCellGrid(position) * X_HEIGHT; + } + + private static double toCellGrid(double position) { + return Math.floor(position * X_HEIGHT_INVERSE); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/UnionPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/UnionPositionProvider.java index 1c9f3fd6..161d6669 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/UnionPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/UnionPositionProvider.java @@ -13,9 +13,9 @@ public class UnionPositionProvider extends PositionProvider { } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { + public void generate(@Nonnull PositionProvider.Context context) { for (PositionProvider position : this.positionProviders) { - position.positionsIn(context); + position.generate(context); } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/cached/CacheThreadMemory.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/cached/CacheThreadMemory.java index a46f4613..f28fb9b5 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/cached/CacheThreadMemory.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/cached/CacheThreadMemory.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders.cached; -import com.hypixel.hytale.math.vector.Vector3d; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; +import org.joml.Vector3d; public class CacheThreadMemory { Map sections; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/cached/CachedPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/cached/CachedPositionProvider.java index c0b35ed5..efd634d3 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/cached/CachedPositionProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/cached/CachedPositionProvider.java @@ -1,12 +1,16 @@ package com.hypixel.hytale.builtin.hytalegenerator.positionproviders.cached; -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.ArrayList; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class CachedPositionProvider extends PositionProvider { @Nonnull @@ -14,7 +18,7 @@ public class CachedPositionProvider extends PositionProvider { private final int sectionSize; private CacheThreadMemory cache; - public CachedPositionProvider(@Nonnull PositionProvider positionProvider, int sectionSize, int cacheSize, boolean useInternalThreadData) { + public CachedPositionProvider(@Nonnull PositionProvider positionProvider, int sectionSize, int cacheSize) { if (sectionSize > 0 && cacheSize >= 0) { this.positionProvider = positionProvider; this.sectionSize = sectionSize; @@ -25,14 +29,14 @@ public class CachedPositionProvider extends PositionProvider { } @Override - public void positionsIn(@Nonnull PositionProvider.Context context) { + public void generate(@Nonnull PositionProvider.Context context) { this.get(context); } public void get(@Nonnull PositionProvider.Context context) { - Vector3i minSection = this.sectionAddress(context.minInclusive); - Vector3i maxSection = this.sectionAddress(context.maxExclusive); - Vector3i sectionAddress = minSection.clone(); + Vector3i minSection = this.sectionAddress(context.bounds.min); + Vector3i maxSection = this.sectionAddress(context.bounds.max); + Vector3i sectionAddress = new Vector3i(minSection); for (sectionAddress.x = minSection.x; sectionAddress.x <= maxSection.x; sectionAddress.x++) { for (sectionAddress.z = minSection.z; sectionAddress.z <= maxSection.z; sectionAddress.z++) { @@ -41,10 +45,11 @@ public class CachedPositionProvider extends PositionProvider { Vector3d[] section = this.cache.sections.get(key); if (section == null) { Vector3d sectionMin = this.sectionMin(sectionAddress); - Vector3d sectionMax = sectionMin.clone().add(this.sectionSize, this.sectionSize, this.sectionSize); + Bounds3d sectionBounds = new Bounds3d(sectionMin, new Vector3d(sectionMin).add(this.sectionSize, this.sectionSize, this.sectionSize)); ArrayList generatedPositions = new ArrayList<>(); - PositionProvider.Context childContext = new PositionProvider.Context(sectionMin, sectionMax, generatedPositions::add, null); - this.positionProvider.positionsIn(childContext); + Pipe.One pipe = (positionx, controlx) -> generatedPositions.add(positionx); + PositionProvider.Context childContext = new PositionProvider.Context(sectionBounds, pipe, null); + this.positionProvider.generate(childContext); section = new Vector3d[generatedPositions.size()]; generatedPositions.toArray(section); this.cache.sections.put(key, section); @@ -55,9 +60,15 @@ public class CachedPositionProvider extends PositionProvider { } } + Control control = new Control(); + for (Vector3d position : section) { - if (VectorUtil.isInside(position, context.minInclusive, context.maxExclusive)) { - context.consumer.accept(position.clone()); + if (context.bounds.contains(position)) { + if (control.stop) { + return; + } + + context.pipe.accept(new Vector3d(position), control); } } } @@ -67,7 +78,7 @@ public class CachedPositionProvider extends PositionProvider { @Nonnull private Vector3i sectionAddress(@Nonnull Vector3d pointer) { - Vector3i address = pointer.toVector3i(); + Vector3i address = Vector3dUtil.toVector3i(pointer); address.x = this.sectionFloor(address.x) / this.sectionSize; address.y = this.sectionFloor(address.y) / this.sectionSize; address.z = this.sectionFloor(address.z) / this.sectionSize; @@ -76,7 +87,7 @@ public class CachedPositionProvider extends PositionProvider { @Nonnull private Vector3d sectionMin(@Nonnull Vector3i sectionAddress) { - Vector3d min = sectionAddress.toVector3d(); + Vector3d min = Vector3iUtil.toVector3d(sectionAddress); min.x = min.x * this.sectionSize; min.y = min.y * this.sectionSize; min.z = min.z * this.sectionSize; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/deprecated/Mesh2DPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/deprecated/Mesh2DPositionProvider.java new file mode 100644 index 00000000..cee857fd --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/deprecated/Mesh2DPositionProvider.java @@ -0,0 +1,29 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders.deprecated; + +import com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider.PointProvider; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector3d; + +public class Mesh2DPositionProvider extends PositionProvider { + @Nonnull + private final PointProvider pointGenerator; + private final int y; + + public Mesh2DPositionProvider(@Nonnull PointProvider positionProvider, int y) { + this.pointGenerator = positionProvider; + this.y = y; + } + + @Override + public void generate(@Nonnull PositionProvider.Context context) { + if (!(context.bounds.min.y > this.y) && !(context.bounds.max.y <= this.y)) { + Vector2d min2d = new Vector2d(context.bounds.min.x, context.bounds.min.z); + Vector2d max2d = new Vector2d(context.bounds.max.x, context.bounds.max.z); + Control control = new Control(); + this.pointGenerator.points2d(min2d, max2d, point -> context.pipe.accept(new Vector3d(point.x, this.y, point.y), control)); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/deprecated/Mesh3DPositionProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/deprecated/Mesh3DPositionProvider.java new file mode 100644 index 00000000..a7bfcf37 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/positionproviders/deprecated/Mesh3DPositionProvider.java @@ -0,0 +1,21 @@ +package com.hypixel.hytale.builtin.hytalegenerator.positionproviders.deprecated; + +import com.hypixel.hytale.builtin.hytalegenerator.noise.pointprovider.PointProvider; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import javax.annotation.Nonnull; + +public class Mesh3DPositionProvider extends PositionProvider { + @Nonnull + private final PointProvider pointGenerator; + + public Mesh3DPositionProvider(@Nonnull PointProvider positionProvider) { + this.pointGenerator = positionProvider; + } + + @Override + public void generate(@Nonnull PositionProvider.Context context) { + Control control = new Control(); + this.pointGenerator.points3d(context.bounds.min, context.bounds.max, position -> context.pipe.accept(position, control)); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/AssignedPropDistribution.java b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/AssignedPropDistribution.java new file mode 100644 index 00000000..2e4c7112 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/AssignedPropDistribution.java @@ -0,0 +1,61 @@ +package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; + +import com.hypixel.hytale.builtin.hytalegenerator.assignments.Assignments; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; +import java.util.Objects; +import java.util.function.Consumer; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class AssignedPropDistribution extends PropDistribution { + @Nonnull + private final PropDistribution propDistribution; + @Nonnull + private final Assignments assignments; + private final boolean isOverrideAllProps; + @Nonnull + private final PropDistribution.Context rPropDistributionContext; + @Nonnull + private PropDistribution.Context rContext; + @Nonnull + private final Pipe.Two rChildPipe = new Pipe.Two() { + { + Objects.requireNonNull(AssignedPropDistribution.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Prop existingProp, @NonNullDecl Control control) { + if (AssignedPropDistribution.this.isOverrideAllProps || existingProp == EmptyProp.INSTANCE) { + Prop newProp = AssignedPropDistribution.this.assignments + .propAt(position, WorkerIndexer.Id.MAIN, AssignedPropDistribution.this.rContext.distanceFromBiomeEdge); + AssignedPropDistribution.this.rContext.pipe.accept(position, newProp, control); + } + } + }; + + public AssignedPropDistribution(@Nonnull PropDistribution propDistribution, @Nonnull Assignments assignments, boolean isOverrideAllProps) { + this.propDistribution = propDistribution; + this.assignments = assignments; + this.isOverrideAllProps = isOverrideAllProps; + this.rPropDistributionContext = new PropDistribution.Context(); + this.rContext = new PropDistribution.Context(); + } + + @Override + public void distribute(@NonNullDecl PropDistribution.Context context) { + this.rContext = context; + this.rPropDistributionContext.assign(context); + this.rPropDistributionContext.pipe = this.rChildPipe; + this.propDistribution.distribute(this.rPropDistributionContext); + } + + @Override + public void forEachPossibleProp(@NonNullDecl Consumer consumer) { + this.assignments.getAllPossibleProps().forEach(consumer); + this.propDistribution.forEachPossibleProp(consumer); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/ConstantPropDistribution.java b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/ConstantPropDistribution.java new file mode 100644 index 00000000..606d7d7f --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/ConstantPropDistribution.java @@ -0,0 +1,61 @@ +package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; + +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import java.util.Objects; +import java.util.function.Consumer; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class ConstantPropDistribution extends PropDistribution { + @Nonnull + private final PositionProvider positionProvider; + @Nonnull + private final Prop prop; + @Nonnull + private final PositionProvider.Context rPositionProviderContext; + @Nonnull + private final Control rControl; + @Nonnull + private PropDistribution.Context rContext; + @Nonnull + private final Pipe.One rPositionsPipe = new Pipe.One() { + { + Objects.requireNonNull(ConstantPropDistribution.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + if (ConstantPropDistribution.this.rControl.stop) { + control.stop = true; + } else { + ConstantPropDistribution.this.rContext.pipe.accept(position, ConstantPropDistribution.this.prop, ConstantPropDistribution.this.rControl); + } + } + }; + + public ConstantPropDistribution(@Nonnull PositionProvider positionProvider, @Nonnull Prop prop) { + this.positionProvider = positionProvider; + this.prop = prop; + this.rPositionProviderContext = new PositionProvider.Context(); + this.rControl = new Control(); + this.rContext = new PropDistribution.Context(); + } + + @Override + public void distribute(@NonNullDecl PropDistribution.Context context) { + this.rContext = context; + this.rControl.stop = false; + this.rPositionProviderContext.bounds.min.set(context.bounds.min); + this.rPositionProviderContext.bounds.max.set(context.bounds.max); + this.rPositionProviderContext.pipe = this.rPositionsPipe; + this.positionProvider.generate(this.rPositionProviderContext); + } + + @Override + public void forEachPossibleProp(@NonNullDecl Consumer consumer) { + consumer.accept(this.prop); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/NoPropDistribution.java b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/NoPropDistribution.java new file mode 100644 index 00000000..c2c49925 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/NoPropDistribution.java @@ -0,0 +1,20 @@ +package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; + +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import java.util.function.Consumer; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class NoPropDistribution extends PropDistribution { + public static final PropDistribution INSTANCE = new NoPropDistribution(); + + private NoPropDistribution() { + } + + @Override + public void distribute(@NonNullDecl PropDistribution.Context context) { + } + + @Override + public void forEachPossibleProp(@NonNullDecl Consumer consumer) { + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/PositionsPropDistribution.java b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/PositionsPropDistribution.java new file mode 100644 index 00000000..a3cea929 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/PositionsPropDistribution.java @@ -0,0 +1,55 @@ +package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; + +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; +import com.hypixel.hytale.builtin.hytalegenerator.props.EmptyProp; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import java.util.Objects; +import java.util.function.Consumer; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; + +public class PositionsPropDistribution extends PropDistribution { + @Nonnull + private final PositionProvider positionProvider; + @Nonnull + private final PositionProvider.Context rPositionProviderContext; + @Nonnull + private PropDistribution.Context rContext; + @Nonnull + private final Pipe.One rPositionsPipe = new Pipe.One() { + { + Objects.requireNonNull(PositionsPropDistribution.this); + } + + public void accept(@NonNullDecl Vector3d position, @NonNullDecl Control control) { + assert PositionsPropDistribution.this.rContext.bounds.contains(position); + + PositionsPropDistribution.this.rContext.pipe.accept(position, EmptyProp.INSTANCE, control); + } + }; + + public PositionsPropDistribution(@Nonnull PositionProvider positionProvider) { + this.positionProvider = positionProvider; + this.rPositionProviderContext = new PositionProvider.Context(); + this.rContext = new PropDistribution.Context(); + } + + @Override + public void distribute(@Nonnull PropDistribution.Context context) { + this.rContext = context; + this.rPositionProviderContext.assign(context); + this.rPositionProviderContext.pipe = (position, control) -> { + assert context.bounds.contains(position); + + context.pipe.accept(position, EmptyProp.INSTANCE, control); + }; + this.positionProvider.generate(this.rPositionProviderContext); + } + + @Override + public void forEachPossibleProp(@NonNullDecl Consumer consumer) { + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/PropDistribution.java b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/PropDistribution.java new file mode 100644 index 00000000..a7ab5585 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/PropDistribution.java @@ -0,0 +1,40 @@ +package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3d; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import java.util.function.Consumer; +import javax.annotation.Nonnull; +import org.joml.Vector3d; + +public abstract class PropDistribution { + public abstract void distribute(@Nonnull PropDistribution.Context var1); + + public abstract void forEachPossibleProp(@Nonnull Consumer var1); + + public static class Context { + @Nonnull + public Bounds3d bounds; + @Nonnull + public Pipe.Two pipe; + public double distanceFromBiomeEdge; + + public Context() { + this.bounds = new Bounds3d(); + this.pipe = (position, prop, control) -> {}; + this.distanceFromBiomeEdge = Double.MAX_VALUE; + } + + public Context(@Nonnull Bounds3d bounds, @Nonnull Pipe.Two pipe, double distanceFromBiomeEdge) { + this.bounds = bounds; + this.pipe = pipe; + this.distanceFromBiomeEdge = distanceFromBiomeEdge; + } + + public void assign(@Nonnull PropDistribution.Context context) { + this.bounds = context.bounds; + this.pipe = context.pipe; + this.distanceFromBiomeEdge = context.distanceFromBiomeEdge; + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/UnionPropDistribution.java b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/UnionPropDistribution.java new file mode 100644 index 00000000..3bfa8445 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/propdistributions/UnionPropDistribution.java @@ -0,0 +1,31 @@ +package com.hypixel.hytale.builtin.hytalegenerator.propdistributions; + +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class UnionPropDistribution extends PropDistribution { + @Nonnull + private final List propDistributions; + + public UnionPropDistribution(@Nonnull List propDistributions) { + this.propDistributions = new ArrayList<>(propDistributions); + } + + @Override + public void distribute(@NonNullDecl PropDistribution.Context context) { + for (PropDistribution propDistribution : this.propDistributions) { + propDistribution.distribute(context); + } + } + + @Override + public void forEachPossibleProp(@NonNullDecl Consumer consumer) { + for (PropDistribution propDistribution : this.propDistributions) { + propDistribution.forEachPossibleProp(consumer); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/BoxProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/BoxProp.java deleted file mode 100644 index 397cd3d2..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/BoxProp.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props; - -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.GridUtils; -import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.List; -import javax.annotation.Nonnull; -import org.checkerframework.checker.nullness.compatqual.NonNullDecl; - -public class BoxProp extends Prop { - @Nonnull - private final Vector3i range; - @Nonnull - private final Material material; - @Nonnull - private final Scanner scanner; - @Nonnull - private final Pattern pattern; - @Nonnull - private final ContextDependency contextDependency; - @Nonnull - private final Bounds3i readBounds_voxelGrid; - @Nonnull - private final Bounds3i writeBounds_voxelGrid; - @Nonnull - private final Bounds3i boxBounds_voxelGrid; - - public BoxProp(@Nonnull Vector3i range, @Nonnull Material material, @Nonnull Scanner scanner, @Nonnull Pattern pattern) { - if (VectorUtil.isAnySmaller(range, new Vector3i())) { - throw new IllegalArgumentException("negative range"); - } else { - this.range = range.clone(); - this.material = material; - this.scanner = scanner; - this.pattern = pattern; - SpaceSize writeSpace = new SpaceSize(new Vector3i(-range.x - 1, 0, -range.z - 1), new Vector3i(range.x + 2, 0, range.z + 2)); - writeSpace = SpaceSize.stack(writeSpace, scanner.readSpaceWith(pattern)); - Vector3i writeRange = writeSpace.getRange(); - Vector3i readRange = scanner.readSpaceWith(pattern).getRange(); - this.contextDependency = new ContextDependency(readRange, writeRange); - this.readBounds_voxelGrid = this.contextDependency.getReadBounds_voxelGrid(); - this.writeBounds_voxelGrid = this.contextDependency.getWriteBounds_voxelGrid(); - this.boxBounds_voxelGrid = GridUtils.createBounds_fromVector_originVoxelInclusive(range); - } - } - - @Nonnull - public PositionListScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, id); - List validPositions = this.scanner.scan(scannerContext); - return new PositionListScanResult(validPositions); - } - - @Override - public void place(@Nonnull Prop.Context context) { - List positions = PositionListScanResult.cast(context.scanResult).getPositions(); - if (positions != null) { - Bounds3i writeSpaceBounds_voxelGrid = context.materialSpace.getBounds(); - - for (Vector3i position : positions) { - Bounds3i localBoxBounds_voxelGrid = this.boxBounds_voxelGrid.clone().offset(position); - if (localBoxBounds_voxelGrid.intersects(writeSpaceBounds_voxelGrid)) { - this.place(position, context.materialSpace); - } - } - } - } - - private void place(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace) { - Vector3i min = position.clone().add(-this.range.x, 0, -this.range.z); - Vector3i max = position.clone().add(this.range.x, this.range.y + this.range.y, this.range.z); - - for (int x = min.x; x <= max.x; x++) { - for (int y = min.y; y <= max.y; y++) { - for (int z = min.z; z <= max.z; z++) { - if (materialSpace.isInsideSpace(x, y, z)) { - materialSpace.set(this.material, x, y, z); - } - } - } - } - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency.clone(); - } - - @NonNullDecl - @Override - public Bounds3i getReadBounds_voxelGrid() { - return this.readBounds_voxelGrid; - } - - @Nonnull - @Override - public Bounds3i getWriteBounds_voxelGrid() { - return this.writeBounds_voxelGrid; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/ClusterProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/ClusterProp.java deleted file mode 100644 index 5f6da4d5..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/ClusterProp.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.WindowVoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; -import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.EntityContainer; -import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector3i; -import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; -import java.util.List; -import javax.annotation.Nonnull; -import org.checkerframework.checker.nullness.compatqual.NonNullDecl; - -public class ClusterProp extends Prop { - @Nonnull - private final Double2DoubleFunction weightCurve; - @Nonnull - private final SeedGenerator seedGenerator; - @Nonnull - private final WeightedMap propWeightedMap; - private final int range; - @Nonnull - private final ContextDependency contextDependency; - @Nonnull - private final Pattern pattern; - @Nonnull - private final Scanner scanner; - @Nonnull - private final Bounds3i readBounds_voxelGrid; - @Nonnull - private final Bounds3i writeBounds_voxelGrid; - - public ClusterProp( - int range, - @Nonnull Double2DoubleFunction weightCurve, - int seed, - @Nonnull WeightedMap propWeightedMap, - @Nonnull Pattern pattern, - @Nonnull Scanner scanner - ) { - if (range < 0) { - throw new IllegalArgumentException("negative range"); - } else { - this.range = range; - this.seedGenerator = new SeedGenerator(seed); - this.weightCurve = weightCurve; - this.pattern = pattern; - this.scanner = scanner; - this.propWeightedMap = new WeightedMap<>(); - propWeightedMap.forEach((prop, weight) -> { - ContextDependency contextDependency = prop.getContextDependency(); - Vector3i readRangex = contextDependency.getReadRange(); - Vector3i writeRangex = contextDependency.getWriteRange(); - if (readRangex.x <= 0 && readRangex.z <= 0 && writeRangex.x <= 0 && writeRangex.z <= 0) { - this.propWeightedMap.add(prop, propWeightedMap.get(prop)); - } - }); - Vector3i readRange = scanner.readSpaceWith(pattern).getRange(); - this.readBounds_voxelGrid = scanner.readSpaceWith(pattern).toBounds3i(); - Vector3i writeRange = new Vector3i(range + readRange.x, 0, range + readRange.z); - this.contextDependency = new ContextDependency(readRange, writeRange); - this.writeBounds_voxelGrid = this.contextDependency.getWriteBounds_voxelGrid(); - } - } - - @Nonnull - public PositionListScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, id); - List validPositions = this.scanner.scan(scannerContext); - return new PositionListScanResult(validPositions); - } - - @Override - public void place(@Nonnull Prop.Context context) { - List positions = PositionListScanResult.cast(context.scanResult).getPositions(); - if (positions != null) { - for (Vector3i position : positions) { - this.place(position, context.materialSpace, context.entityBuffer, context.workerId, context.distanceFromBiomeEdge); - } - } - } - - private void place( - @Nonnull Vector3i position, - @Nonnull VoxelSpace materialSpace, - @Nonnull EntityContainer entityBuffer, - @Nonnull WorkerIndexer.Id id, - double distanceFromBiomeEdge - ) { - WindowVoxelSpace columnSpace = new WindowVoxelSpace<>(materialSpace); - FastRandom random = new FastRandom(this.seedGenerator.seedAt(position.x, position.z)); - - for (int x = position.x - this.range; x < position.x + this.range; x++) { - for (int z = position.z - this.range; z < position.z + this.range; z++) { - double distance = Calculator.distance(x, z, position.x, position.z); - double density = this.weightCurve.get(distance); - if (!(random.nextDouble() > density)) { - Prop pickedProp = this.propWeightedMap.pick(random); - if (materialSpace.isInsideSpace(x, materialSpace.minY(), z)) { - columnSpace.setWindow(x, materialSpace.minY(), z, x + 1, materialSpace.maxY(), z + 1); - ScanResult propScanResult = pickedProp.scan(new Vector3i(x, position.y, z), columnSpace, id); - Prop.Context childContext = new Prop.Context(propScanResult, columnSpace, entityBuffer, id, distanceFromBiomeEdge); - pickedProp.place(childContext); - } - } - } - } - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency.clone(); - } - - @NonNullDecl - @Override - public Bounds3i getReadBounds_voxelGrid() { - return this.readBounds_voxelGrid; - } - - @Nonnull - @Override - public Bounds3i getWriteBounds_voxelGrid() { - return this.writeBounds_voxelGrid; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/CuboidProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/CuboidProp.java new file mode 100644 index 00000000..88cbed87 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/CuboidProp.java @@ -0,0 +1,64 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class CuboidProp extends Prop { + @Nonnull + private final Bounds3i bounds; + @Nonnull + private final MaterialProvider materialProvider; + @Nonnull + private final Bounds3i rIntersectingBounds; + @Nonnull + private final MaterialProvider.Context rContext; + + public CuboidProp(@Nonnull Bounds3i bounds, @Nonnull MaterialProvider materialProvider) { + this.bounds = bounds.clone(); + this.materialProvider = materialProvider; + this.rIntersectingBounds = new Bounds3i(); + this.rContext = new MaterialProvider.Context(new Vector3i(), 1.0, 0, 0, 0, 0, null, Double.MAX_VALUE); + } + + @Override + public boolean generate(@Nonnull Prop.Context context) { + this.rIntersectingBounds.assign(this.bounds); + this.rIntersectingBounds.offset(context.position); + int minYInclusive = this.rIntersectingBounds.min.y; + int maxYExclusive = this.rIntersectingBounds.max.y; + this.rIntersectingBounds.intersect(context.materialWriteSpace.getBounds()); + Vector3i position = this.rContext.position; + this.rContext.density = 1.0; + + for (position.x = this.rIntersectingBounds.min.x; position.x < this.rIntersectingBounds.max.x; position.x++) { + for (position.z = this.rIntersectingBounds.min.z; position.z < this.rIntersectingBounds.max.z; position.z++) { + for (position.y = this.rIntersectingBounds.min.y; position.y < this.rIntersectingBounds.max.y; position.y++) { + this.rContext.depthIntoFloor = maxYExclusive - position.y; + this.rContext.depthIntoCeiling = position.y - minYInclusive; + this.rContext.spaceAboveFloor = Integer.MAX_VALUE; + this.rContext.spaceBelowCeiling = Integer.MAX_VALUE; + Material material = this.materialProvider.getVoxelTypeAt(this.rContext); + context.materialWriteSpace.set(material, position); + } + } + } + + return true; + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return Bounds3i.ZERO; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.bounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/DensityProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/DensityProp.java index 9782c98a..2ac5016e 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/DensityProp.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/DensityProp.java @@ -1,216 +1,172 @@ package com.hypixel.hytale.builtin.hytalegenerator.props; -import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.ArrayVoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; -import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.List; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.ArrayVoxelSpace; import javax.annotation.Nonnull; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; +import org.joml.Vector3i; public class DensityProp extends Prop { - @Nonnull - private final Vector3i range; @Nonnull private final Density density; @Nonnull private final MaterialProvider materialProvider; @Nonnull - private final Scanner scanner; + private final Bounds3i writeBounds; @Nonnull - private final Pattern pattern; + private final Bounds3i solidityBufferBounds; @Nonnull - private final ContextDependency contextDependency; + private final Bounds3i rIntersectingWriteBounds; @Nonnull - private final BlockMask placementMask; + private final ArrayVoxelSpace rSolidityBuffer; @Nonnull - private final Material defaultMaterial; + private final Density.Context rDensityContext; @Nonnull - private final Bounds3i readBounds_voxelGrid; + private final MaterialProvider.Context rMaterialProviderContext; @Nonnull - private final Bounds3i writeBounds_voxelGrid; + private final Vector3i rPosition; + @Nonnull + private final int[] rDepthIntoCeiling; + @Nonnull + private final int[] rDepthIntoFloor; + @Nonnull + private final int[] rSpaceBelowCeiling; + @Nonnull + private final int[] rSpaceAboveFloor; - public DensityProp( - @Nonnull Vector3i range, - @Nonnull Density density, - @Nonnull MaterialProvider materialProvider, - @Nonnull Scanner scanner, - @Nonnull Pattern pattern, - @Nonnull BlockMask placementMask, - @Nonnull Material defaultMaterial - ) { - this.range = range.clone(); + public DensityProp(@Nonnull Density density, @Nonnull MaterialProvider materialProvider, @Nonnull Bounds3i bounds) { this.density = density; this.materialProvider = materialProvider; - this.scanner = scanner; - this.pattern = pattern; - this.placementMask = placementMask; - this.defaultMaterial = defaultMaterial; - SpaceSize writeSpace = new SpaceSize(new Vector3i(-range.x - 1, 0, -range.z - 1), new Vector3i(range.x + 2, 0, range.z + 2)); - writeSpace = SpaceSize.stack(writeSpace, scanner.readSpaceWith(pattern)); - Vector3i writeRange = writeSpace.getRange(); - Vector3i readRange = scanner.readSpaceWith(pattern).getRange(); - this.contextDependency = new ContextDependency(readRange, writeRange); - this.readBounds_voxelGrid = this.contextDependency.getReadBounds_voxelGrid(); - this.writeBounds_voxelGrid = this.contextDependency.getWriteBounds_voxelGrid(); - } - - @Nonnull - public PositionListScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, id); - List validPositions = this.scanner.scan(scannerContext); - return new PositionListScanResult(validPositions); + this.writeBounds = bounds.clone(); + this.solidityBufferBounds = bounds.clone(); + this.solidityBufferBounds.min.y--; + this.solidityBufferBounds.max.y++; + this.rSolidityBuffer = new ArrayVoxelSpace<>(this.solidityBufferBounds); + this.rIntersectingWriteBounds = new Bounds3i(); + this.rDensityContext = new Density.Context(); + this.rDensityContext.densityAnchor = new Vector3d(); + this.rMaterialProviderContext = new MaterialProvider.Context(); + this.rPosition = new Vector3i(); + int bufferHeight = this.writeBounds.max.y - this.writeBounds.min.y + 2; + this.rDepthIntoCeiling = new int[bufferHeight + 1]; + this.rDepthIntoFloor = new int[bufferHeight + 1]; + this.rSpaceBelowCeiling = new int[bufferHeight + 1]; + this.rSpaceAboveFloor = new int[bufferHeight + 1]; } @Override - public void place(@Nonnull Prop.Context context) { - List positions = PositionListScanResult.cast(context.scanResult).getPositions(); - if (positions != null) { - for (Vector3i position : positions) { - this.place(position, context.materialSpace, context.workerId); - } - } - } + public boolean generate(@NonNullDecl Prop.Context context) { + Bounds3i writeSpaceBounds = context.materialWriteSpace.getBounds(); + this.rIntersectingWriteBounds.assign(this.writeBounds); + this.rIntersectingWriteBounds.offset(context.position); + Bounds3i localSolidityBufferBounds = this.rSolidityBuffer.getBounds(); + localSolidityBufferBounds.assign(this.solidityBufferBounds); + localSolidityBufferBounds.offset(context.position); + this.rIntersectingWriteBounds.min.x = Math.max(this.rIntersectingWriteBounds.min.x, writeSpaceBounds.min.x); + this.rIntersectingWriteBounds.min.z = Math.max(this.rIntersectingWriteBounds.min.z, writeSpaceBounds.min.z); + this.rIntersectingWriteBounds.max.x = Math.min(this.rIntersectingWriteBounds.max.x, writeSpaceBounds.max.x); + this.rIntersectingWriteBounds.max.z = Math.min(this.rIntersectingWriteBounds.max.z, writeSpaceBounds.max.z); + this.rIntersectingWriteBounds.min.y--; + this.rIntersectingWriteBounds.max.y++; - private void place(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - Vector3i min = position.clone().add(-this.range.x, -this.range.y, -this.range.z); - Vector3i max = position.clone().add(this.range.x, this.range.y, this.range.z); - Vector3i writeMin = Vector3i.max(min, new Vector3i(materialSpace.minX(), materialSpace.minY(), materialSpace.minZ())); - Vector3i writeMax = Vector3i.min(max, new Vector3i(materialSpace.maxX(), materialSpace.maxY(), materialSpace.maxZ())); - int bottom = min.y; - int top = max.y; - int height = top - bottom; - ArrayVoxelSpace densitySpace = new ArrayVoxelSpace<>(max.x - min.x + 1, max.y - min.y + 1, max.z - min.z + 1); - densitySpace.setOrigin(-min.x, -min.y, -min.z); - Density.Context childContext = new Density.Context(); - childContext.densityAnchor = position.toVector3d(); - Vector3i itPosition = new Vector3i(position); + assert this.rDensityContext.densityAnchor != null; - for (itPosition.x = min.x; itPosition.x <= max.x; itPosition.x++) { - for (itPosition.z = min.z; itPosition.z <= max.z; itPosition.z++) { - for (itPosition.y = min.y; itPosition.y <= max.y; itPosition.y++) { - if (densitySpace.isInsideSpace(itPosition.x, itPosition.y, itPosition.z)) { - childContext.position.x = itPosition.x; - childContext.position.y = itPosition.y; - childContext.position.z = itPosition.z; - double densityValue = this.density.process(childContext); - densitySpace.set(densityValue > 0.0, itPosition.x, itPosition.y, itPosition.z); - } + this.rDensityContext.densityAnchor.set(context.position); + + for (this.rPosition.x = this.rIntersectingWriteBounds.min.x; this.rPosition.x < this.rIntersectingWriteBounds.max.x; this.rPosition.x++) { + for (this.rPosition.y = this.rIntersectingWriteBounds.min.y; this.rPosition.y < this.rIntersectingWriteBounds.max.y; this.rPosition.y++) { + for (this.rPosition.z = this.rIntersectingWriteBounds.min.z; this.rPosition.z < this.rIntersectingWriteBounds.max.z; this.rPosition.z++) { + this.rDensityContext.position.set(this.rPosition); + double densityValue = this.density.process(this.rDensityContext); + this.rSolidityBuffer.set(densityValue > 0.0 ? Boolean.TRUE : Boolean.FALSE, this.rPosition); } } } - for (itPosition.x = min.x; itPosition.x <= max.x; itPosition.x++) { - for (itPosition.z = min.z; itPosition.z <= max.z; itPosition.z++) { - int[] depthIntoCeiling = new int[height + 1]; - int[] depthIntoFloor = new int[height + 1]; - int[] spaceBelowCeiling = new int[height + 1]; - int[] spaceAboveFloor = new int[height + 1]; - - for (itPosition.y = top; itPosition.y >= bottom; itPosition.y--) { - int i = itPosition.y - bottom; - boolean density = densitySpace.getContent(itPosition.x, itPosition.y, itPosition.z); - if (itPosition.y == top) { - if (density) { - depthIntoFloor[i] = 1; + for (this.rPosition.x = this.rIntersectingWriteBounds.min.x; this.rPosition.x < this.rIntersectingWriteBounds.max.x; this.rPosition.x++) { + for (this.rPosition.z = this.rIntersectingWriteBounds.min.z; this.rPosition.z < this.rIntersectingWriteBounds.max.z; this.rPosition.z++) { + for (this.rPosition.y = this.rIntersectingWriteBounds.max.y - 2; this.rPosition.y > this.rIntersectingWriteBounds.min.y; this.rPosition.y--) { + int i = this.rPosition.y - this.rIntersectingWriteBounds.min.y; + boolean solidity = this.rSolidityBuffer.get(this.rPosition.x, this.rPosition.y, this.rPosition.z); + if (this.rPosition.y == this.rIntersectingWriteBounds.max.y - 1) { + if (solidity) { + this.rDepthIntoFloor[i] = 1; } else { - depthIntoFloor[i] = 0; + this.rDepthIntoFloor[i] = 0; } - spaceAboveFloor[i] = 1073741823; - } else if (density) { - depthIntoFloor[i] = depthIntoFloor[i + 1] + 1; - spaceAboveFloor[i] = spaceAboveFloor[i + 1]; + this.rSpaceAboveFloor[i] = 1073741823; + } else if (solidity) { + this.rDepthIntoFloor[i] = this.rDepthIntoFloor[i + 1] + 1; + this.rSpaceAboveFloor[i] = this.rSpaceAboveFloor[i + 1]; } else { - depthIntoFloor[i] = 0; - if (densitySpace.getContent(itPosition.x, itPosition.y + 1, itPosition.z)) { - spaceAboveFloor[i] = 0; + this.rDepthIntoFloor[i] = 0; + if (this.rSolidityBuffer.get(this.rPosition.x, this.rPosition.y + 1, this.rPosition.z)) { + this.rSpaceAboveFloor[i] = 0; } else { - spaceAboveFloor[i] = spaceAboveFloor[i + 1] + 1; + this.rSpaceAboveFloor[i] = this.rSpaceAboveFloor[i + 1] + 1; } } } - for (itPosition.y = bottom; itPosition.y < top; itPosition.y++) { - int i = itPosition.y - bottom; - boolean density = densitySpace.getContent(itPosition.x, itPosition.y, itPosition.z); - if (itPosition.y == bottom) { - if (density) { - depthIntoCeiling[i] = 1; + for (this.rPosition.y = this.rIntersectingWriteBounds.min.y + 1; this.rPosition.y < this.rIntersectingWriteBounds.max.y - 1; this.rPosition.y++) { + int i = this.rPosition.y - this.rIntersectingWriteBounds.min.y; + boolean solidity = this.rSolidityBuffer.get(this.rPosition.x, this.rPosition.y, this.rPosition.z); + if (this.rPosition.y == this.rIntersectingWriteBounds.min.x) { + if (solidity) { + this.rDepthIntoCeiling[i] = 1; } else { - depthIntoCeiling[i] = 0; + this.rDepthIntoCeiling[i] = 0; } - spaceBelowCeiling[i] = Integer.MAX_VALUE; - } else if (density) { - depthIntoCeiling[i] = depthIntoCeiling[i - 1] + 1; - spaceBelowCeiling[i] = spaceBelowCeiling[i - 1]; + this.rSpaceBelowCeiling[i] = Integer.MAX_VALUE; + } else if (solidity) { + this.rDepthIntoCeiling[i] = this.rDepthIntoCeiling[i - 1] + 1; + this.rSpaceBelowCeiling[i] = this.rSpaceBelowCeiling[i - 1]; } else { - depthIntoCeiling[i] = 0; - if (densitySpace.getContent(itPosition.x, itPosition.y - 1, itPosition.z)) { - spaceBelowCeiling[i] = 0; + this.rDepthIntoCeiling[i] = 0; + if (this.rSolidityBuffer.get(this.rPosition.x, this.rPosition.y - 1, this.rPosition.z)) { + this.rSpaceBelowCeiling[i] = 0; } else { - spaceBelowCeiling[i] = spaceBelowCeiling[i - 1] + 1; + this.rSpaceBelowCeiling[i] = this.rSpaceBelowCeiling[i - 1] + 1; } } } - for (itPosition.y = top; itPosition.y >= bottom; itPosition.y--) { - if (itPosition.x >= writeMin.x - && itPosition.y >= writeMin.y - && itPosition.z >= writeMin.z - && itPosition.x < writeMax.x - && itPosition.y < writeMax.y - && itPosition.z < writeMax.z) { - int i = itPosition.y - bottom; - MaterialProvider.Context materialContext = new MaterialProvider.Context( - position, 0.0, depthIntoFloor[i], depthIntoCeiling[i], spaceAboveFloor[i], spaceBelowCeiling[i], functionPosition -> { - childContext.position = functionPosition.toVector3d(); - return this.density.process(childContext); - }, childContext.distanceToBiomeEdge - ); - Material material = this.materialProvider.getVoxelTypeAt(materialContext); - if (material == null) { - material = this.defaultMaterial; - } - - if (this.placementMask.canPlace(material)) { - Material worldMaterial = materialSpace.getContent(itPosition.x, itPosition.y, itPosition.z); - int worldMaterialHash = worldMaterial.hashMaterialIds(); - if (this.placementMask.canReplace(material.hashCode(), worldMaterialHash)) { - materialSpace.set(material, itPosition.x, itPosition.y, itPosition.z); - } + for (this.rPosition.y = this.rIntersectingWriteBounds.max.y - 2; this.rPosition.y > this.rIntersectingWriteBounds.min.y; this.rPosition.y--) { + if (this.rIntersectingWriteBounds.contains(this.rPosition)) { + int i = this.rPosition.y - this.rIntersectingWriteBounds.min.y; + this.rMaterialProviderContext.position.set(this.rPosition); + this.rMaterialProviderContext.depthIntoFloor = this.rDepthIntoFloor[i]; + this.rMaterialProviderContext.depthIntoCeiling = this.rDepthIntoCeiling[i]; + this.rMaterialProviderContext.spaceAboveFloor = this.rSpaceAboveFloor[i]; + this.rMaterialProviderContext.spaceBelowCeiling = this.rSpaceBelowCeiling[i]; + this.rMaterialProviderContext.distanceToBiomeEdge = context.distanceToBiomeEdge; + Material material = this.materialProvider.getVoxelTypeAt(this.rMaterialProviderContext); + if (material != null && context.materialWriteSpace.getBounds().contains(this.rPosition)) { + context.materialWriteSpace.set(material, this.rPosition); } } } } } - } - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency.clone(); + return true; } @NonNullDecl @Override public Bounds3i getReadBounds_voxelGrid() { - return this.readBounds_voxelGrid; + return Bounds3i.ZERO; } - @Nonnull + @NonNullDecl @Override public Bounds3i getWriteBounds_voxelGrid() { - return this.writeBounds_voxelGrid; + return this.writeBounds; } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/DensitySelectorProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/DensitySelectorProp.java new file mode 100644 index 00000000..449d8401 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/DensitySelectorProp.java @@ -0,0 +1,66 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.delimiters.DelimiterDouble; +import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class DensitySelectorProp extends Prop { + @Nonnull + private final List> propDelimiters; + @Nonnull + private final Density density; + @Nonnull + private final Bounds3i readBounds; + @Nonnull + private final Bounds3i writeBounds; + @Nonnull + private final Density.Context rDensityContext; + + public DensitySelectorProp(@Nonnull List> propDelimiters, @Nonnull Density density) { + this.propDelimiters = new ArrayList<>(propDelimiters); + this.density = density; + this.readBounds = new Bounds3i(); + this.writeBounds = new Bounds3i(); + + for (DelimiterDouble delimiter : this.propDelimiters) { + assert delimiter.getValue() != null; + + this.readBounds.encompass(delimiter.getValue().getReadBounds_voxelGrid()); + this.writeBounds.encompass(delimiter.getValue().getWriteBounds_voxelGrid()); + } + + this.rDensityContext = new Density.Context(); + } + + @Override + public boolean generate(@Nonnull Prop.Context context) { + this.rDensityContext.assign(context); + double densityValue = this.density.process(this.rDensityContext); + + for (DelimiterDouble delimiter : this.propDelimiters) { + if (delimiter.getRange().contains(densityValue)) { + assert delimiter.getValue() != null; + + return delimiter.getValue().generate(context); + } + } + + return false; + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.readBounds; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/EmptyProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/EmptyProp.java new file mode 100644 index 00000000..5f86db81 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/EmptyProp.java @@ -0,0 +1,25 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class EmptyProp extends Prop { + public static final EmptyProp INSTANCE = new EmptyProp(); + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + return true; + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return Bounds3i.ZERO; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return Bounds3i.ZERO; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/LocatorProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/LocatorProp.java new file mode 100644 index 00000000..79ace816 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/LocatorProp.java @@ -0,0 +1,100 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class LocatorProp extends Prop { + @Nonnull + private final Bounds3i readBounds; + @Nonnull + private final Bounds3i writeBounds; + @Nonnull + private final Scanner scanner; + @Nonnull + private final Pattern pattern; + @Nonnull + private final Prop prop; + private final int placementCap; + @Nonnull + private final Pattern.Context rPatternContext; + @Nonnull + private final Prop.Context rPropContext; + @Nonnull + private final int[] rPlacedCount; + @Nonnull + private final boolean[] rHasGenerated; + @Nonnull + private Prop.Context rContext; + @Nonnull + private final Pipe.One rScannerPipe = new Pipe.One() { + { + Objects.requireNonNull(LocatorProp.this); + } + + public void accept(@NonNullDecl Vector3i position, @NonNullDecl Control control) { + if (LocatorProp.this.rPlacedCount[0] >= LocatorProp.this.placementCap) { + control.stop = true; + } else { + LocatorProp.this.rPatternContext.position = position; + if (LocatorProp.this.pattern.matches(LocatorProp.this.rPatternContext)) { + LocatorProp.this.rPropContext.assign(LocatorProp.this.rContext); + LocatorProp.this.rPropContext.position = position; + LocatorProp.this.rHasGenerated[0] = LocatorProp.this.rHasGenerated[0] | LocatorProp.this.prop.generate(LocatorProp.this.rPropContext); + LocatorProp.this.rPlacedCount[0]++; + } + } + } + }; + + public LocatorProp(@Nonnull Prop prop, @Nonnull Pattern pattern, @Nonnull Scanner scanner, int placementCap) { + this.prop = prop; + this.scanner = scanner; + this.pattern = pattern; + this.placementCap = placementCap; + this.readBounds = scanner.getBoundsWithPattern_voxelGrid(pattern); + Bounds3i propReadBounds = prop.getReadBounds_voxelGrid().clone(); + if (!propReadBounds.isZeroVolume()) { + this.readBounds.stack(propReadBounds); + } + + this.writeBounds = prop.getWriteBounds_voxelGrid().clone(); + if (!this.readBounds.isZeroVolume()) { + this.writeBounds.stack(this.readBounds); + } + + this.rPatternContext = new Pattern.Context(); + this.rPropContext = new Prop.Context(); + this.rPlacedCount = new int[1]; + this.rHasGenerated = new boolean[1]; + this.rContext = new Prop.Context(); + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + this.rContext = context; + this.rPatternContext.assign(context); + this.rHasGenerated[0] = false; + this.rPlacedCount[0] = 0; + this.scanner.scan(context.position, this.rScannerPipe); + return this.rHasGenerated[0]; + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.readBounds; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/ManualProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/ManualProp.java new file mode 100644 index 00000000..adc764a4 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/ManualProp.java @@ -0,0 +1,71 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class ManualProp extends Prop { + @Nonnull + private final List blocks; + @Nonnull + private final Bounds3i bounds = new Bounds3i(); + @Nonnull + private final Vector3i rPosition; + + public ManualProp(@Nonnull List blocks) { + this.blocks = new ArrayList<>(blocks.size()); + + for (ManualProp.Block block : blocks) { + this.blocks.add(block.clone()); + this.bounds.encompass(block.position); + } + + this.rPosition = new Vector3i(); + } + + @Override + public boolean generate(@Nonnull Prop.Context context) { + Bounds3i bounds = context.materialWriteSpace.getBounds(); + + for (ManualProp.Block block : this.blocks) { + this.rPosition.set(block.position); + this.rPosition.add(context.position); + if (bounds.contains(this.rPosition)) { + context.materialWriteSpace.set(block.material, this.rPosition); + } + } + + return true; + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return Bounds3i.ZERO; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.bounds; + } + + public static class Block { + public final Material material; + public final Vector3i position; + + public Block(@Nonnull Material material, @Nonnull Vector3i position) { + this.material = material; + this.position = new Vector3i(position); + } + + @Nonnull + public ManualProp.Block clone() { + return new ManualProp.Block(this.material, this.position); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/MaskProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/MaskProp.java new file mode 100644 index 00000000..fbb71b2e --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/MaskProp.java @@ -0,0 +1,43 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.MaskVoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.NullSpace; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class MaskProp extends Prop { + @Nonnull + private final Prop prop; + @Nonnull + private final MaskVoxelSpace maskVoxelSpace; + @Nonnull + private final Prop.Context rChildContext; + + public MaskProp(@Nonnull Prop prop, @Nonnull BlockMask mask) { + this.prop = prop; + this.maskVoxelSpace = new MaskVoxelSpace(mask, NullSpace.instance()); + this.rChildContext = new Prop.Context(); + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + this.maskVoxelSpace.setSource(context.materialWriteSpace); + this.rChildContext.assign(context); + this.rChildContext.materialWriteSpace = this.maskVoxelSpace; + return this.prop.generate(this.rChildContext); + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.prop.getReadBounds_voxelGrid(); + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.prop.getWriteBounds_voxelGrid(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/OffsetProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/OffsetProp.java index 9d3bb033..67d60e70 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/OffsetProp.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/OffsetProp.java @@ -1,49 +1,37 @@ package com.hypixel.hytale.builtin.hytalegenerator.props; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; public class OffsetProp extends Prop { @Nonnull - private final Vector3i offset_voxelGrid; + private final Vector3i offset; @Nonnull private final Prop childProp; @Nonnull private final Bounds3i readBounds_voxelGrid; @Nonnull private final Bounds3i writeBounds_voxelGrid; - @Nonnull - private final ContextDependency contextDependency; + private final Vector3i rChildPosition; + private final Prop.Context rChildContext; - public OffsetProp(@Nonnull Vector3i offset_voxelGrid, @Nonnull Prop childProp) { - this.offset_voxelGrid = offset_voxelGrid.clone(); + public OffsetProp(@Nonnull Vector3i offset, @Nonnull Prop childProp) { + this.offset = new Vector3i(offset); this.childProp = childProp; - this.readBounds_voxelGrid = childProp.getReadBounds_voxelGrid().clone().offset(offset_voxelGrid); - this.writeBounds_voxelGrid = childProp.getWriteBounds_voxelGrid().clone().offset(offset_voxelGrid); - this.contextDependency = ContextDependency.from(this.readBounds_voxelGrid, this.writeBounds_voxelGrid); + this.readBounds_voxelGrid = childProp.getReadBounds_voxelGrid().clone().offset(offset); + this.writeBounds_voxelGrid = childProp.getWriteBounds_voxelGrid().clone().offset(offset); + this.rChildPosition = new Vector3i(); + this.rChildContext = new Prop.Context(); } @Override - public ScanResult scan(@NonNullDecl Vector3i position_voxelGrid, @NonNullDecl VoxelSpace materialSpace, @NonNullDecl WorkerIndexer.Id id) { - Vector3i childPosition_voxelGrid = position_voxelGrid.clone().add(this.offset_voxelGrid); - return this.childProp.scan(childPosition_voxelGrid, materialSpace, id); - } - - @Override - public void place(@NonNullDecl Prop.Context context) { - this.childProp.place(context); - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency; + public boolean generate(@NonNullDecl Prop.Context context) { + this.rChildPosition.set(context.position).add(this.offset); + this.rChildContext.assign(context); + this.rChildContext.position = this.rChildPosition; + return this.childProp.generate(this.rChildContext); } @NonNullDecl diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/OrienterProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/OrienterProp.java new file mode 100644 index 00000000..feacbcb6 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/OrienterProp.java @@ -0,0 +1,176 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.RotatorPattern; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.math.util.FastRandom; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class OrienterProp extends Prop { + @Nonnull + private final List props; + @Nonnull + private final List patterns; + @Nonnull + private final Scanner scanner; + @Nonnull + private final OrienterProp.SelectionMode selectionMode; + @Nonnull + private final RngField rngField; + @Nonnull + private final Bounds3i readBounds; + @Nonnull + private final Bounds3i writeBounds; + @Nonnull + private final FastRandom random; + @Nonnull + private final Pattern.Context rPatternContext; + @Nonnull + private final Prop.Context rChildContext; + @Nonnull + private final boolean[] rHasGenerated; + @Nonnull + private final List rValidPatternIndices; + @Nonnull + private Prop.Context rContext; + @Nonnull + private final Pipe.One rFirstAllValidPipe = new Pipe.One() { + { + Objects.requireNonNull(OrienterProp.this); + } + + public void accept(@NonNullDecl Vector3i position, @NonNullDecl Control control) { + OrienterProp.this.rPatternContext.position = position; + + for (int i = 0; i < OrienterProp.this.patterns.size(); i++) { + Pattern pattern = OrienterProp.this.patterns.get(i); + if (pattern.matches(OrienterProp.this.rPatternContext)) { + Prop prop = OrienterProp.this.props.get(i); + OrienterProp.this.rChildContext.assign(OrienterProp.this.rContext); + OrienterProp.this.rChildContext.position = position; + OrienterProp.this.rHasGenerated[0] = prop.generate(OrienterProp.this.rChildContext); + control.stop = true; + if (OrienterProp.this.selectionMode == OrienterProp.SelectionMode.FIRST_VALID) { + return; + } + } + } + } + }; + @Nonnull + private final Pipe.One rRandomValidPipe = new Pipe.One() { + { + Objects.requireNonNull(OrienterProp.this); + } + + public void accept(@NonNullDecl Vector3i position, @NonNullDecl Control control) { + OrienterProp.this.rPatternContext.position = position; + OrienterProp.this.rValidPatternIndices.clear(); + + for (int i = 0; i < OrienterProp.this.patterns.size(); i++) { + Pattern pattern = OrienterProp.this.patterns.get(i); + if (pattern.matches(OrienterProp.this.rPatternContext)) { + OrienterProp.this.rValidPatternIndices.add(i); + } + } + + if (!OrienterProp.this.rValidPatternIndices.isEmpty()) { + OrienterProp.this.random.setSeed(OrienterProp.this.rngField.get(position.x, position.y, position.z)); + int pickedIndex = OrienterProp.this.random.nextInt(OrienterProp.this.rValidPatternIndices.size()); + Prop prop = OrienterProp.this.props.get(OrienterProp.this.rValidPatternIndices.get(pickedIndex)); + OrienterProp.this.rChildContext.assign(OrienterProp.this.rContext); + OrienterProp.this.rChildContext.position = position; + OrienterProp.this.rHasGenerated[0] = prop.generate(OrienterProp.this.rChildContext); + control.stop = true; + } + } + }; + + public OrienterProp( + @Nonnull List rotations, + @Nonnull Prop prop, + @Nonnull Pattern pattern, + @Nonnull Scanner scanner, + @Nonnull MaterialCache materialCache, + @Nonnull OrienterProp.SelectionMode selectionMode, + int seed + ) { + this.props = new ArrayList<>(rotations.size()); + this.patterns = new ArrayList<>(rotations.size()); + this.scanner = scanner; + this.selectionMode = selectionMode; + this.rngField = new RngField(seed); + this.readBounds = new Bounds3i(); + this.writeBounds = new Bounds3i(); + this.random = new FastRandom(); + + for (int i = 0; i < rotations.size(); i++) { + Prop rotatedProp = new StaticRotatorProp(prop, rotations.get(i), materialCache); + Pattern rotatedPattern = new RotatorPattern(pattern, rotations.get(i), materialCache); + this.props.add(rotatedProp); + this.patterns.add(rotatedPattern); + Bounds3i rotatedReadBounds = scanner.getBoundsWithPattern_voxelGrid(rotatedPattern); + Bounds3i rotatedPropReadBounds = rotatedProp.getReadBounds_voxelGrid(); + if (!rotatedPropReadBounds.isZeroVolume()) { + rotatedReadBounds.stack(rotatedPropReadBounds); + } + + this.readBounds.encompass(rotatedReadBounds); + Bounds3i rotatedWriteBounds = rotatedProp.getWriteBounds_voxelGrid().clone(); + if (!rotatedWriteBounds.isZeroVolume()) { + rotatedWriteBounds.stack(rotatedReadBounds); + } + + this.writeBounds.encompass(rotatedWriteBounds); + } + + this.rPatternContext = new Pattern.Context(); + this.rChildContext = new Prop.Context(); + this.rHasGenerated = new boolean[1]; + this.rValidPatternIndices = new ArrayList<>(this.patterns.size()); + this.rContext = new Prop.Context(); + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + this.rContext = context; + this.rPatternContext.assign(context); + this.rHasGenerated[0] = false; + if (this.selectionMode != OrienterProp.SelectionMode.FIRST_VALID && this.selectionMode != OrienterProp.SelectionMode.ALL_VALID) { + this.scanner.scan(context.position, this.rRandomValidPipe); + } else { + this.scanner.scan(context.position, this.rFirstAllValidPipe); + } + + return this.rHasGenerated[0]; + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.readBounds; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds; + } + + public static enum SelectionMode { + ALL_VALID, + FIRST_VALID, + RANDOM_VALID; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/PondFillerProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/PondFillerProp.java new file mode 100644 index 00000000..9dc5f5a3 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/PondFillerProp.java @@ -0,0 +1,206 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.MaterialSet; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.ArrayVoxelSpace; +import java.util.ArrayDeque; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class PondFillerProp extends Prop { + private static final int TRAVERSED = 1; + private static final int LEAKS = 16; + private static final int SOLID = 256; + private static final int STACKED = 4096; + @Nonnull + private final Bounds3i bounds; + @Nonnull + private final MaterialProvider fillerMaterialProvider; + @Nonnull + private final MaterialSet solidSet; + @Nonnull + private final Bounds3i rLocalBounds; + @Nonnull + private final Bounds3i rLocalWriteBounds; + @Nonnull + private final ArrayVoxelSpace rMask; + @Nonnull + private final MaterialProvider.Context rMaterialProviderContext; + + public PondFillerProp(@Nonnull Bounds3i bounds, @Nonnull MaterialProvider fillerMaterialProvider, @Nonnull MaterialSet solidSet) { + this.bounds = bounds.clone(); + this.fillerMaterialProvider = fillerMaterialProvider; + this.solidSet = solidSet; + this.rLocalBounds = new Bounds3i(); + this.rLocalWriteBounds = new Bounds3i(); + this.rMask = new ArrayVoxelSpace<>(bounds); + this.rMaterialProviderContext = new MaterialProvider.Context(new Vector3i(), 0.0, 0, 0, 0, 0, null, Double.MAX_VALUE); + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + this.rLocalBounds.assign(this.bounds).offset(context.position); + this.rLocalWriteBounds.assign(this.rLocalBounds).intersect(context.materialWriteSpace.getBounds()); + if (!context.materialReadSpace.getBounds().contains(this.rLocalBounds)) { + return true; + } else { + Bounds3i localMaskBounds = this.rMask.getBounds(); + localMaskBounds.assign(this.bounds); + localMaskBounds.offset(context.position); + this.rMask.setAll(0); + int y = this.rLocalBounds.min.y; + + for (int x = this.rLocalBounds.min.x; x < this.rLocalBounds.max.x; x++) { + for (int z = this.rLocalBounds.min.z; z < this.rLocalBounds.max.z; z++) { + Material material = context.materialReadSpace.get(x, y, z); + int contextMaterialHash = material.hashMaterialIds(); + int maskValue = 1; + if (this.solidSet.test(contextMaterialHash)) { + maskValue |= 256; + this.rMask.set(maskValue, x, y, z); + } else { + maskValue |= 16; + this.rMask.set(maskValue, x, y, z); + } + } + } + + for (int var14 = this.rLocalBounds.min.y + 1; var14 < this.rLocalBounds.max.y; var14++) { + int underY = var14 - 1; + + for (int x = this.rLocalBounds.min.x; x < this.rLocalBounds.max.x; x++) { + for (int zx = this.rLocalBounds.min.z; zx < this.rLocalBounds.max.z; zx++) { + if (!isTraversed(this.rMask.get(x, var14, zx))) { + int maskValueUnder = this.rMask.get(x, underY, zx); + Material material = context.materialReadSpace.get(x, var14, zx); + int contextMaterialHash = material.hashMaterialIds(); + if (this.solidSet.test(contextMaterialHash)) { + int maskValue = 0; + maskValue |= 1; + maskValue |= 256; + this.rMask.set(maskValue, x, var14, zx); + } else if (isLeaks(maskValueUnder) + || x == this.rLocalBounds.min.x + || x == this.rLocalBounds.max.x - 1 + || zx == this.rLocalBounds.min.z + || zx == this.rLocalBounds.max.z - 1) { + ArrayDeque stack = new ArrayDeque<>(); + stack.push(new Vector3i(x, var14, zx)); + this.rMask.set(4096, x, var14, zx); + + while (!stack.isEmpty()) { + Vector3i poppedPos = stack.pop(); + int maskValue = this.rMask.get(poppedPos.x, poppedPos.y, poppedPos.z); + maskValue |= 16; + this.rMask.set(maskValue, poppedPos.x, poppedPos.y, poppedPos.z); + poppedPos.x--; + if (this.rMask.getBounds().contains(poppedPos.x, poppedPos.y, poppedPos.z)) { + int poppedMaskValue = this.rMask.get(poppedPos.x, poppedPos.y, poppedPos.z); + if (!isStacked(poppedMaskValue)) { + material = context.materialReadSpace.get(poppedPos.x, poppedPos.y, poppedPos.z); + contextMaterialHash = material.hashMaterialIds(); + if (!this.solidSet.test(contextMaterialHash)) { + stack.push(new Vector3i(poppedPos)); + this.rMask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); + } + } + } + + poppedPos.x += 2; + if (this.rMask.getBounds().contains(poppedPos.x, poppedPos.y, poppedPos.z)) { + int poppedMaskValue = this.rMask.get(poppedPos.x, poppedPos.y, poppedPos.z); + if (!isStacked(poppedMaskValue)) { + material = context.materialReadSpace.get(poppedPos.x, poppedPos.y, poppedPos.z); + contextMaterialHash = material.hashMaterialIds(); + if (!this.solidSet.test(contextMaterialHash)) { + stack.push(new Vector3i(poppedPos)); + this.rMask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); + } + } + } + + poppedPos.x--; + poppedPos.z--; + if (this.rMask.getBounds().contains(poppedPos.x, poppedPos.y, poppedPos.z)) { + int poppedMaskValue = this.rMask.get(poppedPos.x, poppedPos.y, poppedPos.z); + if (!isStacked(poppedMaskValue)) { + material = context.materialReadSpace.get(poppedPos.x, var14, poppedPos.z); + contextMaterialHash = material.hashMaterialIds(); + if (!this.solidSet.test(contextMaterialHash)) { + stack.push(new Vector3i(poppedPos)); + this.rMask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); + } + } + } + + poppedPos.z += 2; + if (this.rMask.getBounds().contains(poppedPos.x, poppedPos.y, poppedPos.z)) { + int poppedMaskValue = this.rMask.get(poppedPos.x, poppedPos.y, poppedPos.z); + if (!isStacked(poppedMaskValue)) { + material = context.materialReadSpace.get(poppedPos.x, poppedPos.y, poppedPos.z); + contextMaterialHash = material.hashMaterialIds(); + if (!this.solidSet.test(contextMaterialHash)) { + stack.push(new Vector3i(poppedPos)); + this.rMask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); + } + } + } + + poppedPos.z--; + } + } + } + } + } + } + + this.rMaterialProviderContext.distanceToBiomeEdge = context.distanceToBiomeEdge; + + for (int var15 = this.rLocalWriteBounds.min.y; var15 < this.rLocalWriteBounds.max.y; var15++) { + for (int x = this.rLocalWriteBounds.min.x; x < this.rLocalWriteBounds.max.x; x++) { + for (int zxx = this.rLocalWriteBounds.min.z; zxx < this.rLocalWriteBounds.max.z; zxx++) { + int maskValuex = this.rMask.get(x, var15, zxx); + if (!isSolid(maskValuex) && !isLeaks(maskValuex)) { + this.rMaterialProviderContext.position.set(x, var15, zxx); + Material material = this.fillerMaterialProvider.getVoxelTypeAt(this.rMaterialProviderContext); + context.materialWriteSpace.set(material, x, var15, zxx); + } + } + } + } + + return true; + } + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.bounds; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.bounds; + } + + private static boolean isTraversed(int maskValue) { + return (maskValue & 1) == 1; + } + + private static boolean isLeaks(int maskValue) { + return (maskValue & 16) == 16; + } + + private static boolean isSolid(int maskValue) { + return (maskValue & 256) == 256; + } + + private static boolean isStacked(int maskValue) { + return (maskValue & 4096) == 4096; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/PrefabProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/PrefabProp.java new file mode 100644 index 00000000..8b366351 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/PrefabProp.java @@ -0,0 +1,252 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; +import com.hypixel.hytale.builtin.hytalegenerator.EntityPlacementData; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.FluidMaterial; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; +import com.hypixel.hytale.builtin.hytalegenerator.material.SolidMaterial; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.RotatedPosition; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.StaticDirectionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab.PrefabMoldingConfiguration; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab.PrefabPropUtil; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.DirectScanner; +import com.hypixel.hytale.common.util.ExceptionUtil; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.util.FastRandom; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.prefab.PrefabRotation; +import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall; +import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; +import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.function.Function; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; +import org.joml.Vector3i; + +public class PrefabProp extends Prop { + @Nonnull + private final Bounds3i writeBounds; + @Nonnull + private final WeightedMap> prefabPool; + @Nonnull + private final MaterialCache materialCache; + @Nonnull + private final RngField rngField; + @Nonnull + private final FastRandom random; + private final int prefabId; + @Nonnull + private final List childProps; + @Nonnull + private final List childPositions; + @Nonnull + private final Vector3i rPrefabPosition; + @Nonnull + private final PrefabProp.IntersectingColumnPredicate rColumnPredicate; + @Nonnull + private final Vector3i rWorldPosition; + @Nonnull + private final Vector3d rEntityWorldPosition; + @Nonnull + private final RotatedPosition rRotatedWorldPosition; + + public PrefabProp( + @Nonnull WeightedMap> prefabPool, + @Nonnull MaterialCache materialCache, + @Nonnull SeedBox seedBox, + @Nullable Function> childPrefabLoader + ) { + this.materialCache = materialCache; + this.rngField = new RngField(seedBox.createSupplier().get()); + this.random = new FastRandom(); + this.childProps = new ArrayList<>(0); + this.childPositions = new ArrayList<>(0); + this.prefabPool = new WeightedMap<>(); + this.writeBounds = new Bounds3i(); + prefabPool.forEach( + (sourceList, weight) -> { + if (!sourceList.isEmpty()) { + List prefabList = new ArrayList<>(); + + for (IPrefabBuffer prefab : sourceList) { + assert prefab != null; + + if (prefab == null) { + return; + } + + prefabList.add(prefab); + this.writeBounds.encompass(getWriteBounds(prefab)); + PrefabBuffer.ChildPrefab[] childPrefabs = prefab.getChildPrefabs(); + int childId = 0; + + for (PrefabBuffer.ChildPrefab child : childPrefabs) { + RotatedPosition childPosition = new RotatedPosition(child.getX(), child.getY(), child.getZ(), child.getRotation()); + String childPath = child.getPath().replace('.', '/'); + childPath = childPath.replace("*", ""); + List childPrefabBuffers = childPrefabLoader.apply(childPath); + WeightedMap> weightedChildPrefabs = new WeightedMap<>(); + weightedChildPrefabs.add(childPrefabBuffers, 1.0); + StaticDirectionality childDirectionality = new StaticDirectionality(child.getRotation(), ConstantPattern.INSTANCE_TRUE); + com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab.PrefabProp childProp = new com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab.PrefabProp( + weightedChildPrefabs, + new DirectScanner(), + childDirectionality, + materialCache, + new BlockMask(), + PrefabMoldingConfiguration.none(), + childPrefabLoader, + seedBox.child(String.valueOf(childId++)), + true + ); + this.childProps.add(childProp); + this.childPositions.add(childPosition); + Bounds3i localChildBounds = childProp.getWriteBounds_voxelGrid().clone(); + localChildBounds.offset(childPosition.x, childPosition.y, childPosition.z); + RotationTuple prefabRotationTuple = RotationTuple.of(childPosition.rotation.getRotation(), Rotation.None, Rotation.None); + localChildBounds.applyRotationAroundVoxel(prefabRotationTuple, Vector3iUtil.ZERO); + this.writeBounds.encompass(localChildBounds); + } + } + + this.prefabPool.add(prefabList, weight); + } + } + ); + this.prefabId = this.hashCode(); + this.rPrefabPosition = new Vector3i(); + this.rColumnPredicate = new PrefabProp.IntersectingColumnPredicate<>(); + this.rWorldPosition = new Vector3i(); + this.rEntityWorldPosition = new Vector3d(); + this.rRotatedWorldPosition = new RotatedPosition(0, 0, 0, PrefabRotation.ROTATION_0); + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + if (this.prefabPool.size() == 0) { + return true; + } else { + this.random.setSeed(this.rngField.get(context.position.x, context.position.y, context.position.z)); + PrefabBufferCall callInstance = new PrefabBufferCall(this.random, PrefabRotation.ROTATION_0); + IPrefabBuffer prefab = this.pickPrefab(this.random); + this.rPrefabPosition.set(context.position); + this.rColumnPredicate.bounds.assign(context.materialWriteSpace.getBounds()); + this.rColumnPredicate.bounds.offsetOpposite(context.position); + + try { + prefab.forEach( + this.rColumnPredicate, + (x, y, z, blockId, holder, support, rotation, filler, call, fluidId, fluidLevel) -> { + this.rWorldPosition.set(x + context.position.x, y + context.position.y, z + context.position.z); + if (context.materialWriteSpace.getBounds().contains(this.rWorldPosition)) { + SolidMaterial solid = this.materialCache.getSolidMaterial(blockId, support, rotation, filler, holder != null ? holder.clone() : null); + FluidMaterial fluid = this.materialCache.getFluidMaterial(fluidId, (byte)fluidLevel); + Material material = this.materialCache.getMaterial(solid, fluid); + context.materialWriteSpace.set(material, this.rWorldPosition); + } + }, + (cx, cz, entityWrappers, buffer) -> { + if (entityWrappers != null) { + for (int ix = 0; ix < entityWrappers.length; ix++) { + TransformComponent transformComp = entityWrappers[ix].getComponent(TransformComponent.getComponentType()); + if (transformComp != null) { + Vector3d localPosition = new Vector3d(transformComp.getPosition()); + buffer.rotation.rotate(localPosition); + this.rEntityWorldPosition.set(localPosition).add(context.position.x, context.position.y, context.position.z); + if (context.entityWriteBuffer.getBounds().contains(this.rEntityWorldPosition) + && context.materialWriteSpace.getBounds().contains(this.rEntityWorldPosition)) { + Holder entityClone = entityWrappers[ix].clone(); + transformComp = entityClone.getComponent(TransformComponent.getComponentType()); + if (transformComp != null) { + transformComp.getPosition().set(this.rEntityWorldPosition); + EntityPlacementData placementData = new EntityPlacementData( + new Vector3i(), PrefabRotation.ROTATION_0, entityClone, this.prefabId + ); + context.entityWriteBuffer.addEntity(placementData); + } + } + } + } + } + }, + (x, y, z, path, fitHeightmap, inheritSeed, inheritHeightCondition, weights, rotation, t) -> {}, + callInstance + ); + } catch (Exception var8) { + String msg = "Couldn't place prefab prop."; + msg = msg + "\n"; + msg = msg + ExceptionUtil.toStringWithStack(var8); + HytaleLogger.getLogger().atWarning().log(msg); + } + + this.rRotatedWorldPosition.x = context.position.x; + this.rRotatedWorldPosition.y = context.position.y; + this.rRotatedWorldPosition.z = context.position.z; + + for (int i = 0; i < this.childProps.size(); i++) { + com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab.PrefabProp prop = this.childProps.get(i); + RotatedPosition childPosition = this.childPositions.get(i).getRelativeTo(this.rRotatedWorldPosition); + Vector3i rotatedChildPositionVec = new Vector3i(childPosition.x, childPosition.y, childPosition.z); + this.rRotatedWorldPosition.rotation.rotate(rotatedChildPositionVec); + prop.place(childPosition, context.materialWriteSpace, context.entityWriteBuffer); + } + + return true; + } + } + + @Nonnull + private IPrefabBuffer pickPrefab(@Nonnull Random rand) { + List list = this.prefabPool.pick(rand); + int randomIndex = rand.nextInt(list.size()); + return list.get(randomIndex); + } + + @Nonnull + private static Bounds3i getWriteBounds(@Nonnull IPrefabBuffer prefab) { + Vector3i max = PrefabPropUtil.getMax(prefab, PrefabRotation.ROTATION_0); + max.add(1, 1, 1); + Vector3i min = PrefabPropUtil.getMin(prefab, PrefabRotation.ROTATION_0); + return new Bounds3i(min, max); + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return Bounds3i.ZERO; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds; + } + + private static class IntersectingColumnPredicate implements IPrefabBuffer.ColumnPredicate { + public Bounds3i bounds = new Bounds3i(); + + public IntersectingColumnPredicate() { + } + + @Override + public boolean test(int x, int z, int blocks, T o) { + return x >= this.bounds.min.x && x < this.bounds.max.x && z >= this.bounds.min.z && z < this.bounds.max.z; + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/Prop.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/Prop.java index 769f8483..7123d08f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/Prop.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/Prop.java @@ -1,21 +1,15 @@ package com.hypixel.hytale.builtin.hytalegenerator.props; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.engine.entityfunnel.EntityFunnel; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.EntityContainer; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.NullSpace; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; import javax.annotation.Nonnull; -import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; public abstract class Prop { - public abstract ScanResult scan(@Nonnull Vector3i var1, @Nonnull VoxelSpace var2, @Nonnull WorkerIndexer.Id var3); - - public abstract void place(@Nonnull Prop.Context var1); - - public abstract ContextDependency getContextDependency(); + public abstract boolean generate(@Nonnull Prop.Context var1); @Nonnull public abstract Bounds3i getReadBounds_voxelGrid(); @@ -23,74 +17,53 @@ public abstract class Prop { @Nonnull public abstract Bounds3i getWriteBounds_voxelGrid(); - @Nonnull - public static Prop noProp() { - final ScanResult scanResult = new ScanResult() { - @Override - public boolean isNegative() { - return true; - } - }; - final ContextDependency contextDependency = new ContextDependency(new Vector3i(), new Vector3i()); - final Bounds3i zeroBounds_voxelGrid = new Bounds3i(); - return new Prop() { - @Nonnull - @Override - public ScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - return scanResult; - } - - @Override - public void place(@Nonnull Prop.Context context) { - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return contextDependency; - } - - @NonNullDecl - @Override - public Bounds3i getReadBounds_voxelGrid() { - return zeroBounds_voxelGrid; - } - - @Nonnull - @Override - public Bounds3i getWriteBounds_voxelGrid() { - return zeroBounds_voxelGrid; - } - }; - } - public static class Context { - public ScanResult scanResult; - public VoxelSpace materialSpace; - public EntityContainer entityBuffer; - public WorkerIndexer.Id workerId; - public double distanceFromBiomeEdge; + @Nonnull + public Vector3i position; + @Nonnull + public VoxelSpace materialReadSpace; + @Nonnull + public VoxelSpace materialWriteSpace; + @Nonnull + public EntityFunnel entityWriteBuffer; + public double distanceToBiomeEdge; + + public Context() { + this.position = new Vector3i(); + this.materialReadSpace = NullSpace.instance(); + this.materialWriteSpace = NullSpace.instance(); + this.entityWriteBuffer = EntityFunnel.NULL; + this.distanceToBiomeEdge = 0.0; + } public Context( - @Nonnull ScanResult scanResult, - @Nonnull VoxelSpace materialSpace, - @Nonnull EntityContainer entityBuffer, - WorkerIndexer.Id workerId, - double distanceFromBiomeEdge + @Nonnull Vector3i position, + @Nonnull VoxelSpace materialReadSpace, + @Nonnull VoxelSpace materialWriteSpace, + @Nonnull EntityFunnel entityWriteBuffer, + double distanceToBiomeEdge ) { - this.scanResult = scanResult; - this.materialSpace = materialSpace; - this.entityBuffer = entityBuffer; - this.workerId = workerId; - this.distanceFromBiomeEdge = distanceFromBiomeEdge; + this.position = position; + this.materialReadSpace = materialReadSpace; + this.materialWriteSpace = materialWriteSpace; + this.entityWriteBuffer = entityWriteBuffer; + this.distanceToBiomeEdge = distanceToBiomeEdge; } public Context(@Nonnull Prop.Context other) { - this.scanResult = other.scanResult; - this.materialSpace = other.materialSpace; - this.entityBuffer = other.entityBuffer; - this.workerId = other.workerId; - this.distanceFromBiomeEdge = other.distanceFromBiomeEdge; + this.position = other.position; + this.materialReadSpace = other.materialReadSpace; + this.materialWriteSpace = other.materialWriteSpace; + this.entityWriteBuffer = other.entityWriteBuffer; + this.distanceToBiomeEdge = other.distanceToBiomeEdge; + } + + public void assign(@Nonnull Prop.Context other) { + this.position = other.position; + this.materialReadSpace = other.materialReadSpace; + this.materialWriteSpace = other.materialWriteSpace; + this.entityWriteBuffer = other.entityWriteBuffer; + this.distanceToBiomeEdge = other.distanceToBiomeEdge; } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/QueueProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/QueueProp.java index 0fd43d1b..81862849 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/QueueProp.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/QueueProp.java @@ -1,11 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.props; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; @@ -15,60 +10,37 @@ public class QueueProp extends Prop { @Nonnull private final List props; @Nonnull - private final ContextDependency contextDependency; - @Nonnull private final Bounds3i readBounds_voxelGrid; @Nonnull private final Bounds3i writeBounds_voxelGrid; - public QueueProp(@Nonnull List propsQueue) { - this.props = new ArrayList<>(propsQueue); - this.readBounds_voxelGrid = new Bounds3i(); - this.writeBounds_voxelGrid = new Bounds3i(); - Vector3i writeRange = new Vector3i(); - Vector3i readRange = new Vector3i(); + public QueueProp(@Nonnull List propChain) { + if (propChain.isEmpty()) { + this.props = List.of(); + this.readBounds_voxelGrid = new Bounds3i(); + this.writeBounds_voxelGrid = new Bounds3i(); + } else { + this.props = new ArrayList<>(propChain); + this.readBounds_voxelGrid = new Bounds3i(); + this.writeBounds_voxelGrid = new Bounds3i(); - for (Prop prop : propsQueue) { - writeRange = Vector3i.max(writeRange, prop.getContextDependency().getWriteRange()); - readRange = Vector3i.max(readRange, prop.getContextDependency().getReadRange()); - this.readBounds_voxelGrid.encompass(prop.getReadBounds_voxelGrid()); - this.writeBounds_voxelGrid.encompass(prop.getWriteBounds_voxelGrid()); + for (Prop prop : propChain) { + this.readBounds_voxelGrid.encompass(prop.getReadBounds_voxelGrid()); + this.writeBounds_voxelGrid.encompass(prop.getWriteBounds_voxelGrid()); + } } - - this.contextDependency = new ContextDependency(readRange, writeRange); } - @Nonnull @Override - public ScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - QueueProp.QueueScanResult queueScanResult = new QueueProp.QueueScanResult(); - + public boolean generate(@Nonnull Prop.Context context) { for (Prop prop : this.props) { - ScanResult propScanResult = prop.scan(position, materialSpace, id); - if (!propScanResult.isNegative()) { - queueScanResult.propScanResult = propScanResult; - queueScanResult.prop = prop; - return queueScanResult; + boolean hasGenerated = prop.generate(context); + if (hasGenerated) { + return true; } } - return queueScanResult; - } - - @Override - public void place(@Nonnull Prop.Context context) { - QueueProp.QueueScanResult conditionalScanResult = QueueProp.QueueScanResult.cast(context.scanResult); - if (!conditionalScanResult.isNegative()) { - Prop.Context childContext = new Prop.Context(context); - childContext.scanResult = conditionalScanResult.propScanResult; - conditionalScanResult.prop.place(childContext); - } - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency.clone(); + return false; } @NonNullDecl @@ -82,23 +54,4 @@ public class QueueProp extends Prop { public Bounds3i getWriteBounds_voxelGrid() { return this.writeBounds_voxelGrid; } - - private static class QueueScanResult implements ScanResult { - ScanResult propScanResult; - Prop prop; - - @Nonnull - public static QueueProp.QueueScanResult cast(ScanResult scanResult) { - if (!(scanResult instanceof QueueProp.QueueScanResult)) { - throw new IllegalArgumentException("The provided ScanResult isn't compatible with this prop."); - } else { - return (QueueProp.QueueScanResult)scanResult; - } - } - - @Override - public boolean isNegative() { - return this.propScanResult == null || this.propScanResult.isNegative(); - } - } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/StaticRotatorProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/StaticRotatorProp.java new file mode 100644 index 00000000..ff9a5899 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/StaticRotatorProp.java @@ -0,0 +1,63 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.entityfunnel.RotationEntityFunnel; +import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.RotationVoxelSpace; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; + +public class StaticRotatorProp extends Prop { + @Nonnull + private final Prop prop; + @Nonnull + private final RotationVoxelSpace readRotationVoxelSpace; + @Nonnull + private final RotationVoxelSpace writeRotationVoxelSpace; + @Nonnull + private final RotationEntityFunnel rotationEntityFunnel; + @Nonnull + private final Bounds3i readBounds; + @Nonnull + private final Bounds3i writeBounds; + @Nonnull + private final Prop.Context rChildContext; + + public StaticRotatorProp(@Nonnull Prop prop, @Nonnull RotationTuple rotation, @Nonnull MaterialCache materialCache) { + this.prop = prop; + this.readRotationVoxelSpace = new RotationVoxelSpace(rotation, materialCache); + this.writeRotationVoxelSpace = new RotationVoxelSpace(rotation, materialCache); + this.rotationEntityFunnel = new RotationEntityFunnel(rotation); + this.readBounds = prop.getReadBounds_voxelGrid().clone(); + this.writeBounds = prop.getWriteBounds_voxelGrid().clone(); + this.readBounds.applyRotationAroundVoxel(rotation, Vector3iUtil.ZERO); + this.writeBounds.applyRotationAroundVoxel(rotation, Vector3iUtil.ZERO); + this.rChildContext = new Prop.Context(); + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + this.readRotationVoxelSpace.setSource(context.materialReadSpace, context.position); + this.writeRotationVoxelSpace.setSource(context.materialWriteSpace, context.position); + this.rotationEntityFunnel.setSource(context.entityWriteBuffer, context.position); + this.rChildContext.assign(context); + this.rChildContext.materialReadSpace = this.readRotationVoxelSpace; + this.rChildContext.materialWriteSpace = this.writeRotationVoxelSpace; + this.rChildContext.entityWriteBuffer = this.rotationEntityFunnel; + return this.prop.generate(this.rChildContext); + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.readBounds; + } + + @NonNullDecl + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/UnionProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/UnionProp.java index 75228bed..0f79b9c6 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/UnionProp.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/UnionProp.java @@ -1,11 +1,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.props; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; @@ -15,58 +10,36 @@ public class UnionProp extends Prop { @Nonnull private final List props; @Nonnull - private final ContextDependency contextDependency; - @Nonnull private final Bounds3i readBounds_voxelGrid; @Nonnull private final Bounds3i writeBounds_voxelGrid; - public UnionProp(@Nonnull List propChain) { - this.props = new ArrayList<>(propChain); - this.readBounds_voxelGrid = new Bounds3i(); - this.writeBounds_voxelGrid = new Bounds3i(); - Vector3i writeRange = new Vector3i(); - Vector3i readRange = new Vector3i(); + public UnionProp(@Nonnull List props) { + if (props.isEmpty()) { + this.props = List.of(); + this.readBounds_voxelGrid = new Bounds3i(); + this.writeBounds_voxelGrid = new Bounds3i(); + } else { + this.props = new ArrayList<>(props); + this.readBounds_voxelGrid = new Bounds3i(); + this.writeBounds_voxelGrid = new Bounds3i(); - for (Prop prop : propChain) { - writeRange = Vector3i.max(writeRange, prop.getContextDependency().getWriteRange()); - readRange = Vector3i.max(readRange, prop.getContextDependency().getReadRange()); - this.readBounds_voxelGrid.encompass(prop.getReadBounds_voxelGrid()); - this.writeBounds_voxelGrid.encompass(prop.getWriteBounds_voxelGrid()); + for (Prop prop : props) { + this.readBounds_voxelGrid.encompass(prop.getReadBounds_voxelGrid()); + this.writeBounds_voxelGrid.encompass(prop.getWriteBounds_voxelGrid()); + } } - - this.contextDependency = new ContextDependency(readRange, writeRange); } - @Nonnull @Override - public ScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - UnionProp.ChainedScanResult scanResult = new UnionProp.ChainedScanResult(); - scanResult.scanResults = new ArrayList<>(this.props.size()); + public boolean generate(@Nonnull Prop.Context context) { + boolean hasGenerated = false; for (Prop prop : this.props) { - scanResult.scanResults.add(prop.scan(position, materialSpace, id)); + hasGenerated |= prop.generate(context); } - return scanResult; - } - - @Override - public void place(@Nonnull Prop.Context context) { - List scanResults = UnionProp.ChainedScanResult.cast(context.scanResult).scanResults; - - for (int i = 0; i < this.props.size(); i++) { - Prop prop = this.props.get(i); - Prop.Context childContext = new Prop.Context(context); - childContext.scanResult = scanResults.get(i); - prop.place(childContext); - } - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency.clone(); + return hasGenerated; } @NonNullDecl @@ -80,22 +53,4 @@ public class UnionProp extends Prop { public Bounds3i getWriteBounds_voxelGrid() { return this.writeBounds_voxelGrid; } - - private static class ChainedScanResult implements ScanResult { - List scanResults; - - @Nonnull - public static UnionProp.ChainedScanResult cast(ScanResult scanResult) { - if (!(scanResult instanceof UnionProp.ChainedScanResult)) { - throw new IllegalArgumentException("The provided ScanResult isn't compatible with this prop."); - } else { - return (UnionProp.ChainedScanResult)scanResult; - } - } - - @Override - public boolean isNegative() { - return this.scanResults == null || this.scanResults.isEmpty(); - } - } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/WeightedProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/WeightedProp.java index 68c4b5aa..3cd9e70f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/WeightedProp.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/WeightedProp.java @@ -1,14 +1,10 @@ package com.hypixel.hytale.builtin.hytalegenerator.props; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; -import com.hypixel.hytale.builtin.hytalegenerator.material.Material; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.Random; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.ScanResult; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.math.util.FastRandom; import javax.annotation.Nonnull; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; @@ -16,64 +12,39 @@ public class WeightedProp extends Prop { @Nonnull private final WeightedMap props; @Nonnull - private final ContextDependency contextDependency; - @Nonnull private final Bounds3i readBounds_voxelGrid; @Nonnull private final Bounds3i writeBounds_voxelGrid; @Nonnull - private final SeedGenerator seedGenerator; + private final RngField rngField; + @Nonnull + private final FastRandom random; public WeightedProp(@Nonnull WeightedMap props, int seed) { this.props = new WeightedMap<>(props); this.readBounds_voxelGrid = new Bounds3i(); this.writeBounds_voxelGrid = new Bounds3i(); - this.seedGenerator = new SeedGenerator(seed); - Vector3i writeRange = new Vector3i(); - Vector3i readRange = new Vector3i(); + this.rngField = new RngField(seed); + this.random = new FastRandom(); for (Prop prop : this.props.allElements()) { - writeRange = Vector3i.max(writeRange, prop.getContextDependency().getWriteRange()); - readRange = Vector3i.max(readRange, prop.getContextDependency().getReadRange()); this.readBounds_voxelGrid.encompass(prop.getReadBounds_voxelGrid()); this.writeBounds_voxelGrid.encompass(prop.getWriteBounds_voxelGrid()); } - - this.contextDependency = new ContextDependency(readRange, writeRange); } - @Nonnull @Override - public ScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { + public boolean generate(@NonNullDecl Prop.Context context) { if (this.props.size() == 0) { - return new WeightedProp.PickedScanResult(); + return false; } else { - Random rand = new Random(this.seedGenerator.seedAt((long)position.x, (long)position.y, (long)position.z)); - Prop pickedProp = this.props.pick(rand); - ScanResult scanResult = pickedProp.scan(position, materialSpace, id); - WeightedProp.PickedScanResult pickedScanResult = new WeightedProp.PickedScanResult(); - pickedScanResult.prop = pickedProp; - pickedScanResult.scanResult = scanResult; - return pickedScanResult; + int localSeed = this.rngField.get(context.position.x, context.position.y, context.position.z); + this.random.setSeed(localSeed); + Prop pickedProp = this.props.pick(this.random); + return pickedProp.generate(context); } } - @Override - public void place(@Nonnull Prop.Context context) { - if (!context.scanResult.isNegative()) { - WeightedProp.PickedScanResult pickedScanResult = WeightedProp.PickedScanResult.cast(context.scanResult); - Prop.Context childContext = new Prop.Context(context); - childContext.scanResult = pickedScanResult.scanResult; - pickedScanResult.prop.place(childContext); - } - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency.clone(); - } - @NonNullDecl @Override public Bounds3i getReadBounds_voxelGrid() { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/BoxProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/BoxProp.java new file mode 100644 index 00000000..ce7cddd7 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/BoxProp.java @@ -0,0 +1,102 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated; + +import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +@Deprecated +public class BoxProp extends Prop { + @Nonnull + private final Vector3i range; + @Nonnull + private final Material material; + @Nonnull + private final Scanner scanner; + @Nonnull + private final Pattern pattern; + @Nonnull + private final Bounds3i readBounds_voxelGrid; + @Nonnull + private final Bounds3i writeBounds_voxelGrid; + + public BoxProp(@Nonnull Vector3i range, @Nonnull Material material, @Nonnull Scanner scanner, @Nonnull Pattern pattern) { + if (VectorUtil.isAnySmaller(range, new Vector3i())) { + throw new IllegalArgumentException("negative range"); + } else { + this.range = new Vector3i(range); + this.material = material; + this.scanner = scanner; + this.pattern = pattern; + this.readBounds_voxelGrid = scanner.getBoundsWithPattern_voxelGrid(pattern); + this.writeBounds_voxelGrid = new Bounds3i(new Vector3i(this.range).negate(), new Vector3i(this.range).add(Vector3iUtil.ALL_ONES)); + this.writeBounds_voxelGrid.stack(pattern.getBounds_voxelGrid()); + } + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + PositionListScanResult scanResult = this.scan_deprecated(context.position, context.materialReadSpace, WorkerIndexer.Id.MAIN); + this.place_deprecated(context, scanResult); + return !scanResult.isNegative(); + } + + @Nonnull + public PositionListScanResult scan_deprecated(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { + List validPositions = new ArrayList<>(); + Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, validPositions); + this.scanner.scan(scannerContext); + return new PositionListScanResult(validPositions); + } + + public void place_deprecated(@Nonnull Prop.Context context, @Nonnull PositionListScanResult scanResult) { + List positions = scanResult.getPositions(); + if (positions != null) { + Bounds3i writeSpaceBounds_voxelGrid = context.materialWriteSpace.getBounds(); + + for (Vector3i position : positions) { + Bounds3i localBoxBounds_voxelGrid = this.writeBounds_voxelGrid.clone().offset(position); + if (localBoxBounds_voxelGrid.intersects(writeSpaceBounds_voxelGrid)) { + this.place(position, context.materialWriteSpace); + } + } + } + } + + private void place(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace) { + Vector3i min = new Vector3i(position).add(-this.range.x, 0, -this.range.z); + Vector3i max = new Vector3i(position).add(this.range.x, this.range.y + this.range.y, this.range.z); + + for (int x = min.x; x <= max.x; x++) { + for (int y = min.y; y <= max.y; y++) { + for (int z = min.z; z <= max.z; z++) { + if (materialSpace.getBounds().contains(x, y, z)) { + materialSpace.set(this.material, x, y, z); + } + } + } + } + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.readBounds_voxelGrid; + } + + @Nonnull + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds_voxelGrid; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ClusterProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ClusterProp.java new file mode 100644 index 00000000..62a8e3d0 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ClusterProp.java @@ -0,0 +1,153 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated; + +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.engine.entityfunnel.EntityFunnel; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.WindowVoxelSpace; +import com.hypixel.hytale.math.util.FastRandom; +import it.unimi.dsi.fastutil.doubles.Double2DoubleFunction; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class ClusterProp extends Prop { + @Nonnull + private final Double2DoubleFunction weightCurve; + @Nonnull + private final RngField rngField; + @Nonnull + private final WeightedMap propWeightedMap; + private final int range; + @Nonnull + private final Pattern pattern; + @Nonnull + private final Scanner scanner; + @Nonnull + private final Bounds3i readBounds_voxelGrid; + @Nonnull + private final Bounds3i writeBounds_voxelGrid; + @Nonnull + private final Prop.Context rChildContext; + + public ClusterProp( + int range, + @Nonnull Double2DoubleFunction weightCurve, + int seed, + @Nonnull WeightedMap propWeightedMap, + @Nonnull Pattern pattern, + @Nonnull Scanner scanner + ) { + if (range < 0) { + throw new IllegalArgumentException("negative range"); + } else { + this.range = range; + this.rngField = new RngField(seed); + this.weightCurve = weightCurve; + this.pattern = pattern; + this.scanner = scanner; + this.readBounds_voxelGrid = scanner.getBoundsWithPattern_voxelGrid(pattern); + this.writeBounds_voxelGrid = new Bounds3i(new Vector3i(-range, -10000, -range), new Vector3i(range, 10000, range)); + this.writeBounds_voxelGrid.stack(scanner.getBounds_voxelGrid()); + this.propWeightedMap = new WeightedMap<>(); + propWeightedMap.forEach((prop, weight) -> { + if (this.isColumnBounded(prop)) { + this.propWeightedMap.add(prop, propWeightedMap.get(prop)); + } + }); + this.rChildContext = new Prop.Context(); + } + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + if (this.propWeightedMap.size() == 0) { + return false; + } else { + PositionListScanResult scanResult = this.scan_deprecated(context.position, context.materialReadSpace); + this.place_deprecated(context, scanResult); + return !scanResult.isNegative(); + } + } + + @Nonnull + public PositionListScanResult scan_deprecated(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace) { + Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, new ArrayList<>()); + this.scanner.scan(scannerContext); + return new PositionListScanResult(scannerContext.validPositions_out); + } + + public void place_deprecated(@Nonnull Prop.Context context, @Nonnull PositionListScanResult scanResult) { + List positions = scanResult.getPositions(); + if (positions != null) { + for (Vector3i position : positions) { + this.place(position, context.materialReadSpace, context.materialWriteSpace, context.entityWriteBuffer, context.distanceToBiomeEdge); + } + } + } + + private boolean isColumnBounded(@Nonnull Prop prop) { + Bounds3i readBounds_voxelGrid = prop.getReadBounds_voxelGrid(); + Bounds3i writeBounds_voxelGrid = prop.getWriteBounds_voxelGrid(); + return readBounds_voxelGrid.min.x == 0 + && readBounds_voxelGrid.min.z == 0 + && readBounds_voxelGrid.max.x == 1 + && readBounds_voxelGrid.max.z == 1 + && writeBounds_voxelGrid.min.x == 0 + && writeBounds_voxelGrid.min.z == 0 + && writeBounds_voxelGrid.max.x == 1 + && writeBounds_voxelGrid.max.z == 1; + } + + private void place( + @Nonnull Vector3i position, + @Nonnull VoxelSpace materialReadSpace, + @Nonnull VoxelSpace materialWriteSpace, + @Nonnull EntityFunnel entityBuffer, + double distanceFromBiomeEdge + ) { + WindowVoxelSpace columnReadSpace = new WindowVoxelSpace<>(materialWriteSpace); + WindowVoxelSpace columnWriteSpace = new WindowVoxelSpace<>(materialWriteSpace); + Bounds3i writeBounds_voxelGrid = columnWriteSpace.getBounds(); + this.rChildContext.materialReadSpace = columnReadSpace; + this.rChildContext.materialWriteSpace = columnWriteSpace; + this.rChildContext.entityWriteBuffer = entityBuffer; + this.rChildContext.distanceToBiomeEdge = distanceFromBiomeEdge; + FastRandom random = new FastRandom(this.rngField.get(position.x, position.z)); + + for (int x = position.x - this.range; x < position.x + this.range; x++) { + for (int z = position.z - this.range; z < position.z + this.range; z++) { + double distance = Calculator.distance(x, z, position.x, position.z); + double density = this.weightCurve.get(distance); + if (!(random.nextDouble() > density)) { + Prop pickedProp = this.propWeightedMap.pick(random); + if (materialWriteSpace.getBounds().contains(x, writeBounds_voxelGrid.min.y, z)) { + columnWriteSpace.setBounds(x, writeBounds_voxelGrid.min.y, z, x + 1, writeBounds_voxelGrid.max.y, z + 1); + this.rChildContext.position.set(x, position.y, z); + pickedProp.generate(this.rChildContext); + } + } + } + } + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.readBounds_voxelGrid; + } + + @Nonnull + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds_voxelGrid; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/ColumnProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ColumnProp.java similarity index 66% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/ColumnProp.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ColumnProp.java index 5acc4a38..3a4b0c8a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/ColumnProp.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ColumnProp.java @@ -1,26 +1,25 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated; import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.RotatedPosition; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.RotatedPositionsScanResult; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.RotatedPosition; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.RotatedPositionsScanResult; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; +@Deprecated public class ColumnProp extends Prop { @Nonnull private final int[] yPositions; @@ -37,8 +36,6 @@ public class ColumnProp extends Prop { @Nonnull private final Scanner scanner; @Nonnull - private final ContextDependency contextDependency; - @Nonnull private final Directionality directionality; @Nonnull private final Bounds3i readBounds_voxelGrid; @@ -62,6 +59,8 @@ public class ColumnProp extends Prop { this.blocks90 = new Material[blocks.size()]; this.blocks180 = new Material[blocks.size()]; this.blocks270 = new Material[blocks.size()]; + int minY = Integer.MAX_VALUE; + int maxY = Integer.MIN_VALUE; for (int i = 0; i < this.yPositions.length; i++) { this.yPositions[i] = propYPositions.get(i); @@ -69,31 +68,36 @@ public class ColumnProp extends Prop { this.blocks90[i] = new Material(materialCache.getSolidMaterialRotatedY(blocks.get(i).solid(), Rotation.Ninety), blocks.get(i).fluid()); this.blocks180[i] = new Material(materialCache.getSolidMaterialRotatedY(blocks.get(i).solid(), Rotation.OneEighty), blocks.get(i).fluid()); this.blocks270[i] = new Material(materialCache.getSolidMaterialRotatedY(blocks.get(i).solid(), Rotation.TwoSeventy), blocks.get(i).fluid()); + minY = Math.min(minY, this.yPositions[i]); + maxY = Math.max(maxY, this.yPositions[i] + 1); } this.scanner = scanner; this.directionality = directionality; - SpaceSize writeSpace = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(1, 0, 1)); - writeSpace = SpaceSize.stack(writeSpace, scanner.readSpaceWith(directionality.getGeneralPattern())); - Vector3i writeRange = writeSpace.getRange(); - Vector3i readRange = directionality.getReadRangeWith(scanner); - this.contextDependency = new ContextDependency(readRange, writeRange); - this.readBounds_voxelGrid = this.contextDependency.getReadBounds_voxelGrid(); - this.writeBounds_voxelGrid = this.contextDependency.getWriteBounds_voxelGrid(); + this.readBounds_voxelGrid = directionality.getBoundsWith_voxelGrid(scanner); + this.writeBounds_voxelGrid = new Bounds3i(new Vector3i(0, minY, 0), new Vector3i(1, maxY, 1)); + this.writeBounds_voxelGrid.stack(scanner.getBounds_voxelGrid()); } } - @Nonnull @Override - public ScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - Scanner.Context scannerContext = new Scanner.Context(position, this.directionality.getGeneralPattern(), materialSpace, id); - List validPositions = this.scanner.scan(scannerContext); + public boolean generate(@NonNullDecl Prop.Context context) { + RotatedPositionsScanResult scanResult = this.scan_deprecated(context.position, context.materialReadSpace); + this.place_deprecated(context, scanResult); + return !scanResult.isNegative(); + } + + @Nonnull + public RotatedPositionsScanResult scan_deprecated(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace) { + List validPositions = new ArrayList<>(); + Scanner.Context scannerContext = new Scanner.Context(position, this.directionality.getGeneralPattern(), materialSpace, validPositions); + this.scanner.scan(scannerContext); Vector3i patternPosition = new Vector3i(); Pattern.Context patternContext = new Pattern.Context(patternPosition, materialSpace); RotatedPositionsScanResult scanResult = new RotatedPositionsScanResult(new ArrayList<>()); for (Vector3i validPosition : validPositions) { - patternPosition.assign(validPosition); + patternPosition.set(validPosition); PrefabRotation rotation = this.directionality.getRotationAt(patternContext); if (rotation != null) { scanResult.positions.add(new RotatedPosition(validPosition.x, validPosition.y, validPosition.z, rotation)); @@ -103,10 +107,9 @@ public class ColumnProp extends Prop { return scanResult; } - @Override - public void place(@Nonnull Prop.Context context) { - for (RotatedPosition position : RotatedPositionsScanResult.cast(context.scanResult).positions) { - this.place(position, context.materialSpace); + public void place_deprecated(@Nonnull Prop.Context context, @Nonnull RotatedPositionsScanResult scanResult) { + for (RotatedPosition position : scanResult.positions) { + this.place(position, context.materialWriteSpace); } } @@ -123,8 +126,8 @@ public class ColumnProp extends Prop { for (int i = 0; i < this.yPositions.length; i++) { int y = this.yPositions[i] + position.y; Material propBlock = blocks[i]; - if (materialSpace.isInsideSpace(position.x, y, position.z) && this.blockMask.canPlace(propBlock)) { - Material worldMaterial = materialSpace.getContent(position.x, y, position.z); + if (materialSpace.getBounds().contains(position.x, y, position.z) && this.blockMask.canPlace(propBlock)) { + Material worldMaterial = materialSpace.get(position.x, y, position.z); assert worldMaterial != null; @@ -136,12 +139,6 @@ public class ColumnProp extends Prop { } } - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency.clone(); - } - @NonNullDecl @Override public Bounds3i getReadBounds_voxelGrid() { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/DensityProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/DensityProp.java new file mode 100644 index 00000000..ea8b085d --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/DensityProp.java @@ -0,0 +1,210 @@ +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated; + +import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.density.Density; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.ArrayVoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class DensityProp extends Prop { + @Nonnull + private final Vector3i range; + @Nonnull + private final Density density; + @Nonnull + private final MaterialProvider materialProvider; + @Nonnull + private final Scanner scanner; + @Nonnull + private final Pattern pattern; + @Nonnull + private final BlockMask placementMask; + @Nonnull + private final Material defaultMaterial; + @Nonnull + private final Bounds3i readBounds_voxelGrid; + @Nonnull + private final Bounds3i writeBounds_voxelGrid; + + public DensityProp( + @Nonnull Vector3i range, + @Nonnull Density density, + @Nonnull MaterialProvider materialProvider, + @Nonnull Scanner scanner, + @Nonnull Pattern pattern, + @Nonnull BlockMask placementMask, + @Nonnull Material defaultMaterial + ) { + this.range = new Vector3i(range); + this.density = density; + this.materialProvider = materialProvider; + this.scanner = scanner; + this.pattern = pattern; + this.placementMask = placementMask; + this.defaultMaterial = defaultMaterial; + this.readBounds_voxelGrid = scanner.getBoundsWithPattern_voxelGrid(pattern); + this.writeBounds_voxelGrid = new Bounds3i(new Vector3i(-range.x, -range.y, -range.z), new Vector3i(range.x, range.y, range.z)); + this.writeBounds_voxelGrid.stack(scanner.getBounds_voxelGrid()); + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + PositionListScanResult scanResult = this.scan_deprecated(context.position, context.materialReadSpace); + this.place_deprecated(context, scanResult); + return !scanResult.isNegative(); + } + + @Nonnull + public PositionListScanResult scan_deprecated(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace) { + Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, new ArrayList<>()); + this.scanner.scan(scannerContext); + return new PositionListScanResult(scannerContext.validPositions_out); + } + + public void place_deprecated(@Nonnull Prop.Context context, @Nonnull PositionListScanResult scanResult) { + List positions = scanResult.getPositions(); + if (positions != null) { + for (Vector3i position : positions) { + this.place(position, context.materialWriteSpace); + } + } + } + + private void place(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace) { + Bounds3i bounds = materialSpace.getBounds(); + Vector3i min = new Vector3i(position).add(-this.range.x, -this.range.y, -this.range.z); + Vector3i max = new Vector3i(position).add(this.range.x, this.range.y, this.range.z); + Vector3i writeMin = Vector3iUtil.max(min, bounds.min); + Vector3i writeMax = Vector3iUtil.min(max, bounds.max); + int bottomInclusive = min.y; + int topExclusive = max.y; + int height = topExclusive - bottomInclusive; + ArrayVoxelSpace solidityBuffer = new ArrayVoxelSpace<>(new Bounds3i(min, max)); + Density.Context childContext = new Density.Context(); + childContext.densityAnchor = Vector3iUtil.toVector3d(position); + Vector3i itPosition = new Vector3i(position); + + for (itPosition.x = min.x; itPosition.x < max.x; itPosition.x++) { + for (itPosition.z = min.z; itPosition.z < max.z; itPosition.z++) { + for (itPosition.y = min.y; itPosition.y < max.y; itPosition.y++) { + if (solidityBuffer.getBounds().contains(itPosition.x, itPosition.y, itPosition.z)) { + childContext.position.x = itPosition.x; + childContext.position.y = itPosition.y; + childContext.position.z = itPosition.z; + double densityValue = this.density.process(childContext); + solidityBuffer.set(densityValue > 0.0, itPosition.x, itPosition.y, itPosition.z); + } + } + } + } + + for (itPosition.x = min.x; itPosition.x < max.x; itPosition.x++) { + for (itPosition.z = min.z; itPosition.z < max.z; itPosition.z++) { + int[] depthIntoCeiling = new int[height + 1]; + int[] depthIntoFloor = new int[height + 1]; + int[] spaceBelowCeiling = new int[height + 1]; + int[] spaceAboveFloor = new int[height + 1]; + + for (itPosition.y = topExclusive - 1; itPosition.y >= bottomInclusive; itPosition.y--) { + int i = itPosition.y - bottomInclusive; + boolean density = solidityBuffer.get(itPosition.x, itPosition.y, itPosition.z); + if (itPosition.y == topExclusive - 1) { + if (density) { + depthIntoFloor[i] = 1; + } else { + depthIntoFloor[i] = 0; + } + + spaceAboveFloor[i] = 1073741823; + } else if (density) { + depthIntoFloor[i] = depthIntoFloor[i + 1] + 1; + spaceAboveFloor[i] = spaceAboveFloor[i + 1]; + } else { + depthIntoFloor[i] = 0; + if (solidityBuffer.get(itPosition.x, itPosition.y + 1, itPosition.z)) { + spaceAboveFloor[i] = 0; + } else { + spaceAboveFloor[i] = spaceAboveFloor[i + 1] + 1; + } + } + } + + for (itPosition.y = bottomInclusive; itPosition.y < topExclusive; itPosition.y++) { + int i = itPosition.y - bottomInclusive; + boolean density = solidityBuffer.get(itPosition.x, itPosition.y, itPosition.z); + if (itPosition.y == bottomInclusive) { + if (density) { + depthIntoCeiling[i] = 1; + } else { + depthIntoCeiling[i] = 0; + } + + spaceBelowCeiling[i] = Integer.MAX_VALUE; + } else if (density) { + depthIntoCeiling[i] = depthIntoCeiling[i - 1] + 1; + spaceBelowCeiling[i] = spaceBelowCeiling[i - 1]; + } else { + depthIntoCeiling[i] = 0; + if (solidityBuffer.get(itPosition.x, itPosition.y - 1, itPosition.z)) { + spaceBelowCeiling[i] = 0; + } else { + spaceBelowCeiling[i] = spaceBelowCeiling[i - 1] + 1; + } + } + } + + for (itPosition.y = topExclusive - 1; itPosition.y >= bottomInclusive; itPosition.y--) { + if (itPosition.x >= writeMin.x + && itPosition.y >= writeMin.y + && itPosition.z >= writeMin.z + && itPosition.x < writeMax.x + && itPosition.y < writeMax.y + && itPosition.z < writeMax.z) { + int i = itPosition.y - bottomInclusive; + MaterialProvider.Context materialContext = new MaterialProvider.Context( + position, 0.0, depthIntoFloor[i], depthIntoCeiling[i], spaceAboveFloor[i], spaceBelowCeiling[i], functionPosition -> { + childContext.position = Vector3iUtil.toVector3d(functionPosition); + return this.density.process(childContext); + }, childContext.distanceToBiomeEdge + ); + Material material = this.materialProvider.getVoxelTypeAt(materialContext); + if (material == null) { + material = this.defaultMaterial; + } + + if (this.placementMask.canPlace(material)) { + Material worldMaterial = materialSpace.get(itPosition.x, itPosition.y, itPosition.z); + int worldMaterialHash = worldMaterial.hashMaterialIds(); + if (this.placementMask.canReplace(material.hashCode(), worldMaterialHash)) { + materialSpace.set(material, itPosition.x, itPosition.y, itPosition.z); + } + } + } + } + } + } + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.readBounds_voxelGrid; + } + + @Nonnull + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds_voxelGrid; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/PositionListScanResult.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/PositionListScanResult.java similarity index 89% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/PositionListScanResult.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/PositionListScanResult.java index 1ff3a7ec..3aa3fc86 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/PositionListScanResult.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/PositionListScanResult.java @@ -1,9 +1,9 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PositionListScanResult implements ScanResult { private List positions; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/PositionScanResult.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/PositionScanResult.java similarity index 76% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/PositionScanResult.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/PositionScanResult.java index a6bef1a7..37f4287a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/PositionScanResult.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/PositionScanResult.java @@ -1,21 +1,21 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PositionScanResult implements ScanResult { private Vector3i position; public PositionScanResult(@Nullable Vector3i position) { if (position != null) { - this.position = position.clone(); + this.position = new Vector3i(position); } } @Nullable public Vector3i getPosition() { - return this.position == null ? null : this.position.clone(); + return this.position == null ? null : new Vector3i(this.position); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/ScanResult.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ScanResult.java similarity index 81% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/ScanResult.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ScanResult.java index 622f969a..42ad0b32 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/ScanResult.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/ScanResult.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/Directionality.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/Directionality.java similarity index 64% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/Directionality.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/Directionality.java index 94e1603b..5d5fcb1f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/Directionality.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/Directionality.java @@ -1,22 +1,27 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.directionality; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public abstract class Directionality { @Nullable public abstract PrefabRotation getRotationAt(@Nonnull Pattern.Context var1); + @Nonnull public abstract Pattern getGeneralPattern(); - public abstract Vector3i getReadRangeWith(@Nonnull Scanner var1); + @Nonnull + public abstract Bounds3i getBoundsWith_voxelGrid(@Nonnull Scanner var1); + @Nonnull public abstract List getPossibleRotations(); @Nonnull @@ -30,13 +35,13 @@ public abstract class Directionality { @Nonnull @Override public Pattern getGeneralPattern() { - return Pattern.noPattern(); + return ConstantPattern.INSTANCE_FALSE; } - @Nonnull + @NonNullDecl @Override - public Vector3i getReadRangeWith(@Nonnull Scanner scanner) { - return new Vector3i(); + public Bounds3i getBoundsWith_voxelGrid(@NonNullDecl Scanner scanner) { + return Bounds3i.ZERO; } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/OrthogonalDirection.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/OrthogonalDirection.java similarity index 80% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/OrthogonalDirection.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/OrthogonalDirection.java index f3dd0e18..c399c1b0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/OrthogonalDirection.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/OrthogonalDirection.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.directionality; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.codecs.EnumCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/PatternDirectionality.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/PatternDirectionality.java similarity index 77% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/PatternDirectionality.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/PatternDirectionality.java index 6e3a1f9e..d95bd4a0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/PatternDirectionality.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/PatternDirectionality.java @@ -1,16 +1,17 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.directionality; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.builtin.hytalegenerator.patterns.OrPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class PatternDirectionality extends Directionality { @Nonnull @@ -34,7 +35,9 @@ public class PatternDirectionality extends Directionality { @Nonnull private final Pattern generalPattern; @Nonnull - private final SeedGenerator seedGenerator; + private final RngField rngField; + @Nonnull + private final Bounds3i bounds_voxelGrid; public PatternDirectionality( @Nonnull OrthogonalDirection startingDirection, @@ -49,7 +52,12 @@ public class PatternDirectionality extends Directionality { this.eastPattern = eastPattern; this.westPattern = westPattern; this.generalPattern = new OrPattern(List.of(northPattern, southPattern, eastPattern, westPattern)); - this.seedGenerator = new SeedGenerator(seed); + this.rngField = new RngField(seed); + this.bounds_voxelGrid = this.generalPattern.getBounds_voxelGrid().clone(); + this.bounds_voxelGrid.encompass(southPattern.getBounds_voxelGrid()); + this.bounds_voxelGrid.encompass(northPattern.getBounds_voxelGrid()); + this.bounds_voxelGrid.encompass(eastPattern.getBounds_voxelGrid()); + this.bounds_voxelGrid.encompass(westPattern.getBounds_voxelGrid()); switch (startingDirection) { case S: this.south = PrefabRotation.ROTATION_0; @@ -85,10 +93,10 @@ public class PatternDirectionality extends Directionality { return this.generalPattern; } - @Nonnull + @NonNullDecl @Override - public Vector3i getReadRangeWith(@Nonnull Scanner scanner) { - return scanner.readSpaceWith(this.generalPattern).getRange(); + public Bounds3i getBoundsWith_voxelGrid(@NonNullDecl Scanner scanner) { + return this.bounds_voxelGrid; } @Nonnull @@ -119,7 +127,7 @@ public class PatternDirectionality extends Directionality { if (successful.isEmpty()) { return null; } else { - FastRandom random = new FastRandom(this.seedGenerator.seedAt((long)context.position.x, (long)context.position.y, (long)context.position.z)); + FastRandom random = new FastRandom(this.rngField.get(context.position.x, context.position.y, context.position.z)); return successful.get(random.nextInt(successful.size())); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RandomDirectionality.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RandomDirectionality.java similarity index 65% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RandomDirectionality.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RandomDirectionality.java index accae28f..5b73746b 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RandomDirectionality.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RandomDirectionality.java @@ -1,14 +1,15 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.directionality; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class RandomDirectionality extends Directionality { @Nonnull @@ -16,11 +17,11 @@ public class RandomDirectionality extends Directionality { @Nonnull private final Pattern pattern; @Nonnull - private final SeedGenerator seedGenerator; + private final RngField rngField; public RandomDirectionality(@Nonnull Pattern pattern, int seed) { this.pattern = pattern; - this.seedGenerator = new SeedGenerator(seed); + this.rngField = new RngField(seed); this.rotations = Collections.unmodifiableList( List.of(PrefabRotation.ROTATION_0, PrefabRotation.ROTATION_90, PrefabRotation.ROTATION_180, PrefabRotation.ROTATION_270) ); @@ -32,10 +33,10 @@ public class RandomDirectionality extends Directionality { return this.pattern; } - @Nonnull + @NonNullDecl @Override - public Vector3i getReadRangeWith(@Nonnull Scanner scanner) { - return scanner.readSpaceWith(this.pattern).getRange(); + public Bounds3i getBoundsWith_voxelGrid(@NonNullDecl Scanner scanner) { + return this.pattern.getBounds_voxelGrid(); } @Nonnull @@ -46,7 +47,7 @@ public class RandomDirectionality extends Directionality { @Override public PrefabRotation getRotationAt(@Nonnull Pattern.Context context) { - FastRandom random = new FastRandom(this.seedGenerator.seedAt((long)context.position.x, (long)context.position.y, (long)context.position.z)); + FastRandom random = new FastRandom(this.rngField.get(context.position.x, context.position.y, context.position.z)); return this.rotations.get(random.nextInt(this.rotations.size())); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RotatedPosition.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RotatedPosition.java similarity index 77% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RotatedPosition.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RotatedPosition.java index b4657bce..3e51c492 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RotatedPosition.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RotatedPosition.java @@ -1,15 +1,15 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.directionality; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class RotatedPosition { - public final int x; - public final int y; - public final int z; + public int x; + public int y; + public int z; @Nonnull - public final PrefabRotation rotation; + public PrefabRotation rotation; public RotatedPosition(int x, int y, int z, @Nonnull PrefabRotation rotation) { this.x = x; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RotatedPositionsScanResult.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RotatedPositionsScanResult.java similarity index 82% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RotatedPositionsScanResult.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RotatedPositionsScanResult.java index 721d0e85..c39182c0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/RotatedPositionsScanResult.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/RotatedPositionsScanResult.java @@ -1,6 +1,6 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.directionality; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.ScanResult; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.ScanResult; import java.util.List; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/StaticDirectionality.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/StaticDirectionality.java similarity index 75% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/StaticDirectionality.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/StaticDirectionality.java index 4342c207..3dcb31b5 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/directionality/StaticDirectionality.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/directionality/StaticDirectionality.java @@ -1,12 +1,13 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.directionality; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class StaticDirectionality extends Directionality { @Nonnull @@ -33,10 +34,10 @@ public class StaticDirectionality extends Directionality { return this.pattern; } - @Nonnull + @NonNullDecl @Override - public Vector3i getReadRangeWith(@Nonnull Scanner scanner) { - return scanner.readSpaceWith(this.pattern).getRange(); + public Bounds3i getBoundsWith_voxelGrid(@NonNullDecl Scanner scanner) { + return this.pattern.getBounds_voxelGrid(); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/filler/FillerPropScanResult.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/filler/FillerPropScanResult.java similarity index 82% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/filler/FillerPropScanResult.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/filler/FillerPropScanResult.java index 28f24fe9..178e7572 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/filler/FillerPropScanResult.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/filler/FillerPropScanResult.java @@ -1,10 +1,10 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.filler; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.filler; -import com.hypixel.hytale.builtin.hytalegenerator.props.ScanResult; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.ScanResult; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class FillerPropScanResult implements ScanResult { private List positions; @@ -15,11 +15,6 @@ public class FillerPropScanResult implements ScanResult { } } - @Nullable - public List getFluidBlocks() { - return this.positions; - } - @Nonnull public static FillerPropScanResult cast(ScanResult scanResult) { if (!(scanResult instanceof FillerPropScanResult)) { @@ -29,6 +24,11 @@ public class FillerPropScanResult implements ScanResult { } } + @Nullable + public List getFluidBlocks() { + return this.positions; + } + @Override public boolean isNegative() { return this.positions == null || this.positions.isEmpty(); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/filler/PondFillerProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/filler/PondFillerProp.java similarity index 55% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/filler/PondFillerProp.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/filler/PondFillerProp.java index d05ef307..ef55a1d4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/filler/PondFillerProp.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/filler/PondFillerProp.java @@ -1,23 +1,22 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.filler; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.filler; import com.hypixel.hytale.builtin.hytalegenerator.MaterialSet; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.ArrayVoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.materialproviders.MaterialProvider; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.ScanResult; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.ArrayVoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; +import com.hypixel.hytale.math.vector.Vector3iUtil; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; public class PondFillerProp extends Prop { private static final int TRAVERSED = 1; @@ -29,7 +28,7 @@ public class PondFillerProp extends Prop { @Nonnull private final Vector3i boundingMax; @Nonnull - private final MaterialProvider filledMaterialProvider; + private final MaterialProvider fillerMaterialProvider; @Nonnull private final MaterialSet solidSet; @Nonnull @@ -37,8 +36,6 @@ public class PondFillerProp extends Prop { @Nonnull private final Pattern pattern; @Nonnull - private final ContextDependency contextDependency; - @Nonnull private final Bounds3i readBounds_voxelGrid; @Nonnull private final Bounds3i writeBounds_voxelGrid; @@ -47,203 +44,19 @@ public class PondFillerProp extends Prop { @Nonnull Vector3i boundingMin, @Nonnull Vector3i boundingMax, @Nonnull MaterialSet solidSet, - @Nonnull MaterialProvider filledMaterialProvider, + @Nonnull MaterialProvider fillerMaterialProvider, @Nonnull Scanner scanner, @Nonnull Pattern pattern ) { - this.boundingMin = boundingMin.clone(); - this.boundingMax = boundingMax.clone(); + this.boundingMin = new Vector3i(boundingMin); + this.boundingMax = new Vector3i(boundingMax); this.solidSet = solidSet; - this.filledMaterialProvider = filledMaterialProvider; + this.fillerMaterialProvider = fillerMaterialProvider; this.scanner = scanner; this.pattern = pattern; - SpaceSize boundingSpace = new SpaceSize(boundingMin, boundingMax); - boundingSpace = SpaceSize.stack(boundingSpace, scanner.readSpaceWith(pattern)); - SpaceSize.stack(scanner.readSpaceWith(pattern), boundingSpace); - Vector3i range = boundingSpace.getRange(); - this.contextDependency = new ContextDependency(range, range); - this.readBounds_voxelGrid = this.contextDependency.getReadBounds_voxelGrid(); - this.writeBounds_voxelGrid = this.contextDependency.getWriteBounds_voxelGrid(); - } - - @Nonnull - public FillerPropScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, id); - List scanResults = this.scanner.scan(scannerContext); - if (scanResults.size() == 1) { - List resultList = this.renderFluidBlocks(scanResults.getFirst(), materialSpace); - return new FillerPropScanResult(resultList); - } else { - ArrayList resultList = new ArrayList<>(); - - for (Vector3i scanPosition : scanResults) { - List renderResult = this.renderFluidBlocks(scanPosition, materialSpace); - resultList.addAll(renderResult); - } - - return new FillerPropScanResult(resultList); - } - } - - @Nonnull - private List renderFluidBlocks(@Nonnull Vector3i origin, @Nonnull VoxelSpace materialSpace) { - Vector3i min = this.boundingMin.clone().add(origin); - Vector3i max = this.boundingMax.clone().add(origin); - min = Vector3i.max(min, new Vector3i(materialSpace.minX(), materialSpace.minY(), materialSpace.minZ())); - max = Vector3i.min(max, new Vector3i(materialSpace.maxX(), materialSpace.maxY(), materialSpace.maxZ())); - ArrayVoxelSpace mask = new ArrayVoxelSpace<>(max.x - min.x, max.y - min.y, max.z - min.z); - mask.setOrigin(-min.x, -min.y, -min.z); - mask.set(0); - int y = min.y; - - for (int x = min.x; x < max.x; x++) { - for (int z = min.z; z < max.z; z++) { - Material material = materialSpace.getContent(x, y, z); - int contextMaterialHash = material.hashMaterialIds(); - int maskValue = 1; - if (this.solidSet.test(contextMaterialHash)) { - maskValue |= 256; - mask.set(maskValue, x, y, z); - } else { - maskValue |= 16; - mask.set(maskValue, x, y, z); - } - } - } - - for (int var29 = min.y + 1; var29 < max.y; var29++) { - int underY = var29 - 1; - - for (int x = min.x; x < max.x; x++) { - for (int zx = min.z; zx < max.z; zx++) { - if (!isTraversed(mask.getContent(x, var29, zx))) { - int maskValueUnder = mask.getContent(x, underY, zx); - Material material = materialSpace.getContent(x, var29, zx); - int contextMaterialHash = material.hashMaterialIds(); - if (this.solidSet.test(contextMaterialHash)) { - int maskValue = 0; - maskValue |= 1; - maskValue |= 256; - mask.set(maskValue, x, var29, zx); - } else if (isLeaks(maskValueUnder) || x == min.x || x == max.x - 1 || zx == min.z || zx == max.z - 1) { - ArrayDeque stack = new ArrayDeque<>(); - stack.push(new Vector3i(x, var29, zx)); - mask.set(4096, x, var29, zx); - - while (!stack.isEmpty()) { - Vector3i poppedPos = stack.pop(); - int maskValue = mask.getContent(poppedPos.x, poppedPos.y, poppedPos.z); - maskValue |= 16; - mask.set(maskValue, poppedPos.x, poppedPos.y, poppedPos.z); - poppedPos.x--; - if (mask.isInsideSpace(poppedPos.x, poppedPos.y, poppedPos.z)) { - int poppedMaskValue = mask.getContent(poppedPos.x, poppedPos.y, poppedPos.z); - if (!isStacked(poppedMaskValue)) { - material = materialSpace.getContent(poppedPos.x, poppedPos.y, poppedPos.z); - contextMaterialHash = material.hashMaterialIds(); - if (!this.solidSet.test(contextMaterialHash)) { - stack.push(poppedPos.clone()); - mask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); - } - } - } - - poppedPos.x += 2; - if (mask.isInsideSpace(poppedPos.x, poppedPos.y, poppedPos.z)) { - int poppedMaskValue = mask.getContent(poppedPos.x, poppedPos.y, poppedPos.z); - if (!isStacked(poppedMaskValue)) { - material = materialSpace.getContent(poppedPos.x, poppedPos.y, poppedPos.z); - contextMaterialHash = material.hashMaterialIds(); - if (!this.solidSet.test(contextMaterialHash)) { - stack.push(poppedPos.clone()); - mask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); - } - } - } - - poppedPos.x--; - poppedPos.z--; - if (mask.isInsideSpace(poppedPos.x, poppedPos.y, poppedPos.z)) { - int poppedMaskValue = mask.getContent(poppedPos.x, poppedPos.y, poppedPos.z); - if (!isStacked(poppedMaskValue)) { - material = materialSpace.getContent(poppedPos.x, var29, poppedPos.z); - contextMaterialHash = material.hashMaterialIds(); - if (!this.solidSet.test(contextMaterialHash)) { - stack.push(poppedPos.clone()); - mask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); - } - } - } - - poppedPos.z += 2; - if (mask.isInsideSpace(poppedPos.x, poppedPos.y, poppedPos.z)) { - int poppedMaskValue = mask.getContent(poppedPos.x, poppedPos.y, poppedPos.z); - if (!isStacked(poppedMaskValue)) { - material = materialSpace.getContent(poppedPos.x, poppedPos.y, poppedPos.z); - contextMaterialHash = material.hashMaterialIds(); - if (!this.solidSet.test(contextMaterialHash)) { - stack.push(poppedPos.clone()); - mask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); - } - } - } - - poppedPos.z--; - } - } - } - } - } - } - - ArrayList fluidBlocks = new ArrayList<>(); - - for (int var30 = mask.minY() + 1; var30 < mask.maxY(); var30++) { - for (int x = mask.minX() + 1; x < mask.maxX() - 1; x++) { - for (int zxx = mask.minZ() + 1; zxx < mask.maxZ() - 1; zxx++) { - int maskValuex = mask.getContent(x, var30, zxx); - if (!isSolid(maskValuex) && !isLeaks(maskValuex)) { - fluidBlocks.add(new Vector3i(x, var30, zxx)); - } - } - } - } - - return fluidBlocks; - } - - @Override - public void place(@Nonnull Prop.Context context) { - List fluidBlocks = FillerPropScanResult.cast(context.scanResult).getFluidBlocks(); - if (fluidBlocks != null) { - for (Vector3i position : fluidBlocks) { - if (context.materialSpace.isInsideSpace(position.x, position.y, position.z)) { - MaterialProvider.Context materialsContext = new MaterialProvider.Context(position, 0.0, 0, 0, 0, 0, null, context.distanceFromBiomeEdge); - Material material = this.filledMaterialProvider.getVoxelTypeAt(materialsContext); - if (material != null) { - context.materialSpace.set(material, position.x, position.y, position.z); - } - } - } - } - } - - @Nonnull - @Override - public ContextDependency getContextDependency() { - return this.contextDependency.clone(); - } - - @NonNullDecl - @Override - public Bounds3i getReadBounds_voxelGrid() { - return this.readBounds_voxelGrid; - } - - @Nonnull - @Override - public Bounds3i getWriteBounds_voxelGrid() { - return this.writeBounds_voxelGrid; + this.readBounds_voxelGrid = this.scanner.getBoundsWithPattern_voxelGrid(pattern); + this.writeBounds_voxelGrid = new Bounds3i(boundingMin, boundingMax); + this.writeBounds_voxelGrid.stack(this.readBounds_voxelGrid); } private static boolean isTraversed(int maskValue) { @@ -261,4 +74,184 @@ public class PondFillerProp extends Prop { private static boolean isStacked(int maskValue) { return (maskValue & 4096) == 4096; } + + @Nonnull + public FillerPropScanResult scan_deprecated(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace) { + Scanner.Context scannerContext = new Scanner.Context(position, this.pattern, materialSpace, new ArrayList<>()); + this.scanner.scan(scannerContext); + if (scannerContext.validPositions_out.size() == 1) { + List resultList = this.renderFluidBlocks(scannerContext.validPositions_out.getFirst(), materialSpace); + return new FillerPropScanResult(resultList); + } else { + ArrayList resultList = new ArrayList<>(); + + for (Vector3i scanPosition : scannerContext.validPositions_out) { + List renderResult = this.renderFluidBlocks(scanPosition, materialSpace); + resultList.addAll(renderResult); + } + + return new FillerPropScanResult(resultList); + } + } + + @Nonnull + private List renderFluidBlocks(@Nonnull Vector3i origin, @Nonnull VoxelSpace materialSpace) { + Vector3i min = new Vector3i(this.boundingMin).add(origin); + Vector3i max = new Vector3i(this.boundingMax).add(origin); + min = Vector3iUtil.max(min, materialSpace.getBounds().min); + max = Vector3iUtil.min(max, materialSpace.getBounds().max); + Bounds3i maskBounds = new Bounds3i(min, max); + ArrayVoxelSpace mask = new ArrayVoxelSpace<>(new Bounds3i(min, max)); + mask.setAll(0); + int y = min.y; + + for (int x = min.x; x < max.x; x++) { + for (int z = min.z; z < max.z; z++) { + Material material = materialSpace.get(x, y, z); + int contextMaterialHash = material.hashMaterialIds(); + int maskValue = 1; + if (this.solidSet.test(contextMaterialHash)) { + maskValue |= 256; + mask.set(maskValue, x, y, z); + } else { + maskValue |= 16; + mask.set(maskValue, x, y, z); + } + } + } + + for (int var30 = min.y + 1; var30 < max.y; var30++) { + int underY = var30 - 1; + + for (int x = min.x; x < max.x; x++) { + for (int zx = min.z; zx < max.z; zx++) { + if (!isTraversed(mask.get(x, var30, zx))) { + int maskValueUnder = mask.get(x, underY, zx); + Material material = materialSpace.get(x, var30, zx); + int contextMaterialHash = material.hashMaterialIds(); + if (this.solidSet.test(contextMaterialHash)) { + int maskValue = 0; + maskValue |= 1; + maskValue |= 256; + mask.set(maskValue, x, var30, zx); + } else if (isLeaks(maskValueUnder) || x == min.x || x == max.x - 1 || zx == min.z || zx == max.z - 1) { + ArrayDeque stack = new ArrayDeque<>(); + stack.push(new Vector3i(x, var30, zx)); + mask.set(4096, x, var30, zx); + + while (!stack.isEmpty()) { + Vector3i poppedPos = stack.pop(); + int maskValue = mask.get(poppedPos.x, poppedPos.y, poppedPos.z); + maskValue |= 16; + mask.set(maskValue, poppedPos.x, poppedPos.y, poppedPos.z); + poppedPos.x--; + if (mask.getBounds().contains(poppedPos.x, poppedPos.y, poppedPos.z)) { + int poppedMaskValue = mask.get(poppedPos.x, poppedPos.y, poppedPos.z); + if (!isStacked(poppedMaskValue)) { + material = materialSpace.get(poppedPos.x, poppedPos.y, poppedPos.z); + contextMaterialHash = material.hashMaterialIds(); + if (!this.solidSet.test(contextMaterialHash)) { + stack.push(new Vector3i(poppedPos)); + mask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); + } + } + } + + poppedPos.x += 2; + if (mask.getBounds().contains(poppedPos.x, poppedPos.y, poppedPos.z)) { + int poppedMaskValue = mask.get(poppedPos.x, poppedPos.y, poppedPos.z); + if (!isStacked(poppedMaskValue)) { + material = materialSpace.get(poppedPos.x, poppedPos.y, poppedPos.z); + contextMaterialHash = material.hashMaterialIds(); + if (!this.solidSet.test(contextMaterialHash)) { + stack.push(new Vector3i(poppedPos)); + mask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); + } + } + } + + poppedPos.x--; + poppedPos.z--; + if (mask.getBounds().contains(poppedPos.x, poppedPos.y, poppedPos.z)) { + int poppedMaskValue = mask.get(poppedPos.x, poppedPos.y, poppedPos.z); + if (!isStacked(poppedMaskValue)) { + material = materialSpace.get(poppedPos.x, var30, poppedPos.z); + contextMaterialHash = material.hashMaterialIds(); + if (!this.solidSet.test(contextMaterialHash)) { + stack.push(new Vector3i(poppedPos)); + mask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); + } + } + } + + poppedPos.z += 2; + if (mask.getBounds().contains(poppedPos.x, poppedPos.y, poppedPos.z)) { + int poppedMaskValue = mask.get(poppedPos.x, poppedPos.y, poppedPos.z); + if (!isStacked(poppedMaskValue)) { + material = materialSpace.get(poppedPos.x, poppedPos.y, poppedPos.z); + contextMaterialHash = material.hashMaterialIds(); + if (!this.solidSet.test(contextMaterialHash)) { + stack.push(new Vector3i(poppedPos)); + mask.set(4096 | poppedMaskValue, poppedPos.x, poppedPos.y, poppedPos.z); + } + } + } + + poppedPos.z--; + } + } + } + } + } + } + + ArrayList fluidBlocks = new ArrayList<>(); + + for (int var31 = maskBounds.min.y + 1; var31 < maskBounds.max.y; var31++) { + for (int x = maskBounds.min.x + 1; x < maskBounds.max.x - 1; x++) { + for (int zxx = maskBounds.min.z + 1; zxx < maskBounds.max.z - 1; zxx++) { + int maskValuex = mask.get(x, var31, zxx); + if (!isSolid(maskValuex) && !isLeaks(maskValuex)) { + fluidBlocks.add(new Vector3i(x, var31, zxx)); + } + } + } + } + + return fluidBlocks; + } + + public void place_deprecated(@Nonnull Prop.Context context, @Nonnull ScanResult scanResult) { + List fluidBlocks = FillerPropScanResult.cast(scanResult).getFluidBlocks(); + if (fluidBlocks != null) { + for (Vector3i position : fluidBlocks) { + if (context.materialWriteSpace.getBounds().contains(position.x, position.y, position.z)) { + MaterialProvider.Context materialsContext = new MaterialProvider.Context(position, 0.0, 0, 0, 0, 0, null, context.distanceToBiomeEdge); + Material material = this.fillerMaterialProvider.getVoxelTypeAt(materialsContext); + if (material != null) { + context.materialWriteSpace.set(material, position.x, position.y, position.z); + } + } + } + } + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + ScanResult scanResult = this.scan_deprecated(context.position, context.materialReadSpace); + this.place_deprecated(context, scanResult); + return !scanResult.isNegative(); + } + + @NonNullDecl + @Override + public Bounds3i getReadBounds_voxelGrid() { + return this.readBounds_voxelGrid; + } + + @Nonnull + @Override + public Bounds3i getWriteBounds_voxelGrid() { + return this.writeBounds_voxelGrid; + } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/MoldingDirection.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/MoldingDirection.java similarity index 82% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/MoldingDirection.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/MoldingDirection.java index ae1de3e4..543ae284 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/MoldingDirection.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/MoldingDirection.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.prefab; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.codecs.EnumCodec; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PrefabMoldingConfiguration.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabMoldingConfiguration.java similarity index 91% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PrefabMoldingConfiguration.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabMoldingConfiguration.java index 33171598..e9d1154a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PrefabMoldingConfiguration.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabMoldingConfiguration.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.prefab; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PrefabProp.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabProp.java similarity index 56% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PrefabProp.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabProp.java index 56a554cd..0f846733 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PrefabProp.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabProp.java @@ -1,36 +1,31 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.prefab; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab; import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; +import com.hypixel.hytale.builtin.hytalegenerator.EntityPlacementData; +import com.hypixel.hytale.builtin.hytalegenerator.WeightedMap; import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.ArrayVoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; +import com.hypixel.hytale.builtin.hytalegenerator.engine.entityfunnel.EntityFunnel; import com.hypixel.hytale.builtin.hytalegenerator.material.FluidMaterial; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; import com.hypixel.hytale.builtin.hytalegenerator.material.SolidMaterial; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.EntityContainer; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.props.Prop; -import com.hypixel.hytale.builtin.hytalegenerator.props.ScanResult; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.RotatedPosition; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.RotatedPositionsScanResult; -import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.StaticDirectionality; -import com.hypixel.hytale.builtin.hytalegenerator.props.entity.EntityPlacementData; -import com.hypixel.hytale.builtin.hytalegenerator.scanners.OriginScanner; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.ScanResult; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.Directionality; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.RotatedPosition; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.RotatedPositionsScanResult; +import com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.directionality.StaticDirectionality; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.rng.SeedBox; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.DirectScanner; import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; -import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.ArrayVoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; import com.hypixel.hytale.common.util.ExceptionUtil; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall; @@ -44,17 +39,18 @@ import java.util.function.Function; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PrefabProp extends Prop { @Nonnull - private final WeightedMap> prefabPool; + private final WeightedMap> prefabPool; @Nonnull private final Scanner scanner; - private ContextDependency contextDependency; @Nonnull private final MaterialCache materialCache; @Nonnull - private final SeedGenerator seedGenerator; + private final RngField rngField; @Nonnull private final BlockMask materialMask; @Nonnull @@ -64,13 +60,11 @@ public class PrefabProp extends Prop { @Nonnull private final Bounds3i writeBounds_voxelGrid; @Nonnull - private final Bounds3i prefabBounds_voxelGrid; - @Nonnull private final List childProps; @Nonnull private final List childPositions; @Nonnull - private final Function> childPrefabLoader; + private final Function> childPrefabLoader; private final Scanner moldingScanner; private final Pattern moldingPattern; private final MoldingDirection moldingDirection; @@ -79,13 +73,13 @@ public class PrefabProp extends Prop { private boolean loadEntities; public PrefabProp( - @Nonnull WeightedMap> prefabPool, + @Nonnull WeightedMap> prefabPool, @Nonnull Scanner scanner, @Nonnull Directionality directionality, @Nonnull MaterialCache materialCache, @Nonnull BlockMask materialMask, @Nonnull PrefabMoldingConfiguration prefabMoldingConfiguration, - @Nullable Function> childPrefabLoader, + @Nullable Function> childPrefabLoader, @Nonnull SeedBox seedBox, boolean loadEntities ) { @@ -93,7 +87,7 @@ public class PrefabProp extends Prop { this.scanner = scanner; this.directionality = directionality; this.materialCache = materialCache; - this.seedGenerator = new SeedGenerator(seedBox.createSupplier().get().intValue()); + this.rngField = new RngField(seedBox.createSupplier().get()); this.materialMask = materialMask; this.loadEntities = loadEntities; this.childProps = new ArrayList<>(); @@ -103,34 +97,34 @@ public class PrefabProp extends Prop { this.moldingPattern = prefabMoldingConfiguration.moldingPattern; this.moldingDirection = prefabMoldingConfiguration.moldingDirection; this.moldChildren = prefabMoldingConfiguration.moldChildren; - this.contextDependency = new ContextDependency(); - Vector3i readRange = directionality.getReadRangeWith(scanner); + this.readBounds_voxelGrid = directionality.getBoundsWith_voxelGrid(scanner); + this.writeBounds_voxelGrid = new Bounds3i(); - for (List prefabList : prefabPool.allElements()) { + for (List prefabList : prefabPool.allElements()) { if (prefabList.isEmpty()) { - throw new IllegalArgumentException("prefab pool contains empty list"); + return; } - for (PrefabBuffer prefab : prefabList) { + for (IPrefabBuffer prefab : prefabList) { if (prefab == null) { throw new IllegalArgumentException("prefab pool contains list with null element"); } - PrefabBuffer.PrefabBufferAccessor prefabAccess = prefab.newAccess(); - PrefabBuffer.ChildPrefab[] childPrefabs = prefabAccess.getChildPrefabs(); + this.writeBounds_voxelGrid.encompass(this.getWriteBounds_voxelGrid(prefab)); + PrefabBuffer.ChildPrefab[] childPrefabs = prefab.getChildPrefabs(); int childId = 0; for (PrefabBuffer.ChildPrefab child : childPrefabs) { RotatedPosition childPosition = new RotatedPosition(child.getX(), child.getY(), child.getZ(), child.getRotation()); String childPath = child.getPath().replace('.', '/'); childPath = childPath.replace("*", ""); - List childPrefabBuffers = this.childPrefabLoader.apply(childPath); - WeightedMap> weightedChildPrefabs = new WeightedMap<>(); + List childPrefabBuffers = this.childPrefabLoader.apply(childPath); + WeightedMap> weightedChildPrefabs = new WeightedMap<>(); weightedChildPrefabs.add(childPrefabBuffers, 1.0); - StaticDirectionality childDirectionality = new StaticDirectionality(child.getRotation(), Pattern.yesPattern()); + StaticDirectionality childDirectionality = new StaticDirectionality(child.getRotation(), ConstantPattern.INSTANCE_TRUE); PrefabProp childProp = new PrefabProp( weightedChildPrefabs, - OriginScanner.getInstance(), + new DirectScanner(), childDirectionality, materialCache, materialMask, @@ -143,58 +137,52 @@ public class PrefabProp extends Prop { this.childPositions.add(childPosition); } - Vector3i writeRange = this.getWriteRange(prefabAccess); - for (int i = 0; i < this.childPositions.size(); i++) { PrefabProp child = this.childProps.get(i); - Vector3i position = this.childPositions.get(i).toVector3i(); - Vector3i childWriteRange = child.getContextDependency().getWriteRange(); - int maxRange = Calculator.max(position.x, position.y, position.z); - maxRange += Calculator.max(childWriteRange.x, childWriteRange.y, childWriteRange.z); - writeRange.x = Math.max(writeRange.x, maxRange); - writeRange.y = Math.max(writeRange.y, maxRange); - writeRange.z = Math.max(writeRange.z, maxRange); + Vector3i childPosition_voxelGrid = this.childPositions.get(i).toVector3i(); + Bounds3i childWriteBounds_voxelGrid = child.getWriteBounds_voxelGrid().clone(); + childWriteBounds_voxelGrid.offset(childPosition_voxelGrid); + this.writeBounds_voxelGrid.encompass(childWriteBounds_voxelGrid); } - - ContextDependency contextDependency = new ContextDependency(readRange, writeRange); - this.contextDependency = ContextDependency.mostOf(this.contextDependency, contextDependency); - prefabAccess.release(); } } - this.readBounds_voxelGrid = this.contextDependency.getReadBounds_voxelGrid(); - this.writeBounds_voxelGrid = this.contextDependency.getWriteBounds_voxelGrid(); - this.prefabBounds_voxelGrid = new Bounds3i(); - this.prefabBounds_voxelGrid.min.assign(this.contextDependency.getWriteRange()).scale(-1); - this.prefabBounds_voxelGrid.max.assign(this.contextDependency.getWriteRange()).add(Vector3i.ALL_ONES); + this.writeBounds_voxelGrid.stack(scanner.getBounds_voxelGrid()); } @Nonnull - private Vector3i getWriteRange(@Nonnull PrefabBuffer.PrefabBufferAccessor prefabAccess) { - SpaceSize space = new SpaceSize(); + private Bounds3i getWriteBounds_voxelGrid(@Nonnull IPrefabBuffer prefabAccess) { + Bounds3i bounds_voxelGrid = new Bounds3i(); for (PrefabRotation rotation : this.directionality.getPossibleRotations()) { - Vector3i max = PropPrefabUtil.getMax(prefabAccess, rotation); + Vector3i max = PrefabPropUtil.getMax(prefabAccess, rotation); max.add(1, 1, 1); - Vector3i min = PropPrefabUtil.getMin(prefabAccess, rotation); - space = SpaceSize.merge(space, new SpaceSize(min, max)); + Vector3i min = PrefabPropUtil.getMin(prefabAccess, rotation); + bounds_voxelGrid.encompass(min); + bounds_voxelGrid.encompass(max); } - space = SpaceSize.stack(space, this.scanner.readSpaceWith(this.directionality.getGeneralPattern())); - return space.getRange(); + return bounds_voxelGrid; + } + + @Override + public boolean generate(@NonNullDecl Prop.Context context) { + ScanResult scanResult = this.scan_deprecated(context.position, context.materialReadSpace); + this.place_deprecated(context, scanResult); + return !scanResult.isNegative(); } @Nonnull - @Override - public ScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id id) { - Scanner.Context scannerContext = new Scanner.Context(position, this.directionality.getGeneralPattern(), materialSpace, id); - List validPositions = this.scanner.scan(scannerContext); + public ScanResult scan_deprecated(@Nonnull Vector3i position, @Nonnull VoxelSpace materialSpace) { + List validPositions = new ArrayList<>(); + Scanner.Context scannerContext = new Scanner.Context(position, this.directionality.getGeneralPattern(), materialSpace, validPositions); + this.scanner.scan(scannerContext); Vector3i patternPosition = new Vector3i(); Pattern.Context patternContext = new Pattern.Context(patternPosition, materialSpace); RotatedPositionsScanResult scanResult = new RotatedPositionsScanResult(new ArrayList<>()); for (Vector3i validPosition : validPositions) { - patternPosition.assign(validPosition); + patternPosition.set(validPosition); PrefabRotation rotation = this.directionality.getRotationAt(patternContext); if (rotation != null) { scanResult.positions.add(new RotatedPosition(validPosition.x, validPosition.y, validPosition.z, rotation)); @@ -204,54 +192,51 @@ public class PrefabProp extends Prop { return scanResult; } - @Override - public void place(@Nonnull Prop.Context context) { + public void place_deprecated(@Nonnull Prop.Context context, @Nonnull ScanResult scanResult) { if (this.prefabPool.size() != 0) { - List positions = RotatedPositionsScanResult.cast(context.scanResult).positions; + List positions = RotatedPositionsScanResult.cast(scanResult).positions; if (positions != null) { - Bounds3i writeSpaceBounds_voxelGrid = context.materialSpace.getBounds(); + Bounds3i localWriteBounds_voxelGrid = this.writeBounds_voxelGrid.clone().offset(context.position); + if (localWriteBounds_voxelGrid.intersects(localWriteBounds_voxelGrid)) { + Bounds3i writeSpaceBounds_voxelGrid = context.materialWriteSpace.getBounds(); - for (RotatedPosition position : positions) { - Bounds3i localPrefabWriteBounds_voxelGrid = this.prefabBounds_voxelGrid.clone().offset(position.toVector3i()); - if (localPrefabWriteBounds_voxelGrid.intersects(writeSpaceBounds_voxelGrid)) { - this.place(position, context.materialSpace, context.entityBuffer, context.workerId); + for (RotatedPosition position : positions) { + this.place(position, context.materialWriteSpace, context.entityWriteBuffer); } } } } } - private PrefabBuffer pickPrefab(@Nonnull Random rand) { - List list = this.prefabPool.pick(rand); + private IPrefabBuffer pickPrefab(@Nonnull Random rand) { + List list = this.prefabPool.pick(rand); int randomIndex = rand.nextInt(list.size()); return list.get(randomIndex); } - private void place( - @Nonnull RotatedPosition position, @Nonnull VoxelSpace materialSpace, @Nonnull EntityContainer entityBuffer, @Nonnull WorkerIndexer.Id id - ) { - Random random = new Random(this.seedGenerator.seedAt((long)position.x, (long)position.y, (long)position.z)); + public void place(@Nonnull RotatedPosition position, @Nonnull VoxelSpace materialSpace, @Nonnull EntityFunnel entityBuffer) { + Random random = new Random(this.rngField.get(position.x, position.y, position.z)); PrefabBufferCall callInstance = new PrefabBufferCall(random, position.rotation); - PrefabBuffer prefab = this.pickPrefab(random); - PrefabBuffer.PrefabBufferAccessor prefabAccess = prefab.newAccess(); + IPrefabBuffer prefab = this.pickPrefab(random); VoxelSpace moldingOffsets = null; if (this.moldingDirection != MoldingDirection.NONE) { - int prefabMinX = prefabAccess.getMinX(position.rotation); - int prefabMinZ = prefabAccess.getMinZ(position.rotation); - int prefabMaxX = prefabAccess.getMaxX(position.rotation); - int prefabMaxZ = prefabAccess.getMaxZ(position.rotation); - int prefabSizeX = prefabMaxX - prefabMinX; - int prefabSizeZ = prefabMaxZ - prefabMinZ; - moldingOffsets = new ArrayVoxelSpace<>(prefabSizeX, 1, prefabSizeZ); - moldingOffsets.setOrigin(-position.x - prefabMinX, 0, -position.z - prefabMinZ); + int prefabMinX = prefab.getMinX(position.rotation); + int prefabMinZ = prefab.getMinZ(position.rotation); + int prefabMaxX = prefab.getMaxX(position.rotation); + int prefabMaxZ = prefab.getMaxZ(position.rotation); + Bounds3i bounds_voxelGrid = new Bounds3i( + new Vector3i(prefabMinX + position.x, 0, prefabMinZ + position.z), new Vector3i(prefabMaxX + position.x, 1, prefabMaxZ + position.z) + ); + moldingOffsets = new ArrayVoxelSpace<>(bounds_voxelGrid); if (this.moldingDirection == MoldingDirection.DOWN || this.moldingDirection == MoldingDirection.UP) { Vector3i pointer = new Vector3i(0, position.y, 0); - Scanner.Context scannerContext = new Scanner.Context(pointer, this.moldingPattern, materialSpace, id); + Scanner.Context scannerContext = new Scanner.Context(pointer, this.moldingPattern, materialSpace, new ArrayList<>()); - for (pointer.x = moldingOffsets.minX(); pointer.x < moldingOffsets.maxX(); pointer.x++) { - for (pointer.z = moldingOffsets.minZ(); pointer.z < moldingOffsets.maxZ(); pointer.z++) { - List scanResult = this.moldingScanner.scan(scannerContext); - Integer offset = scanResult.isEmpty() ? null : scanResult.getFirst().y - position.y; + for (pointer.x = bounds_voxelGrid.min.x; pointer.x < bounds_voxelGrid.max.x; pointer.x++) { + for (pointer.z = bounds_voxelGrid.min.z; pointer.z < bounds_voxelGrid.max.z; pointer.z++) { + scannerContext.validPositions_out.clear(); + this.moldingScanner.scan(scannerContext); + Integer offset = scannerContext.validPositions_out.isEmpty() ? null : scannerContext.validPositions_out.getFirst().y - position.y; if (offset != null && this.moldingDirection == MoldingDirection.UP) { offset = offset - 1; } @@ -265,13 +250,13 @@ public class PrefabProp extends Prop { try { Vector3i prefabPositionVector = position.toVector3i(); VoxelSpace moldingOffsetsFinal = moldingOffsets; - prefabAccess.forEach( + prefab.forEach( IPrefabBuffer.iterateAllColumns(), (x, yx, z, blockId, holder, support, rotation, filler, call, fluidId, fluidLevel) -> { int worldX = position.x + x; int worldY = position.y + yx; int worldZ = position.z + z; - if (materialSpace.isInsideSpace(worldX, worldY, worldZ)) { + if (materialSpace.getBounds().contains(worldX, worldY, worldZ)) { SolidMaterial solid = this.materialCache.getSolidMaterial(blockId, support, rotation, filler, holder != null ? holder.clone() : null); FluidMaterial fluid = this.materialCache.getFluidMaterial(fluidId, (byte)fluidLevel); Material material = this.materialCache.getMaterial(solid, fluid); @@ -279,8 +264,8 @@ public class PrefabProp extends Prop { if (this.materialMask.canPlace(materialHash)) { if (this.moldingDirection == MoldingDirection.DOWN || this.moldingDirection == MoldingDirection.UP) { Integer offsetx = null; - if (moldingOffsetsFinal.isInsideSpace(worldX, 0, worldZ)) { - offsetx = moldingOffsetsFinal.getContent(worldX, 0, worldZ); + if (moldingOffsetsFinal.getBounds().contains(worldX, 0, worldZ)) { + offsetx = moldingOffsetsFinal.get(worldX, 0, worldZ); } if (offsetx == null) { @@ -290,7 +275,7 @@ public class PrefabProp extends Prop { worldY += offsetx; } - Material worldMaterial = materialSpace.getContent(worldX, worldY, worldZ); + Material worldMaterial = materialSpace.get(worldX, worldY, worldZ); int worldMaterialHash = worldMaterial.hashMaterialIds(); if (this.materialMask.canReplace(materialHash, worldMaterialHash)) { if (filler == 0) { @@ -306,10 +291,10 @@ public class PrefabProp extends Prop { for (int ix = 0; ix < entityWrappers.length; ix++) { TransformComponent transformComp = entityWrappers[ix].getComponent(TransformComponent.getComponentType()); if (transformComp != null) { - Vector3d entityPosition = transformComp.getPosition().clone(); + Vector3d entityPosition = new Vector3d(transformComp.getPosition()); buffer.rotation.rotate(entityPosition); - Vector3d entityWorldPosition = entityPosition.add(prefabPositionVector); - if (entityBuffer.isInsideBuffer((int)entityWorldPosition.x, (int)entityWorldPosition.y, (int)entityWorldPosition.z)) { + Vector3d entityWorldPosition = entityPosition.add(prefabPositionVector.x, prefabPositionVector.y, prefabPositionVector.z); + if (entityBuffer.getBounds().contains(entityWorldPosition)) { Holder entityClone = entityWrappers[ix].clone(); transformComp = entityClone.getComponent(TransformComponent.getComponentType()); if (transformComp != null) { @@ -317,9 +302,8 @@ public class PrefabProp extends Prop { entityPosition.x = entityWorldPosition.x; entityPosition.y = entityWorldPosition.y; entityPosition.z = entityWorldPosition.z; - if (!materialSpace.isInsideSpace( - (int)Math.floor(entityPosition.x), (int)Math.floor(entityPosition.y), (int)Math.floor(entityPosition.z) - )) { + if (!materialSpace.getBounds() + .contains((int)Math.floor(entityPosition.x), (int)Math.floor(entityPosition.y), (int)Math.floor(entityPosition.z))) { return; } @@ -337,13 +321,11 @@ public class PrefabProp extends Prop { (x, yx, z, path, fitHeightmap, inheritSeed, inheritHeightCondition, weights, rotation, t) -> {}, callInstance ); - } catch (Exception var23) { + } catch (Exception var16) { String msg = "Couldn't place prefab prop."; msg = msg + "\n"; - msg = msg + ExceptionUtil.toStringWithStack(var23); + msg = msg + ExceptionUtil.toStringWithStack(var16); HytaleLogger.getLogger().atWarning().log(msg); - } finally { - prefabAccess.release(); } for (int i = 0; i < this.childProps.size(); i++) { @@ -351,8 +333,8 @@ public class PrefabProp extends Prop { RotatedPosition childPosition = this.childPositions.get(i).getRelativeTo(position); Vector3i rotatedChildPositionVec = new Vector3i(childPosition.x, childPosition.y, childPosition.z); position.rotation.rotate(rotatedChildPositionVec); - if (moldingOffsets != null && moldingOffsets.isInsideSpace(childPosition.x, 0, childPosition.z)) { - Integer offset = moldingOffsets.getContent(childPosition.x, 0, childPosition.z); + if (moldingOffsets != null && moldingOffsets.getBounds().contains(childPosition.x, 0, childPosition.z)) { + Integer offset = moldingOffsets.get(childPosition.x, 0, childPosition.z); if (offset == null) { continue; } @@ -361,15 +343,10 @@ public class PrefabProp extends Prop { childPosition = new RotatedPosition(childPosition.x, y, childPosition.z, childPosition.rotation); } - prop.place(childPosition, materialSpace, entityBuffer, id); + prop.place(childPosition, materialSpace, entityBuffer); } } - @Override - public ContextDependency getContextDependency() { - return this.contextDependency; - } - @NonNullDecl @Override public Bounds3i getReadBounds_voxelGrid() { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PropPrefabUtil.java b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabPropUtil.java similarity index 78% rename from src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PropPrefabUtil.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabPropUtil.java index 1725fa93..f251940a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/props/prefab/PropPrefabUtil.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/props/deprecated/prefab/PrefabPropUtil.java @@ -1,11 +1,12 @@ -package com.hypixel.hytale.builtin.hytalegenerator.props.prefab; +package com.hypixel.hytale.builtin.hytalegenerator.props.deprecated.prefab; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.PrefabRotation; +import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; import javax.annotation.Nonnull; +import org.joml.Vector3i; -public class PropPrefabUtil { +public class PrefabPropUtil { @Nonnull public static Vector3i getMin(@Nonnull PrefabBuffer.PrefabBufferAccessor prefab) { int minX = prefab.getMinX(PrefabRotation.ROTATION_0); @@ -23,7 +24,7 @@ public class PropPrefabUtil { } @Nonnull - public static Vector3i getMin(@Nonnull PrefabBuffer.PrefabBufferAccessor prefab, @Nonnull PrefabRotation rotation) { + public static Vector3i getMin(@Nonnull IPrefabBuffer prefab, @Nonnull PrefabRotation rotation) { int minX = prefab.getMinX(rotation); int minY = prefab.getMinY(); int minZ = prefab.getMinZ(rotation); @@ -31,7 +32,7 @@ public class PropPrefabUtil { } @Nonnull - public static Vector3i getMax(@Nonnull PrefabBuffer.PrefabBufferAccessor prefab, @Nonnull PrefabRotation rotation) { + public static Vector3i getMax(@Nonnull IPrefabBuffer prefab, @Nonnull PrefabRotation rotation) { int maxX = prefab.getMaxX(rotation); int maxY = prefab.getMaxY(); int maxZ = prefab.getMaxZ(rotation); @@ -42,7 +43,7 @@ public class PropPrefabUtil { public static Vector3i getSize(@Nonnull PrefabBuffer.PrefabBufferAccessor prefab) { Vector3i min = getMin(prefab); Vector3i max = getMax(prefab); - return max.addScaled(min, -1); + return max.sub(min); } @Nonnull diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/rng/Rng.java b/src/com/hypixel/hytale/builtin/hytalegenerator/rng/Rng.java new file mode 100644 index 00000000..a088c4b8 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/rng/Rng.java @@ -0,0 +1,43 @@ +package com.hypixel.hytale.builtin.hytalegenerator.rng; + +public class Rng { + public static final int BIT_NOISE_0 = 1759714724; + public static final int BIT_NOISE_1 = -1255572915; + public static final int BIT_NOISE_2 = 458671337; + public static final int PRIME_0 = 198491317; + public static final int PRIME_1 = 6542989; + + public static int getRandomInt(int seed, int key) { + int bits = key * 1759714724; + bits += seed; + bits ^= bits >>> 8; + bits -= 1255572915; + bits ^= bits << 8; + bits *= 458671337; + return bits ^ bits >>> 8; + } + + public static int mix(int seed, int a, int b) { + return getRandomInt(seed, a + 198491317 * b); + } + + public static int mix(int seed, int a, int b, int c) { + return getRandomInt(seed, a + 198491317 * b + 6542989 * c); + } + + public static long splitMixLong(long n) { + n = (n ^ n >>> 30) * -4658895280553007687L; + n = (n ^ n >>> 27) * -7723592293110705685L; + return n ^ n >>> 31; + } + + public static int splitMixInteger(int n) { + n = (n ^ n >>> 16) * -2048144789; + n = (n ^ n >>> 13) * -1028477387; + return n ^ n >>> 16; + } + + public static int rotateLeft(int bits, int distance) { + return bits << distance | bits >>> 32 - distance; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/rng/RngField.java b/src/com/hypixel/hytale/builtin/hytalegenerator/rng/RngField.java new file mode 100644 index 00000000..5538c73a --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/rng/RngField.java @@ -0,0 +1,30 @@ +package com.hypixel.hytale.builtin.hytalegenerator.rng; + +public class RngField { + private final int seed; + + public RngField(int seed) { + this.seed = seed; + } + + public int get(int x, int y, int z) { + return Rng.mix(this.seed, x, y, z); + } + + public int get(int x, int y) { + return Rng.mix(this.seed, x, y); + } + + public int get(double x, double y, double z) { + int bitsX = Float.floatToRawIntBits((float)x); + int bitsY = Float.floatToRawIntBits((float)y); + int bitsZ = Float.floatToRawIntBits((float)z); + return Rng.mix(this.seed, bitsX, bitsY, bitsZ); + } + + public int get(double x, double y) { + int bitsX = Float.floatToRawIntBits((float)x); + int bitsY = Float.floatToRawIntBits((float)y); + return Rng.mix(this.seed, bitsX, bitsY); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/seed/SeedBox.java b/src/com/hypixel/hytale/builtin/hytalegenerator/rng/SeedBox.java similarity index 78% rename from src/com/hypixel/hytale/builtin/hytalegenerator/seed/SeedBox.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/rng/SeedBox.java index 66bb40d4..de7ba44d 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/seed/SeedBox.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/rng/SeedBox.java @@ -1,6 +1,6 @@ -package com.hypixel.hytale.builtin.hytalegenerator.seed; +package com.hypixel.hytale.builtin.hytalegenerator.rng; -import java.util.Random; +import com.hypixel.hytale.math.util.FastRandom; import java.util.function.Supplier; import javax.annotation.Nonnull; @@ -23,7 +23,7 @@ public class SeedBox { @Nonnull public Supplier createSupplier() { - Random rand = new Random(this.key.hashCode()); + FastRandom rand = new FastRandom(this.key.hashCode()); return () -> rand.nextInt(); } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/AreaScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/AreaScanner.java deleted file mode 100644 index ba1f004e..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/AreaScanner.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.scanners; - -import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator; -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.codecs.EnumCodec; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; - -public class AreaScanner extends Scanner { - @Nonnull - private final AreaScanner.ScanShape scanShape; - private final int range; - private final int resultCap; - @Nonnull - private final Scanner childScanner; - @Nonnull - private final List scanOrder; - @Nonnull - private final SpaceSize scanSpaceSize; - - public AreaScanner(int resultCap, @Nonnull AreaScanner.ScanShape scanShape, int range, @Nonnull Scanner childScanner) { - if (resultCap >= 0 && range >= 0) { - this.resultCap = resultCap; - this.childScanner = childScanner; - this.scanShape = scanShape; - this.range = range; - ArrayList scanOrder = new ArrayList<>(); - - for (int x = -range; x <= range; x++) { - for (int z = -range; z <= range; z++) { - if (scanShape != AreaScanner.ScanShape.CIRCLE || !(Calculator.distance(x, z, 0.0, 0.0) > range)) { - scanOrder.add(new Vector2i(x, z)); - } - } - } - - this.scanOrder = VectorUtil.orderByDistanceFrom(new Vector2i(), scanOrder); - this.scanSpaceSize = new SpaceSize(new Vector3i(-range, 0, -range), new Vector3i(1 + range, 0, 1 + range)); - } else { - throw new IllegalArgumentException(); - } - } - - @Nonnull - @Override - public List scan(@Nonnull Scanner.Context context) { - if (this.resultCap == 0) { - return Collections.emptyList(); - } else { - ArrayList validPositions = new ArrayList<>(this.resultCap); - - for (Vector2i column : this.scanOrder) { - Vector3i columnOrigin = new Vector3i(context.position.x + column.x, context.position.y, context.position.z + column.y); - Scanner.Context childContext = new Scanner.Context(columnOrigin, context.pattern, context.materialSpace, context.workerId); - - for (Vector3i result : this.childScanner.scan(childContext)) { - validPositions.add(result); - if (validPositions.size() == this.resultCap) { - return validPositions; - } - } - } - - return validPositions; - } - } - - @Nonnull - @Override - public SpaceSize scanSpace() { - return this.scanSpaceSize.clone(); - } - - public static enum ScanShape { - CIRCLE, - SQUARE; - - @Nonnull - public static final Codec CODEC = new EnumCodec<>(AreaScanner.ScanShape.class, EnumCodec.EnumStyle.LEGACY); - } - - public static enum Verticality { - GLOBAL, - LOCAL; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/ColumnLinearScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/ColumnLinearScanner.java deleted file mode 100644 index d25209e6..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/ColumnLinearScanner.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.scanners; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.ArrayList; -import java.util.List; -import javax.annotation.Nonnull; - -public class ColumnLinearScanner extends Scanner { - private final int minY; - private final int maxY; - private final boolean isRelativeToPosition; - private final double baseHeight; - private final int resultsCap; - private final boolean topDownOrder; - @Nonnull - private final SpaceSize scanSpaceSize; - - public ColumnLinearScanner(int minY, int maxY, int resultsCap, boolean topDownOrder, boolean isRelativeToPosition, double baseHeight) { - if (resultsCap < 0) { - throw new IllegalArgumentException(); - } else { - this.baseHeight = baseHeight; - this.minY = minY; - this.maxY = maxY; - this.isRelativeToPosition = isRelativeToPosition; - this.resultsCap = resultsCap; - this.topDownOrder = topDownOrder; - this.scanSpaceSize = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(1, 0, 1)); - } - } - - @Nonnull - @Override - public List scan(@Nonnull Scanner.Context context) { - ArrayList validPositions = new ArrayList<>(this.resultsCap); - int scanMinY; - int scanMaxY; - if (this.isRelativeToPosition) { - scanMinY = Math.max(context.position.y + this.minY, context.materialSpace.minY()); - scanMaxY = Math.min(context.position.y + this.maxY, context.materialSpace.maxY()); - } else { - int bedY = (int)this.baseHeight; - scanMinY = Math.max(bedY + this.minY, context.materialSpace.minY()); - scanMaxY = Math.min(bedY + this.maxY, context.materialSpace.maxY()); - } - - Vector3i patternPosition = context.position.clone(); - Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace); - if (this.topDownOrder) { - for (patternPosition.y = scanMaxY - 1; patternPosition.y >= scanMinY; patternPosition.y--) { - if (context.pattern.matches(patternContext)) { - validPositions.add(patternPosition.clone()); - if (validPositions.size() >= this.resultsCap) { - return validPositions; - } - } - } - } else { - for (patternPosition.y = scanMinY; patternPosition.y < scanMaxY; patternPosition.y++) { - if (context.pattern.matches(patternContext)) { - validPositions.add(patternPosition.clone()); - if (validPositions.size() >= this.resultsCap) { - return validPositions; - } - } - } - } - - return validPositions; - } - - @Nonnull - @Override - public SpaceSize scanSpace() { - return this.scanSpaceSize.clone(); - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/ColumnRandomScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/ColumnRandomScanner.java deleted file mode 100644 index 592b968e..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/ColumnRandomScanner.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.scanners; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator; -import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; - -public class ColumnRandomScanner extends Scanner { - private final int minY; - private final int maxY; - private final boolean isRelativeToPosition; - private final double baseHeight; - private final int resultsCap; - @Nonnull - private final SeedGenerator seedGenerator; - @Nonnull - private final ColumnRandomScanner.Strategy strategy; - @Nonnull - private final SpaceSize scanSpaceSize; - - public ColumnRandomScanner( - int minY, int maxY, int resultsCap, int seed, @Nonnull ColumnRandomScanner.Strategy strategy, boolean isRelativeToPosition, double baseHeight - ) { - if (resultsCap < 0) { - throw new IllegalArgumentException(); - } else { - this.baseHeight = baseHeight; - this.minY = minY; - this.maxY = maxY; - this.isRelativeToPosition = isRelativeToPosition; - this.resultsCap = resultsCap; - this.seedGenerator = new SeedGenerator(seed); - this.strategy = strategy; - this.scanSpaceSize = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(1, 0, 1)); - } - } - - @Nonnull - @Override - public List scan(@Nonnull Scanner.Context context) { - return switch (this.strategy) { - case DART_THROW -> this.scanDartThrow(context); - case PICK_VALID -> this.scanPickValid(context); - }; - } - - @Nonnull - private List scanPickValid(@Nonnull Scanner.Context context) { - if (this.resultsCap == 0) { - return Collections.emptyList(); - } else { - int scanMinY; - int scanMaxY; - if (this.isRelativeToPosition) { - scanMinY = Math.max(context.position.y + this.minY, context.materialSpace.minY()); - scanMaxY = Math.min(context.position.y + this.maxY, context.materialSpace.maxY()); - } else { - int bedY = (int)this.baseHeight; - scanMinY = Math.max(bedY + this.minY, context.materialSpace.minY()); - scanMaxY = Math.min(bedY + this.maxY, context.materialSpace.maxY()); - } - - int numberOfPossiblePositions = Math.max(0, scanMaxY - scanMinY); - ArrayList validPositions = new ArrayList<>(numberOfPossiblePositions); - Vector3i patternPosition = context.position.clone(); - Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace); - - for (int y = scanMinY; y < scanMaxY; y++) { - patternPosition.y = y; - if (context.pattern.matches(patternContext)) { - Vector3i position = context.position.clone(); - position.setY(y); - validPositions.add(position); - } - } - - if (validPositions.isEmpty()) { - return validPositions; - } else if (validPositions.size() <= this.resultsCap) { - return validPositions; - } else { - ArrayList usedIndices = new ArrayList<>(this.resultsCap); - ArrayList outPositions = new ArrayList<>(this.resultsCap); - FastRandom random = new FastRandom(this.seedGenerator.seedAt((long)context.position.x, (long)context.position.y, (long)context.position.z)); - - for (int i = 0; i < this.resultsCap; i++) { - int pickedIndex = random.nextInt(validPositions.size()); - if (!usedIndices.contains(pickedIndex)) { - usedIndices.add(pickedIndex); - outPositions.add(validPositions.get(pickedIndex)); - } - } - - return outPositions; - } - } - } - - @Nonnull - private List scanDartThrow(@Nonnull Scanner.Context context) { - if (this.resultsCap == 0) { - return Collections.emptyList(); - } else { - int scanMinY = this.isRelativeToPosition - ? Math.max(context.position.y + this.minY, context.materialSpace.minY()) - : Math.max(this.minY, context.materialSpace.minY()); - int scanMaxY = this.isRelativeToPosition - ? Math.min(context.position.y + this.maxY, context.materialSpace.maxY()) - : Math.min(this.maxY, context.materialSpace.maxY()); - int range = scanMaxY - scanMinY; - if (range == 0) { - return Collections.emptyList(); - } else { - int TRY_MULTIPLIER = 1; - int numberOfTries = range * 1; - ArrayList validPositions = new ArrayList<>(this.resultsCap); - FastRandom random = new FastRandom(this.seedGenerator.seedAt((long)context.position.x, (long)context.position.y, (long)context.position.z)); - ArrayList usedYs = new ArrayList<>(this.resultsCap); - Vector3i patternPosition = context.position.clone(); - Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace); - - for (int i = 0; i < numberOfTries; i++) { - patternPosition.y = random.nextInt(range) + scanMinY; - if (context.pattern.matches(patternContext) && !usedYs.contains(patternPosition.y)) { - usedYs.add(patternPosition.y); - Vector3i position = patternPosition.clone(); - validPositions.add(position); - if (validPositions.size() == this.resultsCap) { - break; - } - } - } - - return validPositions; - } - } - } - - @Nonnull - @Override - public SpaceSize scanSpace() { - return this.scanSpaceSize.clone(); - } - - public static enum Strategy { - DART_THROW, - PICK_VALID; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/DirectScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/DirectScanner.java new file mode 100644 index 00000000..6d5c0366 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/DirectScanner.java @@ -0,0 +1,36 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class DirectScanner extends Scanner { + @Nonnull + private static final Bounds3i BOUNDS = new Bounds3i(Vector3iUtil.ZERO, Vector3iUtil.ALL_ONES); + @Nonnull + private final Control rControl = new Control(); + + @Override + public void scan(@Nonnull Scanner.Context context) { + Pattern.Context patternContext = new Pattern.Context(context.position, context.materialSpace); + if (context.pattern.matches(patternContext)) { + context.validPositions_out.add(new Vector3i(context.position)); + } + } + + @Override + public void scan(@NonNullDecl Vector3i position, @NonNullDecl Pipe.One pipe) { + this.rControl.stop = false; + pipe.accept(position, this.rControl); + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return BOUNDS; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/EmptyScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/EmptyScanner.java new file mode 100644 index 00000000..0b727f27 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/EmptyScanner.java @@ -0,0 +1,26 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class EmptyScanner extends Scanner { + public static final EmptyScanner INSTANCE = new EmptyScanner(); + + private EmptyScanner() { + } + + @Override + public void scan(@NonNullDecl Scanner.Context context) { + } + + @Override + public void scan(@NonNullDecl Vector3i position, @NonNullDecl Pipe.One pipe) { + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return Bounds3i.ZERO; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/LinearScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/LinearScanner.java new file mode 100644 index 00000000..c08500ca --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/LinearScanner.java @@ -0,0 +1,122 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.delimiters.RangeInt; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.math.Axis; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class LinearScanner extends Scanner { + @Nonnull + private final Axis axis; + @Nonnull + private final RangeInt range; + @Nonnull + private final Scanner childScanner; + @Nonnull + private final Bounds3i bounds; + private final boolean isAscendingOrder; + @Nonnull + private final Control rControl; + @Nonnull + private final Vector3i rPosition; + @Nonnull + private Pipe.One rContextPipe; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(LinearScanner.this); + } + + public void accept(@NonNullDecl Vector3i position, @NonNullDecl Control control) { + LinearScanner.this.rContextPipe.accept(position, control); + if (control.stop) { + LinearScanner.this.rControl.stop = true; + } + } + }; + + public LinearScanner(@Nonnull Axis axis, @Nonnull RangeInt range, @Nonnull Scanner childScanner, boolean isAscendingOrder) { + this.axis = axis; + this.range = range; + this.childScanner = childScanner; + this.isAscendingOrder = isAscendingOrder; + switch (axis) { + case X: + this.bounds = new Bounds3i(new Vector3i(range.getMinInclusive(), 0, 0), new Vector3i(range.getMaxExclusive(), 1, 1)); + break; + case Y: + this.bounds = new Bounds3i(new Vector3i(0, range.getMinInclusive(), 0), new Vector3i(1, range.getMaxExclusive(), 1)); + break; + case Z: + this.bounds = new Bounds3i(new Vector3i(0, 0, range.getMinInclusive()), new Vector3i(1, 1, range.getMaxExclusive())); + break; + default: + this.bounds = new Bounds3i(); + } + + this.bounds.stack(childScanner.getBounds_voxelGrid()); + this.rControl = new Control(); + this.rPosition = new Vector3i(); + this.rContextPipe = Pipe.getEmptyOne(); + } + + @Override + public void scan(@NonNullDecl Scanner.Context context) { + } + + @Override + public void scan(@NonNullDecl Vector3i anchor, @NonNullDecl Pipe.One pipe) { + this.rContextPipe = pipe; + this.rPosition.set(anchor); + this.rControl.reset(); + if (this.isAscendingOrder) { + for (int i = this.range.getMinInclusive(); i < this.range.getMaxExclusive(); i++) { + if (this.rControl.stop) { + return; + } + + switch (this.axis) { + case X: + this.rPosition.x = i + anchor.x; + break; + case Y: + this.rPosition.y = i + anchor.y; + break; + case Z: + this.rPosition.z = i + anchor.z; + } + + this.childScanner.scan(this.rPosition, this.rChildPipe); + } + } else { + for (int i = this.range.getMaxExclusive() - 1; i >= this.range.getMinInclusive(); i--) { + if (this.rControl.stop) { + return; + } + + switch (this.axis) { + case X: + this.rPosition.x = i + anchor.x; + break; + case Y: + this.rPosition.y = i + anchor.y; + break; + case Z: + this.rPosition.z = i + anchor.z; + } + + this.childScanner.scan(this.rPosition, this.rChildPipe); + } + } + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return this.bounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/OriginScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/OriginScanner.java deleted file mode 100644 index ba9475e8..00000000 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/OriginScanner.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.hypixel.hytale.builtin.hytalegenerator.scanners; - -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nonnull; - -public class OriginScanner extends Scanner { - @Nonnull - private static final OriginScanner instance = new OriginScanner(); - @Nonnull - private static final SpaceSize SCAN_SPACE_SIZE = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(1, 0, 1)); - - private OriginScanner() { - } - - @Nonnull - @Override - public List scan(@Nonnull Scanner.Context context) { - Pattern.Context patternContext = new Pattern.Context(context.position, context.materialSpace); - return context.pattern.matches(patternContext) ? Collections.singletonList(context.position.clone()) : Collections.emptyList(); - } - - @Nonnull - @Override - public SpaceSize scanSpace() { - return SCAN_SPACE_SIZE.clone(); - } - - @Nonnull - public static OriginScanner getInstance() { - return instance; - } -} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/QueueScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/QueueScanner.java new file mode 100644 index 00000000..f1168b56 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/QueueScanner.java @@ -0,0 +1,41 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class QueueScanner extends Scanner { + @Nonnull + private final List scanners; + @Nonnull + private final Bounds3i bounds; + + public QueueScanner(@Nonnull List scanners) { + this.scanners = new ArrayList<>(scanners); + this.bounds = new Bounds3i(); + + for (Scanner scanner : scanners) { + this.bounds.encompass(scanner.getBounds_voxelGrid()); + } + } + + @Override + public void scan(@NonNullDecl Scanner.Context context) { + } + + @Override + public void scan(@NonNullDecl Vector3i anchor, @NonNullDecl Pipe.One pipe) { + for (Scanner scanner : this.scanners) { + scanner.scan(anchor, pipe); + } + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return this.bounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/RadialScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/RadialScanner.java new file mode 100644 index 00000000..a0d10190 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/RadialScanner.java @@ -0,0 +1,112 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class RadialScanner extends Scanner { + @Nonnull + private final Bounds3i bounds; + @Nonnull + private final Scanner childScanner; + @Nonnull + private final byte[] sortedPositions; + private final int positionsCount; + @Nonnull + private final Control rControl; + @Nonnull + private final Vector3i rPosition; + @Nonnull + private Pipe.One rContextPipe; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(RadialScanner.this); + } + + public void accept(@NonNullDecl Vector3i position, @NonNullDecl Control control) { + RadialScanner.this.rContextPipe.accept(position, control); + if (control.stop) { + RadialScanner.this.rControl.stop = true; + } + } + }; + + public RadialScanner(@Nonnull Bounds3i bounds, @Nonnull Scanner childScanner) { + this.bounds = bounds.clone(); + this.childScanner = childScanner; + Vector3i size = bounds.getSize(); + this.positionsCount = size.x * size.y * size.z; + List sortedPositions = new ArrayList<>(this.positionsCount); + + for (Vector3i position = new Vector3i(bounds.min); position.x < bounds.max.x; position.x++) { + for (position.y = bounds.min.y; position.y < bounds.max.y; position.y++) { + for (position.z = bounds.min.z; position.z < bounds.max.z; position.z++) { + sortedPositions.add(new Vector3i(position)); + } + } + } + + sortedPositions.sort(Comparator.comparingDouble(Vector3i::length)); + this.sortedPositions = new byte[this.positionsCount * 3]; + + for (int i = 0; i < this.positionsCount; i++) { + Vector3i position = sortedPositions.get(i); + this.sortedPositions[indexX(i)] = (byte)position.x; + this.sortedPositions[indexY(i)] = (byte)position.y; + this.sortedPositions[indexZ(i)] = (byte)position.z; + } + + this.bounds.stack(childScanner.getBounds_voxelGrid()); + this.rControl = new Control(); + this.rPosition = new Vector3i(); + this.rContextPipe = Pipe.getEmptyOne(); + } + + @Override + public void scan(@NonNullDecl Scanner.Context context) { + } + + @Override + public void scan(@NonNullDecl Vector3i anchor, @NonNullDecl Pipe.One pipe) { + this.rContextPipe = pipe; + this.rControl.reset(); + + for (int i = 0; i < this.positionsCount; i++) { + if (this.rControl.stop) { + return; + } + + int x = this.sortedPositions[indexX(i)]; + int y = this.sortedPositions[indexY(i)]; + int z = this.sortedPositions[indexZ(i)]; + this.rPosition.set(x, y, z); + this.rPosition.add(anchor); + this.childScanner.scan(this.rPosition, this.rChildPipe); + } + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return this.bounds; + } + + private static int indexX(int i) { + return i * 3; + } + + private static int indexY(int i) { + return i * 3 + 1; + } + + private static int indexZ(int i) { + return i * 3 + 2; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/RandomScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/RandomScanner.java new file mode 100644 index 00000000..571eb421 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/RandomScanner.java @@ -0,0 +1,112 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.delimiters.RangeInt; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Control; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.math.Axis; +import com.hypixel.hytale.math.util.FastRandom; +import java.util.Objects; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class RandomScanner extends Scanner { + @Nonnull + private final Axis axis; + @Nonnull + private final RangeInt range; + @Nonnull + private final Scanner childScanner; + @Nonnull + private final Bounds3i bounds; + @Nonnull + private final RngField rngField; + @Nonnull + private final FastRandom random; + private final int attempts; + @Nonnull + private final Control rControl; + @Nonnull + private final Vector3i rPosition; + @Nonnull + private Pipe.One rContextPipe; + @Nonnull + private final Pipe.One rChildPipe = new Pipe.One() { + { + Objects.requireNonNull(RandomScanner.this); + } + + public void accept(@NonNullDecl Vector3i position, @NonNullDecl Control control) { + RandomScanner.this.rContextPipe.accept(position, control); + if (control.stop) { + RandomScanner.this.rControl.stop = true; + } + } + }; + + public RandomScanner(@Nonnull Axis axis, @Nonnull RangeInt range, @Nonnull Scanner childScanner, int seed) { + this.axis = axis; + this.range = range; + this.childScanner = childScanner; + this.rngField = new RngField(seed); + this.attempts = range.getMaxExclusive() - range.getMinInclusive(); + this.random = new FastRandom(); + switch (axis) { + case X: + this.bounds = new Bounds3i(new Vector3i(range.getMinInclusive(), 0, 0), new Vector3i(range.getMaxExclusive(), 1, 1)); + break; + case Y: + this.bounds = new Bounds3i(new Vector3i(0, range.getMinInclusive(), 0), new Vector3i(1, range.getMaxExclusive(), 1)); + break; + case Z: + this.bounds = new Bounds3i(new Vector3i(0, 0, range.getMinInclusive()), new Vector3i(1, 1, range.getMaxExclusive())); + break; + default: + this.bounds = new Bounds3i(); + } + + this.bounds.stack(childScanner.getBounds_voxelGrid()); + this.rControl = new Control(); + this.rPosition = new Vector3i(); + this.rContextPipe = Pipe.getEmptyOne(); + } + + @Override + public void scan(@NonNullDecl Scanner.Context context) { + } + + @Override + public void scan(@NonNullDecl Vector3i anchor, @NonNullDecl Pipe.One pipe) { + this.rContextPipe = pipe; + this.rPosition.set(anchor); + this.rControl.reset(); + this.random.setSeed(this.rngField.get(anchor.x, anchor.y, anchor.z)); + + for (int i = 0; i < this.attempts; i++) { + if (this.rControl.stop) { + return; + } + + int rollResult = this.random.nextInt(this.attempts) + this.range.getMinInclusive(); + switch (this.axis) { + case X: + this.rPosition.x = rollResult + anchor.x; + break; + case Y: + this.rPosition.y = rollResult + anchor.y; + break; + case Z: + this.rPosition.z = rollResult + anchor.z; + } + + this.childScanner.scan(this.rPosition, this.rChildPipe); + } + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return this.bounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/Scanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/Scanner.java index 04bf1600..5dfb3dfb 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/Scanner.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/Scanner.java @@ -1,61 +1,60 @@ package com.hypixel.hytale.builtin.hytalegenerator.scanners; -import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; -import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.ConstantPattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.Collections; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.NullSpace; +import com.hypixel.hytale.builtin.hytalegenerator.voxelspace.VoxelSpace; +import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3i; public abstract class Scanner { - public abstract List scan(@Nonnull Scanner.Context var1); + @Deprecated + public abstract void scan(@Nonnull Scanner.Context var1); - public abstract SpaceSize scanSpace(); + public abstract void scan(@Nonnull Vector3i var1, @Nonnull Pipe.One var2); - @Nonnull - public SpaceSize readSpaceWith(@Nonnull Pattern pattern) { - return SpaceSize.stack(pattern.readSpace(), this.scanSpace()); - } + public abstract Bounds3i getBounds_voxelGrid(); - @Nonnull - public static Scanner noScanner() { - final SpaceSize space = new SpaceSize(new Vector3i(0, 0, 0), new Vector3i(0, 0, 0)); - return new Scanner() { - @Nonnull - @Override - public List scan(@Nonnull Scanner.Context context) { - return Collections.emptyList(); - } - - @Nonnull - @Override - public SpaceSize scanSpace() { - return space; - } - }; + public Bounds3i getBoundsWithPattern_voxelGrid(@Nonnull Pattern pattern) { + return this.getBounds_voxelGrid().clone().stack(pattern.getBounds_voxelGrid()); } public static class Context { + @Nonnull public Vector3i position; + @Nonnull public Pattern pattern; + @Nonnull public VoxelSpace materialSpace; - public WorkerIndexer.Id workerId; + @Nonnull + public List validPositions_out; - public Context(@Nonnull Vector3i position, @Nonnull Pattern pattern, @Nonnull VoxelSpace materialSpace, @Nonnull WorkerIndexer.Id workerId) { + public Context() { + this.position = new Vector3i(); + this.pattern = ConstantPattern.INSTANCE_FALSE; + this.materialSpace = NullSpace.instance(); + this.validPositions_out = new ArrayList<>(); + } + + public Context( + @Nonnull Vector3i position, @Nonnull Pattern pattern, @Nonnull VoxelSpace materialSpace, @Nonnull List validPositions_out + ) { this.position = position; this.pattern = pattern; this.materialSpace = materialSpace; - this.workerId = workerId; + this.validPositions_out = validPositions_out; } public Context(@Nonnull Scanner.Context other) { this.position = other.position; this.pattern = other.pattern; this.materialSpace = other.materialSpace; - this.workerId = other.workerId; + this.validPositions_out = other.validPositions_out; } } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/AreaScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/AreaScanner.java new file mode 100644 index 00000000..a2f32e69 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/AreaScanner.java @@ -0,0 +1,93 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners.deprecated; + +import com.hypixel.hytale.builtin.hytalegenerator.VectorUtil; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.math.Calculator; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector2i; +import org.joml.Vector3i; + +public class AreaScanner extends Scanner { + private final int resultCap; + @Nonnull + private final Scanner childScanner; + @Nonnull + private final List scanOrder; + @Nonnull + private final Bounds3i bounds_voxelGrid; + + public AreaScanner(int resultCap, @Nonnull AreaScanner.ScanShape scanShape, int range, @Nonnull Scanner childScanner) { + if (resultCap >= 0 && range >= 0) { + this.resultCap = resultCap; + this.childScanner = childScanner; + this.bounds_voxelGrid = childScanner.getBounds_voxelGrid().clone(); + Bounds3i stampBounds_voxelGrid = childScanner.getBounds_voxelGrid().clone(); + ArrayList scanOrder = new ArrayList<>(); + Vector3i position = new Vector3i(); + + for (position.x = -range; position.x <= range; position.x++) { + for (position.z = -range; position.z <= range; position.z++) { + if (scanShape != AreaScanner.ScanShape.CIRCLE || !(Calculator.distance(position.x, position.z, 0.0, 0.0) > range)) { + scanOrder.add(new Vector2i(position.x, position.z)); + stampBounds_voxelGrid.offset(position); + this.bounds_voxelGrid.encompass(stampBounds_voxelGrid); + stampBounds_voxelGrid.offset(position.negate()); + position.negate(); + } + } + } + + this.scanOrder = VectorUtil.orderByDistanceFrom(new Vector2i(), scanOrder); + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public void scan(@Nonnull Scanner.Context context) { + if (this.resultCap != 0) { + for (Vector2i column : this.scanOrder) { + Vector3i columnOrigin = new Vector3i(context.position.x + column.x, context.position.y, context.position.z + column.y); + Scanner.Context childContext = new Scanner.Context(context); + childContext.position = columnOrigin; + this.childScanner.scan(childContext); + if (context.validPositions_out.size() >= this.resultCap) { + while (context.validPositions_out.size() > this.resultCap) { + context.validPositions_out.removeLast(); + } + + return; + } + } + } + } + + @Override + public void scan(@NonNullDecl Vector3i anchor, @NonNullDecl Pipe.One pipe) { + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; + } + + public static enum ScanShape { + CIRCLE, + SQUARE; + + @Nonnull + public static final Codec CODEC = new EnumCodec<>(AreaScanner.ScanShape.class, EnumCodec.EnumStyle.LEGACY); + } + + public static enum Verticality { + GLOBAL, + LOCAL; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/ColumnLinearScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/ColumnLinearScanner.java new file mode 100644 index 00000000..efe29e0c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/ColumnLinearScanner.java @@ -0,0 +1,86 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners.deprecated; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class ColumnLinearScanner extends Scanner { + private final int minY; + private final int maxY; + private final boolean isRelativeToPosition; + private final double baseHeight; + private final int resultsCap; + private final boolean topDownOrder; + @Nonnull + private final Bounds3i bounds_voxelGrid; + + public ColumnLinearScanner(int minY, int maxY, int resultsCap, boolean topDownOrder, boolean isRelativeToPosition, double baseHeight) { + if (resultsCap < 0) { + throw new IllegalArgumentException(); + } else { + this.baseHeight = baseHeight; + this.minY = minY; + this.maxY = maxY; + this.isRelativeToPosition = isRelativeToPosition; + this.resultsCap = resultsCap; + this.topDownOrder = topDownOrder; + if (!isRelativeToPosition) { + int MIN_SCAN_Y = -1073741824; + int MAX_SCAN_Y = 1073741823; + this.bounds_voxelGrid = new Bounds3i(new Vector3i(0, -1073741824, 0), new Vector3i(1, 1073741823, 1)); + } else { + this.bounds_voxelGrid = new Bounds3i(new Vector3i(0, minY, 0), new Vector3i(1, maxY, 1)); + } + } + } + + @Override + public void scan(@NonNullDecl Scanner.Context context) { + Bounds3i bounds = context.materialSpace.getBounds(); + int scanMinY; + int scanMaxY; + if (this.isRelativeToPosition) { + scanMinY = Math.max(context.position.y + this.minY, bounds.min.y); + scanMaxY = Math.min(context.position.y + this.maxY, bounds.max.y); + } else { + int bedY = (int)this.baseHeight; + scanMinY = Math.max(bedY + this.minY, bounds.min.y); + scanMaxY = Math.min(bedY + this.maxY, bounds.max.y); + } + + Vector3i patternPosition = new Vector3i(context.position); + Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace); + if (this.topDownOrder) { + for (patternPosition.y = scanMaxY - 1; patternPosition.y >= scanMinY; patternPosition.y--) { + if (context.pattern.matches(patternContext)) { + context.validPositions_out.add(new Vector3i(patternPosition)); + if (context.validPositions_out.size() >= this.resultsCap) { + return; + } + } + } + } else { + for (patternPosition.y = scanMinY; patternPosition.y < scanMaxY; patternPosition.y++) { + if (context.pattern.matches(patternContext)) { + context.validPositions_out.add(new Vector3i(patternPosition)); + if (context.validPositions_out.size() >= this.resultsCap) { + return; + } + } + } + } + } + + @Override + public void scan(@NonNullDecl Vector3i anchor, @NonNullDecl Pipe.One pipe) { + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/ColumnRandomScanner.java b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/ColumnRandomScanner.java new file mode 100644 index 00000000..0972d10c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/scanners/deprecated/ColumnRandomScanner.java @@ -0,0 +1,154 @@ +package com.hypixel.hytale.builtin.hytalegenerator.scanners.deprecated; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; +import com.hypixel.hytale.builtin.hytalegenerator.pipe.Pipe; +import com.hypixel.hytale.builtin.hytalegenerator.rng.RngField; +import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner; +import com.hypixel.hytale.math.util.FastRandom; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class ColumnRandomScanner extends Scanner { + private final int minY; + private final int maxY; + private final boolean isRelativeToPosition; + private final double baseHeight; + private final int resultsCap; + @Nonnull + private final RngField rngField; + @Nonnull + private final ColumnRandomScanner.Strategy strategy; + @Nonnull + private final Bounds3i bounds_voxelGrid; + @Nonnull + private final List rPositions; + + public ColumnRandomScanner( + int minY, int maxY, int resultsCap, int seed, @Nonnull ColumnRandomScanner.Strategy strategy, boolean isRelativeToPosition, double baseHeight + ) { + if (resultsCap < 0) { + throw new IllegalArgumentException(); + } else { + this.baseHeight = baseHeight; + this.minY = minY; + this.maxY = maxY; + this.isRelativeToPosition = isRelativeToPosition; + this.resultsCap = resultsCap; + this.rngField = new RngField(seed); + this.strategy = strategy; + if (!isRelativeToPosition) { + int MIN_SCAN_Y = -1073741824; + int MAX_SCAN_Y = 1073741823; + this.bounds_voxelGrid = new Bounds3i(new Vector3i(0, -1073741824, 0), new Vector3i(1, 1073741823, 1)); + } else { + this.bounds_voxelGrid = new Bounds3i(new Vector3i(0, minY, 0), new Vector3i(1, maxY, 1)); + } + + this.rPositions = new ArrayList<>(); + } + } + + @Override + public void scan(@Nonnull Scanner.Context context) { + switch (this.strategy) { + case DART_THROW: + this.scanDartThrow(context); + break; + case PICK_VALID: + this.scanPickValid(context); + } + } + + @Override + public void scan(@Nonnull Vector3i anchor, @Nonnull Pipe.One pipe) { + } + + private void scanPickValid(@Nonnull Scanner.Context context) { + if (this.resultsCap != 0) { + this.rPositions.clear(); + Bounds3i bounds = context.materialSpace.getBounds(); + int scanMinY; + int scanMaxY; + if (this.isRelativeToPosition) { + scanMinY = Math.max(context.position.y + this.minY, bounds.min.y); + scanMaxY = Math.min(context.position.y + this.maxY, bounds.max.y); + } else { + int bedY = (int)this.baseHeight; + scanMinY = Math.max(bedY + this.minY, bounds.min.y); + scanMaxY = Math.min(bedY + this.maxY, bounds.max.y); + } + + int numberOfPossiblePositions = Math.max(0, scanMaxY - scanMinY); + Vector3i patternPosition = new Vector3i(context.position); + Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace); + + for (int y = scanMinY; y < scanMaxY; y++) { + patternPosition.y = y; + if (context.pattern.matches(patternContext)) { + Vector3i position = new Vector3i(context.position); + position.y = y; + this.rPositions.add(position); + } + } + + if (!this.rPositions.isEmpty()) { + if (this.rPositions.size() > this.resultsCap) { + ArrayList usedIndices = new ArrayList<>(this.resultsCap); + FastRandom random = new FastRandom(this.rngField.get(context.position.x, context.position.y, context.position.z)); + + for (int i = 0; i < this.resultsCap; i++) { + int pickedIndex = random.nextInt(this.rPositions.size()); + if (!usedIndices.contains(pickedIndex)) { + usedIndices.add(pickedIndex); + context.validPositions_out.add(this.rPositions.get(pickedIndex)); + } + } + } + } + } + } + + public void scanDartThrow(@NonNullDecl Scanner.Context context) { + if (this.resultsCap != 0) { + Bounds3i bounds = context.materialSpace.getBounds(); + int scanMinY = this.isRelativeToPosition ? Math.max(context.position.y + this.minY, bounds.min.y) : Math.max(this.minY, bounds.min.y); + int scanMaxY = this.isRelativeToPosition ? Math.min(context.position.y + this.maxY, bounds.max.y) : Math.min(this.maxY, bounds.max.y); + int range = scanMaxY - scanMinY; + if (range != 0) { + int TRY_MULTIPLIER = 1; + int numberOfTries = range * 1; + FastRandom random = new FastRandom(this.rngField.get(context.position.x, context.position.y, context.position.z)); + IntArrayList usedYs = new IntArrayList(this.resultsCap); + Vector3i patternPosition = new Vector3i(context.position); + Pattern.Context patternContext = new Pattern.Context(patternPosition, context.materialSpace); + + for (int i = 0; i < numberOfTries; i++) { + patternPosition.y = random.nextInt(range) + scanMinY; + if (context.pattern.matches(patternContext) && !usedYs.contains(patternPosition.y)) { + usedYs.add(patternPosition.y); + Vector3i position = new Vector3i(patternPosition); + context.validPositions_out.add(position); + if (context.validPositions_out.size() == this.resultsCap) { + break; + } + } + } + } + } + } + + @Override + public Bounds3i getBounds_voxelGrid() { + return this.bounds_voxelGrid; + } + + public static enum Strategy { + DART_THROW, + PICK_VALID; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/tintproviders/TintProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/tintproviders/TintProvider.java index ebca3db7..ceaf703f 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/tintproviders/TintProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/tintproviders/TintProvider.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.builtin.hytalegenerator.tintproviders; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import com.hypixel.hytale.protocol.Color; import com.hypixel.hytale.server.core.asset.util.ColorParseUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public abstract class TintProvider { public static final int DEFAULT_TINT = ColorParseUtil.colorToARGBInt(new Color((byte)91, (byte)-98, (byte)40)); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/CacheVectorProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/CacheVectorProvider.java index 887a30d7..d683fed4 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/CacheVectorProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/CacheVectorProvider.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.builtin.hytalegenerator.vectorproviders; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CacheVectorProvider extends VectorProvider { @Nonnull @@ -17,7 +17,7 @@ public class CacheVectorProvider extends VectorProvider { @Override public void process(@Nonnull VectorProvider.Context context, @Nonnull Vector3d vector_out) { if (this.cache.position != null && this.cache.position.equals(context.position)) { - vector_out.assign(this.cache.value); + vector_out.set(this.cache.value); } if (this.cache.position == null) { @@ -25,9 +25,9 @@ public class CacheVectorProvider extends VectorProvider { this.cache.value = new Vector3d(); } - this.cache.position.assign(context.position); + this.cache.position.set(context.position); this.vectorProvider.process(context, this.cache.value); - vector_out.assign(this.cache.value); + vector_out.set(this.cache.value); } public static class Cache { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/ConstantVectorProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/ConstantVectorProvider.java index edd70b71..044a783a 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/ConstantVectorProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/ConstantVectorProvider.java @@ -1,18 +1,18 @@ package com.hypixel.hytale.builtin.hytalegenerator.vectorproviders; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ConstantVectorProvider extends VectorProvider { @Nonnull private final Vector3d value; public ConstantVectorProvider(@Nonnull Vector3d value) { - this.value = value.clone(); + this.value = new Vector3d(value); } @Override public void process(@Nonnull VectorProvider.Context context, @Nonnull Vector3d vector_out) { - vector_out.assign(this.value); + vector_out.set(this.value); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/DensityGradientVectorProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/DensityGradientVectorProvider.java index fec45e17..0b77fbb0 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/DensityGradientVectorProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/DensityGradientVectorProvider.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.hytalegenerator.vectorproviders; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DensityGradientVectorProvider extends VectorProvider { @Nonnull @@ -24,19 +24,19 @@ public class DensityGradientVectorProvider extends VectorProvider { @Override public void process(@Nonnull VectorProvider.Context context, @Nonnull Vector3d vector_out) { - this.rPosition.assign(context.position); + this.rPosition.set(context.position); this.rChildContext.assign(context); this.rChildContext.position = this.rPosition; double valueAtOrigin = this.density.process(this.rChildContext); double maxX = context.position.x + this.sampleDistance; double maxY = context.position.y + this.sampleDistance; double maxZ = context.position.z + this.sampleDistance; - this.rChildContext.position.assign(maxX, context.position.y, context.position.z); + this.rChildContext.position.set(maxX, context.position.y, context.position.z); double deltaX = this.density.process(this.rChildContext) - valueAtOrigin; - this.rChildContext.position.assign(context.position.x, maxY, context.position.z); + this.rChildContext.position.set(context.position.x, maxY, context.position.z); double deltaY = this.density.process(this.rChildContext) - valueAtOrigin; - this.rChildContext.position.assign(context.position.x, context.position.y, maxZ); + this.rChildContext.position.set(context.position.x, context.position.y, maxZ); double deltaZ = this.density.process(this.rChildContext) - valueAtOrigin; - vector_out.assign(deltaX, deltaY, deltaZ); + vector_out.set(deltaX, deltaY, deltaZ); } } diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/VectorProvider.java b/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/VectorProvider.java index 5c7f20f2..ab4e5531 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/VectorProvider.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/vectorproviders/VectorProvider.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.builtin.hytalegenerator.vectorproviders; import com.hypixel.hytale.builtin.hytalegenerator.density.Density; -import com.hypixel.hytale.builtin.hytalegenerator.newsystem.TerrainDensityProvider; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.builtin.hytalegenerator.engine.TerrainDensityProvider; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class VectorProvider { public abstract void process(@Nonnull VectorProvider.Context var1, @Nonnull Vector3d var2); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/ArrayVoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/ArrayVoxelSpace.java new file mode 100644 index 00000000..c11166af --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/ArrayVoxelSpace.java @@ -0,0 +1,74 @@ +package com.hypixel.hytale.builtin.hytalegenerator.voxelspace; + +import com.hypixel.hytale.builtin.hytalegenerator.GridUtils; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class ArrayVoxelSpace implements VoxelSpace { + @Nonnull + private final Bounds3i bounds; + @Nonnull + private final T[] contents; + + public ArrayVoxelSpace(@Nonnull Bounds3i bounds) { + this.bounds = bounds.clone(); + Vector3i size = bounds.getSize(); + int voxelCount = size.x * size.y * size.z; + this.contents = (T[])(new Object[voxelCount]); + } + + public void offset(@Nonnull Vector3i vector) { + this.bounds.offset(vector); + } + + public void offsetOpposite(@Nonnull Vector3i vector) { + this.bounds.offsetOpposite(vector); + } + + @Override + public void set(T content, int x, int y, int z) { + assert this.bounds.contains(x, y, z); + + int index = GridUtils.toIndexFromPositionYXZ(x, y, z, this.bounds); + this.contents[index] = content; + } + + @Override + public void set(T content, @Nonnull Vector3i position) { + this.set(content, position.x, position.y, position.z); + } + + @Override + public void setAll(T content) { + for (int x = this.bounds.min.x; x < this.bounds.max.x; x++) { + for (int y = this.bounds.min.y; y < this.bounds.max.y; y++) { + for (int z = this.bounds.min.z; z < this.bounds.max.z; z++) { + this.set(content, x, y, z); + } + } + } + } + + @Override + public T get(int x, int y, int z) { + assert this.bounds.contains(x, y, z); + + int index = GridUtils.toIndexFromPositionYXZ(x, y, z, this.bounds); + return this.contents[index]; + } + + @Nullable + @Override + public T get(@Nonnull Vector3i position) { + return this.get(position.x, position.y, position.z); + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return this.bounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/MaskVoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/MaskVoxelSpace.java new file mode 100644 index 00000000..84da5260 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/MaskVoxelSpace.java @@ -0,0 +1,68 @@ +package com.hypixel.hytale.builtin.hytalegenerator.voxelspace; + +import com.hypixel.hytale.builtin.hytalegenerator.BlockMask; +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; +import org.joml.Vector3i; + +public class MaskVoxelSpace implements VoxelSpace { + @Nonnull + private final BlockMask mask; + @Nonnull + private VoxelSpace source; + + public MaskVoxelSpace(@Nonnull BlockMask mask, @Nonnull VoxelSpace source) { + this.mask = mask; + this.source = source; + } + + public void setSource(@Nonnull VoxelSpace source) { + this.source = source; + } + + public void set(@NullableDecl Material content, int x, int y, int z) { + assert this.source.getBounds().contains(x, y, z); + + if (this.mask.canPlace(content.hashMaterialIds())) { + Material existingMaterial = this.source.get(x, y, z); + if (this.mask.canReplace(content.hashMaterialIds(), existingMaterial.hashMaterialIds())) { + this.source.set(content, x, y, z); + } + } + } + + public void set(@NullableDecl Material content, @NonNullDecl Vector3i position) { + this.set(content, position.x, position.y, position.z); + } + + public void setAll(@NullableDecl Material content) { + Bounds3i bounds = this.source.getBounds(); + + for (int x = bounds.min.x; x < bounds.max.x; x++) { + for (int z = bounds.min.z; z < bounds.max.z; z++) { + for (int y = bounds.min.y; y < bounds.max.y; y++) { + this.set(content, x, y, z); + } + } + } + } + + @NullableDecl + public Material get(int x, int y, int z) { + return this.source.get(x, y, z); + } + + @NullableDecl + public Material get(@NonNullDecl Vector3i position) { + return this.source.get(position); + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return this.source.getBounds(); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/NullSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/NullSpace.java new file mode 100644 index 00000000..21265276 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/NullSpace.java @@ -0,0 +1,55 @@ +package com.hypixel.hytale.builtin.hytalegenerator.voxelspace; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class NullSpace implements VoxelSpace { + @Nonnull + private static final NullSpace INSTANCE = new NullSpace(); + + @Nonnull + public static NullSpace instance() { + return INSTANCE; + } + + @Nonnull + public static NullSpace instance(@Nonnull Class clazz) { + return INSTANCE; + } + + private NullSpace() { + } + + @Override + public void set(V content, int x, int y, int z) { + } + + @Override + public void set(V content, @Nonnull Vector3i position) { + } + + @Override + public void setAll(V content) { + } + + @Nullable + @Override + public V get(int x, int y, int z) { + return null; + } + + @Nullable + @Override + public V get(@Nonnull Vector3i position) { + return this.get(position.x, position.y, position.z); + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return Bounds3i.ZERO; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/RotationVoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/RotationVoxelSpace.java new file mode 100644 index 00000000..89b5e0ab --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/RotationVoxelSpace.java @@ -0,0 +1,99 @@ +package com.hypixel.hytale.builtin.hytalegenerator.voxelspace; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import com.hypixel.hytale.builtin.hytalegenerator.material.Material; +import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import javax.annotation.Nonnull; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; +import org.joml.Vector3i; +import org.joml.Vector3ic; + +public class RotationVoxelSpace implements VoxelSpace { + @Nonnull + private final RotationTuple rotation_fromViewToSource; + @Nonnull + private final RotationTuple rotation_materialFromSourceToView; + @Nonnull + private final Bounds3i viewBounds; + @Nonnull + private final MaterialCache materialCache; + @Nonnull + private VoxelSpace source; + @Nonnull + private final Vector3i anchor; + @Nonnull + private final Vector3i rSourcePosition; + + public RotationVoxelSpace(@Nonnull RotationTuple rotation_fromViewToSource, @Nonnull MaterialCache materialCache) { + this.rotation_fromViewToSource = rotation_fromViewToSource; + this.rotation_materialFromSourceToView = RotationTuple.of( + rotation_fromViewToSource.yaw().toInverse(), rotation_fromViewToSource.pitch().toInverse(), rotation_fromViewToSource.roll().toInverse() + ); + this.materialCache = materialCache; + this.viewBounds = new Bounds3i(); + this.anchor = new Vector3i(); + this.setSource(NullSpace.instance(), Vector3iUtil.ZERO); + this.rSourcePosition = new Vector3i(); + } + + public void setSource(@Nonnull VoxelSpace source, @Nonnull Vector3ic anchor) { + this.source = source; + this.anchor.set(anchor); + this.viewBounds.assign(source.getBounds()); + this.viewBounds.undoRotationAroundVoxel(this.rotation_fromViewToSource, anchor); + } + + public void set(@NullableDecl Material material, int x, int y, int z) { + this.loadPosition(x, y, z); + Material rotatedMaterial = this.materialCache.getMaterialRotated(material, this.rotation_fromViewToSource); + this.source.set(rotatedMaterial, this.rSourcePosition); + } + + public void set(@NullableDecl Material material, @NonNullDecl Vector3i position) { + this.set(material, position.x, position.y, position.z); + } + + public void setAll(@NullableDecl Material material) { + Bounds3i bounds = this.source.getBounds(); + + for (int x = bounds.min.x; x < bounds.max.x; x++) { + for (int z = bounds.min.z; z < bounds.max.z; z++) { + for (int y = bounds.min.y; y < bounds.max.y; y++) { + this.set(material, x, y, z); + } + } + } + } + + @NullableDecl + public Material get(int x, int y, int z) { + this.loadPosition(x, y, z); + Material material = this.source.get(this.rSourcePosition); + return this.materialCache.getMaterialRotated(material, this.rotation_materialFromSourceToView); + } + + @NullableDecl + public Material get(@NonNullDecl Vector3i position) { + return this.get(position.x, position.y, position.z); + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return this.viewBounds; + } + + private void loadPosition(int x, int y, int z) { + assert this.viewBounds.contains(x, y, z); + + this.rSourcePosition.set(x, y, z); + this.rSourcePosition.sub(this.anchor); + this.rotation_fromViewToSource.applyRotationTo(this.rSourcePosition); + this.rSourcePosition.add(this.anchor); + + assert this.source.getBounds().contains(this.rSourcePosition); + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/VoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/VoxelSpace.java new file mode 100644 index 00000000..8570340d --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/VoxelSpace.java @@ -0,0 +1,23 @@ +package com.hypixel.hytale.builtin.hytalegenerator.voxelspace; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3i; + +public interface VoxelSpace { + void set(@Nullable T var1, int var2, int var3, int var4); + + void set(@Nullable T var1, @Nonnull Vector3i var2); + + void setAll(@Nullable T var1); + + @Nullable + T get(int var1, int var2, int var3); + + @Nullable + T get(@Nonnull Vector3i var1); + + @Nonnull + Bounds3i getBounds(); +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/VoxelSpaceUtil.java b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/VoxelSpaceUtil.java new file mode 100644 index 00000000..b5f92c11 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/VoxelSpaceUtil.java @@ -0,0 +1,47 @@ +package com.hypixel.hytale.builtin.hytalegenerator.voxelspace; + +import com.hypixel.hytale.builtin.hytalegenerator.LoggerUtil; +import com.hypixel.hytale.common.util.ExceptionUtil; + +public class VoxelSpaceUtil { + private static class BatchTransfer implements Runnable { + private final VoxelSpace source; + private final VoxelSpace destination; + private final int minX; + private final int minY; + private final int minZ; + private final int maxX; + private final int maxY; + private final int maxZ; + + private BatchTransfer(VoxelSpace source, VoxelSpace destination, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { + this.source = source; + this.destination = destination; + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.maxX = maxX; + this.maxY = maxY; + this.maxZ = maxZ; + } + + @Override + public void run() { + try { + for (int x = this.minX; x < this.maxX; x++) { + for (int y = this.minY; y < this.maxY; y++) { + for (int z = this.minZ; z < this.maxZ; z++) { + if (this.destination.getBounds().contains(x, y, z)) { + this.destination.set(this.source.get(x, y, z), x, y, z); + } + } + } + } + } catch (Exception var4) { + String msg = "Exception thrown by HytaleGenerator while attempting a BatchTransfer operation:\n"; + msg = msg + ExceptionUtil.toStringWithStack(var4); + LoggerUtil.getLogger().severe(msg); + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/WindowVoxelSpace.java b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/WindowVoxelSpace.java new file mode 100644 index 00000000..ff5a7efb --- /dev/null +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/voxelspace/WindowVoxelSpace.java @@ -0,0 +1,81 @@ +package com.hypixel.hytale.builtin.hytalegenerator.voxelspace; + +import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3i; + +public class WindowVoxelSpace implements VoxelSpace { + @Nonnull + private final VoxelSpace source; + @Nonnull + private final Bounds3i bounds; + + public WindowVoxelSpace(@Nonnull VoxelSpace voxelSpace) { + this.source = voxelSpace; + this.bounds = voxelSpace.getBounds().clone(); + } + + public void setBounds(@Nonnull Bounds3i bounds) { + assert this.source.getBounds().contains(bounds); + + this.bounds.assign(bounds); + } + + public void setBounds(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { + this.bounds.min.set(minX, minY, minZ); + this.bounds.max.set(maxX, maxY, maxZ); + } + + @Nonnull + public VoxelSpace getSourceVoxelSpace() { + return this.source; + } + + @Override + public void set(T content, int x, int y, int z) { + if (this.bounds.contains(x, y, z)) { + if (this.source.getBounds().contains(x, y, z)) { + this.source.set(content, x, y, z); + } + } + } + + @Override + public void set(T content, @Nonnull Vector3i position) { + this.set(content, position.x, position.y, position.z); + } + + @Override + public void setAll(T content) { + for (int x = this.bounds.min.x; x < this.bounds.max.x; x++) { + for (int y = this.bounds.min.y; y < this.bounds.max.y; y++) { + for (int z = this.bounds.min.z; z < this.bounds.max.z; z++) { + this.set(content, x, y, z); + } + } + } + } + + @Override + public T get(int x, int y, int z) { + if (!this.getBounds().contains(x, y, z)) { + throw new IllegalArgumentException("outside schematic"); + } else { + return this.source.get(x, y, z); + } + } + + @Nullable + @Override + public T get(@Nonnull Vector3i position) { + return this.get(position.x, position.y, position.z); + } + + @NonNullDecl + @Override + public Bounds3i getBounds() { + return this.bounds; + } +} diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/threadindexer/WorkerIndexer.java b/src/com/hypixel/hytale/builtin/hytalegenerator/workerindexer/WorkerIndexer.java similarity index 93% rename from src/com/hypixel/hytale/builtin/hytalegenerator/threadindexer/WorkerIndexer.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/workerindexer/WorkerIndexer.java index 1f77facb..347ced69 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/threadindexer/WorkerIndexer.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/workerindexer/WorkerIndexer.java @@ -1,8 +1,9 @@ -package com.hypixel.hytale.builtin.hytalegenerator.threadindexer; +package com.hypixel.hytale.builtin.hytalegenerator.workerindexer; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Supplier; import javax.annotation.Nonnull; @@ -114,7 +115,13 @@ public class WorkerIndexer { } public class Session { - private int index = 0; + private int index; + + public Session() { + Objects.requireNonNull(WorkerIndexer.this); + super(); + this.index = 0; + } public WorkerIndexer.Id next() { if (this.index >= WorkerIndexer.this.workerCount) { diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/BiCarta.java b/src/com/hypixel/hytale/builtin/hytalegenerator/worldstructure/BiCarta.java similarity index 61% rename from src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/BiCarta.java rename to src/com/hypixel/hytale/builtin/hytalegenerator/worldstructure/BiCarta.java index d49500d8..826b46b7 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/framework/interfaces/functions/BiCarta.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/worldstructure/BiCarta.java @@ -1,9 +1,10 @@ -package com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions; +package com.hypixel.hytale.builtin.hytalegenerator.worldstructure; -import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer; +import com.hypixel.hytale.builtin.hytalegenerator.workerindexer.WorkerIndexer; import java.util.List; import javax.annotation.Nonnull; +@Deprecated public abstract class BiCarta { public abstract R apply(int var1, int var2, @Nonnull WorkerIndexer.Id var3); diff --git a/src/com/hypixel/hytale/builtin/hytalegenerator/worldstructure/WorldStructure.java b/src/com/hypixel/hytale/builtin/hytalegenerator/worldstructure/WorldStructure.java index 5c549bfa..a4676ef8 100644 --- a/src/com/hypixel/hytale/builtin/hytalegenerator/worldstructure/WorldStructure.java +++ b/src/com/hypixel/hytale/builtin/hytalegenerator/worldstructure/WorldStructure.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.builtin.hytalegenerator.worldstructure; import com.hypixel.hytale.builtin.hytalegenerator.Registry; import com.hypixel.hytale.builtin.hytalegenerator.biome.Biome; -import com.hypixel.hytale.builtin.hytalegenerator.framework.interfaces.functions.BiCarta; import com.hypixel.hytale.builtin.hytalegenerator.positionproviders.PositionProvider; import javax.annotation.Nonnull; diff --git a/src/com/hypixel/hytale/builtin/instances/InstancesPlugin.java b/src/com/hypixel/hytale/builtin/instances/InstancesPlugin.java index b083c6e6..0573f0b2 100644 --- a/src/com/hypixel/hytale/builtin/instances/InstancesPlugin.java +++ b/src/com/hypixel/hytale/builtin/instances/InstancesPlugin.java @@ -21,6 +21,7 @@ import com.hypixel.hytale.builtin.instances.removal.RemovalCondition; import com.hypixel.hytale.builtin.instances.removal.RemovalSystem; import com.hypixel.hytale.builtin.instances.removal.TimeoutCondition; import com.hypixel.hytale.builtin.instances.removal.WorldEmptyCondition; +import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.codec.schema.config.ObjectSchema; import com.hypixel.hytale.codec.schema.config.Schema; import com.hypixel.hytale.codec.schema.config.StringSchema; @@ -35,14 +36,13 @@ import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.Options; import com.hypixel.hytale.server.core.asset.AssetModule; -import com.hypixel.hytale.server.core.asset.GenerateSchemaEvent; import com.hypixel.hytale.server.core.asset.LoadAssetEvent; import com.hypixel.hytale.server.core.asset.type.gameplay.respawn.RespawnController; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; @@ -60,6 +60,7 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Int import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; +import com.hypixel.hytale.server.core.schema.SchemaGenerator; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.SoundUtil; @@ -87,6 +88,7 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -95,6 +97,7 @@ import java.util.logging.Level; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class InstancesPlugin extends JavaPlugin { private static InstancesPlugin instance; @@ -122,7 +125,18 @@ public class InstancesPlugin extends JavaPlugin { ComponentRegistryProxy chunkStoreRegistry = this.getChunkStoreRegistry(); this.getCommandRegistry().registerCommand(new InstancesCommand()); eventRegistry.register((short)64, LoadAssetEvent.class, this::validateInstanceAssets); - eventRegistry.register(GenerateSchemaEvent.class, InstancesPlugin::generateSchema); + SchemaGenerator.registerAssetSchema("InstanceConfig.json", ctx -> { + ObjectSchema worldConfig = WorldConfig.CODEC.toSchema(ctx); + Map props = worldConfig.getProperties(); + props.put("UUID", Schema.anyOf(new StringSchema(), new ObjectSchema())); + worldConfig.setTitle("Instance Configuration"); + worldConfig.setId("InstanceConfig.json"); + Schema.HytaleMetadata hytale = worldConfig.getHytale(); + hytale.setPath("Instances"); + hytale.setExtension("instance.bson"); + hytale.setUiEditorIgnore(Boolean.TRUE); + return worldConfig; + }, List.of("Instances/**/instance.bson"), ".bson"); eventRegistry.registerGlobal(AddPlayerToWorldEvent.class, InstancesPlugin::onPlayerAddToWorld); eventRegistry.registerGlobal(DrainPlayerFromWorldEvent.class, InstancesPlugin::onPlayerDrainFromWorld); eventRegistry.register(PlayerConnectEvent.class, InstancesPlugin::onPlayerConnect); @@ -226,7 +240,7 @@ public class InstancesPlugin extends JavaPlugin { assert transformComponent != null; - Transform originalPosition = transformComponent.getTransform().clone(); + Transform originalPosition = new Transform(transformComponent.getTransform()); InstanceEntityConfig instanceEntityConfigComponent = componentAccessor.getComponent(entityRef, InstanceEntityConfig.getComponentType()); if (instanceEntityConfigComponent == null) { instanceEntityConfigComponent = componentAccessor.addComponent(entityRef, InstanceEntityConfig.getComponentType()); @@ -247,6 +261,12 @@ public class InstancesPlugin extends JavaPlugin { assert uuidComponent != null; UUID playerUUID = uuidComponent.getUuid(); + HeadRotation headRotation = componentAccessor.getComponent(entityRef, HeadRotation.getComponentType()); + if (headRotation != null) { + componentAccessor.ensureAndGetComponent(entityRef, TeleportHistory.getComponentType()) + .append(originalWorld, new Vector3d(originalPosition.getPosition()), headRotation.getRotation().clone(), "Instance"); + } + InstanceEntityConfig finalPlayerConfig = instanceEntityConfigComponent; CompletableFuture.runAsync(playerRefComponent::removeFromStore, originalWorld) .thenCombine(worldFuture.orTimeout(1L, TimeUnit.MINUTES), (ignored, world) -> (World)world) @@ -270,7 +290,7 @@ public class InstancesPlugin extends JavaPlugin { defaultWorld.addPlayer(playerRefComponent, null, Boolean.TRUE, Boolean.FALSE); } else { get().getLogger().at(Level.SEVERE).log("No fallback world for %s, disconnecting", playerRefComponent.getUsername()); - playerRefComponent.getPacketHandler().disconnect("Failed to teleport - no world available"); + playerRefComponent.getPacketHandler().disconnect(Message.translation("server.general.disconnect.teleportNoWorld")); } } } @@ -300,6 +320,15 @@ public class InstancesPlugin extends JavaPlugin { if (spawnProvider == null) { throw new IllegalStateException("Spawn provider cannot be null when teleporting player to instance!"); } else { + TransformComponent transformComponent = componentAccessor.getComponent(playerRef, TransformComponent.getComponentType()); + HeadRotation headRotation = componentAccessor.getComponent(playerRef, HeadRotation.getComponentType()); + if (transformComponent != null && headRotation != null) { + componentAccessor.ensureAndGetComponent(playerRef, TeleportHistory.getComponentType()) + .append( + originalWorld, new Vector3d(transformComponent.getPosition()), headRotation.getRotation().clone(), "Instance '" + targetWorld.getName() + "'" + ); + } + Transform spawnTransform = spawnProvider.getSpawnPoint(targetWorld, playerUUID); Teleport teleportComponent = Teleport.createForPlayer(targetWorld, spawnTransform); componentAccessor.addComponent(playerRef, Teleport.getComponentType(), teleportComponent); @@ -324,6 +353,13 @@ public class InstancesPlugin extends JavaPlugin { if (targetWorld == null) { throw new IllegalArgumentException("Missing return world"); } else { + TransformComponent transformComponent = componentAccessor.getComponent(targetRef, TransformComponent.getComponentType()); + HeadRotation headRotation = componentAccessor.getComponent(targetRef, HeadRotation.getComponentType()); + if (transformComponent != null && headRotation != null) { + componentAccessor.ensureAndGetComponent(targetRef, TeleportHistory.getComponentType()) + .append(world, new Vector3d(transformComponent.getPosition()), headRotation.getRotation().clone(), "Instance '" + world.getName() + "'"); + } + Teleport teleportComponent = Teleport.createForPlayer(targetWorld, returnPoint.getReturnPoint()); CompletableFuture future = new CompletableFuture<>(); teleportComponent.setOnComplete(future); @@ -408,6 +444,10 @@ public class InstancesPlugin extends JavaPlugin { if (Files.isDirectory(path)) { try { Files.walkFileTree(path, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor() { + { + Objects.requireNonNull(InstancesPlugin.this); + } + @Nonnull public FileVisitResult preVisitDirectory(@Nonnull Path dir, @Nonnull BasicFileAttributes attrs) { if (Files.exists(dir.resolve("instance.bson"))) { @@ -446,8 +486,8 @@ public class InstancesPlugin extends JavaPlugin { Transform transform = fallbackWorld.getReturnPoint(); TransformComponent transformComponent = holder.ensureAndGetComponent(TransformComponent.getComponentType()); transformComponent.setPosition(transform.getPosition()); - Vector3f rotationClone = transformComponent.getRotation().clone(); - rotationClone.setYaw(transform.getRotation().getYaw()); + Rotation3f rotationClone = new Rotation3f(transformComponent.getRotation()); + rotationClone.setYaw(transform.getRotation().yaw()); transformComponent.setRotation(rotationClone); HeadRotation headRotationComponent = holder.ensureAndGetComponent(HeadRotation.getComponentType()); headRotationComponent.teleportRotation(transform.getRotation()); @@ -552,23 +592,6 @@ public class InstancesPlugin extends JavaPlugin { } } - private static void generateSchema(@Nonnull GenerateSchemaEvent event) { - ObjectSchema worldConfig = WorldConfig.CODEC.toSchema(event.getContext()); - Map props = worldConfig.getProperties(); - props.put("UUID", Schema.anyOf(new StringSchema(), new ObjectSchema())); - worldConfig.setTitle("Instance Configuration"); - worldConfig.setId("InstanceConfig.json"); - Schema.HytaleMetadata hytaleMetadata = worldConfig.getHytale(); - if (hytaleMetadata != null) { - hytaleMetadata.setPath("Instances"); - hytaleMetadata.setExtension("instance.bson"); - hytaleMetadata.setUiEditorIgnore(Boolean.TRUE); - } - - event.addSchema("InstanceConfig.json", worldConfig); - event.addSchemaLink("InstanceConfig", List.of("Instances/**/instance.bson"), ".bson"); - } - private void validateInstanceAssets(@Nonnull LoadAssetEvent event) { Path path = AssetModule.get().getBaseAssetPack().getRoot().resolve("Server").resolve("Instances"); if (Options.getOptionSet().has(Options.VALIDATE_ASSETS) && Files.isDirectory(path) && !event.isShouldShutdown()) { @@ -597,7 +620,7 @@ public class InstancesPlugin extends JavaPlugin { try { World world = universe.makeWorld(worldName, instancePath, config, false).join(); EnumSet options = EnumSet.of(ValidationOption.BLOCK_STATES, ValidationOption.BLOCKS); - world.validate(sb, WorldValidationUtil.blockValidator(errors, options), options); + world.validate(sb, WorldValidationUtil.blockValidator(sb, options), options); } catch (Exception var18) { sb.append("\t").append(var18.getMessage()); this.getLogger().at(Level.SEVERE).withCause(var18).log("Failed to validate: " + name); diff --git a/src/com/hypixel/hytale/builtin/instances/blocks/ConfigurableInstanceBlock.java b/src/com/hypixel/hytale/builtin/instances/blocks/ConfigurableInstanceBlock.java index 55076203..97bd12bc 100644 --- a/src/com/hypixel/hytale/builtin/instances/blocks/ConfigurableInstanceBlock.java +++ b/src/com/hypixel/hytale/builtin/instances/blocks/ConfigurableInstanceBlock.java @@ -13,14 +13,15 @@ import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import java.util.UUID; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ConfigurableInstanceBlock implements Component { @Nonnull @@ -41,13 +42,13 @@ public class ConfigurableInstanceBlock implements Component { .appendInherited(new KeyedCodec<>("InstanceKey", Codec.STRING), (o, i) -> o.instanceKey = i, o -> o.instanceKey, (o, p) -> o.instanceKey = p.instanceKey) .add() .appendInherited( - new KeyedCodec<>("PositionOffset", Vector3d.CODEC), + new KeyedCodec<>("PositionOffset", Vector3dUtil.CODEC), (o, i) -> o.positionOffset = i, o -> o.positionOffset, (o, p) -> o.positionOffset = p.positionOffset ) .add() - .appendInherited(new KeyedCodec<>("Rotation", Vector3f.ROTATION), (o, i) -> o.rotation = i, o -> o.rotation, (o, p) -> o.rotation = p.rotation) + .appendInherited(new KeyedCodec<>("Rotation", Rotation3f.CODEC), (o, i) -> o.rotation = i, o -> o.rotation, (o, p) -> o.rotation = p.rotation) .add() .appendInherited( new KeyedCodec<>("PersonalReturnPoint", Codec.BOOLEAN), @@ -72,7 +73,7 @@ public class ConfigurableInstanceBlock implements Component { @Nullable private Vector3d positionOffset; @Nullable - private Vector3f rotation; + private Rotation3f rotation; private boolean personalReturnPoint = false; private double removeBlockAfter = -1.0; @@ -90,7 +91,7 @@ public class ConfigurableInstanceBlock implements Component { String instanceName, String instanceKey, @Nullable Vector3d positionOffset, - @Nullable Vector3f rotation, + @Nullable Rotation3f rotation, boolean personalReturnPoint, double removeBlockAfter ) { @@ -154,11 +155,11 @@ public class ConfigurableInstanceBlock implements Component { } @Nullable - public Vector3f getRotation() { + public Rotation3f getRotation() { return this.rotation; } - public void setRotation(@Nullable Vector3f rotation) { + public void setRotation(@Nullable Rotation3f rotation) { this.rotation = rotation; } diff --git a/src/com/hypixel/hytale/builtin/instances/command/InstanceMigrateCommand.java b/src/com/hypixel/hytale/builtin/instances/command/InstanceMigrateCommand.java index e1885b7f..e1dea5f3 100644 --- a/src/com/hypixel/hytale/builtin/instances/command/InstanceMigrateCommand.java +++ b/src/com/hypixel/hytale/builtin/instances/command/InstanceMigrateCommand.java @@ -15,12 +15,14 @@ import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncCommand; +import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.migrations.ChunkColumnMigrationSystem; import com.hypixel.hytale.server.core.modules.migrations.ChunkSectionMigrationSystem; import com.hypixel.hytale.server.core.modules.migrations.MigrationModule; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.WorldConfig; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.EntityChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; @@ -29,6 +31,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.IChunkLoader; import com.hypixel.hytale.server.core.universe.world.storage.IChunkSaver; import com.hypixel.hytale.server.core.universe.world.storage.component.ChunkSavingSystems; import com.hypixel.hytale.sneakythrow.SneakyThrow; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; @@ -102,7 +105,7 @@ public class InstanceMigrateCommand extends AbstractAsyncCommand { return CompletableFuture.>supplyAsync(() -> { ChunkStore chunkStore = world.getChunkStore(); ChunkSavingSystems.Data data = chunkStore.getStore().getResource(ChunkStore.SAVE_RESOURCE); - data.isSaving = false; + world.lockSaving(); return data.waitForSavingChunks(); }, world).thenCompose(val -> (CompletionStage)val).thenComposeAsync(SneakyThrow.sneakyFunction(_void -> { LongSet chunks = loader.getIndexes(); @@ -136,7 +139,7 @@ public class InstanceMigrateCommand extends AbstractAsyncCommand { while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { ChunkColumnMigrationSystem system = data.getSystem(systemIndex, systemType); if (system.test(ChunkStore.REGISTRY, holder.getArchetype())) { - system.onEntityRemoved((Holder)holder, RemoveReason.REMOVE, store); + system.onEntityRemoved((Holder)holder, RemoveReason.UNLOAD, store); } } @@ -169,7 +172,38 @@ public class InstanceMigrateCommand extends AbstractAsyncCommand { for (int ix = 0; ix < entities.size(); ix++) { Holder section = entities.get(ix); if (system.test(EntityStore.REGISTRY, section.getArchetype())) { - system.onEntityRemoved(section, RemoveReason.REMOVE, entityStore); + system.onEntityRemoved(section, RemoveReason.UNLOAD, entityStore); + } + } + } + } + + BlockComponentChunk blockComponentChunk = holder.getComponent(BlockComponentChunk.getComponentType()); + if (blockComponentChunk != null && !blockComponentChunk.getEntityHolders().isEmpty()) { + Int2ObjectMap> blockHolders = blockComponentChunk.getEntityHolders(); + SystemType systemTypex = BlockModule.get().getMigrationSystemType(); + BitSet systemIndexesx = data.getSystemIndexesForType(systemTypex); + int systemIndexx = -1; + + while ((systemIndexx = systemIndexesx.nextSetBit(systemIndexx + 1)) >= 0) { + BlockModule.MigrationSystem system = data.getSystem(systemIndexx, systemTypex); + + for (Holder blockHolder : blockHolders.values()) { + if (system.test(ChunkStore.REGISTRY, blockHolder.getArchetype())) { + system.onEntityAdd(blockHolder, AddReason.LOAD, store); + shouldSave = true; + } + } + } + + systemIndexx = -1; + + while ((systemIndexx = systemIndexesx.nextSetBit(systemIndexx + 1)) >= 0) { + BlockModule.MigrationSystem system = data.getSystem(systemIndexx, systemTypex); + + for (Holder blockHolderx : blockHolders.values()) { + if (system.test(ChunkStore.REGISTRY, blockHolderx.getArchetype())) { + system.onEntityRemoved(blockHolderx, RemoveReason.REMOVE, store); } } } @@ -217,6 +251,7 @@ public class InstanceMigrateCommand extends AbstractAsyncCommand { } return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).whenCompleteAsync((result, throwable) -> { + world.unlockSaving(); context.sendMessage(Message.translation("server.commands.instances.migrate.worldDone").param("asset", asset)); Universe.get().removeWorld(worldName); }); diff --git a/src/com/hypixel/hytale/builtin/instances/command/InstanceSpawnCommand.java b/src/com/hypixel/hytale/builtin/instances/command/InstanceSpawnCommand.java index 68bc6c8f..54487fd7 100644 --- a/src/com/hypixel/hytale/builtin/instances/command/InstanceSpawnCommand.java +++ b/src/com/hypixel/hytale/builtin/instances/command/InstanceSpawnCommand.java @@ -5,9 +5,9 @@ import com.hypixel.hytale.builtin.instances.InstancesPlugin; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -21,6 +21,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class InstanceSpawnCommand extends AbstractPlayerCommand { @Nonnull @@ -31,8 +32,12 @@ public class InstanceSpawnCommand extends AbstractPlayerCommand { "position", "server.commands.instances.spawn.arg.position", ArgTypes.RELATIVE_POSITION ); @Nonnull - private final DefaultArg rotationArg = this.withDefaultArg( - "rotation", "server.commands.instances.spawn.arg.rotation", ArgTypes.ROTATION, Vector3f.FORWARD, "server.commands.instances.spawn.arg.rotation.default" + private final DefaultArg rotationArg = this.withDefaultArg( + "rotation", + "server.commands.instances.spawn.arg.rotation", + ArgTypes.ROTATION, + Rotation3f.IDENTITY, + "server.commands.instances.spawn.arg.rotation.default" ); public InstanceSpawnCommand() { @@ -40,10 +45,10 @@ public class InstanceSpawnCommand extends AbstractPlayerCommand { this.addAliases("sp"); } - protected Vector3f getSpawnRotation( + protected Rotation3fc getSpawnRotation( @Nonnull Ref ref, @Nonnull CommandContext context, - @Nonnull DefaultArg rotationArg, + @Nonnull DefaultArg rotationArg, @Nonnull ComponentAccessor componentAccessor ) { if (!rotationArg.provided(context) && context.isPlayer()) { @@ -51,7 +56,7 @@ public class InstanceSpawnCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - return headRotationComponent.getRotation().clone(); + return new Rotation3f(headRotationComponent.getRotation()); } else { return rotationArg.get(context); } @@ -72,7 +77,7 @@ public class InstanceSpawnCommand extends AbstractPlayerCommand { position = this.positionArg.get(context).getRelativePosition(context, world, store); } - Transform returnLocation = new Transform(position.clone(), this.getSpawnRotation(ref, context, this.rotationArg, store).clone()); + Transform returnLocation = new Transform(new Vector3d(position), new Rotation3f(this.getSpawnRotation(ref, context, this.rotationArg, store))); String instanceName = this.instanceNameArg.get(context); CompletableFuture instanceWorld = InstancesPlugin.get().spawnInstance(instanceName, world, returnLocation); InstancesPlugin.teleportPlayerToLoadingInstance(ref, store, instanceWorld, null); diff --git a/src/com/hypixel/hytale/builtin/instances/interactions/TeleportConfigInstanceInteraction.java b/src/com/hypixel/hytale/builtin/instances/interactions/TeleportConfigInstanceInteraction.java index 6af985f7..913a2666 100644 --- a/src/com/hypixel/hytale/builtin/instances/interactions/TeleportConfigInstanceInteraction.java +++ b/src/com/hypixel/hytale/builtin/instances/interactions/TeleportConfigInstanceInteraction.java @@ -13,10 +13,8 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -31,6 +29,7 @@ import com.hypixel.hytale.server.core.modules.entity.teleport.PendingTeleport; import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; @@ -44,6 +43,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class TeleportConfigInstanceInteraction extends SimpleBlockInteraction { @Nonnull @@ -81,6 +82,10 @@ public class TeleportConfigInstanceInteraction extends SimpleBlockInteraction { Ref ref = context.getEntity(); Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); if (playerComponent != null && !playerComponent.isWaitingForClientReady()) { + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + + assert playerRefComponent != null; + Archetype archetype = commandBuffer.getArchetype(ref); if (!archetype.contains(Teleport.getComponentType()) && !archetype.contains(PendingTeleport.getComponentType())) { InstancesPlugin module = InstancesPlugin.get(); @@ -98,7 +103,7 @@ public class TeleportConfigInstanceInteraction extends SimpleBlockInteraction { .getComponent(blockRef, ConfigurableInstanceBlock.getComponentType()); if (configurableInstanceBlock != null) { if (configurableInstanceBlock.getInstanceName() == null) { - playerComponent.sendMessage(MESSAGE_GENERAL_INTERACTION_CONFIGURE_INSTANCE_NO_INSTANCE_NAME); + playerRefComponent.sendMessage(MESSAGE_GENERAL_INTERACTION_CONFIGURE_INSTANCE_NO_INSTANCE_NAME); } else { CompletableFuture targetWorldFuture = null; Transform returnPoint = null; @@ -232,15 +237,17 @@ public class TeleportConfigInstanceInteraction extends SimpleBlockInteraction { throw new IllegalArgumentException("Hitbox asset not found for block type: " + blockType.getId()); } else { Box hitbox = hitboxAsset.get(rotationIndex).getBoundingBox(); - Vector3d position = state.getPositionOffset() != null ? rotation.rotate(state.getPositionOffset()) : new Vector3d(); + Vector3d position = state.getPositionOffset() != null ? rotation.rotatedVector(state.getPositionOffset()) : new Vector3d(); position.x = position.x + (hitbox.middleX() + targetBlock.x); position.y = position.y + (hitbox.middleY() + targetBlock.y); position.z = position.z + (hitbox.middleZ() + targetBlock.z); - Vector3f rotationOutput = Vector3f.NaN; + Rotation3f rotationOutput; if (state.getRotation() != null) { - rotationOutput = state.getRotation().clone(); + rotationOutput = new Rotation3f(state.getRotation()); rotationOutput.addRotationOnAxis(Axis.Y, rotation.yaw().getDegrees()); rotationOutput.addRotationOnAxis(Axis.X, rotation.pitch().getDegrees()); + } else { + rotationOutput = new Rotation3f(Rotation3f.NaN); } return new Transform(position, rotationOutput); diff --git a/src/com/hypixel/hytale/builtin/instances/interactions/TeleportInstanceInteraction.java b/src/com/hypixel/hytale/builtin/instances/interactions/TeleportInstanceInteraction.java index 8607b6fe..0f88358d 100644 --- a/src/com/hypixel/hytale/builtin/instances/interactions/TeleportInstanceInteraction.java +++ b/src/com/hypixel/hytale/builtin/instances/interactions/TeleportInstanceInteraction.java @@ -18,9 +18,10 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -46,6 +47,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class TeleportInstanceInteraction extends SimpleInstantInteraction { @Nonnull @@ -66,14 +68,14 @@ public class TeleportInstanceInteraction extends SimpleInstantInteraction { .documentation("The key to name the world. Random if not provided") .add() .appendInherited( - new KeyedCodec<>("PositionOffset", Vector3d.CODEC), + new KeyedCodec<>("PositionOffset", Vector3dUtil.CODEC), (o, i) -> o.positionOffset = i, o -> o.positionOffset, (o, p) -> o.positionOffset = p.positionOffset ) .documentation("The offset to apply to the return point.\n\nUsed to prevent repeated interactions when returning from the instance.") .add() - .appendInherited(new KeyedCodec<>("Rotation", Vector3f.ROTATION), (o, i) -> o.rotation = i, o -> o.rotation, (o, p) -> o.rotation = p.rotation) + .appendInherited(new KeyedCodec<>("Rotation", Rotation3f.CODEC), (o, i) -> o.rotation = i, o -> o.rotation, (o, p) -> o.rotation = p.rotation) .documentation("The rotation to set the player to when returning from an instance.") .add() .appendInherited( @@ -115,7 +117,7 @@ public class TeleportInstanceInteraction extends SimpleInstantInteraction { .add() .afterDecode(i -> { if (i.rotation != null) { - i.rotation.scale((float) (Math.PI / 180.0)); + i.rotation.mul((float) (Math.PI / 180.0)); } }) .build(); @@ -123,7 +125,7 @@ public class TeleportInstanceInteraction extends SimpleInstantInteraction { private String instanceName; private String instanceKey; private Vector3d positionOffset; - private Vector3f rotation; + private Rotation3f rotation; @Nonnull private TeleportInstanceInteraction.OriginSource originSource = TeleportInstanceInteraction.OriginSource.PLAYER; private boolean personalReturnPoint = false; @@ -272,9 +274,9 @@ public class TeleportInstanceInteraction extends SimpleInstantInteraction { assert transformComponent != null; - transform = transformComponent.getTransform().clone(); + transform = new Transform(transformComponent.getTransform()); transform.getPosition().add(this.positionOffset); - transform.setRotation(this.rotation != null ? this.rotation : Vector3f.NaN); + transform.setRotation((Rotation3fc)(this.rotation != null ? this.rotation : Rotation3f.NaN)); break; case BLOCK: BlockPosition targetBlock = context.getTargetBlock(); @@ -293,15 +295,17 @@ public class TeleportInstanceInteraction extends SimpleInstantInteraction { RotationTuple rotationTuple = RotationTuple.get(rotationIndex); IndexedLookupTableAssetMap hitboxAssetMap = BlockBoundingBoxes.getAssetMap(); Box hitbox = hitboxAssetMap.getAsset(blockType.getHitboxTypeIndex()).get(rotationIndex).getBoundingBox(); - Vector3d position = this.positionOffset != null ? rotationTuple.rotate(this.positionOffset) : new Vector3d(); + Vector3d position = this.positionOffset != null ? rotationTuple.rotatedVector(this.positionOffset) : new Vector3d(); position.x = position.x + (hitbox.middleX() + targetBlock.x); position.y = position.y + (hitbox.middleY() + targetBlock.y); position.z = position.z + (hitbox.middleZ() + targetBlock.z); - Vector3f rotation = Vector3f.NaN; + Rotation3f rotation; if (this.rotation != null) { - rotation = this.rotation.clone(); + rotation = new Rotation3f(this.rotation); rotation.addRotationOnAxis(Axis.Y, rotationTuple.yaw().getDegrees()); rotation.addRotationOnAxis(Axis.X, rotationTuple.pitch().getDegrees()); + } else { + rotation = new Rotation3f(Rotation3f.NaN); } transform = new Transform(position, rotation); diff --git a/src/com/hypixel/hytale/builtin/instances/page/ConfigureInstanceBlockPage.java b/src/com/hypixel/hytale/builtin/instances/page/ConfigureInstanceBlockPage.java index 7025dbf9..e9b9c862 100644 --- a/src/com/hypixel/hytale/builtin/instances/page/ConfigureInstanceBlockPage.java +++ b/src/com/hypixel/hytale/builtin/instances/page/ConfigureInstanceBlockPage.java @@ -8,8 +8,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.Page; @@ -29,6 +28,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ConfigureInstanceBlockPage extends InteractiveCustomUIPage { @Nonnull @@ -38,14 +38,14 @@ public class ConfigureInstanceBlockPage extends InteractiveCustomUIPage ref) { super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, ConfigureInstanceBlockPage.PageData.CODEC); this.instanceBlock = ref.getStore().getComponent(ref, ConfigurableInstanceBlock.getComponentType()); this.ref = ref; - this.positionOffset = this.instanceBlock.getPositionOffset() != null ? this.instanceBlock.getPositionOffset().clone() : null; - this.rotation = this.instanceBlock.getRotation() != null ? this.instanceBlock.getRotation().clone() : null; + this.positionOffset = this.instanceBlock.getPositionOffset() != null ? new Vector3d(this.instanceBlock.getPositionOffset()) : null; + this.rotation = this.instanceBlock.getRotation() != null ? new Rotation3f(this.instanceBlock.getRotation()) : null; } @Override @@ -128,7 +128,7 @@ public class ConfigureInstanceBlockPage extends InteractiveCustomUIPage { @Nonnull @@ -172,7 +173,7 @@ public class InstanceListPage extends InteractiveCustomUIPage instanceWorld = InstancesPlugin.get().spawnInstance(instanceName, world, returnLocation); InstancesPlugin.teleportPlayerToLoadingInstance(ref, store, instanceWorld, null); }); diff --git a/src/com/hypixel/hytale/builtin/model/pages/ChangeModelPage.java b/src/com/hypixel/hytale/builtin/model/pages/ChangeModelPage.java index 02469289..3132cc74 100644 --- a/src/com/hypixel/hytale/builtin/model/pages/ChangeModelPage.java +++ b/src/com/hypixel/hytale/builtin/model/pages/ChangeModelPage.java @@ -10,9 +10,8 @@ import com.hypixel.hytale.component.NonSerialized; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.Page; @@ -42,6 +41,7 @@ import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ChangeModelPage extends InteractiveCustomUIPage { @Nonnull @@ -58,7 +58,7 @@ public class ChangeModelPage extends InteractiveCustomUIPage modelPreview; private Vector3d position; - private Vector3f rotation; + private Rotation3f rotation; private float scale = 1.0F; public ChangeModelPage(@Nonnull PlayerRef playerRef) { @@ -193,10 +193,10 @@ public class ChangeModelPage extends InteractiveCustomUIPage holder = store.getRegistry().newHolder(); diff --git a/src/com/hypixel/hytale/builtin/mounts/BlockMountAPI.java b/src/com/hypixel/hytale/builtin/mounts/BlockMountAPI.java index 6677b984..5310d5e4 100644 --- a/src/com/hypixel/hytale/builtin/mounts/BlockMountAPI.java +++ b/src/com/hypixel/hytale/builtin/mounts/BlockMountAPI.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.BlockMountType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.mountpoints.BlockMountPoint; @@ -18,6 +17,8 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public final class BlockMountAPI { private BlockMountAPI() { @@ -25,7 +26,7 @@ public final class BlockMountAPI { @Nonnull public static BlockMountAPI.BlockMountResult mountOnBlock( - @Nonnull Ref entity, @Nonnull CommandBuffer commandBuffer, @Nonnull Vector3i targetBlock, @Nonnull Vector3f interactPos + @Nonnull Ref entity, @Nonnull CommandBuffer commandBuffer, @Nonnull Vector3i targetBlock, @Nonnull Vector3d interactPos ) { MountedComponent existingMounted = commandBuffer.getComponent(entity, MountedComponent.getComponentType()); if (existingMounted != null) { @@ -88,13 +89,13 @@ public final class BlockMountAPI { } else { TransformComponent transformComponent = commandBuffer.getComponent(entity, TransformComponent.getComponentType()); if (transformComponent != null) { - Vector3f position = pickedMountPoint.computeWorldSpacePosition(blockMountComponent.getBlockPos()); - Vector3f rotationEuler = pickedMountPoint.computeRotationEuler(blockMountComponent.getExpectedRotation()); - transformComponent.setPosition(position.toVector3d()); + Vector3d position = pickedMountPoint.computeWorldSpacePosition(blockMountComponent.getBlockPos()); + Rotation3f rotationEuler = pickedMountPoint.computeRotationEuler(blockMountComponent.getExpectedRotation()); + transformComponent.setPosition(position); transformComponent.setRotation(rotationEuler); } - MountedComponent mountedComponent = new MountedComponent(blockRef, new Vector3f(0.0F, 0.0F, 0.0F), blockMountType); + MountedComponent mountedComponent = new MountedComponent(blockRef, new Rotation3f(0.0F, 0.0F, 0.0F), blockMountType); commandBuffer.addComponent(entity, MountedComponent.getComponentType(), mountedComponent); blockMountComponent.putSeatedEntity(pickedMountPoint, entity); return new BlockMountAPI.Mounted(blockType, mountedComponent); diff --git a/src/com/hypixel/hytale/builtin/mounts/BlockMountComponent.java b/src/com/hypixel/hytale/builtin/mounts/BlockMountComponent.java index a8175b24..a4f681d4 100644 --- a/src/com/hypixel/hytale/builtin/mounts/BlockMountComponent.java +++ b/src/com/hypixel/hytale/builtin/mounts/BlockMountComponent.java @@ -3,18 +3,19 @@ package com.hypixel.hytale.builtin.mounts; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockMountType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.mountpoints.BlockMountPoint; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import java.util.Collection; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class BlockMountComponent implements Component { private BlockMountType type; @@ -22,9 +23,9 @@ public class BlockMountComponent implements Component { private BlockType expectedBlockType; private int expectedRotation; @Nonnull - private Map> entitiesByMountPoint = new Object2ObjectOpenHashMap<>(); + private Map> entitiesByMountPoint = new Object2ReferenceOpenHashMap<>(); @Nonnull - private Map, BlockMountPoint> mountPointByEntity = new Object2ObjectOpenHashMap<>(); + private Map, BlockMountPoint> mountPointByEntity = new Reference2ObjectOpenHashMap<>(); public static ComponentType getComponentType() { return MountPlugin.getInstance().getBlockMountComponentType(); @@ -89,15 +90,15 @@ public class BlockMountComponent implements Component { } @Nullable - public BlockMountPoint findAvailableSeat(@Nonnull Vector3i targetBlock, @Nonnull BlockMountPoint[] choices, @Nonnull Vector3f whereWasClicked) { + public BlockMountPoint findAvailableSeat(@Nonnull Vector3i targetBlock, @Nonnull BlockMountPoint[] choices, @Nonnull Vector3d whereWasClicked) { this.clean(); double minDistSq = Double.MAX_VALUE; BlockMountPoint closestSeat = null; for (BlockMountPoint choice : choices) { if (!this.entitiesByMountPoint.containsKey(choice)) { - Vector3f seatInWorldSpace = choice.computeWorldSpacePosition(targetBlock); - double distSq = whereWasClicked.distanceSquaredTo(seatInWorldSpace); + Vector3d seatInWorldSpace = choice.computeWorldSpacePosition(targetBlock); + double distSq = whereWasClicked.distanceSquared(seatInWorldSpace); if (distSq < minDistSq) { minDistSq = distSq; closestSeat = choice; @@ -115,8 +116,8 @@ public class BlockMountComponent implements Component { seat.type = this.type; seat.blockPos = this.blockPos; seat.expectedBlockType = this.expectedBlockType; - seat.entitiesByMountPoint = new Object2ObjectOpenHashMap<>(this.entitiesByMountPoint); - seat.mountPointByEntity = new Object2ObjectOpenHashMap<>(this.mountPointByEntity); + seat.entitiesByMountPoint = new Object2ReferenceOpenHashMap<>(this.entitiesByMountPoint); + seat.mountPointByEntity = new Reference2ObjectOpenHashMap<>(this.mountPointByEntity); return seat; } } diff --git a/src/com/hypixel/hytale/builtin/mounts/MountPlugin.java b/src/com/hypixel/hytale/builtin/mounts/MountPlugin.java index 61fafe88..43b4d7cb 100644 --- a/src/com/hypixel/hytale/builtin/mounts/MountPlugin.java +++ b/src/com/hypixel/hytale/builtin/mounts/MountPlugin.java @@ -143,6 +143,8 @@ public class MountPlugin extends JavaPlugin { resetOriginalMountRole(entityReference, store, mountComponent); resetOriginalPlayerMovementSettings(playerRef, store); + } else { + resetOriginalPlayerMovementSettings(playerRef, store); } } diff --git a/src/com/hypixel/hytale/builtin/mounts/MountSystems.java b/src/com/hypixel/hytale/builtin/mounts/MountSystems.java index 0f76d43d..351bffcc 100644 --- a/src/com/hypixel/hytale/builtin/mounts/MountSystems.java +++ b/src/com/hypixel/hytale/builtin/mounts/MountSystems.java @@ -23,7 +23,7 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.protocol.BlockMount; import com.hypixel.hytale.protocol.ComponentUpdateType; @@ -63,6 +63,8 @@ import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class MountSystems { private static void handleMountedRemoval( @@ -211,7 +213,7 @@ public class MountSystems { absolute.apply(commandBuffer, archetypeChunk, index); TransformComponent transform = commandBuffer.getComponent(targetRef, this.transformComponentType); if (transform != null) { - transform.getPosition().assign(absolute.getX(), absolute.getY(), absolute.getZ()); + transform.getPosition().set(absolute.getX(), absolute.getY(), absolute.getZ()); } } else if (inputUpdate instanceof PlayerInput.SetMovementStates sx) { MovementStates states = sx.movementStates(); @@ -223,7 +225,7 @@ public class MountSystems { body.apply(commandBuffer, archetypeChunk, index); TransformComponent transform = commandBuffer.getComponent(targetRef, this.transformComponentType); if (transform != null) { - transform.getRotation().assign(body.direction().pitch, body.direction().yaw, body.direction().roll); + transform.getRotation().set(body.direction().pitch, body.direction().yaw, body.direction().roll); } } else if (inputUpdate instanceof PlayerInput.SetHead head) { head.apply(commandBuffer, archetypeChunk, index); @@ -856,8 +858,8 @@ public class MountSystems { ) { Ref mountedToEntity = component.getMountedToEntity(); Ref mountedToBlock = component.getMountedToBlock(); - Vector3f offset = component.getAttachmentOffset(); - com.hypixel.hytale.protocol.Vector3f netOffset = new com.hypixel.hytale.protocol.Vector3f(offset.x, offset.y, offset.z); + Rotation3f offset = component.getAttachmentOffset(); + Vector3f netOffset = new Vector3f(offset.x, offset.y, offset.z); MountedUpdate mountedUpdate; if (mountedToEntity != null) { NetworkId mountedToNetworkIdComponent = ref.getStore().getComponent(mountedToEntity, NetworkId.getComponentType()); @@ -883,12 +885,12 @@ public class MountSystems { } BlockType blockType = blockMountComponent.getExpectedBlockType(); - Vector3f position = occupiedSeat.computeWorldSpacePosition(blockMountComponent.getBlockPos()); - Vector3f rotationEuler = occupiedSeat.computeRotationEuler(blockMountComponent.getExpectedRotation()); + Vector3d position = occupiedSeat.computeWorldSpacePosition(blockMountComponent.getBlockPos()); + Rotation3f rotationEuler = occupiedSeat.computeRotationEuler(blockMountComponent.getExpectedRotation()); BlockMount blockMount = new BlockMount( blockMountComponent.getType(), - new com.hypixel.hytale.protocol.Vector3f(position.x, position.y, position.z), - new com.hypixel.hytale.protocol.Vector3f(rotationEuler.x, rotationEuler.y, rotationEuler.z), + new Vector3f((float)position.x, (float)position.y, (float)position.z), + new Vector3f(rotationEuler.x, rotationEuler.y, rotationEuler.z), BlockType.getAssetMap().getIndex(blockType.getId()) ); mountedUpdate = new MountedUpdate(0, netOffset, component.getControllerType(), blockMount); diff --git a/src/com/hypixel/hytale/builtin/mounts/MountedByComponent.java b/src/com/hypixel/hytale/builtin/mounts/MountedByComponent.java index 0f5c6f1c..6be1f86d 100644 --- a/src/com/hypixel/hytale/builtin/mounts/MountedByComponent.java +++ b/src/com/hypixel/hytale/builtin/mounts/MountedByComponent.java @@ -4,13 +4,13 @@ import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import javax.annotation.Nonnull; public class MountedByComponent implements Component { @Nonnull - private final List> passengers = new ObjectArrayList<>(); + private final List> passengers = new ReferenceArrayList<>(); public static ComponentType getComponentType() { return MountPlugin.getInstance().getMountedByComponentType(); diff --git a/src/com/hypixel/hytale/builtin/mounts/MountedComponent.java b/src/com/hypixel/hytale/builtin/mounts/MountedComponent.java index 28598681..ee3a8be1 100644 --- a/src/com/hypixel/hytale/builtin/mounts/MountedComponent.java +++ b/src/com/hypixel/hytale/builtin/mounts/MountedComponent.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.builtin.mounts; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.BlockMountType; import com.hypixel.hytale.protocol.MountController; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; @@ -16,7 +16,7 @@ public class MountedComponent implements Component { private Ref mountedToBlock; private MountController controller; private BlockMountType blockMountType; - private Vector3f attachmentOffset = new Vector3f(0.0F, 0.0F, 0.0F); + private Rotation3f attachmentOffset = new Rotation3f(0.0F, 0.0F, 0.0F); private long mountStartMs; private boolean isNetworkOutdated = true; @@ -24,14 +24,14 @@ public class MountedComponent implements Component { return MountPlugin.getInstance().getMountedComponentType(); } - public MountedComponent(Ref mountedToEntity, Vector3f attachmentOffset, MountController controller) { + public MountedComponent(Ref mountedToEntity, Rotation3f attachmentOffset, MountController controller) { this.mountedToEntity = mountedToEntity; this.attachmentOffset = attachmentOffset; this.controller = controller; this.mountStartMs = System.currentTimeMillis(); } - public MountedComponent(Ref mountedToBlock, Vector3f attachmentOffset, BlockMountType blockMountType) { + public MountedComponent(Ref mountedToBlock, Rotation3f attachmentOffset, BlockMountType blockMountType) { this.mountedToBlock = mountedToBlock; this.attachmentOffset = attachmentOffset; this.controller = MountController.BlockMount; @@ -49,7 +49,7 @@ public class MountedComponent implements Component { return this.mountedToBlock; } - public Vector3f getAttachmentOffset() { + public Rotation3f getAttachmentOffset() { return this.attachmentOffset; } diff --git a/src/com/hypixel/hytale/builtin/mounts/NPCMountSystems.java b/src/com/hypixel/hytale/builtin/mounts/NPCMountSystems.java index 48c6e473..5852496e 100644 --- a/src/com/hypixel/hytale/builtin/mounts/NPCMountSystems.java +++ b/src/com/hypixel/hytale/builtin/mounts/NPCMountSystems.java @@ -154,6 +154,23 @@ public class NPCMountSystems { public void onEntityRemove( @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { + NPCEntity npcEntity = store.getComponent(ref, this.npcEntityComponentType); + + assert npcEntity != null; + + if (!npcEntity.getRole().isRoleChangeRequested()) { + NPCMountComponent mountComponent = store.getComponent(ref, this.mountComponentType); + + assert mountComponent != null; + + PlayerRef playerRef = mountComponent.getOwnerPlayerRef(); + if (playerRef != null) { + Ref playerEntityRef = playerRef.getReference(); + if (playerEntityRef != null && playerEntityRef.isValid()) { + MountPlugin.resetOriginalPlayerMovementSettings(playerEntityRef, store); + } + } + } } } diff --git a/src/com/hypixel/hytale/builtin/mounts/interactions/MountInteraction.java b/src/com/hypixel/hytale/builtin/mounts/interactions/MountInteraction.java index 922bfe8d..f4ac41ed 100644 --- a/src/com/hypixel/hytale/builtin/mounts/interactions/MountInteraction.java +++ b/src/com/hypixel/hytale/builtin/mounts/interactions/MountInteraction.java @@ -8,24 +8,25 @@ import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.MountController; -import com.hypixel.hytale.protocol.Vector3f; -import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3f; public class MountInteraction extends SimpleInstantInteraction { public static final BuilderCodec CODEC = BuilderCodec.builder( MountInteraction.class, MountInteraction::new, SimpleInstantInteraction.CODEC ) .appendInherited( - new KeyedCodec<>("AttachmentOffset", ProtocolCodecs.VECTOR3F), - (o, v) -> o.attachmentOffset.assign(v.x, v.y, v.z), + new KeyedCodec<>("AttachmentOffset", Vector3fUtil.CODEC), + (o, v) -> o.attachmentOffset.set(v.x, v.y, v.z), o -> new Vector3f(o.attachmentOffset.x, o.attachmentOffset.y, o.attachmentOffset.z), (o, p) -> o.attachmentOffset = p.attachmentOffset ) @@ -39,7 +40,7 @@ public class MountInteraction extends SimpleInstantInteraction { .addValidator(Validators.nonNull()) .add() .build(); - private com.hypixel.hytale.math.vector.Vector3f attachmentOffset = new com.hypixel.hytale.math.vector.Vector3f(0.0F, 0.0F, 0.0F); + private Rotation3f attachmentOffset = new Rotation3f(0.0F, 0.0F, 0.0F); private MountController controller; @Override diff --git a/src/com/hypixel/hytale/builtin/mounts/interactions/SeatingInteraction.java b/src/com/hypixel/hytale/builtin/mounts/interactions/SeatingInteraction.java index 8e13fd76..be46c55c 100644 --- a/src/com/hypixel/hytale/builtin/mounts/interactions/SeatingInteraction.java +++ b/src/com/hypixel/hytale/builtin/mounts/interactions/SeatingInteraction.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.builtin.mounts.BlockMountAPI; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.BlockSoundEvent; import com.hypixel.hytale.protocol.InteractionType; @@ -14,15 +13,17 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocksound.config.BlockSoundSet; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class SeatingInteraction extends SimpleBlockInteraction { @Nonnull @@ -43,10 +44,10 @@ public class SeatingInteraction extends SimpleBlockInteraction { @Nonnull CooldownHandler cooldownHandler ) { Ref ref = context.getEntity(); - Player player = commandBuffer.getComponent(ref, Player.getComponentType()); - if (player != null) { + PlayerRef playerRef = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + if (playerRef != null) { BlockPosition rawTarget = context.getMetaStore().getMetaObject(TARGET_BLOCK_RAW); - Vector3f whereWasHit = new Vector3f(rawTarget.x + 0.5F, rawTarget.y + 0.5F, rawTarget.z + 0.5F); + Vector3d whereWasHit = new Vector3d(rawTarget.x + 0.5, rawTarget.y + 0.5, rawTarget.z + 0.5); BlockMountAPI.BlockMountResult result = BlockMountAPI.mountOnBlock(ref, commandBuffer, targetBlock, whereWasHit); if (result == BlockMountAPI.DidNotMount.ALREADY_MOUNTED) { int soundEventIndex = SoundEvent.getAssetMap().getIndex("SFX_Creative_Play_Add_Mask"); @@ -56,10 +57,10 @@ public class SeatingInteraction extends SimpleBlockInteraction { String seatSoundId = soundSet == null ? null : soundSet.getSoundEventIds().getOrDefault(BlockSoundEvent.Walk, null); if (seatSoundId != null) { int soundEventIndex = SoundEvent.getAssetMap().getIndex(seatSoundId); - SoundUtil.playSoundEvent3dToPlayer(ref, soundEventIndex, SoundCategory.SFX, targetBlock.toVector3d(), commandBuffer); + SoundUtil.playSoundEvent3dToPlayer(ref, soundEventIndex, SoundCategory.SFX, Vector3iUtil.toVector3d(targetBlock), commandBuffer); } } else { - player.sendMessage(Message.translation("server.interactions.didNotMount").param("state", result.toString())); + playerRef.sendMessage(Message.translation("server.interactions.didNotMount").param("state", result.toString())); } } } diff --git a/src/com/hypixel/hytale/builtin/mounts/interactions/SpawnMinecartInteraction.java b/src/com/hypixel/hytale/builtin/mounts/interactions/SpawnMinecartInteraction.java index ac80335d..ebdab540 100644 --- a/src/com/hypixel/hytale/builtin/mounts/interactions/SpawnMinecartInteraction.java +++ b/src/com/hypixel/hytale/builtin/mounts/interactions/SpawnMinecartInteraction.java @@ -10,9 +10,9 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.util.TrigMathUtil; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.RailConfig; import com.hypixel.hytale.protocol.RailPoint; @@ -40,6 +40,9 @@ import java.util.Collections; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3fc; +import org.joml.Vector3i; public class SpawnMinecartInteraction extends SimpleBlockInteraction { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -73,12 +76,12 @@ public class SpawnMinecartInteraction extends SimpleBlockInteraction { ) { Ref ref = context.getEntity(); Holder holder = EntityStore.REGISTRY.newHolder(); - Vector3d targetPosition = targetBlock.toVector3d(); + Vector3d targetPosition = Vector3iUtil.toVector3d(targetBlock); targetPosition.add(0.5, 0.5, 0.5); - Vector3f rotation = new Vector3f(); + Rotation3f rotation = new Rotation3f(); HeadRotation headRotation = commandBuffer.getComponent(ref, HeadRotation.getComponentType()); if (headRotation != null) { - rotation.setYaw(headRotation.getRotation().getYaw()); + rotation.setYaw(headRotation.getRotation().yaw()); } WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z)); @@ -87,7 +90,7 @@ public class SpawnMinecartInteraction extends SimpleBlockInteraction { int blockRotation = chunk.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z); RailConfig railConfig = block.getRailConfig(blockRotation); if (railConfig != null) { - alignToRail(targetBlock, targetPosition, rotation, rotation.getYaw(), railConfig); + alignToRail(targetBlock, targetPosition, rotation, rotation.yaw(), railConfig); } else { BlockBoundingBoxes.RotatedVariantBoxes bounding = BlockBoundingBoxes.getAssetMap().getAsset(block.getHitboxTypeIndex()).get(blockRotation); targetPosition.add(0.0, bounding.getBoundingBox().max.y - 0.5, 0.0); @@ -119,46 +122,64 @@ public class SpawnMinecartInteraction extends SimpleBlockInteraction { ) { } - private static void alignToRail(@Nonnull Vector3i targetBlock, @Nonnull Vector3d target, @Nonnull Vector3f rotation, float yaw, @Nonnull RailConfig config) { + private static void alignToRail(@Nonnull Vector3i targetBlock, @Nonnull Vector3d target, @Nonnull Rotation3f rotation, float yaw, @Nonnull RailConfig config) { RailPoint[] points = config.points; double smallestDistance = Double.MAX_VALUE; double ox = target.x; double oy = target.y; double oz = target.z; - Vector3d facingDir = new Vector3d(); - facingDir.assign(yaw, 0.0); + double fx = -TrigMathUtil.sin(yaw); + double fz = -TrigMathUtil.cos(yaw); for (int index = 0; index < points.length - 1; index++) { - RailPoint p = points[index]; - RailPoint p2 = points[index + 1]; - Vector3d point = new Vector3d(targetBlock.x + p.point.x, targetBlock.y + p.point.y, targetBlock.z + p.point.z); - Vector3d point2 = new Vector3d(targetBlock.x + p2.point.x, targetBlock.y + p2.point.y, targetBlock.z + p2.point.z); - Vector3d dir = point2.clone().subtract(point); - double maxLength = dir.length(); - dir.normalize(); - Vector3d toPoint = target.clone().subtract(point); - double distance = dir.dot(toPoint); - Vector3d pointOnLine = point.clone(); - pointOnLine.addScaled(dir, Math.min(maxLength, Math.max(0.0, distance))); - double pointDist = pointOnLine.distanceSquaredTo(target); + Vector3fc p = points[index].point; + Vector3fc p2 = points[index + 1].point; + double p1x = targetBlock.x + p.x(); + double p1y = targetBlock.y + p.y(); + double p1z = targetBlock.z + p.z(); + double p2x = targetBlock.x + p2.x(); + double p2y = targetBlock.y + p2.y(); + double p2z = targetBlock.z + p2.z(); + double dx = p2x - p1x; + double dy = p2y - p1y; + double dz = p2z - p1z; + double maxLength = Math.sqrt(dx * dx + dy * dy + dz * dz); + double invLen = 1.0 / maxLength; + dx *= invLen; + dy *= invLen; + dz *= invLen; + double tx = target.x - p1x; + double ty = target.y - p1y; + double tz = target.z - p1z; + double distance = dx * tx + dy * ty + dz * tz; + double t = Math.min(maxLength, Math.max(0.0, distance)); + double clx = p1x + dx * t; + double cly = p1y + dy * t; + double clz = p1z + dz * t; + double ex = clx - target.x; + double ey = cly - target.y; + double ez = clz - target.z; + double pointDist = ex * ex + ey * ey + ez * ez; if (pointDist >= 0.0 && pointDist <= 0.8F && pointDist < smallestDistance) { - ox = pointOnLine.x; - oy = pointOnLine.y; - oz = pointOnLine.z; + ox = clx; + oy = cly; + oz = clz; smallestDistance = pointDist; - if (facingDir.dot(dir) < 0.0) { - dir.scale(-1.0); + if (fx * dx + fz * dz < 0.0) { + dx = -dx; + dy = -dy; + dz = -dz; } - float newYaw = (float)(Math.atan2(dir.x, dir.z) + Math.PI); - float newPitch = (float)Math.asin(dir.y); + float newYaw = (float)(Math.atan2(dx, dz) + Math.PI); + float newPitch = (float)Math.asin(dy); rotation.setYaw(newYaw); rotation.setPitch(newPitch); } } if (!(smallestDistance >= Double.MAX_VALUE)) { - target.assign(ox, oy, oz); + target.set(ox, oy, oz); } } } diff --git a/src/com/hypixel/hytale/builtin/mounts/npc/ActionMount.java b/src/com/hypixel/hytale/builtin/mounts/npc/ActionMount.java index cb05d049..9ce5e624 100644 --- a/src/com/hypixel/hytale/builtin/mounts/npc/ActionMount.java +++ b/src/com/hypixel/hytale/builtin/mounts/npc/ActionMount.java @@ -19,6 +19,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.systems.RoleChangeSystem; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionMount extends ActionBase { public static final String EMPTY_ROLE_ID = "Empty_Role"; @@ -38,14 +39,14 @@ public class ActionMount extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { Ref target = role.getStateSupport().getInteractionIterationTarget(); boolean targetExists = target != null && !store.getArchetype(target).contains(DeathComponent.getComponentType()); return super.canExecute(ref, role, sensorInfo, dt, store) && targetExists; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); ComponentType mountComponentType = NPCMountComponent.getComponentType(); NPCMountComponent mountComponent = store.getComponent(ref, mountComponentType); diff --git a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/CombatActionEvaluatorSystems.java b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/CombatActionEvaluatorSystems.java index c96ad1f4..de60e3bb 100644 --- a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/CombatActionEvaluatorSystems.java +++ b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/CombatActionEvaluatorSystems.java @@ -25,7 +25,6 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -48,6 +47,7 @@ import java.util.Set; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CombatActionEvaluatorSystems { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/ActionCombatAbility.java b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/ActionCombatAbility.java index 3b389b9c..7c49eee7 100644 --- a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/ActionCombatAbility.java +++ b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/ActionCombatAbility.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.builtin.npccombatactionevaluator.evaluator.CombatActio import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; @@ -31,6 +30,7 @@ import com.hypixel.hytale.server.npc.util.AimingData; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ActionCombatAbility extends ActionBase { protected static final ComponentType COMPONENT_TYPE = CombatActionEvaluator.getComponentType(); @@ -117,8 +117,8 @@ public class ActionCombatAbility extends ActionBase { assert headRotationComponent != null; - Vector3f rotation = aimingData != null && aimingData.getChargeDistance() > 0.0 ? transformComponent.getRotation() : headRotationComponent.getRotation(); - if (aimingData != null && !aimingData.isOnTarget(rotation.getYaw(), rotation.getPitch(), (float) (Math.PI / 12))) { + Rotation3f rotation = aimingData != null && aimingData.getChargeDistance() > 0.0 ? transformComponent.getRotation() : headRotationComponent.getRotation(); + if (aimingData != null && !aimingData.isOnTarget(rotation.yaw(), rotation.pitch(), (float) (Math.PI / 12))) { aimingData.clearSolution(); return false; } else { @@ -140,8 +140,8 @@ public class ActionCombatAbility extends ActionBase { assert targetTransformComponent != null; Vector3d targetPosition = targetTransformComponent.getPosition(); - float selfYaw = NPCPhysicsMath.lookatHeading(transformComponent.getPosition(), targetPosition, transformComponent.getRotation().getYaw()); - float difference = PhysicsMath.normalizeTurnAngle(targetTransformComponent.getRotation().getYaw() - selfYaw - (float)positioningAngle); + float selfYaw = NPCPhysicsMath.lookatHeading(transformComponent.getPosition(), targetPosition, transformComponent.getRotation().yaw()); + float difference = PhysicsMath.normalizeTurnAngle(targetTransformComponent.getRotation().yaw() - selfYaw - (float)positioningAngle); if (Math.abs(difference) > 0.08726646F) { return false; } diff --git a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/CombatTargetCollector.java b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/CombatTargetCollector.java index 8ec1d152..afd67e27 100644 --- a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/CombatTargetCollector.java +++ b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/CombatTargetCollector.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.attitude.Attitude; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -16,6 +15,7 @@ import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CombatTargetCollector implements ISensorEntityCollector { @Nonnull @@ -66,7 +66,7 @@ public class CombatTargetCollector implements ISensorEntityCollector { assert targetTransformComponent != null; Vector3d targetPos = targetTransformComponent.getPosition(); - double distanceSquared = selfPos.distanceSquaredTo(targetPos); + double distanceSquared = selfPos.distanceSquared(targetPos); if (distanceSquared < this.closestHostileDistanceSquared) { this.targetMemory.setClosestHostile(targetRef); this.closestHostileDistanceSquared = distanceSquared; diff --git a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/SensorCombatActionEvaluator.java b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/SensorCombatActionEvaluator.java index be66a365..74b8ef84 100644 --- a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/SensorCombatActionEvaluator.java +++ b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/corecomponents/SensorCombatActionEvaluator.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.parameterproviders.MultipleParam import com.hypixel.hytale.server.npc.sensorinfo.parameterproviders.SingleDoubleParameterProvider; import com.hypixel.hytale.server.npc.valuestore.ValueStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorCombatActionEvaluator extends SensorBase { @Nonnull @@ -92,7 +92,7 @@ public class SensorCombatActionEvaluator extends SensorBase { this.positioningAngleParameterProvider.overrideDouble(positioningAngle); Vector3d selfPosition = store.getComponent(ref, TRANSFORM_COMPONENT_TYPE).getPosition(); Vector3d targetPosition = store.getComponent(target, TRANSFORM_COMPONENT_TYPE).getPosition(); - double distance = targetPosition.distanceTo(selfPosition); + double distance = targetPosition.distance(selfPosition); return this.targetInRange == distance <= maxRange + this.allowableDeviation; } } diff --git a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/CombatActionEvaluator.java b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/CombatActionEvaluator.java index 28cc7556..e6deec57 100644 --- a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/CombatActionEvaluator.java +++ b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/CombatActionEvaluator.java @@ -29,6 +29,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Map.Entry; import java.util.function.Function; import javax.annotation.Nonnull; @@ -471,10 +472,12 @@ public class CombatActionEvaluator extends Evaluator impleme } public abstract class CombatOptionHolder extends Evaluator.OptionHolder { - protected long lastUsedNanos = Evaluator.NOT_USED; + protected long lastUsedNanos; protected CombatOptionHolder(CombatActionOption option) { + Objects.requireNonNull(CombatActionEvaluator.this); super(option); + this.lastUsedNanos = Evaluator.NOT_USED; } public void setLastUsedNanos(long lastUsedNanos) { @@ -490,12 +493,14 @@ public class CombatActionEvaluator extends Evaluator impleme public class MultipleTargetCombatOptionHolder extends CombatActionEvaluator.CombatOptionHolder { protected List> targets; @Nonnull - protected final DoubleList targetUtilities = new DoubleArrayList(); + protected final DoubleList targetUtilities; @Nullable protected Ref pickedTarget; protected MultipleTargetCombatOptionHolder(CombatActionOption option) { + Objects.requireNonNull(CombatActionEvaluator.this); super(option); + this.targetUtilities = new DoubleArrayList(); } @Override @@ -570,6 +575,7 @@ public class CombatActionEvaluator extends Evaluator impleme public class SelfCombatOptionHolder extends CombatActionEvaluator.CombatOptionHolder { protected SelfCombatOptionHolder(CombatActionOption option) { + Objects.requireNonNull(CombatActionEvaluator.this); super(option); } diff --git a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/combatactions/AbilityCombatAction.java b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/combatactions/AbilityCombatAction.java index ea2ee20e..e16f203b 100644 --- a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/combatactions/AbilityCombatAction.java +++ b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/combatactions/AbilityCombatAction.java @@ -14,7 +14,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; @@ -35,6 +34,7 @@ import java.util.Collections; import java.util.Map; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class AbilityCombatAction extends CombatActionOption { @Nonnull @@ -241,8 +241,9 @@ public class AbilityCombatAction extends CombatActionOption { ctx.log("%s: Executing option %s", archetypeChunk.getReferenceTo(index), this.getId()); } - InventoryHelper.setHotbarSlot(npcComponent.getInventory(), (byte)this.weaponSlot); - InventoryHelper.setOffHandSlot(npcComponent.getInventory(), (byte)this.offhandSlot); + Ref ref = archetypeChunk.getReferenceTo(index); + InventoryHelper.setHotbarSlot(ref, npcComponent.getInventory(), (byte)this.weaponSlot, commandBuffer); + InventoryHelper.setOffHandSlot(ref, npcComponent.getInventory(), (byte)this.offhandSlot, commandBuffer); if (this.subState != null) { role.getStateSupport().setSubState(this.subState); ctx = CombatActionEvaluator.LOGGER.at(Level.FINEST); @@ -252,7 +253,6 @@ public class AbilityCombatAction extends CombatActionOption { } if (this.actionTarget == CombatActionOption.Target.Self) { - Ref ref = archetypeChunk.getReferenceTo(index); RootInteraction interaction = RootInteraction.getAssetMap().getAsset(this.ability); if (interaction == null) { throw new IllegalStateException("No such interaction: " + this.ability); @@ -331,7 +331,7 @@ public class AbilityCombatAction extends CombatActionOption { assert selfTransformComponent != null; Vector3d selfPos = selfTransformComponent.getPosition(); - double distance = selfPos.distanceSquaredTo(targetPos); + double distance = selfPos.distanceSquared(targetPos); return distance > this.maxRangeSquared; } else { return true; diff --git a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/combatactions/BasicAttackTargetCombatAction.java b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/combatactions/BasicAttackTargetCombatAction.java index d723cf55..0bbaf7e6 100644 --- a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/combatactions/BasicAttackTargetCombatAction.java +++ b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/evaluator/combatactions/BasicAttackTargetCombatAction.java @@ -7,7 +7,9 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.decisionmaker.core.Option; import com.hypixel.hytale.server.npc.entities.NPCEntity; @@ -63,8 +65,10 @@ public class BasicAttackTargetCombatAction extends CombatActionOption { ctx.log("%s: Executing option %s", archetypeChunk.getReferenceTo(index), this.getId()); } - InventoryHelper.setHotbarSlot(npcComponent.getInventory(), (byte)this.weaponSlot); - InventoryHelper.setOffHandSlot(npcComponent.getInventory(), (byte)this.offhandSlot); + Ref ref = archetypeChunk.getReferenceTo(index); + Inventory inventory = npcComponent.getInventory(); + InventoryHelper.setHotbarSlot(ref, inventory, (byte)this.weaponSlot, commandBuffer); + InventoryHelper.setOffHandSlot(ref, inventory, (byte)this.offhandSlot, commandBuffer); CombatActionEvaluatorConfig.BasicAttacks basicAttacks = evaluator.getCurrentBasicAttackSet(); if (basicAttacks != null) { double range = basicAttacks.getMaxRange() - 0.1; diff --git a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/memory/TargetMemory.java b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/memory/TargetMemory.java index 15eeced1..a9db4544 100644 --- a/src/com/hypixel/hytale/builtin/npccombatactionevaluator/memory/TargetMemory.java +++ b/src/com/hypixel/hytale/builtin/npccombatactionevaluator/memory/TargetMemory.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -15,11 +15,11 @@ public class TargetMemory implements Component { @Nonnull private final Int2FloatOpenHashMap knownFriendlies = new Int2FloatOpenHashMap(); @Nonnull - private final List> knownFriendliesList = new ObjectArrayList<>(); + private final List> knownFriendliesList = new ReferenceArrayList<>(); @Nonnull private final Int2FloatOpenHashMap knownHostiles = new Int2FloatOpenHashMap(); @Nonnull - private final List> knownHostilesList = new ObjectArrayList<>(); + private final List> knownHostilesList = new ReferenceArrayList<>(); private final float rememberFor; @Nullable private Ref closestHostile; diff --git a/src/com/hypixel/hytale/builtin/npceditor/NPCEditorPlugin.java b/src/com/hypixel/hytale/builtin/npceditor/NPCEditorPlugin.java index 03db9d12..12f9b393 100644 --- a/src/com/hypixel/hytale/builtin/npceditor/NPCEditorPlugin.java +++ b/src/com/hypixel/hytale/builtin/npceditor/NPCEditorPlugin.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.builtin.npceditor; import com.hypixel.hytale.builtin.asseteditor.AssetEditorPlugin; import com.hypixel.hytale.builtin.asseteditor.event.AssetEditorSelectAssetEvent; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorPreviewCameraSettings; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorUpdateModelPreview; import com.hypixel.hytale.server.core.Message; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.spawning.ISpawnableWithModel; import com.hypixel.hytale.server.spawning.SpawningContext; import javax.annotation.Nonnull; +import org.joml.Vector3f; public class NPCEditorPlugin extends JavaPlugin { private static final AssetEditorPreviewCameraSettings DEFAULT_PREVIEW_CAMERA_SETTINGS = new AssetEditorPreviewCameraSettings( diff --git a/src/com/hypixel/hytale/builtin/parkour/ParkourCheckpointSystems.java b/src/com/hypixel/hytale/builtin/parkour/ParkourCheckpointSystems.java index 7e37f770..57b43ca0 100644 --- a/src/com/hypixel/hytale/builtin/parkour/ParkourCheckpointSystems.java +++ b/src/com/hypixel/hytale/builtin/parkour/ParkourCheckpointSystems.java @@ -18,21 +18,21 @@ import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.UUIDComponent; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.system.PlayerSpatialSystem; import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2LongMap; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ParkourCheckpointSystems { public static class EnsureNetworkSendable extends HolderSystem { @@ -92,7 +92,7 @@ public class ParkourCheckpointSystems { public static class Ticking extends EntityTickingSystem { private final ComponentType parkourCheckpointComponentType; - private final ComponentType playerComponentType; + private final ComponentType playerRefComponentType; private final ResourceType, EntityStore>> playerSpatialComponent; private final ComponentType transformComponentType; @Nonnull @@ -104,11 +104,11 @@ public class ParkourCheckpointSystems { public Ticking( ComponentType parkourCheckpointComponentType, - ComponentType playerComponentType, + ComponentType playerRefComponentType, ResourceType, EntityStore>> playerSpatialComponent ) { this.parkourCheckpointComponentType = parkourCheckpointComponentType; - this.playerComponentType = playerComponentType; + this.playerRefComponentType = playerRefComponentType; this.playerSpatialComponent = playerSpatialComponent; this.transformComponentType = TransformComponent.getComponentType(); this.uuidComponentType = UUIDComponent.getComponentType(); @@ -140,7 +140,7 @@ public class ParkourCheckpointSystems { if (lastIndex != 0) { int parkourCheckpointIndex = archetypeChunk.getComponent(index, this.parkourCheckpointComponentType).getIndex(); SpatialResource, EntityStore> spatialResource = store.getResource(this.playerSpatialComponent); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); Vector3d position = archetypeChunk.getComponent(index, this.transformComponentType).getPosition(); spatialResource.getSpatialStructure().ordered(position, 1.0, results); ParkourPlugin parkourPlugin = ParkourPlugin.get(); @@ -151,8 +151,8 @@ public class ParkourCheckpointSystems { Ref otherReference = results.get(i); UUIDComponent uuidComponent = commandBuffer.getComponent(otherReference, this.uuidComponentType); UUID playerUuid = uuidComponent.getUuid(); - Player player = commandBuffer.getComponent(otherReference, this.playerComponentType); - handleCheckpointUpdate(currentCheckpointByPlayerMap, startTimeByPlayerMap, player, playerUuid, parkourCheckpointIndex, lastIndex); + PlayerRef playerRef = commandBuffer.getComponent(otherReference, this.playerRefComponentType); + handleCheckpointUpdate(currentCheckpointByPlayerMap, startTimeByPlayerMap, playerRef, playerUuid, parkourCheckpointIndex, lastIndex); } } } @@ -160,7 +160,7 @@ public class ParkourCheckpointSystems { private static void handleCheckpointUpdate( @Nonnull Object2IntMap currentCheckpointByPlayerMap, @Nonnull Object2LongMap startTimeByPlayerMap, - @Nonnull Player player, + @Nonnull PlayerRef playerRef, UUID playerUuid, int checkpointIndex, int lastIndex @@ -173,7 +173,7 @@ public class ParkourCheckpointSystems { currentCheckpointByPlayerMap.put(playerUuid, 0); startTimeByPlayerMap.put(playerUuid, System.nanoTime()); - player.sendMessage(Message.translation("server.general.parkourRun.started")); + playerRef.sendMessage(Message.translation("server.general.parkourRun.started")); } else { if (currentCheckpoint + 1 != checkpointIndex) { return; @@ -182,13 +182,13 @@ public class ParkourCheckpointSystems { if (lastIndex == checkpointIndex) { long completionTimeNano = System.nanoTime() - startTimeByPlayerMap.getLong(playerUuid); long completionTimeMillis = TimeUnit.NANOSECONDS.toMillis(completionTimeNano); - player.sendMessage(Message.translation("server.general.parkourRun.completed").param("seconds", completionTimeMillis / 1000.0)); + playerRef.sendMessage(Message.translation("server.general.parkourRun.completed").param("seconds", completionTimeMillis / 1000.0)); currentCheckpointByPlayerMap.remove(playerUuid, currentCheckpoint); return; } currentCheckpointByPlayerMap.put(playerUuid, checkpointIndex); - player.sendMessage( + playerRef.sendMessage( Message.translation("server.general.parkourRun.checkpointReached").param("checkpoint", checkpointIndex).param("checkpoints", lastIndex) ); } diff --git a/src/com/hypixel/hytale/builtin/parkour/ParkourPlugin.java b/src/com/hypixel/hytale/builtin/parkour/ParkourPlugin.java index b15caa1b..26088010 100644 --- a/src/com/hypixel/hytale/builtin/parkour/ParkourPlugin.java +++ b/src/com/hypixel/hytale/builtin/parkour/ParkourPlugin.java @@ -6,10 +6,10 @@ import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -69,12 +69,11 @@ public class ParkourPlugin extends JavaPlugin { this.parkourCheckpointComponentType = this.getEntityStoreRegistry() .registerComponent(ParkourCheckpoint.class, "ParkourCheckpoint", ParkourCheckpoint.CODEC); EntityModule entityModule = EntityModule.get(); - ComponentType playerComponentType = entityModule.getPlayerComponentType(); ResourceType, EntityStore>> playerSpatialComponent = entityModule.getPlayerSpatialResourceType(); this.getEntityStoreRegistry().registerSystem(new ParkourCheckpointSystems.EnsureNetworkSendable()); this.getEntityStoreRegistry().registerSystem(new ParkourCheckpointSystems.Init(this.parkourCheckpointComponentType)); this.getEntityStoreRegistry() - .registerSystem(new ParkourCheckpointSystems.Ticking(this.parkourCheckpointComponentType, playerComponentType, playerSpatialComponent)); + .registerSystem(new ParkourCheckpointSystems.Ticking(this.parkourCheckpointComponentType, PlayerRef.getComponentType(), playerSpatialComponent)); this.getCommandRegistry().registerCommand(new ParkourCommand()); } diff --git a/src/com/hypixel/hytale/builtin/parkour/commands/CheckpointAddCommand.java b/src/com/hypixel/hytale/builtin/parkour/commands/CheckpointAddCommand.java index 91c93a65..b3ef34dd 100644 --- a/src/com/hypixel/hytale/builtin/parkour/commands/CheckpointAddCommand.java +++ b/src/com/hypixel/hytale/builtin/parkour/commands/CheckpointAddCommand.java @@ -6,8 +6,7 @@ import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -27,6 +26,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CheckpointAddCommand extends AbstractPlayerCommand { @Nonnull @@ -54,7 +54,7 @@ public class CheckpointAddCommand extends AbstractPlayerCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - Vector3f rotation = transformComponent.getRotation(); + Rotation3f rotation = transformComponent.getRotation(); Holder holder = EntityStore.REGISTRY.newHolder(); holder.addComponent(ParkourCheckpoint.getComponentType(), new ParkourCheckpoint(index)); Model model = ParkourPlugin.get().getParkourCheckpointModel(); diff --git a/src/com/hypixel/hytale/builtin/path/PathSpatialSystem.java b/src/com/hypixel/hytale/builtin/path/PathSpatialSystem.java index 8e6f1993..7e4c8822 100644 --- a/src/com/hypixel/hytale/builtin/path/PathSpatialSystem.java +++ b/src/com/hypixel/hytale/builtin/path/PathSpatialSystem.java @@ -9,10 +9,10 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PathSpatialSystem extends SpatialSystem { private static final Archetype QUERY = Archetype.of(PatrolPathMarkerEntity.getComponentType(), TransformComponent.getComponentType()); diff --git a/src/com/hypixel/hytale/builtin/path/PrefabPathCollection.java b/src/com/hypixel/hytale/builtin/path/PrefabPathCollection.java index 35730c9c..6bc9f543 100644 --- a/src/com/hypixel/hytale/builtin/path/PrefabPathCollection.java +++ b/src/com/hypixel/hytale/builtin/path/PrefabPathCollection.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.assetstore.AssetRegistry; import com.hypixel.hytale.builtin.path.path.IPrefabPath; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -19,6 +18,8 @@ import java.util.function.BiConsumer; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class PrefabPathCollection { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @@ -64,7 +65,7 @@ public class PrefabPathCollection { for (IPrefabPath path : this.paths.values()) { if (disallowedPaths == null || !disallowedPaths.contains(path.getId())) { - double dist2 = position.distanceSquaredTo(path.getNearestWaypointPosition(position, componentAccessor)); + double dist2 = position.distanceSquared(path.getNearestWaypointPosition(position, componentAccessor)); if (dist2 < minDist2) { nearest = path; minDist2 = dist2; @@ -134,8 +135,8 @@ public class PrefabPathCollection { for (int i = 0; i < this.paths.size(); i++) { IPrefabPath path = this.paths.get(i); if (disallowedPaths == null || !disallowedPaths.contains(path.getId())) { - Vector3d nearestWp = path.getNearestWaypointPosition(position, componentAccessor); - double dist2 = position.distanceSquaredTo(nearestWp); + Vector3dc nearestWp = path.getNearestWaypointPosition(position, componentAccessor); + double dist2 = position.distanceSquared(nearestWp); if (dist2 < minDist2) { nearest = path; minDist2 = dist2; diff --git a/src/com/hypixel/hytale/builtin/path/PrefabPathSystems.java b/src/com/hypixel/hytale/builtin/path/PrefabPathSystems.java index 143961db..f569c66d 100644 --- a/src/com/hypixel/hytale/builtin/path/PrefabPathSystems.java +++ b/src/com/hypixel/hytale/builtin/path/PrefabPathSystems.java @@ -69,26 +69,30 @@ public class PrefabPathSystems { WorldPathData worldPathData = store.getResource(STORE_WORLD_PATH_DATA_RESOURCE_TYPE); WorldGenId worldGenIdComponent = holder.getComponent(WORLD_GEN_ID_COMPONENT_TYPE); int worldgenId = worldGenIdComponent != null ? worldGenIdComponent.getWorldGenId() : 0; - String pathName = pathMarker.getPathName(); - UUID pathId = pathMarker.getPathId(); - if (pathId == null) { - pathId = UUID.nameUUIDFromBytes((pathName + worldgenId).getBytes(StandardCharsets.UTF_8)); - pathMarker.setPathId(pathId); - int lastIndex = pathName.lastIndexOf(126); - if (lastIndex != -1) { - pathMarker.setPathName(pathName.substring(0, lastIndex)); + if (pathMarker != null) { + String pathName = pathMarker.getPathName(); + if (pathName != null) { + UUID pathId = pathMarker.getPathId(); + if (pathId == null) { + pathId = UUID.nameUUIDFromBytes((pathName + worldgenId).getBytes(StandardCharsets.UTF_8)); + pathMarker.setPathId(pathId); + int lastIndex = pathName.lastIndexOf(126); + if (lastIndex != -1) { + pathMarker.setPathName(pathName.substring(0, lastIndex)); + pathMarker.markNeedsSave(); + LOGGER.at(Level.INFO).log("Migrating path marker from path %s to use new UUID %s", pathName, pathId); + } + } + + IPrefabPath path = worldPathData.getOrConstructPrefabPath(worldgenId, pathId, pathName, PatrolPath::new); + path.addLoadedWaypoint(pathMarker, pathMarker.getTempPathLength(), pathMarker.getOrder(), worldgenId); + pathMarker.setParentPath(path); + holder.putComponent(MODEL_COMPONENT_TYPE, new ModelComponent(PathPlugin.get().getPathMarkerModel())); pathMarker.markNeedsSave(); - LOGGER.at(Level.INFO).log("Migrating path marker from path %s to use new UUID %s", pathName, pathId); + holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType()); + holder.ensureComponent(PrefabCopyableComponent.getComponentType()); } } - - IPrefabPath path = worldPathData.getOrConstructPrefabPath(worldgenId, pathId, pathName, PatrolPath::new); - path.addLoadedWaypoint(pathMarker, pathMarker.getTempPathLength(), pathMarker.getOrder(), worldgenId); - pathMarker.setParentPath(path); - holder.putComponent(MODEL_COMPONENT_TYPE, new ModelComponent(PathPlugin.get().getPathMarkerModel())); - pathMarker.markNeedsSave(); - holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType()); - holder.ensureComponent(PrefabCopyableComponent.getComponentType()); } @Override diff --git a/src/com/hypixel/hytale/builtin/path/WorldPathData.java b/src/com/hypixel/hytale/builtin/path/WorldPathData.java index 3a9d96ca..164fdab4 100644 --- a/src/com/hypixel/hytale/builtin/path/WorldPathData.java +++ b/src/com/hypixel/hytale/builtin/path/WorldPathData.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.builtin.path.path.IPrefabPath; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Resource; import com.hypixel.hytale.component.ResourceType; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -16,6 +15,7 @@ import java.util.Set; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class WorldPathData implements Resource { private final Int2ObjectMap prefabPaths = new Int2ObjectOpenHashMap<>(); diff --git a/src/com/hypixel/hytale/builtin/path/commands/PrefabPathHelper.java b/src/com/hypixel/hytale/builtin/path/commands/PrefabPathHelper.java index b15dc26b..a7a607c2 100644 --- a/src/com/hypixel/hytale/builtin/path/commands/PrefabPathHelper.java +++ b/src/com/hypixel/hytale/builtin/path/commands/PrefabPathHelper.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.builtin.path.PathPlugin; import com.hypixel.hytale.builtin.path.entities.PatrolPathMarkerEntity; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; @@ -16,6 +15,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3d; public final class PrefabPathHelper { private PrefabPathHelper() { @@ -38,15 +38,15 @@ public final class PrefabPathHelper { assert transformComponent != null; - Vector3d playerPosition = transformComponent.getPosition().clone(); - Vector3f playerBodyRotation = transformComponent.getRotation().clone(); + Vector3d playerPosition = new Vector3d(transformComponent.getPosition()); + Rotation3f playerBodyRotation = new Rotation3f(transformComponent.getRotation()); PatrolPathMarkerEntity waypointEntity = world.spawnEntity(waypoint, playerPosition, playerBodyRotation); if (waypointEntity != null) { Ref waypointRef = waypointEntity.getReference(); if (waypointRef != null && waypointRef.isValid()) { TransformComponent waypointTransformComponent = store.getComponent(waypointRef, TransformComponent.getComponentType()); - Vector3f waypointRotation = waypointTransformComponent.getRotation(); - waypointRotation.assign(playerBodyRotation); + Rotation3f waypointRotation = waypointTransformComponent.getRotation(); + waypointRotation.set(playerBodyRotation); Model model = PathPlugin.get().getPathMarkerModel(); store.putComponent(waypointRef, ModelComponent.getComponentType(), new ModelComponent(model)); String displayName = PatrolPathMarkerEntity.generateDisplayName(worldgenId, waypointEntity); diff --git a/src/com/hypixel/hytale/builtin/path/commands/PrefabPathNodesCommand.java b/src/com/hypixel/hytale/builtin/path/commands/PrefabPathNodesCommand.java index 094b6a57..614d991e 100644 --- a/src/com/hypixel/hytale/builtin/path/commands/PrefabPathNodesCommand.java +++ b/src/com/hypixel/hytale/builtin/path/commands/PrefabPathNodesCommand.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.builtin.path.WorldPathData; import com.hypixel.hytale.builtin.path.path.IPrefabPath; import com.hypixel.hytale.builtin.path.waypoint.IPrefabPathWaypoint; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -18,6 +17,7 @@ import java.util.List; import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PrefabPathNodesCommand extends AbstractWorldCommand { @Nonnull @@ -57,7 +57,7 @@ public class PrefabPathNodesCommand extends AbstractWorldCommand { order[0]++; } else { Vector3d pos = waypoint.getWaypointPosition(store); - Vector3f rotation = waypoint.getWaypointRotation(store); + Rotation3f rotation = waypoint.getWaypointRotation(store); sb.append("\n ").append('#').append(waypoint.getOrder()); sb.append(" (").append(pos.x).append(", ").append(pos.y).append(", ").append(pos.z).append(')'); sb.append("\n ").append("Rotation: (").append(rotation.x).append(", ").append(rotation.y).append(", ").append(rotation.z).append(')'); diff --git a/src/com/hypixel/hytale/builtin/path/commands/WorldPathBuilderCommand.java b/src/com/hypixel/hytale/builtin/path/commands/WorldPathBuilderCommand.java index a7d60b42..366d09f7 100644 --- a/src/com/hypixel/hytale/builtin/path/commands/WorldPathBuilderCommand.java +++ b/src/com/hypixel/hytale/builtin/path/commands/WorldPathBuilderCommand.java @@ -102,7 +102,7 @@ public class WorldPathBuilderCommand extends AbstractCommandCollection { assert transformComponent != null; - Transform transform = transformComponent.getTransform().clone(); + Transform transform = new Transform(transformComponent.getTransform()); WorldPathBuilderCommand.getOrCreateBuilder(ref, store).getPath().getWaypoints().add(transform); context.sendMessage(MESSAGE_UNIVERSE_WORLD_PATH_POINT_ADDED); } @@ -247,7 +247,7 @@ public class WorldPathBuilderCommand extends AbstractCommandCollection { WorldPath worldPath = builder.getPath(); int index = this.indexArg.provided(context) ? this.indexArg.get(context) : worldPath.getWaypoints().size() - 1; - worldPath.getWaypoints().set(index, transformComponent.getTransform().clone()); + worldPath.getWaypoints().set(index, new Transform(transformComponent.getTransform())); context.sendMessage(MESSAGE_UNIVERSE_WORLD_PATH_POINT_SET); } } diff --git a/src/com/hypixel/hytale/builtin/path/entities/PatrolPathMarkerEntity.java b/src/com/hypixel/hytale/builtin/path/entities/PatrolPathMarkerEntity.java index 4e55578f..cd835964 100644 --- a/src/com/hypixel/hytale/builtin/path/entities/PatrolPathMarkerEntity.java +++ b/src/com/hypixel/hytale/builtin/path/entities/PatrolPathMarkerEntity.java @@ -10,8 +10,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -23,6 +22,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PatrolPathMarkerEntity extends Entity implements IPrefabPathWaypoint { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -227,7 +227,7 @@ public class PatrolPathMarkerEntity extends Entity implements IPrefabPathWaypoin @Nonnull @Override - public Vector3f getWaypointRotation(@Nonnull ComponentAccessor componentAccessor) { + public Rotation3f getWaypointRotation(@Nonnull ComponentAccessor componentAccessor) { Ref ref = this.getReference(); assert ref != null && ref.isValid() : "Entity reference is null or invalid"; diff --git a/src/com/hypixel/hytale/builtin/path/path/IPrefabPath.java b/src/com/hypixel/hytale/builtin/path/path/IPrefabPath.java index 5100484c..5a833b67 100644 --- a/src/com/hypixel/hytale/builtin/path/path/IPrefabPath.java +++ b/src/com/hypixel/hytale/builtin/path/path/IPrefabPath.java @@ -2,10 +2,11 @@ package com.hypixel.hytale.builtin.path.path; import com.hypixel.hytale.builtin.path.waypoint.IPrefabPathWaypoint; import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.path.IPath; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; public interface IPrefabPath extends IPath { short registerNewWaypoint(@Nonnull IPrefabPathWaypoint var1, int var2); @@ -26,7 +27,7 @@ public interface IPrefabPath extends IPath { int getWorldGenId(); - Vector3d getNearestWaypointPosition(@Nonnull Vector3d var1, @Nonnull ComponentAccessor var2); + Vector3dc getNearestWaypointPosition(@Nonnull Vector3d var1, @Nonnull ComponentAccessor var2); void mergeInto(@Nonnull IPrefabPath var1, int var2, @Nonnull ComponentAccessor var3); diff --git a/src/com/hypixel/hytale/builtin/path/path/PatrolPath.java b/src/com/hypixel/hytale/builtin/path/path/PatrolPath.java index fdc69ab8..31db53e6 100644 --- a/src/com/hypixel/hytale/builtin/path/path/PatrolPath.java +++ b/src/com/hypixel/hytale/builtin/path/path/PatrolPath.java @@ -5,7 +5,7 @@ import com.hypixel.hytale.builtin.path.PathPlugin; import com.hypixel.hytale.builtin.path.entities.PatrolPathMarkerEntity; import com.hypixel.hytale.builtin.path.waypoint.IPrefabPathWaypoint; import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Collections; @@ -16,6 +16,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class PatrolPath implements IPrefabPath { private final UUID id; @@ -183,14 +185,14 @@ public class PatrolPath implements IPrefabPath { } @Override - public Vector3d getNearestWaypointPosition(@Nonnull Vector3d origin, @Nonnull ComponentAccessor componentAccessor) { - Vector3d nearest = Vector3d.MAX; + public Vector3dc getNearestWaypointPosition(@Nonnull Vector3d origin, @Nonnull ComponentAccessor componentAccessor) { + Vector3dc nearest = Vector3dUtil.MAX; double minDist2 = Double.MAX_VALUE; for (int i = 0; i < this.length.get(); i++) { IPrefabPathWaypoint wp = this.waypoints.get(i); if (wp != null) { - double dist2 = origin.distanceSquaredTo(wp.getWaypointPosition(componentAccessor)); + double dist2 = origin.distanceSquared(wp.getWaypointPosition(componentAccessor)); if (dist2 < minDist2) { nearest = wp.getWaypointPosition(componentAccessor); minDist2 = dist2; diff --git a/src/com/hypixel/hytale/builtin/path/path/TransientPath.java b/src/com/hypixel/hytale/builtin/path/path/TransientPath.java index eb6c6eed..2ba1689b 100644 --- a/src/com/hypixel/hytale/builtin/path/path/TransientPath.java +++ b/src/com/hypixel/hytale/builtin/path/path/TransientPath.java @@ -1,9 +1,8 @@ package com.hypixel.hytale.builtin.path.path; import com.hypixel.hytale.builtin.path.waypoint.RelativeWaypointDefinition; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import com.hypixel.hytale.server.core.universe.world.path.IPath; import com.hypixel.hytale.server.core.universe.world.path.SimplePathWaypoint; @@ -14,15 +13,16 @@ import java.util.Queue; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class TransientPath implements IPath { protected final List waypoints = new ObjectArrayList<>(); - public void addWaypoint(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public void addWaypoint(@Nonnull Vector3d position, @Nonnull Rotation3f rotation) { this.waypoints .add( new SimplePathWaypoint( - (short)this.waypoints.size(), new Transform(position.x, position.y, position.z, rotation.getPitch(), rotation.getYaw(), rotation.getRoll()) + (short)this.waypoints.size(), new Transform(position.x, position.y, position.z, rotation.pitch(), rotation.yaw(), rotation.roll()) ) ); } @@ -56,19 +56,19 @@ public class TransientPath implements IPath { @Nonnull public static IPath buildPath( - @Nonnull Vector3d origin, @Nonnull Vector3f rotation, @Nonnull Queue instructions, double scale + @Nonnull Vector3d origin, @Nonnull Rotation3f rotation, @Nonnull Queue instructions, double scale ) { TransientPath path = new TransientPath(); path.addWaypoint(origin, rotation); Vector3d position = new Vector3d(origin); Vector3d directionVector = new Vector3d(); - Vector3f rotationVector = new Vector3f(rotation); + Rotation3f rotationVector = new Rotation3f(rotation); while (!instructions.isEmpty()) { RelativeWaypointDefinition instruction = instructions.poll(); rotationVector.addYaw(instruction.getRotation()); - directionVector.assign(PhysicsMath.headingX(rotationVector.getYaw()), 0.0, PhysicsMath.headingZ(rotationVector.getYaw())); - directionVector.setLength(instruction.getDistance() * scale); + directionVector.set(PhysicsMath.headingX(rotationVector.yaw()), 0.0, PhysicsMath.headingZ(rotationVector.yaw())); + directionVector.normalize(instruction.getDistance() * scale); position.add(directionVector); path.addWaypoint(position, rotationVector); } diff --git a/src/com/hypixel/hytale/builtin/path/path/TransientPathDefinition.java b/src/com/hypixel/hytale/builtin/path/path/TransientPathDefinition.java index 1337c33e..ad1a36a7 100644 --- a/src/com/hypixel/hytale/builtin/path/path/TransientPathDefinition.java +++ b/src/com/hypixel/hytale/builtin/path/path/TransientPathDefinition.java @@ -1,13 +1,13 @@ package com.hypixel.hytale.builtin.path.path; import com.hypixel.hytale.builtin.path.waypoint.RelativeWaypointDefinition; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.universe.world.path.IPath; import com.hypixel.hytale.server.core.universe.world.path.SimplePathWaypoint; import java.util.ArrayDeque; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TransientPathDefinition { protected final List waypointDefinitions; @@ -19,7 +19,7 @@ public class TransientPathDefinition { } @Nonnull - public IPath buildPath(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public IPath buildPath(@Nonnull Vector3d position, @Nonnull Rotation3f rotation) { ArrayDeque queue = new ArrayDeque<>(this.waypointDefinitions); return TransientPath.buildPath(position, rotation, queue, this.scale); } diff --git a/src/com/hypixel/hytale/builtin/portals/commands/player/LeaveCommand.java b/src/com/hypixel/hytale/builtin/portals/commands/player/LeaveCommand.java index 494faf99..99800a25 100644 --- a/src/com/hypixel/hytale/builtin/portals/commands/player/LeaveCommand.java +++ b/src/com/hypixel/hytale/builtin/portals/commands/player/LeaveCommand.java @@ -9,6 +9,8 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -34,7 +36,8 @@ public class LeaveCommand extends AbstractPlayerCommand { if (!portalWorldResource.exists()) { playerRef.sendMessage(MESSAGE_COMMANDS_LEAVE_NOT_IN_PORTAL); } else { - boolean uncursedAny = CursedItems.uncurseAll(playerComponent.getInventory().getCombinedEverything()); + CombinedItemContainer everythingInventoryComponent = InventoryComponent.getCombined(store, ref, InventoryComponent.EVERYTHING); + boolean uncursedAny = CursedItems.uncurseAll(everythingInventoryComponent); if (uncursedAny) { playerRef.sendMessage(MESSAGE_COMMANDS_LEAVE_UNCURSED_TEMP); } diff --git a/src/com/hypixel/hytale/builtin/portals/components/PortalDevice.java b/src/com/hypixel/hytale/builtin/portals/components/PortalDevice.java index a48cdb38..6984f72e 100644 --- a/src/com/hypixel/hytale/builtin/portals/components/PortalDevice.java +++ b/src/com/hypixel/hytale/builtin/portals/components/PortalDevice.java @@ -11,6 +11,7 @@ import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -27,6 +28,8 @@ public class PortalDevice implements Component { private PortalDeviceConfig config; private String baseBlockTypeKey; private UUID destinationWorldUuid; + @Nullable + private CompletableFuture pendingWorld; public static ComponentType getComponentType() { return PortalsPlugin.getInstance().getPortalDeviceComponentType(); @@ -72,12 +75,21 @@ public class PortalDevice implements Component { this.destinationWorldUuid = world.getWorldConfig().getUuid(); } + public boolean isLoadingWorld() { + return this.pendingWorld == null ? false : !this.pendingWorld.isDone(); + } + + public void setPendingWorld(@Nullable CompletableFuture pendingWorld) { + this.pendingWorld = pendingWorld; + } + @Override public Component clone() { PortalDevice portal = new PortalDevice(); portal.config = this.config; portal.baseBlockTypeKey = this.baseBlockTypeKey; portal.destinationWorldUuid = this.destinationWorldUuid; + portal.pendingWorld = this.pendingWorld; return portal; } } diff --git a/src/com/hypixel/hytale/builtin/portals/interactions/EnterPortalInteraction.java b/src/com/hypixel/hytale/builtin/portals/interactions/EnterPortalInteraction.java index a42b35cb..8e32d2c4 100644 --- a/src/com/hypixel/hytale/builtin/portals/interactions/EnterPortalInteraction.java +++ b/src/com/hypixel/hytale/builtin/portals/interactions/EnterPortalInteraction.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -33,6 +32,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class EnterPortalInteraction extends SimpleBlockInteraction { @Nonnull @@ -90,58 +90,57 @@ public class EnterPortalInteraction extends SimpleBlockInteraction { RotationTuple rotation = chunk.getRotation(targetBlock.x, targetBlock.y, targetBlock.z); double yaw = rotation.yaw().getRadians() + Math.PI; Transform returnTransform = new Transform(targetBlock.x + 0.5, targetBlock.y + 0.5, targetBlock.z + 0.5, 0.0F, (float)yaw, 0.0F); - World targetWorld = portalDevice.getDestinationWorld(); - if (targetWorld == null) { - playerComponent.sendMessage(MESSAGE_PORTALS_DEVICE_WORLD_IS_DEAD); + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent == null) { context.getState().state = InteractionState.Failed; } else { - UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); - if (uuidComponent == null) { + World targetWorld = portalDevice.getDestinationWorld(); + if (targetWorld == null) { + playerRefComponent.sendMessage(MESSAGE_PORTALS_DEVICE_WORLD_IS_DEAD); context.getState().state = InteractionState.Failed; } else { - UUID playerUuid = uuidComponent.getUuid(); - fetchTargetWorldState(targetWorld, playerUuid) - .thenAcceptAsync( - state -> { - if (!ref.isValid()) { - playerComponent.sendMessage(MESSAGE_PORTALS_DEVICE_REF_INVALID); - context.getState().state = InteractionState.Failed; - } else { - switch (state) { - case OKAY: - InstancesPlugin.teleportPlayerToInstance(ref, commandBuffer, targetWorld, returnTransform); - break; - case WORLD_DEAD: - playerComponent.sendMessage(MESSAGE_PORTALS_DEVICE_WORLD_IS_DEAD); - context.getState().state = InteractionState.Failed; - break; - case DIED_IN_WORLD: - PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); - if (playerRefComponent == null) { + UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); + if (uuidComponent == null) { + context.getState().state = InteractionState.Failed; + } else { + UUID playerUuid = uuidComponent.getUuid(); + fetchTargetWorldState(targetWorld, playerUuid) + .thenAcceptAsync( + state -> { + if (!ref.isValid()) { + playerRefComponent.sendMessage(MESSAGE_PORTALS_DEVICE_REF_INVALID); + context.getState().state = InteractionState.Failed; + } else { + switch (state) { + case OKAY: + InstancesPlugin.teleportPlayerToInstance(ref, commandBuffer, targetWorld, returnTransform); + break; + case WORLD_DEAD: + playerRefComponent.sendMessage(MESSAGE_PORTALS_DEVICE_WORLD_IS_DEAD); context.getState().state = InteractionState.Failed; - return; - } + break; + case DIED_IN_WORLD: + Ref blockEntityRef = BlockModule.getBlockEntity(world, targetBlock.x, targetBlock.y, targetBlock.z); + if (blockEntityRef == null || !blockEntityRef.isValid()) { + playerRefComponent.sendMessage(MESSAGE_PORTALS_DEVICE_BLOCK_ENTITY_REF_INVALID); + context.getState().state = InteractionState.Failed; + return; + } - Ref blockEntityRef = BlockModule.getBlockEntity(world, targetBlock.x, targetBlock.y, targetBlock.z); - if (blockEntityRef == null || !blockEntityRef.isValid()) { - playerComponent.sendMessage(MESSAGE_PORTALS_DEVICE_BLOCK_ENTITY_REF_INVALID); + PortalDeviceActivePage activePage = new PortalDeviceActivePage( + playerRefComponent, portalDevice.getConfig(), blockEntityRef + ); + playerComponent.getPageManager().openCustomPage(ref, world.getEntityStore().getStore(), activePage); + break; + case NO_SPAWN_AVAILABLE: + playerRefComponent.sendMessage(MESSAGE_PORTALS_DEVICE_NO_SPAWN); context.getState().state = InteractionState.Failed; - return; - } - - PortalDeviceActivePage activePage = new PortalDeviceActivePage( - playerRefComponent, portalDevice.getConfig(), blockEntityRef - ); - playerComponent.getPageManager().openCustomPage(ref, world.getEntityStore().getStore(), activePage); - break; - case NO_SPAWN_AVAILABLE: - playerComponent.sendMessage(MESSAGE_PORTALS_DEVICE_NO_SPAWN); - context.getState().state = InteractionState.Failed; + } } - } - }, - world - ); + }, + world + ); + } } } } diff --git a/src/com/hypixel/hytale/builtin/portals/interactions/ReturnPortalInteraction.java b/src/com/hypixel/hytale/builtin/portals/interactions/ReturnPortalInteraction.java index 94b97804..87ae4d8a 100644 --- a/src/com/hypixel/hytale/builtin/portals/interactions/ReturnPortalInteraction.java +++ b/src/com/hypixel/hytale/builtin/portals/interactions/ReturnPortalInteraction.java @@ -6,21 +6,24 @@ import com.hypixel.hytale.builtin.portals.utils.CursedItems; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.time.Duration; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class ReturnPortalInteraction extends SimpleBlockInteraction { @Nonnull @@ -48,25 +51,31 @@ public class ReturnPortalInteraction extends SimpleBlockInteraction { @Nonnull CooldownHandler cooldownHandler ) { Ref ref = context.getEntity(); - Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); - if (playerComponent == null) { + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent == null) { context.getState().state = InteractionState.Failed; } else { - long elapsedNanosInWorld = playerComponent.getSinceLastSpawnNanos(); - if (elapsedNanosInWorld < MINIMUM_TIME_IN_WORLD.toNanos()) { - if (elapsedNanosInWorld > WARNING_TIME.toNanos()) { - playerComponent.sendMessage(MESSAGE_PORTALS_ATTUNING_TO_WORLD); - } - + Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); + if (playerComponent == null) { context.getState().state = InteractionState.Failed; } else { - PortalWorld portalWorld = commandBuffer.getResource(PortalWorld.getResourceType()); - if (!portalWorld.exists()) { - playerComponent.sendMessage(MESSAGE_PORTALS_DEVICE_NOT_IN_PORTAL_WORLD); + long elapsedNanosInWorld = playerComponent.getSinceLastSpawnNanos(); + if (elapsedNanosInWorld < MINIMUM_TIME_IN_WORLD.toNanos()) { + if (elapsedNanosInWorld > WARNING_TIME.toNanos()) { + playerRefComponent.sendMessage(MESSAGE_PORTALS_ATTUNING_TO_WORLD); + } + context.getState().state = InteractionState.Failed; } else { - CursedItems.uncurseAll(playerComponent.getInventory().getCombinedEverything()); - InstancesPlugin.exitInstance(ref, commandBuffer); + PortalWorld portalWorld = commandBuffer.getResource(PortalWorld.getResourceType()); + if (!portalWorld.exists()) { + playerRefComponent.sendMessage(MESSAGE_PORTALS_DEVICE_NOT_IN_PORTAL_WORLD); + context.getState().state = InteractionState.Failed; + } else { + CombinedItemContainer everythingInventoryComponent = InventoryComponent.getCombined(commandBuffer, ref, InventoryComponent.EVERYTHING); + CursedItems.uncurseAll(everythingInventoryComponent); + InstancesPlugin.exitInstance(ref, commandBuffer); + } } } } diff --git a/src/com/hypixel/hytale/builtin/portals/systems/curse/DeleteCursedItemsOnSpawnSystem.java b/src/com/hypixel/hytale/builtin/portals/systems/curse/DeleteCursedItemsOnSpawnSystem.java index 87e46d7c..ca0ad6ec 100644 --- a/src/com/hypixel/hytale/builtin/portals/systems/curse/DeleteCursedItemsOnSpawnSystem.java +++ b/src/com/hypixel/hytale/builtin/portals/systems/curse/DeleteCursedItemsOnSpawnSystem.java @@ -10,6 +10,8 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -19,10 +21,10 @@ public class DeleteCursedItemsOnSpawnSystem extends RefSystem { public void onEntityAdded( @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { - PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType()); - if (!portalWorld.exists()) { - Player player = store.getComponent(ref, Player.getComponentType()); - CursedItems.deleteAll(player); + PortalWorld portalWorldResource = store.getResource(PortalWorld.getResourceType()); + if (!portalWorldResource.exists()) { + CombinedItemContainer everythingInventoryComponent = InventoryComponent.getCombined(store, ref, InventoryComponent.EVERYTHING); + CursedItems.deleteAll(everythingInventoryComponent); } } diff --git a/src/com/hypixel/hytale/builtin/portals/systems/curse/DiedInPortalSystem.java b/src/com/hypixel/hytale/builtin/portals/systems/curse/DiedInPortalSystem.java index 3c9a61bf..2d0a7efd 100644 --- a/src/com/hypixel/hytale/builtin/portals/systems/curse/DiedInPortalSystem.java +++ b/src/com/hypixel/hytale/builtin/portals/systems/curse/DiedInPortalSystem.java @@ -8,6 +8,8 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -19,12 +21,16 @@ public class DiedInPortalSystem extends DeathSystems.OnDeathSystem { public void onComponentAdded( @Nonnull Ref ref, @Nonnull DeathComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { - PortalWorld portalWorld = commandBuffer.getResource(PortalWorld.getResourceType()); - if (portalWorld.exists()) { - UUID playerId = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()).getUuid(); - portalWorld.getDiedInWorld().add(playerId); - Player player = store.getComponent(ref, Player.getComponentType()); - CursedItems.deleteAll(player); + PortalWorld portalWorldResource = commandBuffer.getResource(PortalWorld.getResourceType()); + if (portalWorldResource.exists()) { + UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); + + assert uuidComponent != null; + + UUID playerUUID = uuidComponent.getUuid(); + portalWorldResource.getDiedInWorld().add(playerUUID); + CombinedItemContainer everythingInventoryComponent = InventoryComponent.getCombined(store, ref, InventoryComponent.EVERYTHING); + CursedItems.deleteAll(everythingInventoryComponent); } } diff --git a/src/com/hypixel/hytale/builtin/portals/systems/voidevent/VoidInvasionPortalsSpawnSystem.java b/src/com/hypixel/hytale/builtin/portals/systems/voidevent/VoidInvasionPortalsSpawnSystem.java index ee42c624..689da32f 100644 --- a/src/com/hypixel/hytale/builtin/portals/systems/voidevent/VoidInvasionPortalsSpawnSystem.java +++ b/src/com/hypixel/hytale/builtin/portals/systems/voidevent/VoidInvasionPortalsSpawnSystem.java @@ -17,34 +17,38 @@ import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Resource; +import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.DelayedEntitySystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class VoidInvasionPortalsSpawnSystem extends DelayedEntitySystem { @Nonnull private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); private static final int MAX_PORTALS = 24; - @Nullable - private CompletableFuture findPortalSpawnPos; + private final ResourceType voidInvasionDataResourceType = this.registerResource( + VoidInvasionPortalsSpawnSystem.VoidInvasionData.class, VoidInvasionPortalsSpawnSystem.VoidInvasionData::new + ); public VoidInvasionPortalsSpawnSystem() { super(2.0F); @@ -63,25 +67,26 @@ public class VoidInvasionPortalsSpawnSystem extends DelayedEntitySystem> spawners = cleanupAndGetSpawners(voidEventComponent); if (spawners.size() < 24) { - this.findPortalSpawnPos = findPortalSpawnPosition(world, voidEventComponent, commandBuffer); + data.findPortalSpawnPos = findPortalSpawnPosition(world, voidEventComponent, commandBuffer); } - } else if (this.findPortalSpawnPos.isDone()) { + } else if (data.findPortalSpawnPos.isDone()) { Vector3d portalPos; try { - portalPos = this.findPortalSpawnPos.join(); - this.findPortalSpawnPos = null; - } catch (Throwable var16) { - LOGGER.at(Level.SEVERE).withCause(var16).log("Error trying to find a void event spawn position"); + portalPos = data.findPortalSpawnPos.join(); + data.findPortalSpawnPos = null; + } catch (Throwable var17) { + LOGGER.at(Level.SEVERE).withCause(var17).log("Error trying to find a void event spawn position"); return; } if (portalPos != null) { Holder voidSpawnerHolder = EntityStore.REGISTRY.newHolder(); voidSpawnerHolder.addComponent(VoidSpawner.getComponentType(), new VoidSpawner()); - voidSpawnerHolder.addComponent(TransformComponent.getComponentType(), new TransformComponent(portalPos, new Vector3f())); + voidSpawnerHolder.addComponent(TransformComponent.getComponentType(), new TransformComponent(portalPos, new Rotation3f())); Ref voidSpawner = commandBuffer.addEntity(voidSpawnerHolder, AddReason.SPAWN); voidEventComponent.getVoidSpawners().add(portalPos, voidSpawner); VoidEventConfig eventConfig = VoidEvent.getConfig(world); @@ -89,7 +94,7 @@ public class VoidInvasionPortalsSpawnSystem extends DelayedEntitySystem> existingSpawners = voidEvent.getVoidSpawners(); NotNearAnyInHashGrid noNearbySpawners = new NotNearAnyInHashGrid(existingSpawners, 62.0); @@ -156,7 +161,7 @@ public class VoidInvasionPortalsSpawnSystem extends DelayedEntitySystem> players = new ObjectArrayList<>(playerRefs.size()); + List> players = new ReferenceArrayList<>(playerRefs.size()); for (PlayerRef playerRef : playerRefs) { players.add(playerRef.getReference()); @@ -183,4 +188,15 @@ public class VoidInvasionPortalsSpawnSystem extends DelayedEntitySystem getQuery() { return VoidEvent.getComponentType(); } + + public static class VoidInvasionData implements Resource { + @Nullable + private CompletableFuture findPortalSpawnPos; + + @Nullable + @Override + public Resource clone() { + return new VoidInvasionPortalsSpawnSystem.VoidInvasionData(); + } + } } diff --git a/src/com/hypixel/hytale/builtin/portals/systems/voidevent/VoidSpawnerSystems.java b/src/com/hypixel/hytale/builtin/portals/systems/voidevent/VoidSpawnerSystems.java index dcfb660b..0e6c0428 100644 --- a/src/com/hypixel/hytale/builtin/portals/systems/voidevent/VoidSpawnerSystems.java +++ b/src/com/hypixel/hytale/builtin/portals/systems/voidevent/VoidSpawnerSystems.java @@ -13,7 +13,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.ParticleUtil; @@ -28,6 +27,7 @@ import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public final class VoidSpawnerSystems { @Nonnull @@ -55,7 +55,7 @@ public final class VoidSpawnerSystems { for (int i = 0; i < spawnBeacons.size(); i++) { String spawnBeacon = spawnBeacons.get(i); - Vector3d beaconPos = position.clone().add(0.0, 0.5 + 0.1 * i, 0.0); + Vector3d beaconPos = new Vector3d(position).add(0.0, 0.5 + 0.1 * i, 0.0); int beaconAssetId = BeaconNPCSpawn.getAssetMap().getIndexOrDefault(spawnBeacon, -1); if (beaconAssetId == -1) { HytaleLogger.getLogger().at(Level.WARNING).log("No asset found for spawn beacon \"" + spawnBeacon + "\" in GameplayConfig for portal world"); diff --git a/src/com/hypixel/hytale/builtin/portals/ui/PortalDeviceActivePage.java b/src/com/hypixel/hytale/builtin/portals/ui/PortalDeviceActivePage.java index b74a9cb6..c9447cd2 100644 --- a/src/com/hypixel/hytale/builtin/portals/ui/PortalDeviceActivePage.java +++ b/src/com/hypixel/hytale/builtin/portals/ui/PortalDeviceActivePage.java @@ -40,42 +40,48 @@ public class PortalDeviceActivePage extends InteractiveCustomUIPage { private final PortalDeviceConfig config; @@ -193,6 +197,7 @@ public class PortalDeviceSummonPage extends InteractiveCustomUIPage { - WorldConfig worldConfig = spawnedWorld.getWorldConfig(); - worldConfig.setDeleteOnUniverseStart(true); - worldConfig.setDeleteOnRemove(true); - worldConfig.setGameplayConfig(portalType.getGameplayConfigId()); - InstanceWorldConfig instanceConfig = InstanceWorldConfig.ensureAndGet(worldConfig); - if (instanceConfig.getDiscovery() == null) { - InstanceDiscoveryConfig discoveryConfig = new InstanceDiscoveryConfig(); - discoveryConfig.setTitleKey(portalType.getDescription().getDisplayNameKey()); - discoveryConfig.setSubtitleKey("server.portals.discoverySubtitle"); - discoveryConfig.setDisplay(true); - discoveryConfig.setAlwaysDisplay(true); - instanceConfig.setDiscovery(discoveryConfig); - } + CompletableFuture future = InstancesPlugin.get() + .spawnInstance(portalType.getInstanceId(), originWorld, transform) + .thenCompose(spawnedWorld -> { + WorldConfig worldConfig = spawnedWorld.getWorldConfig(); + worldConfig.setDeleteOnUniverseStart(true); + worldConfig.setDeleteOnRemove(true); + worldConfig.setGameplayConfig(portalType.getGameplayConfigId()); + InstanceWorldConfig instanceConfig = InstanceWorldConfig.ensureAndGet(worldConfig); + if (instanceConfig.getDiscovery() == null) { + InstanceDiscoveryConfig discoveryConfig = new InstanceDiscoveryConfig(); + discoveryConfig.setTitleKey(portalType.getDescription().getDisplayNameKey()); + discoveryConfig.setSubtitleKey("server.portals.discoverySubtitle"); + discoveryConfig.setDisplay(true); + discoveryConfig.setAlwaysDisplay(true); + instanceConfig.setDiscovery(discoveryConfig); + } - PortalRemovalCondition portalRemoval = new PortalRemovalCondition(portalKey.getTimeLimitSeconds()); - instanceConfig.setRemovalConditions(portalRemoval); - PortalWorld portalWorld = spawnedWorld.getEntityStore().getStore().getResource(PortalWorld.getResourceType()); - portalWorld.init(portalType, portalKey.getTimeLimitSeconds(), portalRemoval, gameplayConfig); - String returnBlockType = portalDevice.getConfig().getReturnBlock(); - if (returnBlockType == null) { - throw new RuntimeException("Return block type on PortalDevice is misconfigured"); - } else { - return spawnReturnPortal(spawnedWorld, portalWorld, playerUUID, returnBlockType); - } - }).thenAcceptAsync(spawnedWorld -> { - portalDevice.setDestinationWorld(spawnedWorld); - worldChunk.setBlock(x, y, z, BlockType.getAssetMap().getIndex(onType.getId()), onType, rotation, 0, 6); - }, originWorld).exceptionallyAsync(t -> { - playerComponent.sendMessage(Message.translation("server.portals.device.internalErrorSpawning")); - HytaleLogger.getLogger().at(Level.SEVERE).withCause(t).log("Error creating instance for Portal Device " + portalKey, t); - worldChunk.setBlock(x, y, z, BlockType.getAssetMap().getIndex(offType.getId()), offType, rotation, 0, 6); - return null; - }, originWorld); + PortalRemovalCondition portalRemoval = new PortalRemovalCondition(portalKey.getTimeLimitSeconds()); + instanceConfig.setRemovalConditions(portalRemoval); + PortalWorld portalWorld = spawnedWorld.getEntityStore().getStore().getResource(PortalWorld.getResourceType()); + portalWorld.init(portalType, portalKey.getTimeLimitSeconds(), portalRemoval, gameplayConfig); + String returnBlockType = portalDevice.getConfig().getReturnBlock(); + if (returnBlockType == null) { + throw new RuntimeException("Return block type on PortalDevice is misconfigured"); + } else { + BlockType overrideFromPortalType = portalType.getSpawn().getReturnBlockOverride(); + if (overrideFromPortalType != null) { + returnBlockType = overrideFromPortalType.getId(); + } + + return spawnReturnPortal(spawnedWorld, portalWorld, playerUUID, returnBlockType); + } + }) + .thenAcceptAsync(spawnedWorld -> { + portalDevice.setDestinationWorld(spawnedWorld); + worldChunk.setBlock(x, y, z, BlockType.getAssetMap().getIndex(onType.getId()), onType, rotation, 0, 6); + }, originWorld) + .exceptionallyAsync(t -> { + HytaleLogger.getLogger().at(Level.SEVERE).withCause(t).log("Error creating instance for Portal Device " + portalKey, t); + + try { + playerRefComponent.sendMessage(Message.translation("server.portals.device.internalErrorSpawning")); + combinedInventoryHotbarFirst.addItemStack(removedItem); + worldChunk.setBlock(x, y, z, BlockType.getAssetMap().getIndex(offType.getId()), offType, rotation, 0, 6); + } catch (Throwable var12x) { + HytaleLogger.getLogger().at(Level.SEVERE).withCause(var12x).log("Error while resolving portal device error"); + } + + return null; + }, originWorld) + .whenComplete((unused, throwable) -> portalDevice.setPendingWorld(null)); + portalDevice.setPendingWorld(future); } } } @@ -281,29 +308,57 @@ public class PortalDeviceSummonPage extends InteractiveCustomUIPage spawnReturnPortal( @Nonnull World world, @Nonnull PortalWorld portalWorld, @Nonnull UUID sampleUuid, @Nonnull String portalBlockType ) { - return getSpawnTransform(world, sampleUuid) + PortalType portalType = portalWorld.getPortalType(); + PortalSpawnConfig spawnConfig = portalType.getSpawn(); + return getSpawnTransform(portalType, world, sampleUuid) .thenCompose( spawnTransform -> { Vector3d spawnPoint = spawnTransform.getPosition(); + Transform playerSpawnTransform = new Transform(spawnTransform); return world.getChunkAsync(ChunkUtil.indexChunkFromBlock((int)spawnPoint.x, (int)spawnPoint.z)) .thenAccept( chunk -> { - for (int dy = 0; dy < 3; dy++) { - for (int dx = -1; dx <= 1; dx++) { - for (int dz = -1; dz <= 1; dz++) { - chunk.setBlock((int)spawnPoint.x + dx, (int)spawnPoint.y + dy, (int)spawnPoint.z + dz, BlockType.EMPTY); + if (spawnConfig.isSpawningReturnPortal()) { + for (int dy = 0; dy < 3; dy++) { + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + chunk.setBlock((int)spawnPoint.x + dx, (int)spawnPoint.y + dy, (int)spawnPoint.z + dz, BlockType.EMPTY); + } } } + + chunk.setBlock((int)spawnPoint.x, (int)spawnPoint.y, (int)spawnPoint.z, portalBlockType); + playerSpawnTransform.getPosition().add(0.0, 0.5, 0.0); + HytaleLogger.getLogger() + .at(Level.INFO) + .log( + "Spawned return portal for " + + world.getName() + + " at " + + (int)spawnPoint.x + + ", " + + (int)spawnPoint.y + + ", " + + (int)spawnPoint.z + ); } - chunk.setBlock((int)spawnPoint.x, (int)spawnPoint.y, (int)spawnPoint.z, portalBlockType); - portalWorld.setSpawnPoint(spawnTransform); - world.getWorldConfig().setSpawnProvider(new IndividualSpawnProvider(spawnTransform)); - HytaleLogger.getLogger() - .at(Level.INFO) - .log( - "Spawned return portal for " + world.getName() + " at " + (int)spawnPoint.x + ", " + (int)spawnPoint.y + ", " + (int)spawnPoint.z - ); + portalWorld.setSpawnPoint(playerSpawnTransform); + world.getWorldConfig().setSpawnProvider(new IndividualSpawnProvider(playerSpawnTransform)); + if (!spawnConfig.isSpawningReturnPortal()) { + HytaleLogger.getLogger() + .at(Level.INFO) + .log( + "Fragment spawn point for " + + world.getName() + + " at " + + (int)spawnPoint.x + + ", " + + (int)spawnPoint.y + + ", " + + (int)spawnPoint.z + ); + } } ) .thenApply(nothing -> world); @@ -312,11 +367,18 @@ public class PortalDeviceSummonPage extends InteractiveCustomUIPage getSpawnTransform(@Nonnull World world, @Nonnull UUID sampleUuid) { - return CompletableFuture.supplyAsync(() -> { - List hintedSpawns = fetchHintedSpawns(world, sampleUuid); - return PortalSpawnFinder.computeSpawnTransform(world, hintedSpawns); - }, world); + private static CompletableFuture getSpawnTransform(@Nonnull PortalType portalType, @Nonnull World world, @Nonnull UUID sampleUuid) { + PortalSpawnConfig spawnConfig = portalType.getSpawn(); + ISpawnProvider override = spawnConfig.getSpawnProviderOverride(); + if (override != null) { + Transform spawnPoint = override.getSpawnPoint(world, sampleUuid); + return CompletableFuture.completedFuture(spawnPoint); + } else { + return CompletableFuture.supplyAsync(() -> { + List hintedSpawns = fetchHintedSpawns(world, sampleUuid); + return PortalSpawnFinder.computeSpawnTransform(world, hintedSpawns); + }, world); + } } private static List fetchHintedSpawns(World world, UUID sampleUuid) { @@ -356,7 +418,7 @@ public class PortalDeviceSummonPage extends InteractiveCustomUIPage hintedSpawns) { @@ -40,10 +42,10 @@ public final class PortalSpawnFinder { HytaleLogger.getLogger().atWarning().log("Both dart and fallback spawn finder failed for portal spawn"); return null; } else { - Vector3f direction = Vector3f.lookAt(spawn).scale(-1.0F); + Rotation3f direction = Rotation3f.lookAt(spawn).mul(-1.0F); direction.setPitch(0.0F); direction.setRoll(0.0F); - return new Transform(spawn.clone().add(0.0, 0.5, 0.0), direction); + return new Transform(new Vector3d(spawn).add(0.0, 0.5, 0.0), direction); } } @@ -152,7 +154,7 @@ public final class PortalSpawnFinder { @Nullable private static Vector3d findFallbackPositionOnGround(@Nonnull World world) { - Vector3d center = FALLBACK_POSITION.clone(); + Vector3d center = new Vector3d(FALLBACK_POSITION); long chunkIndex = ChunkUtil.indexChunkFromBlock(center.x, center.z); WorldChunk centerChunk = world.getChunk(chunkIndex); return centerChunk == null ? null : findWithGroundBelow(centerChunk, 0, 319, 0, 319, true); diff --git a/src/com/hypixel/hytale/builtin/portals/utils/CursedItems.java b/src/com/hypixel/hytale/builtin/portals/utils/CursedItems.java index 16112f7f..9036bf68 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/CursedItems.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/CursedItems.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.builtin.portals.utils; import com.hypixel.hytale.server.core.asset.type.item.config.metadata.AdventureMetadata; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import java.util.concurrent.atomic.AtomicBoolean; @@ -28,10 +27,6 @@ public final class CursedItems { return uncursedAny.get(); } - public static void deleteAll(@Nonnull Player player) { - deleteAll(player.getInventory().getCombinedEverything()); - } - public static void deleteAll(@Nonnull ItemContainer itemContainer) { itemContainer.replaceAll((slot, existing) -> { AdventureMetadata adventureMeta = existing.getFromMetadataOrNull(AdventureMetadata.KEYED_CODEC); diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/PositionPredicate.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/PositionPredicate.java index 6e7520c7..a7e909c4 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/PositionPredicate.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/PositionPredicate.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import javax.annotation.Nonnull; +import org.joml.Vector3d; public interface PositionPredicate { boolean test(@Nonnull World var1, @Nonnull Vector3d var2); diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/SpatialQuery.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/SpatialQuery.java index 58258f15..26a581e1 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/SpatialQuery.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/SpatialQuery.java @@ -3,13 +3,13 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries; import com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.generic.FilterQuery; import com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.generic.FlatMapQuery; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import java.util.Optional; import java.util.logging.Level; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public interface SpatialQuery { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/SpatialQueryDebug.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/SpatialQueryDebug.java index 2980c35d..e00dcd1b 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/SpatialQueryDebug.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/SpatialQueryDebug.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import java.util.Stack; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpatialQueryDebug { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchBelow.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchBelow.java index 1bef7e0a..149ccf02 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchBelow.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchBelow.java @@ -2,12 +2,12 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries.generators; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQuery; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQueryDebug; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import java.util.stream.IntStream; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SearchBelow implements SpatialQuery { private final int height; @@ -23,6 +23,6 @@ public class SearchBelow implements SpatialQuery { debug.appendLine("Searching up to " + this.height + " blocks below " + SpatialQueryDebug.fmt(origin) + ":"); } - return IntStream.rangeClosed(0, this.height).mapToObj(dy -> origin.clone().add(0.0, -dy, 0.0)); + return IntStream.rangeClosed(0, this.height).mapToObj(dy -> new Vector3d(origin).add(0.0, -dy, 0.0)); } } diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchCircular.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchCircular.java index 1d0b7882..dbc27a2f 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchCircular.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchCircular.java @@ -2,12 +2,12 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries.generators; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQuery; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQueryDebug; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SearchCircular implements SpatialQuery { private final double minRadius; @@ -38,7 +38,7 @@ public class SearchCircular implements SpatialQuery { ThreadLocalRandom rand = ThreadLocalRandom.current(); double rad = rand.nextDouble() * Math.PI * 2.0; double radius = this.minRadius + rand.nextDouble() * (this.maxRadius - this.minRadius); - return origin.clone().add(Math.cos(rad) * radius, 0.0, Math.sin(rad) * radius); + return new Vector3d(origin).add(Math.cos(rad) * radius, 0.0, Math.sin(rad) * radius); }).limit(this.attempts); } } diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchCone.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchCone.java index 96616090..a5c0d32e 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchCone.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/generators/SearchCone.java @@ -2,12 +2,12 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries.generators; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQuery; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQueryDebug; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SearchCone implements SpatialQuery { private final Vector3d direction; @@ -53,7 +53,7 @@ public class SearchCone implements SpatialQuery { ThreadLocalRandom random = ThreadLocalRandom.current(); double distance = this.minRadius + random.nextDouble() * (this.maxRadius - this.minRadius); double yawOffset = (random.nextDouble() - 0.5) * maxRadians; - Vector3d dir = this.direction.clone().rotateY((float)yawOffset).setLength(distance); + Vector3d dir = new Vector3d(this.direction).rotateY((float)yawOffset).normalize(distance); return dir.add(origin); }).limit(this.attempts); } diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/FitsAPortal.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/FitsAPortal.java index 3fc1ab72..401c1814 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/FitsAPortal.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/FitsAPortal.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.builtin.portals.utils.posqueries.PositionPredicate; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.modules.collision.WorldUtil; @@ -16,6 +15,8 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class FitsAPortal implements PositionPredicate { @Nonnull @@ -32,7 +33,7 @@ public class FitsAPortal implements PositionPredicate { for (int x : THREES) { for (int z : THREES) { for (int y = -1; y <= 3; y++) { - Vector3i rel = point.toVector3i().add(x, y, z); + Vector3i rel = Vector3dUtil.toVector3i(point).add(x, y, z); long chunkIndex = ChunkUtil.indexChunkFromBlock(rel.x, rel.z); WorldChunk chunk = world.getChunk(chunkIndex); if (chunk == null) { diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearAnyInHashGrid.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearAnyInHashGrid.java index baea9e93..d7eaf0e8 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearAnyInHashGrid.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearAnyInHashGrid.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries.predicates; import com.hypixel.hytale.builtin.portals.utils.posqueries.PositionPredicate; import com.hypixel.hytale.builtin.portals.utils.spatial.SpatialHashGrid; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import javax.annotation.Nonnull; +import org.joml.Vector3d; public record NotNearAnyInHashGrid(SpatialHashGrid hashGrid, double radius) implements PositionPredicate { @Override diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearPoint.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearPoint.java index 0c181326..0b8c25c4 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearPoint.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearPoint.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries.predicates; import com.hypixel.hytale.builtin.portals.utils.posqueries.PositionPredicate; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import javax.annotation.Nonnull; +import org.joml.Vector3d; public final class NotNearPoint implements PositionPredicate { @Nonnull @@ -17,6 +17,6 @@ public final class NotNearPoint implements PositionPredicate { @Override public boolean test(@Nonnull World world, @Nonnull Vector3d origin) { - return origin.distanceSquaredTo(this.point) >= this.radiusSq; + return origin.distanceSquared(this.point) >= this.radiusSq; } } diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearPointXZ.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearPointXZ.java index b8886752..95691454 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearPointXZ.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/NotNearPointXZ.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries.predicates; import com.hypixel.hytale.builtin.portals.utils.posqueries.PositionPredicate; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import javax.annotation.Nonnull; +import org.joml.Vector3d; public final class NotNearPointXZ implements PositionPredicate { private final Vector3d point; @@ -16,8 +16,8 @@ public final class NotNearPointXZ implements PositionPredicate { @Override public boolean test(@Nonnull World world, @Nonnull Vector3d origin) { - Vector3d pointAtHeight = this.point.clone(); + Vector3d pointAtHeight = new Vector3d(this.point); pointAtHeight.y = origin.y; - return origin.distanceSquaredTo(pointAtHeight) >= this.radiusSq; + return origin.distanceSquared(pointAtHeight) >= this.radiusSq; } } diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/generic/FilterQuery.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/generic/FilterQuery.java index 3050b7f6..1d637001 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/generic/FilterQuery.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/generic/FilterQuery.java @@ -3,12 +3,12 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.generic; import com.hypixel.hytale.builtin.portals.utils.posqueries.PositionPredicate; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQuery; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQueryDebug; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class FilterQuery implements SpatialQuery { private final SpatialQuery query; diff --git a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/generic/FlatMapQuery.java b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/generic/FlatMapQuery.java index b608edf8..3d458e34 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/generic/FlatMapQuery.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/posqueries/predicates/generic/FlatMapQuery.java @@ -2,11 +2,11 @@ package com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.generic; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQuery; import com.hypixel.hytale.builtin.portals.utils.posqueries.SpatialQueryDebug; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class FlatMapQuery implements SpatialQuery { @Nonnull diff --git a/src/com/hypixel/hytale/builtin/portals/utils/spatial/OctTree.java b/src/com/hypixel/hytale/builtin/portals/utils/spatial/OctTree.java deleted file mode 100644 index 567a704a..00000000 --- a/src/com/hypixel/hytale/builtin/portals/utils/spatial/OctTree.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.hypixel.hytale.builtin.portals.utils.spatial; - -import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.List; -import java.util.Map; - -public class OctTree { - private static final int SIZE = 8; - private static final int DEFAULT_NODE_CAPACITY = 4; - private final OctTree.Node root; - private final int nodeCapacity; - - public OctTree(double inradius) { - this(new Box(-inradius, -inradius, -inradius, inradius, inradius, inradius), 4); - } - - public OctTree(Box boundary) { - this(boundary, 4); - } - - public OctTree(Box boundary, int nodeCapacity) { - this.root = new OctTree.Node(boundary); - this.nodeCapacity = nodeCapacity; - } - - public void add(Vector3d pos, T value) { - this.add(this.root, pos, value); - } - - private boolean add(OctTree.Node node, Vector3d pos, T value) { - if (node != null && node.boundary.containsPosition(pos)) { - if (node.size() < this.nodeCapacity) { - node.addPoint(pos, value); - return true; - } else { - if (node.dirs.isEmpty()) { - this.subdivide(node); - } - - for (int i = 0; i < 8; i++) { - if (this.add(node.dirs.get(i), pos, value)) { - return true; - } - } - - return false; - } - } else { - return false; - } - } - - private void subdivide(OctTree.Node node) { - Vector3d min = node.boundary.min; - double side = node.boundary.width() / 2.0; - - for (int i = 0; i < 8; i++) { - Vector3d subMin = new Vector3d(min.x + ((i & 1) > 0 ? side : 0.0), min.y + ((i & 2) > 0 ? side : 0.0), min.z + ((i & 4) > 0 ? side : 0.0)); - Box sub = Box.cube(subMin, side); - node.dirs.add(new OctTree.Node(sub)); - } - } - - public Map getAllPoints() { - return this.queryRange(this.root.boundary); - } - - public Map queryRange(Vector3d position, double inradius) { - Box range = Box.centeredCube(position, inradius); - return this.queryRange(range); - } - - public Map queryRange(Box range) { - Map out = new Object2ObjectOpenHashMap<>(); - this.queryRange(this.root, range, out); - return out; - } - - private void queryRange(OctTree.Node node, Box range, Map out) { - if (node != null && node.boundary.isIntersecting(range)) { - for (int i = 0; i < node.size(); i++) { - Vector3d point = node.points[i]; - if (range.containsPosition(point)) { - T value = node.values.get(i); - out.put(value, point); - } - } - - for (OctTree.Node dir : node.dirs) { - this.queryRange(dir, range, out); - } - } - } - - private class Node { - private final Box boundary; - private final List.Node> dirs = new ObjectArrayList<>(8); - private final Vector3d[] points = new Vector3d[OctTree.this.nodeCapacity]; - private final List values = new ObjectArrayList<>(OctTree.this.nodeCapacity); - private int count; - - public Node(Box boundary) { - this.boundary = boundary; - } - - public int size() { - return this.count; - } - - public void addPoint(Vector3d pos, T value) { - this.points[this.count++] = pos; - this.values.add(value); - } - } -} diff --git a/src/com/hypixel/hytale/builtin/portals/utils/spatial/SpatialHashGrid.java b/src/com/hypixel/hytale/builtin/portals/utils/spatial/SpatialHashGrid.java index 5c7ee0a1..9b236739 100644 --- a/src/com/hypixel/hytale/builtin/portals/utils/spatial/SpatialHashGrid.java +++ b/src/com/hypixel/hytale/builtin/portals/utils/spatial/SpatialHashGrid.java @@ -1,8 +1,6 @@ package com.hypixel.hytale.builtin.portals.utils.spatial; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Collection; @@ -10,9 +8,13 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; public class SpatialHashGrid { private final double cellSize; @@ -23,8 +25,8 @@ public class SpatialHashGrid { this.cellSize = cellSize; } - private Vector3i cellFor(Vector3d p) { - return new Vector3i(MathUtil.floor(p.x / this.cellSize), MathUtil.floor(p.y / this.cellSize), MathUtil.floor(p.z / this.cellSize)); + private Vector3i cellFor(Vector3dc p) { + return new Vector3i(MathUtil.floor(p.x() / this.cellSize), MathUtil.floor(p.y() / this.cellSize), MathUtil.floor(p.z() / this.cellSize)); } public Collection getAll() { @@ -41,7 +43,7 @@ public class SpatialHashGrid { public void add(Vector3d pos, T value) { Vector3i cell = this.cellFor(pos); - SpatialHashGrid.Entry entry = new SpatialHashGrid.Entry<>(pos.clone(), cell, value); + SpatialHashGrid.Entry entry = new SpatialHashGrid.Entry<>(new Vector3d(pos), cell, value); this.index.put(value, entry); this.grid.computeIfAbsent(cell, x -> new ObjectArrayList<>()).add(entry); } @@ -82,7 +84,7 @@ public class SpatialHashGrid { if (entry != null) { Vector3i oldCell = entry.cell; Vector3i newCell = this.cellFor(newPos); - entry.pos.assign(newPos); + entry.pos.set(newPos); if (!oldCell.equals(newCell)) { List> oldBucket = this.grid.get(oldCell); oldBucket.remove(entry); @@ -101,7 +103,7 @@ public class SpatialHashGrid { double radiusSq = radius * radius; this.query(center, radius, bucket -> { for (SpatialHashGrid.Entry entry : bucket) { - if (entry.pos.distanceSquaredTo(center) <= radiusSq) { + if (entry.pos.distanceSquared(center) <= radiusSq) { out.put(entry.value, entry.pos); } } @@ -114,13 +116,19 @@ public class SpatialHashGrid { @Nullable public T findClosest(final Vector3d center, double searchRadius) { var closestVisitor = new SpatialHashGrid.CellVisitor() { - double closestDist = Double.MAX_VALUE; - T closest = (T)null; + double closestDist; + T closest; + + { + Objects.requireNonNull(SpatialHashGrid.this); + this.closestDist = Double.MAX_VALUE; + this.closest = null; + } @Override public boolean visit(List> bucket) { for (SpatialHashGrid.Entry entry : bucket) { - double dist = entry.pos.distanceSquaredTo(center); + double dist = entry.pos.distanceSquared(center); if (dist <= this.closestDist) { this.closestDist = dist; this.closest = entry.value; @@ -136,13 +144,19 @@ public class SpatialHashGrid { public boolean hasAnyWithin(final Vector3d center, final double radius) { var withinVisitor = new SpatialHashGrid.CellVisitor() { - final double radiusSq = radius * radius; - boolean hasWithin = false; + final double radiusSq; + boolean hasWithin; + + { + Objects.requireNonNull(SpatialHashGrid.this); + this.radiusSq = radius * radius; + this.hasWithin = false; + } @Override public boolean visit(List> bucket) { for (SpatialHashGrid.Entry entry : bucket) { - double dist = entry.pos.distanceSquaredTo(center); + double dist = entry.pos.distanceSquared(center); if (dist <= this.radiusSq) { this.hasWithin = true; return false; @@ -168,7 +182,7 @@ public class SpatialHashGrid { for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { - lookup.assign(x, y, z); + lookup.set(x, y, z); List> bucket = this.grid.get(lookup); if (bucket != null) { boolean keepGoing = visitor.visit(bucket); diff --git a/src/com/hypixel/hytale/builtin/randomtick/procedures/SpreadToProcedure.java b/src/com/hypixel/hytale/builtin/randomtick/procedures/SpreadToProcedure.java index a2e1724a..4a5ecace 100644 --- a/src/com/hypixel/hytale/builtin/randomtick/procedures/SpreadToProcedure.java +++ b/src/com/hypixel/hytale/builtin/randomtick/procedures/SpreadToProcedure.java @@ -10,7 +10,7 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.DrawType; import com.hypixel.hytale.server.core.asset.type.blocktick.config.RandomTickProcedure; @@ -19,11 +19,12 @@ import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import it.unimi.dsi.fastutil.ints.IntSet; +import org.joml.Vector3i; public class SpreadToProcedure implements RandomTickProcedure { public static final BuilderCodec CODEC = BuilderCodec.builder(SpreadToProcedure.class, SpreadToProcedure::new) .appendInherited( - new KeyedCodec<>("SpreadDirections", new ArrayCodec<>(Vector3i.CODEC, Vector3i[]::new)), + new KeyedCodec<>("SpreadDirections", new ArrayCodec<>(Vector3iUtil.CODEC, Vector3i[]::new)), (o, i) -> o.spreadDirections = i, o -> o.spreadDirections, (o, p) -> o.spreadDirections = p.spreadDirections @@ -104,10 +105,7 @@ public class SpreadToProcedure implements RandomTickProcedure { double sunlightFactor = worldTimeResource.getSunlightFactor(); BlockSection aboveSection = blockSection; if (!ChunkUtil.isSameChunkSection(worldX, worldY, worldZ, worldX, worldY + 1, worldZ)) { - Ref aboveChunk = store.getExternalData() - .getChunkSectionReference( - commandBuffer, ChunkUtil.chunkCoordinate(worldX), ChunkUtil.chunkCoordinate(worldY + 1), ChunkUtil.chunkCoordinate(worldZ) - ); + Ref aboveChunk = store.getExternalData().getChunkSectionReferenceAtBlock(worldX, worldY + 1, worldZ); if (aboveChunk == null) { return; } @@ -144,10 +142,7 @@ public class SpreadToProcedure implements RandomTickProcedure { int targetZ = worldZ + direction.z; BlockSection targetBlockSection = blockSection; if (!ChunkUtil.isSameChunkSection(worldX, worldY, worldZ, targetX, targetY, targetZ)) { - Ref otherChunk = store.getExternalData() - .getChunkSectionReference( - commandBuffer, ChunkUtil.chunkCoordinate(targetX), ChunkUtil.chunkCoordinate(targetY), ChunkUtil.chunkCoordinate(targetZ) - ); + Ref otherChunk = store.getExternalData().getChunkSectionReferenceAtBlock(targetX, targetY, targetZ); if (otherChunk == null) { continue; } @@ -166,10 +161,7 @@ public class SpreadToProcedure implements RandomTickProcedure { if (ChunkUtil.isSameChunkSection(targetX, targetY, targetZ, targetX, targetY + 1, targetZ)) { aboveTargetBlockId = targetBlockSection.get(ChunkUtil.indexBlock(targetX, targetY + 1, targetZ)); } else { - Ref aboveChunkx = store.getExternalData() - .getChunkSectionReference( - commandBuffer, ChunkUtil.chunkCoordinate(targetX), ChunkUtil.chunkCoordinate(targetY + 1), ChunkUtil.chunkCoordinate(targetZ) - ); + Ref aboveChunkx = store.getExternalData().getChunkSectionReferenceAtBlock(targetX, targetY + 1, targetZ); if (aboveChunkx == null) { continue; } diff --git a/src/com/hypixel/hytale/builtin/teleport/TeleportPlugin.java b/src/com/hypixel/hytale/builtin/teleport/TeleportPlugin.java index 11350cf0..0251ef0c 100644 --- a/src/com/hypixel/hytale/builtin/teleport/TeleportPlugin.java +++ b/src/com/hypixel/hytale/builtin/teleport/TeleportPlugin.java @@ -15,8 +15,8 @@ import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; @@ -50,11 +50,11 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import javax.annotation.Nonnull; import org.bson.BsonArray; import org.bson.BsonDocument; +import org.joml.Vector3d; public class TeleportPlugin extends JavaPlugin { private static TeleportPlugin instance; @@ -64,10 +64,6 @@ public class TeleportPlugin extends JavaPlugin { @Nonnull private final AtomicBoolean loaded = new AtomicBoolean(); @Nonnull - private final ReentrantLock saveLock = new ReentrantLock(); - @Nonnull - private final AtomicBoolean postSaveRedo = new AtomicBoolean(false); - @Nonnull private final Map warps = new ConcurrentHashMap<>(); private Model warpModel; @@ -155,30 +151,17 @@ public class TeleportPlugin extends JavaPlugin { this.loaded.set(true); } - private void saveWarps0() { - Warp[] array = this.warps.values().toArray(Warp[]::new); - BsonDocument document = new BsonDocument("Warps", Warp.ARRAY_CODEC.encode(array)); - Path path = Universe.get().getPath().resolve("warps.json"); - BsonUtil.writeDocument(path, document).join(); - this.getLogger().at(Level.INFO).log("Saved %d warps to warps.json", array.length); - } - public void saveWarps() { - if (this.saveLock.tryLock()) { - try { - this.saveWarps0(); - } catch (Throwable var5) { - this.getLogger().at(Level.SEVERE).withCause(var5).log("Failed to save warps:"); - } finally { - this.saveLock.unlock(); - } - - if (this.postSaveRedo.getAndSet(false)) { - this.saveWarps(); - } - } else { - this.postSaveRedo.set(true); - } + Path path = Universe.get().getPath().resolve("warps.json"); + Universe.get().getStorageManager().doSave(path, () -> { + Warp[] array = this.warps.values().toArray(Warp[]::new); + BsonDocument document = new BsonDocument("Warps", Warp.ARRAY_CODEC.encode(array)); + this.getLogger().at(Level.INFO).log("Saving %d warps to warps.json", array.length); + return BsonUtil.writeDocument(path, document); + }).exceptionally(e -> { + this.getLogger().at(Level.SEVERE).withCause(e).log("Failed to save warps"); + return null; + }); } public Map getWarps() { @@ -258,7 +241,7 @@ public class TeleportPlugin extends JavaPlugin { for (Warp warp : warps.values()) { if (warp.getWorld().equals(world.getName())) { MapMarker marker = new MapMarkerBuilder("Warp-" + warp.getId(), "Warp.png", warp.getTransform()) - .withCustomName("Warp: " + warp.getId()) + .withName(Message.translation("server.map.warp").param("warpName", warp.getId())) .build(); collector.add(marker); } diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/SpawnCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/SpawnCommand.java index 66436114..ae249e2f 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/SpawnCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/SpawnCommand.java @@ -3,9 +3,8 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -24,6 +23,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpawnCommand extends AbstractPlayerCommand { @Nonnull @@ -50,16 +50,14 @@ public class SpawnCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3d previousPos = transformComponent.getPosition().clone(); - Vector3f previousRotation = headRotationComponent.getRotation().clone(); + Vector3d previousPos = new Vector3d(transformComponent.getPosition()); + Rotation3f previousRotation = new Rotation3f(headRotationComponent.getRotation()); TeleportHistory teleportHistoryComponent = store.ensureAndGetComponent(ref, TeleportHistory.getComponentType()); teleportHistoryComponent.append(world, previousPos, previousRotation, "World " + world.getName() + "'s spawn"); Teleport teleportComponent = Teleport.createForPlayer(world, spawnTransform); store.addComponent(ref, Teleport.getComponentType(), teleportComponent); Vector3d position = spawnTransform.getPosition(); - context.sendMessage( - Message.translation("server.commands.spawn.teleported").param("x", position.getX()).param("y", position.getY()).param("z", position.getZ()) - ); + context.sendMessage(Message.translation("server.commands.spawn.teleported").param("x", position.x()).param("y", position.y()).param("z", position.z())); } private static Transform resolveSpawn( @@ -121,8 +119,8 @@ public class SpawnCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3d previousPos = transformComponent.getPosition().clone(); - Vector3f previousRotation = headRotationComponent.getRotation().clone(); + Vector3d previousPos = new Vector3d(transformComponent.getPosition()); + Rotation3f previousRotation = new Rotation3f(headRotationComponent.getRotation()); TeleportHistory teleportHistoryComponent = store.ensureAndGetComponent(ref, TeleportHistory.getComponentType()); teleportHistoryComponent.append(world, previousPos, previousRotation, "World " + world.getName() + "'s spawn"); Teleport teleportComponent = Teleport.createForPlayer(world, spawn); @@ -131,9 +129,9 @@ public class SpawnCommand extends AbstractPlayerCommand { context.sendMessage( Message.translation("server.commands.spawn.teleportedOther") .param("username", targetPlayerRef.getUsername()) - .param("x", position.getX()) - .param("y", position.getY()) - .param("z", position.getZ()) + .param("x", position.x()) + .param("y", position.y()) + .param("z", position.z()) ); } } diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/SpawnSetCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/SpawnSetCommand.java index 4b275b9f..e0eb6a65 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/SpawnSetCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/SpawnSetCommand.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg; @@ -22,6 +22,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.text.DecimalFormat; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpawnSetCommand extends AbstractWorldCommand { @Nonnull @@ -33,8 +34,8 @@ public class SpawnSetCommand extends AbstractWorldCommand { "position", "server.commands.spawn.set.position.desc", ArgTypes.RELATIVE_POSITION ); @Nonnull - private final DefaultArg rotationArg = this.withDefaultArg( - "rotation", "server.commands.spawn.set.rotation.desc", ArgTypes.ROTATION, Vector3f.FORWARD, "server.commands.spawn.set.rotation.default.desc" + private final DefaultArg rotationArg = this.withDefaultArg( + "rotation", "server.commands.spawn.set.rotation.desc", ArgTypes.ROTATION, Rotation3f.IDENTITY, "server.commands.spawn.set.rotation.default.desc" ); public SpawnSetCommand() { @@ -61,10 +62,10 @@ public class SpawnSetCommand extends AbstractWorldCommand { assert transformComponent != null; - position = transformComponent.getPosition().clone(); + position = new Vector3d(transformComponent.getPosition()); } - Vector3f rotation; + Rotation3fc rotation; if (this.rotationArg.provided(context)) { rotation = this.rotationArg.get(context); } else if (context.isPlayer()) { @@ -82,19 +83,19 @@ public class SpawnSetCommand extends AbstractWorldCommand { rotation = this.rotationArg.get(context); } - Transform spawnTransform = new Transform(position.clone(), rotation.clone()); + Transform spawnTransform = new Transform(new Vector3d(position), new Rotation3f(rotation)); WorldConfig worldConfig = world.getWorldConfig(); worldConfig.setSpawnProvider(new GlobalSpawnProvider(spawnTransform)); worldConfig.markChanged(); world.getLogger().at(Level.INFO).log("Set spawn provider to: %s", worldConfig.getSpawnProvider()); context.sendMessage( Message.translation("server.universe.setspawn.info") - .param("posX", DECIMAL.format(position.getX())) - .param("posY", DECIMAL.format(position.getY())) - .param("posZ", DECIMAL.format(position.getZ())) - .param("rotX", DECIMAL.format(rotation.getX())) - .param("rotY", DECIMAL.format(rotation.getY())) - .param("rotZ", DECIMAL.format(rotation.getZ())) + .param("posX", DECIMAL.format(position.x())) + .param("posY", DECIMAL.format(position.y())) + .param("posZ", DECIMAL.format(position.z())) + .param("rotX", DECIMAL.format(rotation.x())) + .param("rotY", DECIMAL.format(rotation.y())) + .param("rotZ", DECIMAL.format(rotation.z())) ); } } diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportAllCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportAllCommand.java index 7d3703c1..41595484 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportAllCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportAllCommand.java @@ -3,8 +3,7 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -23,6 +22,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.NotificationUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportAllCommand extends CommandBase { @Nonnull @@ -86,9 +86,9 @@ public class TeleportAllCommand extends CommandBase { TransformComponent transformComponent = senderStore.getComponent(senderRefx, TransformComponent.getComponentType()); if (transformComponent != null) { Vector3d pos = transformComponent.getPosition(); - baseX = pos.getX(); - baseY = pos.getY(); - baseZ = pos.getZ(); + baseX = pos.x(); + baseY = pos.y(); + baseZ = pos.z(); } } } @@ -105,22 +105,20 @@ public class TeleportAllCommand extends CommandBase { TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); if (transformComponent != null && headRotationComponent != null) { - Vector3d previousPos = transformComponent.getPosition().clone(); - Vector3f previousHeadRotation = headRotationComponent.getRotation().clone(); - Vector3f previousBodyRotation = transformComponent.getRotation().clone(); + Vector3d previousPos = new Vector3d(transformComponent.getPosition()); + Rotation3f previousHeadRotation = new Rotation3f(headRotationComponent.getRotation()); + Rotation3f previousBodyRotation = new Rotation3f(transformComponent.getRotation()); float yaw = this.yawArg.provided(context) - ? this.yawArg.get(context).resolve(previousHeadRotation.getYaw() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.yawArg.get(context).resolve(previousHeadRotation.yaw() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; float pitch = this.pitchArg.provided(context) - ? this.pitchArg.get(context).resolve(previousHeadRotation.getPitch() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.pitchArg.get(context).resolve(previousHeadRotation.pitch() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; float roll = this.rollArg.provided(context) - ? this.rollArg.get(context).resolve(previousHeadRotation.getRoll() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.rollArg.get(context).resolve(previousHeadRotation.roll() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; Teleport teleport = Teleport.createExact( - new Vector3d(x, y, z), - new Vector3f(previousBodyRotation.getPitch(), yaw, previousBodyRotation.getRoll()), - new Vector3f(pitch, yaw, roll) + new Vector3d(x, y, z), new Rotation3f(previousBodyRotation.pitch(), yaw, previousBodyRotation.roll()), new Rotation3f(pitch, yaw, roll) ); store.addComponent(ref, Teleport.getComponentType(), teleport); Player playerComponent = store.getComponent(ref, Player.getComponentType()); @@ -134,16 +132,14 @@ public class TeleportAllCommand extends CommandBase { targetWorld, previousPos, previousHeadRotation, - String.format("Teleport to (%s, %s, %s) by %s", x, y, z, context.sender().getDisplayName()) + String.format("Teleport to (%s, %s, %s) by %s", x, y, z, context.sender().getUsername()) ); if (hasRotation) { - float displayYaw = Float.isNaN(yaw) ? previousHeadRotation.getYaw() * (180.0F / (float)Math.PI) : yaw * (180.0F / (float)Math.PI); + float displayYaw = Float.isNaN(yaw) ? previousHeadRotation.yaw() * (180.0F / (float)Math.PI) : yaw * (180.0F / (float)Math.PI); float displayPitch = Float.isNaN(pitch) - ? previousHeadRotation.getPitch() * (180.0F / (float)Math.PI) + ? previousHeadRotation.pitch() * (180.0F / (float)Math.PI) : pitch * (180.0F / (float)Math.PI); - float displayRoll = Float.isNaN(roll) - ? previousHeadRotation.getRoll() * (180.0F / (float)Math.PI) - : roll * (180.0F / (float)Math.PI); + float displayRoll = Float.isNaN(roll) ? previousHeadRotation.roll() * (180.0F / (float)Math.PI) : roll * (180.0F / (float)Math.PI); NotificationUtil.sendNotification( playerRefComponent.getPacketHandler(), Message.translation("server.commands.teleport.teleportedWithLookNotification") @@ -153,7 +149,7 @@ public class TeleportAllCommand extends CommandBase { .param("yaw", displayYaw) .param("pitch", displayPitch) .param("roll", displayRoll) - .param("sender", context.sender().getDisplayName()), + .param("sender", context.sender().getUsername()), null, "teleportation" ); @@ -164,7 +160,7 @@ public class TeleportAllCommand extends CommandBase { .param("x", x) .param("y", y) .param("z", z) - .param("sender", context.sender().getDisplayName()), + .param("sender", context.sender().getUsername()), null, "teleportation" ); diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportHomeCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportHomeCommand.java index a2c57749..6192c217 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportHomeCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportHomeCommand.java @@ -3,8 +3,7 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; @@ -17,6 +16,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportHomeCommand extends AbstractPlayerCommand { @Nonnull @@ -39,8 +39,8 @@ public class TeleportHomeCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3d previousPos = transformComponent.getPosition().clone(); - Vector3f previousHeadRotation = headRotationComponent.getRotation().clone(); + Vector3d previousPos = new Vector3d(transformComponent.getPosition()); + Rotation3f previousHeadRotation = new Rotation3f(headRotationComponent.getRotation()); TeleportHistory teleportHistoryComponent = store.ensureAndGetComponent(ref, TeleportHistory.getComponentType()); teleportHistoryComponent.append(world, previousPos, previousHeadRotation, "Home"); Player.getRespawnPosition(ref, world.getName(), store).thenAcceptAsync(homeTransform -> { diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportTopCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportTopCommand.java index 832bfb34..7709c9b2 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportTopCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportTopCommand.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportTopCommand extends AbstractPlayerCommand { @Nonnull @@ -41,7 +41,7 @@ public class TeleportTopCommand extends AbstractPlayerCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - WorldChunk worldChunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(position.getX(), position.getZ())); + WorldChunk worldChunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(position.x(), position.z())); if (worldChunk == null) { context.sendMessage(MESSAGE_COMMANDS_TELEPORT_TOP_CHUNK_NOT_LOADED_AT_POS); } else { @@ -49,12 +49,11 @@ public class TeleportTopCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation().clone(); - int height = worldChunk.getHeight(MathUtil.floor(position.getX()), MathUtil.floor(position.getZ())); - store.ensureAndGetComponent(ref, TeleportHistory.getComponentType()).append(world, position.clone(), headRotation.clone(), "Underground"); - store.addComponent( - ref, Teleport.getComponentType(), Teleport.createForPlayer(new Vector3d(position.getX(), height + 2, position.getZ()), Vector3f.NaN) - ); + Rotation3f headRotation = new Rotation3f(headRotationComponent.getRotation()); + int height = worldChunk.getHeight(MathUtil.floor(position.x()), MathUtil.floor(position.z())); + store.ensureAndGetComponent(ref, TeleportHistory.getComponentType()) + .append(world, new Vector3d(position), new Rotation3f(headRotation), "Underground"); + store.addComponent(ref, Teleport.getComponentType(), Teleport.createForPlayer(new Vector3d(position.x(), height + 2, position.z()), Rotation3f.NaN)); context.sendMessage(MESSAGE_COMMANDS_TELEPORT_TELEPORTED_TO_TOP); } } diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportWorldCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportWorldCommand.java index 2db6df84..948211a6 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportWorldCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/TeleportWorldCommand.java @@ -3,9 +3,8 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -16,14 +15,14 @@ import com.hypixel.hytale.server.core.modules.entity.component.TransformComponen import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport; import com.hypixel.hytale.server.core.permissions.HytalePermissions; import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportWorldCommand extends AbstractPlayerCommand { @Nonnull - private final RequiredArg worldNameArg = this.withRequiredArg("worldName", "server.commands.worldport.worldName.desc", ArgTypes.STRING); + private final RequiredArg worldNameArg = this.withRequiredArg("worldName", "server.commands.worldport.worldName.desc", ArgTypes.WORLD); public TeleportWorldCommand() { super("world", "server.commands.worldport.desc"); @@ -35,35 +34,30 @@ public class TeleportWorldCommand extends AbstractPlayerCommand { protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - String worldName = this.worldNameArg.get(context); - World targetWorld = Universe.get().getWorld(worldName); - if (targetWorld == null) { - context.sendMessage(Message.translation("server.world.notFound").param("worldName", worldName)); + World targetWorld = this.worldNameArg.get(context); + Transform spawnPoint = targetWorld.getWorldConfig().getSpawnProvider().getSpawnPoint(ref, store); + if (spawnPoint == null) { + context.sendMessage(Message.translation("server.world.spawn.notSet").param("worldName", targetWorld.getName())); } else { - Transform spawnPoint = targetWorld.getWorldConfig().getSpawnProvider().getSpawnPoint(ref, store); - if (spawnPoint == null) { - context.sendMessage(Message.translation("server.world.spawn.notSet").param("worldName", worldName)); - } else { - TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); - HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); - if (transformComponent != null && headRotationComponent != null) { - Vector3d previousPos = transformComponent.getPosition().clone(); - Vector3f previousRotation = headRotationComponent.getRotation().clone(); - TeleportHistory teleportHistoryComponent = store.ensureAndGetComponent(ref, TeleportHistory.getComponentType()); - teleportHistoryComponent.append(world, previousPos, previousRotation, "World " + targetWorld.getName()); - } - - Teleport teleportComponent = Teleport.createForPlayer(targetWorld, spawnPoint); - store.addComponent(ref, Teleport.getComponentType(), teleportComponent); - Vector3d spawnPos = spawnPoint.getPosition(); - context.sendMessage( - Message.translation("server.commands.teleport.teleportedToWorld") - .param("worldName", worldName) - .param("x", spawnPos.getX()) - .param("y", spawnPos.getY()) - .param("z", spawnPos.getZ()) - ); + TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); + HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); + if (transformComponent != null && headRotationComponent != null) { + Vector3d previousPos = new Vector3d(transformComponent.getPosition()); + Rotation3f previousRotation = new Rotation3f(headRotationComponent.getRotation()); + TeleportHistory teleportHistoryComponent = store.ensureAndGetComponent(ref, TeleportHistory.getComponentType()); + teleportHistoryComponent.append(world, previousPos, previousRotation, "World " + targetWorld.getName()); } + + Teleport teleportComponent = Teleport.createForPlayer(targetWorld, spawnPoint); + store.addComponent(ref, Teleport.getComponentType(), teleportComponent); + Vector3d spawnPos = spawnPoint.getPosition(); + context.sendMessage( + Message.translation("server.commands.teleport.teleportedToWorld") + .param("worldName", targetWorld.getName()) + .param("x", spawnPos.x()) + .param("y", spawnPos.y()) + .param("z", spawnPos.z()) + ); } } } diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportOtherToPlayerCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportOtherToPlayerCommand.java index 1a0143eb..4ce6fec6 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportOtherToPlayerCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportOtherToPlayerCommand.java @@ -3,9 +3,8 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport.variant; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportOtherToPlayerCommand extends CommandBase { @Nonnull @@ -64,8 +64,8 @@ public class TeleportOtherToPlayerCommand extends CommandBase { assert headRotationComponent != null; - Vector3d pos = transformComponent.getPosition().clone(); - Vector3f rotation = headRotationComponent.getRotation().clone(); + Vector3d pos = new Vector3d(transformComponent.getPosition()); + Rotation3f rotation = new Rotation3f(headRotationComponent.getRotation()); targetWorld.execute( () -> { TransformComponent targetTransformComponent = targetStore.getComponent(targetRef, TransformComponent.getComponentType()); @@ -76,8 +76,8 @@ public class TeleportOtherToPlayerCommand extends CommandBase { assert targetHeadRotationComponent != null; - Vector3d targetPosition = targetTransformComponent.getPosition().clone(); - Vector3f targetHeadRotation = targetHeadRotationComponent.getRotation().clone(); + Vector3d targetPosition = new Vector3d(targetTransformComponent.getPosition()); + Rotation3f targetHeadRotation = new Rotation3f(targetHeadRotationComponent.getRotation()); Transform targetTransform = new Transform(targetPosition, targetHeadRotation); sourceWorld.execute( () -> { @@ -106,7 +106,7 @@ public class TeleportOtherToPlayerCommand extends CommandBase { sourceWorld, pos, rotation, - "Teleport to " + targetPlayerRefComponent.getUsername() + " by " + context.sender().getDisplayName() + "Teleport to " + targetPlayerRefComponent.getUsername() + " by " + context.sender().getUsername() ); } } diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportPlayerToCoordinatesCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportPlayerToCoordinatesCommand.java index 9543ce8a..eca346ac 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportPlayerToCoordinatesCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportPlayerToCoordinatesCommand.java @@ -3,8 +3,7 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport.variant; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportPlayerToCoordinatesCommand extends CommandBase { @Nonnull @@ -29,7 +29,8 @@ public class TeleportPlayerToCoordinatesCommand extends CommandBase { @Nonnull private final RequiredArg playerArg = this.withRequiredArg("player", "server.commands.teleport.targetPlayer.desc", ArgTypes.PLAYER_REF); @Nonnull - private final RequiredArg xArg = this.withRequiredArg("x", "server.commands.teleport.x.desc", ArgTypes.RELATIVE_DOUBLE_COORD); + private final RequiredArg xArg = this.withRequiredArg("x", "server.commands.teleport.x.desc", ArgTypes.RELATIVE_DOUBLE_COORD) + .withSuggestionOverride(ArgTypes.RELATIVE_POSITION); @Nonnull private final RequiredArg yArg = this.withRequiredArg("y", "server.commands.teleport.y.desc", ArgTypes.RELATIVE_DOUBLE_COORD); @Nonnull @@ -63,26 +64,26 @@ public class TeleportPlayerToCoordinatesCommand extends CommandBase { assert headRotationComponent != null; - Vector3d previousPos = transformComponent.getPosition().clone(); - Vector3f previousHeadRotation = headRotationComponent.getRotation().clone(); - Vector3f previousBodyRotation = transformComponent.getRotation().clone(); + Vector3d previousPos = new Vector3d(transformComponent.getPosition()); + Rotation3f previousHeadRotation = new Rotation3f(headRotationComponent.getRotation()); + Rotation3f previousBodyRotation = new Rotation3f(transformComponent.getRotation()); Coord relX = this.xArg.get(context); Coord relY = this.yArg.get(context); Coord relZ = this.zArg.get(context); - double x = relX.resolveXZ(previousPos.getX()); - double z = relZ.resolveXZ(previousPos.getZ()); - double y = relY.resolveYAtWorldCoords(previousPos.getY(), targetWorld, x, z); + double x = relX.resolveXZ(previousPos.x()); + double z = relZ.resolveXZ(previousPos.z()); + double y = relY.resolveYAtWorldCoords(previousPos.y(), targetWorld, x, z); float yaw = this.yawArg.provided(context) - ? this.yawArg.get(context).resolve(previousHeadRotation.getYaw() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.yawArg.get(context).resolve(previousHeadRotation.yaw() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; float pitch = this.pitchArg.provided(context) - ? this.pitchArg.get(context).resolve(previousHeadRotation.getPitch() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.pitchArg.get(context).resolve(previousHeadRotation.pitch() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; float roll = this.rollArg.provided(context) - ? this.rollArg.get(context).resolve(previousHeadRotation.getRoll() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.rollArg.get(context).resolve(previousHeadRotation.roll() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; Teleport teleport = Teleport.createExact( - new Vector3d(x, y, z), new Vector3f(previousBodyRotation.getPitch(), yaw, previousBodyRotation.getRoll()), new Vector3f(pitch, yaw, roll) + new Vector3d(x, y, z), new Rotation3f(previousBodyRotation.pitch(), yaw, previousBodyRotation.roll()), new Rotation3f(pitch, yaw, roll) ); store.addComponent(ref, Teleport.getComponentType(), teleport); Player player = store.getComponent(ref, Player.getComponentType()); @@ -93,9 +94,9 @@ public class TeleportPlayerToCoordinatesCommand extends CommandBase { boolean hasRotation = this.yawArg.provided(context) || this.pitchArg.provided(context) || this.rollArg.provided(context); if (hasRotation) { - float displayYaw = Float.isNaN(yaw) ? previousHeadRotation.getYaw() * (180.0F / (float)Math.PI) : yaw * (180.0F / (float)Math.PI); - float displayPitch = Float.isNaN(pitch) ? previousHeadRotation.getPitch() * (180.0F / (float)Math.PI) : pitch * (180.0F / (float)Math.PI); - float displayRoll = Float.isNaN(roll) ? previousHeadRotation.getRoll() * (180.0F / (float)Math.PI) : roll * (180.0F / (float)Math.PI); + float displayYaw = Float.isNaN(yaw) ? previousHeadRotation.yaw() * (180.0F / (float)Math.PI) : yaw * (180.0F / (float)Math.PI); + float displayPitch = Float.isNaN(pitch) ? previousHeadRotation.pitch() * (180.0F / (float)Math.PI) : pitch * (180.0F / (float)Math.PI); + float displayRoll = Float.isNaN(roll) ? previousHeadRotation.roll() * (180.0F / (float)Math.PI) : roll * (180.0F / (float)Math.PI); context.sendMessage( Message.translation("server.commands.teleport.teleportedToCoordinatesWithLook") .param("x", x) diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportToCoordinatesCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportToCoordinatesCommand.java index 6cef2e5c..64a11640 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportToCoordinatesCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportToCoordinatesCommand.java @@ -3,8 +3,7 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport.variant; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -21,10 +20,12 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportToCoordinatesCommand extends AbstractPlayerCommand { @Nonnull - private final RequiredArg xArg = this.withRequiredArg("x", "server.commands.teleport.x.desc", ArgTypes.RELATIVE_DOUBLE_COORD); + private final RequiredArg xArg = this.withRequiredArg("x", "server.commands.teleport.x.desc", ArgTypes.RELATIVE_DOUBLE_COORD) + .withSuggestionOverride(ArgTypes.RELATIVE_POSITION); @Nonnull private final RequiredArg yArg = this.withRequiredArg("y", "server.commands.teleport.y.desc", ArgTypes.RELATIVE_DOUBLE_COORD); @Nonnull @@ -53,32 +54,32 @@ public class TeleportToCoordinatesCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3d previousPos = transformComponent.getPosition().clone(); - Vector3f previousHeadRotation = headRotationComponent.getRotation().clone(); - Vector3f previousBodyRotation = transformComponent.getRotation().clone(); + Vector3d previousPos = new Vector3d(transformComponent.getPosition()); + Rotation3f previousHeadRotation = new Rotation3f(headRotationComponent.getRotation()); + Rotation3f previousBodyRotation = new Rotation3f(transformComponent.getRotation()); Coord relX = this.xArg.get(context); Coord relY = this.yArg.get(context); Coord relZ = this.zArg.get(context); - double x = relX.resolveXZ(previousPos.getX()); - double z = relZ.resolveXZ(previousPos.getZ()); - double y = relY.resolveYAtWorldCoords(previousPos.getY(), world, x, z); + double x = relX.resolveXZ(previousPos.x()); + double z = relZ.resolveXZ(previousPos.z()); + double y = relY.resolveYAtWorldCoords(previousPos.y(), world, x, z); float yaw = this.yawArg.provided(context) - ? this.yawArg.get(context).resolve(previousHeadRotation.getYaw() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.yawArg.get(context).resolve(previousHeadRotation.yaw() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; float pitch = this.pitchArg.provided(context) - ? this.pitchArg.get(context).resolve(previousHeadRotation.getPitch() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.pitchArg.get(context).resolve(previousHeadRotation.pitch() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; float roll = this.rollArg.provided(context) - ? this.rollArg.get(context).resolve(previousHeadRotation.getRoll() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) + ? this.rollArg.get(context).resolve(previousHeadRotation.roll() * (180.0F / (float)Math.PI)) * (float) (Math.PI / 180.0) : Float.NaN; - Teleport teleport = Teleport.createForPlayer(new Vector3d(x, y, z), new Vector3f(previousBodyRotation.getPitch(), yaw, previousBodyRotation.getRoll())) - .setHeadRotation(new Vector3f(pitch, yaw, roll)); + Teleport teleport = Teleport.createForPlayer(new Vector3d(x, y, z), new Rotation3f(previousBodyRotation.pitch(), yaw, previousBodyRotation.roll())) + .setHeadRotation(new Rotation3f(pitch, yaw, roll)); store.addComponent(ref, Teleport.getComponentType(), teleport); boolean hasRotation = this.yawArg.provided(context) || this.pitchArg.provided(context) || this.rollArg.provided(context); if (hasRotation) { - float displayYaw = Float.isNaN(yaw) ? previousHeadRotation.getYaw() * (180.0F / (float)Math.PI) : yaw * (180.0F / (float)Math.PI); - float displayPitch = Float.isNaN(pitch) ? previousHeadRotation.getPitch() * (180.0F / (float)Math.PI) : pitch * (180.0F / (float)Math.PI); - float displayRoll = Float.isNaN(roll) ? previousHeadRotation.getRoll() * (180.0F / (float)Math.PI) : roll * (180.0F / (float)Math.PI); + float displayYaw = Float.isNaN(yaw) ? previousHeadRotation.yaw() * (180.0F / (float)Math.PI) : yaw * (180.0F / (float)Math.PI); + float displayPitch = Float.isNaN(pitch) ? previousHeadRotation.pitch() * (180.0F / (float)Math.PI) : pitch * (180.0F / (float)Math.PI); + float displayRoll = Float.isNaN(roll) ? previousHeadRotation.roll() * (180.0F / (float)Math.PI) : roll * (180.0F / (float)Math.PI); context.sendMessage( Message.translation("server.commands.teleport.teleportedToCoordinatesWithLook") .param("x", x) diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportToPlayerCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportToPlayerCommand.java index a988e5bb..10403354 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportToPlayerCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/teleport/variant/TeleportToPlayerCommand.java @@ -3,9 +3,8 @@ package com.hypixel.hytale.builtin.teleport.commands.teleport.variant; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportToPlayerCommand extends AbstractPlayerCommand { @Nonnull @@ -50,8 +50,8 @@ public class TeleportToPlayerCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3d pos = transformComponent.getPosition().clone(); - Vector3f rotation = headRotationComponent.getRotation().clone(); + Vector3d pos = new Vector3d(transformComponent.getPosition()); + Rotation3f rotation = new Rotation3f(headRotationComponent.getRotation()); targetWorld.execute( () -> { TransformComponent targetTransformComponent = targetStore.getComponent(targetRef, TransformComponent.getComponentType()); @@ -62,8 +62,8 @@ public class TeleportToPlayerCommand extends AbstractPlayerCommand { assert targetHeadRotationComponent != null; - Vector3d targetPosition = targetTransformComponent.getPosition().clone(); - Vector3f targetHeadRotation = targetHeadRotationComponent.getRotation().clone(); + Vector3d targetPosition = new Vector3d(targetTransformComponent.getPosition()); + Rotation3f targetHeadRotation = new Rotation3f(targetHeadRotationComponent.getRotation()); Transform targetTransform = new Transform(targetPosition, targetHeadRotation); world.execute( () -> { diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/warp/WarpCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/warp/WarpCommand.java index 631dc172..e7fda72a 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/warp/WarpCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/warp/WarpCommand.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.builtin.teleport.Warp; import com.hypixel.hytale.builtin.teleport.components.TeleportHistory; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; @@ -17,6 +16,7 @@ import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class WarpCommand extends AbstractCommandCollection { @Nonnull @@ -53,9 +53,9 @@ public class WarpCommand extends AbstractCommandCollection { assert headRotationComponent != null; Vector3d playerPosition = transformComponent.getPosition(); - Vector3f playerHeadRotation = headRotationComponent.getRotation(); + Rotation3f playerHeadRotation = headRotationComponent.getRotation(); store.ensureAndGetComponent(ref, TeleportHistory.getComponentType()) - .append(world, playerPosition.clone(), playerHeadRotation.clone(), "Warp '" + warp + "'"); + .append(world, new Vector3d(playerPosition), new Rotation3f(playerHeadRotation), "Warp '" + warp + "'"); store.addComponent(ref, Teleport.getComponentType(), teleportComponent); context.sendMessage(Message.translation("server.commands.teleport.warp.warpedTo").param("name", warp)); } else { diff --git a/src/com/hypixel/hytale/builtin/teleport/commands/warp/WarpSetCommand.java b/src/com/hypixel/hytale/builtin/teleport/commands/warp/WarpSetCommand.java index e8be71d9..4be20a50 100644 --- a/src/com/hypixel/hytale/builtin/teleport/commands/warp/WarpSetCommand.java +++ b/src/com/hypixel/hytale/builtin/teleport/commands/warp/WarpSetCommand.java @@ -5,9 +5,8 @@ import com.hypixel.hytale.builtin.teleport.Warp; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.time.Instant; import java.util.Map; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class WarpSetCommand extends AbstractPlayerCommand { @Nonnull @@ -55,8 +55,8 @@ public class WarpSetCommand extends AbstractPlayerCommand { assert headRotationComponent != null; Vector3d position = transformComponent.getPosition(); - Vector3f headRotation = headRotationComponent.getRotation(); - Transform transform = new Transform(position.clone(), headRotation.clone()); + Rotation3f headRotation = headRotationComponent.getRotation(); + Transform transform = new Transform(new Vector3d(position), new Rotation3f(headRotation)); Warp newWarp = new Warp(transform, newId, world, playerRef.getUsername(), Instant.now()); warps.put(newWarp.getId().toLowerCase(), newWarp); TeleportPlugin plugin = TeleportPlugin.get(); diff --git a/src/com/hypixel/hytale/builtin/teleport/components/TeleportHistory.java b/src/com/hypixel/hytale/builtin/teleport/components/TeleportHistory.java index 6f3b92f9..e846aa15 100644 --- a/src/com/hypixel/hytale/builtin/teleport/components/TeleportHistory.java +++ b/src/com/hypixel/hytale/builtin/teleport/components/TeleportHistory.java @@ -5,9 +5,10 @@ import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.Universe; @@ -16,11 +17,11 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.ArrayDeque; import java.util.Deque; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportHistory implements Component { private static final int MAX_TELEPORT_HISTORY = 100; private static final Message MESSAGE_COMMANDS_TELEPORT_NOT_FURTHER = Message.translation("server.commands.teleport.notFurther"); - private static final Message MESSAGE_COMMANDS_TELEPORT_WORLD_NOT_LOADED = Message.translation("server.commands.teleport.worldNotLoaded"); @Nonnull private final Deque back = new ArrayDeque<>(); @Nonnull @@ -64,9 +65,11 @@ public class TeleportHistory implements Component { assert playerRef != null; + Universe universe = Universe.get(); TeleportHistory.Waypoint point = null; + World targetWorld = null; - for (int i = 0; i < count; i++) { + for (int steps = 0; steps < count; steps++) { if (from.isEmpty()) { if (point == null) { playerRef.sendMessage(MESSAGE_COMMANDS_TELEPORT_NOT_FURTHER); @@ -75,53 +78,68 @@ public class TeleportHistory implements Component { break; } - point = from.pop(); - to.push(point); + TeleportHistory.Waypoint candidate = from.pop(); + World candidateWorld = universe.getWorld(candidate.world); + if (candidateWorld == null) { + playerRef.sendMessage(Message.translation("server.commands.teleport.worldRemoved").param("name", candidate.world)); + return; + } + + point = candidate; + targetWorld = candidateWorld; } if (point == null) { - throw new NullPointerException(to.toString()); + playerRef.sendMessage(MESSAGE_COMMANDS_TELEPORT_NOT_FURTHER); } else { - World targetWorld = Universe.get().getWorld(point.world); - if (targetWorld == null) { - playerRef.sendMessage(MESSAGE_COMMANDS_TELEPORT_WORLD_NOT_LOADED); + World currentWorld = store.getExternalData().getWorld(); + TransformComponent currentTransform = store.getComponent(ref, TransformComponent.getComponentType()); + HeadRotation currentHeadRotation = store.getComponent(ref, HeadRotation.getComponentType()); + if (currentTransform != null && currentHeadRotation != null) { + to.push( + new TeleportHistory.Waypoint( + currentWorld.getName(), + new Vector3d(currentTransform.getPosition()), + currentHeadRotation.getRotation().clone(), + point.message != null ? point.message : "" + ) + ); + } + + Teleport teleportComponent = Teleport.createForPlayer(targetWorld, point.position, point.rotation); + store.addComponent(ref, Teleport.getComponentType(), teleportComponent); + Vector3d pos = point.position; + int remainingInDirection = from.size(); + int totalInOtherDirection = to.size(); + if (point.message != null && !point.message.isEmpty()) { + playerRef.sendMessage( + (isForward + ? Message.translation("server.commands.teleport.teleportedForwardToWaypoint") + : Message.translation("server.commands.teleport.teleportedBackToWaypoint")) + .param("name", point.message) + .param("x", pos.x()) + .param("y", pos.y()) + .param("z", pos.z()) + .param("remaining", remainingInDirection) + .param("otherDirection", totalInOtherDirection) + ); } else { - to.push(point); - Teleport teleportComponent = Teleport.createForPlayer(targetWorld, point.position, point.rotation); - store.addComponent(ref, Teleport.getComponentType(), teleportComponent); - Vector3d pos = point.position; - int remainingInDirection = from.size(); - int totalInOtherDirection = to.size() - 1; - if (point.message != null && !point.message.isEmpty()) { - playerRef.sendMessage( - (isForward - ? Message.translation("server.commands.teleport.teleportedForwardToWaypoint") - : Message.translation("server.commands.teleport.teleportedBackToWaypoint")) - .param("name", point.message) - .param("x", pos.getX()) - .param("y", pos.getY()) - .param("z", pos.getZ()) - .param("remaining", remainingInDirection) - .param("otherDirection", totalInOtherDirection) - ); - } else { - playerRef.sendMessage( - (isForward - ? Message.translation("server.commands.teleport.teleportedForwardToCoordinates") - : Message.translation("server.commands.teleport.teleportedBackToCoordinates")) - .param("x", pos.getX()) - .param("y", pos.getY()) - .param("z", pos.getZ()) - .param("remaining", remainingInDirection) - .param("otherDirection", totalInOtherDirection) - ); - } + playerRef.sendMessage( + (isForward + ? Message.translation("server.commands.teleport.teleportedForwardToCoordinates") + : Message.translation("server.commands.teleport.teleportedBackToCoordinates")) + .param("x", pos.x()) + .param("y", pos.y()) + .param("z", pos.z()) + .param("remaining", remainingInDirection) + .param("otherDirection", totalInOtherDirection) + ); } } } } - public void append(@Nonnull World world, @Nonnull Vector3d pos, @Nonnull Vector3f rotation, @Nonnull String key) { + public void append(@Nonnull World world, @Nonnull Vector3d pos, @Nonnull Rotation3f rotation, @Nonnull String key) { this.back.push(new TeleportHistory.Waypoint(world.getName(), pos, rotation, key)); this.forward.clear(); @@ -148,10 +166,10 @@ public class TeleportHistory implements Component { public static class Waypoint { private final String world; private final Vector3d position; - private final Vector3f rotation; + private final Rotation3f rotation; private final String message; - public Waypoint(@Nonnull String world, @Nonnull Vector3d position, @Nonnull Vector3f rotation, @Nonnull String message) { + public Waypoint(@Nonnull String world, @Nonnull Vector3d position, @Nonnull Rotation3f rotation, @Nonnull String message) { this.world = world; this.position = position; this.rotation = rotation; diff --git a/src/com/hypixel/hytale/builtin/weather/components/WeatherTracker.java b/src/com/hypixel/hytale/builtin/weather/components/WeatherTracker.java index e87c5999..2e8745cc 100644 --- a/src/com/hypixel/hytale/builtin/weather/components/WeatherTracker.java +++ b/src/com/hypixel/hytale/builtin/weather/components/WeatherTracker.java @@ -8,8 +8,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.world.UpdateWeather; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.PlayerRef; @@ -18,6 +16,8 @@ import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class WeatherTracker implements Component { private final UpdateWeather updateWeather = new UpdateWeather(0, 10.0F); @@ -35,7 +35,7 @@ public class WeatherTracker implements Component { private WeatherTracker(@Nonnull WeatherTracker other) { this.environmentId = other.environmentId; this.updateWeather.weatherIndex = other.updateWeather.weatherIndex; - this.previousBlockPosition.assign(other.previousBlockPosition); + this.previousBlockPosition.set(other.previousBlockPosition); } public void updateWeather( @@ -83,10 +83,10 @@ public class WeatherTracker implements Component { public void updateEnvironment(@Nonnull TransformComponent transformComponent, @Nonnull ComponentAccessor componentAccessor) { Vector3d vector = transformComponent.getPosition(); - int blockX = MathUtil.floor(vector.getX()); - int blockY = MathUtil.floor(vector.getY()); - int blockZ = MathUtil.floor(vector.getZ()); - if (this.previousBlockPosition.getX() != blockX || this.previousBlockPosition.getY() != blockY || this.previousBlockPosition.getZ() != blockZ) { + int blockX = MathUtil.floor(vector.x()); + int blockY = MathUtil.floor(vector.y()); + int blockZ = MathUtil.floor(vector.z()); + if (this.previousBlockPosition.x() != blockX || this.previousBlockPosition.y() != blockY || this.previousBlockPosition.z() != blockZ) { Ref chunkRef = transformComponent.getChunkRef(); if (chunkRef == null || !chunkRef.isValid()) { return; diff --git a/src/com/hypixel/hytale/builtin/weather/systems/WeatherSystem.java b/src/com/hypixel/hytale/builtin/weather/systems/WeatherSystem.java index 4cfef55d..df52f623 100644 --- a/src/com/hypixel/hytale/builtin/weather/systems/WeatherSystem.java +++ b/src/com/hypixel/hytale/builtin/weather/systems/WeatherSystem.java @@ -22,6 +22,8 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.component.system.StoreSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; +import com.hypixel.hytale.math.util.FastRandom; +import com.hypixel.hytale.math.util.HashUtil; import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.core.asset.type.environment.config.WeatherForecast; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -31,9 +33,9 @@ import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2IntMap; +import java.time.LocalDateTime; import java.util.Set; import java.util.Map.Entry; -import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -135,9 +137,10 @@ public class WeatherSystem { if (weatherResource.getForcedWeatherIndex() == 0) { WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType()); int currentHour = worldTimeResource.getCurrentHour(); + LocalDateTime dateTime = worldTimeResource.getGameDateTime(); if (weatherResource.compareAndSwapHour(currentHour)) { Int2IntMap environmentWeather = weatherResource.getEnvironmentWeather(); - ThreadLocalRandom random = ThreadLocalRandom.current(); + long worldSeed = store.getExternalData().getWorld().getWorldConfig().getSeed(); IndexedLookupTableAssetMap assetMap = Environment.getAssetMap(); for (Entry entry : assetMap.getAssetMap().entrySet()) { @@ -147,7 +150,11 @@ public class WeatherSystem { throw new IllegalArgumentException("Unknown key! " + key); } - IWeightedMap weatherForecast = entry.getValue().getWeatherForecast(currentHour); + Environment environment = entry.getValue(); + IWeightedMap weatherForecast = environment.getWeatherForecast(currentHour); + String seedKey = environment.getWeatherSeedKey(); + long seed = HashUtil.hash(worldSeed, seedKey.hashCode(), dateTime.hashCode()); + FastRandom random = new FastRandom(seed); int selectedWeatherIndex = weatherForecast.get(random).getWeatherIndex(); environmentWeather.put(index, selectedWeatherIndex); } diff --git a/src/com/hypixel/hytale/builtin/worldgen/WorldGenPlugin.java b/src/com/hypixel/hytale/builtin/worldgen/WorldGenPlugin.java index 4fae40a9..c5b409b2 100644 --- a/src/com/hypixel/hytale/builtin/worldgen/WorldGenPlugin.java +++ b/src/com/hypixel/hytale/builtin/worldgen/WorldGenPlugin.java @@ -1,5 +1,25 @@ package com.hypixel.hytale.builtin.worldgen; +import com.hypixel.hytale.builtin.worldgen.modifier.EventHandler; +import com.hypixel.hytale.builtin.worldgen.modifier.WorldGenModifier; +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.FileContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.cave.CaveTypeContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.cave.CaveTypeGenerator; +import com.hypixel.hytale.builtin.worldgen.modifier.content.cave.ore.OreCluster; +import com.hypixel.hytale.builtin.worldgen.modifier.content.cover.BiomeCoverContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.cover.CaveCoverContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.fluid.BiomeFluidContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.layer.BiomeDynamicLayerContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.layer.BiomeStaticLayerContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.prefab.BiomePrefabContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.prefab.CavePrefabContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.tint.BiomeTintContent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.builtin.worldgen.modifier.op.AddOp; +import com.hypixel.hytale.builtin.worldgen.modifier.op.LogOp; +import com.hypixel.hytale.builtin.worldgen.modifier.op.Op; +import com.hypixel.hytale.builtin.worldgen.modifier.op.RemoveOp; import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.codec.lookup.Priority; import com.hypixel.hytale.codec.util.RawJsonReader; @@ -7,6 +27,7 @@ import com.hypixel.hytale.common.plugin.PluginManifest; import com.hypixel.hytale.common.semver.Semver; import com.hypixel.hytale.procedurallib.file.FileIO; import com.hypixel.hytale.server.core.asset.AssetModule; +import com.hypixel.hytale.server.core.asset.HytaleAssetStore; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.Universe; @@ -49,6 +70,44 @@ public class WorldGenPlugin extends JavaPlugin { instance = this; this.getEntityStoreRegistry().registerSystem(new BiomeDataSystem()); IWorldGenProvider.CODEC.register(Priority.DEFAULT.before(1), "Hytale", HytaleWorldGenProvider.class, HytaleWorldGenProvider.CODEC); + this.getEventRegistry().register(ModifyEvents.BiomeCovers.class, Content.Type.BIOME_COVER, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.BiomeEnvironments.class, Content.Type.BIOME_ENVIRONMENT, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.BiomeFluids.class, Content.Type.BIOME_FLUID, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.BiomeDynamicLayers.class, Content.Type.BIOME_DYNAMIC_LAYER, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.BiomeStaticLayers.class, Content.Type.BIOME_STATIC_LAYER, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.BiomePrefabs.class, Content.Type.BIOME_PREFAB, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.BiomeTints.class, Content.Type.BIOME_TINT, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.CaveTypes.class, Content.Type.CAVE_TYPE, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.CaveCovers.class, Content.Type.CAVE_COVER, EventHandler::handle); + this.getEventRegistry().register(ModifyEvents.CavePrefabs.class, Content.Type.CAVE_PREFAB, EventHandler::handle); + this.getCodecRegistry(CaveTypeGenerator.TYPE_CODEC).register("OreCluster", OreCluster.class, OreCluster.CODEC); + this.getCodecRegistry(Content.TYPE_CODEC) + .register("File", FileContent.class, FileContent.CODEC) + .register("BiomeCover", BiomeCoverContent.class, BiomeCoverContent.CODEC) + .register("BiomeFluid", BiomeFluidContent.class, BiomeFluidContent.CODEC) + .register("BiomeDynamicLayer", BiomeDynamicLayerContent.class, BiomeDynamicLayerContent.CODEC) + .register("BiomeStaticLayer", BiomeStaticLayerContent.class, BiomeStaticLayerContent.CODEC) + .register("BiomePrefab", BiomePrefabContent.class, BiomePrefabContent.CODEC) + .register("BiomeTint", BiomeTintContent.class, BiomeTintContent.CODEC) + .register("CaveCover", CaveCoverContent.class, CaveCoverContent.CODEC) + .register("CavePrefab", CavePrefabContent.class, CavePrefabContent.CODEC) + .register("CaveType", CaveTypeContent.class, CaveTypeContent.CODEC); + this.getCodecRegistry(Op.TYPE_CODEC) + .register("Add", AddOp.class, AddOp.CODEC) + .register("Remove", RemoveOp.class, RemoveOp.CODEC) + .register("Log", LogOp.class, LogOp.CODEC); + this.getAssetRegistry() + .register( + ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( + WorldGenModifier.class, WorldGenModifier.ASSET_MAP + ) + .setCodec(WorldGenModifier.ASSET_CODEC)) + .setPath("WorldGen/Modifier")) + .setIdProvider(WorldGenModifier.class)) + .setKeyFunction(WorldGenModifier::getId)) + .setReplaceOnRemove(WorldGenModifier::new)) + .build() + ); AssetModule assets = AssetModule.get(); if (assets.getAssetPacks().isEmpty()) { this.getLogger().at(Level.SEVERE).log("No asset packs loaded"); diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/EventHandler.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/EventHandler.java new file mode 100644 index 00000000..78cefa99 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/EventHandler.java @@ -0,0 +1,108 @@ +package com.hypixel.hytale.builtin.worldgen.modifier; + +import com.hypixel.hytale.assetstore.AssetPack; +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.op.Op; +import com.hypixel.hytale.server.core.asset.AssetModule; +import com.hypixel.hytale.server.worldgen.util.ListPool; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import java.nio.file.Path; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map.Entry; +import javax.annotation.Nonnull; + +public final class EventHandler implements AutoCloseable { + private static final EventHandler EMPTY = new EventHandler(); + private static final ThreadLocal SCOPED_HANDLER = ThreadLocal.withInitial(() -> EMPTY); + private static final ListPool POOL = new ListPool<>(5, EventHandler.Modifier.EMPTY_ARRAY); + private static final ListPool ENTRY_POOL = new ListPool<>(5, EventHandler.PriorityEntry.EMPTY_ARRAY); + @Nonnull + private final EnumMap events = new EnumMap<>(Content.Type.class); + + private EventHandler() { + } + + private EventHandler(@Nonnull String root) { + try (ListPool.Resource entries = ENTRY_POOL.acquire()) { + List packs = AssetModule.get().getAssetPacks(); + Object2IntOpenHashMap packPriorities = new Object2IntOpenHashMap<>(); + + for (int i = 0; i < packs.size(); i++) { + packPriorities.put(packs.get(i).getName(), i); + } + + for (Entry entry : WorldGenModifier.ASSET_MAP.getAssetMap().entrySet()) { + if (entry.getValue().getTarget().matchesRoot(root)) { + String pack = WorldGenModifier.ASSET_MAP.getAssetPack(entry.getKey()); + int priority = packPriorities.getOrDefault(pack, 0); + entries.add(new EventHandler.PriorityEntry(entry.getValue(), priority)); + } + } + + Collections.sort(entries); + + for (Content.Type type : Content.Type.VALUES) { + try (ListPool.Resource modifiers = POOL.acquire()) { + for (int i = 0; i < entries.size(); i++) { + EventHandler.PriorityEntry entryx = entries.get(i); + Op[] ops = entryx.modifier.getOperations(type); + if (ops.length != 0) { + modifiers.add(new EventHandler.Modifier(entryx.modifier.target, ops)); + } + } + + this.events.put(type, modifiers.toArray()); + } + } + } + } + + @Nonnull + public EventHandler.Modifier[] get(@Nonnull Content.Type type) { + return this.events.getOrDefault(type, EventHandler.Modifier.EMPTY_ARRAY); + } + + @Override + public void close() { + this.events.clear(); + SCOPED_HANDLER.set(EMPTY); + } + + public static void handle(@Nonnull ModifyEvent event) { + EventHandler handler = SCOPED_HANDLER.get(); + String contentPath = event.file().getContentPath(); + + for (EventHandler.Modifier modifier : handler.get(event.type())) { + if (modifier.target().matchesRule(contentPath)) { + for (Op op : modifier.ops()) { + op.apply(event); + } + } + } + } + + public static EventHandler acquire(@Nonnull Path root) { + assert SCOPED_HANDLER.get() == EMPTY : "EventHandler already open or was not closed!"; + + EventHandler handler = new EventHandler(root.getFileName().toString()); + SCOPED_HANDLER.set(handler); + return handler; + } + + public record Modifier(@Nonnull Target target, @Nonnull Op[] ops) { + public static final EventHandler.Modifier[] EMPTY_ARRAY = new EventHandler.Modifier[0]; + } + + public record PriorityEntry(WorldGenModifier modifier, int packPriority) implements Comparable { + public static final EventHandler.PriorityEntry[] EMPTY_ARRAY = new EventHandler.PriorityEntry[0]; + + public int compareTo(EventHandler.PriorityEntry o) { + return this.modifier.priority == o.modifier.priority + ? Integer.compare(this.packPriority, o.packPriority) + : Integer.compare(this.modifier.priority.getValue(), o.modifier.priority.getValue()); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/Target.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/Target.java new file mode 100644 index 00000000..48c8e808 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/Target.java @@ -0,0 +1,37 @@ +package com.hypixel.hytale.builtin.worldgen.modifier; + +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.common.util.ArrayUtil; +import com.hypixel.hytale.common.util.StringUtil; +import javax.annotation.Nonnull; + +public class Target { + public static final BuilderCodec CODEC = BuilderCodec.builder(Target.class, Target::new) + .documentation("Configures the world-gen root and content path rules that should be targeted by the WorldGenModifier") + .append(new KeyedCodec<>("Root", BuilderCodec.STRING), (instance, root) -> instance.root = root, instance -> instance.root) + .documentation("The name of the world-gen root configuration folder to target") + .add() + .append(new KeyedCodec<>("Rules", BuilderCodec.STRING_ARRAY), (instance, paths) -> instance.rules = paths, instance -> instance.rules) + .documentation("A list of glob-matching path rules to match world-gen asset files against") + .add() + .build(); + @Nonnull + private String root = "Default"; + @Nonnull + private String[] rules = ArrayUtil.EMPTY_STRING_ARRAY; + + public boolean matchesRoot(@Nonnull String name) { + return this.root.equals(name); + } + + public boolean matchesRule(@Nonnull String path) { + for (String rule : this.rules) { + if (!rule.isEmpty() && StringUtil.isGlobMatching(rule, path)) { + return true; + } + } + + return false; + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/WorldGenModifier.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/WorldGenModifier.java new file mode 100644 index 00000000..7e4aa0b4 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/WorldGenModifier.java @@ -0,0 +1,91 @@ +package com.hypixel.hytale.builtin.worldgen.modifier; + +import com.hypixel.hytale.assetstore.AssetExtraInfo; +import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; +import com.hypixel.hytale.assetstore.map.DefaultAssetMap; +import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.op.Op; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.event.EventPriority; +import com.hypixel.hytale.server.worldgen.util.ListPool; +import java.util.EnumMap; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class WorldGenModifier implements JsonAssetWithMap> { + public static final BuilderCodec CODEC = BuilderCodec.builder(WorldGenModifier.class, WorldGenModifier::new) + .documentation("Asset type used to data-drive user modifications to world-gen-v1 assets") + .append( + new KeyedCodec<>("Priority", new EnumCodec<>(EventPriority.class)), + (instance, priority) -> instance.priority = priority, + instance -> instance.priority + ) + .documentation("The order this modifier will be applied relative to others") + .add() + .append(new KeyedCodec<>("Target", Target.CODEC), (instance, target) -> instance.target = target, instance -> instance.target) + .documentation("The target world-gen configuration to modify") + .add() + .append(new KeyedCodec<>("Operations", Op.ARRAY_CODEC), (instance, array) -> instance.operations = array, instance -> instance.operations) + .documentation("List of operations to perform on the target") + .add() + .afterDecode(WorldGenModifier::init) + .build(); + public static final AssetBuilderCodec ASSET_CODEC = AssetBuilderCodec.wrap( + CODEC, Codec.STRING, (instance, id) -> instance.id = id, instance -> instance.id, (instance, data) -> instance.data = data, instance -> instance.data + ); + public static final DefaultAssetMap ASSET_MAP = new DefaultAssetMap<>(); + private static final String UNKNOWN_ID = "Unknown"; + private static final ListPool POOL = new ListPool<>(10, Op.EMPTY_ARRAY); + @Nonnull + protected String id = "Unknown"; + @Nullable + protected AssetExtraInfo.Data data = null; + @Nonnull + protected EventPriority priority = EventPriority.NORMAL; + @Nonnull + protected Target target = new Target(); + @Nonnull + protected Op[] operations = Op.EMPTY_ARRAY; + protected final transient EnumMap opsLookup = new EnumMap<>(Content.Type.class); + + public WorldGenModifier() { + } + + public WorldGenModifier(@Nonnull String id) { + this.id = id; + } + + @Nonnull + public String getId() { + return this.id; + } + + @Nonnull + public Target getTarget() { + return this.target; + } + + public Op[] getOperations(Content.Type type) { + return this.opsLookup.getOrDefault(type, Op.EMPTY_ARRAY); + } + + protected static void init(WorldGenModifier modifier) { + modifier.opsLookup.clear(); + + for (Content.Type type : Content.Type.VALUES) { + try (ListPool.Resource list = POOL.acquire(modifier.operations.length)) { + for (Op op : modifier.operations) { + if (op.type() == type) { + list.add(op); + } + } + + modifier.opsLookup.put(type, list.toArray(Op.EMPTY_ARRAY)); + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/Codecs.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/Codecs.java new file mode 100644 index 00000000..0b01f635 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/Codecs.java @@ -0,0 +1,43 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content; + +import com.hypixel.hytale.assetstore.codec.ContainedAssetCodec; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.range.FloatRange; +import com.hypixel.hytale.math.range.IntRange; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; + +public interface Codecs { + BuilderCodec INT_RANGE = BuilderCodec.builder(IntRange.class, IntRange::new) + .append(new KeyedCodec<>("Min", Codec.INTEGER), IntRange::setInclusiveMin, IntRange::getInclusiveMin) + .documentation("The inclusive minimum value of the range") + .add() + .append(new KeyedCodec<>("Max", Codec.INTEGER), IntRange::setInclusiveMax, IntRange::getInclusiveMax) + .documentation("The inclusive maximum value of the range") + .add() + .build(); + BuilderCodec DENSITY_RANGE = BuilderCodec.builder(FloatRange.class, FloatRange::new) + .append(new KeyedCodec<>("Min", Codec.FLOAT), FloatRange::setInclusiveMin, FloatRange::getInclusiveMin) + .addValidator(Validators.range(0.0F, 1.0F)) + .documentation("The inclusive minimum value of the range") + .add() + .append(new KeyedCodec<>("Max", Codec.FLOAT), FloatRange::setInclusiveMax, FloatRange::getInclusiveMax) + .addValidator(Validators.range(0.0F, 1.0F)) + .documentation("The inclusive maximum value of the range") + .add() + .afterDecode(range -> { + float min = Math.min(range.getInclusiveMin(), range.getInclusiveMax()); + float max = Math.max(range.getInclusiveMin(), range.getInclusiveMax()); + range.setInclusiveMin(min); + range.setInclusiveMax(max); + }) + .build(); + Codec BLOCK_TYPE = new ContainedAssetCodec<>(BlockType.class, BlockType.CODEC); + Codec FLUID_TYPE = new ContainedAssetCodec<>(Fluid.class, Fluid.CODEC); + ArrayCodec BLOCK_TYPE_ARRAY = new ArrayCodec<>(BLOCK_TYPE, String[]::new); + ArrayCodec FLUID_TYPE_ARRAY = new ArrayCodec<>(FLUID_TYPE, String[]::new); +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/Content.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/Content.java new file mode 100644 index 00000000..c0d8184c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/Content.java @@ -0,0 +1,35 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content; + +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.codec.lookup.CodecMapCodec; + +public interface Content { + String TYPE_KEY = "Type"; + Content[] EMPTY_ARRAY = new Content[0]; + CodecMapCodec TYPE_CODEC = new CodecMapCodec<>("Type"); + ArrayCodec ARRAY_CODEC = new ArrayCodec<>(TYPE_CODEC, Content[]::new); + + Content.Type type(); + + void applyTo(ModifyEvent var1) throws Exception; + + public static enum Type { + NONE, + BIOME_COVER, + BIOME_ENVIRONMENT, + BIOME_FLUID, + BIOME_DYNAMIC_LAYER, + BIOME_STATIC_LAYER, + BIOME_PREFAB, + BIOME_TINT, + CAVE_TYPE, + CAVE_COVER, + CAVE_PREFAB; + + public static final EnumCodec CODEC = new EnumCodec<>(Content.Type.class); + public static final String KEY = "ContentType"; + public static final Content.Type[] VALUES = values(); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/FileContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/FileContent.java new file mode 100644 index 00000000..0d0c72b2 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/FileContent.java @@ -0,0 +1,38 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content; + +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; + +public class FileContent implements Content { + public static final String ID = "File"; + public static final BuilderCodec CODEC = BuilderCodec.builder(FileContent.class, FileContent::new) + .documentation("File content to be loaded and added to the target content list") + .append(new KeyedCodec<>("ContentType", Content.Type.CODEC), (instance, type) -> instance.type = type, instance -> instance.type) + .documentation("The type of the content to load") + .add() + .append(new KeyedCodec<>("Path", BuilderCodec.STRING), (instance, path) -> instance.path = path, instance -> instance.path) + .documentation("A dot-separated path to a content file within the target world-gen root folder") + .add() + .build(); + protected Content.Type type = Content.Type.NONE; + protected String path = ""; + + @Override + public Content.Type type() { + return this.type; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + T value = event.loader().load(this.path); + if (value != null) { + event.entries().add(value); + } + } + + @Override + public String toString() { + return "FileContent{Path=" + this.path + "}"; + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/CaveTypeContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/CaveTypeContent.java new file mode 100644 index 00000000..2dcbdc89 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/CaveTypeContent.java @@ -0,0 +1,129 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.cave; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.BlockMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.HeightMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.PointGrid; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.common.map.IWeightedMap; +import com.hypixel.hytale.common.map.WeightedMap; +import com.hypixel.hytale.math.range.IntRange; +import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition; +import com.hypixel.hytale.procedurallib.supplier.DoubleRange; +import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; +import com.hypixel.hytale.server.core.prefab.PrefabRotation; +import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; +import com.hypixel.hytale.server.worldgen.cave.CaveYawMode; +import com.hypixel.hytale.server.worldgen.cave.prefab.CavePrefabContainer; +import com.hypixel.hytale.server.worldgen.cave.shape.CaveNodeShapeEnum; +import com.hypixel.hytale.server.worldgen.cave.shape.EmptyLineCaveNodeShape; +import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; + +public class CaveTypeContent implements Content { + public static final String ID = "CaveType"; + public static final BuilderCodec CODEC = BuilderCodec.builder(CaveTypeContent.class, CaveTypeContent::new) + .append(new KeyedCodec<>("Name", Codec.STRING), (t, v) -> t.name = v, t -> t.name) + .documentation("The name of the cave type") + .add() + .append(new KeyedCodec<>("PointGrid", PointGrid.CODEC), (t, v) -> t.grid = v, t -> t.grid) + .documentation("The point grid configuration controlling where the cave starting points are placed horizontally") + .add() + .append(new KeyedCodec<>("HeightRange", HeightMask.CODEC), (t, v) -> t.height = v, t -> t.height) + .documentation("The height range within which the cave type can be placed") + .add() + .append(new KeyedCodec<>("BlockMask", BlockMask.CODEC), (t, v) -> t.blockMask = v, t -> t.blockMask) + .documentation("The block and fluid types that this cave type can replace") + .add() + .append(new KeyedCodec<>("Generator", CaveTypeGenerator.TYPE_CODEC), (t, v) -> t.generator = v, t -> t.generator) + .documentation("The generator configuration for this cave type") + .add() + .build(); + protected String name = ""; + protected PointGrid grid = new PointGrid(); + protected HeightMask height = HeightMask.DEFAULT; + protected BlockMask blockMask = BlockMask.REPLACE_SOLID; + @Nullable + protected CaveTypeGenerator generator = null; + + public String name() { + return this.name; + } + + public PointGrid grid() { + return this.grid; + } + + public BlockMask mask() { + return this.blockMask; + } + + public IntRange height() { + return this.height.range(); + } + + @Override + public Content.Type type() { + return Content.Type.CAVE_TYPE; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (this.generator != null) { + if (event instanceof ModifyEvents.CaveTypes container) { + container.entries().add(this.generator.create(event.seed(), this)); + } + } + } + + public interface Util { + IntRange INT_RANGE_ONE = new IntRange(1, 1); + DoubleRange.Constant FIXED_ENTRY_HEIGHT = DoubleRange.ZERO; + CaveNodeType.CaveNodeChildEntry.OrientationModifier DIRECT = (angle, rand) -> angle; + CaveNodeType.CaveNodeChildEntry.OrientationModifier RANDOM = (angle, rand) -> angle + rand.nextFloat() * (float) (Math.PI * 2); + CaveNodeShapeEnum.CaveNodeShapeGenerator EMPTY_SHAPE = new EmptyLineCaveNodeShape.EmptyLineCaveNodeShapeGenerator(DoubleRange.ZERO); + IWeightedMap EMPTY_FILLING = WeightedMap.builder(BlockFluidEntry.EMPTY_ARRAY).put(BlockFluidEntry.EMPTY, 1.0).build(); + + static CaveNodeType createNodeType( + @Nonnull String name, + @Nonnull IWeightedMap filling, + @Nonnull CaveNodeShapeEnum.CaveNodeShapeGenerator shape, + @Nonnull CaveNodeType.CaveNodeChildEntry... children + ) { + CaveNodeType type = new CaveNodeType( + name, + (CavePrefabContainer)null, + filling, + shape, + DefaultCoordinateCondition.DEFAULT_TRUE, + (IDoubleRange)null, + CaveNodeType.CaveNodeCoverEntry.EMPTY_ARRAY, + 6, + 0 + ); + type.setChildren(children); + return type; + } + + static CaveNodeType.CaveNodeChildEntry createChildEntry(@Nonnull CaveNodeType type, @Nonnull IntRange repetitions, boolean randomDir) { + return new CaveNodeType.CaveNodeChildEntry( + WeightedMap.builder(CaveNodeType.EMPTY_ARRAY).put(type, 1.0).build(), + new Vector3d(0.0), + new Vector3d(0.0), + PrefabRotation.VALUES, + (IDoubleRange)null, + new DoubleRange.Normal(repetitions.getInclusiveMin(), repetitions.getInclusiveMax()), + randomDir ? RANDOM : DIRECT, + randomDir ? RANDOM : DIRECT, + 1.0, + CaveYawMode.NODE + ); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/CaveTypeGenerator.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/CaveTypeGenerator.java new file mode 100644 index 00000000..7040e4d3 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/CaveTypeGenerator.java @@ -0,0 +1,15 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.cave; + +import com.hypixel.hytale.codec.lookup.CodecMapCodec; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.cave.CaveType; +import javax.annotation.Nonnull; + +public interface CaveTypeGenerator { + String TYPE_KEY = "Type"; + CodecMapCodec TYPE_CODEC = new CodecMapCodec<>("Type"); + + @Nonnull + CaveType create(@Nonnull SeedString var1, @Nonnull CaveTypeContent var2); +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/ore/OreCluster.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/ore/OreCluster.java new file mode 100644 index 00000000..301e3c4e --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cave/ore/OreCluster.java @@ -0,0 +1,96 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.cave.ore; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.builtin.worldgen.modifier.content.cave.CaveTypeContent; +import com.hypixel.hytale.builtin.worldgen.modifier.content.cave.CaveTypeGenerator; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.BlockEntry; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.HeightMask; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.math.range.IntRange; +import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.procedurallib.property.NoiseProperty; +import com.hypixel.hytale.procedurallib.supplier.DoubleRange; +import com.hypixel.hytale.procedurallib.supplier.FloatRange; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.cave.CaveBiomeMaskFlags; +import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; +import com.hypixel.hytale.server.worldgen.cave.CaveType; +import com.hypixel.hytale.server.worldgen.cave.shape.CaveNodeShapeEnum; +import com.hypixel.hytale.server.worldgen.cave.shape.EmptyLineCaveNodeShape; +import com.hypixel.hytale.server.worldgen.cave.shape.PipeCaveNodeShape; +import javax.annotation.Nonnull; + +public class OreCluster implements CaveTypeGenerator { + public static final String ID = "OreCluster"; + public static final BuilderCodec CODEC = BuilderCodec.builder(OreCluster.class, OreCluster::new) + .documentation( + "Ore cluster generator: at each grid point picks a random height, radius, length, for the cluster shape, and a random selection of blocks to place" + ) + .append(new KeyedCodec<>("Blocks", BlockEntry.ARRAY_CODEC), (t, v) -> t.blocks = v, t -> t.blocks) + .documentation("Weighted list of block types to randomly fill the ore cluster with") + .add() + .append(new KeyedCodec<>("Radius", Codecs.INT_RANGE), (t, v) -> t.radius = v, t -> t.radius) + .documentation("The radius range of the ore cluster shape") + .add() + .append(new KeyedCodec<>("Length", Codecs.INT_RANGE), (t, v) -> t.length = v, t -> t.length) + .documentation("The length range of the ore cluster shape") + .add() + .append(new KeyedCodec<>("Repetitions", Codecs.INT_RANGE), (t, v) -> t.repetitions = v, t -> t.repetitions) + .documentation("The number of ore clusters to generate per grid point") + .add() + .build(); + protected static final FloatRange.Normal YAW = new FloatRange.Normal((float) -Math.PI, (float) Math.PI); + protected static final FloatRange.Constant PITCH_UP = new FloatRange.Constant((float) Math.PI); + protected static final FloatRange.Constant NODE_RECURSION_DEPTH = new FloatRange.Constant(2.0F); + protected BlockEntry[] blocks = BlockEntry.EMPTY_ARRAY; + protected IntRange radius = new IntRange(); + protected IntRange length = new IntRange(); + protected IntRange repetitions = new IntRange(1, 3); + + @Nonnull + @Override + public CaveType create(@Nonnull SeedString seed, @Nonnull CaveTypeContent cave) { + CaveNodeType clusterType = CaveTypeContent.Util.createNodeType(cave.name(), BlockEntry.build(this.blocks), this.buildClusterShape()); + CaveNodeType.CaveNodeChildEntry clusterNode = CaveTypeContent.Util.createChildEntry(clusterType, CaveTypeContent.Util.INT_RANGE_ONE, false); + CaveNodeType startType = CaveTypeContent.Util.createNodeType("Start", CaveTypeContent.Util.EMPTY_FILLING, this.buildStartShape(cave), clusterNode); + CaveNodeType.CaveNodeChildEntry startNode = CaveTypeContent.Util.createChildEntry(startType, this.repetitions, true); + CaveNodeType entryNodeType = CaveTypeContent.Util.createNodeType("Entry", CaveTypeContent.Util.EMPTY_FILLING, CaveTypeContent.Util.EMPTY_SHAPE, startNode); + return new CaveType( + cave.name(), + entryNodeType, + YAW, + PITCH_UP, + NODE_RECURSION_DEPTH, + HeightMask.DEFAULT_HEIGHT_THRESHOLD, + cave.grid().build(seed), + CaveBiomeMaskFlags.DEFAULT_ALLOW, + cave.mask().getBlockMask(), + DefaultCoordinateCondition.DEFAULT_TRUE, + DefaultCoordinateCondition.DEFAULT_TRUE, + CaveTypeContent.Util.FIXED_ENTRY_HEIGHT, + (NoiseProperty)null, + CaveType.FluidLevel.EMPTY, + 0, + true, + true, + this.radius.getInclusiveMax() + ); + } + + protected CaveNodeShapeEnum.CaveNodeShapeGenerator buildStartShape(@Nonnull CaveTypeContent config) { + return new EmptyLineCaveNodeShape.EmptyLineCaveNodeShapeGenerator( + new DoubleRange.Normal(config.height().getInclusiveMin(), config.height().getInclusiveMax()) + ); + } + + protected CaveNodeShapeEnum.CaveNodeShapeGenerator buildClusterShape() { + return new PipeCaveNodeShape.PipeCaveNodeShapeGenerator( + new DoubleRange.Normal(this.radius.getInclusiveMin(), this.radius.getInclusiveMax()), + new DoubleRange.Normal(this.radius.getInclusiveMin(), this.radius.getInclusiveMax()), + new DoubleRange.Normal(this.length.getInclusiveMin(), this.length.getInclusiveMax()), + false + ); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/BlockEntry.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/BlockEntry.java new file mode 100644 index 00000000..01293ace --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/BlockEntry.java @@ -0,0 +1,50 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.common; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.common.map.IWeightedMap; +import com.hypixel.hytale.common.map.WeightedMap; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import javax.annotation.Nonnull; + +public class BlockEntry { + public static final BuilderCodec CODEC = BuilderCodec.builder(BlockEntry.class, BlockEntry::new) + .documentation("Defines a weighted block entry used to randomly select a block type from a list of entries") + .append(new KeyedCodec<>("Block", Codecs.BLOCK_TYPE), (t, v) -> t.block = v, t -> t.block) + .documentation("The block type to place") + .add() + .append(new KeyedCodec<>("Weight", Codec.DOUBLE), (t, v) -> t.weight = v, t -> t.weight) + .addValidator(Validators.range(0.0, 100.0)) + .documentation("The random chance of this block type being chosen") + .add() + .build(); + public static final ArrayCodec ARRAY_CODEC = new ArrayCodec<>(CODEC, BlockEntry[]::new); + public static final BlockEntry[] EMPTY_ARRAY = new BlockEntry[0]; + protected String block = "Empty"; + protected double weight = 1.0; + + @Nonnull + public String block() { + return this.block; + } + + public double weight() { + return this.weight; + } + + public static IWeightedMap build(@Nonnull BlockEntry[] entries) { + WeightedMap.Builder map = WeightedMap.builder(BlockFluidEntry.EMPTY_ARRAY); + + for (BlockEntry block : entries) { + int blockId = BlockType.getAssetMap().getIndex(block.block()); + map.put(new BlockFluidEntry(blockId, 0, 0), block.weight()); + } + + return map.build(); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/BlockMask.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/BlockMask.java new file mode 100644 index 00000000..b6c602b1 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/BlockMask.java @@ -0,0 +1,191 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.common; + +import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; +import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.common.util.ArrayUtil; +import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.procedurallib.condition.ConstantBlockFluidCondition; +import com.hypixel.hytale.procedurallib.condition.IBlockFluidCondition; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; +import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import com.hypixel.hytale.server.worldgen.util.ListPool; +import com.hypixel.hytale.server.worldgen.util.ResolvedBlockArray; +import com.hypixel.hytale.server.worldgen.util.condition.BlockMaskCondition; +import com.hypixel.hytale.server.worldgen.util.condition.FilteredBlockFluidCondition; +import com.hypixel.hytale.server.worldgen.util.condition.HashSetBlockFluidCondition; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import javax.annotation.Nonnull; + +public class BlockMask { + public static final BuilderCodec CODEC = BuilderCodec.builder(BlockMask.class, BlockMask::new) + .documentation("Defines a block mask that filters which blocks and fluids can be replaced, using include and exclude rules to control placement targets") + .append(new KeyedCodec<>("Include", BlockMask.Rule.CODEC), (m, v) -> m.include = v, m -> m.include) + .documentation("Set of block and fluid types that should be included by this mask") + .add() + .append(new KeyedCodec<>("Exclude", BlockMask.Rule.CODEC), (m, v) -> m.exclude = v, m -> m.exclude) + .documentation("Set of block and fluid types that should be excluded by this mask") + .add() + .afterDecode(BlockMask::rebuild) + .build(); + public static final BlockMask REPLACE_ANY = new BlockMask() { + { + this.include.any = true; + this.rebuild(); + } + }; + public static final BlockMask REPLACE_EMPTY = new BlockMask() { + { + this.include.empty = true; + this.rebuild(); + } + }; + public static final BlockMask REPLACE_SOLID = new BlockMask() { + { + this.include.any = true; + this.exclude.empty = true; + this.rebuild(); + } + }; + protected BlockMask.Rule include = new BlockMask.Rule(); + protected BlockMask.Rule exclude = new BlockMask.Rule(); + @Nonnull + protected transient BlockMaskCondition mask = BlockMaskCondition.DEFAULT_TRUE; + @Nonnull + protected transient IBlockFluidCondition condition = ConstantBlockFluidCondition.DEFAULT_TRUE; + + @Nonnull + public BlockMaskCondition getBlockMask() { + return this.mask; + } + + @Nonnull + public IBlockFluidCondition getCondition() { + return this.condition; + } + + protected void rebuild() { + this.condition = new FilteredBlockFluidCondition(this.exclude.buildCondition(), this.include.buildCondition()); + this.mask = new BlockMaskCondition(); + this.mask + .set( + new BlockMaskCondition.Mask(new BlockMaskCondition.MaskEntry[]{this.exclude.buildMaskEntry(false), this.include.buildMaskEntry(true)}), + Long2ObjectMaps.emptyMap() + ); + } + + public static class Rule { + public static final BuilderCodec CODEC = BuilderCodec.builder(BlockMask.Rule.class, BlockMask.Rule::new) + .documentation("Defines a block mask rule specifying which blocks and fluids to match, using any, empty, or explicit block and fluid lists") + .append(new KeyedCodec<>("Any", Codec.BOOLEAN), (m, v) -> m.any = v, m -> m.any) + .documentation("Flag indicating the rule matches on any block or fluid") + .add() + .append(new KeyedCodec<>("Empty", Codec.BOOLEAN), (m, v) -> m.empty = v, m -> m.empty) + .documentation("Flag indicating the rule matches on the empty block") + .add() + .append(new KeyedCodec<>("Blocks", Codecs.BLOCK_TYPE_ARRAY), (m, v) -> m.blocks = v, m -> m.blocks) + .documentation("List of blocks that this rule should match on") + .add() + .append(new KeyedCodec<>("Fluids", Codecs.FLUID_TYPE_ARRAY), (m, v) -> m.fluids = v, m -> m.fluids) + .documentation("List of fluids that this rule should match on") + .add() + .build(); + protected static final ListPool POOL = new ListPool<>(10, BlockFluidEntry.EMPTY_ARRAY); + protected boolean any = false; + protected boolean empty = false; + protected String[] blocks = ArrayUtil.EMPTY_STRING_ARRAY; + protected String[] fluids = ArrayUtil.EMPTY_STRING_ARRAY; + + public IBlockFluidCondition buildCondition() { + if (this.any) { + return ConstantBlockFluidCondition.DEFAULT_TRUE; + } else if (!this.empty && this.blocks.length == 0 && this.fluids.length == 0) { + return ConstantBlockFluidCondition.DEFAULT_FALSE; + } else { + IndexedLookupTableAssetMap fluidTypes = Fluid.getAssetMap(); + BlockTypeAssetMap blockTypes = BlockType.getAssetMap(); + LongOpenHashSet ids = new LongOpenHashSet(); + if (this.empty) { + ids.add(MathUtil.packLong(0, 0)); + } + + for (String block : this.blocks) { + for (String name : blockTypes.getSubKeys(block)) { + int blockId = blockTypes.getIndex(name); + if (blockId != 1) { + ids.add(MathUtil.packLong(blockId, 0)); + + for (String fluid : this.fluids) { + int fluidId = fluidTypes.getIndex(fluid); + if (fluidId != 1) { + ids.add(MathUtil.packLong(blockId, fluidId)); + } + } + } + } + } + + for (String fluidx : this.fluids) { + int fluidId = fluidTypes.getIndex(fluidx); + if (fluidId != 1) { + ids.add(MathUtil.packLong(0, fluidId)); + } + } + + return new HashSetBlockFluidCondition(ids); + } + } + + public BlockMaskCondition.MaskEntry buildMaskEntry(boolean match) { + if (this.any) { + return match ? BlockMaskCondition.MaskEntry.WILDCARD_TRUE : BlockMaskCondition.MaskEntry.WILDCARD_FALSE; + } else if (!this.empty && this.blocks.length == 0 && this.fluids.length == 0) { + return match ? BlockMaskCondition.MaskEntry.WILDCARD_FALSE : BlockMaskCondition.MaskEntry.WILDCARD_TRUE; + } else { + IndexedLookupTableAssetMap fluidTypes = Fluid.getAssetMap(); + BlockTypeAssetMap blockTypes = BlockType.getAssetMap(); + + BlockMaskCondition.MaskEntry var23; + try (ListPool.Resource list = POOL.acquire()) { + if (this.empty) { + list.add(BlockFluidEntry.EMPTY); + } + + for (String block : this.blocks) { + for (String name : blockTypes.getSubKeys(block)) { + int blockId = blockTypes.getIndex(name); + if (blockId != 1) { + list.add(new BlockFluidEntry(blockId, 0, 0)); + + for (String fluid : this.fluids) { + int fluidId = fluidTypes.getIndex(fluid); + if (fluidId != 1) { + list.add(new BlockFluidEntry(blockId, 0, fluidId)); + } + } + } + } + } + + for (String fluidx : this.fluids) { + int fluidId = fluidTypes.getIndex(fluidx); + if (fluidId != 1) { + list.add(new BlockFluidEntry(0, 0, fluidId)); + } + } + + BlockMaskCondition.MaskEntry mask = new BlockMaskCondition.MaskEntry(false, match); + mask.set(new ResolvedBlockArray(list.toArray()), match); + var23 = mask; + } + + return var23; + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/HeightMask.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/HeightMask.java new file mode 100644 index 00000000..2d16adb4 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/HeightMask.java @@ -0,0 +1,79 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.common; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.range.IntRange; +import com.hypixel.hytale.procedurallib.condition.BasicHeightThresholdInterpreter; +import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateRndCondition; +import com.hypixel.hytale.procedurallib.condition.ICoordinateRndCondition; +import com.hypixel.hytale.procedurallib.condition.IHeightThresholdInterpreter; +import javax.annotation.Nonnull; + +public class HeightMask { + public static final BuilderCodec CODEC = BuilderCodec.builder(HeightMask.class, HeightMask::new) + .documentation("Defines a height mask that restricts placement to a specific vertical range within the world") + .append(new KeyedCodec<>("Min", Codec.INTEGER), (i, v) -> i.range.setInclusiveMin(v), i -> i.range.getInclusiveMin()) + .addValidator(Validators.range(0, 319)) + .documentation("The inclusive minimum height of the mask range") + .add() + .append(new KeyedCodec<>("Max", Codec.INTEGER), (i, v) -> i.range.setInclusiveMax(v), i -> i.range.getInclusiveMax()) + .addValidator(Validators.range(0, 319)) + .documentation("The inclusive maximum height of the mask range") + .add() + .afterDecode(HeightMask::rebuild) + .build(); + public static final IHeightThresholdInterpreter DEFAULT_HEIGHT_THRESHOLD = new BasicHeightThresholdInterpreter( + new int[]{0, 319}, new float[]{1.0F, 1.0F}, 320 + ); + public static final HeightMask DEFAULT = new HeightMask() { + { + this.rebuild(); + } + }; + public static final HeightMask DEFAULT_ZERO = new HeightMask() { + { + this.range.setInclusiveMin(0); + this.range.setInclusiveMax(0); + this.rebuild(); + } + }; + public static final HeightMask DEFAULT_ONE = new HeightMask() { + { + this.range.setInclusiveMin(1); + this.range.setInclusiveMax(1); + this.rebuild(); + } + }; + public static final HeightMask DEFAULT_FLUID = new HeightMask() { + { + this.range.setInclusiveMin(114); + this.range.setInclusiveMax(114); + this.rebuild(); + } + }; + @Nonnull + protected IntRange range = new IntRange(0, 319); + @Nonnull + protected transient ICoordinateRndCondition condition = DefaultCoordinateRndCondition.DEFAULT_TRUE; + + @Nonnull + public IntRange range() { + return this.range; + } + + @Nonnull + public ICoordinateRndCondition getCondition() { + return this.condition; + } + + protected void rebuild() { + int min = Math.min(this.range.getInclusiveMin(), this.range.getInclusiveMax()); + int max = Math.max(this.range.getInclusiveMin(), this.range.getInclusiveMax()); + this.range.setInclusiveMin(min); + this.range.setInclusiveMax(max); + IntRange range = this.range; + this.condition = (seed, x, z, y, random) -> y >= range.getInclusiveMin() && y <= range.getInclusiveMax(); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/NoiseMask.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/NoiseMask.java new file mode 100644 index 00000000..df676b41 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/NoiseMask.java @@ -0,0 +1,80 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.common; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.range.FloatRange; +import com.hypixel.hytale.procedurallib.condition.DoubleThreshold; +import com.hypixel.hytale.procedurallib.condition.DoubleThresholdCondition; +import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; +import com.hypixel.hytale.procedurallib.condition.NoiseMaskCondition; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.procedurallib.logic.SimplexNoise; +import com.hypixel.hytale.procedurallib.property.FractalNoiseProperty; +import com.hypixel.hytale.procedurallib.property.NoiseProperty; +import com.hypixel.hytale.procedurallib.property.ScaleNoiseProperty; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.util.ConstantNoiseProperty; + +public class NoiseMask { + public static final BuilderCodec CODEC = BuilderCodec.builder(NoiseMask.class, NoiseMask::new) + .documentation("Defines a noise mask that uses fractal simplex noise to control placement density within a configurable threshold range") + .append(new KeyedCodec<>("Seed", Codec.STRING), (t, v) -> t.seed = v, t -> t.seed) + .documentation("Seed for the mask noise") + .add() + .append(new KeyedCodec<>("Scale", Codec.DOUBLE), (t, v) -> t.scale = v, t -> t.scale) + .addValidator(Validators.greaterThan(0.0)) + .documentation("The scale of the mask noise") + .add() + .append(new KeyedCodec<>("Octaves", Codec.INTEGER), (t, v) -> t.octaves = v, t -> t.octaves) + .addValidator(Validators.range(0, 5)) + .documentation("The number of octaves of the noise to use") + .add() + .append(new KeyedCodec<>("Lacunarity", Codec.DOUBLE), (t, v) -> t.lacunarity = v, t -> t.lacunarity) + .addValidator(Validators.range(1.0, 10.0)) + .documentation("The cumulative frequency multiplier for each noise octave") + .add() + .append(new KeyedCodec<>("Persistence", Codec.DOUBLE), (t, v) -> t.persistence = v, t -> t.persistence) + .addValidator(Validators.range(0.0, 1.0)) + .documentation("The cumulative gain multiplier for each noise octave") + .add() + .append(new KeyedCodec<>("Density", Codecs.DENSITY_RANGE), (t, v) -> t.density = v, t -> t.density) + .documentation("Threshold range for the mask. Noise values outside the range result in the prefab position being occluded") + .add() + .build(); + public static final NoiseMask DEFAULT = new NoiseMask(); + public static final NoiseMask ZERO = new NoiseMask() { + @Override + public NoiseProperty buildNoise(SeedString seed) { + return ConstantNoiseProperty.DEFAULT_ZERO; + } + }; + protected String seed = "Default_Mask"; + protected double scale = 128.0; + protected int octaves = 2; + protected double lacunarity = 4.0; + protected double persistence = 0.6; + protected FloatRange density = new FloatRange(0.0F, 1.0F); + + public NoiseProperty buildNoise(SeedString seed) { + return new ScaleNoiseProperty( + new FractalNoiseProperty( + seed.append(this.seed).hashCode(), + SimplexNoise.INSTANCE, + FractalNoiseProperty.FractalMode.FBM.getFunction(), + this.octaves, + this.lacunarity, + this.persistence + ), + this.scale == 0.0 ? 0.0 : 1.0 / this.scale + ); + } + + public ICoordinateCondition build(SeedString seed) { + return new NoiseMaskCondition( + this.buildNoise(seed), new DoubleThresholdCondition(new DoubleThreshold.Single(this.density.getInclusiveMin(), this.density.getInclusiveMax())) + ); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/PointGrid.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/PointGrid.java new file mode 100644 index 00000000..d66416a8 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/common/PointGrid.java @@ -0,0 +1,73 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.common; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.math.range.FloatRange; +import com.hypixel.hytale.procedurallib.condition.DoubleThreshold; +import com.hypixel.hytale.procedurallib.condition.DoubleThresholdCondition; +import com.hypixel.hytale.procedurallib.json.SeedResourcePointGenerator; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.procedurallib.logic.cell.DistanceCalculationMode; +import com.hypixel.hytale.procedurallib.logic.cell.GridCellDistanceFunction; +import com.hypixel.hytale.procedurallib.logic.cell.evaluator.PointEvaluator; +import com.hypixel.hytale.procedurallib.logic.cell.jitter.ConstantCellJitter; +import com.hypixel.hytale.procedurallib.logic.point.IPointGenerator; +import com.hypixel.hytale.procedurallib.logic.point.ScaledPointGenerator; +import com.hypixel.hytale.procedurallib.supplier.DoubleRange; +import com.hypixel.hytale.server.worldgen.SeedStringResource; + +public class PointGrid { + public static final BuilderCodec CODEC = BuilderCodec.builder(PointGrid.class, PointGrid::new) + .documentation( + "Defines a point grid that distributes placement positions across the world using a seeded cell-based layout with configurable scale, jitter, and density" + ) + .append(new KeyedCodec<>("Seed", Codec.STRING), (t, v) -> t.seed = v, t -> t.seed) + .documentation("Seed for the point grid") + .add() + .append(new KeyedCodec<>("Scale", Codec.DOUBLE), (t, v) -> t.scale = v, t -> t.scale) + .documentation("Scale of the point grid") + .add() + .append(new KeyedCodec<>("Jitter", Codec.DOUBLE), (t, v) -> t.jitter = v, t -> t.jitter) + .documentation("Jitter of the points in the grid") + .add() + .append(new KeyedCodec<>("Density", Codecs.DENSITY_RANGE), (t, v) -> t.density = v, t -> t.density) + .documentation("Density threshold for the point grid") + .add() + .build(); + public static final PointGrid DEFAULT = new PointGrid(); + protected String seed = "Default_Grid"; + protected double scale = 16.0; + protected double jitter = 0.75; + protected FloatRange density = new FloatRange(0.0F, 1.0F); + + public double scale() { + return this.scale; + } + + public double jitter() { + return this.jitter; + } + + public FloatRange density() { + return this.density; + } + + public IPointGenerator build(SeedString seed) { + return new ScaledPointGenerator( + new SeedResourcePointGenerator( + seed.appendToOriginal(this.seed).hashCode(), + GridCellDistanceFunction.DISTANCE_FUNCTION, + PointEvaluator.of( + DistanceCalculationMode.EUCLIDEAN.getFunction(), + new DoubleThresholdCondition(new DoubleThreshold.Single(this.density.getInclusiveMin(), this.density.getInclusiveMax())), + new DoubleRange.Constant(1.0), + new ConstantCellJitter(this.jitter, this.jitter, this.jitter) + ), + seed.get() + ), + this.scale == 0.0 ? 0.0 : 1.0 / this.scale + ); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/BiomeCoverContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/BiomeCoverContent.java new file mode 100644 index 00000000..02bd8fc9 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/BiomeCoverContent.java @@ -0,0 +1,53 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.cover; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.container.CoverContainer; +import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import javax.annotation.Nonnull; + +public class BiomeCoverContent extends CoverContent { + public static final String ID = "BiomeCover"; + public static final BuilderCodec CODEC = BuilderCodec.builder(BiomeCoverContent.class, BiomeCoverContent::new, CoverContent.CODEC) + .documentation("Define a block that will be placed on the surface of the terrain") + .append(new KeyedCodec<>("PlaceOnFluids", Codec.BOOLEAN), (c, v) -> c.placeOnFluids = v, c -> c.placeOnFluids) + .documentation("Whether the cover should be placed on fluids") + .add() + .build(); + protected boolean placeOnFluids = false; + + @Override + public Content.Type type() { + return Content.Type.BIOME_COVER; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (this.entries.length != 0) { + if (event instanceof ModifyEvents.BiomeCovers container) { + container.entries().add(this.build(event.seed())); + } + } + } + + protected CoverContainer.CoverContainerEntry build(@Nonnull SeedString seed) { + return new CoverContainer.CoverContainerEntry( + this.buildEntries(CoverContainer.CoverContainerEntry.CoverContainerEntryPart.EMPTY_ARRAY, BiomeCoverContent::createEntry), + this.noiseMask.build(seed), + this.heightMask.getCondition(), + this.parentMask.getCondition(), + this.chance, + this.placeOnFluids + ); + } + + protected static CoverContainer.CoverContainerEntry.CoverContainerEntryPart createEntry(@Nonnull CoverContent.Entry entry, @Nonnull BlockFluidEntry block) { + return new CoverContainer.CoverContainerEntry.CoverContainerEntryPart(block, entry.offset); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/CaveCoverContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/CaveCoverContent.java new file mode 100644 index 00000000..a617ff47 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/CaveCoverContent.java @@ -0,0 +1,56 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.cover; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; +import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import com.hypixel.hytale.server.worldgen.util.condition.RandomCoordinateCondition; +import javax.annotation.Nonnull; + +public class CaveCoverContent extends CoverContent { + public static final String ID = "CaveCover"; + public static final BuilderCodec CODEC = BuilderCodec.builder(CaveCoverContent.class, CaveCoverContent::new, CoverContent.CODEC) + .documentation("Define a block that will be placed on the floor or ceiling of a cave") + .append( + new KeyedCodec<>("Placement", new EnumCodec<>(CaveNodeType.CaveNodeCoverType.class)), (c, v) -> c.placement = v, c -> c.placement + ) + .documentation("The side of the cave where the cover should be placed") + .add() + .build(); + protected CaveNodeType.CaveNodeCoverType placement = CaveNodeType.CaveNodeCoverType.FLOOR; + + @Override + public Content.Type type() { + return Content.Type.CAVE_COVER; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (this.entries.length != 0) { + if (event instanceof ModifyEvents.CaveCovers container) { + container.entries().add(this.buildEntry(event.seed())); + } + } + } + + protected CaveNodeType.CaveNodeCoverEntry buildEntry(@Nonnull SeedString seed) { + return new CaveNodeType.CaveNodeCoverEntry( + this.buildEntries(CaveNodeType.CaveNodeCoverEntry.Entry.EMPTY_ARRAY, CaveCoverContent::createEntry), + this.heightMask.getCondition(), + this.noiseMask.build(seed), + new RandomCoordinateCondition(this.chance), + this.parentMask.getCondition(), + this.placement + ); + } + + protected static CaveNodeType.CaveNodeCoverEntry.Entry createEntry(@Nonnull CoverContent.Entry entry, @Nonnull BlockFluidEntry block) { + return new CaveNodeType.CaveNodeCoverEntry.Entry(block, entry.offset); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/CoverContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/CoverContent.java new file mode 100644 index 00000000..613c2e6e --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/cover/CoverContent.java @@ -0,0 +1,67 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.cover; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.BlockEntry; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.BlockMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.HeightMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.NoiseMask; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.common.map.IWeightedMap; +import com.hypixel.hytale.common.map.WeightedMap; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import java.util.function.BiFunction; +import javax.annotation.Nonnull; + +public abstract class CoverContent implements Content { + public static final BuilderCodec CODEC = BuilderCodec.abstractBuilder(CoverContent.class) + .append(new KeyedCodec<>("Entries", new ArrayCodec<>(CoverContent.Entry.CODEC, CoverContent.Entry[]::new)), (c, v) -> c.entries = v, c -> c.entries) + .documentation("A list of blocks that can be placed as a cover") + .add() + .append(new KeyedCodec<>("NoiseMask", NoiseMask.CODEC), (c, v) -> c.noiseMask = v, c -> c.noiseMask) + .documentation("A mask used to determine where the cover should be placed based on noise") + .add() + .append(new KeyedCodec<>("HeightMask", HeightMask.CODEC), (c, v) -> c.heightMask = v, c -> c.heightMask) + .documentation("A mask used to determine where the cover should be placed based on height") + .add() + .append(new KeyedCodec<>("ParentMask", BlockMask.CODEC), (c, v) -> c.parentMask = v, c -> c.parentMask) + .documentation("A mask used to filter which blocks can be covered") + .add() + .append(new KeyedCodec<>("Chance", Codec.DOUBLE), (c, v) -> c.chance = v, c -> c.chance) + .addValidator(Validators.range(0.0, 1.0)) + .documentation("The random chance that the cover will be placed at a given position") + .add() + .build(); + protected CoverContent.Entry[] entries = CoverContent.Entry.EMPTY_ARRAY; + protected NoiseMask noiseMask = NoiseMask.DEFAULT; + protected HeightMask heightMask = HeightMask.DEFAULT; + protected BlockMask parentMask = BlockMask.REPLACE_SOLID; + protected double chance = 1.0; + + protected IWeightedMap buildEntries(@Nonnull T[] empty, @Nonnull BiFunction constructor) { + WeightedMap.Builder map = WeightedMap.builder(empty); + + for (CoverContent.Entry entry : this.entries) { + int blockId = BlockType.getAssetMap().getIndex(entry.block()); + BlockFluidEntry blockFluid = new BlockFluidEntry(blockId, 0, 0); + map.put(constructor.apply(entry, blockFluid), entry.weight()); + } + + return map.build(); + } + + public static class Entry extends BlockEntry { + public static final BuilderCodec CODEC = BuilderCodec.builder(CoverContent.Entry.class, CoverContent.Entry::new, BlockEntry.CODEC) + .documentation("Define a block to be placed as a cover") + .append(new KeyedCodec<>("Offset", Codec.INTEGER), (c, v) -> c.offset = v, c -> c.offset) + .documentation("The vertical offset from the surface for this cover entry") + .add() + .build(); + public static final CoverContent.Entry[] EMPTY_ARRAY = new CoverContent.Entry[0]; + protected int offset = 0; + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/fluid/BiomeFluidContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/fluid/BiomeFluidContent.java new file mode 100644 index 00000000..626c206c --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/fluid/BiomeFluidContent.java @@ -0,0 +1,77 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.fluid; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.HeightMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.NoiseMask; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.procedurallib.supplier.DoubleRange; +import com.hypixel.hytale.procedurallib.supplier.DoubleRangeNoiseSupplier; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; +import com.hypixel.hytale.server.worldgen.container.WaterContainer; + +public class BiomeFluidContent implements Content { + public static final String ID = "BiomeFluid"; + public static final BuilderCodec CODEC = BuilderCodec.builder(BiomeFluidContent.class, BiomeFluidContent::new) + .documentation("Define a fluid that should generate between certain heights in the biome") + .append(new KeyedCodec<>("Fluid", Codecs.FLUID_TYPE), (t, v) -> t.fluid = v, t -> t.fluid) + .documentation("The fluid type to place") + .add() + .append(new KeyedCodec<>("Block", Codecs.BLOCK_TYPE), (t, v) -> t.block = v, t -> t.block) + .documentation("The block type to place in the fluid") + .add() + .append(new KeyedCodec<>("MinY", HeightMask.CODEC), (t, v) -> t.minY = v, t -> t.minY) + .documentation("The minimum height that the fluid should generate at") + .add() + .append(new KeyedCodec<>("MinYNoise", NoiseMask.CODEC), (t, v) -> t.minYNoise = v, t -> t.minYNoise) + .documentation("The noise used to modulate the min-y height") + .add() + .append(new KeyedCodec<>("MaxY", HeightMask.CODEC), (t, v) -> t.maxY = v, t -> t.maxY) + .documentation("The maximum height that the fluid should generate at") + .add() + .append(new KeyedCodec<>("MaxYNoise", NoiseMask.CODEC), (t, v) -> t.maxYNoise = v, t -> t.maxYNoise) + .documentation("The noise used to modulate the max-y height") + .add() + .append(new KeyedCodec<>("NoiseMask", NoiseMask.CODEC), (t, v) -> t.noiseMask = v, t -> t.noiseMask) + .documentation("The noise mask used to determine where the fluid should be placed") + .add() + .build(); + protected String fluid = "Empty"; + protected String block = "Empty"; + protected HeightMask minY = HeightMask.DEFAULT_ZERO; + protected NoiseMask minYNoise = NoiseMask.ZERO; + protected HeightMask maxY = HeightMask.DEFAULT_FLUID; + protected NoiseMask maxYNoise = NoiseMask.ZERO; + protected NoiseMask noiseMask = NoiseMask.DEFAULT; + + @Override + public Content.Type type() { + return Content.Type.BIOME_FLUID; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (event instanceof ModifyEvents.BiomeFluids container) { + int fluidId = Fluid.getAssetMap().getIndexOrDefault(this.fluid, 0); + int blockId = BlockType.getAssetMap().getIndexOrDefault(this.block, 0); + container.entries() + .add( + new WaterContainer.Entry( + blockId, + fluidId, + new DoubleRangeNoiseSupplier( + new DoubleRange.Normal(this.minY.range().getInclusiveMin(), this.minY.range().getInclusiveMax()), this.minYNoise.buildNoise(event.seed()) + ), + new DoubleRangeNoiseSupplier( + new DoubleRange.Normal(this.maxY.range().getInclusiveMin(), this.maxY.range().getInclusiveMax()), this.maxYNoise.buildNoise(event.seed()) + ), + this.noiseMask.build(event.seed()) + ) + ); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/BiomeDynamicLayerContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/BiomeDynamicLayerContent.java new file mode 100644 index 00000000..9915721a --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/BiomeDynamicLayerContent.java @@ -0,0 +1,88 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.layer; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.HeightMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.NoiseMask; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.procedurallib.supplier.DoubleRange; +import com.hypixel.hytale.procedurallib.supplier.DoubleRangeNoiseSupplier; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.container.LayerContainer; +import javax.annotation.Nonnull; + +public class BiomeDynamicLayerContent extends LayerContent { + public static final String ID = "BiomeDynamicLayer"; + public static final BuilderCodec CODEC = BuilderCodec.builder( + BiomeDynamicLayerContent.class, BiomeDynamicLayerContent::new, LayerContent.CODEC + ) + .documentation("Define a layer that is placed relative to terrain surfaces and any other dynamic layers that apply before it") + .append( + new KeyedCodec<>("Entries", BiomeDynamicLayerContent.DynamicEntry.ARRAY_CODEC), (t, v) -> t.entries = v, t -> t.entries + ) + .documentation("The entries contained within the layer") + .add() + .append(new KeyedCodec<>("Offset", HeightMask.CODEC), (t, v) -> t.offset = v, t -> t.offset) + .documentation("The depth offset for the dynamic layer") + .add() + .append(new KeyedCodec<>("OffsetNoise", NoiseMask.CODEC), (t, v) -> t.offsetNoise = v, t -> t.offsetNoise) + .documentation("The noise used to modulate the depth offset") + .add() + .build(); + protected BiomeDynamicLayerContent.DynamicEntry[] entries = BiomeDynamicLayerContent.DynamicEntry.EMPTY_ARRAY; + protected HeightMask offset = HeightMask.DEFAULT_ONE; + protected NoiseMask offsetNoise = NoiseMask.ZERO; + + @Override + public Content.Type type() { + return Content.Type.BIOME_DYNAMIC_LAYER; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (this.entries.length != 0) { + if (event instanceof ModifyEvents.BiomeDynamicLayers container) { + container.entries() + .add( + new LayerContainer.DynamicLayer( + this.buildEntries(event.seed()), + this.noiseMask.build(event.seed()), + 0, + new DoubleRangeNoiseSupplier( + new DoubleRange.Normal(this.offset.range().getInclusiveMin(), this.offset.range().getInclusiveMax()), + this.offsetNoise.buildNoise(event.seed()) + ) + ) + ); + } + } + } + + protected LayerContainer.DynamicLayerEntry[] buildEntries(@Nonnull SeedString seed) { + LayerContainer.DynamicLayerEntry[] layerEntries = new LayerContainer.DynamicLayerEntry[this.entries.length]; + + for (int i = 0; i < this.entries.length; i++) { + layerEntries[i] = this.entries[i].build(seed); + } + + return layerEntries; + } + + public static class DynamicEntry extends LayerContent.Entry { + public static final BuilderCodec CODEC = BuilderCodec.builder( + BiomeDynamicLayerContent.DynamicEntry.class, BiomeDynamicLayerContent.DynamicEntry::new, LayerContent.Entry.CODEC + ) + .documentation("Define an entry that generates within the dynamic layer") + .build(); + public static final ArrayCodec ARRAY_CODEC = new ArrayCodec<>(CODEC, BiomeDynamicLayerContent.DynamicEntry[]::new); + protected static final BiomeDynamicLayerContent.DynamicEntry[] EMPTY_ARRAY = new BiomeDynamicLayerContent.DynamicEntry[0]; + + public LayerContainer.DynamicLayerEntry build(SeedString seed) { + return new LayerContainer.DynamicLayerEntry(NoiseBlockEntry.buildArray(seed, this.blocks), this.entryNoiseMask.build(seed)); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/BiomeStaticLayerContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/BiomeStaticLayerContent.java new file mode 100644 index 00000000..9c6ead70 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/BiomeStaticLayerContent.java @@ -0,0 +1,93 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.layer; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.NoiseMask; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.math.range.IntRange; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.procedurallib.supplier.DoubleRange; +import com.hypixel.hytale.procedurallib.supplier.DoubleRangeNoiseSupplier; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.container.LayerContainer; +import javax.annotation.Nonnull; + +public class BiomeStaticLayerContent extends LayerContent { + public static final String ID = "BiomeStaticLayer"; + public static final BuilderCodec CODEC = BuilderCodec.builder( + BiomeStaticLayerContent.class, BiomeStaticLayerContent::new, LayerContent.CODEC + ) + .documentation("Define a layer of material that generates between a static height range") + .append( + new KeyedCodec<>("Entries", BiomeStaticLayerContent.StaticEntry.ARRAY_CODEC), (t, v) -> t.entries = v, t -> t.entries + ) + .documentation("The entries contained within the layer") + .add() + .build(); + protected BiomeStaticLayerContent.StaticEntry[] entries = BiomeStaticLayerContent.StaticEntry.EMPTY_ARRAY; + + @Override + public Content.Type type() { + return Content.Type.BIOME_STATIC_LAYER; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (this.entries.length != 0) { + if (event instanceof ModifyEvents.BiomeStaticLayers container) { + container.entries().add(new LayerContainer.StaticLayer(this.buildEntries(event.seed()), this.noiseMask.build(event.seed()), 0)); + } + } + } + + protected LayerContainer.StaticLayerEntry[] buildEntries(@Nonnull SeedString seed) { + LayerContainer.StaticLayerEntry[] layerEntries = new LayerContainer.StaticLayerEntry[this.entries.length]; + + for (int i = 0; i < this.entries.length; i++) { + layerEntries[i] = this.entries[i].build(seed); + } + + return layerEntries; + } + + public static class StaticEntry extends LayerContent.Entry { + public static final BuilderCodec CODEC = BuilderCodec.builder( + BiomeStaticLayerContent.StaticEntry.class, BiomeStaticLayerContent.StaticEntry::new, LayerContent.Entry.CODEC + ) + .documentation("Define an entry that generates within the static layer") + .append(new KeyedCodec<>("MinY", Codecs.INT_RANGE), (t, v) -> t.minY = v, t -> t.minY) + .documentation("The range for the minimum y-height of the layer") + .add() + .append(new KeyedCodec<>("MinYNoise", NoiseMask.CODEC), (t, v) -> t.minYNoise = v, t -> t.minYNoise) + .documentation("The noise for modulating the minimum height of the layer") + .add() + .append(new KeyedCodec<>("MaxY", Codecs.INT_RANGE), (t, v) -> t.maxY = v, t -> t.maxY) + .documentation("The range for the maximum y-height of the layer") + .add() + .append(new KeyedCodec<>("MaxYNoise", NoiseMask.CODEC), (t, v) -> t.maxYNoise = v, t -> t.maxYNoise) + .documentation("The noise for modulating the maximum height of the layer") + .add() + .build(); + public static final ArrayCodec ARRAY_CODEC = new ArrayCodec<>(CODEC, BiomeStaticLayerContent.StaticEntry[]::new); + protected static final BiomeStaticLayerContent.StaticEntry[] EMPTY_ARRAY = new BiomeStaticLayerContent.StaticEntry[0]; + protected static final IntRange DEFAULT_MIN = new IntRange(0, 0); + protected static final IntRange DEFAULT_MAX = new IntRange(319, 319); + protected IntRange minY = DEFAULT_MIN; + protected NoiseMask minYNoise = NoiseMask.ZERO; + protected IntRange maxY = DEFAULT_MAX; + protected NoiseMask maxYNoise = NoiseMask.ZERO; + + public LayerContainer.StaticLayerEntry build(SeedString seed) { + return new LayerContainer.StaticLayerEntry( + NoiseBlockEntry.buildArray(seed, this.blocks), + this.entryNoiseMask.build(seed), + new DoubleRangeNoiseSupplier(new DoubleRange.Normal(this.minY.getInclusiveMin(), this.minY.getInclusiveMax()), this.minYNoise.buildNoise(seed)), + new DoubleRangeNoiseSupplier(new DoubleRange.Normal(this.maxY.getInclusiveMin(), this.maxY.getInclusiveMax()), this.maxYNoise.buildNoise(seed)) + ); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/LayerContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/LayerContent.java new file mode 100644 index 00000000..d8a24ea3 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/LayerContent.java @@ -0,0 +1,29 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.layer; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.NoiseMask; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; + +public abstract class LayerContent implements Content { + public static final BuilderCodec CODEC = BuilderCodec.abstractBuilder(LayerContent.class) + .append(new KeyedCodec<>("NoiseMask", NoiseMask.CODEC), (t, v) -> t.noiseMask = v, t -> t.noiseMask) + .documentation("The noise mask used to determine where the layer should be placed") + .add() + .build(); + protected NoiseMask noiseMask = NoiseMask.DEFAULT; + + public static class Entry { + public static final BuilderCodec CODEC = BuilderCodec.builder(LayerContent.Entry.class, LayerContent.Entry::new) + .documentation("An entry within a layer containing blocks and a noise mask") + .append(new KeyedCodec<>("Blocks", NoiseBlockEntry.ARRAY_CODEC), (t, v) -> t.blocks = v, t -> t.blocks) + .documentation("The collection of blocks to use for this entry") + .add() + .append(new KeyedCodec<>("NoiseMask", NoiseMask.CODEC), (t, v) -> t.entryNoiseMask = v, t -> t.entryNoiseMask) + .documentation("The noise mask used to determine where the blocks should be placed") + .add() + .build(); + protected NoiseBlockEntry[] blocks = NoiseBlockEntry.EMPTY_ARRAY; + protected NoiseMask entryNoiseMask = NoiseMask.DEFAULT; + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/NoiseBlockEntry.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/NoiseBlockEntry.java new file mode 100644 index 00000000..e488e7d3 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/layer/NoiseBlockEntry.java @@ -0,0 +1,63 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.layer; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.NoiseMask; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.math.range.IntRange; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.procedurallib.supplier.DoubleRange; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import com.hypixel.hytale.server.worldgen.util.ConstantNoiseProperty; +import com.hypixel.hytale.server.worldgen.util.NoiseBlockArray; +import javax.annotation.Nonnull; + +public class NoiseBlockEntry { + public static final BuilderCodec CODEC = BuilderCodec.builder(NoiseBlockEntry.class, NoiseBlockEntry::new) + .documentation("Defines a block entry that is repeated based on a noise mask") + .append(new KeyedCodec<>("Block", Codecs.BLOCK_TYPE), (t, v) -> t.block = v, t -> t.block) + .documentation("The block type of the entry") + .add() + .append(new KeyedCodec<>("Noise", NoiseMask.CODEC), (t, v) -> t.noise = v, t -> t.noise) + .documentation("The noise mask used to determine where the block should be placed") + .add() + .append(new KeyedCodec<>("Repetitions", Codecs.INT_RANGE), (t, v) -> t.repetitions = v, t -> t.repetitions) + .documentation("The number of times the block should be repeated") + .add() + .build(); + public static final ArrayCodec ARRAY_CODEC = new ArrayCodec<>(CODEC, NoiseBlockEntry[]::new); + public static final NoiseBlockEntry[] EMPTY_ARRAY = new NoiseBlockEntry[0]; + protected static final IntRange DEFAULT_REPETITIONS = new IntRange(1, 1); + protected static final NoiseBlockArray.Entry NOOP_ENTRY = new NoiseBlockArray.Entry( + "Empty", BlockFluidEntry.EMPTY, DoubleRange.ZERO, ConstantNoiseProperty.DEFAULT_ZERO + ); + protected String block = "Unknown"; + protected NoiseMask noise = NoiseMask.DEFAULT; + protected IntRange repetitions = DEFAULT_REPETITIONS; + + @Nonnull + public NoiseBlockArray.Entry buildEntry(@Nonnull SeedString seed) { + int blockId = BlockType.getAssetMap().getIndex(this.block); + return blockId == 1 + ? NOOP_ENTRY + : new NoiseBlockArray.Entry( + this.block, + new BlockFluidEntry(blockId, 0, 0), + new DoubleRange.Normal(this.repetitions.getInclusiveMin(), this.repetitions.getInclusiveMax()), + this.noise.buildNoise(seed) + ); + } + + public static NoiseBlockArray buildArray(@Nonnull SeedString seed, NoiseBlockEntry[] blocks) { + NoiseBlockArray.Entry[] entries = new NoiseBlockArray.Entry[blocks.length]; + + for (int i = 0; i < blocks.length; i++) { + entries[i] = blocks[i].buildEntry(seed); + } + + return new NoiseBlockArray(entries); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/BiomePrefabContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/BiomePrefabContent.java new file mode 100644 index 00000000..59ab826b --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/BiomePrefabContent.java @@ -0,0 +1,80 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.prefab; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.BlockMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.HeightMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.PointGrid; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.container.PrefabContainer; +import com.hypixel.hytale.server.worldgen.prefab.PrefabCategory; +import com.hypixel.hytale.server.worldgen.prefab.PrefabPatternGenerator; +import javax.annotation.Nonnull; + +public class BiomePrefabContent extends PrefabContent { + public static final String ID = "BiomePrefab"; + public static final BuilderCodec CODEC = BuilderCodec.builder(BiomePrefabContent.class, BiomePrefabContent::new, PrefabContent.CODEC) + .documentation("Define a set of one or more prefabs that should be generated in the target biome(s)") + .append(new KeyedCodec<>("PointGrid", PointGrid.CODEC), (t, v) -> t.grid = v, t -> t.grid) + .documentation("The point grid for the prefab") + .add() + .append(new KeyedCodec<>("FitHeightmap", Codec.BOOLEAN), (t, v) -> t.fitHeightmap = v, t -> t.fitHeightmap) + .documentation("Whether the prefab should fit to the heightmap") + .add() + .append(new KeyedCodec<>("Submerge", Codec.BOOLEAN), (t, v) -> t.submerge = v, t -> t.submerge) + .documentation("Whether the prefab should be submerged if placed underwater") + .add() + .append(new KeyedCodec<>("ParentMask", BlockMask.CODEC), (p, v) -> p.parentMask = v, p -> p.parentMask) + .documentation("Define which blocks the prefab can be placed on") + .add() + .afterDecode(PrefabContent::rebuild) + .build(); + protected static final int DEFAULT_MAX_SIZE = 24; + protected static final int DEFAULT_EXCLUSION_RADIUS = 0; + protected static final boolean DEFAULT_DEEP_SEARCH = false; + protected static final boolean DEFAULT_ON_FLUID = false; + protected boolean fitHeightmap = false; + protected boolean submerge = false; + protected PointGrid grid = PointGrid.DEFAULT; + protected BlockMask parentMask = BlockMask.REPLACE_SOLID; + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (this.prefabs.length != 0) { + if (event instanceof ModifyEvents.BiomePrefabs container) { + container.entries() + .add( + new PrefabContainer.PrefabContainerEntry( + this.buildPrefabs(event.seed()), this.buildPattern(event.seed()), Environment.getAssetMap().getIndex(this.environment.getId()) + ) + ); + } + } + } + + protected PrefabPatternGenerator buildPattern(@Nonnull SeedString seed) { + return new PrefabPatternGenerator( + seed.hashCode(), + PrefabCategory.NONE, + this.grid.build(seed), + this.heightMask.getCondition(), + HeightMask.DEFAULT_HEIGHT_THRESHOLD, + this.blockMask.getBlockMask(), + this.noiseMask.build(seed), + this.parentMask.getCondition(), + this.rotations, + this.offset, + this.fitHeightmap, + false, + false, + this.submerge, + 24, + 0 + ); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/CavePrefabContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/CavePrefabContent.java new file mode 100644 index 00000000..2916c521 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/CavePrefabContent.java @@ -0,0 +1,72 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.prefab; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Codecs; +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.math.range.IntRange; +import com.hypixel.hytale.math.util.HashUtil; +import com.hypixel.hytale.procedurallib.condition.ConstantIntCondition; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.procedurallib.supplier.DoubleRange; +import com.hypixel.hytale.procedurallib.supplier.IDoubleCoordinateHashSupplier; +import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.cave.CavePrefabPlacement; +import com.hypixel.hytale.server.worldgen.cave.prefab.CavePrefabContainer; +import javax.annotation.Nonnull; + +public class CavePrefabContent extends PrefabContent { + public static final String ID = "CavePrefab"; + public static final BuilderCodec CODEC = BuilderCodec.builder(CavePrefabContent.class, CavePrefabContent::new, PrefabContent.CODEC) + .documentation("Define a set of one or more prefabs that should be generated in the target cave(s)") + .append(new KeyedCodec<>("Placement", new EnumCodec<>(CavePrefabPlacement.class)), (t, v) -> t.placement = v, t -> t.placement) + .documentation("The cave prefab placement mode") + .add() + .append(new KeyedCodec<>("Iterations", Codecs.INT_RANGE), (t, v) -> t.iterations = v, t -> t.iterations) + .documentation("The iteration range for cave prefab placement") + .add() + .build(); + protected CavePrefabPlacement placement = CavePrefabPlacement.FLOOR; + protected IntRange iterations = new IntRange(1, 1); + + @Override + public Content.Type type() { + return Content.Type.CAVE_PREFAB; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (this.prefabs.length != 0) { + if (event instanceof ModifyEvents.CavePrefabs container) { + container.entries().add(new CavePrefabContainer.CavePrefabEntry(this.buildPrefabs(event.seed()), this.buildConfig(event.seed()))); + } + } + } + + protected CavePrefabContainer.CavePrefabEntry.CavePrefabConfig buildConfig(@Nonnull SeedString seed) { + return new CavePrefabContainer.CavePrefabEntry.CavePrefabConfig( + this.rotations, + this.placement, + ConstantIntCondition.DEFAULT_TRUE, + this.blockMask.getBlockMask(), + this.buildIterations(), + this.buildDisplacementModifier(), + this.noiseMask.build(seed), + this.heightMask.getCondition() + ); + } + + protected IDoubleCoordinateHashSupplier buildDisplacementModifier() { + return (seed, x, y, hash) -> HashUtil.random(seed, x, y, hash) * this.offsetY; + } + + protected IDoubleRange buildIterations() { + return (IDoubleRange)(this.iterations.getInclusiveMax() == this.iterations.getInclusiveMin() + ? new DoubleRange.Constant(this.iterations.getInclusiveMin()) + : new DoubleRange.Normal(this.iterations.getInclusiveMin(), this.iterations.getInclusiveMax())); + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/PrefabContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/PrefabContent.java new file mode 100644 index 00000000..b646475f --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/prefab/PrefabContent.java @@ -0,0 +1,107 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.prefab; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.BlockMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.HeightMask; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.NoiseMask; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.common.map.IWeightedMap; +import com.hypixel.hytale.common.map.WeightedMap; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; +import com.hypixel.hytale.server.core.prefab.PrefabRotation; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.loader.WorldGenPrefabLoader; +import com.hypixel.hytale.server.worldgen.loader.WorldGenPrefabSupplier; +import com.hypixel.hytale.server.worldgen.util.function.ConstantCoordinateDoubleSupplier; +import com.hypixel.hytale.server.worldgen.util.function.ICoordinateDoubleSupplier; +import javax.annotation.Nonnull; + +public abstract class PrefabContent implements Content { + public static final BuilderCodec CODEC = BuilderCodec.abstractBuilder(PrefabContent.class) + .append(new KeyedCodec<>("Prefabs", PrefabContent.Entry.ARRAY_CODEC), (t, v) -> t.prefabs = v, t -> t.prefabs) + .documentation("Weighted list of prefab paths to randomly choose from") + .add() + .append(new KeyedCodec<>("BlockMask", BlockMask.CODEC), (t, v) -> t.blockMask = v, t -> t.blockMask) + .documentation("The types of blocks that the prefab's blocks can replace") + .add() + .append(new KeyedCodec<>("NoiseMask", NoiseMask.CODEC), (t, v) -> t.noiseMask = v, t -> t.noiseMask) + .documentation("The horizontal noise mask for the prefab placement position") + .add() + .append(new KeyedCodec<>("HeightMask", HeightMask.CODEC), (t, v) -> t.heightMask = v, t -> t.heightMask) + .documentation("The height range mask for the prefab placement position") + .add() + .append(new KeyedCodec<>("OffsetY", Codec.INTEGER), (t, v) -> t.offsetY = v, t -> t.offsetY) + .documentation("The vertical offset for the prefab placement position") + .add() + .append( + new KeyedCodec<>("Rotations", new ArrayCodec<>(new EnumCodec<>(PrefabRotation.class), PrefabRotation[]::new)), + (t, v) -> t.rotations = v, + t -> t.rotations + ) + .documentation("The rotation directions that the prefab can be randomly placed in") + .add() + .append(new KeyedCodec<>("Environment", Environment.CODEC), (t, v) -> t.environment = v, t -> t.environment) + .documentation("The environment to place where the prefab is placed") + .add() + .afterDecode(PrefabContent::rebuild) + .build(); + protected PrefabContent.Entry[] prefabs = PrefabContent.Entry.EMPTY_ARRAY; + protected NoiseMask noiseMask = NoiseMask.DEFAULT; + protected HeightMask heightMask = HeightMask.DEFAULT; + protected BlockMask blockMask = BlockMask.REPLACE_ANY; + protected int offsetY = 0; + protected PrefabRotation[] rotations = PrefabRotation.VALUES; + protected Environment environment = Environment.UNKNOWN; + protected transient ICoordinateDoubleSupplier offset = ConstantCoordinateDoubleSupplier.DEFAULT_ZERO; + + @Override + public Content.Type type() { + return Content.Type.BIOME_PREFAB; + } + + @Override + public abstract void applyTo(ModifyEvent var1) throws Exception; + + protected void rebuild() { + this.offset = new ConstantCoordinateDoubleSupplier(this.offsetY); + } + + protected IWeightedMap buildPrefabs(@Nonnull SeedString seed) { + WorldGenPrefabLoader loader = seed.get().getLoader(); + WeightedMap.Builder builder = WeightedMap.builder(WorldGenPrefabSupplier.EMPTY_ARRAY); + + for (PrefabContent.Entry entry : this.prefabs) { + WorldGenPrefabSupplier[] prefabs = loader.get(entry.path); + if (prefabs != null) { + for (WorldGenPrefabSupplier prefab : prefabs) { + builder.put(prefab, entry.weight); + } + } + } + + return builder.build(); + } + + public static class Entry { + public static final BuilderCodec CODEC = BuilderCodec.builder(PrefabContent.Entry.class, PrefabContent.Entry::new) + .append(new KeyedCodec<>("Path", Codec.STRING), (t, v) -> t.path = v, t -> t.path) + .documentation("The path of the prefab") + .add() + .append(new KeyedCodec<>("Weight", Codec.DOUBLE), (t, v) -> t.weight = v, t -> t.weight) + .addValidator(Validators.range(0.0, 100.0)) + .documentation("The random chance of the path being chosen") + .add() + .build(); + public static final ArrayCodec ARRAY_CODEC = new ArrayCodec<>(CODEC, PrefabContent.Entry[]::new); + public static final PrefabContent.Entry[] EMPTY_ARRAY = new PrefabContent.Entry[0]; + private String path = ""; + private double weight = 1.0; + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/content/tint/BiomeTintContent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/tint/BiomeTintContent.java new file mode 100644 index 00000000..01a0a60b --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/content/tint/BiomeTintContent.java @@ -0,0 +1,85 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.content.tint; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.content.common.NoiseMask; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.common.map.IWeightedMap; +import com.hypixel.hytale.common.map.WeightedMap; +import com.hypixel.hytale.common.util.ArrayUtil; +import com.hypixel.hytale.protocol.Color; +import com.hypixel.hytale.server.core.asset.util.ColorParseUtil; +import com.hypixel.hytale.server.core.codec.ProtocolCodecs; +import com.hypixel.hytale.server.worldgen.container.TintContainer; + +public class BiomeTintContent implements Content { + public static final String ID = "BiomeTint"; + public static final BuilderCodec CODEC = BuilderCodec.builder(BiomeTintContent.class, BiomeTintContent::new) + .documentation("Define a set of tints that should be applied to parts of the biome") + .append( + new KeyedCodec<>("Entries", new ArrayCodec<>(BiomeTintContent.Entry.CODEC, BiomeTintContent.Entry[]::new)), (t, v) -> t.entries = v, t -> t.entries + ) + .documentation("The list of weighted colors that will be selected from in order to tint parts of the world") + .add() + .append(new KeyedCodec<>("Noise", NoiseMask.CODEC), (t, v) -> t.noise = v, t -> t.noise) + .documentation("The noise used to select a random tint from the entries list") + .add() + .append(new KeyedCodec<>("NoiseMask", NoiseMask.CODEC), (t, v) -> t.noiseMask = v, t -> t.noiseMask) + .documentation("The noise mask determining where the tint should be applied in the world") + .add() + .build(); + protected BiomeTintContent.Entry[] entries = BiomeTintContent.Entry.EMPTY_ARRAY; + protected NoiseMask noise = NoiseMask.DEFAULT; + protected NoiseMask noiseMask = NoiseMask.DEFAULT; + + @Override + public Content.Type type() { + return Content.Type.BIOME_TINT; + } + + @Override + public void applyTo(ModifyEvent event) throws Exception { + if (this.entries.length != 0) { + if (event instanceof ModifyEvents.BiomeTints container) { + container.entries() + .add(new TintContainer.TintContainerEntry(this.buildColors(), this.noise.buildNoise(event.seed()), this.noiseMask.build(event.seed()))); + } + } + } + + protected IWeightedMap buildColors() { + WeightedMap.Builder map = WeightedMap.builder(ArrayUtil.EMPTY_INTEGER_ARRAY); + + for (BiomeTintContent.Entry entry : this.entries) { + map.put(entry.color(), entry.weight); + } + + return map.build(); + } + + public static class Entry { + public static final BuilderCodec CODEC = BuilderCodec.builder(BiomeTintContent.Entry.class, BiomeTintContent.Entry::new) + .documentation("Define a weighted color entry for biome tinting") + .append(new KeyedCodec<>("Color", ProtocolCodecs.COLOR), (e, v) -> e.color = v, e -> e.color) + .documentation("The color value") + .add() + .append(new KeyedCodec<>("Weight", Codec.DOUBLE), (e, v) -> e.weight = v, e -> e.weight) + .addValidator(Validators.range(0.0, 100.0)) + .documentation("The relative weight of this color entry") + .add() + .build(); + public static final BiomeTintContent.Entry[] EMPTY_ARRAY = new BiomeTintContent.Entry[0]; + protected static final Color DEFAULT_COLOR = new Color(); + protected Color color = DEFAULT_COLOR; + protected double weight = 1.0; + + public Integer color() { + return ColorParseUtil.colorToARGBInt(this.color); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/event/ModifyEvent.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/event/ModifyEvent.java new file mode 100644 index 00000000..830bbdbc --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/event/ModifyEvent.java @@ -0,0 +1,54 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.event; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.event.IEvent; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.loader.context.FileContext; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface ModifyEvent extends IEvent { + SeedString seed(); + + @Nonnull + Content.Type type(); + + @Nonnull + FileContext file(); + + @Nonnull + List entries(); + + @Nonnull + ModifyEvent.ContentLoader loader(); + + static > void dispatch(@Nonnull Class type, @Nonnull E event) throws Error { + try { + HytaleServer.get().getEventBus().dispatchFor(type, event.type()).dispatch(event); + } catch (Throwable var3) { + throw new Error(String.format("Failed to invoke ModifyEvent %s for file %s", event.type(), event.file().getContentPath()), var3); + } + } + + @FunctionalInterface + public interface ContentLoader { + @Nullable + T load(@Nonnull String var1) throws Exception; + } + + public static class SeedGenerator { + private final SeedString seed; + private int id = 0; + + public SeedGenerator(@Nonnull SeedString seed) { + this.seed = seed; + } + + public SeedString next() { + return this.seed.append("-modified-" + this.id++); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/event/ModifyEvents.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/event/ModifyEvents.java new file mode 100644 index 00000000..0763cb87 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/event/ModifyEvents.java @@ -0,0 +1,150 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.event; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.procedurallib.json.SeedString; +import com.hypixel.hytale.server.worldgen.SeedStringResource; +import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; +import com.hypixel.hytale.server.worldgen.cave.CaveType; +import com.hypixel.hytale.server.worldgen.cave.prefab.CavePrefabContainer; +import com.hypixel.hytale.server.worldgen.container.CoverContainer; +import com.hypixel.hytale.server.worldgen.container.EnvironmentContainer; +import com.hypixel.hytale.server.worldgen.container.LayerContainer; +import com.hypixel.hytale.server.worldgen.container.PrefabContainer; +import com.hypixel.hytale.server.worldgen.container.TintContainer; +import com.hypixel.hytale.server.worldgen.container.WaterContainer; +import com.hypixel.hytale.server.worldgen.loader.context.BiomeFileContext; +import com.hypixel.hytale.server.worldgen.loader.context.CaveFileContext; +import java.util.List; +import javax.annotation.Nonnull; + +public interface ModifyEvents { + public record BiomeCovers( + @Nonnull SeedString seed, + @Nonnull BiomeFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.BIOME_COVER; + } + } + + public record BiomeDynamicLayers( + @Nonnull SeedString seed, + @Nonnull BiomeFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.BIOME_DYNAMIC_LAYER; + } + } + + public record BiomeEnvironments( + @Nonnull SeedString seed, + @Nonnull BiomeFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.BIOME_ENVIRONMENT; + } + } + + public record BiomeFluids( + @Nonnull SeedString seed, + @Nonnull BiomeFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.BIOME_FLUID; + } + } + + public record BiomePrefabs( + @Nonnull SeedString seed, + @Nonnull BiomeFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.BIOME_PREFAB; + } + } + + public record BiomeStaticLayers( + @Nonnull SeedString seed, + @Nonnull BiomeFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.BIOME_STATIC_LAYER; + } + } + + public record BiomeTints( + @Nonnull SeedString seed, + @Nonnull BiomeFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.BIOME_TINT; + } + } + + public record CaveCovers( + @Nonnull SeedString seed, + @Nonnull CaveFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.CAVE_COVER; + } + } + + public record CavePrefabs( + @Nonnull SeedString seed, + @Nonnull CaveFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.CAVE_PREFAB; + } + } + + public record CaveTypes( + @Nonnull SeedString seed, + @Nonnull CaveFileContext file, + @Nonnull List entries, + @Nonnull ModifyEvent.ContentLoader loader + ) implements ModifyEvent { + @Nonnull + @Override + public Content.Type type() { + return Content.Type.CAVE_TYPE; + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/op/AddOp.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/op/AddOp.java new file mode 100644 index 00000000..e235582d --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/op/AddOp.java @@ -0,0 +1,39 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.op; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.server.worldgen.util.LogUtil; +import java.util.logging.Level; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class AddOp implements Op { + public static final String ID = "Add"; + public static final BuilderCodec CODEC = BuilderCodec.builder(AddOp.class, AddOp::new) + .documentation("Adds content to the target content list") + .append(new KeyedCodec<>("Content", Content.TYPE_CODEC), (instance, array) -> instance.content = array, instance -> instance.content) + .documentation("Content to add to the target content list") + .add() + .build(); + @Nullable + protected Content content = null; + + @Override + public Content.Type type() { + return this.content == null ? Content.Type.NONE : this.content.type(); + } + + @Override + public void apply(@Nonnull ModifyEvent event) throws Error { + if (this.content != null) { + try { + this.content.applyTo(event); + LogUtil.getLogger().at(Level.FINE).log("[%s] Added content %s to %s", event.type(), this.content, event.file().getContentPath()); + } catch (Throwable var3) { + throw new Error("Failed to load content " + this.content, var3); + } + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/op/LogOp.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/op/LogOp.java new file mode 100644 index 00000000..edd66c4f --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/op/LogOp.java @@ -0,0 +1,33 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.op; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.server.worldgen.util.LogUtil; +import java.util.logging.Level; +import javax.annotation.Nonnull; + +public class LogOp implements Op { + public static final String ID = "Log"; + public static final BuilderCodec CODEC = BuilderCodec.builder(LogOp.class, LogOp::new) + .documentation("Log the filepath of the content container for a given content-type") + .append(new KeyedCodec<>("ContentType", Content.Type.CODEC), (t, v) -> t.type = v, t -> t.type) + .documentation("The content-type to log the container of") + .add() + .build(); + @Nonnull + protected Content.Type type = Content.Type.NONE; + + @Override + public Content.Type type() { + return this.type; + } + + @Override + public void apply(@Nonnull ModifyEvent event) throws Error { + if (event.type() == this.type) { + LogUtil.getLogger().at(Level.INFO).log("[%s] %s", event.type(), event.file().getContentPath()); + } + } +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/op/Op.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/op/Op.java new file mode 100644 index 00000000..a6eb7c87 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/op/Op.java @@ -0,0 +1,18 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.op; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.codec.lookup.CodecMapCodec; +import javax.annotation.Nonnull; + +public interface Op { + Op[] EMPTY_ARRAY = new Op[0]; + String TYPE_KEY = "Type"; + CodecMapCodec TYPE_CODEC = new CodecMapCodec<>("Type"); + ArrayCodec ARRAY_CODEC = new ArrayCodec<>(TYPE_CODEC, Op[]::new); + + void apply(@Nonnull ModifyEvent var1) throws Error; + + Content.Type type(); +} diff --git a/src/com/hypixel/hytale/builtin/worldgen/modifier/op/RemoveOp.java b/src/com/hypixel/hytale/builtin/worldgen/modifier/op/RemoveOp.java new file mode 100644 index 00000000..27141799 --- /dev/null +++ b/src/com/hypixel/hytale/builtin/worldgen/modifier/op/RemoveOp.java @@ -0,0 +1,126 @@ +package com.hypixel.hytale.builtin.worldgen.modifier.op; + +import com.hypixel.hytale.builtin.worldgen.modifier.content.Content; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.common.map.IWeightedMap; +import com.hypixel.hytale.common.util.ArrayUtil; +import com.hypixel.hytale.common.util.StringUtil; +import com.hypixel.hytale.server.worldgen.cave.CaveType; +import com.hypixel.hytale.server.worldgen.cave.prefab.CavePrefabContainer; +import com.hypixel.hytale.server.worldgen.container.PrefabContainer; +import com.hypixel.hytale.server.worldgen.loader.WorldGenPrefabSupplier; +import com.hypixel.hytale.server.worldgen.util.ListPool; +import com.hypixel.hytale.server.worldgen.util.LogUtil; +import java.util.List; +import java.util.function.Function; +import java.util.logging.Level; +import javax.annotation.Nonnull; + +public class RemoveOp implements Op { + public static final String ID = "Remove"; + public static final BuilderCodec CODEC = BuilderCodec.builder(RemoveOp.class, RemoveOp::new) + .documentation("Removes matching content from the target content list") + .append(new KeyedCodec<>("ContentType", Content.Type.CODEC), (instance, type) -> instance.type = type, instance -> instance.type) + .documentation("The type of content to remove") + .add() + .append(new KeyedCodec<>("Rules", BuilderCodec.STRING_ARRAY), (instance, array) -> instance.rules = array, instance -> instance.rules) + .documentation( + "List of glob-matching rules to match entries. Rules are only implemented for Prefab paths and CaveType names, but can be set to '*' to remove all entries of any content type" + ) + .add() + .afterDecode(op -> op.isClearAll = ArrayUtil.contains(op.rules, "*")) + .build(); + private Content.Type type = Content.Type.NONE; + private String[] rules = ArrayUtil.EMPTY_STRING_ARRAY; + private transient boolean isClearAll = false; + + @Override + public Content.Type type() { + return this.type; + } + + @Override + public void apply(@Nonnull ModifyEvent event) throws Error { + if (this.rules.length != 0) { + if (this.isClearAll) { + event.entries().clear(); + } else { + switch (event) { + case ModifyEvents.BiomePrefabs prefabs: + removePrefabs(prefabs, this.rules, PrefabContainer.ENTRY_POOL, PrefabContainer.PrefabContainerEntry::getPrefabs); + break; + case ModifyEvents.CaveTypes types: + removeContent(types, this.rules, CaveType.ENTRY_POOL, CaveType::getName); + break; + case ModifyEvents.CavePrefabs prefabsx: + removePrefabs(prefabsx, this.rules, CavePrefabContainer.ENTRY_POOL, CavePrefabContainer.CavePrefabEntry::getPrefabs); + break; + default: + } + } + } + } + + protected static void removePrefabs( + @Nonnull ModifyEvent event, + @Nonnull String[] rules, + @Nonnull ListPool pool, + @Nonnull Function> prefabGetter + ) { + try (ListPool.Resource temp = pool.acquire(event.entries().size())) { + label53: + for (int i = 0; i < event.entries().size(); i++) { + T entry = event.entries().get(i); + IWeightedMap prefabs = prefabGetter.apply(entry); + + for (WorldGenPrefabSupplier prefab : prefabs.internalKeys()) { + String identity = prefab.getPrefabKey(); + + for (String rule : rules) { + if (StringUtil.isGlobMatching(rule, identity)) { + continue label53; + } + } + } + + temp.add(entry); + } + + modifyEventContent(temp, event); + } + } + + protected static void removeContent( + @Nonnull ModifyEvent event, @Nonnull String[] rules, @Nonnull ListPool pool, @Nonnull Function identityGetter + ) { + try (ListPool.Resource temp = pool.acquire(event.entries().size())) { + label44: + for (int i = 0; i < event.entries().size(); i++) { + T entry = event.entries().get(i); + String identity = identityGetter.apply(entry); + + for (String rule : rules) { + if (StringUtil.isGlobMatching(rule, identity)) { + continue label44; + } + } + + temp.add(entry); + } + + modifyEventContent(temp, event); + } + } + + protected static void modifyEventContent(@Nonnull List entries, @Nonnull ModifyEvent event) { + if (entries.size() < event.entries().size()) { + int count = event.entries().size() - entries.size(); + event.entries().clear(); + event.entries().addAll(entries); + LogUtil.getLogger().at(Level.FINE).log("[%s] Removed %d entries from: %s", event.type(), count, event.file().getContentPath()); + } + } +} diff --git a/src/com/hypixel/hytale/codec/builder/BuilderField.java b/src/com/hypixel/hytale/codec/builder/BuilderField.java index b4c182ce..754fc49e 100644 --- a/src/com/hypixel/hytale/codec/builder/BuilderField.java +++ b/src/com/hypixel/hytale/codec/builder/BuilderField.java @@ -349,6 +349,10 @@ public class BuilderField { this.validators.add(new LateValidator() { private LateValidator validator; + { + Objects.requireNonNull(FieldBuilder.this); + } + @Override public void accept(FieldType fieldType, ValidationResults results) { if (this.validator == null) { diff --git a/src/com/hypixel/hytale/codec/function/BsonFunctionCodec.java b/src/com/hypixel/hytale/codec/function/BsonFunctionCodec.java index 83d7f69d..93604f89 100644 --- a/src/com/hypixel/hytale/codec/function/BsonFunctionCodec.java +++ b/src/com/hypixel/hytale/codec/function/BsonFunctionCodec.java @@ -38,7 +38,7 @@ public class BsonFunctionCodec implements Codec, WrappedCodec { @Nonnull @Override public Schema toSchema(@Nonnull SchemaContext context) { - return this.codec.toSchema(context); + return context.refDefinition(this.codec); } @Nonnull diff --git a/src/com/hypixel/hytale/codec/validation/Validator.java b/src/com/hypixel/hytale/codec/validation/Validator.java index b071e6c0..bce6af97 100644 --- a/src/com/hypixel/hytale/codec/validation/Validator.java +++ b/src/com/hypixel/hytale/codec/validation/Validator.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.codec.validation; import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.codec.schema.SchemaContext; import com.hypixel.hytale.codec.schema.config.Schema; +import java.util.Objects; import java.util.function.BiConsumer; import javax.annotation.Nonnull; @@ -15,6 +16,10 @@ public interface Validator extends BiConsumer { default LateValidator late() { final Validator current = this; return new LateValidator() { + { + Objects.requireNonNull(Validator.this); + } + @Override public void accept(T t, ValidationResults results) { } diff --git a/src/com/hypixel/hytale/common/collection/BucketItemPool.java b/src/com/hypixel/hytale/common/collection/BucketItemPool.java index 3ee0a1dd..74e55993 100644 --- a/src/com/hypixel/hytale/common/collection/BucketItemPool.java +++ b/src/com/hypixel/hytale/common/collection/BucketItemPool.java @@ -1,21 +1,30 @@ package com.hypixel.hytale.common.collection; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Arrays; -import java.util.List; import javax.annotation.Nonnull; public class BucketItemPool { @Nonnull - protected final List> pool = new ObjectArrayList<>(); + protected BucketItem[] pool = new BucketItem[16]; + protected int size; public void deallocate(BucketItem[] entityHolders, int count) { - this.pool.addAll(Arrays.asList(entityHolders).subList(0, count)); + int required = this.size + count; + if (required > this.pool.length) { + this.pool = Arrays.copyOf(this.pool, Math.max(required, this.pool.length << 1)); + } + + System.arraycopy(entityHolders, 0, this.pool, this.size, count); + this.size += count; } public BucketItem allocate(E reference, double squaredDistance) { - int l = this.pool.size(); - BucketItem holder = l == 0 ? new BucketItem<>() : this.pool.remove(l - 1); - return holder.set(reference, squaredDistance); + if (this.size == 0) { + return new BucketItem().set(reference, squaredDistance); + } else { + BucketItem holder = this.pool[--this.size]; + this.pool[this.size] = null; + return holder.set(reference, squaredDistance); + } } } diff --git a/src/com/hypixel/hytale/common/plugin/Mod.java b/src/com/hypixel/hytale/common/plugin/Mod.java new file mode 100644 index 00000000..12b270b8 --- /dev/null +++ b/src/com/hypixel/hytale/common/plugin/Mod.java @@ -0,0 +1,211 @@ +package com.hypixel.hytale.common.plugin; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.function.Predicate; +import javax.annotation.Nonnull; + +public interface Mod { + PluginManifest getManifest(); + + default boolean isCoreMod() { + return false; + } + + @Nonnull + static List calculateLoadOrder(@Nonnull Map pending) throws ModLoadOrderException { + return calculateLoadOrder(pending, pluginIdentifier -> false); + } + + @Nonnull + static List calculateLoadOrder(@Nonnull Map pending, @Nonnull Predicate externalDepsValidator) throws ModLoadOrderException { + return calculateLoadOrder(pending, externalDepsValidator, List.of()); + } + + @Nonnull + static List calculateLoadOrder( + @Nonnull Map pending, @Nonnull Predicate externalDepsValidator, @Nonnull List manualOrder + ) throws ModLoadOrderException { + HashMap> nodes = new HashMap<>(pending.size()); + + for (Entry entry : pending.entrySet()) { + nodes.put(entry.getKey(), new Mod.EntryNode<>(entry.getValue())); + } + + HashSet classpathPlugins = new HashSet<>(); + + for (Entry entry : pending.entrySet()) { + if (entry.getValue().isCoreMod() && "Hytale".equals(entry.getKey().getGroup())) { + classpathPlugins.add(entry.getKey()); + } + } + + HashMap> missingDependencies = new HashMap<>(); + + for (Entry> node : nodes.entrySet()) { + PluginManifest manifest = node.getValue().mod.getManifest(); + + for (PluginIdentifier depId : manifest.getDependencies().keySet()) { + if (nodes.containsKey(depId)) { + node.getValue().edges.add(depId); + } else if (!externalDepsValidator.test(depId)) { + missingDependencies.computeIfAbsent(node.getKey(), k -> new HashSet<>()).add(depId); + } + } + + for (PluginIdentifier identifier : manifest.getOptionalDependencies().keySet()) { + Mod.EntryNode dep = nodes.get(identifier); + if (dep != null) { + node.getValue().edges.add(identifier); + } + } + + if (!classpathPlugins.contains(node.getKey())) { + node.getValue().edges.addAll(classpathPlugins); + } + } + + for (Entry entryx : pending.entrySet()) { + PluginManifest manifest = entryx.getValue().getManifest(); + + for (PluginIdentifier targetId : manifest.getLoadBefore().keySet()) { + Mod.EntryNode targetNode = nodes.get(targetId); + if (targetNode != null) { + targetNode.edges.add(entryx.getKey()); + } + } + } + + ArrayList filteredManualOrder = new ArrayList<>(); + HashSet seen = new HashSet<>(); + + for (PluginIdentifier id : manualOrder) { + if (nodes.containsKey(id) && seen.add(id)) { + filteredManualOrder.add(id); + } + } + + for (int i = 0; i < filteredManualOrder.size() - 1; i++) { + PluginIdentifier earlier = filteredManualOrder.get(i); + PluginIdentifier later = filteredManualOrder.get(i + 1); + Mod.EntryNode laterNode = nodes.get(later); + laterNode.edges.add(earlier); + laterNode.manualOrderEdges.add(earlier); + } + + if (!missingDependencies.isEmpty()) { + StringBuilder sb = new StringBuilder("Missing required dependencies:\n"); + + for (Entry> entryx : missingDependencies.entrySet()) { + sb.append(" ") + .append(entryx.getKey()) + .append(" requires: [") + .append(String.join(", ", entryx.getValue().stream().map(PluginIdentifier::toString).toList())) + .append("]\n"); + } + + throw new ModLoadOrderException(ModLoadOrderException.Type.MISSING_DEPENDENCY, sb.toString()); + } else { + ObjectArrayList loadOrder = new ObjectArrayList<>(nodes.size()); + + while (!nodes.isEmpty()) { + ArrayList resolved = new ArrayList<>(); + + for (Entry> entryx : nodes.entrySet()) { + if (entryx.getValue().edges.isEmpty()) { + resolved.add(entryx.getKey()); + } + } + + resolved.sort(Comparator.comparing(PluginIdentifier::toString)); + if (resolved.isEmpty()) { + boolean hasManualOrderConflict = false; + + for (Entry> entryxx : nodes.entrySet()) { + HashSet remaining = new HashSet<>(entryxx.getValue().manualOrderEdges); + remaining.retainAll(nodes.keySet()); + if (!remaining.isEmpty()) { + hasManualOrderConflict = true; + break; + } + } + + StringBuilder sb; + ModLoadOrderException.Type errorType; + if (hasManualOrderConflict) { + errorType = ModLoadOrderException.Type.MANUAL_ORDER_CONFLICT; + sb = new StringBuilder("Manual load order conflicts with plugin dependencies:\n"); + + for (Entry> entryxxx : nodes.entrySet()) { + HashSet depEdges = new HashSet<>(entryxxx.getValue().edges); + depEdges.removeAll(entryxxx.getValue().manualOrderEdges); + HashSet manualEdges = new HashSet<>(entryxxx.getValue().manualOrderEdges); + manualEdges.retainAll(nodes.keySet()); + ArrayList parts = new ArrayList<>(); + if (!depEdges.isEmpty()) { + parts.add("[" + String.join(", ", depEdges.stream().map(PluginIdentifier::toString).toList()) + "] (dependencies)"); + } + + if (!manualEdges.isEmpty()) { + parts.add("[" + String.join(", ", manualEdges.stream().map(PluginIdentifier::toString).toList()) + "] (manual order)"); + } + + if (!parts.isEmpty()) { + sb.append(" ").append(entryxxx.getKey()).append(" waiting on: ").append(String.join(" ", parts)).append("\n"); + } + } + } else { + errorType = ModLoadOrderException.Type.CYCLIC_DEPENDENCY; + sb = new StringBuilder("Found cyclic dependency between plugins:\n"); + + for (Entry> entryxxx : nodes.entrySet()) { + sb.append(" ") + .append(entryxxx.getKey()) + .append(" waiting on: [") + .append(String.join(", ", entryxxx.getValue().edges.stream().map(PluginIdentifier::toString).toList())) + .append("]\n"); + } + } + + throw new ModLoadOrderException(errorType, sb.toString()); + } + + for (PluginIdentifier idx : resolved) { + loadOrder.add(nodes.get(idx).mod); + nodes.remove(idx); + } + + for (PluginIdentifier idx : resolved) { + for (Mod.EntryNode otherNode : nodes.values()) { + otherNode.edges.remove(idx); + } + } + } + + return loadOrder; + } + } + + public static final class EntryNode { + private final Set edges = new HashSet<>(); + private final Set manualOrderEdges = new HashSet<>(); + private final T mod; + + private EntryNode(T mod) { + this.mod = mod; + } + + @Nonnull + @Override + public String toString() { + return "EntryNode{plugin=" + this.mod + ", dependencies=" + this.edges + "}"; + } + } +} diff --git a/src/com/hypixel/hytale/common/plugin/ModLoadOrderException.java b/src/com/hypixel/hytale/common/plugin/ModLoadOrderException.java new file mode 100644 index 00000000..48686354 --- /dev/null +++ b/src/com/hypixel/hytale/common/plugin/ModLoadOrderException.java @@ -0,0 +1,24 @@ +package com.hypixel.hytale.common.plugin; + +import javax.annotation.Nonnull; + +public class ModLoadOrderException extends Exception { + @Nonnull + private final ModLoadOrderException.Type type; + + public ModLoadOrderException(@Nonnull ModLoadOrderException.Type type, @Nonnull String message) { + super(message); + this.type = type; + } + + @Nonnull + public ModLoadOrderException.Type getType() { + return this.type; + } + + public static enum Type { + MISSING_DEPENDENCY, + CYCLIC_DEPENDENCY, + MANUAL_ORDER_CONFLICT; + } +} diff --git a/src/com/hypixel/hytale/common/semver/Semver.java b/src/com/hypixel/hytale/common/semver/Semver.java index b293a4f0..d63df35b 100644 --- a/src/com/hypixel/hytale/common/semver/Semver.java +++ b/src/com/hypixel/hytale/common/semver/Semver.java @@ -219,8 +219,19 @@ public class Semver implements Comparable { private static void validateBuild(@Nullable String build) { if (build != null) { - if (build.isEmpty() || !StringUtil.isAlphaNumericHyphenString(build)) { - throw new IllegalArgumentException("Build must only be alphanumeric (" + build + ")"); + if (!build.isEmpty() && build.charAt(0) != '.' && build.charAt(build.length() - 1) != '.') { + for (int i = 0; i < build.length(); i++) { + char c = build.charAt(i); + if (c == '.') { + if (build.charAt(i - 1) == '.') { + throw new IllegalArgumentException("Build identifiers must only contain ASCII alphanumerics and hyphens [0-9A-Za-z-] (" + build + ")"); + } + } else if ((c < '0' || c > 'z' || c > '9' && c < 'A' || c > 'Z' && c < 'a') && c != '-') { + throw new IllegalArgumentException("Build identifiers must only contain ASCII alphanumerics and hyphens [0-9A-Za-z-] (" + build + ")"); + } + } + } else { + throw new IllegalArgumentException("Build identifiers must only contain ASCII alphanumerics and hyphens [0-9A-Za-z-] (" + build + ")"); } } } diff --git a/src/com/hypixel/hytale/common/util/StringUtil.java b/src/com/hypixel/hytale/common/util/StringUtil.java index a47de5f0..03516301 100644 --- a/src/com/hypixel/hytale/common/util/StringUtil.java +++ b/src/com/hypixel/hytale/common/util/StringUtil.java @@ -9,7 +9,6 @@ import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.function.DoubleFunction; import java.util.function.IntToDoubleFunction; import java.util.function.IntToLongFunction; @@ -124,199 +123,6 @@ public class StringUtil { return null; } - @Nonnull - @Deprecated(forRemoval = true) - public static String[] parseArgs(String rawString, @Nonnull Map argOptions) { - String[] rawSplit = RAW_ARGS_PATTERN.split(rawString, 2); - String argsStr = rawSplit[0]; - boolean hasRaw = rawSplit.length > 1; - char quote = 0; - int start = 0; - List argsList = new ObjectArrayList<>(); - - for (int i = 0; i < argsStr.length(); i++) { - char c = argsStr.charAt(i); - switch (c) { - case ' ': - if (quote == 0) { - if (start != i) { - argsList.add(argsStr.substring(start, i)); - } - - start = i + 1; - } - break; - case '"': - switch (quote) { - case '\u0000': - quote = '"'; - continue; - case '"': - quote = 0; - argsList.add(argsStr.substring(start, i + 1)); - start = i + 1; - default: - continue; - } - case '\'': - switch (quote) { - case '\u0000': - quote = '\''; - continue; - case '\'': - quote = 0; - argsList.add(argsStr.substring(start, i + 1)); - start = i + 1; - default: - continue; - } - case '\\': - if (i + 1 >= argsStr.length()) { - throw new IllegalStateException("Invalid escape at end of string, index " + (i + 1) + " in: " + rawString); - } - - char c1 = argsStr.charAt(i + 1); - switch (c1) { - case '"': - case '\'': - case '\\': - argsStr = argsStr.substring(0, i) + argsStr.substring(i + 1); - i++; - break; - default: - throw new IllegalStateException("Invalid escape for char " + c1 + " at index " + (i + 1) + " in: " + rawString); - } - } - } - - if (quote != 0) { - throw new IllegalStateException("Unbalanced quotes!"); - } else { - if (start != argsStr.length()) { - argsList.add(argsStr.substring(start)); - } - - argsList.removeIf(arg -> { - if (arg.startsWith("--")) { - String[] split = arg.substring(2).split("=", 2); - String value = ""; - if (split.length > 1) { - value = split[1]; - value = removeQuotes(value); - } - - argOptions.put(split[0], value); - return true; - } else { - return false; - } - }); - argsList.replaceAll(value -> removeQuotes(value.trim())); - if (hasRaw) { - String[] strings = new String[argsList.size() + 1]; - argsList.toArray(strings); - strings[argsList.size()] = rawSplit[1].trim(); - return strings; - } else { - return argsList.toArray(String[]::new); - } - } - } - - @Nonnull - public static String[] parseArgs(String rawString) { - String[] rawSplit = RAW_ARGS_PATTERN.split(rawString, 2); - String argsStr = rawSplit[0]; - boolean hasRaw = rawSplit.length > 1; - char quote = 0; - int start = 0; - List argsList = new ObjectArrayList<>(); - - for (int i = 0; i < argsStr.length(); i++) { - char c = argsStr.charAt(i); - switch (c) { - case ' ': - if (quote == 0) { - if (start != i) { - argsList.add(argsStr.substring(start, i)); - } - - start = i + 1; - } - break; - case '"': - switch (quote) { - case '\u0000': - quote = '"'; - continue; - case '"': - quote = 0; - argsList.add(argsStr.substring(start, i + 1)); - start = i + 1; - default: - continue; - } - case '\'': - switch (quote) { - case '\u0000': - quote = '\''; - continue; - case '\'': - quote = 0; - argsList.add(argsStr.substring(start, i + 1)); - start = i + 1; - default: - continue; - } - case '\\': - if (i + 1 >= argsStr.length()) { - throw new IllegalStateException("Invalid escape at end of string, index " + (i + 1) + " in: " + rawString); - } - - char c1 = argsStr.charAt(i + 1); - switch (c1) { - case '"': - case '\'': - case '\\': - argsStr = argsStr.substring(0, i) + argsStr.substring(i + 1); - i++; - break; - default: - throw new IllegalStateException("Invalid escape for char " + c1 + " at index " + (i + 1) + " in: " + rawString); - } - } - } - - if (quote != 0) { - throw new IllegalStateException("Unbalanced quotes!"); - } else { - if (start != argsStr.length()) { - argsList.add(argsStr.substring(start)); - } - - argsList.replaceAll(value -> removeQuotes(value.trim())); - if (hasRaw) { - String[] strings = new String[argsList.size() + 1]; - argsList.toArray(strings); - strings[argsList.size()] = rawSplit[1].trim(); - return strings; - } else { - return argsList.toArray(String[]::new); - } - } - } - - @Nonnull - public static String removeQuotes(@Nonnull String value) { - switch (value.charAt(0)) { - case '"': - case '\'': - value = value.substring(1, value.length() - 1); - default: - return value; - } - } - @Nonnull public static String stripQuotes(@Nonnull String s) { if (s.length() >= 2) { diff --git a/src/com/hypixel/hytale/component/Archetype.java b/src/com/hypixel/hytale/component/Archetype.java index bd12d655..4ed75fdc 100644 --- a/src/com/hypixel/hytale/component/Archetype.java +++ b/src/com/hypixel/hytale/component/Archetype.java @@ -14,8 +14,8 @@ public class Archetype implements Query { private final int count; @Nonnull private final ComponentType[] componentTypes; - @Nonnull - private final ExactArchetypeQuery exactQuery = new ExactArchetypeQuery<>(this); + @Nullable + private ExactArchetypeQuery exactQuery; public static Archetype empty() { return EMPTY; @@ -153,6 +153,10 @@ public class Archetype implements Query { @Nonnull public ExactArchetypeQuery asExactQuery() { + if (this.exactQuery == null) { + this.exactQuery = new ExactArchetypeQuery<>(this); + } + return this.exactQuery; } @@ -170,23 +174,22 @@ public class Archetype implements Query { @SafeVarargs public static Archetype of(@Nonnull ComponentType... componentTypes) { - if (componentTypes.length == 0) { + int componentTypesLen = componentTypes.length; + if (componentTypesLen == 0) { return EMPTY; } else { - for (int i = 0; i < componentTypes.length; i++) { - ComponentType componentType = componentTypes[i]; - if (componentType == null) { - throw new IllegalArgumentException("ComponentType in Archetype cannot be null (Index: " + i + ")"); - } - } - ComponentRegistry registry = componentTypes[0].getRegistry(); int minIndex = Integer.MAX_VALUE; int maxIndex = Integer.MIN_VALUE; - for (int ix = 0; ix < componentTypes.length; ix++) { - componentTypes[ix].validateRegistry(registry); - int index = componentTypes[ix].getIndex(); + for (int itr = 0; itr < componentTypesLen; itr++) { + ComponentType componentType = componentTypes[itr]; + if (componentType == null) { + throw new IllegalArgumentException("ComponentType in Archetype cannot be null (Index: " + itr + ")"); + } + + componentType.validateRegistry(registry); + int index = componentType.getIndex(); if (index < minIndex) { minIndex = index; } @@ -194,21 +197,20 @@ public class Archetype implements Query { if (index > maxIndex) { maxIndex = index; } - - for (int n = ix + 1; n < componentTypes.length; n++) { - if (componentTypes[ix] == componentTypes[n]) { - throw new IllegalArgumentException("ComponentType provided multiple times! " + Arrays.toString((Object[])componentTypes)); - } - } } ComponentType[] arr = new ComponentType[maxIndex + 1]; - for (ComponentType componentType : componentTypes) { - arr[componentType.getIndex()] = componentType; + for (ComponentType componentTypex : componentTypes) { + int indexx = componentTypex.getIndex(); + if (arr[indexx] != null) { + throw new IllegalArgumentException("ComponentType provided multiple times! " + Arrays.toString((Object[])componentTypes)); + } + + arr[indexx] = componentTypex; } - return new Archetype<>(minIndex, componentTypes.length, arr); + return new Archetype<>(minIndex, componentTypesLen, arr); } } diff --git a/src/com/hypixel/hytale/component/ArchetypeChunk.java b/src/com/hypixel/hytale/component/ArchetypeChunk.java index f394ec15..c19ce9ca 100644 --- a/src/com/hypixel/hytale/component/ArchetypeChunk.java +++ b/src/com/hypixel/hytale/component/ArchetypeChunk.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.component; import com.hypixel.hytale.common.util.ArrayUtil; import com.hypixel.hytale.function.consumer.IntObjectConsumer; import java.util.Arrays; +import java.util.BitSet; import java.util.function.Consumer; import java.util.function.IntPredicate; import javax.annotation.Nonnull; @@ -19,6 +20,8 @@ public class ArchetypeChunk { @Nonnull protected Ref[] refs = new Ref[16]; protected Component[][] components; + private final BitSet systemIndexes = new BitSet(); + private int archetypeIndex = Integer.MIN_VALUE; public static ArchetypeChunk[] emptyArray() { return EMPTY_ARRAY; @@ -32,7 +35,7 @@ public class ArchetypeChunk { for (int i = archetype.getMinIndex(); i < archetype.length(); i++) { ComponentType> componentType = (ComponentType>)archetype.get(i); if (componentType != null) { - this.components[componentType.getIndex()] = new Component[16]; + this.components[i] = new Component[16]; } } } @@ -90,23 +93,27 @@ public class ArchetypeChunk { ComponentType> componentType = (ComponentType>)this.archetype .get(i); if (componentType != null) { - int componentTypeIndex = componentType.getIndex(); - this.components[componentTypeIndex] = Arrays.copyOf(this.components[componentTypeIndex], newLength); + Component[] grown = Arrays.copyOf(this.components[i], newLength); + grown[entityIndex] = holder.getComponent((ComponentType>)componentType); + this.components[i] = grown; } } - } - this.refs[entityIndex] = ref; + this.refs[entityIndex] = ref; + return entityIndex; + } else { + this.refs[entityIndex] = ref; - for (int ix = this.archetype.getMinIndex(); ix < this.archetype.length(); ix++) { - ComponentType> componentType = (ComponentType>)this.archetype - .get(ix); - if (componentType != null) { - this.components[componentType.getIndex()][entityIndex] = holder.getComponent((ComponentType>)componentType); + for (int ix = this.archetype.getMinIndex(); ix < this.archetype.length(); ix++) { + ComponentType> componentType = (ComponentType>)this.archetype + .get(ix); + if (componentType != null) { + this.components[ix][entityIndex] = holder.getComponent((ComponentType>)componentType); + } } - } - return entityIndex; + return entityIndex; + } } } @@ -121,9 +128,8 @@ public class ArchetypeChunk { ComponentType> componentType = (ComponentType>)this.archetype .get(i); if (componentType != null) { - int componentTypeIndex = componentType.getIndex(); - Component component = this.components[componentTypeIndex][entityIndex]; - entityComponents[componentTypeIndex] = component.clone(); + Component component = this.components[i][entityIndex]; + entityComponents[i] = component.clone(); } } @@ -145,9 +151,8 @@ public class ArchetypeChunk { i ); if (componentType != null) { - int componentTypeIndex = componentType.getIndex(); - Component component = this.components[componentTypeIndex][entityIndex]; - entityComponents[componentTypeIndex] = component.cloneSerializable(); + Component component = this.components[i][entityIndex]; + entityComponents[i] = component.cloneSerializable(); } } @@ -161,32 +166,36 @@ public class ArchetypeChunk { if (entityIndex >= this.entitiesSize) { throw new IndexOutOfBoundsException(entityIndex); } else { - Component[] entityComponents = target.ensureComponentsSize(this.archetype.length()); - - for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); i++) { - ComponentType> componentType = (ComponentType>)this.archetype - .get(i); - if (componentType != null) { - int componentTypeIndex = componentType.getIndex(); - entityComponents[componentTypeIndex] = this.components[componentTypeIndex][entityIndex]; - } - } - int lastIndex = this.entitiesSize - 1; + Component[] entityComponents = target.ensureComponentsSize(this.archetype.length()); if (entityIndex != lastIndex) { - this.fillEmptyIndex(entityIndex, lastIndex); + Ref ref = this.refs[lastIndex]; + this.store.setEntityChunkIndex(ref, entityIndex); + this.refs[entityIndex] = ref; + + for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); i++) { + ComponentType> componentType = (ComponentType>)this.archetype + .get(i); + if (componentType != null) { + Component[] col = this.components[i]; + entityComponents[i] = col[entityIndex]; + col[entityIndex] = col[lastIndex]; + col[lastIndex] = null; + } + } + } else { + for (int ix = this.archetype.getMinIndex(); ix < this.archetype.length(); ix++) { + ComponentType> componentType = (ComponentType>)this.archetype + .get(ix); + if (componentType != null) { + Component[] col = this.components[ix]; + entityComponents[ix] = col[lastIndex]; + col[lastIndex] = null; + } + } } this.refs[lastIndex] = null; - - for (int ix = this.archetype.getMinIndex(); ix < this.archetype.length(); ix++) { - ComponentType> componentType = (ComponentType>)this.archetype - .get(ix); - if (componentType != null) { - this.components[componentType.getIndex()][lastIndex] = null; - } - } - this.entitiesSize = lastIndex; target.init(this.archetype, entityComponents); return target; @@ -204,14 +213,16 @@ public class ArchetypeChunk { for (int entityIndex = 0; entityIndex < this.entitiesSize; entityIndex++) { Ref ref = this.refs[entityIndex]; this.refs[entityIndex] = null; + Arrays.fill(entityComponents, 0, this.archetype.getMinIndex(), null); for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); i++) { ComponentType> componentType = (ComponentType>)this.archetype .get(i); - if (componentType != null) { - int componentTypeIndex = componentType.getIndex(); - entityComponents[componentTypeIndex] = this.components[componentTypeIndex][entityIndex]; - this.components[componentTypeIndex][entityIndex] = null; + if (componentType == null) { + entityComponents[i] = null; + } else { + entityComponents[i] = this.components[i][entityIndex]; + this.components[i][entityIndex] = null; } } @@ -232,8 +243,7 @@ public class ArchetypeChunk { @Nonnull IntObjectConsumer> referenceConsumer ) { int firstTransfered = Integer.MIN_VALUE; - int lastTransfered = Integer.MIN_VALUE; - Component[] entityComponents = new Component[this.archetype.length()]; + Component[] entityComponents = new Component[this.archetype.length()]; for (int entityIndex = 0; entityIndex < this.entitiesSize; entityIndex++) { if (shouldTransfer.test(entityIndex)) { @@ -241,17 +251,18 @@ public class ArchetypeChunk { firstTransfered = entityIndex; } - lastTransfered = entityIndex; Ref ref = this.refs[entityIndex]; this.refs[entityIndex] = null; + Arrays.fill(entityComponents, 0, this.archetype.getMinIndex(), null); for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); i++) { ComponentType> componentType = (ComponentType>)this.archetype .get(i); - if (componentType != null) { - int componentTypeIndex = componentType.getIndex(); - entityComponents[componentTypeIndex] = this.components[componentTypeIndex][entityIndex]; - this.components[componentTypeIndex][entityIndex] = null; + if (componentType == null) { + entityComponents[i] = null; + } else { + entityComponents[i] = this.components[i][entityIndex]; + this.components[i][entityIndex] = null; } } @@ -263,29 +274,31 @@ public class ArchetypeChunk { } if (firstTransfered != Integer.MIN_VALUE) { - if (lastTransfered == this.entitiesSize - 1) { - this.entitiesSize = firstTransfered; - return; - } + int writeIndex = firstTransfered; - int newSize = this.entitiesSize - (lastTransfered - firstTransfered + 1); - - for (int entityIndexx = firstTransfered; entityIndexx <= lastTransfered; entityIndexx++) { - if (this.refs[entityIndexx] == null) { - int lastIndex = this.entitiesSize - 1; - if (lastIndex == lastTransfered) { - break; + for (int readIndex = firstTransfered + 1; readIndex < this.entitiesSize; readIndex++) { + if (this.refs[readIndex] != null) { + if (writeIndex != readIndex) { + this.fillEmptyIndex(writeIndex, readIndex); } - if (entityIndexx != lastIndex) { - this.fillEmptyIndex(entityIndexx, lastIndex); - } - - this.entitiesSize--; + writeIndex++; } } - this.entitiesSize = newSize; + for (int ix = writeIndex; ix < this.entitiesSize; ix++) { + this.refs[ix] = null; + + for (int j = this.archetype.getMinIndex(); j < this.archetype.length(); j++) { + ComponentType> componentType = (ComponentType>)this.archetype + .get(j); + if (componentType != null) { + this.components[j][ix] = null; + } + } + } + + this.entitiesSize = writeIndex; } } @@ -296,7 +309,7 @@ public class ArchetypeChunk { for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); i++) { ComponentType> componentType = (ComponentType>)this.archetype.get(i); if (componentType != null) { - Component[] componentArr = this.components[componentType.getIndex()]; + Component[] componentArr = this.components[i]; componentArr[entityIndex] = componentArr[lastIndex]; } } @@ -304,6 +317,18 @@ public class ArchetypeChunk { this.refs[entityIndex] = ref; } + BitSet getSystemIndexes() { + return this.systemIndexes; + } + + int getArchetypeIndex() { + return this.archetypeIndex; + } + + void setArchetypeIndex(int archetypeIndex) { + this.archetypeIndex = archetypeIndex; + } + public void appendDump(@Nonnull String prefix, @Nonnull StringBuilder sb) { sb.append(prefix).append("archetype=").append(this.archetype).append("\n"); sb.append(prefix).append("entitiesSize=").append(this.entitiesSize).append("\n"); @@ -316,12 +341,7 @@ public class ArchetypeChunk { ComponentType> componentType = (ComponentType>)this.archetype .get(x); if (componentType != null) { - sb.append(prefix) - .append("\t\t- ") - .append(componentType.getIndex()) - .append("\t") - .append(this.components[componentType.getIndex()][x]) - .append("\n"); + sb.append(prefix).append("\t\t- ").append(x).append("\t").append(this.components[x][i]).append("\n"); } } } diff --git a/src/com/hypixel/hytale/component/CommandBuffer.java b/src/com/hypixel/hytale/component/CommandBuffer.java index a4fa1f39..d071ba69 100644 --- a/src/com/hypixel/hytale/component/CommandBuffer.java +++ b/src/com/hypixel/hytale/component/CommandBuffer.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.component; import com.hypixel.hytale.component.event.EntityEventType; +import com.hypixel.hytale.component.event.EntityHolderEventType; import com.hypixel.hytale.component.event.WorldEventType; import com.hypixel.hytale.component.system.EcsEvent; import java.util.ArrayDeque; @@ -290,6 +291,22 @@ public class CommandBuffer implements ComponentAccessor { this.store.internal_invoke(this, systemType, ref, param); } + @Override + public void invoke(@Nonnull Holder holder, @Nonnull Event param) { + assert Thread.currentThread() == this.thread; + + this.store.internal_invoke(this, holder, param); + } + + @Override + public void invoke( + @Nonnull EntityHolderEventType systemType, @Nonnull Holder holder, @Nonnull Event param + ) { + assert Thread.currentThread() == this.thread; + + this.store.internal_invoke(this, systemType, holder, param); + } + @Override public void invoke(@Nonnull Event param) { assert Thread.currentThread() == this.thread; diff --git a/src/com/hypixel/hytale/component/ComponentAccessor.java b/src/com/hypixel/hytale/component/ComponentAccessor.java index 20edb4f2..813f5d8c 100644 --- a/src/com/hypixel/hytale/component/ComponentAccessor.java +++ b/src/com/hypixel/hytale/component/ComponentAccessor.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.component; import com.hypixel.hytale.component.event.EntityEventType; +import com.hypixel.hytale.component.event.EntityHolderEventType; import com.hypixel.hytale.component.event.WorldEventType; import com.hypixel.hytale.component.system.EcsEvent; import javax.annotation.Nonnull; @@ -44,6 +45,10 @@ public interface ComponentAccessor { void invoke(@Nonnull EntityEventType var1, @Nonnull Ref var2, @Nonnull Event var3); + void invoke(@Nonnull Holder var1, @Nonnull Event var2); + + void invoke(@Nonnull EntityHolderEventType var1, @Nonnull Holder var2, @Nonnull Event var3); + void invoke(@Nonnull Event var1); void invoke(@Nonnull WorldEventType var1, @Nonnull Event var2); diff --git a/src/com/hypixel/hytale/component/ComponentRegistry.java b/src/com/hypixel/hytale/component/ComponentRegistry.java index 84e3f0a4..f4b14891 100644 --- a/src/com/hypixel/hytale/component/ComponentRegistry.java +++ b/src/com/hypixel/hytale/component/ComponentRegistry.java @@ -17,6 +17,7 @@ import com.hypixel.hytale.component.data.unknown.TempUnknownComponent; import com.hypixel.hytale.component.data.unknown.UnknownComponents; import com.hypixel.hytale.component.dependency.Dependency; import com.hypixel.hytale.component.event.EntityEventType; +import com.hypixel.hytale.component.event.EntityHolderEventType; import com.hypixel.hytale.component.event.WorldEventType; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.query.ReadWriteArchetypeQuery; @@ -24,6 +25,7 @@ import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialStructure; import com.hypixel.hytale.component.system.EcsEvent; import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.component.system.EntityHolderEventSystem; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.ISystem; import com.hypixel.hytale.component.system.QuerySystem; @@ -106,6 +108,8 @@ public class ComponentRegistry implements IComponentRegistry @Nonnull private final Object2IntMap> entityEventTypeClassToIndex = new Object2IntOpenHashMap<>(16); @Nonnull + private final Object2IntMap> entityHolderEventTypeClassToIndex = new Object2IntOpenHashMap<>(16); + @Nonnull private final Object2IntMap> worldEventTypeClassToIndex = new Object2IntOpenHashMap<>(16); private final BitSet systemTypeIndexReuse = new BitSet(); private int systemTypeSize; @@ -164,6 +168,7 @@ public class ComponentRegistry implements IComponentRegistry this.resourceIdToIndex.defaultReturnValue(Integer.MIN_VALUE); this.systemTypeClassToIndex.defaultReturnValue(Integer.MIN_VALUE); this.entityEventTypeClassToIndex.defaultReturnValue(Integer.MIN_VALUE); + this.entityHolderEventTypeClassToIndex.defaultReturnValue(Integer.MIN_VALUE); this.worldEventTypeClassToIndex.defaultReturnValue(Integer.MIN_VALUE); for (int i = 0; i < 16; i++) { @@ -540,6 +545,24 @@ public class ComponentRegistry implements IComponentRegistry } } + public void unregisterEntityHolderEventType(@Nonnull EntityHolderEventType eventType) { + if (this.shutdown) { + throw new IllegalStateException("Registry has been shutdown"); + } else { + eventType.validate(); + long lock = this.dataLock.writeLock(); + + try { + this.unregisterEntityHolderEventType0(eventType); + lock = this.dataLock.tryConvertToReadLock(lock); + this.updateData0(new SystemTypeChange<>(ChangeType.UNREGISTERED, eventType)); + eventType.invalidate(); + } finally { + this.dataLock.unlock(lock); + } + } + } + public void unregisterWorldEventType(@Nonnull WorldEventType eventType) { if (this.shutdown) { throw new IllegalStateException("Registry has been shutdown"); @@ -671,6 +694,12 @@ public class ComponentRegistry implements IComponentRegistry changes.add(new SystemTypeChange<>(ChangeType.REGISTERED, (SystemType>)eventType)); } + if (system instanceof EntityHolderEventSystem eventSystem + && !this.entityHolderEventTypeClassToIndex.containsKey(eventSystem.getEventType())) { + EntityHolderEventType eventType = this.registerEntityHolderEventType0(eventSystem.getEventType()); + changes.add(new SystemTypeChange<>(ChangeType.REGISTERED, (SystemType>)eventType)); + } + if (system instanceof WorldEventSystem eventSystem && !this.worldEventTypeClassToIndex.containsKey(eventSystem.getEventType())) { WorldEventType eventType = this.registerWorldEventType0(eventSystem.getEventType()); changes.add(new SystemTypeChange<>(ChangeType.REGISTERED, (SystemType>)eventType)); @@ -1062,6 +1091,54 @@ public class ComponentRegistry implements IComponentRegistry return index == Integer.MIN_VALUE ? null : (EntityEventType)this.systemTypes[index]; } + @Nonnull + private EntityHolderEventType registerEntityHolderEventType0(@Nonnull Class eventTypeClass) { + if (this.entityHolderEventTypeClassToIndex.containsKey(eventTypeClass)) { + throw new IllegalArgumentException("event type '" + eventTypeClass + "' already exists!"); + } else { + int systemTypeIndex; + if (this.systemTypeIndexReuse.isEmpty()) { + systemTypeIndex = this.systemTypeSize++; + } else { + systemTypeIndex = this.systemTypeIndexReuse.nextSetBit(0); + this.systemTypeIndexReuse.clear(systemTypeIndex); + } + + if (this.systemTypes.length <= systemTypeIndex) { + this.systemTypes = Arrays.copyOf(this.systemTypes, ArrayUtil.grow(systemTypeIndex)); + this.systemTypeToSystemIndex = Arrays.copyOf(this.systemTypeToSystemIndex, ArrayUtil.grow(systemTypeIndex)); + } + + EntityHolderEventType systemType = new EntityHolderEventType<>( + this, EntityHolderEventSystem.class, (Class)eventTypeClass, systemTypeIndex + ); + this.entityHolderEventTypeClassToIndex.put((Class)eventTypeClass, systemTypeIndex); + this.systemTypes[systemTypeIndex] = systemType; + return systemType; + } + } + + private void unregisterEntityHolderEventType0(@Nonnull EntityHolderEventType eventType) { + int systemTypeIndex = eventType.getIndex(); + if (systemTypeIndex == this.systemTypeSize - 1) { + int highestUsedIndex = this.systemTypeIndexReuse.previousClearBit(systemTypeIndex - 1); + this.systemTypeSize = highestUsedIndex + 1; + this.systemTypeIndexReuse.clear(this.systemTypeSize, systemTypeIndex); + } else { + this.systemTypeIndexReuse.set(systemTypeIndex); + } + + this.entityHolderEventTypeClassToIndex.removeInt(eventType.getEventClass()); + this.systemTypes[systemTypeIndex] = null; + this.systemTypeToSystemIndex[systemTypeIndex].clear(); + } + + @Nullable + public EntityHolderEventType getEntityHolderEventTypeForClass(Class eClass) { + int index = this.entityHolderEventTypeClassToIndex.getInt(eClass); + return index == Integer.MIN_VALUE ? null : (EntityHolderEventType)this.systemTypes[index]; + } + @Nonnull private WorldEventType registerWorldEventType0(@Nonnull Class eventTypeClass) { if (this.worldEventTypeClassToIndex.containsKey(eventTypeClass)) { diff --git a/src/com/hypixel/hytale/component/ComponentType.java b/src/com/hypixel/hytale/component/ComponentType.java index 96ef25e0..2b1e2afc 100644 --- a/src/com/hypixel/hytale/component/ComponentType.java +++ b/src/com/hypixel/hytale/component/ComponentType.java @@ -53,7 +53,7 @@ public class ComponentType> implements C @Override public void validateRegistry(@Nonnull ComponentRegistry registry) { - if (!this.registry.equals(registry)) { + if (this.registry != registry) { throw new IllegalArgumentException("ComponentType is for a different registry! " + this); } } @@ -75,7 +75,7 @@ public class ComponentType> implements C return true; } else if (o != null && this.getClass() == o.getClass()) { ComponentType that = (ComponentType)o; - return this.index != that.index ? false : this.registry.equals(that.registry); + return this.index != that.index ? false : this.registry == that.registry; } else { return false; } diff --git a/src/com/hypixel/hytale/component/Holder.java b/src/com/hypixel/hytale/component/Holder.java index 18b07b02..5a07ed6d 100644 --- a/src/com/hypixel/hytale/component/Holder.java +++ b/src/com/hypixel/hytale/component/Holder.java @@ -127,28 +127,62 @@ public class Holder { @Nonnull public > T ensureAndGetComponent(@Nonnull ComponentType componentType) { - this.ensureComponent(componentType); - return this.getComponent(componentType); + assert this.archetype != null; + + assert this.registry != null; + + if (this.ensureValidComponents) { + componentType.validate(); + } + + long writeStamp = this.lock.writeLock(); + + Component var5; + try { + if (this.archetype.contains(componentType)) { + return (T)this.components[componentType.getIndex()]; + } + + T component = this.registry.createComponent(componentType); + this.addComponent0(componentType, component); + var5 = component; + } finally { + this.lock.unlockWrite(writeStamp); + } + + return (T)var5; } public > void addComponent(@Nonnull ComponentType componentType, @Nonnull T component) { assert this.archetype != null; + if (!this.addComponentInternal(componentType, component)) { + throw new IllegalArgumentException("Entity contains component type: " + componentType); + } + } + + > boolean addComponentInternal(@Nonnull ComponentType componentType, @Nonnull T component) { + assert this.archetype != null; + long stamp = this.lock.writeLock(); + boolean var5; try { if (this.ensureValidComponents) { componentType.validate(); } - if (this.archetype.contains(componentType)) { - throw new IllegalArgumentException("Entity contains component type: " + componentType); + if (!this.archetype.contains(componentType)) { + this.addComponent0(componentType, component); + return true; } - this.addComponent0(componentType, component); + var5 = false; } finally { this.lock.unlockWrite(stamp); } + + return var5; } private > void addComponent0(@Nonnull ComponentType componentType, @Nonnull T component) { @@ -185,10 +219,25 @@ public class Holder { } public > void putComponent(@Nonnull ComponentType componentType, @Nonnull T component) { - if (this.getComponent(componentType) != null) { - this.replaceComponent(componentType, component); - } else { - this.addComponent(componentType, component); + assert this.archetype != null; + + assert this.components != null; + + if (this.ensureValidComponents) { + componentType.validate(); + } + + long stamp = this.lock.writeLock(); + + try { + if (this.archetype.contains(componentType)) { + this.archetype.validateComponentType(componentType); + this.components[componentType.getIndex()] = component; + } else { + this.addComponent0(componentType, component); + } + } finally { + this.lock.unlockWrite(stamp); } } @@ -239,12 +288,31 @@ public class Holder { } public > boolean tryRemoveComponent(@Nonnull ComponentType componentType) { - if (this.getComponent(componentType) == null) { - return false; - } else { - this.removeComponent(componentType); - return true; + assert this.archetype != null; + + assert this.components != null; + + if (this.ensureValidComponents) { + componentType.validate(); } + + long stamp = this.lock.writeLock(); + + boolean var4; + try { + if (this.archetype.contains(componentType)) { + this.archetype.validateComponentType(componentType); + this.archetype = Archetype.remove(this.archetype, componentType); + this.components[componentType.getIndex()] = null; + return true; + } + + var4 = false; + } finally { + this.lock.unlockWrite(stamp); + } + + return var4; } public boolean hasSerializableComponents(@Nonnull ComponentRegistry.Data data) { diff --git a/src/com/hypixel/hytale/component/Ref.java b/src/com/hypixel/hytale/component/Ref.java index 64ae42b8..15ca8400 100644 --- a/src/com/hypixel/hytale/component/Ref.java +++ b/src/com/hypixel/hytale/component/Ref.java @@ -8,6 +8,9 @@ public class Ref { @Nonnull private final Store store; private volatile int index; + @Nullable + private ArchetypeChunk archetypeChunk; + private int chunkEntityIndex; private volatile Throwable invalidatedBy; public Ref(@Nonnull Store store) { @@ -17,6 +20,7 @@ public class Ref { public Ref(@Nonnull Store store, int index) { this.store = store; this.index = index; + this.chunkEntityIndex = Integer.MIN_VALUE; } @Nonnull @@ -28,25 +32,49 @@ public class Ref { return this.index; } + @Nullable + ArchetypeChunk getArchetypeChunk() { + return this.archetypeChunk; + } + + int getChunkEntityIndex() { + return this.chunkEntityIndex; + } + void setIndex(int index) { this.index = index; } + void setArchetypeChunk(@Nonnull ArchetypeChunk archetypeChunk) { + this.archetypeChunk = archetypeChunk; + } + + void setChunkEntityIndex(int chunkEntityIndex) { + this.chunkEntityIndex = chunkEntityIndex; + } + void invalidate() { this.index = Integer.MIN_VALUE; + this.archetypeChunk = null; + this.chunkEntityIndex = Integer.MIN_VALUE; this.invalidatedBy = new Throwable(); } void invalidate(@Nullable Throwable invalidatedBy) { this.index = Integer.MIN_VALUE; + this.archetypeChunk = null; + this.chunkEntityIndex = Integer.MIN_VALUE; this.invalidatedBy = invalidatedBy != null ? invalidatedBy : new Throwable(); } - public void validate(@Nonnull Store store) { - if (this.store != store) { - throw new IllegalStateException("Incorrect store for entity reference"); - } else if (this.index == Integer.MIN_VALUE) { + public int validate(@Nonnull Store store) { + int localIndex = this.index; + if (localIndex == Integer.MIN_VALUE) { throw new IllegalStateException("Invalid entity reference!", this.invalidatedBy); + } else if (this.store != store) { + throw new IllegalStateException("Incorrect store for entity reference"); + } else { + return localIndex; } } diff --git a/src/com/hypixel/hytale/component/Store.java b/src/com/hypixel/hytale/component/Store.java index 672a647c..2309e101 100644 --- a/src/com/hypixel/hytale/component/Store.java +++ b/src/com/hypixel/hytale/component/Store.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.component.data.change.DataChange; import com.hypixel.hytale.component.data.change.SystemChange; import com.hypixel.hytale.component.data.unknown.UnknownComponents; import com.hypixel.hytale.component.event.EntityEventType; +import com.hypixel.hytale.component.event.EntityHolderEventType; import com.hypixel.hytale.component.event.WorldEventType; import com.hypixel.hytale.component.metric.ArchetypeChunkData; import com.hypixel.hytale.component.metric.SystemMetricData; @@ -18,6 +19,7 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.ArchetypeChunkSystem; import com.hypixel.hytale.component.system.EcsEvent; import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.component.system.EntityHolderEventSystem; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.ISystem; import com.hypixel.hytale.component.system.MetricSystem; @@ -113,14 +115,8 @@ public class Store implements ComponentAccessor { @Nonnull private Ref[] refs = new Ref[16]; @Nonnull - private int[] entityToArchetypeChunk = new int[16]; - @Nonnull - private int[] entityChunkIndex = new int[16]; - @Nonnull private BitSet[] systemIndexToArchetypeChunkIndexes = ArrayUtil.EMPTY_BITSET_ARRAY; @Nonnull - private BitSet[] archetypeChunkIndexesToSystemIndex = ArrayUtil.EMPTY_BITSET_ARRAY; - @Nonnull private final Object2IntMap> archetypeToIndexMap = new Object2IntOpenHashMap<>(); private int archetypeSize; @Nonnull @@ -140,8 +136,6 @@ public class Store implements ComponentAccessor { this.externalData = externalData; this.resourceStorage = resourceStorage; this.archetypeToIndexMap.defaultReturnValue(Integer.MIN_VALUE); - Arrays.fill(this.entityToArchetypeChunk, Integer.MIN_VALUE); - Arrays.fill(this.entityChunkIndex, Integer.MIN_VALUE); } @Nonnull @@ -243,7 +237,7 @@ public class Store implements ComponentAccessor { } void shutdown0(@Nonnull ComponentRegistry.Data data) { - if (this.thread.isAlive() && !this.thread.equals(Thread.currentThread())) { + if (this.thread.isAlive() && this.thread != Thread.currentThread()) { throw new IllegalArgumentException("Unable to shutdown store while thread is still running!"); } else { for (int systemIndex = data.getSystemSize() - 1; systemIndex >= 0; systemIndex--) { @@ -361,7 +355,7 @@ public class Store implements ComponentAccessor { protected void setEntityChunkIndex(@Nonnull Ref ref, int newEntityChunkIndex) { if (ref.isValid()) { - this.entityChunkIndex[ref.getIndex()] = newEntityChunkIndex; + ref.setChunkEntityIndex(newEntityChunkIndex); } } @@ -423,10 +417,6 @@ public class Store implements ComponentAccessor { if (oldLength <= entityIndex) { systemIndex = ArrayUtil.grow(entityIndex); this.refs = Arrays.copyOf(this.refs, systemIndex); - this.entityToArchetypeChunk = Arrays.copyOf(this.entityToArchetypeChunk, systemIndex); - this.entityChunkIndex = Arrays.copyOf(this.entityChunkIndex, systemIndex); - Arrays.fill(this.entityToArchetypeChunk, oldLength, systemIndex, Integer.MIN_VALUE); - Arrays.fill(this.entityChunkIndex, oldLength, systemIndex, Integer.MIN_VALUE); } this.refs[entityIndex] = ref; @@ -434,11 +424,11 @@ public class Store implements ComponentAccessor { systemIndex = this.findOrCreateArchetypeChunk(holder.getArchetype()); ArchetypeChunk archetypeChunk = this.archetypeChunks[systemIndex]; int chunkEntityRef = archetypeChunk.addEntity(ref, holder); - this.entityToArchetypeChunk[entityIndex] = systemIndex; - this.entityChunkIndex[entityIndex] = chunkEntityRef; + ref.setArchetypeChunk(archetypeChunk); + ref.setChunkEntityIndex(chunkEntityRef); SystemType> systemTypex = this.registry.getRefSystemType(); BitSet systemIndexesx = data.getSystemIndexesForType(systemTypex); - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[systemIndex]; + BitSet entityProcessedBySystemIndexes = archetypeChunk.getSystemIndexes(); commandBuffer.track(ref); int systemIndexx = -1; @@ -537,10 +527,6 @@ public class Store implements ComponentAccessor { if (oldLength <= this.entitiesSize) { systemIndex = ArrayUtil.grow(this.entitiesSize); this.refs = Arrays.copyOf(this.refs, systemIndex); - this.entityToArchetypeChunk = Arrays.copyOf(this.entityToArchetypeChunk, systemIndex); - this.entityChunkIndex = Arrays.copyOf(this.entityChunkIndex, systemIndex); - Arrays.fill(this.entityToArchetypeChunk, oldLength, systemIndex, Integer.MIN_VALUE); - Arrays.fill(this.entityChunkIndex, oldLength, systemIndex, Integer.MIN_VALUE); } System.arraycopy(refs, refStart, this.refs, firstIndex, length); @@ -559,8 +545,8 @@ public class Store implements ComponentAccessor { int archetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype()); ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; int chunkEntityRef = archetypeChunk.addEntity(refx, holder); - this.entityToArchetypeChunk[entityIndex] = archetypeIndex; - this.entityChunkIndex[entityIndex] = chunkEntityRef; + refx.setArchetypeChunk(archetypeChunk); + refx.setChunkEntityIndex(chunkEntityRef); systemIndex++; } @@ -571,8 +557,7 @@ public class Store implements ComponentAccessor { while ((systemIndexx = systemIndexesx.nextSetBit(systemIndexx + 1)) >= 0) { for (int ix = refStart; ix < refEnd; ix++) { Ref refx = refs[ix]; - int archetypeIndex = this.entityToArchetypeChunk[refx.getIndex()]; - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; + BitSet entityProcessedBySystemIndexes = refx.getArchetypeChunk().getSystemIndexes(); if (entityProcessedBySystemIndexes.get(systemIndexx)) { RefSystem system = data.getSystem(systemIndexx, systemTypex); boolean oldDisableProcessingAssert = this.disableProcessingAssert; @@ -612,9 +597,7 @@ public class Store implements ComponentAccessor { public Holder copyEntity(@Nonnull Ref ref, @Nonnull Holder holder) { this.assertThread(); ref.validate(this); - int refIndex = ref.getIndex(); - int archetypeIndex = this.entityToArchetypeChunk[refIndex]; - return this.archetypeChunks[archetypeIndex].copyEntity(this.entityChunkIndex[refIndex], holder); + return ref.getArchetypeChunk().copyEntity(ref.getChunkEntityIndex(), holder); } @Nonnull @@ -626,9 +609,7 @@ public class Store implements ComponentAccessor { public Holder copySerializableEntity(@Nonnull Ref ref, @Nonnull Holder holder) { this.assertThread(); ref.validate(this); - int refIndex = ref.getIndex(); - int archetypeIndex = this.entityToArchetypeChunk[refIndex]; - return this.archetypeChunks[archetypeIndex].copySerializableEntity(this.registry.getData(), this.entityChunkIndex[refIndex], holder); + return ref.getArchetypeChunk().copySerializableEntity(this.registry.getData(), ref.getChunkEntityIndex(), holder); } @Nonnull @@ -636,15 +617,13 @@ public class Store implements ComponentAccessor { public Archetype getArchetype(@Nonnull Ref ref) { this.assertThread(); ref.validate(this); - int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()]; - return this.archetypeChunks[archetypeIndex].getArchetype(); + return ref.getArchetypeChunk().getArchetype(); } @Nonnull protected Archetype __internal_getArchetype(@Nonnull Ref ref) { ref.validate(this); - int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()]; - return this.archetypeChunks[archetypeIndex].getArchetype(); + return ref.getArchetypeChunk().getArchetype(); } @Nonnull @@ -662,18 +641,17 @@ public class Store implements ComponentAccessor { Holder removeEntity(@Nonnull Ref ref, @Nonnull Holder holder, @Nonnull RemoveReason reason, @Nullable Throwable proxyReason) { this.assertThread(); this.assertWriteProcessing(); - ref.validate(this); + int entityIndex = ref.validate(this); CommandBuffer commandBuffer = this.takeCommandBuffer(); - int entityIndex = ref.getIndex(); - int archetypeIndex = this.entityToArchetypeChunk[entityIndex]; - int chunkEntityRef = this.entityChunkIndex[entityIndex]; + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + int chunkEntityRef = ref.getChunkEntityIndex(); ComponentRegistry.Data data = this.registry._internal_getData(); this.processing.lock(); try { SystemType> systemType = this.registry.getRefSystemType(); BitSet systemIndexes = data.getSystemIndexesForType(systemType); - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; + BitSet entityProcessedBySystemIndexes = archetypeChunk.getSystemIndexes(); int systemIndex = -1; while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { @@ -685,22 +663,15 @@ public class Store implements ComponentAccessor { int lastIndex = this.entitiesSize - 1; if (entityIndex != lastIndex) { Ref lastEntityRef = this.refs[lastIndex]; - int lastSelfEntityRef = this.entityToArchetypeChunk[lastIndex]; - systemIndex = this.entityChunkIndex[lastIndex]; lastEntityRef.setIndex(entityIndex); this.refs[entityIndex] = lastEntityRef; - this.entityToArchetypeChunk[entityIndex] = lastSelfEntityRef; - this.entityChunkIndex[entityIndex] = systemIndex; } this.refs[lastIndex] = null; - this.entityToArchetypeChunk[lastIndex] = Integer.MIN_VALUE; - this.entityChunkIndex[lastIndex] = Integer.MIN_VALUE; this.entitiesSize = lastIndex; - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; archetypeChunk.removeEntity(chunkEntityRef, holder); if (archetypeChunk.size() == 0) { - this.removeArchetypeChunk(archetypeIndex); + this.removeArchetypeChunk(archetypeChunk); } ref.invalidate(proxyReason); @@ -783,8 +754,8 @@ public class Store implements ComponentAccessor { while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { for (int i = refStart; i < refEnd; i++) { Ref ref = refArr[i]; - int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()]; - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + BitSet entityProcessedBySystemIndexes = archetypeChunk.getSystemIndexes(); if (entityProcessedBySystemIndexes.get(systemIndex)) { data.getSystem(systemIndex, systemType).onEntityRemove(refArr[i], reason, this, commandBuffer); } @@ -792,29 +763,23 @@ public class Store implements ComponentAccessor { } for (int ix = 0; ix < length; ix++) { - int entityIndex = refArr[refStart + ix].getIndex(); - systemIndex = this.entityToArchetypeChunk[entityIndex]; - int chunkEntityRef = this.entityChunkIndex[entityIndex]; + Ref ref = refArr[refStart + ix]; + systemIndex = ref.getIndex(); + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + int chunkEntityRef = ref.getChunkEntityIndex(); int lastIndex = this.entitiesSize - 1; - if (entityIndex != lastIndex) { + if (systemIndex != lastIndex) { Ref lastEntityRef = this.refs[lastIndex]; - int lastSelfEntityRef = this.entityToArchetypeChunk[lastIndex]; - int lastEntityChunkIndex = this.entityChunkIndex[lastIndex]; - lastEntityRef.setIndex(entityIndex); - this.refs[entityIndex] = lastEntityRef; - this.entityToArchetypeChunk[entityIndex] = lastSelfEntityRef; - this.entityChunkIndex[entityIndex] = lastEntityChunkIndex; + lastEntityRef.setIndex(systemIndex); + this.refs[systemIndex] = lastEntityRef; } this.refs[lastIndex] = null; - this.entityToArchetypeChunk[lastIndex] = Integer.MIN_VALUE; - this.entityChunkIndex[lastIndex] = Integer.MIN_VALUE; this.entitiesSize = lastIndex; Holder holder = holders[holderStart + ix]; - ArchetypeChunk archetypeChunk = this.archetypeChunks[systemIndex]; archetypeChunk.removeEntity(chunkEntityRef, holder); if (archetypeChunk.size() == 0) { - this.removeArchetypeChunk(systemIndex); + this.removeArchetypeChunk(archetypeChunk); } } @@ -856,14 +821,13 @@ public class Store implements ComponentAccessor { componentType.validateRegistry(this.registry); componentType.validate(); CommandBuffer commandBuffer = this.takeCommandBuffer(); - int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()]; this.processing.lock(); try { - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); if (!archetypeChunk.getArchetype().contains(componentType)) { T component = this.registry._internal_getData().createComponent(componentType); - this.datachunk_addComponent(ref, archetypeIndex, componentType, component, commandBuffer); + this.datachunk_addComponent(ref, archetypeChunk, componentType, component, commandBuffer); } } finally { this.processing.unlock(); @@ -877,20 +841,18 @@ public class Store implements ComponentAccessor { public > T ensureAndGetComponent(@Nonnull Ref ref, @Nonnull ComponentType componentType) { this.assertThread(); this.assertWriteProcessing(); - int refIndex = ref.getIndex(); componentType.validateRegistry(this.registry); componentType.validate(); CommandBuffer commandBuffer = this.takeCommandBuffer(); - int archetypeIndex = this.entityToArchetypeChunk[refIndex]; this.processing.lock(); T component; try { - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; - component = archetypeChunk.getComponent(this.entityChunkIndex[refIndex], componentType); + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + component = archetypeChunk.getComponent(ref.getChunkEntityIndex(), componentType); if (component == null) { component = this.registry._internal_getData().createComponent(componentType); - this.datachunk_addComponent(ref, archetypeIndex, componentType, component, commandBuffer); + this.datachunk_addComponent(ref, archetypeChunk, componentType, component, commandBuffer); } } finally { this.processing.unlock(); @@ -919,11 +881,10 @@ public class Store implements ComponentAccessor { componentType.validate(); Objects.requireNonNull(component); CommandBuffer commandBuffer = this.takeCommandBuffer(); - int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()]; this.processing.lock(); try { - this.datachunk_addComponent(ref, archetypeIndex, componentType, component, commandBuffer); + this.datachunk_addComponent(ref, ref.getArchetypeChunk(), componentType, component, commandBuffer); } finally { this.processing.unlock(); } @@ -941,15 +902,14 @@ public class Store implements ComponentAccessor { componentType.validate(); Objects.requireNonNull(component); CommandBuffer commandBuffer = this.takeCommandBuffer(); - int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()]; this.processing.lock(); try { - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; - int chunkEntityRef = this.entityChunkIndex[ref.getIndex()]; + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + int chunkEntityRef = ref.getChunkEntityIndex(); T oldComponent = archetypeChunk.getComponent(chunkEntityRef, componentType); archetypeChunk.setComponent(chunkEntityRef, componentType, component); - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; + BitSet entityProcessedBySystemIndexes = archetypeChunk.getSystemIndexes(); ComponentRegistry.Data data = this.registry._internal_getData(); BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType()); int systemIndex = -1; @@ -978,16 +938,15 @@ public class Store implements ComponentAccessor { componentType.validate(); Objects.requireNonNull(component); CommandBuffer commandBuffer = this.takeCommandBuffer(); - int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()]; this.processing.lock(); try { - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); if (archetypeChunk.getArchetype().contains(componentType)) { - int chunkEntityRef = this.entityChunkIndex[ref.getIndex()]; + int chunkEntityRef = ref.getChunkEntityIndex(); T oldComponent = archetypeChunk.getComponent(chunkEntityRef, componentType); archetypeChunk.setComponent(chunkEntityRef, componentType, component); - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; + BitSet entityProcessedBySystemIndexes = archetypeChunk.getSystemIndexes(); ComponentRegistry.Data data = this.registry._internal_getData(); BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType()); int systemIndex = -1; @@ -1001,7 +960,7 @@ public class Store implements ComponentAccessor { } } } else { - this.datachunk_addComponent(ref, archetypeIndex, componentType, component, commandBuffer); + this.datachunk_addComponent(ref, archetypeChunk, componentType, component, commandBuffer); } } finally { this.processing.unlock(); @@ -1021,9 +980,8 @@ public class Store implements ComponentAccessor { ref.validate(this); componentType.validateRegistry(this.registry); componentType.validate(); - int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()]; - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; - return archetypeChunk.getComponent(this.entityChunkIndex[ref.getIndex()], componentType); + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + return archetypeChunk.getComponent(ref.getChunkEntityIndex(), componentType); } @Override @@ -1034,14 +992,12 @@ public class Store implements ComponentAccessor { componentType.validateRegistry(this.registry); componentType.validate(); CommandBuffer commandBuffer = this.takeCommandBuffer(); - int entityIndex = ref.getIndex(); - int fromArchetypeIndex = this.entityToArchetypeChunk[entityIndex]; this.processing.lock(); try { - ArchetypeChunk fromArchetypeChunk = this.archetypeChunks[fromArchetypeIndex]; + ArchetypeChunk fromArchetypeChunk = ref.getArchetypeChunk(); Holder holder = this.registry._internal_newEntityHolder(); - fromArchetypeChunk.removeEntity(this.entityChunkIndex[entityIndex], holder); + fromArchetypeChunk.removeEntity(ref.getChunkEntityIndex(), holder); T component = holder.getComponent(componentType); assert component != null; @@ -1050,9 +1006,9 @@ public class Store implements ComponentAccessor { int toArchetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype()); ArchetypeChunk toArchetypeChunk = this.archetypeChunks[toArchetypeIndex]; int chunkEntityRef = toArchetypeChunk.addEntity(ref, holder); - this.entityToArchetypeChunk[entityIndex] = toArchetypeIndex; - this.entityChunkIndex[entityIndex] = chunkEntityRef; - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[fromArchetypeIndex]; + ref.setArchetypeChunk(toArchetypeChunk); + ref.setChunkEntityIndex(chunkEntityRef); + BitSet entityProcessedBySystemIndexes = fromArchetypeChunk.getSystemIndexes(); ComponentRegistry.Data data = this.registry._internal_getData(); BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType()); int systemIndex = -1; @@ -1067,7 +1023,7 @@ public class Store implements ComponentAccessor { } if (fromArchetypeChunk.size() == 0) { - this.removeArchetypeChunk(fromArchetypeIndex); + this.removeArchetypeChunk(fromArchetypeChunk); } } finally { this.processing.unlock(); @@ -1088,18 +1044,16 @@ public class Store implements ComponentAccessor { componentType.validateRegistry(this.registry); componentType.validate(); CommandBuffer commandBuffer = this.takeCommandBuffer(); - int entityIndex = ref.getIndex(); - int fromArchetypeIndex = this.entityToArchetypeChunk[entityIndex]; this.processing.lock(); boolean result; try { - ArchetypeChunk fromArchetypeChunk = this.archetypeChunks[fromArchetypeIndex]; + ArchetypeChunk fromArchetypeChunk = ref.getArchetypeChunk(); if (!fromArchetypeChunk.getArchetype().contains(componentType)) { result = false; } else { Holder holder = this.registry._internal_newEntityHolder(); - fromArchetypeChunk.removeEntity(this.entityChunkIndex[entityIndex], holder); + fromArchetypeChunk.removeEntity(ref.getChunkEntityIndex(), holder); T component = holder.getComponent(componentType); assert component != null; @@ -1108,9 +1062,9 @@ public class Store implements ComponentAccessor { int toArchetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype()); ArchetypeChunk toArchetypeChunk = this.archetypeChunks[toArchetypeIndex]; int chunkEntityRef = toArchetypeChunk.addEntity(ref, holder); - this.entityToArchetypeChunk[entityIndex] = toArchetypeIndex; - this.entityChunkIndex[entityIndex] = chunkEntityRef; - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[fromArchetypeIndex]; + ref.setArchetypeChunk(toArchetypeChunk); + ref.setChunkEntityIndex(chunkEntityRef); + BitSet entityProcessedBySystemIndexes = fromArchetypeChunk.getSystemIndexes(); ComponentRegistry.Data data = this.registry._internal_getData(); BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType()); int systemIndex = -1; @@ -1125,7 +1079,7 @@ public class Store implements ComponentAccessor { } if (fromArchetypeChunk.size() == 0) { - this.removeArchetypeChunk(fromArchetypeIndex); + this.removeArchetypeChunk(fromArchetypeChunk); } result = true; @@ -1463,12 +1417,10 @@ public class Store implements ComponentAccessor { T system = (T)data.getSystem(systemIndex, systemType); for (Ref ref : refs) { - int entityIndex = ref.getIndex(); - int archetypeIndex = this.entityToArchetypeChunk[entityIndex]; - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + BitSet entityProcessedBySystemIndexes = archetypeChunk.getSystemIndexes(); if (entityProcessedBySystemIndexes.get(systemIndex)) { - int index = this.entityChunkIndex[entityIndex]; - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; + int index = ref.getChunkEntityIndex(); system.fetch(index, archetypeChunk, this, commandBuffer, query, results); } } @@ -1504,11 +1456,9 @@ public class Store implements ComponentAccessor { this.processing.lock(); try { - int entityIndex = ref.getIndex(); - int archetypeIndex = this.entityToArchetypeChunk[entityIndex]; - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; - int index = this.entityChunkIndex[entityIndex]; + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + BitSet entityProcessedBySystemIndexes = archetypeChunk.getSystemIndexes(); + int index = ref.getChunkEntityIndex(); int systemIndex = -1; while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { @@ -1533,6 +1483,45 @@ public class Store implements ComponentAccessor { } } + @Override + public void invoke(@Nonnull Holder holder, @Nonnull Event param) { + EntityHolderEventType eventType = this.registry.getEntityHolderEventTypeForClass(param.getClass()); + if (eventType != null) { + ((Store)this).invoke(eventType, holder, param); + } + } + + @Override + public void invoke( + @Nonnull EntityHolderEventType systemType, @Nonnull Holder holder, @Nonnull Event param + ) { + if (this.shutdown) { + throw new IllegalStateException("Store is shutdown!"); + } else { + this.assertThread(); + ComponentRegistry.Data data = this.registry._internal_getData(); + CommandBuffer commandBuffer = this.takeCommandBuffer(); + BitSet systemIndexes = data.getSystemIndexesForType(systemType); + this.processing.lock(); + + try { + Archetype archetype = holder.getArchetype(); + int systemIndex = -1; + + while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { + EntityHolderEventSystem system = data.getSystem(systemIndex, systemType); + if (system.getQuery() != null && system.getQuery().test(archetype)) { + system.handleInternal(holder, this, commandBuffer, param); + } + } + } finally { + this.processing.unlock(); + } + + commandBuffer.consume(); + } + } + @Override public void invoke(@Nonnull Event param) { WorldEventType eventType = this.registry.getWorldEventTypeForClass(param.getClass()); @@ -1581,11 +1570,9 @@ public class Store implements ComponentAccessor { CommandBuffer commandBuffer = sourceCommandBuffer.fork(); commandBuffer.track(ref); BitSet systemIndexes = data.getSystemIndexesForType(systemType); - int entityIndex = ref.getIndex(); - int archetypeIndex = this.entityToArchetypeChunk[entityIndex]; - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; - int index = this.entityChunkIndex[entityIndex]; + ArchetypeChunk archetypeChunk = ref.getArchetypeChunk(); + BitSet entityProcessedBySystemIndexes = archetypeChunk.getSystemIndexes(); + int index = ref.getChunkEntityIndex(); int systemIndex = -1; while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { @@ -1606,6 +1593,29 @@ public class Store implements ComponentAccessor { commandBuffer.mergeParallel(sourceCommandBuffer); } + protected void internal_invoke(CommandBuffer sourceCommandBuffer, Holder holder, Event param) { + EntityHolderEventType eventType = this.registry.getEntityHolderEventTypeForClass(param.getClass()); + if (eventType != null) { + ((Store)this).internal_invoke(sourceCommandBuffer, eventType, holder, param); + } + } + + protected void internal_invoke( + CommandBuffer commandBuffer, @Nonnull EntityHolderEventType systemType, Holder holder, Event param + ) { + ComponentRegistry.Data data = this.registry._internal_getData(); + BitSet systemIndexes = data.getSystemIndexesForType(systemType); + Archetype archetype = holder.getArchetype(); + int systemIndex = -1; + + while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { + EntityHolderEventSystem system = data.getSystem(systemIndex, systemType); + if (system.getQuery() != null && system.getQuery().test(archetype)) { + system.handleInternal(holder, this, commandBuffer, param); + } + } + } + protected void internal_invoke(CommandBuffer sourceCommandBuffer, Event param) { WorldEventType eventType = this.registry.getWorldEventTypeForClass(param.getClass()); if (eventType != null) { @@ -1804,30 +1814,30 @@ public class Store implements ComponentAccessor { Component component = unknownComponents.removeComponent(componentId, componentCodec); entity.addComponent(componentType, component); }, (newChunkEntityRef, ref) -> { - this.entityToArchetypeChunk[ref.getIndex()] = toArchetypeIndex; - this.entityChunkIndex[ref.getIndex()] = newChunkEntityRef; + ref.setArchetypeChunk(toArchetypeChunk); + ref.setChunkEntityIndex(newChunkEntityRef); }); if (archetypeChunk.size() == 0) { - this.archetypeToIndexMap.removeInt(this.archetypeChunks[archetypeIndexx].getArchetype()); + this.archetypeToIndexMap.removeInt(archetypeChunk.getArchetype()); this.archetypeChunks[archetypeIndexx] = null; for (int systemIndex = 0; systemIndex < oldData.getSystemSize(); systemIndex++) { this.systemIndexToArchetypeChunkIndexes[systemIndex].clear(archetypeIndexx); } - this.archetypeChunkIndexesToSystemIndex[archetypeIndexx].clear(); + archetypeChunk.getSystemIndexes().clear(); this.archetypeChunkReuse.set(archetypeIndexx); } if (toArchetypeChunk.size() == 0) { - this.archetypeToIndexMap.removeInt(this.archetypeChunks[toArchetypeIndex].getArchetype()); + this.archetypeToIndexMap.removeInt(toArchetypeChunk.getArchetype()); this.archetypeChunks[toArchetypeIndex] = null; for (int systemIndex = 0; systemIndex < oldData.getSystemSize(); systemIndex++) { this.systemIndexToArchetypeChunkIndexes[systemIndex].clear(toArchetypeIndex); } - this.archetypeChunkIndexesToSystemIndex[toArchetypeIndex].clear(); + toArchetypeChunk.getSystemIndexes().clear(); this.archetypeChunkReuse.set(toArchetypeIndex); } } @@ -1846,14 +1856,14 @@ public class Store implements ComponentAccessor { if (archetypeChunk != null) { Archetype archetype = archetypeChunk.getArchetype(); if (archetype.contains(componentType)) { - this.archetypeToIndexMap.removeInt(this.archetypeChunks[archetypeIndex].getArchetype()); + this.archetypeToIndexMap.removeInt(archetypeChunk.getArchetype()); this.archetypeChunks[archetypeIndex] = null; for (int systemIndex = 0; systemIndex < oldData.getSystemSize(); systemIndex++) { this.systemIndexToArchetypeChunkIndexes[systemIndex].clear(archetypeIndex); } - this.archetypeChunkIndexesToSystemIndex[archetypeIndex].clear(); + archetypeChunk.getSystemIndexes().clear(); this.archetypeChunkReuse.set(archetypeIndex); Archetype newArchetype = Archetype.remove(archetype, componentType); if (componentCodec != null && !newArchetype.contains(unknownComponentType)) { @@ -1880,8 +1890,8 @@ public class Store implements ComponentAccessor { entity.removeComponent(componentType); }, (newChunkEntityRef, ref) -> { - this.entityToArchetypeChunk[ref.getIndex()] = toArchetypeIndex; - this.entityChunkIndex[ref.getIndex()] = newChunkEntityRef; + ref.setArchetypeChunk(toArchetypeChunk); + ref.setChunkEntityIndex(newChunkEntityRef); }); } } @@ -1933,7 +1943,10 @@ public class Store implements ComponentAccessor { } for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; archetypeIndex++) { - this.archetypeChunkIndexesToSystemIndex[archetypeIndex].clear(); + ArchetypeChunk chunk = this.archetypeChunks[archetypeIndex]; + if (chunk != null) { + chunk.getSystemIndexes().clear(); + } } SystemType> entityQuerySystemType = this.registry.getQuerySystemType(); @@ -1944,11 +1957,11 @@ public class Store implements ComponentAccessor { QuerySystem system = data.getSystem(systemIndex, entityQuerySystemType); BitSet archetypeChunkIndexes = this.systemIndexToArchetypeChunkIndexes[systemIndex]; - for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; archetypeIndex++) { - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; + for (int archetypeIndexx = 0; archetypeIndexx < this.archetypeSize; archetypeIndexx++) { + ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndexx]; if (archetypeChunk != null && system.test(this.registry, archetypeChunk.getArchetype())) { - archetypeChunkIndexes.set(archetypeIndex); - this.archetypeChunkIndexesToSystemIndex[archetypeIndex].set(systemIndex); + archetypeChunkIndexes.set(archetypeIndexx); + archetypeChunk.getSystemIndexes().set(systemIndex); } } } @@ -1967,17 +1980,17 @@ public class Store implements ComponentAccessor { public void assertThread() { Thread currentThread = Thread.currentThread(); - if (!currentThread.equals(this.thread) && this.thread.isAlive()) { + if (currentThread != this.thread && this.thread.isAlive()) { throw new IllegalStateException("Assert not in thread! " + this.thread + " but was in " + currentThread); } } public boolean isInThread() { - return Thread.currentThread().equals(this.thread); + return Thread.currentThread() == this.thread; } public boolean isAliveInDifferentThread() { - return this.thread.isAlive() && !Thread.currentThread().equals(this.thread); + return this.thread.isAlive() && Thread.currentThread() != this.thread; } @Nonnull @@ -2006,38 +2019,42 @@ public class Store implements ComponentAccessor { private > void datachunk_addComponent( @Nonnull Ref ref, - int fromArchetypeIndex, + @Nonnull ArchetypeChunk fromArchetypeChunk, @Nonnull ComponentType componentType, @Nonnull T component, @Nonnull CommandBuffer commandBuffer ) { - int entityIndex = ref.getIndex(); - ArchetypeChunk fromArchetypeChunk = this.archetypeChunks[fromArchetypeIndex]; - int oldChunkEntityRef = this.entityChunkIndex[entityIndex]; + int oldChunkEntityRef = ref.getChunkEntityIndex(); Holder holder = this.registry._internal_newEntityHolder(); fromArchetypeChunk.removeEntity(oldChunkEntityRef, holder); - holder.addComponent(componentType, component); - int toArchetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype()); - ArchetypeChunk toArchetypeChunk = this.archetypeChunks[toArchetypeIndex]; - int chunkEntityRef = toArchetypeChunk.addEntity(ref, holder); - this.entityToArchetypeChunk[entityIndex] = toArchetypeIndex; - this.entityChunkIndex[entityIndex] = chunkEntityRef; - BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[toArchetypeIndex]; - ComponentRegistry.Data data = this.registry._internal_getData(); - BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType()); - int systemIndex = -1; + if (!holder.addComponentInternal(componentType, component)) { + int chunkEntityRef = fromArchetypeChunk.addEntity(ref, holder); + ref.setArchetypeChunk(fromArchetypeChunk); + ref.setChunkEntityIndex(chunkEntityRef); + throw new IllegalArgumentException("Entity already contains component type: " + componentType); + } else { + int toArchetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype()); + ArchetypeChunk toArchetypeChunk = this.archetypeChunks[toArchetypeIndex]; + int chunkEntityRef = toArchetypeChunk.addEntity(ref, holder); + ref.setArchetypeChunk(toArchetypeChunk); + ref.setChunkEntityIndex(chunkEntityRef); + BitSet entityProcessedBySystemIndexes = toArchetypeChunk.getSystemIndexes(); + ComponentRegistry.Data data = this.registry._internal_getData(); + BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType()); + int systemIndex = -1; - while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { - if (entityProcessedBySystemIndexes.get(systemIndex)) { - RefChangeSystem system = (RefChangeSystem)data.getSystem(systemIndex); - if (system.componentType().getIndex() == componentType.getIndex()) { - system.onComponentAdded(ref, component, this, commandBuffer); + while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) { + if (entityProcessedBySystemIndexes.get(systemIndex)) { + RefChangeSystem system = (RefChangeSystem)data.getSystem(systemIndex); + if (system.componentType().getIndex() == componentType.getIndex()) { + system.onComponentAdded(ref, component, this, commandBuffer); + } } } - } - if (fromArchetypeChunk.size() == 0) { - this.removeArchetypeChunk(fromArchetypeIndex); + if (fromArchetypeChunk.size() == 0) { + this.removeArchetypeChunk(fromArchetypeChunk); + } } } @@ -2058,18 +2075,13 @@ public class Store implements ComponentAccessor { if (oldLength <= archetypeIndex) { int newLength = ArrayUtil.grow(archetypeIndex); this.archetypeChunks = Arrays.copyOf(this.archetypeChunks, newLength); - this.archetypeChunkIndexesToSystemIndex = Arrays.copyOf(this.archetypeChunkIndexesToSystemIndex, newLength); - int systemSize = data.getSystemSize(); - - for (int i = oldLength; i < newLength; i++) { - this.archetypeChunkIndexesToSystemIndex[i] = new BitSet(systemSize); - } } ArchetypeChunk archetypeChunk = new ArchetypeChunk<>(this, archetype); + archetypeChunk.setArchetypeIndex(archetypeIndex); this.archetypeChunks[archetypeIndex] = archetypeChunk; this.archetypeToIndexMap.put(archetype, archetypeIndex); - BitSet archetypeChunkToSystemIndex = this.archetypeChunkIndexesToSystemIndex[archetypeIndex]; + BitSet archetypeChunkToSystemIndex = archetypeChunk.getSystemIndexes(); SystemType> entityQuerySystemType = this.registry.getQuerySystemType(); BitSet systemIndexes = data.getSystemIndexesForType(entityQuerySystemType); int systemIndex = -1; @@ -2089,12 +2101,12 @@ public class Store implements ComponentAccessor { } } - private void removeArchetypeChunk(int archetypeIndex) { - ArchetypeChunk archetypeChunk = this.archetypeChunks[archetypeIndex]; + private void removeArchetypeChunk(@Nonnull ArchetypeChunk archetypeChunk) { + int archetypeIndex = archetypeChunk.getArchetypeIndex(); Archetype archetype = archetypeChunk.getArchetype(); this.archetypeToIndexMap.removeInt(archetype); this.archetypeChunks[archetypeIndex] = null; - this.archetypeChunkIndexesToSystemIndex[archetypeIndex].clear(); + archetypeChunk.getSystemIndexes().clear(); ComponentRegistry.Data data = this.registry._internal_getData(); BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getQuerySystemType()); int systemIndex = -1; diff --git a/src/com/hypixel/hytale/component/event/EntityHolderEventType.java b/src/com/hypixel/hytale/component/event/EntityHolderEventType.java new file mode 100644 index 00000000..5808001e --- /dev/null +++ b/src/com/hypixel/hytale/component/event/EntityHolderEventType.java @@ -0,0 +1,17 @@ +package com.hypixel.hytale.component.event; + +import com.hypixel.hytale.component.ComponentRegistry; +import com.hypixel.hytale.component.system.EcsEvent; +import com.hypixel.hytale.component.system.EntityHolderEventSystem; +import javax.annotation.Nonnull; + +public class EntityHolderEventType extends EventSystemType> { + public EntityHolderEventType( + @Nonnull ComponentRegistry registry, + @Nonnull Class> tClass, + @Nonnull Class eClass, + int index + ) { + super(registry, tClass, eClass, index); + } +} diff --git a/src/com/hypixel/hytale/component/spatial/KDTree.java b/src/com/hypixel/hytale/component/spatial/KDTree.java index 85252f09..bd01fba8 100644 --- a/src/com/hypixel/hytale/component/spatial/KDTree.java +++ b/src/com/hypixel/hytale/component/spatial/KDTree.java @@ -1,12 +1,12 @@ package com.hypixel.hytale.component.spatial; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Comparator; import java.util.List; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class KDTree implements SpatialStructure { @Nonnull @@ -245,7 +245,7 @@ public class KDTree implements SpatialStructure { } else { int axis = depth % 3; int compare = compare(node.vector, vector, axis); - double distanceSq = node.vector.distanceSquaredTo(vector); + double distanceSq = node.vector.distanceSquared(vector); if (distanceSq < closestState.distanceSq) { closestState.node = node; closestState.distanceSq = distanceSq; @@ -276,7 +276,7 @@ public class KDTree implements SpatialStructure { if (node != null) { int axis = depth % 3; int compare = compare(node.vector, vector, axis); - double nodeDistanceSq = node.vector.distanceSquaredTo(vector); + double nodeDistanceSq = node.vector.distanceSquared(vector); if (nodeDistanceSq < distanceSq) { int i = 0; @@ -388,7 +388,7 @@ public class KDTree implements SpatialStructure { if (node != null) { int axis = depth % 3; int compare = compare(node.vector, vector, axis); - double nodeDistanceSq = node.vector.distanceSquaredTo(vector); + double nodeDistanceSq = node.vector.distanceSquared(vector); if (nodeDistanceSq < distanceSq) { results.add(new KDTree.OrderedEntry<>(nodeDistanceSq, node.data)); } @@ -431,7 +431,7 @@ public class KDTree implements SpatialStructure { && node.vector.z >= center.z - zSearchRadius && node.vector.z <= center.z + zSearchRadius; if (inCuboid) { - double nodeDistanceSq = node.vector.distanceSquaredTo(center); + double nodeDistanceSq = node.vector.distanceSquared(center); results.add(new KDTree.OrderedEntry<>(nodeDistanceSq, node.data)); } diff --git a/src/com/hypixel/hytale/component/spatial/SpatialData.java b/src/com/hypixel/hytale/component/spatial/SpatialData.java index 17564d76..5c1ded99 100644 --- a/src/com/hypixel/hytale/component/spatial/SpatialData.java +++ b/src/com/hypixel/hytale/component/spatial/SpatialData.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.component.spatial; import com.hypixel.hytale.common.util.ArrayUtil; -import com.hypixel.hytale.math.vector.Vector3d; import it.unimi.dsi.fastutil.ints.IntArrays; import java.util.Arrays; import java.util.Objects; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpatialData { public static final Vector3d[] EMPTY_VECTOR_ARRAY = new Vector3d[0]; @@ -51,7 +51,7 @@ public class SpatialData { int index = this.size++; this.indexes[index] = index; - this.vectors[index].assign(vector); + this.vectors[index].set(vector); this.data[index] = value; } @@ -73,7 +73,7 @@ public class SpatialData { Objects.requireNonNull(value); int index = this.size++; this.indexes[index] = index; - this.vectors[index].assign(vector); + this.vectors[index].set(vector); this.data[index] = value; } diff --git a/src/com/hypixel/hytale/component/spatial/SpatialResource.java b/src/com/hypixel/hytale/component/spatial/SpatialResource.java index 1c8b66d1..df9965eb 100644 --- a/src/com/hypixel/hytale/component/spatial/SpatialResource.java +++ b/src/com/hypixel/hytale/component/spatial/SpatialResource.java @@ -2,21 +2,21 @@ package com.hypixel.hytale.component.spatial; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Resource; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import java.util.List; import javax.annotation.Nonnull; public class SpatialResource implements Resource { @Nonnull - private static final ThreadLocal>> THREAD_LOCAL_REFERENCE_LIST = ThreadLocal.withInitial(ObjectArrayList::new); + private static final ThreadLocal>> THREAD_LOCAL_REFERENCE_LIST = ThreadLocal.withInitial(ReferenceArrayList::new); @Nonnull private final SpatialData> spatialData = new SpatialData<>(); @Nonnull private final SpatialStructure spatialStructure; @Nonnull - public static ObjectList> getThreadLocalReferenceList() { - ObjectList list = THREAD_LOCAL_REFERENCE_LIST.get(); + public static List> getThreadLocalReferenceList() { + List list = THREAD_LOCAL_REFERENCE_LIST.get(); list.clear(); return list; } diff --git a/src/com/hypixel/hytale/component/spatial/SpatialStructure.java b/src/com/hypixel/hytale/component/spatial/SpatialStructure.java index 8c6a06ab..4a138269 100644 --- a/src/com/hypixel/hytale/component/spatial/SpatialStructure.java +++ b/src/com/hypixel/hytale/component/spatial/SpatialStructure.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.component.spatial; -import com.hypixel.hytale.math.vector.Vector3d; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public interface SpatialStructure { int size(); diff --git a/src/com/hypixel/hytale/component/spatial/SpatialSystem.java b/src/com/hypixel/hytale/component/spatial/SpatialSystem.java index 099fe57b..9997d86c 100644 --- a/src/com/hypixel/hytale/component/spatial/SpatialSystem.java +++ b/src/com/hypixel/hytale/component/spatial/SpatialSystem.java @@ -6,9 +6,9 @@ import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.system.QuerySystem; import com.hypixel.hytale.component.system.tick.TickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class SpatialSystem extends TickingSystem implements QuerySystem { @Nonnull diff --git a/src/com/hypixel/hytale/component/system/EntityHolderEventSystem.java b/src/com/hypixel/hytale/component/system/EntityHolderEventSystem.java new file mode 100644 index 00000000..e99f8ff7 --- /dev/null +++ b/src/com/hypixel/hytale/component/system/EntityHolderEventSystem.java @@ -0,0 +1,22 @@ +package com.hypixel.hytale.component.system; + +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Store; +import javax.annotation.Nonnull; + +public abstract class EntityHolderEventSystem extends EventSystem implements QuerySystem { + protected EntityHolderEventSystem(@Nonnull Class eventType) { + super(eventType); + } + + public abstract void handle(@Nonnull Holder var1, @Nonnull Store var2, @Nonnull CommandBuffer var3, @Nonnull EventType var4); + + public void handleInternal( + @Nonnull Holder holder, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer, @Nonnull EventType event + ) { + if (this.shouldProcessEvent(event)) { + this.handle(holder, store, commandBuffer, event); + } + } +} diff --git a/src/com/hypixel/hytale/event/EventBus.java b/src/com/hypixel/hytale/event/EventBus.java index 162b0f77..84619a21 100644 --- a/src/com/hypixel/hytale/event/EventBus.java +++ b/src/com/hypixel/hytale/event/EventBus.java @@ -274,7 +274,7 @@ public class EventBus implements IEventBus { @Nonnull Class eventClass, KeyType key ) { SyncEventBusRegistry registry = (SyncEventBusRegistry)this.registryMap.get(eventClass); - return registry == null ? SyncEventBusRegistry.NO_OP : registry.dispatchFor(key); + return registry != null && registry.isAlive() ? registry.dispatchFor(key) : SyncEventBusRegistry.NO_OP; } @Nonnull @@ -283,6 +283,6 @@ public class EventBus implements IEventBus { @Nonnull Class eventClass, KeyType key ) { AsyncEventBusRegistry registry = (AsyncEventBusRegistry)this.registryMap.get(eventClass); - return registry == null ? AsyncEventBusRegistry.NO_OP : registry.dispatchFor(key); + return registry != null && registry.isAlive() ? registry.dispatchFor(key) : AsyncEventBusRegistry.NO_OP; } } diff --git a/src/com/hypixel/hytale/function/consumer/BiIntConsumer.java b/src/com/hypixel/hytale/function/consumer/BiIntConsumer.java new file mode 100644 index 00000000..b28adda6 --- /dev/null +++ b/src/com/hypixel/hytale/function/consumer/BiIntConsumer.java @@ -0,0 +1,6 @@ +package com.hypixel.hytale.function.consumer; + +@FunctionalInterface +public interface BiIntConsumer { + void accept(int var1, int var2); +} diff --git a/src/com/hypixel/hytale/function/consumer/TriIntObjectConsumer.java b/src/com/hypixel/hytale/function/consumer/TriIntObjectConsumer.java new file mode 100644 index 00000000..be145a9f --- /dev/null +++ b/src/com/hypixel/hytale/function/consumer/TriIntObjectConsumer.java @@ -0,0 +1,6 @@ +package com.hypixel.hytale.function.consumer; + +@FunctionalInterface +public interface TriIntObjectConsumer { + void accept(int var1, int var2, int var3, T var4); +} diff --git a/src/com/hypixel/hytale/logger/HytaleLogger.java b/src/com/hypixel/hytale/logger/HytaleLogger.java index d3a1ed2d..43e2b8ae 100644 --- a/src/com/hypixel/hytale/logger/HytaleLogger.java +++ b/src/com/hypixel/hytale/logger/HytaleLogger.java @@ -14,6 +14,7 @@ import com.hypixel.hytale.logger.backend.HytaleUncaughtExceptionHandler; import com.hypixel.hytale.logger.util.LoggerPrintStream; import io.sentry.IScopes; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.LogManager; @@ -131,6 +132,7 @@ public class HytaleLogger extends AbstractLogger { final class Context extends LogContext implements HytaleLogger.Api { private Context(@Nonnull Level level) { + Objects.requireNonNull(HytaleLogger.this); super(level, false); } diff --git a/src/com/hypixel/hytale/logger/util/GithubMessageUtil.java b/src/com/hypixel/hytale/logger/util/GithubMessageUtil.java index 2917395c..b1eeb4cb 100644 --- a/src/com/hypixel/hytale/logger/util/GithubMessageUtil.java +++ b/src/com/hypixel/hytale/logger/util/GithubMessageUtil.java @@ -11,21 +11,21 @@ public class GithubMessageUtil { @Nonnull public static String messageError(@Nonnull String file, int line, int column, @Nonnull String message) { - return "::error file=%s,line=%d,col=%d::%s\n".formatted(file, line, column, message.replace("\n", "%0A")); + return "::error file=%s,line=%d,col=%d::%s".formatted(file, line, column, message.replace("\n", "%0A")); } @Nonnull public static String messageError(@Nonnull String file, @Nonnull String message) { - return "::error file=%s::%s\n".formatted(file, message.replace("\n", "%0A")); + return "::error file=%s::%s".formatted(file, message.replace("\n", "%0A")); } @Nonnull public static String messageWarning(@Nonnull String file, int line, int column, @Nonnull String message) { - return "::warning file=%s,line=%d,col=%d::%s\n".formatted(file, line, column, message.replace("\n", "%0A")); + return "::warning file=%s,line=%d,col=%d::%s".formatted(file, line, column, message.replace("\n", "%0A")); } @Nonnull public static String messageWarning(@Nonnull String file, @Nonnull String message) { - return "::warning file=%s::%s\n".formatted(file, message.replace("\n", "%0A")); + return "::warning file=%s::%s".formatted(file, message.replace("\n", "%0A")); } } diff --git a/src/com/hypixel/hytale/math/Axis.java b/src/com/hypixel/hytale/math/Axis.java index 57c5f70a..914b1e5a 100644 --- a/src/com/hypixel/hytale/math/Axis.java +++ b/src/com/hypixel/hytale/math/Axis.java @@ -1,24 +1,25 @@ package com.hypixel.hytale.math; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.joml.Vector3ic; public enum Axis { X(new Vector3i(1, 0, 0)), Y(new Vector3i(0, 1, 0)), Z(new Vector3i(0, 0, 1)); - private final Vector3i direction; + private final Vector3ic direction; private Axis(@Nonnull final Vector3i direction) { this.direction = direction; } @Nonnull - public Vector3i getDirection() { - return this.direction.clone(); + public Vector3ic getDirection() { + return this.direction; } public void rotate(@Nonnull Vector3i vector, int angle) { @@ -44,65 +45,65 @@ public enum Axis { public void rotate(@Nonnull Vector3i vector) { switch (this) { case X: - vector.assign(vector.getX(), -vector.getZ(), vector.getY()); + vector.set(vector.x(), -vector.z(), vector.y()); break; case Y: - vector.assign(vector.getZ(), vector.getY(), -vector.getX()); + vector.set(vector.z(), vector.y(), -vector.x()); break; case Z: - vector.assign(-vector.getY(), vector.getX(), vector.getZ()); + vector.set(-vector.y(), vector.x(), vector.z()); } } public void rotate(@Nonnull Vector3d vector) { switch (this) { case X: - vector.assign(vector.getX(), -vector.getZ(), vector.getY()); + vector.set(vector.x(), -vector.z(), vector.y()); break; case Y: - vector.assign(vector.getZ(), vector.getY(), -vector.getX()); + vector.set(vector.z(), vector.y(), -vector.x()); break; case Z: - vector.assign(-vector.getY(), vector.getX(), vector.getZ()); + vector.set(-vector.y(), vector.x(), vector.z()); } } public void flip(@Nonnull Vector3i vector) { switch (this) { case X: - vector.assign(-vector.getX(), vector.getY(), vector.getZ()); + vector.set(-vector.x(), vector.y(), vector.z()); break; case Y: - vector.assign(vector.getX(), -vector.getY(), vector.getZ()); + vector.set(vector.x(), -vector.y(), vector.z()); break; case Z: - vector.assign(vector.getX(), vector.getY(), -vector.getZ()); + vector.set(vector.x(), vector.y(), -vector.z()); } } public void flip(@Nonnull Vector3d vector) { switch (this) { case X: - vector.assign(-vector.getX(), vector.getY(), vector.getZ()); + vector.set(-vector.x(), vector.y(), vector.z()); break; case Y: - vector.assign(vector.getX(), -vector.getY(), vector.getZ()); + vector.set(vector.x(), -vector.y(), vector.z()); break; case Z: - vector.assign(vector.getX(), vector.getY(), -vector.getZ()); + vector.set(vector.x(), vector.y(), -vector.z()); } } - public void flipRotation(@Nonnull Vector3f rotation) { + public void flipRotation(@Nonnull Rotation3f rotation) { switch (this) { case X: - rotation.setYaw(-rotation.getYaw()); + rotation.setYaw(-rotation.yaw()); break; case Y: - rotation.setPitch(-rotation.getPitch()); + rotation.setPitch(-rotation.pitch()); break; case Z: - rotation.setYaw((float) Math.PI - rotation.getYaw()); + rotation.setYaw((float) Math.PI - rotation.yaw()); } } } diff --git a/src/com/hypixel/hytale/math/Mat4f.java b/src/com/hypixel/hytale/math/Mat4f.java deleted file mode 100644 index 9b54ecc1..00000000 --- a/src/com/hypixel/hytale/math/Mat4f.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.hypixel.hytale.math; - -import io.netty.buffer.ByteBuf; -import javax.annotation.Nonnull; - -public class Mat4f { - public static final int SIZE = 64; - public final float m11; - public final float m12; - public final float m13; - public final float m14; - public final float m21; - public final float m22; - public final float m23; - public final float m24; - public final float m31; - public final float m32; - public final float m33; - public final float m34; - public final float m41; - public final float m42; - public final float m43; - public final float m44; - - public Mat4f( - float m11, - float m12, - float m13, - float m14, - float m21, - float m22, - float m23, - float m24, - float m31, - float m32, - float m33, - float m34, - float m41, - float m42, - float m43, - float m44 - ) { - this.m11 = m11; - this.m12 = m12; - this.m13 = m13; - this.m14 = m14; - this.m21 = m21; - this.m22 = m22; - this.m23 = m23; - this.m24 = m24; - this.m31 = m31; - this.m32 = m32; - this.m33 = m33; - this.m34 = m34; - this.m41 = m41; - this.m42 = m42; - this.m43 = m43; - this.m44 = m44; - } - - @Nonnull - public static Mat4f identity() { - return new Mat4f(1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F); - } - - @Nonnull - public static Mat4f deserialize(@Nonnull ByteBuf buf, int offset) { - return new Mat4f( - Float.intBitsToFloat(buf.getIntLE(offset)), - Float.intBitsToFloat(buf.getIntLE(offset + 4)), - Float.intBitsToFloat(buf.getIntLE(offset + 8)), - Float.intBitsToFloat(buf.getIntLE(offset + 12)), - Float.intBitsToFloat(buf.getIntLE(offset + 16)), - Float.intBitsToFloat(buf.getIntLE(offset + 20)), - Float.intBitsToFloat(buf.getIntLE(offset + 24)), - Float.intBitsToFloat(buf.getIntLE(offset + 28)), - Float.intBitsToFloat(buf.getIntLE(offset + 32)), - Float.intBitsToFloat(buf.getIntLE(offset + 36)), - Float.intBitsToFloat(buf.getIntLE(offset + 40)), - Float.intBitsToFloat(buf.getIntLE(offset + 44)), - Float.intBitsToFloat(buf.getIntLE(offset + 48)), - Float.intBitsToFloat(buf.getIntLE(offset + 52)), - Float.intBitsToFloat(buf.getIntLE(offset + 56)), - Float.intBitsToFloat(buf.getIntLE(offset + 60)) - ); - } - - public void serialize(@Nonnull ByteBuf buf) { - buf.writeIntLE(Float.floatToRawIntBits(this.m11)); - buf.writeIntLE(Float.floatToRawIntBits(this.m12)); - buf.writeIntLE(Float.floatToRawIntBits(this.m13)); - buf.writeIntLE(Float.floatToRawIntBits(this.m14)); - buf.writeIntLE(Float.floatToRawIntBits(this.m21)); - buf.writeIntLE(Float.floatToRawIntBits(this.m22)); - buf.writeIntLE(Float.floatToRawIntBits(this.m23)); - buf.writeIntLE(Float.floatToRawIntBits(this.m24)); - buf.writeIntLE(Float.floatToRawIntBits(this.m31)); - buf.writeIntLE(Float.floatToRawIntBits(this.m32)); - buf.writeIntLE(Float.floatToRawIntBits(this.m33)); - buf.writeIntLE(Float.floatToRawIntBits(this.m34)); - buf.writeIntLE(Float.floatToRawIntBits(this.m41)); - buf.writeIntLE(Float.floatToRawIntBits(this.m42)); - buf.writeIntLE(Float.floatToRawIntBits(this.m43)); - buf.writeIntLE(Float.floatToRawIntBits(this.m44)); - } -} diff --git a/src/com/hypixel/hytale/math/Quatf.java b/src/com/hypixel/hytale/math/Quatf.java deleted file mode 100644 index f83161c3..00000000 --- a/src/com/hypixel/hytale/math/Quatf.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.hypixel.hytale.math; - -import io.netty.buffer.ByteBuf; -import javax.annotation.Nonnull; - -public class Quatf { - public static final int SIZE = 16; - public final float x; - public final float y; - public final float z; - public final float w; - - public Quatf(float x, float y, float z, float w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - @Nonnull - public static Quatf deserialize(@Nonnull ByteBuf buf, int offset) { - return new Quatf( - Float.intBitsToFloat(buf.getIntLE(offset)), - Float.intBitsToFloat(buf.getIntLE(offset + 4)), - Float.intBitsToFloat(buf.getIntLE(offset + 8)), - Float.intBitsToFloat(buf.getIntLE(offset + 12)) - ); - } - - public void serialize(@Nonnull ByteBuf buf) { - buf.writeIntLE(Float.floatToRawIntBits(this.x)); - buf.writeIntLE(Float.floatToRawIntBits(this.y)); - buf.writeIntLE(Float.floatToRawIntBits(this.z)); - buf.writeIntLE(Float.floatToRawIntBits(this.w)); - } -} diff --git a/src/com/hypixel/hytale/math/Vec2f.java b/src/com/hypixel/hytale/math/Vec2f.java deleted file mode 100644 index 6da0ad59..00000000 --- a/src/com/hypixel/hytale/math/Vec2f.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.hypixel.hytale.math; - -import io.netty.buffer.ByteBuf; -import javax.annotation.Nonnull; - -public final class Vec2f { - public static final int SIZE = 8; - public float x; - public float y; - - public Vec2f(float x, float y) { - this.x = x; - this.y = y; - } - - public Vec2f() { - } - - @Nonnull - public static Vec2f deserialize(@Nonnull ByteBuf buf, int offset) { - return new Vec2f(Float.intBitsToFloat(buf.getIntLE(offset)), Float.intBitsToFloat(buf.getIntLE(offset + 4))); - } - - public void serialize(@Nonnull ByteBuf buf) { - buf.writeIntLE(Float.floatToRawIntBits(this.x)); - buf.writeIntLE(Float.floatToRawIntBits(this.y)); - } - - @Override - public String toString() { - return "Vec2f(" + this.x + ", " + this.y + ")"; - } -} diff --git a/src/com/hypixel/hytale/math/Vec3f.java b/src/com/hypixel/hytale/math/Vec3f.java deleted file mode 100644 index cb7d9333..00000000 --- a/src/com/hypixel/hytale/math/Vec3f.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.hypixel.hytale.math; - -import io.netty.buffer.ByteBuf; -import javax.annotation.Nonnull; - -public final class Vec3f { - public static final int SIZE = 12; - public float x; - public float y; - public float z; - - public Vec3f(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - } - - public Vec3f() { - } - - @Nonnull - public static Vec3f deserialize(@Nonnull ByteBuf buf, int offset) { - return new Vec3f( - Float.intBitsToFloat(buf.getIntLE(offset)), Float.intBitsToFloat(buf.getIntLE(offset + 4)), Float.intBitsToFloat(buf.getIntLE(offset + 8)) - ); - } - - public void serialize(@Nonnull ByteBuf buf) { - buf.writeIntLE(Float.floatToRawIntBits(this.x)); - buf.writeIntLE(Float.floatToRawIntBits(this.y)); - buf.writeIntLE(Float.floatToRawIntBits(this.z)); - } - - @Override - public String toString() { - return "Vec3f(" + this.x + ", " + this.y + ", " + this.z + ")"; - } -} diff --git a/src/com/hypixel/hytale/math/Vec4f.java b/src/com/hypixel/hytale/math/Vec4f.java deleted file mode 100644 index 47bac9be..00000000 --- a/src/com/hypixel/hytale/math/Vec4f.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.hypixel.hytale.math; - -import io.netty.buffer.ByteBuf; -import javax.annotation.Nonnull; - -public class Vec4f { - public static final int SIZE = 16; - public final float x; - public final float y; - public final float z; - public final float w; - - public Vec4f(float x, float y, float z, float w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - @Nonnull - public static Vec4f deserialize(@Nonnull ByteBuf buf, int offset) { - return new Vec4f( - Float.intBitsToFloat(buf.getIntLE(offset)), - Float.intBitsToFloat(buf.getIntLE(offset + 4)), - Float.intBitsToFloat(buf.getIntLE(offset + 8)), - Float.intBitsToFloat(buf.getIntLE(offset + 12)) - ); - } - - public void serialize(@Nonnull ByteBuf buf) { - buf.writeIntLE(Float.floatToRawIntBits(this.x)); - buf.writeIntLE(Float.floatToRawIntBits(this.y)); - buf.writeIntLE(Float.floatToRawIntBits(this.z)); - buf.writeIntLE(Float.floatToRawIntBits(this.w)); - } -} diff --git a/src/com/hypixel/hytale/math/block/BlockConeUtil.java b/src/com/hypixel/hytale/math/block/BlockConeUtil.java index 7a630e97..3a97b9b0 100644 --- a/src/com/hypixel/hytale/math/block/BlockConeUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockConeUtil.java @@ -6,6 +6,12 @@ import javax.annotation.Nonnull; public class BlockConeUtil { public static void forEachBlock( int originX, int originY, int originZ, int radiusX, int height, int radiusZ, T t, @Nonnull TriIntObjPredicate consumer + ) { + forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, false, false, t, consumer); + } + + public static void forEachBlock( + int originX, int originY, int originZ, int radiusX, int height, int radiusZ, boolean evenXZ, boolean evenH, T t, @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -14,24 +20,37 @@ public class BlockConeUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetH = evenH ? 0.5F : 0.0F; + int maxXi = evenXZ ? radiusX - 1 : radiusX; + int maxZi = evenXZ ? radiusZ - 1 : radiusZ; float radiusXAdjusted = radiusX + 0.41F; float radiusZAdjusted = radiusZ + 0.41F; for (int y = height - 1; y >= 0; y--) { - double rf = 1.0 - (double)y / height; + double sy = y + offsetH; + double rf = 1.0 - sy / height; double dx = radiusXAdjusted * rf; - int maxX; - int minX = -(maxX = (int)dx); - for (int x = minX; x <= maxX; x++) { - double qx = 1.0 - x * x / (dx * dx); - double dz = Math.sqrt(qx) * radiusZAdjusted * rf; - int maxZ; - int minZ = -(maxZ = (int)dz); + for (int x = -radiusX; x <= maxXi; x++) { + double sx = x + offsetXZ; + if (!(Math.abs(sx) > dx)) { + double qx = 1.0 - sx * sx / (dx * dx); + double dz = Math.sqrt(qx) * radiusZAdjusted * rf; + int minZi = (int)Math.ceil(-dz - offsetXZ); + int maxZc = (int)(dz - offsetXZ); + if (minZi < -radiusZ) { + minZi = -radiusZ; + } - for (int z = minZ; z <= maxZ; z++) { - if (!consumer.test(originX + x, originY + y, originZ + z, t)) { - return; + if (maxZc > maxZi) { + maxZc = maxZi; + } + + for (int z = minZi; z <= maxZc; z++) { + if (!consumer.test(originX + x, originY + y, originZ + z, t)) { + return; + } } } } @@ -86,8 +105,35 @@ public class BlockConeUtil { } } + public static void forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int height, + int radiusZ, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenH, + T t, + @Nonnull TriIntObjPredicate consumer + ) { + if (!evenXZ && !evenH) { + forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, thickness, capped, t, consumer); + } else { + forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, evenXZ, evenH, t, consumer); + } + } + public static void forEachBlockInverted( int originX, int originY, int originZ, int radiusX, int height, int radiusZ, T t, @Nonnull TriIntObjPredicate consumer + ) { + forEachBlockInverted(originX, originY, originZ, radiusX, height, radiusZ, false, false, t, consumer); + } + + public static void forEachBlockInverted( + int originX, int originY, int originZ, int radiusX, int height, int radiusZ, boolean evenXZ, boolean evenH, T t, @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -96,24 +142,37 @@ public class BlockConeUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetH = evenH ? 0.5F : 0.0F; + int maxXi = evenXZ ? radiusX - 1 : radiusX; + int maxZi = evenXZ ? radiusZ - 1 : radiusZ; float radiusXAdjusted = radiusX + 0.41F; float radiusZAdjusted = radiusZ + 0.41F; for (int y = height - 1; y >= 0; y--) { - double rf = 1.0 - (double)y / height; + double sy = y + offsetH; + double rf = 1.0 - sy / height; double dx = radiusXAdjusted * rf; - int maxX; - int minX = -(maxX = (int)dx); - for (int x = minX; x <= maxX; x++) { - double qx = 1.0 - x * x / (dx * dx); - double dz = Math.sqrt(qx) * radiusZAdjusted * rf; - int maxZ; - int minZ = -(maxZ = (int)dz); + for (int x = -radiusX; x <= maxXi; x++) { + double sx = x + offsetXZ; + if (!(Math.abs(sx) > dx)) { + double qx = 1.0 - sx * sx / (dx * dx); + double dz = Math.sqrt(qx) * radiusZAdjusted * rf; + int minZi = (int)Math.ceil(-dz - offsetXZ); + int maxZc = (int)(dz - offsetXZ); + if (minZi < -radiusZ) { + minZi = -radiusZ; + } - for (int z = minZ; z <= maxZ; z++) { - if (!consumer.test(originX + x, originY + height - 1 - y, originZ + z, t)) { - return; + if (maxZc > maxZi) { + maxZc = maxZi; + } + + for (int z = minZi; z <= maxZc; z++) { + if (!consumer.test(originX + x, originY + height - 1 - y, originZ + z, t)) { + return; + } } } } @@ -167,4 +226,25 @@ public class BlockConeUtil { } } } + + public static void forEachBlockInverted( + int originX, + int originY, + int originZ, + int radiusX, + int height, + int radiusZ, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenH, + T t, + @Nonnull TriIntObjPredicate consumer + ) { + if (!evenXZ && !evenH) { + forEachBlockInverted(originX, originY, originZ, radiusX, height, radiusZ, thickness, capped, t, consumer); + } else { + forEachBlockInverted(originX, originY, originZ, radiusX, height, radiusZ, evenXZ, evenH, t, consumer); + } + } } diff --git a/src/com/hypixel/hytale/math/block/BlockCubeUtil.java b/src/com/hypixel/hytale/math/block/BlockCubeUtil.java index 06602948..e575b604 100644 --- a/src/com/hypixel/hytale/math/block/BlockCubeUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockCubeUtil.java @@ -1,18 +1,28 @@ package com.hypixel.hytale.math.block; import com.hypixel.hytale.function.predicate.TriIntObjPredicate; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BlockCubeUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int radiusX, int height, int radiusZ, T t, @Nonnull TriIntObjPredicate consumer ) { - int radiusY = height / 2; + return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, false, false, t, consumer); + } - for (int dx = -radiusX; dx <= radiusX; dx++) { - for (int dz = -radiusZ; dz <= radiusZ; dz++) { - for (int dy = -radiusY; dy <= radiusY; dy++) { + public static boolean forEachBlock( + int originX, int originY, int originZ, int radiusX, int height, int radiusZ, boolean evenXZ, boolean evenY, T t, @Nonnull TriIntObjPredicate consumer + ) { + int maxDx = evenXZ ? radiusX - 1 : radiusX; + int maxDz = evenXZ ? radiusZ - 1 : radiusZ; + int radiusY = height / 2; + int maxDy = evenY ? radiusY - 1 : radiusY; + + for (int dx = -radiusX; dx <= maxDx; dx++) { + for (int dz = -radiusZ; dz <= maxDz; dz++) { + for (int dy = -radiusY; dy <= maxDy; dy++) { if (!consumer.test(originX + dx, originY + dy, originZ + dz, t)) { return false; } @@ -48,9 +58,28 @@ public class BlockCubeUtil { boolean hollow, T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, thickness, cappedTop, cappedBottom, hollow, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int height, + int radiusZ, + int thickness, + boolean cappedTop, + boolean cappedBottom, + boolean hollow, + boolean evenXZ, + boolean evenY, + T t, + @Nonnull TriIntObjPredicate consumer ) { if (thickness < 1) { - return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, t, consumer); + return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, evenXZ, evenY, t, consumer); } else { int radiusY = height / 2; int innerMinX = -radiusX + thickness; @@ -59,10 +88,13 @@ public class BlockCubeUtil { int innerMaxZ = radiusZ - thickness; int innerMinY = cappedBottom ? -radiusY + thickness : -height; int innerMaxY = cappedTop ? radiusY - thickness : height; + int maxDx = evenXZ ? radiusX - 1 : radiusX; + int maxDz = evenXZ ? radiusZ - 1 : radiusZ; + int maxDy = evenY ? radiusY - 1 : radiusY; - for (int dx = -radiusX; dx <= radiusX; dx++) { - for (int dz = -radiusZ; dz <= radiusZ; dz++) { - for (int dy = -radiusY; dy <= radiusY; dy++) { + for (int dx = -radiusX; dx <= maxDx; dx++) { + for (int dz = -radiusZ; dz <= maxDz; dz++) { + for (int dy = -radiusY; dy <= maxDy; dy++) { if (dy < innerMinY || dy > innerMaxY || dx < innerMinX || dx > innerMaxX || dz < innerMinZ || dz > innerMaxZ) { if (!consumer.test(originX + dx, originY + dy, originZ + dz, t)) { return false; @@ -79,8 +111,8 @@ public class BlockCubeUtil { } public static boolean forEachBlock(@Nonnull Vector3i pointOne, @Nonnull Vector3i pointTwo, T t, @Nonnull TriIntObjPredicate consumer) { - Vector3i min = Vector3i.min(pointOne, pointTwo); - Vector3i max = Vector3i.max(pointOne, pointTwo); + Vector3i min = Vector3iUtil.min(pointOne, pointTwo); + Vector3i max = Vector3iUtil.max(pointOne, pointTwo); for (int x = min.x; x <= max.x; x++) { for (int z = min.z; z <= max.z; z++) { @@ -108,8 +140,8 @@ public class BlockCubeUtil { if (thickness < 1) { return forEachBlock(pointOne, pointTwo, t, consumer); } else { - Vector3i min = Vector3i.min(pointOne, pointTwo); - Vector3i max = Vector3i.max(pointOne, pointTwo); + Vector3i min = Vector3iUtil.min(pointOne, pointTwo); + Vector3i max = Vector3iUtil.max(pointOne, pointTwo); int innerMinX = min.x + thickness; int innerMaxX = max.x - thickness; int innerMinZ = min.z + thickness; diff --git a/src/com/hypixel/hytale/math/block/BlockCylinderUtil.java b/src/com/hypixel/hytale/math/block/BlockCylinderUtil.java index 4e68c46f..60e173f2 100644 --- a/src/com/hypixel/hytale/math/block/BlockCylinderUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockCylinderUtil.java @@ -7,6 +7,12 @@ import javax.annotation.Nonnull; public class BlockCylinderUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int radiusX, int height, int radiusZ, T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, int originY, int originZ, int radiusX, int height, int radiusZ, boolean evenXZ, boolean evenH, T t, @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -15,20 +21,29 @@ public class BlockCylinderUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + int maxX = evenXZ ? radiusX - 1 : radiusX; + int maxZBound = evenXZ ? radiusZ - 1 : radiusZ; + int sizeH = height; float radiusXAdjusted = radiusX + 0.41F; float radiusZAdjusted = radiusZ + 0.41F; double invRadiusXSqr = 1.0 / (radiusXAdjusted * radiusXAdjusted); - for (int x = -radiusX; x <= radiusX; x++) { - double qx = 1.0 - x * x * invRadiusXSqr; - double dz = Math.sqrt(qx) * radiusZAdjusted; - int maxZ; - int minZ = -(maxZ = (int)dz); + for (int x = -radiusX; x <= maxX; x++) { + double sx = x + offsetXZ; + double qx = 1.0 - sx * sx * invRadiusXSqr; + if (!(qx < 0.0)) { + double dz = Math.sqrt(qx) * radiusZAdjusted; + int minZi = (int)Math.ceil(-dz - offsetXZ); + int maxZi = (int)(dz - offsetXZ); + minZi = Math.max(minZi, -radiusZ); + maxZi = Math.min(maxZi, maxZBound); - for (int z = minZ; z <= maxZ; z++) { - for (int y = height - 1; y >= 0; y--) { - if (!consumer.test(originX + x, originY + y, originZ + z, t)) { - return false; + for (int z = minZi; z <= maxZi; z++) { + for (int y = sizeH - 1; y >= 0; y--) { + if (!consumer.test(originX + x, originY + y, originZ + z, t)) { + return false; + } } } } @@ -46,9 +61,26 @@ public class BlockCylinderUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int radiusX, int height, int radiusZ, int thickness, boolean capped, T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, thickness, capped, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int height, + int radiusZ, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenH, + T t, + @Nonnull TriIntObjPredicate consumer ) { if (thickness < 1) { - return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, t, consumer); + return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, evenXZ, evenH, t, consumer); } else if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); } else if (height <= 0) { @@ -56,6 +88,9 @@ public class BlockCylinderUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + int maxX = evenXZ ? radiusX - 1 : radiusX; + int maxZBound = evenXZ ? radiusZ - 1 : radiusZ; float radiusXAdjusted = radiusX + 0.41F; float radiusZAdjusted = radiusZ + 0.41F; float innerRadiusXAdjusted = radiusXAdjusted - thickness; @@ -69,39 +104,33 @@ public class BlockCylinderUtil { for (int y = height - 1; y >= 0; y--) { boolean cap = capped && (y < innerMinY || y > innerMaxY); - for (int x = -radiusX; x <= radiusX; x++) { - double qx = 1.0 - x * x * invRadiusXSqr; - double dz = Math.sqrt(qx) * radiusZAdjusted; - int maxZ = (int)dz; - double innerQx = x < innerRadiusXAdjusted ? 1.0 - x * x * invInnerRadiusXSqr : 0.0; - double innerDZ = innerQx > 0.0 ? Math.sqrt(innerQx) * innerRadiusZAdjusted : 0.0; - int minZ = cap ? 0 : MathUtil.ceil(innerDZ); - int z = minZ; - if (minZ == 0) { - if (!consumer.test(originX + x, originY + y, originZ, t)) { - return false; + for (int x = -radiusX; x <= maxX; x++) { + double sx = x + offsetXZ; + double qx = 1.0 - sx * sx * invRadiusXSqr; + if (!(qx < 0.0)) { + double dz = Math.sqrt(qx) * radiusZAdjusted; + int minZi = (int)Math.ceil(-dz - offsetXZ); + int maxZi = (int)(dz - offsetXZ); + minZi = Math.max(minZi, -radiusZ); + maxZi = Math.min(maxZi, maxZBound); + if (minZi <= maxZi) { + double innerQx = 1.0 - sx * sx * invInnerRadiusXSqr; + double innerDZ = innerQx > 0.0 ? Math.sqrt(innerQx) * innerRadiusZAdjusted : 0.0; + int innerBound = !cap && !(innerDZ <= 0.0) ? MathUtil.ceil(innerDZ - offsetXZ) : 0; + + for (int z = minZi; z <= maxZi; z++) { + if ((cap || !(innerDZ > 0.0) || Math.abs(z) >= innerBound) && !consumer.test(originX + x, originY + y, originZ + z, t)) { + return false; + } + } } - - z = minZ + 1; - } - - while (z <= maxZ) { - if (!consumer.test(originX + x, originY + y, originZ + z, t)) { - return false; - } - - if (!consumer.test(originX + x, originY + y, originZ - z, t)) { - return false; - } - - z++; } } } return true; } else { - return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, t, consumer); + return forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, evenXZ, evenH, t, consumer); } } } diff --git a/src/com/hypixel/hytale/math/block/BlockDiamondUtil.java b/src/com/hypixel/hytale/math/block/BlockDiamondUtil.java index 7286eada..01deb0ec 100644 --- a/src/com/hypixel/hytale/math/block/BlockDiamondUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockDiamondUtil.java @@ -7,6 +7,21 @@ import javax.annotation.Nullable; public class BlockDiamondUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int radiusX, int radiusY, int radiusZ, @Nullable T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int radiusY, + int radiusZ, + boolean evenXZ, + boolean evenY, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -15,20 +30,28 @@ public class BlockDiamondUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { - float radiusXAdjusted = radiusX + 0.41F; - float radiusZAdjusted = radiusZ + 0.41F; + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetY = evenY ? 0.5F : 0.0F; + int maxXi = evenXZ ? radiusX - 1 : radiusX; + int maxZi = evenXZ ? radiusZ - 1 : radiusZ; + int maxYi = evenY ? radiusY - 1 : radiusY; + float radiusXAdj = radiusX + 0.41F; + float radiusZAdj = radiusZ + 0.41F; - for (int y = 0; y <= radiusY; y++) { - float normalizedY = (float)y / radiusY; - float currentRadiusX = radiusXAdjusted * (1.0F - normalizedY); - float currentRadiusZ = radiusZAdjusted * (1.0F - normalizedY); - int maxX = (int)currentRadiusX; - int maxZ = (int)currentRadiusZ; + for (int y = -radiusY; y <= maxYi; y++) { + float sy = y + offsetY; + float normalizedY = Math.abs(sy) / radiusY; + float currentRadiusX = radiusXAdj * (1.0F - normalizedY); + float currentRadiusZ = radiusZAdj * (1.0F - normalizedY); - for (int x = 0; x <= maxX; x++) { - for (int z = 0; z <= maxZ; z++) { - if (Math.abs(x) <= currentRadiusX && Math.abs(z) <= currentRadiusZ && !test(originX, originY, originZ, x, y, z, t, consumer)) { - return false; + for (int x = -radiusX; x <= maxXi; x++) { + float sx = x + offsetXZ; + if (!(Math.abs(sx) > currentRadiusX)) { + for (int z = -radiusZ; z <= maxZi; z++) { + float sz = z + offsetXZ; + if (Math.abs(sz) <= currentRadiusZ && !consumer.test(originX + x, originY + y, originZ + z, t)) { + return false; + } } } } @@ -88,6 +111,25 @@ public class BlockDiamondUtil { } } + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int radiusY, + int radiusZ, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenY, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer + ) { + return !evenXZ && !evenY + ? forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, thickness, capped, t, consumer) + : forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, evenXZ, evenY, t, consumer); + } + private static boolean test(int originX, int originY, int originZ, int x, int y, int z, T context, @Nonnull TriIntObjPredicate consumer) { if (!consumer.test(originX + x, originY + y, originZ + z, context)) { return false; diff --git a/src/com/hypixel/hytale/math/block/BlockDomeUtil.java b/src/com/hypixel/hytale/math/block/BlockDomeUtil.java index 1a8ee4fa..44ccd702 100644 --- a/src/com/hypixel/hytale/math/block/BlockDomeUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockDomeUtil.java @@ -7,6 +7,21 @@ import javax.annotation.Nullable; public class BlockDomeUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int radiusX, int radiusY, int radiusZ, @Nullable T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int radiusY, + int radiusZ, + boolean evenXZ, + boolean evenH, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -15,24 +30,41 @@ public class BlockDomeUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetH = evenH ? 0.5F : 0.0F; + int maxXi = evenXZ ? radiusX - 1 : radiusX; + int maxZi = evenXZ ? radiusZ - 1 : radiusZ; + int maxHi = radiusY - 1; float radiusXAdjusted = radiusX + 0.41F; float radiusYAdjusted = radiusY + 0.41F; float radiusZAdjusted = radiusZ + 0.41F; float invRadiusXSqr = 1.0F / (radiusXAdjusted * radiusXAdjusted); float invRadiusYSqr = 1.0F / (radiusYAdjusted * radiusYAdjusted); - for (int x = 0; x <= radiusX; x++) { - float qx = 1.0F - x * x * invRadiusXSqr; - double dy = Math.sqrt(qx) * radiusYAdjusted; - int maxY = (int)dy; + for (int x = -radiusX; x <= maxXi; x++) { + float sx = x + offsetXZ; + float qx = 1.0F - sx * sx * invRadiusXSqr; + if (!(qx < 0.0F)) { + double dy = Math.sqrt(qx) * radiusYAdjusted; + int maxYi = Math.min(maxHi, (int)(dy - offsetH)); - for (int y = 0; y <= maxY; y++) { - double dz = Math.sqrt(qx - y * y * invRadiusYSqr) * radiusZAdjusted; - int maxZ = (int)dz; + for (int y = 0; y <= maxYi; y++) { + float sy = y + offsetH; + double dz = Math.sqrt(qx - sy * sy * invRadiusYSqr) * radiusZAdjusted; + int minZi = (int)Math.ceil(-dz - offsetXZ); + int maxZc = (int)(dz - offsetXZ); + if (minZi < -radiusZ) { + minZi = -radiusZ; + } - for (int z = 0; z <= maxZ; z++) { - if (!test(originX, originY, originZ, x, y, z, t, consumer)) { - return false; + if (maxZc > maxZi) { + maxZc = maxZi; + } + + for (int z = minZi; z <= maxZc; z++) { + if (!consumer.test(originX + x, originY + y, originZ + z, t)) { + return false; + } } } } @@ -77,7 +109,7 @@ public class BlockDomeUtil { float invInnerRadiusZ2 = 1.0F / (innerRadiusZAdjusted * innerRadiusZAdjusted); int y = 0; - for (int y1 = 1; y <= radiusY; y1++) { + for (int y1 = 1; y < radiusY; y1++) { float qy = y * y * invRadiusY2; double dx = Math.sqrt(1.0F - qy) * radiusXAdjusted; int maxX = (int)dx; @@ -128,6 +160,25 @@ public class BlockDomeUtil { } } + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int radiusY, + int radiusZ, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenH, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer + ) { + return !evenXZ && !evenH + ? forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, thickness, capped, t, consumer) + : forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, evenXZ, evenH, t, consumer); + } + private static boolean test(int originX, int originY, int originZ, int x, int y, int z, T context, @Nonnull TriIntObjPredicate consumer) { if (!consumer.test(originX + x, originY + y, originZ + z, context)) { return false; diff --git a/src/com/hypixel/hytale/math/block/BlockInvertedDomeUtil.java b/src/com/hypixel/hytale/math/block/BlockInvertedDomeUtil.java index 3c821212..384dd584 100644 --- a/src/com/hypixel/hytale/math/block/BlockInvertedDomeUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockInvertedDomeUtil.java @@ -7,6 +7,21 @@ import javax.annotation.Nullable; public class BlockInvertedDomeUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int radiusX, int radiusY, int radiusZ, @Nullable T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int radiusY, + int radiusZ, + boolean evenXZ, + boolean evenH, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -15,24 +30,41 @@ public class BlockInvertedDomeUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetH = evenH ? 0.5F : 0.0F; + int maxXi = evenXZ ? radiusX - 1 : radiusX; + int maxZi = evenXZ ? radiusZ - 1 : radiusZ; + int maxHi = radiusY - 1; float radiusXAdjusted = radiusX + 0.41F; float radiusYAdjusted = radiusY + 0.41F; float radiusZAdjusted = radiusZ + 0.41F; float invRadiusXSqr = 1.0F / (radiusXAdjusted * radiusXAdjusted); float invRadiusYSqr = 1.0F / (radiusYAdjusted * radiusYAdjusted); - for (int x = 0; x <= radiusX; x++) { - float qx = 1.0F - x * x * invRadiusXSqr; - double dy = Math.sqrt(qx) * radiusYAdjusted; - int maxY = (int)dy; + for (int x = -radiusX; x <= maxXi; x++) { + float sx = x + offsetXZ; + float qx = 1.0F - sx * sx * invRadiusXSqr; + if (!(qx < 0.0F)) { + double dy = Math.sqrt(qx) * radiusYAdjusted; + int maxYi = Math.min(maxHi, (int)(dy - offsetH)); - for (int y = 0; y <= maxY; y++) { - double dz = Math.sqrt(qx - y * y * invRadiusYSqr) * radiusZAdjusted; - int maxZ = (int)dz; + for (int y = 0; y <= maxYi; y++) { + float sy = y + offsetH; + double dz = Math.sqrt(qx - sy * sy * invRadiusYSqr) * radiusZAdjusted; + int minZi = (int)Math.ceil(-dz - offsetXZ); + int maxZc = (int)(dz - offsetXZ); + if (minZi < -radiusZ) { + minZi = -radiusZ; + } - for (int z = 0; z <= maxZ; z++) { - if (!test(originX, originY, originZ, x, -y, z, t, consumer)) { - return false; + if (maxZc > maxZi) { + maxZc = maxZi; + } + + for (int z = minZi; z <= maxZc; z++) { + if (!consumer.test(originX + x, originY - y, originZ + z, t)) { + return false; + } } } } @@ -77,7 +109,7 @@ public class BlockInvertedDomeUtil { float invInnerRadiusZ2 = 1.0F / (innerRadiusZAdjusted * innerRadiusZAdjusted); int y = 0; - for (int y1 = 1; y <= radiusY; y1++) { + for (int y1 = 1; y < radiusY; y1++) { float qy = y * y * invRadiusY2; double dx = Math.sqrt(1.0F - qy) * radiusXAdjusted; int maxX = (int)dx; @@ -128,6 +160,25 @@ public class BlockInvertedDomeUtil { } } + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int radiusY, + int radiusZ, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenH, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer + ) { + return !evenXZ && !evenH + ? forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, thickness, capped, t, consumer) + : forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, evenXZ, evenH, t, consumer); + } + private static boolean test(int originX, int originY, int originZ, int x, int y, int z, T context, @Nonnull TriIntObjPredicate consumer) { if (!consumer.test(originX + x, originY + y, originZ + z, context)) { return false; diff --git a/src/com/hypixel/hytale/math/block/BlockPyramidUtil.java b/src/com/hypixel/hytale/math/block/BlockPyramidUtil.java index 0e5d4b98..ddd006cf 100644 --- a/src/com/hypixel/hytale/math/block/BlockPyramidUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockPyramidUtil.java @@ -6,6 +6,12 @@ import javax.annotation.Nonnull; public class BlockPyramidUtil { public static void forEachBlock( int originX, int originY, int originZ, int radiusX, int height, int radiusZ, T t, @Nonnull TriIntObjPredicate consumer + ) { + forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, false, false, t, consumer); + } + + public static void forEachBlock( + int originX, int originY, int originZ, int radiusX, int height, int radiusZ, boolean evenXZ, boolean evenH, T t, @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -14,20 +20,34 @@ public class BlockPyramidUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetH = evenH ? 0.5F : 0.0F; + int maxXi = evenXZ ? radiusX - 1 : radiusX; + int maxZi = evenXZ ? radiusZ - 1 : radiusZ; + for (int y = height - 1; y >= 0; y--) { - double rf = 1.0 - (double)y / height; + double sy = y + offsetH; + double rf = 1.0 - sy / height; double dx = radiusX * rf; - int maxX; - int minX = -(maxX = (int)dx); + double dz = radiusZ * rf; - for (int x = minX; x <= maxX; x++) { - double dz = radiusZ * rf; - int maxZ; - int minZ = -(maxZ = (int)dz); + for (int x = -radiusX; x <= maxXi; x++) { + double sx = x + offsetXZ; + if (!(Math.abs(sx) > dx)) { + int minZi = (int)Math.ceil(-dz - offsetXZ); + int maxZc = (int)(dz - offsetXZ); + if (minZi < -radiusZ) { + minZi = -radiusZ; + } - for (int z = minZ; z <= maxZ; z++) { - if (!consumer.test(originX + x, originY + y, originZ + z, t)) { - return; + if (maxZc > maxZi) { + maxZc = maxZi; + } + + for (int z = minZi; z <= maxZc; z++) { + if (!consumer.test(originX + x, originY + y, originZ + z, t)) { + return; + } } } } @@ -83,8 +103,35 @@ public class BlockPyramidUtil { } } + public static void forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int height, + int radiusZ, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenH, + T t, + @Nonnull TriIntObjPredicate consumer + ) { + if (!evenXZ && !evenH) { + forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, thickness, capped, t, consumer); + } else { + forEachBlock(originX, originY, originZ, radiusX, height, radiusZ, evenXZ, evenH, t, consumer); + } + } + public static void forEachBlockInverted( int originX, int originY, int originZ, int radiusX, int height, int radiusZ, T t, @Nonnull TriIntObjPredicate consumer + ) { + forEachBlockInverted(originX, originY, originZ, radiusX, height, radiusZ, false, false, t, consumer); + } + + public static void forEachBlockInverted( + int originX, int originY, int originZ, int radiusX, int height, int radiusZ, boolean evenXZ, boolean evenH, T t, @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -93,20 +140,34 @@ public class BlockPyramidUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetH = evenH ? 0.5F : 0.0F; + int maxXi = evenXZ ? radiusX - 1 : radiusX; + int maxZi = evenXZ ? radiusZ - 1 : radiusZ; + for (int y = height - 1; y >= 0; y--) { - double rf = 1.0 - (double)y / height; + double sy = y + offsetH; + double rf = 1.0 - sy / height; double dx = radiusX * rf; - int maxX; - int minX = -(maxX = (int)dx); + double dz = radiusZ * rf; - for (int x = minX; x <= maxX; x++) { - double dz = radiusZ * rf; - int maxZ; - int minZ = -(maxZ = (int)dz); + for (int x = -radiusX; x <= maxXi; x++) { + double sx = x + offsetXZ; + if (!(Math.abs(sx) > dx)) { + int minZi = (int)Math.ceil(-dz - offsetXZ); + int maxZc = (int)(dz - offsetXZ); + if (minZi < -radiusZ) { + minZi = -radiusZ; + } - for (int z = minZ; z <= maxZ; z++) { - if (!consumer.test(originX + x, originY + height - 1 - y, originZ + z, t)) { - return; + if (maxZc > maxZi) { + maxZc = maxZi; + } + + for (int z = minZi; z <= maxZc; z++) { + if (!consumer.test(originX + x, originY + height - 1 - y, originZ + z, t)) { + return; + } } } } @@ -162,4 +223,25 @@ public class BlockPyramidUtil { } } } + + public static void forEachBlockInverted( + int originX, + int originY, + int originZ, + int radiusX, + int height, + int radiusZ, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenH, + T t, + @Nonnull TriIntObjPredicate consumer + ) { + if (!evenXZ && !evenH) { + forEachBlockInverted(originX, originY, originZ, radiusX, height, radiusZ, thickness, capped, t, consumer); + } else { + forEachBlockInverted(originX, originY, originZ, radiusX, height, radiusZ, evenXZ, evenH, t, consumer); + } + } } diff --git a/src/com/hypixel/hytale/math/block/BlockSphereUtil.java b/src/com/hypixel/hytale/math/block/BlockSphereUtil.java index 1b19cc29..019c4225 100644 --- a/src/com/hypixel/hytale/math/block/BlockSphereUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockSphereUtil.java @@ -41,6 +41,21 @@ public class BlockSphereUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int radiusX, int radiusY, int radiusZ, @Nullable T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int radiusY, + int radiusZ, + boolean evenXZ, + boolean evenY, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer ) { if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); @@ -49,24 +64,47 @@ public class BlockSphereUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetY = evenY ? 0.5F : 0.0F; + int minX = -radiusX; + int maxX = evenXZ ? radiusX - 1 : radiusX; + int minY = -radiusY; + int maxY = evenY ? radiusY - 1 : radiusY; + int minZ = -radiusZ; + int maxZ = evenXZ ? radiusZ - 1 : radiusZ; float radiusXAdjusted = radiusX + 0.41F; float radiusYAdjusted = radiusY + 0.41F; float radiusZAdjusted = radiusZ + 0.41F; float invRadiusXSqr = 1.0F / (radiusXAdjusted * radiusXAdjusted); float invRadiusYSqr = 1.0F / (radiusYAdjusted * radiusYAdjusted); - for (int x = 0; x <= radiusX; x++) { - float qx = 1.0F - x * x * invRadiusXSqr; - double dy = Math.sqrt(qx) * radiusYAdjusted; - int maxY = (int)dy; - - for (int y = 0; y <= maxY; y++) { - double dz = Math.sqrt(qx - y * y * invRadiusYSqr) * radiusZAdjusted; - int maxZ = (int)dz; - - for (int z = 0; z <= maxZ; z++) { - if (!test(originX, originY, originZ, x, y, z, t, consumer)) { - return false; + for (int x = minX; x <= maxX; x++) { + float sx = x + offsetXZ; + float qx = 1.0F - sx * sx * invRadiusXSqr; + if (!(qx < 0.0F)) { + double dy = Math.sqrt(qx) * radiusYAdjusted; + int loY = MathUtil.ceil(-dy - offsetY); + int hiY = (int)(dy - offsetY); + loY = Math.max(loY, minY); + hiY = Math.min(hiY, maxY); + if (loY <= hiY) { + for (int y = loY; y <= hiY; y++) { + float sy = y + offsetY; + float qxy = qx - sy * sy * invRadiusYSqr; + if (!(qxy < 0.0F)) { + double dz = Math.sqrt(qxy) * radiusZAdjusted; + int loZ = MathUtil.ceil(-dz - offsetXZ); + int hiZ = (int)(dz - offsetXZ); + loZ = Math.max(loZ, minZ); + hiZ = Math.min(hiZ, maxZ); + if (loZ <= hiZ) { + for (int z = loZ; z <= hiZ; z++) { + if (!consumer.test(originX + x, originY + y, originZ + z, t)) { + return false; + } + } + } + } } } } @@ -82,9 +120,25 @@ public class BlockSphereUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int radiusX, int radiusY, int radiusZ, int thickness, @Nullable T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, thickness, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int radiusX, + int radiusY, + int radiusZ, + int thickness, + boolean evenXZ, + boolean evenY, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer ) { if (thickness < 1) { - return forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, t, consumer); + return forEachBlock(originX, originY, originZ, radiusX, radiusY, radiusZ, evenXZ, evenY, t, consumer); } else if (radiusX <= 0) { throw new IllegalArgumentException(String.valueOf(radiusX)); } else if (radiusY <= 0) { @@ -92,6 +146,14 @@ public class BlockSphereUtil { } else if (radiusZ <= 0) { throw new IllegalArgumentException(String.valueOf(radiusZ)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetY = evenY ? 0.5F : 0.0F; + int minX = -radiusX; + int maxX = evenXZ ? radiusX - 1 : radiusX; + int minY = -radiusY; + int maxY = evenY ? radiusY - 1 : radiusY; + int minZ = -radiusZ; + int maxZ = evenXZ ? radiusZ - 1 : radiusZ; float radiusXAdjusted = radiusX + 0.41F; float radiusYAdjusted = radiusY + 0.41F; float radiusZAdjusted = radiusZ + 0.41F; @@ -104,46 +166,44 @@ public class BlockSphereUtil { float invInnerRadiusX2 = 1.0F / (innerRadiusXAdjusted * innerRadiusXAdjusted); float invInnerRadiusY2 = 1.0F / (innerRadiusYAdjusted * innerRadiusYAdjusted); float invInnerRadiusZ2 = 1.0F / (innerRadiusZAdjusted * innerRadiusZAdjusted); - int y = 0; + float invRadiusXSqr = invRadiusX2; + float invRadiusYSqr = invRadiusY2; - for (int y1 = 1; y <= radiusY; y1++) { - float qy = y * y * invRadiusY2; - double dx = Math.sqrt(1.0F - qy) * radiusXAdjusted; - int maxX = (int)dx; - float innerQy = y * y * invInnerRadiusY2; - float outerQy = y1 * y1 * invRadiusY2; - int x = 0; - - for (int x1 = 1; x <= maxX; x1++) { - float qx = x * x * invRadiusX2; - double dz = Math.sqrt(1.0F - qx - qy) * radiusZAdjusted; - int maxZ = (int)dz; - float innerQx = x * x * invInnerRadiusX2; - float outerQx = x1 * x1 * invRadiusX2; - int z = 0; - - for (int z1 = 1; z <= maxZ; z1++) { - label47: { - float innerQz = z * z * invInnerRadiusZ2; - if (innerQx + innerQy + innerQz < 1.0F) { - float outerQz = z1 * z1 * invRadiusZ2; - if (outerQx + outerQy + outerQz < 1.0F) { - break label47; + for (int x = minX; x <= maxX; x++) { + float sx = x + offsetXZ; + float qx = 1.0F - sx * sx * invRadiusXSqr; + if (!(qx < 0.0F)) { + double dy = Math.sqrt(qx) * radiusYAdjusted; + int loY = MathUtil.ceil(-dy - offsetY); + int hiY = (int)(dy - offsetY); + loY = Math.max(loY, minY); + hiY = Math.min(hiY, maxY); + if (loY <= hiY) { + for (int y = loY; y <= hiY; y++) { + float sy = y + offsetY; + float qxy = qx - sy * sy * invRadiusYSqr; + if (!(qxy < 0.0F)) { + double dz = Math.sqrt(qxy) * radiusZAdjusted; + int loZ = MathUtil.ceil(-dz - offsetXZ); + int hiZ = (int)(dz - offsetXZ); + loZ = Math.max(loZ, minZ); + hiZ = Math.min(hiZ, maxZ); + if (loZ <= hiZ) { + for (int z = loZ; z <= hiZ; z++) { + float sz = z + offsetXZ; + float outerVal = sx * sx * invRadiusX2 + sy * sy * invRadiusY2 + sz * sz * invRadiusZ2; + if (!(outerVal > 1.0F)) { + float innerVal = sx * sx * invInnerRadiusX2 + sy * sy * invInnerRadiusY2 + sz * sz * invInnerRadiusZ2; + if (!(innerVal < 1.0F) && !consumer.test(originX + x, originY + y, originZ + z, t)) { + return false; + } + } + } } } - - if (!test(originX, originY, originZ, x, y, z, t, consumer)) { - return false; - } } - - z++; } - - x++; } - - y++; } return true; diff --git a/src/com/hypixel/hytale/math/block/BlockTorusUtil.java b/src/com/hypixel/hytale/math/block/BlockTorusUtil.java index 3fff077b..01506562 100644 --- a/src/com/hypixel/hytale/math/block/BlockTorusUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockTorusUtil.java @@ -7,23 +7,45 @@ import javax.annotation.Nullable; public class BlockTorusUtil { public static boolean forEachBlock( int originX, int originY, int originZ, int outerRadius, int minorRadius, @Nullable T t, @Nonnull TriIntObjPredicate consumer + ) { + return forEachBlock(originX, originY, originZ, outerRadius, minorRadius, false, false, t, consumer); + } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int outerRadius, + int minorRadius, + boolean evenXZ, + boolean evenY, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer ) { if (outerRadius <= 0) { throw new IllegalArgumentException(String.valueOf(outerRadius)); } else if (minorRadius <= 0) { throw new IllegalArgumentException(String.valueOf(minorRadius)); } else { + float offsetXZ = evenXZ ? 0.5F : 0.0F; + float offsetY = evenY ? 0.5F : 0.0F; int majorRadius = Math.max(1, outerRadius - minorRadius); int sizeXZ = majorRadius + minorRadius; + int maxXZ = evenXZ ? sizeXZ - 1 : sizeXZ; + int maxY = evenY ? minorRadius - 1 : minorRadius; float minorRadiusAdjusted = minorRadius + 0.41F; - for (int x = -sizeXZ; x <= sizeXZ; x++) { - for (int z = -sizeXZ; z <= sizeXZ; z++) { - double distFromCenter = Math.sqrt(x * x + z * z); + for (int x = -sizeXZ; x <= maxXZ; x++) { + double sx = x + offsetXZ; + + for (int z = -sizeXZ; z <= maxXZ; z++) { + double sz = z + offsetXZ; + double distFromCenter = Math.sqrt(sx * sx + sz * sz); double distFromRing = distFromCenter - majorRadius; - for (int y = -minorRadius; y <= minorRadius; y++) { - double distFromTube = Math.sqrt(distFromRing * distFromRing + y * y); + for (int y = -minorRadius; y <= maxY; y++) { + double sy = y + offsetY; + double distFromTube = Math.sqrt(distFromRing * distFromRing + sy * sy); if (distFromTube <= minorRadiusAdjusted && !consumer.test(originX + x, originY + y, originZ + z, t)) { return false; } @@ -79,4 +101,22 @@ public class BlockTorusUtil { return true; } } + + public static boolean forEachBlock( + int originX, + int originY, + int originZ, + int outerRadius, + int minorRadius, + int thickness, + boolean capped, + boolean evenXZ, + boolean evenY, + @Nullable T t, + @Nonnull TriIntObjPredicate consumer + ) { + return !evenXZ && !evenY + ? forEachBlock(originX, originY, originZ, outerRadius, minorRadius, thickness, capped, t, consumer) + : forEachBlock(originX, originY, originZ, outerRadius, minorRadius, evenXZ, evenY, t, consumer); + } } diff --git a/src/com/hypixel/hytale/math/block/BlockUtil.java b/src/com/hypixel/hytale/math/block/BlockUtil.java index 02a24a10..03b1a32d 100644 --- a/src/com/hypixel/hytale/math/block/BlockUtil.java +++ b/src/com/hypixel/hytale/math/block/BlockUtil.java @@ -1,7 +1,8 @@ package com.hypixel.hytale.math.block; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class BlockUtil { public static final float RADIUS_ADJUST = 0.41F; @@ -20,6 +21,10 @@ public class BlockUtil { return pack(val.x, val.y, val.z); } + public static long pack(@Nonnull Vector3ic val) { + return pack(val.x(), val.y(), val.z()); + } + public static long pack(int x, int y, int z) { if (y <= -513L || y >= 512L) { throw new IllegalArgumentException(String.valueOf(y)); diff --git a/src/com/hypixel/hytale/math/codec/Vector2dArrayCodec.java b/src/com/hypixel/hytale/math/codec/Vector2dArrayCodec.java index cbf65339..6c42288e 100644 --- a/src/com/hypixel/hytale/math/codec/Vector2dArrayCodec.java +++ b/src/com/hypixel/hytale/math/codec/Vector2dArrayCodec.java @@ -7,12 +7,12 @@ import com.hypixel.hytale.codec.schema.config.ArraySchema; import com.hypixel.hytale.codec.schema.config.NumberSchema; import com.hypixel.hytale.codec.schema.config.Schema; import com.hypixel.hytale.codec.util.RawJsonReader; -import com.hypixel.hytale.math.vector.Vector2d; import java.io.IOException; import javax.annotation.Nonnull; import org.bson.BsonArray; import org.bson.BsonDouble; import org.bson.BsonValue; +import org.joml.Vector2d; @Deprecated public class Vector2dArrayCodec implements Codec { @@ -25,8 +25,8 @@ public class Vector2dArrayCodec implements Codec { @Nonnull public BsonValue encode(@Nonnull Vector2d t, ExtraInfo extraInfo) { BsonArray array = new BsonArray(); - array.add((BsonValue)(new BsonDouble(t.getX()))); - array.add((BsonValue)(new BsonDouble(t.getY()))); + array.add((BsonValue)(new BsonDouble(t.x()))); + array.add((BsonValue)(new BsonDouble(t.y()))); return array; } diff --git a/src/com/hypixel/hytale/math/codec/Vector3dArrayCodec.java b/src/com/hypixel/hytale/math/codec/Vector3dArrayCodec.java index f333cd55..9e74dabc 100644 --- a/src/com/hypixel/hytale/math/codec/Vector3dArrayCodec.java +++ b/src/com/hypixel/hytale/math/codec/Vector3dArrayCodec.java @@ -7,12 +7,12 @@ import com.hypixel.hytale.codec.schema.config.ArraySchema; import com.hypixel.hytale.codec.schema.config.NumberSchema; import com.hypixel.hytale.codec.schema.config.Schema; import com.hypixel.hytale.codec.util.RawJsonReader; -import com.hypixel.hytale.math.vector.Vector3d; import java.io.IOException; import javax.annotation.Nonnull; import org.bson.BsonArray; import org.bson.BsonDouble; import org.bson.BsonValue; +import org.joml.Vector3d; @Deprecated public class Vector3dArrayCodec implements Codec { @@ -25,9 +25,9 @@ public class Vector3dArrayCodec implements Codec { @Nonnull public BsonValue encode(@Nonnull Vector3d t, ExtraInfo extraInfo) { BsonArray array = new BsonArray(); - array.add((BsonValue)(new BsonDouble(t.getX()))); - array.add((BsonValue)(new BsonDouble(t.getY()))); - array.add((BsonValue)(new BsonDouble(t.getZ()))); + array.add((BsonValue)(new BsonDouble(t.x()))); + array.add((BsonValue)(new BsonDouble(t.y()))); + array.add((BsonValue)(new BsonDouble(t.z()))); return array; } diff --git a/src/com/hypixel/hytale/math/codec/Vector3iArrayCodec.java b/src/com/hypixel/hytale/math/codec/Vector3iArrayCodec.java index 8a8434d4..196ef1a2 100644 --- a/src/com/hypixel/hytale/math/codec/Vector3iArrayCodec.java +++ b/src/com/hypixel/hytale/math/codec/Vector3iArrayCodec.java @@ -7,12 +7,12 @@ import com.hypixel.hytale.codec.schema.config.ArraySchema; import com.hypixel.hytale.codec.schema.config.NumberSchema; import com.hypixel.hytale.codec.schema.config.Schema; import com.hypixel.hytale.codec.util.RawJsonReader; -import com.hypixel.hytale.math.vector.Vector3i; import java.io.IOException; import javax.annotation.Nonnull; import org.bson.BsonArray; import org.bson.BsonInt32; import org.bson.BsonValue; +import org.joml.Vector3i; @Deprecated public class Vector3iArrayCodec implements Codec { @@ -25,9 +25,9 @@ public class Vector3iArrayCodec implements Codec { @Nonnull public BsonValue encode(@Nonnull Vector3i t, ExtraInfo extraInfo) { BsonArray array = new BsonArray(); - array.add((BsonValue)(new BsonInt32(t.getX()))); - array.add((BsonValue)(new BsonInt32(t.getY()))); - array.add((BsonValue)(new BsonInt32(t.getZ()))); + array.add((BsonValue)(new BsonInt32(t.x()))); + array.add((BsonValue)(new BsonInt32(t.y()))); + array.add((BsonValue)(new BsonInt32(t.z()))); return array; } diff --git a/src/com/hypixel/hytale/math/data/Int3ObjectOpenHashMap.java b/src/com/hypixel/hytale/math/data/Int3ObjectOpenHashMap.java new file mode 100644 index 00000000..f741fc62 --- /dev/null +++ b/src/com/hypixel/hytale/math/data/Int3ObjectOpenHashMap.java @@ -0,0 +1,234 @@ +package com.hypixel.hytale.math.data; + +import com.hypixel.hytale.function.consumer.TriIntObjectConsumer; +import java.util.Arrays; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class Int3ObjectOpenHashMap { + private static final int DEFAULT_CAPACITY = 16; + private static final float DEFAULT_LOAD_FACTOR = 0.75F; + private static final long PHI = -7046029254386353131L; + private int[] keys; + private Object[] values; + private int mask; + private int shift; + private int size; + private int maxFill; + private final float loadFactor; + + public Int3ObjectOpenHashMap() { + this(16); + } + + public Int3ObjectOpenHashMap(int expectedSize) { + this(expectedSize, 0.75F); + } + + public Int3ObjectOpenHashMap(int expectedSize, float loadFactor) { + this.loadFactor = loadFactor; + int capacity = arraySize(expectedSize, loadFactor); + this.keys = new int[capacity * 3]; + this.values = new Object[capacity]; + this.mask = capacity - 1; + this.shift = 64 - Integer.numberOfTrailingZeros(capacity); + this.maxFill = (int)(capacity * loadFactor); + } + + private static long mix(int x, int y, int z) { + long h = x * 7640891576956012808L ^ y * -4942790177534073029L ^ z * 4354685564936845355L; + h ^= h >>> 33; + h *= -49064778989728563L; + h ^= h >>> 33; + h *= -4265267296055464877L; + return h ^ h >>> 33; + } + + private int fibIndex(long hash) { + return (int)(hash * -7046029254386353131L >>> this.shift); + } + + @Nullable + public V get(int x, int y, int z) { + int[] k = this.keys; + int pos = this.fibIndex(mix(x, y, z)); + + while (true) { + Object v = this.values[pos]; + if (v == null) { + return null; + } + + int base = pos * 3; + if (k[base] == x && k[base + 1] == y && k[base + 2] == z) { + return (V)v; + } + + pos = pos + 1 & this.mask; + } + } + + @Nullable + public V put(int x, int y, int z, @Nonnull V value) { + Objects.requireNonNull(value, "value"); + int[] k = this.keys; + int pos = this.fibIndex(mix(x, y, z)); + + while (true) { + Object existing = this.values[pos]; + if (existing == null) { + int base = pos * 3; + k[base] = x; + k[base + 1] = y; + k[base + 2] = z; + this.values[pos] = value; + if (++this.size > this.maxFill) { + this.rehash(this.values.length << 1); + } + + return null; + } + + int base = pos * 3; + if (k[base] == x && k[base + 1] == y && k[base + 2] == z) { + this.values[pos] = value; + return (V)existing; + } + + pos = pos + 1 & this.mask; + } + } + + @Nullable + public V remove(int x, int y, int z) { + int[] k = this.keys; + int pos = this.fibIndex(mix(x, y, z)); + + while (true) { + Object existing = this.values[pos]; + if (existing == null) { + return null; + } + + int base = pos * 3; + if (k[base] == x && k[base + 1] == y && k[base + 2] == z) { + this.size--; + this.shiftKeys(pos); + int capacity = this.values.length; + if (capacity > 16 && this.size < this.maxFill >>> 2) { + this.rehash(capacity >>> 1); + } + + return (V)existing; + } + + pos = pos + 1 & this.mask; + } + } + + public boolean containsKey(int x, int y, int z) { + int[] k = this.keys; + + for (int pos = this.fibIndex(mix(x, y, z)); this.values[pos] != null; pos = pos + 1 & this.mask) { + int base = pos * 3; + if (k[base] == x && k[base + 1] == y && k[base + 2] == z) { + return true; + } + } + + return false; + } + + public int size() { + return this.size; + } + + public boolean isEmpty() { + return this.size == 0; + } + + public void clear() { + this.size = 0; + Arrays.fill(this.values, null); + } + + public void forEach(@Nonnull TriIntObjectConsumer consumer) { + int[] k = this.keys; + + for (int i = 0; i < this.values.length; i++) { + Object v = this.values[i]; + if (v != null) { + int base = i * 3; + consumer.accept(k[base], k[base + 1], k[base + 2], (V)v); + } + } + } + + private void shiftKeys(int pos) { + int[] k = this.keys; + + label30: + while (true) { + int last = pos; + + for (pos = pos + 1 & this.mask; this.values[pos] != null; pos = pos + 1 & this.mask) { + int base = pos * 3; + int slot = this.fibIndex(mix(k[base], k[base + 1], k[base + 2])); + if (last <= pos ? last >= slot || slot > pos : last >= slot && slot > pos) { + base = pos * 3; + slot = last * 3; + k[slot] = k[base]; + k[slot + 1] = k[base + 1]; + k[slot + 2] = k[base + 2]; + this.values[last] = this.values[pos]; + continue label30; + } + } + + this.values[last] = null; + return; + } + } + + private void rehash(int newCapacity) { + int[] oldKeys = this.keys; + Object[] oldValues = this.values; + this.keys = new int[newCapacity * 3]; + this.values = new Object[newCapacity]; + this.mask = newCapacity - 1; + this.shift = 64 - Integer.numberOfTrailingZeros(newCapacity); + this.maxFill = (int)(newCapacity * this.loadFactor); + int[] k = this.keys; + + for (int i = 0; i < oldValues.length; i++) { + if (oldValues[i] != null) { + int srcBase = i * 3; + int ox = oldKeys[srcBase]; + int oy = oldKeys[srcBase + 1]; + int oz = oldKeys[srcBase + 2]; + int pos = this.fibIndex(mix(ox, oy, oz)); + + while (this.values[pos] != null) { + pos = pos + 1 & this.mask; + } + + int dstBase = pos * 3; + k[dstBase] = ox; + k[dstBase + 1] = oy; + k[dstBase + 2] = oz; + this.values[pos] = oldValues[i]; + } + } + } + + private static int arraySize(int expected, float loadFactor) { + if (expected <= 0) { + return 16; + } else { + int needed = (int)Math.ceil(expected / loadFactor); + int capacity = Integer.highestOneBit(needed - 1) << 1; + return Math.max(capacity, 16); + } + } +} diff --git a/src/com/hypixel/hytale/math/hitdetection/HitDetectionBuffer.java b/src/com/hypixel/hytale/math/hitdetection/HitDetectionBuffer.java index 320cf533..076805ef 100644 --- a/src/com/hypixel/hytale/math/hitdetection/HitDetectionBuffer.java +++ b/src/com/hypixel/hytale/math/hitdetection/HitDetectionBuffer.java @@ -3,16 +3,16 @@ package com.hypixel.hytale.math.hitdetection; import com.hypixel.hytale.math.shape.Quad4d; import com.hypixel.hytale.math.shape.Triangle4d; import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector4d; import java.util.Random; +import org.joml.Vector4d; public class HitDetectionBuffer { private static final int VECTOR_BUFFER_SIZE = 16; public Random random = new FastRandom(); - public Vector4d hitPosition = new Vector4d(); - public Vector4d tempHitPosition = new Vector4d(); + public Vector4d hitPosition = new Vector4d().zero(); + public Vector4d tempHitPosition = new Vector4d().zero(); public Quad4d transformedQuad; - public Vector4d transformedPoint = new Vector4d(); + public Vector4d transformedPoint = new Vector4d().zero(); public Triangle4d visibleTriangle; public Vector4dBufferList vertexList1; public Vector4dBufferList vertexList2; diff --git a/src/com/hypixel/hytale/math/hitdetection/HitDetectionExecutor.java b/src/com/hypixel/hytale/math/hitdetection/HitDetectionExecutor.java index 651aa8d4..4c159f88 100644 --- a/src/com/hypixel/hytale/math/hitdetection/HitDetectionExecutor.java +++ b/src/com/hypixel/hytale/math/hitdetection/HitDetectionExecutor.java @@ -1,25 +1,26 @@ package com.hypixel.hytale.math.hitdetection; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.matrix.Matrix4d; import com.hypixel.hytale.math.shape.Quad4d; import com.hypixel.hytale.math.shape.Triangle4d; -import com.hypixel.hytale.math.vector.Vector4d; +import com.hypixel.hytale.math.vector.Vector4dUtil; import java.util.Arrays; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Matrix4d; +import org.joml.Vector4d; public class HitDetectionExecutor { public static final HytaleLogger log = HytaleLogger.forEnclosingClass(); private static final Vector4d[] VERTEX_POINTS = new Vector4d[]{ - Vector4d.newPosition(0.0, 1.0, 1.0), - Vector4d.newPosition(0.0, 1.0, 0.0), - Vector4d.newPosition(1.0, 1.0, 1.0), - Vector4d.newPosition(1.0, 1.0, 0.0), - Vector4d.newPosition(0.0, 0.0, 1.0), - Vector4d.newPosition(0.0, 0.0, 0.0), - Vector4d.newPosition(1.0, 0.0, 1.0), - Vector4d.newPosition(1.0, 0.0, 0.0) + new Vector4d(0.0, 1.0, 1.0, 1.0), + new Vector4d(0.0, 1.0, 0.0, 1.0), + new Vector4d(1.0, 1.0, 1.0, 1.0), + new Vector4d(1.0, 1.0, 0.0, 1.0), + new Vector4d(0.0, 0.0, 1.0, 1.0), + new Vector4d(0.0, 0.0, 0.0, 1.0), + new Vector4d(1.0, 0.0, 1.0, 1.0), + new Vector4d(1.0, 0.0, 0.0, 1.0) }; public static final Quad4d[] CUBE_QUADS = new Quad4d[]{ new Quad4d(VERTEX_POINTS, 0, 1, 3, 2), @@ -34,7 +35,7 @@ public class HitDetectionExecutor { @Nonnull private final Matrix4d invPvMatrix = new Matrix4d(); @Nonnull - private final Vector4d origin = new Vector4d(); + private final Vector4d origin = new Vector4d().zero(); @Nonnull private final HitDetectionBuffer buffer = new HitDetectionBuffer(); private MatrixProvider projectionProvider; @@ -72,16 +73,16 @@ public class HitDetectionExecutor { @Nonnull public HitDetectionExecutor setOrigin(double x, double y, double z) { - this.origin.assign(x, y, z, 1.0); + this.origin.set(x, y, z, 1.0); return this; } private void setupMatrices(@Nonnull Matrix4d modelMatrix) { Matrix4d projectionMatrix = this.projectionProvider.getMatrix(); Matrix4d viewMatrix = this.viewProvider.getMatrix(); - this.pvmMatrix.assign(projectionMatrix).multiply(viewMatrix); - this.invPvMatrix.assign(this.pvmMatrix).invert(); - this.pvmMatrix.multiply(modelMatrix); + this.pvmMatrix.set(projectionMatrix).mul(viewMatrix); + this.invPvMatrix.set(this.pvmMatrix).invert(); + this.pvmMatrix.mul(modelMatrix); } public boolean test(@Nonnull Vector4d point, @Nonnull Matrix4d modelMatrix) { @@ -104,13 +105,13 @@ public class HitDetectionExecutor { } private boolean testPoint(@Nonnull Vector4d point) { - this.pvmMatrix.multiply(point, this.buffer.transformedPoint); - if (!this.buffer.transformedPoint.isInsideFrustum()) { + this.pvmMatrix.transform(point, this.buffer.transformedPoint); + if (!Vector4dUtil.isInsideFrustum(this.buffer.transformedPoint)) { return false; } else { Vector4d hit = this.buffer.transformedPoint; - this.invPvMatrix.multiply(hit); - hit.perspectiveTransform(); + this.invPvMatrix.transform(hit); + Vector4dUtil.perspectiveTransform(hit); return this.losProvider.test(this.origin.x, this.origin.y, this.origin.z, hit.x, hit.y, hit.z); } } @@ -133,15 +134,15 @@ public class HitDetectionExecutor { this.buffer.visibleTriangle.getRandom(this.buffer.random, hit); } - this.invPvMatrix.multiply(hit); - hit.perspectiveTransform(); + this.invPvMatrix.transform(hit); + Vector4dUtil.perspectiveTransform(hit); double dx = this.origin.x - hit.x; double dy = this.origin.y - hit.y; double dz = this.origin.z - hit.z; double distanceSquared = dx * dx + dy * dy + dz * dz; if (!(distanceSquared >= minDistanceSquared) && this.losProvider.test(this.origin.x, this.origin.y, this.origin.z, hit.x, hit.y, hit.z)) { minDistanceSquared = distanceSquared; - this.buffer.hitPosition.assign(hit); + this.buffer.hitPosition.set(hit); } } } @@ -160,10 +161,10 @@ public class HitDetectionExecutor { Vector4dBufferList auxillaryList = this.buffer.vertexList2; vertices.clear(); auxillaryList.clear(); - vertices.next().assign(quad.getA()); - vertices.next().assign(quad.getB()); - vertices.next().assign(quad.getC()); - vertices.next().assign(quad.getD()); + vertices.next().set(quad.getA()); + vertices.next().set(quad.getB()); + vertices.next().set(quad.getC()); + vertices.next().set(quad.getD()); if (this.clipPolygonAxis(0) && this.clipPolygonAxis(1) && this.clipPolygonAxis(2)) { Vector4d initialVertex = vertices.get(0); int i = 1; @@ -207,7 +208,7 @@ public class HitDetectionExecutor { } if (inside) { - result.next().assign(vertex); + result.next().set(vertex); } previousVertex = vertex; diff --git a/src/com/hypixel/hytale/math/hitdetection/MatrixProvider.java b/src/com/hypixel/hytale/math/hitdetection/MatrixProvider.java index f4482ec4..54445b0c 100644 --- a/src/com/hypixel/hytale/math/hitdetection/MatrixProvider.java +++ b/src/com/hypixel/hytale/math/hitdetection/MatrixProvider.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.math.hitdetection; -import com.hypixel.hytale.math.matrix.Matrix4d; +import org.joml.Matrix4d; public interface MatrixProvider { Matrix4d getMatrix(); diff --git a/src/com/hypixel/hytale/math/hitdetection/Vector4dBufferList.java b/src/com/hypixel/hytale/math/hitdetection/Vector4dBufferList.java index 14a41863..6c748b1a 100644 --- a/src/com/hypixel/hytale/math/hitdetection/Vector4dBufferList.java +++ b/src/com/hypixel/hytale/math/hitdetection/Vector4dBufferList.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.math.hitdetection; -import com.hypixel.hytale.math.vector.Vector4d; import javax.annotation.Nonnull; +import org.joml.Vector4d; public class Vector4dBufferList { private Vector4d[] vectors; @@ -11,7 +11,7 @@ public class Vector4dBufferList { this.vectors = new Vector4d[size]; for (int i = 0; i < size; i++) { - this.vectors[i] = new Vector4d(); + this.vectors[i] = new Vector4d().zero(); } this.size = 0; diff --git a/src/com/hypixel/hytale/math/hitdetection/projection/FrustumProjectionProvider.java b/src/com/hypixel/hytale/math/hitdetection/projection/FrustumProjectionProvider.java index d919a051..fe6a3417 100644 --- a/src/com/hypixel/hytale/math/hitdetection/projection/FrustumProjectionProvider.java +++ b/src/com/hypixel/hytale/math/hitdetection/projection/FrustumProjectionProvider.java @@ -4,8 +4,8 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.math.hitdetection.MatrixProvider; -import com.hypixel.hytale.math.matrix.Matrix4d; import javax.annotation.Nonnull; +import org.joml.Matrix4d; public class FrustumProjectionProvider implements MatrixProvider { public static final BuilderCodec CODEC = BuilderCodec.builder(FrustumProjectionProvider.class, FrustumProjectionProvider::new) @@ -101,10 +101,10 @@ public class FrustumProjectionProvider implements MatrixProvider { @Override public Matrix4d getMatrix() { if (this.invalid) { - this.matrix.projectionFrustum(this.left, this.right, this.bottom, this.top, this.near, this.far); - this.matrix.rotateAxis(this.pitch, 1.0, 0.0, 0.0, this.rotMatrix); - this.matrix.rotateAxis(this.yaw, 0.0, 1.0, 0.0, this.rotMatrix); - this.matrix.rotateAxis(this.roll, 0.0, 0.0, 1.0, this.rotMatrix); + this.matrix.setFrustum(-this.left, this.right, -this.bottom, this.top, this.near, this.far); + this.matrix.rotate(-this.pitch, 1.0, 0.0, 0.0); + this.matrix.rotate(-this.yaw, 0.0, 1.0, 0.0); + this.matrix.rotate(-this.roll, 0.0, 0.0, 1.0); this.invalid = false; } diff --git a/src/com/hypixel/hytale/math/hitdetection/projection/OrthogonalProjectionProvider.java b/src/com/hypixel/hytale/math/hitdetection/projection/OrthogonalProjectionProvider.java index c626ce2f..5ce3536c 100644 --- a/src/com/hypixel/hytale/math/hitdetection/projection/OrthogonalProjectionProvider.java +++ b/src/com/hypixel/hytale/math/hitdetection/projection/OrthogonalProjectionProvider.java @@ -4,8 +4,8 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.math.hitdetection.MatrixProvider; -import com.hypixel.hytale.math.matrix.Matrix4d; import javax.annotation.Nonnull; +import org.joml.Matrix4d; public class OrthogonalProjectionProvider implements MatrixProvider { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -112,10 +112,10 @@ public class OrthogonalProjectionProvider implements MatrixProvider { @Override public Matrix4d getMatrix() { if (this.invalid) { - this.matrix.projectionOrtho(this.left, this.right, this.bottom, this.top, this.near, this.far); - this.matrix.rotateAxis(this.roll, 0.0, 0.0, 1.0, this.rotMatrix); - this.matrix.rotateAxis(this.pitch, 1.0, 0.0, 0.0, this.rotMatrix); - this.matrix.rotateAxis(this.yaw, 0.0, 1.0, 0.0, this.rotMatrix); + this.matrix.setOrtho(-this.left, this.right, -this.bottom, this.top, this.near, this.far); + this.matrix.rotate(-this.roll, 0.0, 0.0, 1.0); + this.matrix.rotate(-this.pitch, 1.0, 0.0, 0.0); + this.matrix.rotate(-this.yaw, 0.0, 1.0, 0.0); this.invalid = false; } diff --git a/src/com/hypixel/hytale/math/hitdetection/view/DirectionViewProvider.java b/src/com/hypixel/hytale/math/hitdetection/view/DirectionViewProvider.java index a4c7a1e4..074bd8b1 100644 --- a/src/com/hypixel/hytale/math/hitdetection/view/DirectionViewProvider.java +++ b/src/com/hypixel/hytale/math/hitdetection/view/DirectionViewProvider.java @@ -4,9 +4,10 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.math.hitdetection.MatrixProvider; -import com.hypixel.hytale.math.matrix.Matrix4d; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Matrix4d; +import org.joml.Vector3d; public class DirectionViewProvider implements MatrixProvider { public static final BuilderCodec CODEC = BuilderCodec.builder(DirectionViewProvider.class, DirectionViewProvider::new) @@ -23,7 +24,7 @@ public class DirectionViewProvider implements MatrixProvider { ) .add() .append( - new KeyedCodec<>("Up", Vector3d.CODEC), (projectionProvider, vec) -> projectionProvider.up.assign(vec), projectionProvider -> projectionProvider.up + new KeyedCodec<>("Up", Vector3dUtil.CODEC), (projectionProvider, vec) -> projectionProvider.up.set(vec), projectionProvider -> projectionProvider.up ) .add() .build(); @@ -66,7 +67,7 @@ public class DirectionViewProvider implements MatrixProvider { @Nonnull public DirectionViewProvider setPosition(double x, double y, double z) { - this.position.assign(x, y, z); + this.position.set(x, y, z); this.invalid = true; return this; } @@ -89,21 +90,21 @@ public class DirectionViewProvider implements MatrixProvider { public DirectionViewProvider setDirection(double yaw, double pitch) { yaw += this.yawOffset; pitch += this.pitchOffset; - this.direction.assign(yaw, pitch); + Vector3dUtil.setYawPitch(yaw, pitch, this.direction); this.invalid = true; return this; } @Nonnull public DirectionViewProvider setDirection(double x, double y, double z) { - this.direction.assign(x, y, z); + this.direction.set(x, y, z); this.invalid = true; return this; } @Nonnull public DirectionViewProvider setUp(double x, double y, double z) { - this.up.assign(x, y, z); + this.up.set(x, y, z); this.invalid = true; return this; } @@ -112,9 +113,8 @@ public class DirectionViewProvider implements MatrixProvider { public Matrix4d getMatrix() { if (this.invalid) { this.matrix - .viewDirection( - this.position.x, this.position.y, this.position.z, this.direction.x, this.direction.y, this.direction.z, this.up.x, this.up.y, this.up.z - ); + .setLookAlong(this.direction.x, this.direction.y, this.direction.z, this.up.x, this.up.y, this.up.z) + .translate(-this.position.x, -this.position.y, -this.position.z); this.invalid = false; } diff --git a/src/com/hypixel/hytale/math/iterator/BlockIterator.java b/src/com/hypixel/hytale/math/iterator/BlockIterator.java index 6554dc8f..5961e34f 100644 --- a/src/com/hypixel/hytale/math/iterator/BlockIterator.java +++ b/src/com/hypixel/hytale/math/iterator/BlockIterator.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.math.iterator; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public final class BlockIterator { private BlockIterator() { diff --git a/src/com/hypixel/hytale/math/iterator/BoxBlockIterator.java b/src/com/hypixel/hytale/math/iterator/BoxBlockIterator.java index 21ba8c5b..fd932302 100644 --- a/src/com/hypixel/hytale/math/iterator/BoxBlockIterator.java +++ b/src/com/hypixel/hytale/math/iterator/BoxBlockIterator.java @@ -2,8 +2,8 @@ package com.hypixel.hytale.math.iterator; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public final class BoxBlockIterator { @Nonnull diff --git a/src/com/hypixel/hytale/math/iterator/CircleIterator.java b/src/com/hypixel/hytale/math/iterator/CircleIterator.java index e6cb75e2..a2af116b 100644 --- a/src/com/hypixel/hytale/math/iterator/CircleIterator.java +++ b/src/com/hypixel/hytale/math/iterator/CircleIterator.java @@ -1,22 +1,23 @@ package com.hypixel.hytale.math.iterator; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import java.util.Iterator; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class CircleIterator implements Iterator { - private final Vector3d origin; + private final Vector3dc origin; private final int pointTotal; private final double radius; private final float angleOffset; private int pointIndex; - public CircleIterator(Vector3d origin, double radius, int pointTotal) { + public CircleIterator(Vector3dc origin, double radius, int pointTotal) { this(origin, radius, pointTotal, 0.0F); } - public CircleIterator(Vector3d origin, double radius, int pointTotal, float angleOffset) { + public CircleIterator(Vector3dc origin, double radius, int pointTotal, float angleOffset) { this.origin = origin; this.pointTotal = pointTotal; this.angleOffset = angleOffset; @@ -33,8 +34,6 @@ public class CircleIterator implements Iterator { public Vector3d next() { this.pointIndex++; float angle = (float)this.pointIndex / this.pointTotal * (float) (Math.PI * 2) + this.angleOffset; - return new Vector3d( - TrigMathUtil.cos(angle) * this.radius + this.origin.getX(), this.origin.getY(), TrigMathUtil.sin(angle) * this.radius + this.origin.getZ() - ); + return new Vector3d(TrigMathUtil.cos(angle) * this.radius + this.origin.x(), this.origin.y(), TrigMathUtil.sin(angle) * this.radius + this.origin.z()); } } diff --git a/src/com/hypixel/hytale/math/iterator/LineIterator.java b/src/com/hypixel/hytale/math/iterator/LineIterator.java index 794d78b3..b0210009 100644 --- a/src/com/hypixel/hytale/math/iterator/LineIterator.java +++ b/src/com/hypixel/hytale/math/iterator/LineIterator.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.math.iterator; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.Iterator; import java.util.NoSuchElementException; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class LineIterator implements Iterator { private final int x_inc; diff --git a/src/com/hypixel/hytale/math/matrix/Matrix4d.java b/src/com/hypixel/hytale/math/matrix/Matrix4d.java deleted file mode 100644 index ced4be42..00000000 --- a/src/com/hypixel/hytale/math/matrix/Matrix4d.java +++ /dev/null @@ -1,542 +0,0 @@ -package com.hypixel.hytale.math.matrix; - -import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector4d; -import java.util.Arrays; -import javax.annotation.Nonnull; - -public class Matrix4d { - public static final int M00 = 0; - public static final int M10 = 4; - public static final int M20 = 8; - public static final int M30 = 12; - public static final int M01 = 1; - public static final int M11 = 5; - public static final int M21 = 9; - public static final int M31 = 13; - public static final int M02 = 2; - public static final int M12 = 6; - public static final int M22 = 10; - public static final int M32 = 14; - public static final int M03 = 3; - public static final int M13 = 7; - public static final int M23 = 11; - public static final int M33 = 15; - public static final int COLUMNS = 4; - public static final int ROWS = 4; - public static final int FIELDS = 16; - private final double[] m; - - public Matrix4d() { - this(new double[16]); - } - - public Matrix4d(@Nonnull Matrix4d other) { - this(); - this.assign(other); - } - - public Matrix4d(double[] m) { - this.m = m; - } - - public double get(int idx) { - return this.m[idx]; - } - - public double get(int col, int row) { - return this.get(idx(col, row)); - } - - @Nonnull - public Matrix4d set(int idx, double val) { - this.m[idx] = val; - return this; - } - - @Nonnull - public Matrix4d set(int col, int row, double val) { - return this.set(idx(col, row), val); - } - - @Nonnull - public Matrix4d add(int idx, double val) { - this.m[idx] = this.m[idx] + val; - return this; - } - - @Nonnull - public Matrix4d add(int col, int row, double val) { - return this.set(idx(col, row), val); - } - - @Nonnull - public Matrix4d identity() { - Arrays.fill(this.m, 0.0); - - for (int i = 0; i < 16; i += 5) { - this.m[i] = 1.0; - } - - return this; - } - - @Nonnull - public Matrix4d assign(@Nonnull Matrix4d other) { - System.arraycopy(other.m, 0, this.m, 0, 16); - return this; - } - - @Nonnull - public Matrix4d assign( - double m00, - double m10, - double m20, - double m30, - double m01, - double m11, - double m21, - double m31, - double m02, - double m12, - double m22, - double m32, - double m03, - double m13, - double m23, - double m33 - ) { - this.m[0] = m00; - this.m[1] = m01; - this.m[2] = m02; - this.m[3] = m03; - this.m[4] = m10; - this.m[5] = m11; - this.m[6] = m12; - this.m[7] = m13; - this.m[8] = m20; - this.m[9] = m21; - this.m[10] = m22; - this.m[11] = m23; - this.m[12] = m30; - this.m[13] = m31; - this.m[14] = m32; - this.m[15] = m33; - return this; - } - - @Nonnull - public Matrix4d translate(@Nonnull Vector3d vec) { - return this.translate(vec.x, vec.y, vec.z); - } - - @Nonnull - public Matrix4d translate(double x, double y, double z) { - for (int i = 0; i < 4; i++) { - this.m[i + 12] = this.m[i + 12] + (this.m[i] * x + this.m[i + 4] * y + this.m[i + 8] * z); - } - - return this; - } - - @Nonnull - public Matrix4d scale(double x, double y, double z) { - for (int i = 0; i < 4; i++) { - this.m[i] = this.m[i] * x; - this.m[i + 4] = this.m[i + 4] * y; - this.m[i + 8] = this.m[i + 8] * z; - } - - return this; - } - - @Nonnull - public Vector3d multiplyPosition(@Nonnull Vector3d vec) { - return this.multiplyPosition(vec, vec); - } - - @Nonnull - public Vector3d multiplyPosition(@Nonnull Vector3d vec, @Nonnull Vector3d result) { - double x = this.m[0] * vec.x + this.m[4] * vec.y + this.m[8] * vec.z + this.m[12]; - double y = this.m[1] * vec.x + this.m[5] * vec.y + this.m[9] * vec.z + this.m[13]; - double z = this.m[2] * vec.x + this.m[6] * vec.y + this.m[10] * vec.z + this.m[14]; - double w = this.m[3] * vec.x + this.m[7] * vec.y + this.m[11] * vec.z + this.m[15]; - double invW = 1.0 / w; - result.assign(x * invW, y * invW, z * invW); - return result; - } - - @Nonnull - public Vector3d multiplyDirection(@Nonnull Vector3d vec) { - double x = this.m[0] * vec.x + this.m[4] * vec.y + this.m[8] * vec.z; - double y = this.m[1] * vec.x + this.m[5] * vec.y + this.m[9] * vec.z; - double z = this.m[2] * vec.x + this.m[6] * vec.y + this.m[10] * vec.z; - vec.assign(x, y, z); - return vec; - } - - @Nonnull - public Vector4d multiply(@Nonnull Vector4d vec) { - return this.multiply(vec, vec); - } - - @Nonnull - public Vector4d multiply(@Nonnull Vector4d vec, @Nonnull Vector4d result) { - double x = this.m[0] * vec.x + this.m[4] * vec.y + this.m[8] * vec.z + this.m[12] * vec.w; - double y = this.m[1] * vec.x + this.m[5] * vec.y + this.m[9] * vec.z + this.m[13] * vec.w; - double z = this.m[2] * vec.x + this.m[6] * vec.y + this.m[10] * vec.z + this.m[14] * vec.w; - double w = this.m[3] * vec.x + this.m[7] * vec.y + this.m[11] * vec.z + this.m[15] * vec.w; - result.assign(x, y, z, w); - return result; - } - - @Nonnull - public Matrix4d multiply(@Nonnull Matrix4d other) { - double a00 = this.m[0]; - double a01 = this.m[1]; - double a02 = this.m[2]; - double a03 = this.m[3]; - double a10 = this.m[4]; - double a11 = this.m[5]; - double a12 = this.m[6]; - double a13 = this.m[7]; - double a20 = this.m[8]; - double a21 = this.m[9]; - double a22 = this.m[10]; - double a23 = this.m[11]; - double a30 = this.m[12]; - double a31 = this.m[13]; - double a32 = this.m[14]; - double a33 = this.m[15]; - double b00 = other.m[0]; - double b01 = other.m[1]; - double b02 = other.m[2]; - double b03 = other.m[3]; - double b10 = other.m[4]; - double b11 = other.m[5]; - double b12 = other.m[6]; - double b13 = other.m[7]; - double b20 = other.m[8]; - double b21 = other.m[9]; - double b22 = other.m[10]; - double b23 = other.m[11]; - double b30 = other.m[12]; - double b31 = other.m[13]; - double b32 = other.m[14]; - double b33 = other.m[15]; - this.m[0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03; - this.m[1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03; - this.m[2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03; - this.m[3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03; - this.m[4] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13; - this.m[5] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13; - this.m[6] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13; - this.m[7] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13; - this.m[8] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23; - this.m[9] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23; - this.m[10] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23; - this.m[11] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23; - this.m[12] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33; - this.m[13] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33; - this.m[14] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33; - this.m[15] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33; - return this; - } - - public boolean invert() { - double src0 = this.m[0]; - double src4 = this.m[1]; - double src8 = this.m[2]; - double src12 = this.m[3]; - double src1 = this.m[4]; - double src5 = this.m[5]; - double src9 = this.m[6]; - double src13 = this.m[7]; - double src2 = this.m[8]; - double src6 = this.m[9]; - double src10 = this.m[10]; - double src14 = this.m[11]; - double src3 = this.m[12]; - double src7 = this.m[13]; - double src11 = this.m[14]; - double src15 = this.m[15]; - double atmp0 = src10 * src15; - double atmp1 = src11 * src14; - double atmp2 = src9 * src15; - double atmp3 = src11 * src13; - double atmp4 = src9 * src14; - double atmp5 = src10 * src13; - double atmp6 = src8 * src15; - double atmp7 = src11 * src12; - double atmp8 = src8 * src14; - double atmp9 = src10 * src12; - double atmp10 = src8 * src13; - double atmp11 = src9 * src12; - double dst0 = atmp0 * src5 + atmp3 * src6 + atmp4 * src7 - (atmp1 * src5 + atmp2 * src6 + atmp5 * src7); - double dst1 = atmp1 * src4 + atmp6 * src6 + atmp9 * src7 - (atmp0 * src4 + atmp7 * src6 + atmp8 * src7); - double dst2 = atmp2 * src4 + atmp7 * src5 + atmp10 * src7 - (atmp3 * src4 + atmp6 * src5 + atmp11 * src7); - double dst3 = atmp5 * src4 + atmp8 * src5 + atmp11 * src6 - (atmp4 * src4 + atmp9 * src5 + atmp10 * src6); - double dst4 = atmp1 * src1 + atmp2 * src2 + atmp5 * src3 - (atmp0 * src1 + atmp3 * src2 + atmp4 * src3); - double dst5 = atmp0 * src0 + atmp7 * src2 + atmp8 * src3 - (atmp1 * src0 + atmp6 * src2 + atmp9 * src3); - double dst6 = atmp3 * src0 + atmp6 * src1 + atmp11 * src3 - (atmp2 * src0 + atmp7 * src1 + atmp10 * src3); - double dst7 = atmp4 * src0 + atmp9 * src1 + atmp10 * src2 - (atmp5 * src0 + atmp8 * src1 + atmp11 * src2); - double btmp0 = src2 * src7; - double btmp1 = src3 * src6; - double btmp2 = src1 * src7; - double btmp3 = src3 * src5; - double btmp4 = src1 * src6; - double btmp5 = src2 * src5; - double btmp6 = src0 * src7; - double btmp7 = src3 * src4; - double btmp8 = src0 * src6; - double btmp9 = src2 * src4; - double btmp10 = src0 * src5; - double btmp11 = src1 * src4; - double dst8 = btmp0 * src13 + btmp3 * src14 + btmp4 * src15 - (btmp1 * src13 + btmp2 * src14 + btmp5 * src15); - double dst9 = btmp1 * src12 + btmp6 * src14 + btmp9 * src15 - (btmp0 * src12 + btmp7 * src14 + btmp8 * src15); - double dst10 = btmp2 * src12 + btmp7 * src13 + btmp10 * src15 - (btmp3 * src12 + btmp6 * src13 + btmp11 * src15); - double dst11 = btmp5 * src12 + btmp8 * src13 + btmp11 * src14 - (btmp4 * src12 + btmp9 * src13 + btmp10 * src14); - double dst12 = btmp2 * src10 + btmp5 * src11 + btmp1 * src9 - (btmp4 * src11 + btmp0 * src9 + btmp3 * src10); - double dst13 = btmp8 * src11 + btmp0 * src8 + btmp7 * src10 - (btmp6 * src10 + btmp9 * src11 + btmp1 * src8); - double dst14 = btmp6 * src9 + btmp11 * src11 + btmp3 * src8 - (btmp10 * src11 + btmp2 * src8 + btmp7 * src9); - double dst15 = btmp10 * src10 + btmp4 * src8 + btmp9 * src9 - (btmp8 * src9 + btmp11 * src10 + btmp5 * src8); - double det = src0 * dst0 + src1 * dst1 + src2 * dst2 + src3 * dst3; - if (det == 0.0) { - return false; - } else { - double invdet = 1.0 / det; - this.m[0] = dst0 * invdet; - this.m[1] = dst1 * invdet; - this.m[2] = dst2 * invdet; - this.m[3] = dst3 * invdet; - this.m[4] = dst4 * invdet; - this.m[5] = dst5 * invdet; - this.m[6] = dst6 * invdet; - this.m[7] = dst7 * invdet; - this.m[8] = dst8 * invdet; - this.m[9] = dst9 * invdet; - this.m[10] = dst10 * invdet; - this.m[11] = dst11 * invdet; - this.m[12] = dst12 * invdet; - this.m[13] = dst13 * invdet; - this.m[14] = dst14 * invdet; - this.m[15] = dst15 * invdet; - return true; - } - } - - @Nonnull - public Matrix4d projectionOrtho(double left, double right, double bottom, double top, double near, double far) { - double r_width = 1.0 / (right + left); - double r_height = 1.0 / (top + bottom); - double r_depth = -1.0 / (far - near); - double x = 2.0 * r_width; - double y = 2.0 * r_height; - double z = 2.0 * r_depth; - this.m[1] = this.m[2] = this.m[3] = 0.0; - this.m[4] = this.m[6] = this.m[7] = 0.0; - this.m[8] = this.m[9] = this.m[11] = 0.0; - this.m[15] = 1.0; - this.m[0] = x; - this.m[5] = y; - this.m[10] = z; - this.m[12] = -(right - left) * r_width; - this.m[13] = -(top - bottom) * r_height; - this.m[14] = (far + near) * r_depth; - return this; - } - - @Nonnull - public Matrix4d projectionFrustum(double left, double right, double bottom, double top, double near, double far) { - double r_width = 1.0 / (right + left); - double r_height = 1.0 / (top + bottom); - double r_depth = 1.0 / (near - far); - this.m[1] = this.m[2] = this.m[3] = 0.0; - this.m[4] = this.m[6] = this.m[7] = 0.0; - this.m[12] = this.m[13] = this.m[15] = 0.0; - this.m[11] = -1.0; - this.m[0] = 2.0 * (near * r_width); - this.m[5] = 2.0 * (near * r_height); - this.m[14] = 2.0 * (far * near * r_depth); - this.m[8] = 2.0 * (right - left) * r_width; - this.m[9] = (top - bottom) * r_height; - this.m[10] = (far + near) * r_depth; - return this; - } - - @Nonnull - public Matrix4d projectionCone(double fov, double aspect, double near, double far) { - double f = 1.0 / Math.tan(fov * 0.5); - double r = 1.0 / (near - far); - this.m[0] = f / aspect; - this.m[1] = this.m[2] = this.m[3] = 0.0; - this.m[5] = f; - this.m[4] = this.m[6] = this.m[7] = 0.0; - this.m[8] = this.m[9] = 0.0; - this.m[10] = (far + near) * r; - this.m[11] = -1.0; - this.m[12] = this.m[13] = this.m[15] = 0.0; - this.m[14] = 2.0 * far * near * r; - return this; - } - - @Nonnull - public Matrix4d viewTarget(double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ) { - double dirX = centerX - eyeX; - double dirY = centerY - eyeY; - double dirZ = centerZ - eyeZ; - return this.viewDirection(eyeX, eyeY, eyeZ, dirX, dirY, dirZ, upX, upY, upZ); - } - - @Nonnull - public Matrix4d viewDirection(double eyeX, double eyeY, double eyeZ, double dirX, double dirY, double dirZ, double upX, double upY, double upZ) { - double rlf = 1.0 / MathUtil.length(dirX, dirY, dirZ); - dirX *= rlf; - dirY *= rlf; - dirZ *= rlf; - double sx = dirY * upZ - dirZ * upY; - double sy = dirZ * upX - dirX * upZ; - double sz = dirX * upY - dirY * upX; - double rls = 1.0 / MathUtil.length(sx, sy, sz); - sx *= rls; - sy *= rls; - sz *= rls; - double ux = sy * dirZ - sz * dirY; - double uy = sz * dirX - sx * dirZ; - double uz = sx * dirY - sy * dirX; - this.m[0] = sx; - this.m[1] = ux; - this.m[2] = -dirX; - this.m[3] = 0.0; - this.m[4] = sy; - this.m[5] = uy; - this.m[6] = -dirY; - this.m[7] = 0.0; - this.m[8] = sz; - this.m[9] = uz; - this.m[10] = -dirZ; - this.m[11] = 0.0; - this.m[12] = 0.0; - this.m[13] = 0.0; - this.m[14] = 0.0; - this.m[15] = 1.0; - this.translate(-eyeX, -eyeY, -eyeZ); - return this; - } - - @Nonnull - public Matrix4d rotateAxis(double a, double x, double y, double z, @Nonnull Matrix4d tmp) { - return this.multiply(tmp.setRotateAxis(a, x, y, z)); - } - - @Nonnull - public Matrix4d setRotateAxis(double a, double x, double y, double z) { - double sin = TrigMathUtil.sin(a); - double cos = TrigMathUtil.cos(a); - this.m[0] = cos + x * x * (1.0 - cos); - this.m[1] = x * y * (1.0 - cos) - z * sin; - this.m[2] = x * z * (1.0 - cos) + y * sin; - this.m[3] = 0.0; - this.m[4] = y * x * (1.0 - cos) + z * sin; - this.m[5] = cos + y * y * (1.0 - cos); - this.m[6] = y * z * (1.0 - cos) - x * sin; - this.m[7] = 0.0; - this.m[8] = z * x * (1.0 - cos) - y * sin; - this.m[9] = z * y * (1.0 - cos) + x * sin; - this.m[10] = cos + z * z * (1.0 - cos); - this.m[11] = 0.0; - this.m[12] = 0.0; - this.m[13] = 0.0; - this.m[14] = 0.0; - this.m[15] = 1.0; - return this; - } - - @Nonnull - public Matrix4d rotateEuler(double x, double y, double z, @Nonnull Matrix4d tmp) { - return this.multiply(tmp.setRotateEuler(x, y, z)); - } - - @Nonnull - public Matrix4d setRotateEuler(double x, double y, double z) { - double cx = TrigMathUtil.cos(x); - double sx = TrigMathUtil.sin(x); - double cy = TrigMathUtil.cos(y); - double sy = TrigMathUtil.sin(y); - double cz = TrigMathUtil.cos(z); - double sz = TrigMathUtil.sin(z); - double cxsy = cx * sy; - double sxsy = sx * sy; - this.m[0] = cy * cz; - this.m[1] = -cy * sz; - this.m[2] = sy; - this.m[4] = sxsy * cz + cx * sz; - this.m[5] = -sxsy * sz + cx * cz; - this.m[6] = -sx * cy; - this.m[8] = -cxsy * cz + sx * sz; - this.m[9] = cxsy * sz + sx * cz; - this.m[10] = cx * cy; - this.m[3] = this.m[7] = this.m[11] = 0.0; - this.m[12] = this.m[13] = this.m[14] = 0.0; - this.m[15] = 1.0; - return this; - } - - public double[] getData() { - return this.m; - } - - public float[] asFloatData() { - float[] data = new float[16]; - - for (int i = 0; i < 16; i++) { - data[i] = (float)this.m[i]; - } - - return data; - } - - @Nonnull - @Override - public String toString() { - return "Matrix4d{\n " - + this.m[0] - + " " - + this.m[4] - + " " - + this.m[8] - + " " - + this.m[12] - + "\n " - + this.m[1] - + " " - + this.m[5] - + " " - + this.m[9] - + " " - + this.m[13] - + "\n " - + this.m[2] - + " " - + this.m[6] - + " " - + this.m[10] - + " " - + this.m[14] - + "\n " - + this.m[3] - + " " - + this.m[7] - + " " - + this.m[11] - + " " - + this.m[15] - + "\n}"; - } - - public static int idx(int col, int row) { - return col << 2 | row; - } -} diff --git a/src/com/hypixel/hytale/math/matrix/Matrix4dUtil.java b/src/com/hypixel/hytale/math/matrix/Matrix4dUtil.java new file mode 100644 index 00000000..df5af848 --- /dev/null +++ b/src/com/hypixel/hytale/math/matrix/Matrix4dUtil.java @@ -0,0 +1,29 @@ +package com.hypixel.hytale.math.matrix; + +import org.joml.Matrix4d; + +public final class Matrix4dUtil { + public static float[] asFloatData(Matrix4d m) { + return new float[]{ + (float)m.m00(), + (float)m.m01(), + (float)m.m02(), + (float)m.m03(), + (float)m.m10(), + (float)m.m11(), + (float)m.m12(), + (float)m.m13(), + (float)m.m20(), + (float)m.m21(), + (float)m.m22(), + (float)m.m23(), + (float)m.m30(), + (float)m.m31(), + (float)m.m32(), + (float)m.m33() + }; + } + + private Matrix4dUtil() { + } +} diff --git a/src/com/hypixel/hytale/math/random/RandomExtra.java b/src/com/hypixel/hytale/math/random/RandomExtra.java index d1d4e270..d2c5f64d 100644 --- a/src/com/hypixel/hytale/math/random/RandomExtra.java +++ b/src/com/hypixel/hytale/math/random/RandomExtra.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.math.random; import com.hypixel.hytale.function.function.TriFunction; -import com.hypixel.hytale.math.vector.Vector3d; import java.time.Duration; import java.util.Collection; import java.util.Iterator; @@ -14,6 +13,7 @@ import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public final class RandomExtra { private RandomExtra() { diff --git a/src/com/hypixel/hytale/math/shape/Box.java b/src/com/hypixel/hytale/math/shape/Box.java index 98a513dc..154532b3 100644 --- a/src/com/hypixel/hytale/math/shape/Box.java +++ b/src/com/hypixel/hytale/math/shape/Box.java @@ -7,15 +7,17 @@ import com.hypixel.hytale.function.predicate.TriIntObjPredicate; import com.hypixel.hytale.function.predicate.TriIntPredicate; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; public class Box implements Shape { public static final Codec CODEC = BuilderCodec.builder(Box.class, Box::new) - .append(new KeyedCodec<>("Min", Vector3d.CODEC), (box, v) -> box.min.assign(v), box -> box.min) + .append(new KeyedCodec<>("Min", Vector3dUtil.CODEC), (box, v) -> box.min.set(v), box -> box.min) .add() - .append(new KeyedCodec<>("Max", Vector3d.CODEC), (box, v) -> box.max.assign(v), box -> box.max) + .append(new KeyedCodec<>("Max", Vector3dUtil.CODEC), (box, v) -> box.max.set(v), box -> box.max) .add() .validator((box, results) -> { if (box.width() <= 0.0) { @@ -31,8 +33,8 @@ public class Box implements Shape { } }) .build(); - public static final Box UNIT = new Box(Vector3d.ZERO, Vector3d.ALL_ONES); - public static final Box ZERO = new Box(Vector3d.ZERO, Vector3d.ZERO); + public static final Box UNIT = new Box(Vector3dUtil.ZERO, Vector3dUtil.ALL_ONES); + public static final Box ZERO = new Box(Vector3dUtil.ZERO, Vector3dUtil.ZERO); @Nonnull public final Vector3d min = new Vector3d(); @Nonnull @@ -48,20 +50,20 @@ public class Box implements Shape { public Box(@Nonnull Box box) { this(); - this.min.assign(box.min); - this.max.assign(box.max); + this.min.set(box.min); + this.max.set(box.max); } - public Box(@Nonnull Vector3d min, @Nonnull Vector3d max) { + public Box(@Nonnull Vector3dc min, @Nonnull Vector3dc max) { this(); - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); } public Box(double xMin, double yMin, double zMin, double xMax, double yMax, double zMax) { this(); - this.min.assign(xMin, yMin, zMin); - this.max.assign(xMax, yMax, zMax); + this.min.set(xMin, yMin, zMin); + this.max.set(xMax, yMax, zMax); } public static Box cube(@Nonnull Vector3d min, double side) { @@ -74,22 +76,22 @@ public class Box implements Shape { @Nonnull public Box setMinMax(@Nonnull Vector3d min, @Nonnull Vector3d max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); return this; } @Nonnull public Box setMinMax(@Nonnull double[] min, @Nonnull double[] max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); return this; } @Nonnull public Box setMinMax(@Nonnull float[] min, @Nonnull float[] max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); return this; } @@ -101,8 +103,8 @@ public class Box implements Shape { @Nonnull public Box setMinMax(double min, double max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); return this; } @@ -137,29 +139,29 @@ public class Box implements Shape { @Nonnull public Box assign(@Nonnull Box other) { - this.min.assign(other.min); - this.max.assign(other.max); + this.min.set(other.min); + this.max.set(other.max); return this; } @Nonnull public Box assign(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { - this.min.assign(minX, minY, minZ); - this.max.assign(maxX, maxY, maxZ); + this.min.set(minX, minY, minZ); + this.max.set(maxX, maxY, maxZ); return this; } @Nonnull public Box minkowskiSum(@Nonnull Box bb) { - this.min.subtract(bb.max); - this.max.subtract(bb.min); + this.min.sub(bb.max); + this.max.sub(bb.min); return this; } @Nonnull public Box scale(float scale) { - this.min.scale(scale); - this.max.scale(scale); + this.min.mul(scale); + this.max.mul(scale); return this; } @@ -246,7 +248,7 @@ public class Box implements Shape { @Nonnull public Box extend(double extentX, double extentY, double extentZ) { - this.min.subtract(extentX, extentY, extentZ); + this.min.sub(extentX, extentY, extentZ); this.max.add(extentX, extentY, extentZ); return this; } @@ -308,7 +310,7 @@ public class Box implements Shape { } public boolean isUnitBox() { - return this.min.equals(Vector3d.ZERO) && this.max.equals(Vector3d.ALL_ONES); + return this.min.equals(Vector3dUtil.ZERO) && this.max.equals(Vector3dUtil.ALL_ONES); } public double middleX() { @@ -343,12 +345,12 @@ public class Box implements Shape { @Nonnull @Override public Box getBox(double x, double y, double z) { - return new Box(this.min.getX() + x, this.min.getY() + y, this.min.getZ() + z, this.max.getX() + x, this.max.getY() + y, this.max.getZ() + z); + return new Box(this.min.x() + x, this.min.y() + y, this.min.z() + z, this.max.x() + x, this.max.y() + y, this.max.z() + z); } @Override public boolean containsPosition(double x, double y, double z) { - return x >= this.min.getX() && x <= this.max.getX() && y >= this.min.getY() && y <= this.max.getY() && z >= this.min.getZ() && z <= this.max.getZ(); + return x >= this.min.x() && x <= this.max.x() && y >= this.min.y() && y <= this.max.y() && z >= this.min.z() && z <= this.max.z(); } @Override @@ -357,31 +359,31 @@ public class Box implements Shape { } public boolean containsBlock(int x, int y, int z) { - int minX = MathUtil.floor(this.min.getX()); - int minY = MathUtil.floor(this.min.getY()); - int minZ = MathUtil.floor(this.min.getZ()); - int maxX = MathUtil.ceil(this.max.getX()); - int maxY = MathUtil.ceil(this.max.getY()); - int maxZ = MathUtil.ceil(this.max.getZ()); + int minX = MathUtil.floor(this.min.x()); + int minY = MathUtil.floor(this.min.y()); + int minZ = MathUtil.floor(this.min.z()); + int maxX = MathUtil.ceil(this.max.x()); + int maxY = MathUtil.ceil(this.max.y()); + int maxZ = MathUtil.ceil(this.max.z()); return x >= minX && x < maxX && y >= minY && y < maxY && z >= minZ && z < maxZ; } public boolean containsBlock(@Nonnull Vector3i origin, int x, int y, int z) { - return this.containsBlock(x - origin.getX(), y - origin.getY(), z - origin.getZ()); + return this.containsBlock(x - origin.x(), y - origin.y(), z - origin.z()); } @Override public boolean forEachBlock(double x, double y, double z, double epsilon, @Nonnull TriIntPredicate consumer) { - int minX = MathUtil.floor(x + this.min.getX() - epsilon); - int minY = MathUtil.floor(y + this.min.getY() - epsilon); - int minZ = MathUtil.floor(z + this.min.getZ() - epsilon); - int maxX = MathUtil.floor(x + this.max.getX() + epsilon); - int maxY = MathUtil.floor(y + this.max.getY() + epsilon); - int maxZ = MathUtil.floor(z + this.max.getZ() + epsilon); + int minX = MathUtil.floor(x + this.min.x() - epsilon); + int minY = MathUtil.floor(y + this.min.y() - epsilon); + int minZ = MathUtil.floor(z + this.min.z() - epsilon); + int maxX = MathUtil.floor(x + this.max.x() + epsilon); + int maxY = MathUtil.floor(y + this.max.y() + epsilon); + int maxZ = MathUtil.floor(z + this.max.z() + epsilon); - for (int _x = minX; _x <= maxX; _x++) { - for (int _y = minY; _y <= maxY; _y++) { - for (int _z = minZ; _z <= maxZ; _z++) { + for (int _x = minX; _x <= maxX && _x >= minX; _x++) { + for (int _y = minY; _y <= maxY && _y >= minY; _y++) { + for (int _z = minZ; _z <= maxZ && _z >= minZ; _z++) { if (!consumer.test(_x, _y, _z)) { return false; } @@ -394,16 +396,16 @@ public class Box implements Shape { @Override public boolean forEachBlock(double x, double y, double z, double epsilon, T t, @Nonnull TriIntObjPredicate consumer) { - int minX = MathUtil.floor(x + this.min.getX() - epsilon); - int minY = MathUtil.floor(y + this.min.getY() - epsilon); - int minZ = MathUtil.floor(z + this.min.getZ() - epsilon); - int maxX = MathUtil.floor(x + this.max.getX() + epsilon); - int maxY = MathUtil.floor(y + this.max.getY() + epsilon); - int maxZ = MathUtil.floor(z + this.max.getZ() + epsilon); + int minX = MathUtil.floor(x + this.min.x() - epsilon); + int minY = MathUtil.floor(y + this.min.y() - epsilon); + int minZ = MathUtil.floor(z + this.min.z() - epsilon); + int maxX = MathUtil.floor(x + this.max.x() + epsilon); + int maxY = MathUtil.floor(y + this.max.y() + epsilon); + int maxZ = MathUtil.floor(z + this.max.z() + epsilon); - for (int _x = minX; _x <= maxX; _x++) { - for (int _y = minY; _y <= maxY; _y++) { - for (int _z = minZ; _z <= maxZ; _z++) { + for (int _x = minX; _x <= maxX && _x >= minX; _x++) { + for (int _y = minY; _y <= maxY && _y >= minY; _y++) { + for (int _z = minZ; _z <= maxZ && _z >= minZ; _z++) { if (!consumer.test(_x, _y, _z, t)) { return false; } @@ -444,16 +446,21 @@ public class Box implements Shape { } public boolean intersectsLine(@Nonnull Vector3d start, @Nonnull Vector3d end) { - Vector3d direction = end.clone().subtract(start); + double ox = start.x; + double oy = start.y; + double oz = start.z; + double dx = end.x - ox; + double dy = end.y - oy; + double dz = end.z - oz; double tmin = 0.0; double tmax = 1.0; - if (Math.abs(direction.x) < 1.0E-10) { + if (Math.abs(dx) < 1.0E-10) { if (start.x < this.min.x || start.x > this.max.x) { return false; } } else { - double t1 = (this.min.x - start.x) / direction.x; - double t2 = (this.max.x - start.x) / direction.x; + double t1 = (this.min.x - start.x) / dx; + double t2 = (this.max.x - start.x) / dx; if (t1 > t2) { double temp = t1; t1 = t2; @@ -467,13 +474,13 @@ public class Box implements Shape { } } - if (Math.abs(direction.y) < 1.0E-10) { + if (Math.abs(dy) < 1.0E-10) { if (start.y < this.min.y || start.y > this.max.y) { return false; } } else { - double t1x = (this.min.y - start.y) / direction.y; - double t2x = (this.max.y - start.y) / direction.y; + double t1x = (this.min.y - start.y) / dy; + double t2x = (this.max.y - start.y) / dy; if (t1x > t2x) { double temp = t1x; t1x = t2x; @@ -487,9 +494,9 @@ public class Box implements Shape { } } - if (!(Math.abs(direction.z) < 1.0E-10)) { - double t1xx = (this.min.z - start.z) / direction.z; - double t2xx = (this.max.z - start.z) / direction.z; + if (!(Math.abs(dz) < 1.0E-10)) { + double t1xx = (this.min.z - start.z) / dz; + double t2xx = (this.max.z - start.z) / dz; if (t1xx > t2xx) { double temp = t1xx; t1xx = t2xx; diff --git a/src/com/hypixel/hytale/math/shape/Box2D.java b/src/com/hypixel/hytale/math/shape/Box2D.java index a4faa010..5669df90 100644 --- a/src/com/hypixel/hytale/math/shape/Box2D.java +++ b/src/com/hypixel/hytale/math/shape/Box2D.java @@ -2,14 +2,15 @@ package com.hypixel.hytale.math.shape; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector2d; +import com.hypixel.hytale.math.vector.Vector2dUtil; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class Box2D implements Shape2D { public static final BuilderCodec CODEC = BuilderCodec.builder(Box2D.class, Box2D::new) - .append(new KeyedCodec<>("Min", Vector2d.CODEC), (shape, min) -> shape.min.assign(min), shape -> shape.min) + .append(new KeyedCodec<>("Min", Vector2dUtil.CODEC), (shape, min) -> shape.min.set(min), shape -> shape.min) .add() - .append(new KeyedCodec<>("Max", Vector2d.CODEC), (shape, max) -> shape.max.assign(max), shape -> shape.max) + .append(new KeyedCodec<>("Max", Vector2dUtil.CODEC), (shape, max) -> shape.max.set(max), shape -> shape.max) .add() .build(); @Nonnull @@ -22,40 +23,40 @@ public class Box2D implements Shape2D { public Box2D(@Nonnull Box2D box) { this(); - this.min.assign(box.min); - this.max.assign(box.max); + this.min.set(box.min); + this.max.set(box.max); } public Box2D(@Nonnull Vector2d min, @Nonnull Vector2d max) { this(); - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); } public Box2D(double xMin, double yMin, double xMax, double yMax) { this(); - this.min.assign(xMin, yMin); - this.max.assign(xMax, yMax); + this.min.set(xMin, yMin); + this.max.set(xMax, yMax); } @Nonnull public Box2D setMinMax(@Nonnull Vector2d min, @Nonnull Vector2d max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); return this; } @Nonnull public Box2D setMinMax(@Nonnull double[] min, @Nonnull double[] max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); return this; } @Nonnull public Box2D setMinMax(@Nonnull float[] min, @Nonnull float[] max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); return this; } @@ -67,8 +68,8 @@ public class Box2D implements Shape2D { @Nonnull public Box2D setMinMax(double min, double max) { - this.min.assign(min); - this.max.assign(max); + this.min.set(min); + this.max.set(max); return this; } @@ -95,15 +96,15 @@ public class Box2D implements Shape2D { @Nonnull public Box2D assign(@Nonnull Box2D other) { - this.min.assign(other.min); - this.max.assign(other.max); + this.min.set(other.min); + this.max.set(other.max); return this; } @Nonnull public Box2D minkowskiSum(@Nonnull Box2D bb) { - this.min.subtract(bb.max); - this.max.subtract(bb.min); + this.min.sub(bb.max); + this.max.sub(bb.min); return this; } @@ -157,7 +158,7 @@ public class Box2D implements Shape2D { @Nonnull public Box2D extend(double extentX, double extentY) { - this.min.subtract(extentX, extentY); + this.min.sub(extentX, extentY); this.max.add(extentX, extentY); return this; } @@ -177,21 +178,21 @@ public class Box2D implements Shape2D { @Nonnull @Override public Box2D getBox(double x, double y) { - return new Box2D(this.min.getX() + x, this.min.getY() + y, this.max.getX() + x, this.max.getY() + y); + return new Box2D(this.min.x() + x, this.min.y() + y, this.max.x() + x, this.max.y() + y); } @Override public boolean containsPosition(@Nonnull Vector2d origin, @Nonnull Vector2d position) { - double x = position.getX() - origin.getX(); - double y = position.getY() - origin.getY(); - return x >= this.min.getX() && x <= this.max.getX() && y >= this.min.getY() && y <= this.max.getY(); + double x = position.x() - origin.x(); + double y = position.y() - origin.y(); + return x >= this.min.x() && x <= this.max.x() && y >= this.min.y() && y <= this.max.y(); } @Override public boolean containsPosition(@Nonnull Vector2d origin, double xx, double yy) { - double x = xx - origin.getX(); - double y = yy - origin.getY(); - return x >= this.min.getX() && x <= this.max.getX() && y >= this.min.getY() && y <= this.max.getY(); + double x = xx - origin.x(); + double y = yy - origin.y(); + return x >= this.min.x() && x <= this.max.x() && y >= this.min.y() && y <= this.max.y(); } @Nonnull diff --git a/src/com/hypixel/hytale/math/shape/Cylinder.java b/src/com/hypixel/hytale/math/shape/Cylinder.java index d77ea5ab..96c83fc4 100644 --- a/src/com/hypixel/hytale/math/shape/Cylinder.java +++ b/src/com/hypixel/hytale/math/shape/Cylinder.java @@ -88,8 +88,8 @@ public class Cylinder implements Shape { public Box getBox(double x, double y, double z) { double biggestRadius = Math.max(this.radiusX, this.radiusZ); Box boundingBox = new Box(); - boundingBox.min.assign(x - biggestRadius, y, z - biggestRadius); - boundingBox.max.assign(x + biggestRadius, y + this.height, z + biggestRadius); + boundingBox.min.set(x - biggestRadius, y, z - biggestRadius); + boundingBox.max.set(x + biggestRadius, y + this.height, z + biggestRadius); return boundingBox; } diff --git a/src/com/hypixel/hytale/math/shape/Ellipsoid.java b/src/com/hypixel/hytale/math/shape/Ellipsoid.java index e289d630..aac652fd 100644 --- a/src/com/hypixel/hytale/math/shape/Ellipsoid.java +++ b/src/com/hypixel/hytale/math/shape/Ellipsoid.java @@ -36,8 +36,8 @@ public class Ellipsoid implements Shape { @Override public Box getBox(double x, double y, double z) { Box boundingBox = new Box(); - boundingBox.min.assign(x - this.radiusX, y - this.radiusY, z - this.radiusZ); - boundingBox.max.assign(x + this.radiusX, y + this.radiusY, z + this.radiusZ); + boundingBox.min.set(x - this.radiusX, y - this.radiusY, z - this.radiusZ); + boundingBox.max.set(x + this.radiusX, y + this.radiusY, z + this.radiusZ); return boundingBox; } diff --git a/src/com/hypixel/hytale/math/shape/OriginShape.java b/src/com/hypixel/hytale/math/shape/OriginShape.java index 676bd8a2..376ac0d3 100644 --- a/src/com/hypixel/hytale/math/shape/OriginShape.java +++ b/src/com/hypixel/hytale/math/shape/OriginShape.java @@ -2,8 +2,8 @@ package com.hypixel.hytale.math.shape; import com.hypixel.hytale.function.predicate.TriIntObjPredicate; import com.hypixel.hytale.function.predicate.TriIntPredicate; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class OriginShape implements Shape { public final Vector3d origin; @@ -28,12 +28,12 @@ public class OriginShape implements Shape { @Override public Box getBox(double x, double y, double z) { - return this.shape.getBox(x + this.origin.getX(), y + this.origin.getY(), z + this.origin.getZ()); + return this.shape.getBox(x + this.origin.x(), y + this.origin.y(), z + this.origin.z()); } @Override public boolean containsPosition(double x, double y, double z) { - return this.shape.containsPosition(x - this.origin.getX(), y - this.origin.getY(), z - this.origin.getZ()); + return this.shape.containsPosition(x - this.origin.x(), y - this.origin.y(), z - this.origin.z()); } @Override @@ -43,12 +43,12 @@ public class OriginShape implements Shape { @Override public boolean forEachBlock(double x, double y, double z, double epsilon, TriIntPredicate consumer) { - return this.shape.forEachBlock(x + this.origin.getX(), y + this.origin.getY(), z + this.origin.getZ(), epsilon, consumer); + return this.shape.forEachBlock(x + this.origin.x(), y + this.origin.y(), z + this.origin.z(), epsilon, consumer); } @Override public boolean forEachBlock(double x, double y, double z, double epsilon, T t, TriIntObjPredicate consumer) { - return this.shape.forEachBlock(x + this.origin.getX(), y + this.origin.getY(), z + this.origin.getZ(), epsilon, t, consumer); + return this.shape.forEachBlock(x + this.origin.x(), y + this.origin.y(), z + this.origin.z(), epsilon, t, consumer); } @Nonnull diff --git a/src/com/hypixel/hytale/math/shape/Quad2d.java b/src/com/hypixel/hytale/math/shape/Quad2d.java index f0acaf61..86369521 100644 --- a/src/com/hypixel/hytale/math/shape/Quad2d.java +++ b/src/com/hypixel/hytale/math/shape/Quad2d.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.math.shape; -import com.hypixel.hytale.math.vector.Vector2d; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class Quad2d { private Vector2d a; @@ -120,7 +120,7 @@ public class Quad2d { @Nonnull public Vector2d getCenter(@Nonnull Vector2d target) { - return target.assign((this.a.x + this.c.x) * 0.5, (this.a.y + this.c.y) * 0.5); + return target.set((this.a.x + this.c.x) * 0.5, (this.a.y + this.c.y) * 0.5); } @Nonnull @@ -139,9 +139,9 @@ public class Quad2d { double pq = 1.0 - p - q; if (random.nextBoolean()) { - vec.assign(-this.a.x * pq + this.b.x * p + this.c.x * q, -this.a.y * pq + this.b.y * p + this.c.y * q); + vec.set(-this.a.x * pq + this.b.x * p + this.c.x * q, -this.a.y * pq + this.b.y * p + this.c.y * q); } else { - vec.assign(-this.a.x * pq + this.c.x * p + this.d.x * q, -this.a.y * pq + this.c.y * p + this.d.y * q); + vec.set(-this.a.x * pq + this.c.x * p + this.d.x * q, -this.a.y * pq + this.c.y * p + this.d.y * q); } return vec; diff --git a/src/com/hypixel/hytale/math/shape/Quad4d.java b/src/com/hypixel/hytale/math/shape/Quad4d.java index 7b63be2a..a0186163 100644 --- a/src/com/hypixel/hytale/math/shape/Quad4d.java +++ b/src/com/hypixel/hytale/math/shape/Quad4d.java @@ -1,9 +1,10 @@ package com.hypixel.hytale.math.shape; -import com.hypixel.hytale.math.matrix.Matrix4d; -import com.hypixel.hytale.math.vector.Vector4d; +import com.hypixel.hytale.math.vector.Vector4dUtil; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Matrix4d; +import org.joml.Vector4d; public class Quad4d { private Vector4d a; @@ -19,7 +20,7 @@ public class Quad4d { } public Quad4d() { - this(new Vector4d(), new Vector4d(), new Vector4d(), new Vector4d()); + this(new Vector4d().zero(), new Vector4d().zero(), new Vector4d().zero(), new Vector4d().zero()); } public Quad4d(@Nonnull Vector4d[] points) { @@ -31,7 +32,10 @@ public class Quad4d { } public boolean isFullyInsideFrustum() { - return this.a.isInsideFrustum() && this.b.isInsideFrustum() && this.c.isInsideFrustum() && this.d.isInsideFrustum(); + return Vector4dUtil.isInsideFrustum(this.a) + && Vector4dUtil.isInsideFrustum(this.b) + && Vector4dUtil.isInsideFrustum(this.c) + && Vector4dUtil.isInsideFrustum(this.d); } public Vector4d getA() { @@ -101,19 +105,19 @@ public class Quad4d { @Nonnull public Quad4d multiply(@Nonnull Matrix4d matrix, @Nonnull Quad4d target) { - matrix.multiply(this.a, target.a); - matrix.multiply(this.b, target.b); - matrix.multiply(this.c, target.c); - matrix.multiply(this.d, target.d); + matrix.transform(this.a, target.a); + matrix.transform(this.b, target.b); + matrix.transform(this.c, target.c); + matrix.transform(this.d, target.d); return target; } @Nonnull public Quad2d to2d(@Nonnull Quad2d target) { - target.getA().assign(this.a.x, this.a.y); - target.getB().assign(this.b.x, this.b.y); - target.getC().assign(this.c.x, this.c.y); - target.getD().assign(this.d.x, this.d.y); + target.getA().set(this.a.x, this.a.y); + target.getB().set(this.b.x, this.b.y); + target.getC().set(this.c.x, this.c.y); + target.getD().set(this.d.x, this.d.y); return target; } @@ -124,7 +128,7 @@ public class Quad4d { @Nonnull public Vector4d getCenter(@Nonnull Vector4d target) { - return target.assign( + return target.set( this.a.x + (this.c.x - this.a.x) * 0.5, this.b.x + (this.b.x - this.b.x) * 0.5, this.c.x + (this.c.x - this.c.x) * 0.5, @@ -133,10 +137,10 @@ public class Quad4d { } public void perspectiveTransform() { - this.a.perspectiveTransform(); - this.b.perspectiveTransform(); - this.c.perspectiveTransform(); - this.d.perspectiveTransform(); + Vector4dUtil.perspectiveTransform(this.a); + Vector4dUtil.perspectiveTransform(this.b); + Vector4dUtil.perspectiveTransform(this.c); + Vector4dUtil.perspectiveTransform(this.d); } @Nonnull @@ -150,14 +154,14 @@ public class Quad4d { double q = random.nextDouble() * (1.0 - p); double pq = 1.0 - p - q; if (random.nextBoolean()) { - target.assign( + target.set( this.a.x * pq + this.b.x * p + this.c.x * q, this.a.y * pq + this.b.y * p + this.c.y * q, this.a.z * pq + this.b.z * p + this.c.z * q, this.a.w * pq + this.b.w * p + this.c.w * q ); } else { - target.assign( + target.set( this.a.x * pq + this.c.x * p + this.d.x * q, this.a.y * pq + this.c.y * p + this.d.y * q, this.a.z * pq + this.c.z * p + this.d.z * q, diff --git a/src/com/hypixel/hytale/math/shape/Rectangle.java b/src/com/hypixel/hytale/math/shape/Rectangle.java index 778cf2cd..50330bb7 100644 --- a/src/com/hypixel/hytale/math/shape/Rectangle.java +++ b/src/com/hypixel/hytale/math/shape/Rectangle.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.math.shape; -import com.hypixel.hytale.math.vector.Vector2d; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; public class Rectangle { private Vector2d min; diff --git a/src/com/hypixel/hytale/math/shape/Shape.java b/src/com/hypixel/hytale/math/shape/Shape.java index e4948d17..489f801d 100644 --- a/src/com/hypixel/hytale/math/shape/Shape.java +++ b/src/com/hypixel/hytale/math/shape/Shape.java @@ -2,22 +2,22 @@ package com.hypixel.hytale.math.shape; import com.hypixel.hytale.function.predicate.TriIntObjPredicate; import com.hypixel.hytale.function.predicate.TriIntPredicate; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public interface Shape { default Box getBox(@Nonnull Vector3d position) { - return this.getBox(position.getX(), position.getY(), position.getZ()); + return this.getBox(position.x(), position.y(), position.z()); } Box getBox(double var1, double var3, double var5); default boolean containsPosition(@Nonnull Vector3d origin, @Nonnull Vector3d position) { - return this.containsPosition(position.getX() - origin.getX(), position.getY() - origin.getY(), position.getZ() - origin.getZ()); + return this.containsPosition(position.x() - origin.x(), position.y() - origin.y(), position.z() - origin.z()); } default boolean containsPosition(@Nonnull Vector3d position) { - return this.containsPosition(position.getX(), position.getY(), position.getZ()); + return this.containsPosition(position.x(), position.y(), position.z()); } boolean containsPosition(double var1, double var3, double var5); @@ -25,11 +25,11 @@ public interface Shape { void expand(double var1); default boolean forEachBlock(@Nonnull Vector3d origin, TriIntPredicate consumer) { - return this.forEachBlock(origin.getX(), origin.getY(), origin.getZ(), consumer); + return this.forEachBlock(origin.x(), origin.y(), origin.z(), consumer); } default boolean forEachBlock(@Nonnull Vector3d origin, double epsilon, TriIntPredicate consumer) { - return this.forEachBlock(origin.getX(), origin.getY(), origin.getZ(), epsilon, consumer); + return this.forEachBlock(origin.x(), origin.y(), origin.z(), epsilon, consumer); } default boolean forEachBlock(double x, double y, double z, TriIntPredicate consumer) { @@ -39,11 +39,11 @@ public interface Shape { boolean forEachBlock(double var1, double var3, double var5, double var7, TriIntPredicate var9); default boolean forEachBlock(@Nonnull Vector3d origin, T t, TriIntObjPredicate consumer) { - return this.forEachBlock(origin.getX(), origin.getY(), origin.getZ(), t, consumer); + return this.forEachBlock(origin.x(), origin.y(), origin.z(), t, consumer); } default boolean forEachBlock(@Nonnull Vector3d origin, double epsilon, T t, TriIntObjPredicate consumer) { - return this.forEachBlock(origin.getX(), origin.getY(), origin.getZ(), epsilon, t, consumer); + return this.forEachBlock(origin.x(), origin.y(), origin.z(), epsilon, t, consumer); } default boolean forEachBlock(double x, double y, double z, T t, TriIntObjPredicate consumer) { diff --git a/src/com/hypixel/hytale/math/shape/Shape2D.java b/src/com/hypixel/hytale/math/shape/Shape2D.java index 279a7f5f..25b3c8e9 100644 --- a/src/com/hypixel/hytale/math/shape/Shape2D.java +++ b/src/com/hypixel/hytale/math/shape/Shape2D.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.math.shape; -import com.hypixel.hytale.math.vector.Vector2d; import javax.annotation.Nonnull; +import org.joml.Vector2d; public interface Shape2D { default Box2D getBox(@Nonnull Vector2d position) { - return this.getBox(position.getX(), position.getY()); + return this.getBox(position.x(), position.y()); } Box2D getBox(double var1, double var3); diff --git a/src/com/hypixel/hytale/math/shape/Triangle2d.java b/src/com/hypixel/hytale/math/shape/Triangle2d.java index 02d6501b..d4cbab98 100644 --- a/src/com/hypixel/hytale/math/shape/Triangle2d.java +++ b/src/com/hypixel/hytale/math/shape/Triangle2d.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.math.shape; -import com.hypixel.hytale.math.vector.Vector2d; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class Triangle2d { private Vector2d a; @@ -117,7 +117,7 @@ public class Triangle2d { q = 1.0 - q; } - vec.assign(-this.a.x * (1.0 - p - q) + this.b.x * p + this.c.x * q, -this.a.y * (1.0 - p - q) + this.b.y * p + this.c.y * q); + vec.set(-this.a.x * (1.0 - p - q) + this.b.x * p + this.c.x * q, -this.a.y * (1.0 - p - q) + this.b.y * p + this.c.y * q); return vec; } } diff --git a/src/com/hypixel/hytale/math/shape/Triangle4d.java b/src/com/hypixel/hytale/math/shape/Triangle4d.java index 10a13c75..af237b94 100644 --- a/src/com/hypixel/hytale/math/shape/Triangle4d.java +++ b/src/com/hypixel/hytale/math/shape/Triangle4d.java @@ -1,9 +1,10 @@ package com.hypixel.hytale.math.shape; -import com.hypixel.hytale.math.matrix.Matrix4d; -import com.hypixel.hytale.math.vector.Vector4d; +import com.hypixel.hytale.math.vector.Vector4dUtil; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Matrix4d; +import org.joml.Vector4d; public class Triangle4d { private Vector4d a; @@ -17,7 +18,7 @@ public class Triangle4d { } public Triangle4d() { - this(new Vector4d(), new Vector4d(), new Vector4d()); + this(new Vector4d().zero(), new Vector4d().zero(), new Vector4d().zero()); } public Triangle4d(@Nonnull Vector4d[] points) { @@ -68,15 +69,15 @@ public class Triangle4d { @Nonnull public Triangle4d assign(@Nonnull Vector4d v1, @Nonnull Vector4d v2, @Nonnull Vector4d v3) { - this.a.assign(v1); - this.b.assign(v2); - this.c.assign(v3); + this.a.set(v1); + this.b.set(v2); + this.c.set(v3); return this; } @Nonnull public Vector4d getRandom(@Nonnull Random random) { - return this.getRandom(random, new Vector4d()); + return this.getRandom(random, new Vector4d().zero()); } @Nonnull @@ -84,7 +85,7 @@ public class Triangle4d { double p = random.nextDouble(); double q = random.nextDouble() * (1.0 - p); double pq = 1.0 - p - q; - vec.assign( + vec.set( this.a.x * pq + this.b.x * p + this.c.x * q, this.a.y * pq + this.b.y * p + this.c.y * q, this.a.z * pq + this.b.z * p + this.c.z * q, @@ -100,25 +101,25 @@ public class Triangle4d { @Nonnull public Triangle4d multiply(@Nonnull Matrix4d matrix, @Nonnull Triangle4d target) { - matrix.multiply(this.a, target.a); - matrix.multiply(this.b, target.b); - matrix.multiply(this.c, target.c); + matrix.transform(this.a, target.a); + matrix.transform(this.b, target.b); + matrix.transform(this.c, target.c); return target; } @Nonnull public Triangle2d to2d(@Nonnull Triangle2d target) { - target.getA().assign(this.a.x, this.a.y); - target.getB().assign(this.b.x, this.b.y); - target.getC().assign(this.c.x, this.c.y); + target.getA().set(this.a.x, this.a.y); + target.getB().set(this.b.x, this.b.y); + target.getC().set(this.c.x, this.c.y); return target; } @Nonnull public Triangle4d perspectiveTransform() { - this.a.perspectiveTransform(); - this.b.perspectiveTransform(); - this.c.perspectiveTransform(); + Vector4dUtil.perspectiveTransform(this.a); + Vector4dUtil.perspectiveTransform(this.b); + Vector4dUtil.perspectiveTransform(this.c); return this; } diff --git a/src/com/hypixel/hytale/math/shape/ViewUtil.java b/src/com/hypixel/hytale/math/shape/ViewUtil.java index ddb4cc30..d838c2f4 100644 --- a/src/com/hypixel/hytale/math/shape/ViewUtil.java +++ b/src/com/hypixel/hytale/math/shape/ViewUtil.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.math.shape; -import com.hypixel.hytale.math.vector.Vector2d; import java.awt.Graphics2D; +import org.joml.Vector2d; public class ViewUtil { public static final int INSIDE = 0; diff --git a/src/com/hypixel/hytale/math/util/ChunkUtil.java b/src/com/hypixel/hytale/math/util/ChunkUtil.java index f839c931..53d2876a 100644 --- a/src/com/hypixel/hytale/math/util/ChunkUtil.java +++ b/src/com/hypixel/hytale/math/util/ChunkUtil.java @@ -25,6 +25,8 @@ public class ChunkUtil { public static final int MIN_Y = 0; public static final int MIN_ENTITY_Y = -32; public static final int MIN_SECTION = 0; + public static final int MIN_CHUNK_COORD = -67108864; + public static final int MAX_CHUNK_COORD = 67108863; private ChunkUtil() { } @@ -174,4 +176,16 @@ public class ChunkUtil { public static int worldCoordFromLocalCoord(int chunkCoord, int localCoord) { return chunkCoord << 5 | localCoord; } + + public static boolean isValidChunkIndex(long chunkIndex) { + return isValidChunkCoords(xOfChunkIndex(chunkIndex), zOfChunkIndex(chunkIndex)); + } + + public static boolean isValidChunkCoords(int chunkCoordX, int chunkCoordZ) { + return isValidChunkCoord(chunkCoordX) && isValidChunkCoord(chunkCoordZ); + } + + public static boolean isValidChunkCoord(int chunkCoord) { + return chunkCoord >= -67108864 && chunkCoord <= 67108863; + } } diff --git a/src/com/hypixel/hytale/math/util/MathUtil.java b/src/com/hypixel/hytale/math/util/MathUtil.java index 313bbb9f..bd5a11d1 100644 --- a/src/com/hypixel/hytale/math/util/MathUtil.java +++ b/src/com/hypixel/hytale/math/util/MathUtil.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.math.util; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class MathUtil { public static final double EPSILON_DOUBLE = Math.ulp(1.0); @@ -526,39 +526,39 @@ public class MathUtil { return (ax - x) * (by - y) - (ay - y) * (bx - x) >= 0.0 ? 1 : -1; } - public static Vector3f getRotationForHitNormal(Vector3f normal) { + public static Rotation3f getRotationForHitNormal(Vector3d normal) { if (normal == null) { - return Vector3f.ZERO; - } else if (normal.y == 1.0F) { - return Vector3f.ZERO; - } else if (normal.y == -1.0F) { - return new Vector3f(0.0F, 0.0F, (float) Math.PI); - } else if (normal.x == 1.0F) { - return new Vector3f(0.0F, 0.0F, (float) (-Math.PI / 2)); - } else if (normal.x == -1.0F) { - return new Vector3f(0.0F, 0.0F, (float) (Math.PI / 2)); - } else if (normal.z == 1.0F) { - return new Vector3f((float) (Math.PI / 2), 0.0F, 0.0F); + return new Rotation3f(Rotation3f.IDENTITY); + } else if (normal.y == 1.0) { + return new Rotation3f(Rotation3f.IDENTITY); + } else if (normal.y == -1.0) { + return new Rotation3f(0.0F, 0.0F, (float) Math.PI); + } else if (normal.x == 1.0) { + return new Rotation3f(0.0F, 0.0F, (float) (-Math.PI / 2)); + } else if (normal.x == -1.0) { + return new Rotation3f(0.0F, 0.0F, (float) (Math.PI / 2)); + } else if (normal.z == 1.0) { + return new Rotation3f((float) (Math.PI / 2), 0.0F, 0.0F); } else { - return normal.z == -1.0F ? new Vector3f((float) (-Math.PI / 2), 0.0F, 0.0F) : Vector3f.ZERO; + return normal.z == -1.0 ? new Rotation3f((float) (-Math.PI / 2), 0.0F, 0.0F) : new Rotation3f(Rotation3f.IDENTITY); } } - public static String getNameForHitNormal(Vector3f normal) { + public static String getNameForHitNormal(Vector3d normal) { if (normal == null) { return "UP"; - } else if (normal.y == 1.0F) { + } else if (normal.y == 1.0) { return "UP"; - } else if (normal.y == -1.0F) { + } else if (normal.y == -1.0) { return "DOWN"; - } else if (normal.x == 1.0F) { + } else if (normal.x == 1.0) { return "WEST"; - } else if (normal.x == -1.0F) { + } else if (normal.x == -1.0) { return "EAST"; - } else if (normal.z == 1.0F) { + } else if (normal.z == 1.0) { return "NORTH"; } else { - return normal.z == -1.0F ? "SOUTH" : "UP"; + return normal.z == -1.0 ? "SOUTH" : "UP"; } } diff --git a/src/com/hypixel/hytale/math/util/NearestBlockUtil.java b/src/com/hypixel/hytale/math/util/NearestBlockUtil.java index e693b3f7..826a3dd3 100644 --- a/src/com/hypixel/hytale/math/util/NearestBlockUtil.java +++ b/src/com/hypixel/hytale/math/util/NearestBlockUtil.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.math.util; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.function.BiPredicate; import java.util.function.DoubleUnaryOperator; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public final class NearestBlockUtil { public static final NearestBlockUtil.IterationElement[] DEFAULT_ELEMENTS = new NearestBlockUtil.IterationElement[]{ @@ -23,14 +23,14 @@ public final class NearestBlockUtil { @Nullable public static Vector3i findNearestBlock(@Nonnull Vector3d position, @Nonnull BiPredicate validBlock, T t) { - return findNearestBlock(DEFAULT_ELEMENTS, position.getX(), position.getY(), position.getZ(), validBlock, t); + return findNearestBlock(DEFAULT_ELEMENTS, position.x(), position.y(), position.z(), validBlock, t); } @Nullable public static Vector3i findNearestBlock( @Nonnull NearestBlockUtil.IterationElement[] elements, @Nonnull Vector3d position, @Nonnull BiPredicate validBlock, T t ) { - return findNearestBlock(elements, position.getX(), position.getY(), position.getZ(), validBlock, t); + return findNearestBlock(elements, position.x(), position.y(), position.z(), validBlock, t); } @Nullable @@ -57,14 +57,14 @@ public final class NearestBlockUtil { double dy = ry - element.getY().applyAsDouble(ry); double dz = rz - element.getZ().applyAsDouble(rz); double dist = dx * dx + dy * dy + dz * dz; - tmp.assign(blockX + element.getOffsetX(), blockY + element.getOffsetY(), blockZ + element.getOffsetZ()); + tmp.set(blockX + element.getOffsetX(), blockY + element.getOffsetY(), blockZ + element.getOffsetZ()); if (dist < nearestDist && validBlock.test(tmp, t)) { nearestDist = dist; if (nearest == null) { nearest = new Vector3i(); } - nearest.assign(tmp); + nearest.set(tmp); } } diff --git a/src/com/hypixel/hytale/math/vector/Location.java b/src/com/hypixel/hytale/math/vector/Location.java index 424b2dd9..d4c62b96 100644 --- a/src/com/hypixel/hytale/math/vector/Location.java +++ b/src/com/hypixel/hytale/math/vector/Location.java @@ -6,6 +6,8 @@ import com.hypixel.hytale.math.util.TrigMathUtil; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class Location { @Nullable @@ -13,45 +15,45 @@ public class Location { @Nonnull protected Vector3d position; @Nonnull - protected Vector3f rotation; + protected Rotation3f rotation; public Location() { - this(null, new Vector3d(), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(null, new Vector3d(), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Location(@Nonnull Vector3i position) { - this(null, new Vector3d(position), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(null, new Vector3d(position), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Location(@Nullable String world, @Nonnull Vector3i position) { - this(world, new Vector3d(position), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(world, new Vector3d(position), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Location(@Nonnull Vector3d position) { - this(null, new Vector3d(position), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(null, new Vector3d(position), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Location(@Nullable String world, @Nonnull Vector3d position) { - this(world, new Vector3d(position), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(world, new Vector3d(position), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Location(double x, double y, double z) { - this(null, new Vector3d(x, y, z), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(null, new Vector3d(x, y, z), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Location(@Nullable String world, double x, double y, double z) { - this(world, new Vector3d(x, y, z), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(world, new Vector3d(x, y, z), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Location(double x, double y, double z, float pitch, float yaw, float roll) { - this(null, new Vector3d(x, y, z), new Vector3f(pitch, yaw, roll)); + this(null, new Vector3d(x, y, z), new Rotation3f(pitch, yaw, roll)); } public Location(@Nullable String world, double x, double y, double z, float pitch, float yaw, float roll) { - this(world, new Vector3d(x, y, z), new Vector3f(pitch, yaw, roll)); + this(world, new Vector3d(x, y, z), new Rotation3f(pitch, yaw, roll)); } - public Location(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public Location(@Nonnull Vector3d position, @Nonnull Rotation3f rotation) { this(null, position, rotation); } @@ -63,7 +65,7 @@ public class Location { this(world, transform.position, transform.rotation); } - public Location(@Nullable String world, @Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public Location(@Nullable String world, @Nonnull Vector3d position, @Nonnull Rotation3f rotation) { this.world = world; this.position = position; this.rotation = rotation; @@ -88,22 +90,22 @@ public class Location { } @Nonnull - public Vector3f getRotation() { + public Rotation3f getRotation() { return this.rotation; } - public void setRotation(@Nonnull Vector3f rotation) { + public void setRotation(@Nonnull Rotation3f rotation) { this.rotation = rotation; } @Nonnull public Vector3d getDirection() { - return Transform.getDirection(this.rotation.getPitch(), this.rotation.getYaw()); + return Transform.getDirection(this.rotation.pitch(), this.rotation.yaw()); } @Nonnull public Vector3i getAxisDirection() { - return this.getAxisDirection(this.rotation.getPitch(), this.rotation.getYaw()); + return this.getAxisDirection(this.rotation.pitch(), this.rotation.yaw()); } @Nonnull @@ -124,10 +126,10 @@ public class Location { @Nonnull public Axis getAxis() { Vector3i axisDirection = this.getAxisDirection(); - if (axisDirection.getX() != 0) { + if (axisDirection.x() != 0) { return Axis.X; } else { - return axisDirection.getY() != 0 ? Axis.Y : Axis.Z; + return axisDirection.y() != 0 ? Axis.Y : Axis.Z; } } diff --git a/src/com/hypixel/hytale/math/vector/Rotation3f.java b/src/com/hypixel/hytale/math/vector/Rotation3f.java new file mode 100644 index 00000000..0060a07d --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Rotation3f.java @@ -0,0 +1,349 @@ +package com.hypixel.hytale.math.vector; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; +import com.hypixel.hytale.math.Axis; +import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.math.util.TrigMathUtil; +import javax.annotation.Nonnull; +import org.joml.Quaterniond; +import org.joml.Quaterniondc; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; + +public class Rotation3f implements Rotation3fc { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(Rotation3f.class, () -> new Rotation3f(Float.NaN, Float.NaN, Float.NaN)) + .metadata(UIDisplayMode.COMPACT) + .appendInherited(new KeyedCodec<>("Pitch", Codec.FLOAT), (o, i) -> o.x = i, o -> Float.isNaN(o.x) ? null : o.x, (o, p) -> o.x = p.x) + .add() + .appendInherited(new KeyedCodec<>("Yaw", Codec.FLOAT), (o, i) -> o.y = i, o -> Float.isNaN(o.y) ? null : o.y, (o, p) -> o.y = p.y) + .add() + .appendInherited(new KeyedCodec<>("Roll", Codec.FLOAT), (o, i) -> o.z = i, o -> Float.isNaN(o.z) ? null : o.z, (o, p) -> o.z = p.z) + .add() + .build(); + public static final Rotation3fc ZERO = new Rotation3f(0.0F, 0.0F, 0.0F); + public static final Rotation3fc IDENTITY = ZERO; + public static final Rotation3fc NaN = new Rotation3f(Float.NaN, Float.NaN, Float.NaN); + public float x; + public float y; + public float z; + + public Rotation3f() { + this(0.0F, 0.0F, 0.0F); + } + + public Rotation3f(@Nonnull Rotation3fc v) { + this(v.x(), v.y(), v.z()); + } + + public Rotation3f(float pitch, float yaw, float roll) { + this.x = pitch; + this.y = yaw; + this.z = roll; + } + + @Override + public float x() { + return this.x; + } + + @Override + public float pitch() { + return this.x; + } + + public void setX(float x) { + this.x = x; + } + + public void setPitch(float pitch) { + this.x = pitch; + } + + @Override + public float y() { + return this.y; + } + + @Override + public float yaw() { + return this.y; + } + + public void setY(float y) { + this.y = y; + } + + public void setYaw(float yaw) { + this.y = yaw; + } + + @Override + public float z() { + return this.z; + } + + @Override + public float roll() { + return this.z; + } + + public void setZ(float z) { + this.z = z; + } + + public void setRoll(float roll) { + this.z = roll; + } + + @Nonnull + public Rotation3f set(@Nonnull Rotation3fc v) { + this.x = v.x(); + this.y = v.y(); + this.z = v.z(); + return this; + } + + @Nonnull + public Rotation3f set(@Nonnull float[] v) { + this.x = v[0]; + this.y = v[1]; + this.z = v[2]; + return this; + } + + @Nonnull + public Rotation3f set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Nonnull + public Rotation3f add(@Nonnull Rotation3f v) { + this.x = this.x + v.x; + this.y = this.y + v.y; + this.z = this.z + v.z; + return this; + } + + @Nonnull + public Rotation3f add(float x, float y, float z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public void addPitch(float pitch) { + this.x += pitch; + } + + public void addYaw(float yaw) { + this.y += yaw; + } + + public void addRoll(float roll) { + this.z += roll; + } + + @Nonnull + public Rotation3f sub(@Nonnull Rotation3f v) { + this.x = this.x - v.x; + this.y = this.y - v.y; + this.z = this.z - v.z; + return this; + } + + @Nonnull + public Rotation3f sub(@Nonnull Vector3i v) { + this.x = this.x - v.x; + this.y = this.y - v.y; + this.z = this.z - v.z; + return this; + } + + @Nonnull + public Rotation3f sub(float x, float y, float z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + public void addRotationOnAxis(@Nonnull Axis axis, int degrees) { + float rad = (float) (Math.PI / 180.0) * degrees; + switch (axis) { + case X: + this.setPitch(this.pitch() + rad); + break; + case Y: + this.setYaw(this.yaw() + rad); + break; + case Z: + this.setRoll(this.roll() + rad); + } + } + + public void flipRotationOnAxis(@Nonnull Axis axis) { + this.addRotationOnAxis(axis, 180); + } + + @Nonnull + public Rotation3f negate() { + this.x = -this.x; + this.y = -this.y; + this.z = -this.z; + return this; + } + + @Nonnull + public Rotation3f mul(float s) { + this.x *= s; + this.y *= s; + this.z *= s; + return this; + } + + @Nonnull + public Rotation3f mul(@Nonnull Rotation3f p) { + this.x = this.x * p.x; + this.y = this.y * p.y; + this.z = this.z * p.z; + return this; + } + + public float distanceSquaredTo(float x, float y, float z) { + float dx = x - this.x; + float dy = y - this.y; + float dz = z - this.z; + return dx * dx + dy * dy + dz * dz; + } + + public boolean isFinite() { + return Float.isFinite(this.x) && Float.isFinite(this.y) && Float.isFinite(this.z); + } + + @Override + public Quaterniond getQuaternion(Quaterniond dest) { + return dest.rotationYXZ(this.y, this.x, this.z); + } + + @Override + public Rotation3f premul(Quaterniondc q, Rotation3f dest) { + Quaterniond resultQuat = this.getQuaternion(new Quaterniond()); + Vector3d eulerAngles = resultQuat.premul(q).getEulerAnglesYXZ(new Vector3d()); + return dest.set((float)eulerAngles.x, (float)eulerAngles.y, (float)eulerAngles.z); + } + + @Override + public Rotation3f mul(Quaterniondc q, Rotation3f dest) { + Quaterniond tempQuat = this.getQuaternion(new Quaterniond()); + Quaterniond resultQuat = tempQuat.mul(q); + Vector3d eulerAngles = resultQuat.getEulerAnglesYXZ(new Vector3d()); + return dest.set((float)eulerAngles.x, (float)eulerAngles.y, (float)eulerAngles.z); + } + + public Rotation3f premul(Quaterniondc q) { + return this.premul(q, this); + } + + public Rotation3f mul(Quaterniondc q) { + return this.mul(q, this); + } + + @Override + public Vector3d transform(Vector3dc vec, Vector3d dest) { + return vec.rotateZ(this.z, dest).rotateX(this.x).rotateY(this.y); + } + + @Override + public Vector3d transform(Vector3d vec) { + return this.transform(vec, vec); + } + + @Nonnull + public Rotation3f clone() { + return new Rotation3f(this.x, this.y, this.z); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (this.getClass() != obj.getClass()) { + return false; + } else { + Rotation3f other = (Rotation3f)obj; + if (Float.floatToIntBits(this.x) != Float.floatToIntBits(other.x)) { + return false; + } else { + return Float.floatToIntBits(this.y) != Float.floatToIntBits(other.y) ? false : Float.floatToIntBits(this.z) == Float.floatToIntBits(other.z); + } + } + } + + @Override + public int hashCode() { + int prime = 31; + int result = 1; + result = 31 * result + Float.floatToIntBits(this.x); + result = 31 * result + Float.floatToIntBits(this.y); + return 31 * result + Float.floatToIntBits(this.z); + } + + @Nonnull + @Override + public String toString() { + return "Rotation3f{x=" + this.x + ", y=" + this.y + ", z=" + this.z + "}"; + } + + @Nonnull + public static Rotation3f lerp(@Nonnull Rotation3f a, @Nonnull Rotation3f b, float t) { + return lerpUnclamped(a, b, MathUtil.clamp(t, 0.0F, 1.0F)); + } + + @Nonnull + public static Rotation3f lerpUnclamped(@Nonnull Rotation3f a, @Nonnull Rotation3f b, float t) { + return new Rotation3f(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y), a.z + t * (b.z - a.z)); + } + + @Nonnull + public static Rotation3f lerpAngle(@Nonnull Rotation3fc a, @Nonnull Rotation3fc b, float t) { + return lerpAngle(a, b, t, new Rotation3f()); + } + + @Nonnull + public static Rotation3f lerpAngle(@Nonnull Rotation3fc a, @Nonnull Rotation3fc b, float t, @Nonnull Rotation3f target) { + target.set(MathUtil.lerpAngle(a.x(), b.x(), t), MathUtil.lerpAngle(a.y(), b.y(), t), MathUtil.lerpAngle(a.z(), b.z(), t)); + return target; + } + + @Nonnull + public static Rotation3f lookAt(@Nonnull Vector3d relative) { + return lookAt(relative, new Rotation3f()); + } + + @Nonnull + public static Rotation3f lookAt(@Nonnull Vector3d relative, @Nonnull Rotation3f result) { + if (!MathUtil.closeToZero(relative.x) || !MathUtil.closeToZero(relative.z)) { + float yaw = TrigMathUtil.atan2((float)(-relative.x), (float)(-relative.z)); + result.y = MathUtil.wrapAngle(yaw); + } + + double length = relative.lengthSquared(); + if (length > 0.0) { + float pitch = (float) (Math.PI / 2) - (float)Math.acos(relative.y / Math.sqrt(length)); + result.x = MathUtil.clamp(pitch, (float) (-Math.PI / 2) + MathUtil.PITCH_EDGE_PADDING, (float) (Math.PI / 2) - MathUtil.PITCH_EDGE_PADDING); + } + + return result; + } +} diff --git a/src/com/hypixel/hytale/math/vector/Rotation3fc.java b/src/com/hypixel/hytale/math/vector/Rotation3fc.java new file mode 100644 index 00000000..dbd0d47b --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Rotation3fc.java @@ -0,0 +1,30 @@ +package com.hypixel.hytale.math.vector; + +import org.joml.Quaterniond; +import org.joml.Quaterniondc; +import org.joml.Vector3d; +import org.joml.Vector3dc; + +public interface Rotation3fc { + float x(); + + float pitch(); + + float y(); + + float yaw(); + + float z(); + + float roll(); + + Quaterniond getQuaternion(Quaterniond var1); + + Rotation3f premul(Quaterniondc var1, Rotation3f var2); + + Rotation3f mul(Quaterniondc var1, Rotation3f var2); + + Vector3d transform(Vector3dc var1, Vector3d var2); + + Vector3d transform(Vector3d var1); +} diff --git a/src/com/hypixel/hytale/math/vector/Transform.java b/src/com/hypixel/hytale/math/vector/Transform.java index b294b3c9..84fd6694 100644 --- a/src/com/hypixel/hytale/math/vector/Transform.java +++ b/src/com/hypixel/hytale/math/vector/Transform.java @@ -10,6 +10,9 @@ import com.hypixel.hytale.math.util.TrigMathUtil; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; public class Transform { @Nonnull @@ -93,7 +96,7 @@ public class Transform { @Nonnull protected Vector3d position; @Nonnull - protected Vector3f rotation; + protected Rotation3f rotation; public static final int X_IS_RELATIVE = 1; public static final int Y_IS_RELATIVE = 2; public static final int Z_IS_RELATIVE = 4; @@ -103,33 +106,37 @@ public class Transform { public static final int RELATIVE_TO_BLOCK = 64; public Transform() { - this(new Vector3d(), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(new Vector3d(), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Transform(@Nonnull Vector3i position) { - this(new Vector3d(position), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(new Vector3d(position), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Transform(@Nonnull Vector3d position) { - this(new Vector3d(position), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(new Vector3d(position), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Transform(double x, double y, double z) { - this(new Vector3d(x, y, z), new Vector3f(Float.NaN, Float.NaN, Float.NaN)); + this(new Vector3d(x, y, z), new Rotation3f(Float.NaN, Float.NaN, Float.NaN)); } public Transform(double x, double y, double z, float pitch, float yaw, float roll) { - this(new Vector3d(x, y, z), new Vector3f(pitch, yaw, roll)); + this(new Vector3d(x, y, z), new Rotation3f(pitch, yaw, roll)); } - public Transform(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public Transform(@Nonnull Transform transform) { + this(new Vector3d(transform.position), new Rotation3f(transform.rotation)); + } + + public Transform(@Nonnull Vector3d position, @Nonnull Rotation3f rotation) { this.position = position; this.rotation = rotation; } - public void assign(@Nonnull Transform transform) { - this.position.assign(transform.getPosition()); - this.rotation.assign(transform.getRotation()); + public void set(@Nonnull Transform transform) { + this.position.set(transform.position); + this.rotation.set(transform.rotation); } @Nonnull @@ -137,22 +144,22 @@ public class Transform { return this.position; } - public void setPosition(@Nonnull Vector3d position) { - this.position = position; + public void setPosition(@Nonnull Vector3dc position) { + this.position.set(position); } @Nonnull - public Vector3f getRotation() { + public Rotation3f getRotation() { return this.rotation; } - public void setRotation(@Nonnull Vector3f rotation) { - this.rotation = rotation; + public void setRotation(@Nonnull Rotation3fc rotation) { + this.rotation.set(rotation); } @Nonnull public Vector3d getDirection() { - return getDirection(this.rotation.getPitch(), this.rotation.getYaw()); + return getDirection(this.rotation.pitch(), this.rotation.yaw()); } @Nonnull @@ -172,7 +179,7 @@ public class Transform { @Nonnull public Vector3i getAxisDirection() { - return this.getAxisDirection(this.rotation.getPitch(), this.rotation.getYaw()); + return this.getAxisDirection(this.rotation.pitch(), this.rotation.yaw()); } @Nonnull @@ -193,16 +200,16 @@ public class Transform { @Nonnull public Axis getAxis() { Vector3i axisDirection = this.getAxisDirection(); - if (axisDirection.getX() != 0) { + if (axisDirection.x() != 0) { return Axis.X; } else { - return axisDirection.getY() != 0 ? Axis.Y : Axis.Z; + return axisDirection.y() != 0 ? Axis.Y : Axis.Z; } } @Nonnull public Transform clone() { - return new Transform(this.position.clone(), this.rotation.clone()); + return new Transform(new Vector3d(this.position), new Rotation3f(this.rotation)); } @Override @@ -230,45 +237,47 @@ public class Transform { } public static void applyMaskedRelativeTransform( - @Nonnull Transform transform, byte relativeMask, @Nonnull Vector3d sourcePosition, @Nonnull Vector3f sourceRotation, @Nonnull Vector3i blockPosition + @Nonnull Transform transform, byte relativeMask, @Nonnull Vector3d sourcePosition, @Nonnull Rotation3f sourceRotation, @Nonnull Vector3i blockPosition ) { if (relativeMask != 0) { + Vector3d position = transform.position; if ((relativeMask & 64) != 0) { if ((relativeMask & 1) != 0) { - transform.getPosition().setX(transform.getPosition().getX() + blockPosition.getX()); + position.x = position.x() + blockPosition.x(); } if ((relativeMask & 2) != 0) { - transform.getPosition().setY(transform.getPosition().getY() + blockPosition.getY()); + position.y = position.y() + blockPosition.y(); } if ((relativeMask & 4) != 0) { - transform.getPosition().setZ(transform.getPosition().getZ() + blockPosition.getZ()); + position.z = position.z() + blockPosition.z(); } } else { if ((relativeMask & 1) != 0) { - transform.getPosition().setX(transform.getPosition().getX() + sourcePosition.getX()); + position.x = position.x() + sourcePosition.x(); } if ((relativeMask & 2) != 0) { - transform.getPosition().setY(transform.getPosition().getY() + sourcePosition.getY()); + position.y = position.y() + sourcePosition.y(); } if ((relativeMask & 4) != 0) { - transform.getPosition().setZ(transform.getPosition().getZ() + sourcePosition.getZ()); + position.z = position.z() + sourcePosition.z(); } } + Rotation3f rotation = transform.rotation; if ((relativeMask & 8) != 0) { - transform.getRotation().setYaw(transform.getRotation().getYaw() + sourceRotation.getYaw()); + rotation.setYaw(rotation.yaw() + sourceRotation.yaw()); } if ((relativeMask & 16) != 0) { - transform.getRotation().setPitch(transform.getRotation().getPitch() + sourceRotation.getPitch()); + rotation.setPitch(rotation.pitch() + sourceRotation.pitch()); } if ((relativeMask & 32) != 0) { - transform.getRotation().setRoll(transform.getRotation().getRoll() + sourceRotation.getRoll()); + rotation.setRoll(rotation.roll() + sourceRotation.roll()); } } } diff --git a/src/com/hypixel/hytale/math/vector/Vector2d.java b/src/com/hypixel/hytale/math/vector/Vector2d.java deleted file mode 100644 index bc7c45bd..00000000 --- a/src/com/hypixel/hytale/math/vector/Vector2d.java +++ /dev/null @@ -1,336 +0,0 @@ -package com.hypixel.hytale.math.vector; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.codec.Vector2dArrayCodec; -import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.util.TrigMathUtil; -import java.util.Random; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class Vector2d { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(Vector2d.class, Vector2d::new) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.DOUBLE), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.DOUBLE), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) - .addValidator(Validators.nonNull()) - .add() - .build(); - @Deprecated - public static final Vector2dArrayCodec AS_ARRAY_CODEC = new Vector2dArrayCodec(); - public static final Vector2d ZERO = new Vector2d(0.0, 0.0); - public static final Vector2d UP = new Vector2d(0.0, 1.0); - public static final Vector2d POS_Y = UP; - public static final Vector2d DOWN = new Vector2d(0.0, -1.0); - public static final Vector2d NEG_Y = DOWN; - public static final Vector2d RIGHT = new Vector2d(1.0, 0.0); - public static final Vector2d POS_X = RIGHT; - public static final Vector2d LEFT = new Vector2d(-1.0, 0.0); - public static final Vector2d NEG_X = LEFT; - public static final Vector2d ALL_ONES = new Vector2d(1.0, 1.0); - public static final Vector2d[] DIRECTIONS = new Vector2d[]{UP, DOWN, LEFT, RIGHT}; - public double x; - public double y; - private transient int hash; - - public Vector2d() { - this(0.0, 0.0); - } - - public Vector2d(@Nonnull Vector2d v) { - this(v.x, v.y); - } - - public Vector2d(double x, double y) { - this.x = x; - this.y = y; - this.hash = 0; - } - - public Vector2d(@Nonnull Random random, double length) { - float yaw = random.nextFloat() * (float) (Math.PI * 2); - float pitch = random.nextFloat() * (float) (Math.PI * 2); - this.x = TrigMathUtil.sin(pitch) * TrigMathUtil.cos(yaw); - this.y = TrigMathUtil.cos(pitch); - this.scale(length); - this.hash = 0; - } - - public double getX() { - return this.x; - } - - public void setX(double x) { - this.x = x; - this.hash = 0; - } - - public double getY() { - return this.y; - } - - public void setY(double y) { - this.y = y; - this.hash = 0; - } - - @Nonnull - public Vector2d assign(@Nonnull Vector2d v) { - this.x = v.x; - this.y = v.y; - this.hash = v.hash; - return this; - } - - @Nonnull - public Vector2d assign(double v) { - this.x = v; - this.y = v; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d assign(@Nonnull double[] v) { - this.x = v[0]; - this.y = v[1]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d assign(@Nonnull float[] v) { - this.x = v[0]; - this.y = v[1]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d assign(double x, double y) { - this.x = x; - this.y = y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d add(@Nonnull Vector2d v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d add(double x, double y) { - this.x += x; - this.y += y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d addScaled(@Nonnull Vector2d v, double s) { - this.x = this.x + v.x * s; - this.y = this.y + v.y * s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d subtract(@Nonnull Vector2d v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d subtract(double x, double y) { - this.x -= x; - this.y -= y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d negate() { - this.x = -this.x; - this.y = -this.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d scale(double s) { - this.x *= s; - this.y *= s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d scale(@Nonnull Vector2d p) { - this.x = this.x * p.x; - this.y = this.y * p.y; - this.hash = 0; - return this; - } - - public double dot(@Nonnull Vector2d other) { - return this.x * other.x + this.y * other.y; - } - - public double distanceTo(@Nonnull Vector2d v) { - return Math.sqrt(this.distanceSquaredTo(v)); - } - - public double distanceTo(double x, double y) { - return Math.sqrt(this.distanceSquaredTo(x, y)); - } - - public double distanceSquaredTo(@Nonnull Vector2d v) { - double x0 = v.x - this.x; - double y0 = v.y - this.y; - return x0 * x0 + y0 * y0; - } - - public double distanceSquaredTo(double x, double y) { - x -= this.x; - y -= this.y; - return x * x + y * y; - } - - @Nonnull - public Vector2d normalize() { - return this.setLength(1.0); - } - - public double length() { - return Math.sqrt(this.squaredLength()); - } - - public double squaredLength() { - return this.x * this.x + this.y * this.y; - } - - @Nonnull - public Vector2d setLength(double newLen) { - return this.scale(newLen / this.length()); - } - - @Nonnull - public Vector2d clampLength(double maxLength) { - double length = this.length(); - return maxLength > length ? this : this.scale(maxLength / length); - } - - @Nonnull - public Vector2d floor() { - this.x = Math.floor(this.x); - this.y = Math.floor(this.y); - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d ceil() { - this.x = Math.ceil(this.x); - this.y = Math.ceil(this.y); - this.hash = 0; - return this; - } - - @Nonnull - public Vector2d clipToZero(double epsilon) { - this.x = MathUtil.clipToZero(this.x, epsilon); - this.y = MathUtil.clipToZero(this.y, epsilon); - this.hash = 0; - return this; - } - - public boolean closeToZero(double epsilon) { - return MathUtil.closeToZero(this.x, epsilon) && MathUtil.closeToZero(this.y, epsilon); - } - - public boolean isFinite() { - return Double.isFinite(this.x) && Double.isFinite(this.y); - } - - @Nonnull - public Vector2d dropHash() { - this.hash = 0; - return this; - } - - @Nonnull - public static Vector2d max(@Nonnull Vector2d a, @Nonnull Vector2d b) { - return new Vector2d(Math.max(a.x, b.x), Math.max(a.y, b.y)); - } - - @Nonnull - public static Vector2d min(@Nonnull Vector2d a, @Nonnull Vector2d b) { - return new Vector2d(Math.min(a.x, b.x), Math.min(a.y, b.y)); - } - - @Nonnull - public static Vector2d lerp(@Nonnull Vector2d a, @Nonnull Vector2d b, double t) { - return lerpUnclamped(a, b, MathUtil.clamp(t, 0.0, 1.0)); - } - - @Nonnull - public static Vector2d lerpUnclamped(@Nonnull Vector2d a, @Nonnull Vector2d b, double t) { - return new Vector2d(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y)); - } - - public static double distance(double x1, double y1, double x2, double y2) { - return Math.sqrt(distanceSquared(x1, y1, x2, y2)); - } - - public static double distanceSquared(double x1, double y1, double x2, double y2) { - x1 -= x2; - y1 -= y2; - return x1 * x1 + y1 * y1; - } - - @Nonnull - public Vector2d clone() { - return new Vector2d(this.x, this.y); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - Vector2d vector2d = (Vector2d)o; - return vector2d.x == this.x && vector2d.y == this.y; - } else { - return false; - } - } - - @Override - public int hashCode() { - if (this.hash == 0) { - this.hash = (int)HashUtil.hash(Double.doubleToLongBits(this.x), Double.doubleToLongBits(this.y)); - } - - return this.hash; - } - - @Nonnull - @Override - public String toString() { - return "Vector2d{x=" + this.x + ", y=" + this.y + "}"; - } -} diff --git a/src/com/hypixel/hytale/math/vector/Vector2dUtil.java b/src/com/hypixel/hytale/math/vector/Vector2dUtil.java new file mode 100644 index 00000000..9ebe845a --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Vector2dUtil.java @@ -0,0 +1,40 @@ +package com.hypixel.hytale.math.vector; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.codec.Vector2dArrayCodec; +import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector2dc; + +public final class Vector2dUtil { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(Vector2d.class, Vector2d::new) + .metadata(UIDisplayMode.COMPACT) + .appendInherited(new KeyedCodec<>("X", Codec.DOUBLE), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Y", Codec.DOUBLE), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) + .addValidator(Validators.nonNull()) + .add() + .build(); + @Deprecated + public static final Vector2dArrayCodec AS_ARRAY_CODEC = new Vector2dArrayCodec(); + public static final Vector2dc ZERO = new Vector2d(0.0, 0.0); + public static final Vector2dc UP = new Vector2d(0.0, 1.0); + public static final Vector2dc POS_Y = UP; + public static final Vector2dc DOWN = new Vector2d(0.0, -1.0); + public static final Vector2dc NEG_Y = DOWN; + public static final Vector2dc RIGHT = new Vector2d(1.0, 0.0); + public static final Vector2dc POS_X = RIGHT; + public static final Vector2dc LEFT = new Vector2d(-1.0, 0.0); + public static final Vector2dc NEG_X = LEFT; + public static final Vector2dc ALL_ONES = new Vector2d(1.0, 1.0); + public static final Vector2dc[] DIRECTIONS = new Vector2dc[]{UP, DOWN, LEFT, RIGHT}; + + private Vector2dUtil() { + } +} diff --git a/src/com/hypixel/hytale/math/vector/Vector2fUtil.java b/src/com/hypixel/hytale/math/vector/Vector2fUtil.java new file mode 100644 index 00000000..c60f1c5f --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Vector2fUtil.java @@ -0,0 +1,27 @@ +package com.hypixel.hytale.math.vector; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; +import org.joml.Vector2f; +import org.joml.Vector2fc; + +public final class Vector2fUtil { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(Vector2f.class, Vector2f::new) + .metadata(UIDisplayMode.COMPACT) + .appendInherited(new KeyedCodec<>("X", Codec.FLOAT), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Y", Codec.FLOAT), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) + .addValidator(Validators.nonNull()) + .add() + .build(); + public static final Vector2fc ZERO = new Vector2f(0.0F, 0.0F); + + private Vector2fUtil() { + } +} diff --git a/src/com/hypixel/hytale/math/vector/Vector2i.java b/src/com/hypixel/hytale/math/vector/Vector2i.java deleted file mode 100644 index 102eedb2..00000000 --- a/src/com/hypixel/hytale/math/vector/Vector2i.java +++ /dev/null @@ -1,273 +0,0 @@ -package com.hypixel.hytale.math.vector; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.util.HashUtil; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class Vector2i { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(Vector2i.class, Vector2i::new) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.INTEGER), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.INTEGER), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) - .addValidator(Validators.nonNull()) - .add() - .build(); - public static final Vector2i ZERO = new Vector2i(0, 0); - public static final Vector2i UP = new Vector2i(0, 1); - public static final Vector2i POS_Y = UP; - public static final Vector2i DOWN = new Vector2i(0, -1); - public static final Vector2i NEG_Y = DOWN; - public static final Vector2i RIGHT = new Vector2i(1, 0); - public static final Vector2i POS_X = RIGHT; - public static final Vector2i LEFT = new Vector2i(-1, 0); - public static final Vector2i NEG_X = LEFT; - public static final Vector2i ALL_ONES = new Vector2i(1, 1); - public static final Vector2i[] DIRECTIONS = new Vector2i[]{UP, DOWN, LEFT, RIGHT}; - public int x; - public int y; - private transient int hash; - - public Vector2i() { - this(0, 0); - } - - public Vector2i(@Nonnull Vector2i v) { - this(v.x, v.y); - } - - public Vector2i(int x, int y) { - this.x = x; - this.y = y; - this.hash = 0; - } - - public int getX() { - return this.x; - } - - public void setX(int x) { - this.x = x; - this.hash = 0; - } - - public int getY() { - return this.y; - } - - public void setY(int y) { - this.y = y; - this.hash = 0; - } - - @Nonnull - public Vector2i assign(@Nonnull Vector2i v) { - this.x = v.x; - this.y = v.y; - this.hash = v.hash; - return this; - } - - @Nonnull - public Vector2i assign(int v) { - this.x = v; - this.y = v; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i assign(@Nonnull int[] v) { - this.x = v[0]; - this.y = v[1]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i assign(int x, int y) { - this.x = x; - this.y = y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i add(@Nonnull Vector2i v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i add(int x, int y) { - this.x += x; - this.y += y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i addScaled(@Nonnull Vector2i v, int s) { - this.x = this.x + v.x * s; - this.y = this.y + v.y * s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i subtract(@Nonnull Vector2i v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i subtract(int x, int y) { - this.x -= x; - this.y -= y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i negate() { - this.x = -this.x; - this.y = -this.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i scale(int s) { - this.x *= s; - this.y *= s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i scale(@Nonnull Vector2i p) { - this.x = this.x * p.x; - this.y = this.y * p.y; - this.hash = 0; - return this; - } - - public int dot(@Nonnull Vector2i other) { - return this.x * other.x + this.y * other.y; - } - - public double distanceTo(@Nonnull Vector2i v) { - return Math.sqrt(this.distanceSquaredTo(v)); - } - - public double distanceTo(int x, int y) { - return Math.sqrt(this.distanceSquaredTo(x, y)); - } - - public int distanceSquaredTo(@Nonnull Vector2i v) { - int x0 = v.x - this.x; - int y0 = v.y - this.y; - return x0 * x0 + y0 * y0; - } - - public int distanceSquaredTo(int x, int y) { - x -= this.x; - y -= this.y; - return x * x + y * y; - } - - @Nonnull - public Vector2i normalize() { - return this.setLength(1); - } - - public double length() { - return Math.sqrt(this.squaredLength()); - } - - public double squaredLength() { - return this.x * this.x + this.y * this.y; - } - - @Nonnull - public Vector2i setLength(int newLen) { - double scale = newLen / this.length(); - this.x = (int)(this.x * scale); - this.y = (int)(this.y * scale); - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i clampLength(int maxLength) { - double length = this.length(); - if (maxLength > length) { - return this; - } else { - double scale = maxLength / length; - this.x = (int)(this.x * scale); - this.y = (int)(this.y * scale); - this.hash = 0; - return this; - } - } - - @Nonnull - public Vector2i dropHash() { - this.hash = 0; - return this; - } - - @Nonnull - public Vector2i clone() { - return new Vector2i(this.x, this.y); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - Vector2i vector2i = (Vector2i)o; - return vector2i.x == this.x && vector2i.y == this.y; - } else { - return false; - } - } - - @Override - public int hashCode() { - if (this.hash == 0) { - this.hash = (int)HashUtil.hash(this.x, this.y); - } - - return this.hash; - } - - @Nonnull - @Override - public String toString() { - return "Vector2i{x=" + this.x + ", y=" + this.y + "}"; - } - - @Nonnull - public static Vector2i max(@Nonnull Vector2i a, @Nonnull Vector2i b) { - return new Vector2i(Math.max(a.x, b.x), Math.max(a.y, b.y)); - } - - @Nonnull - public static Vector2i min(@Nonnull Vector2i a, @Nonnull Vector2i b) { - return new Vector2i(Math.min(a.x, b.x), Math.min(a.y, b.y)); - } -} diff --git a/src/com/hypixel/hytale/math/vector/Vector2iUtil.java b/src/com/hypixel/hytale/math/vector/Vector2iUtil.java new file mode 100644 index 00000000..0f1da052 --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Vector2iUtil.java @@ -0,0 +1,37 @@ +package com.hypixel.hytale.math.vector; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; +import org.joml.Vector2i; +import org.joml.Vector2ic; + +public final class Vector2iUtil { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(Vector2i.class, Vector2i::new) + .metadata(UIDisplayMode.COMPACT) + .appendInherited(new KeyedCodec<>("X", Codec.INTEGER), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Y", Codec.INTEGER), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) + .addValidator(Validators.nonNull()) + .add() + .build(); + public static final Vector2ic ZERO = new Vector2i(0, 0); + public static final Vector2ic UP = new Vector2i(0, 1); + public static final Vector2ic POS_Y = UP; + public static final Vector2ic DOWN = new Vector2i(0, -1); + public static final Vector2ic NEG_Y = DOWN; + public static final Vector2ic RIGHT = new Vector2i(1, 0); + public static final Vector2ic POS_X = RIGHT; + public static final Vector2ic LEFT = new Vector2i(-1, 0); + public static final Vector2ic NEG_X = LEFT; + public static final Vector2ic ALL_ONES = new Vector2i(1, 1); + public static final Vector2ic[] DIRECTIONS = new Vector2ic[]{UP, DOWN, LEFT, RIGHT}; + + private Vector2iUtil() { + } +} diff --git a/src/com/hypixel/hytale/math/vector/Vector2l.java b/src/com/hypixel/hytale/math/vector/Vector2l.java deleted file mode 100644 index 31450e5c..00000000 --- a/src/com/hypixel/hytale/math/vector/Vector2l.java +++ /dev/null @@ -1,269 +0,0 @@ -package com.hypixel.hytale.math.vector; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.util.HashUtil; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class Vector2l { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(Vector2l.class, Vector2l::new) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.LONG), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.LONG), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) - .addValidator(Validators.nonNull()) - .add() - .build(); - public static final Vector2l ZERO = new Vector2l(0L, 0L); - public static final Vector2l UP = new Vector2l(0L, 1L); - public static final Vector2l POS_Y = UP; - public static final Vector2l DOWN = new Vector2l(0L, -1L); - public static final Vector2l NEG_Y = DOWN; - public static final Vector2l RIGHT = new Vector2l(1L, 0L); - public static final Vector2l POS_X = RIGHT; - public static final Vector2l LEFT = new Vector2l(-1L, 0L); - public static final Vector2l NEG_X = LEFT; - public static final Vector2l ALL_ONES = new Vector2l(1L, 1L); - public static final Vector2l[] DIRECTIONS = new Vector2l[]{UP, DOWN, LEFT, RIGHT}; - public long x; - public long y; - private transient int hash; - - public Vector2l() { - this(0L, 0L); - } - - public Vector2l(@Nonnull Vector2l v) { - this(v.x, v.y); - } - - public Vector2l(long x, long y) { - this.x = x; - this.y = y; - this.hash = 0; - } - - public long getX() { - return this.x; - } - - public void setX(long x) { - this.x = x; - this.hash = 0; - } - - public long getY() { - return this.y; - } - - public void setY(long y) { - this.y = y; - this.hash = 0; - } - - @Nonnull - public Vector2l assign(@Nonnull Vector2l v) { - this.x = v.x; - this.y = v.y; - this.hash = v.hash; - return this; - } - - @Nonnull - public Vector2l assign(long v) { - this.x = v; - this.y = v; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l assign(@Nonnull long[] v) { - this.x = v[0]; - this.y = v[1]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l assign(long x, long y) { - this.x = x; - this.y = y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l add(@Nonnull Vector2l v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l add(long x, long y) { - this.x += x; - this.y += y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l addScaled(@Nonnull Vector2l v, long s) { - this.x = this.x + v.x * s; - this.y = this.y + v.y * s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l subtract(@Nonnull Vector2l v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l subtract(long x, long y) { - this.x -= x; - this.y -= y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l negate() { - this.x = -this.x; - this.y = -this.y; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l scale(long s) { - this.x *= s; - this.y *= s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l scale(double s) { - this.x = (long)(this.x * s); - this.y = (long)(this.y * s); - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l scale(@Nonnull Vector2l p) { - this.x = this.x * p.x; - this.y = this.y * p.y; - this.hash = 0; - return this; - } - - public long dot(@Nonnull Vector2l other) { - return this.x * other.x + this.y * other.y; - } - - public double distanceTo(@Nonnull Vector2l v) { - return Math.sqrt(this.distanceSquaredTo(v)); - } - - public double distanceTo(long x, long y) { - return Math.sqrt(this.distanceSquaredTo(x, y)); - } - - public long distanceSquaredTo(@Nonnull Vector2l v) { - long x0 = v.x - this.x; - long y0 = v.y - this.y; - return x0 * x0 + y0 * y0; - } - - public long distanceSquaredTo(long x, long y) { - long dx = x - this.x; - long dy = y - this.y; - return dx * dx + dy * dy; - } - - @Nonnull - public Vector2l normalize() { - return this.setLength(1L); - } - - public double length() { - return Math.sqrt(this.squaredLength()); - } - - public long squaredLength() { - return this.x * this.x + this.y * this.y; - } - - @Nonnull - public Vector2l setLength(long newLen) { - return this.scale(newLen / this.length()); - } - - @Nonnull - public Vector2l clampLength(long maxLength) { - double length = this.length(); - return maxLength > length ? this : this.scale(maxLength / length); - } - - @Nonnull - public Vector2l dropHash() { - this.hash = 0; - return this; - } - - @Nonnull - public Vector2l clone() { - return new Vector2l(this.x, this.y); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - Vector2l vector2l = (Vector2l)o; - return vector2l.x == this.x && vector2l.y == this.y; - } else { - return false; - } - } - - @Override - public int hashCode() { - if (this.hash == 0) { - this.hash = (int)HashUtil.hash(this.x, this.y); - } - - return this.hash; - } - - @Nonnull - @Override - public String toString() { - return "Vector2l{x=" + this.x + ", y=" + this.y + "}"; - } - - @Nonnull - public static Vector2l max(@Nonnull Vector2l a, @Nonnull Vector2l b) { - return new Vector2l(Math.max(a.x, b.x), Math.max(a.y, b.y)); - } - - @Nonnull - public static Vector2l min(@Nonnull Vector2l a, @Nonnull Vector2l b) { - return new Vector2l(Math.min(a.x, b.x), Math.min(a.y, b.y)); - } -} diff --git a/src/com/hypixel/hytale/math/vector/Vector3LUtil.java b/src/com/hypixel/hytale/math/vector/Vector3LUtil.java new file mode 100644 index 00000000..3fca1d96 --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Vector3LUtil.java @@ -0,0 +1,28 @@ +package com.hypixel.hytale.math.vector; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; +import org.joml.Vector3L; + +public final class Vector3LUtil { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(Vector3L.class, Vector3L::new) + .metadata(UIDisplayMode.COMPACT) + .appendInherited(new KeyedCodec<>("X", Codec.LONG), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Y", Codec.LONG), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Z", Codec.LONG), (o, i) -> o.z = i, o -> o.z, (o, p) -> o.z = p.z) + .addValidator(Validators.nonNull()) + .add() + .build(); + + private Vector3LUtil() { + } +} diff --git a/src/com/hypixel/hytale/math/vector/Vector3d.java b/src/com/hypixel/hytale/math/vector/Vector3d.java deleted file mode 100644 index bcda467d..00000000 --- a/src/com/hypixel/hytale/math/vector/Vector3d.java +++ /dev/null @@ -1,559 +0,0 @@ -package com.hypixel.hytale.math.vector; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.codec.Vector3dArrayCodec; -import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.util.TrigMathUtil; -import java.util.Random; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class Vector3d { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(Vector3d.class, Vector3d::new) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.DOUBLE), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.DOUBLE), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Z", Codec.DOUBLE), (o, i) -> o.z = i, o -> o.z, (o, p) -> o.z = p.z) - .addValidator(Validators.nonNull()) - .add() - .build(); - @Deprecated - public static final Vector3dArrayCodec AS_ARRAY_CODEC = new Vector3dArrayCodec(); - public static final Vector3d ZERO = new Vector3d(0.0, 0.0, 0.0); - public static final Vector3d UP = new Vector3d(0.0, 1.0, 0.0); - public static final Vector3d POS_Y = UP; - public static final Vector3d DOWN = new Vector3d(0.0, -1.0, 0.0); - public static final Vector3d NEG_Y = DOWN; - public static final Vector3d FORWARD = new Vector3d(0.0, 0.0, -1.0); - public static final Vector3d NEG_Z = FORWARD; - public static final Vector3d NORTH = FORWARD; - public static final Vector3d BACKWARD = new Vector3d(0.0, 0.0, 1.0); - public static final Vector3d POS_Z = BACKWARD; - public static final Vector3d SOUTH = BACKWARD; - public static final Vector3d RIGHT = new Vector3d(1.0, 0.0, 0.0); - public static final Vector3d POS_X = RIGHT; - public static final Vector3d EAST = RIGHT; - public static final Vector3d LEFT = new Vector3d(-1.0, 0.0, 0.0); - public static final Vector3d NEG_X = LEFT; - public static final Vector3d WEST = LEFT; - public static final Vector3d ALL_ONES = new Vector3d(1.0, 1.0, 1.0); - public static final Vector3d MIN = new Vector3d(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE); - public static final Vector3d MAX = new Vector3d(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); - public static final Vector3d[] BLOCK_SIDES = new Vector3d[]{UP, DOWN, FORWARD, BACKWARD, LEFT, RIGHT}; - public static final Vector3d[] BLOCK_EDGES = new Vector3d[]{ - add(UP, FORWARD), - add(DOWN, FORWARD), - add(UP, BACKWARD), - add(DOWN, BACKWARD), - add(UP, LEFT), - add(DOWN, LEFT), - add(UP, RIGHT), - add(DOWN, RIGHT), - add(FORWARD, LEFT), - add(FORWARD, RIGHT), - add(BACKWARD, LEFT), - add(BACKWARD, RIGHT) - }; - public static final Vector3d[] BLOCK_CORNERS = new Vector3d[]{ - add(UP, FORWARD, LEFT), - add(UP, FORWARD, RIGHT), - add(DOWN, FORWARD, LEFT), - add(DOWN, FORWARD, RIGHT), - add(UP, BACKWARD, LEFT), - add(UP, BACKWARD, RIGHT), - add(DOWN, BACKWARD, LEFT), - add(DOWN, BACKWARD, RIGHT) - }; - public static final Vector3d[][] BLOCK_PARTS = new Vector3d[][]{BLOCK_SIDES, BLOCK_EDGES, BLOCK_CORNERS}; - public static final Vector3d[] CARDINAL_DIRECTIONS = new Vector3d[]{NORTH, SOUTH, EAST, WEST}; - public double x; - public double y; - public double z; - private transient int hash; - - public Vector3d() { - this(0.0, 0.0, 0.0); - } - - public Vector3d(@Nonnull Vector3d v) { - this(v.x, v.y, v.z); - } - - public Vector3d(@Nonnull Vector3i v) { - this(v.x, v.y, v.z); - } - - public Vector3d(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - this.hash = 0; - } - - public Vector3d(float yaw, float pitch) { - this(); - this.assign(yaw, pitch); - } - - public Vector3d(@Nonnull Random random, double length) { - this(random.nextFloat() * (float) (Math.PI * 2), random.nextFloat() * (float) (Math.PI * 2)); - this.scale(length); - } - - public double getX() { - return this.x; - } - - public void setX(double x) { - this.x = x; - this.hash = 0; - } - - public double getY() { - return this.y; - } - - public void setY(double y) { - this.y = y; - this.hash = 0; - } - - public double getZ() { - return this.z; - } - - public void setZ(double z) { - this.z = z; - this.hash = 0; - } - - @Nonnull - public Vector3d assign(@Nonnull Vector3d v) { - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.hash = v.hash; - return this; - } - - @Nonnull - public Vector3d assign(double v) { - this.x = v; - this.y = v; - this.z = v; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d assign(@Nonnull double[] v) { - this.x = v[0]; - this.y = v[1]; - this.z = v[2]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d assign(@Nonnull float[] v) { - this.x = v[0]; - this.y = v[1]; - this.z = v[2]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d assign(double yaw, double pitch) { - double len = TrigMathUtil.cos(pitch); - double x = len * -TrigMathUtil.sin(yaw); - double y = TrigMathUtil.sin(pitch); - double z = len * -TrigMathUtil.cos(yaw); - return this.assign(x, y, z); - } - - @Nonnull - public Vector3d assign(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d add(@Nonnull Vector3d v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.z = this.z + v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d add(@Nonnull Vector3i v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.z = this.z + v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d add(double x, double y, double z) { - this.x += x; - this.y += y; - this.z += z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d add(double value) { - this.x += value; - this.y += value; - this.z += value; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d addScaled(@Nonnull Vector3d v, double s) { - this.x = this.x + v.x * s; - this.y = this.y + v.y * s; - this.z = this.z + v.z * s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d subtract(@Nonnull Vector3d v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.z = this.z - v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d subtract(@Nonnull Vector3i v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.z = this.z - v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d subtract(double x, double y, double z) { - this.x -= x; - this.y -= y; - this.z -= z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d subtract(double value) { - this.x -= value; - this.y -= value; - this.z -= value; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d negate() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d scale(double s) { - this.x *= s; - this.y *= s; - this.z *= s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d scale(@Nonnull Vector3d p) { - this.x = this.x * p.x; - this.y = this.y * p.y; - this.z = this.z * p.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d cross(@Nonnull Vector3d v) { - double x0 = this.y * v.z - this.z * v.y; - double y0 = this.z * v.x - this.x * v.z; - double z0 = this.x * v.y - this.y * v.x; - return new Vector3d(x0, y0, z0); - } - - @Nonnull - public Vector3d cross(@Nonnull Vector3d v, @Nonnull Vector3d res) { - res.assign(this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x); - return this; - } - - public double dot(@Nonnull Vector3d other) { - return this.x * other.x + this.y * other.y + this.z * other.z; - } - - public double distanceTo(@Nonnull Vector3d v) { - return Math.sqrt(this.distanceSquaredTo(v)); - } - - public double distanceTo(@Nonnull Vector3i v) { - return Math.sqrt(this.distanceSquaredTo(v)); - } - - public double distanceTo(double x, double y, double z) { - return Math.sqrt(this.distanceSquaredTo(x, y, z)); - } - - public double distanceSquaredTo(@Nonnull Vector3d v) { - double x0 = v.x - this.x; - double y0 = v.y - this.y; - double z0 = v.z - this.z; - return x0 * x0 + y0 * y0 + z0 * z0; - } - - public double distanceSquaredTo(@Nonnull Vector3i v) { - double x0 = v.x - this.x; - double y0 = v.y - this.y; - double z0 = v.z - this.z; - return x0 * x0 + y0 * y0 + z0 * z0; - } - - public double distanceSquaredTo(double x, double y, double z) { - x -= this.x; - y -= this.y; - z -= this.z; - return x * x + y * y + z * z; - } - - @Nonnull - public Vector3d normalize() { - return this.setLength(1.0); - } - - public double length() { - return Math.sqrt(this.squaredLength()); - } - - public double squaredLength() { - return this.x * this.x + this.y * this.y + this.z * this.z; - } - - @Nonnull - public Vector3d setLength(double newLen) { - return this.scale(newLen / this.length()); - } - - @Nonnull - public Vector3d clampLength(double maxLength) { - double length = this.length(); - return maxLength > length ? this : this.scale(maxLength / length); - } - - @Nonnull - public Vector3d rotateX(float angle) { - double cos = TrigMathUtil.cos(angle); - double sin = TrigMathUtil.sin(angle); - double cy = this.y * cos - this.z * sin; - double cz = this.y * sin + this.z * cos; - this.y = cy; - this.z = cz; - return this; - } - - @Nonnull - public Vector3d rotateY(float angle) { - double cos = TrigMathUtil.cos(angle); - double sin = TrigMathUtil.sin(angle); - double cx = this.x * cos + this.z * sin; - double cz = this.x * -sin + this.z * cos; - this.x = cx; - this.z = cz; - return this; - } - - @Nonnull - public Vector3d rotateZ(float angle) { - double cos = TrigMathUtil.cos(angle); - double sin = TrigMathUtil.sin(angle); - double cx = this.x * cos - this.y * sin; - double cy = this.x * sin + this.y * cos; - this.x = cx; - this.y = cy; - return this; - } - - @Nonnull - public Vector3d floor() { - this.x = MathUtil.fastFloor(this.x); - this.y = MathUtil.fastFloor(this.y); - this.z = MathUtil.fastFloor(this.z); - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d ceil() { - this.x = MathUtil.fastCeil(this.x); - this.y = MathUtil.fastCeil(this.y); - this.z = MathUtil.fastCeil(this.z); - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d clipToZero(double epsilon) { - this.x = MathUtil.clipToZero(this.x, epsilon); - this.y = MathUtil.clipToZero(this.y, epsilon); - this.z = MathUtil.clipToZero(this.z, epsilon); - this.hash = 0; - return this; - } - - public boolean closeToZero(double epsilon) { - return MathUtil.closeToZero(this.x, epsilon) && MathUtil.closeToZero(this.y, epsilon) && MathUtil.closeToZero(this.z, epsilon); - } - - public boolean isInside(int x, int y, int z) { - double dx = this.x - x; - double dy = this.y - y; - double dz = this.z - z; - return dx >= 0.0 && dx < 1.0 && dy >= 0.0 && dy < 1.0 && dz >= 0.0 && dz < 1.0; - } - - public boolean isFinite() { - return Double.isFinite(this.x) && Double.isFinite(this.y) && Double.isFinite(this.z); - } - - @Nonnull - public Vector3d dropHash() { - this.hash = 0; - return this; - } - - @Nonnull - public Vector3d clone() { - return new Vector3d(this.x, this.y, this.z); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - Vector3d vector3d = (Vector3d)o; - return vector3d.x == this.x && vector3d.y == this.y && vector3d.z == this.z; - } else { - return false; - } - } - - public boolean equals(@Nullable Vector3d o) { - return o == null ? false : o.x == this.x && o.y == this.y && o.z == this.z; - } - - @Override - public int hashCode() { - if (this.hash == 0) { - this.hash = (int)HashUtil.hash(Double.doubleToLongBits(this.x), Double.doubleToLongBits(this.y), Double.doubleToLongBits(this.z)); - } - - return this.hash; - } - - @Nonnull - @Override - public String toString() { - return "Vector3d{x=" + this.x + ", y=" + this.y + ", z=" + this.z + "}"; - } - - @Nonnull - public static Vector3d max(@Nonnull Vector3d a, @Nonnull Vector3d b) { - return new Vector3d(Math.max(a.x, b.x), Math.max(a.y, b.y), Math.max(a.z, b.z)); - } - - @Nonnull - public static Vector3d min(@Nonnull Vector3d a, @Nonnull Vector3d b) { - return new Vector3d(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z)); - } - - @Nonnull - public static Vector3d lerp(@Nonnull Vector3d a, @Nonnull Vector3d b, double t) { - return lerpUnclamped(a, b, MathUtil.clamp(t, 0.0, 1.0)); - } - - @Nonnull - public static Vector3d lerpUnclamped(@Nonnull Vector3d a, @Nonnull Vector3d b, double t) { - return new Vector3d(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y), a.z + t * (b.z - a.z)); - } - - @Nonnull - public static Vector3d directionTo(@Nonnull Vector3d from, @Nonnull Vector3d to) { - return to.clone().subtract(from).normalize(); - } - - @Nonnull - public static Vector3d directionTo(@Nonnull Vector3i from, @Nonnull Vector3d to) { - return to.clone().subtract(from).normalize(); - } - - public static double distance(double x1, double y1, double z1, double x2, double y2, double z2) { - return Math.sqrt(distanceSquared(x1, y1, z1, x2, y2, z2)); - } - - public static double distanceSquared(double x1, double y1, double z1, double x2, double y2, double z2) { - x1 -= x2; - y1 -= y2; - z1 -= z2; - return x1 * x1 + y1 * y1 + z1 * z1; - } - - @Nonnull - public static Vector3d add(@Nonnull Vector3d one, @Nonnull Vector3d two) { - return new Vector3d().add(one).add(two); - } - - @Nonnull - public static Vector3d add(@Nonnull Vector3d one, @Nonnull Vector3d two, @Nonnull Vector3d three) { - return new Vector3d().add(one).add(two).add(three); - } - - @Nonnull - public static String formatShortString(@Nullable Vector3d v) { - return v == null ? "" : v.x + "/" + v.y + "/" + v.z; - } - - @Nonnull - public Vector3i toVector3i() { - return new Vector3i(MathUtil.floor(this.x), MathUtil.floor(this.y), MathUtil.floor(this.z)); - } - - @Nonnull - public Vector3f toVector3f() { - return new Vector3f((float)this.x, (float)this.y, (float)this.z); - } -} diff --git a/src/com/hypixel/hytale/math/vector/Vector3dUtil.java b/src/com/hypixel/hytale/math/vector/Vector3dUtil.java new file mode 100644 index 00000000..5ff05721 --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Vector3dUtil.java @@ -0,0 +1,89 @@ +package com.hypixel.hytale.math.vector; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.codec.Vector3dArrayCodec; +import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.math.util.TrigMathUtil; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; + +public final class Vector3dUtil { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(Vector3d.class, Vector3d::new) + .metadata(UIDisplayMode.COMPACT) + .appendInherited(new KeyedCodec<>("X", Codec.DOUBLE), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Y", Codec.DOUBLE), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Z", Codec.DOUBLE), (o, i) -> o.z = i, o -> o.z, (o, p) -> o.z = p.z) + .addValidator(Validators.nonNull()) + .add() + .build(); + @Deprecated + public static final Vector3dArrayCodec AS_ARRAY_CODEC = new Vector3dArrayCodec(); + public static final Vector3dc ZERO = new Vector3d(0.0, 0.0, 0.0); + public static final Vector3dc UP = new Vector3d(0.0, 1.0, 0.0); + public static final Vector3dc POS_Y = UP; + public static final Vector3dc DOWN = new Vector3d(0.0, -1.0, 0.0); + public static final Vector3dc NEG_Y = DOWN; + public static final Vector3dc FORWARD = new Vector3d(0.0, 0.0, -1.0); + public static final Vector3dc NEG_Z = FORWARD; + public static final Vector3dc NORTH = FORWARD; + public static final Vector3dc BACKWARD = new Vector3d(0.0, 0.0, 1.0); + public static final Vector3dc POS_Z = BACKWARD; + public static final Vector3dc SOUTH = BACKWARD; + public static final Vector3dc RIGHT = new Vector3d(1.0, 0.0, 0.0); + public static final Vector3dc POS_X = RIGHT; + public static final Vector3dc EAST = RIGHT; + public static final Vector3dc LEFT = new Vector3d(-1.0, 0.0, 0.0); + public static final Vector3dc NEG_X = LEFT; + public static final Vector3dc WEST = LEFT; + public static final Vector3dc ALL_ONES = new Vector3d(1.0, 1.0, 1.0); + public static final Vector3dc MIN = new Vector3d(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE); + public static final Vector3dc MAX = new Vector3d(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + + private Vector3dUtil() { + } + + public static Vector3d setYawPitch(double yaw, double pitch, @Nonnull Vector3d dest) { + float len = TrigMathUtil.cos(pitch); + float x = len * -TrigMathUtil.sin(yaw); + float y = TrigMathUtil.sin(pitch); + float z = len * -TrigMathUtil.cos(yaw); + return dest.set(x, y, z); + } + + @Nonnull + public static Vector3d directionTo(@Nonnull Vector3dc from, @Nonnull Vector3dc to) { + return new Vector3d(to).sub(from).normalize(); + } + + @Nonnull + public static String formatShortString(@Nullable Vector3dc v) { + return v == null ? "" : v.x() + "/" + v.y() + "/" + v.z(); + } + + public static Vector3i toVector3i(@Nonnull Vector3d v) { + return new Vector3i(MathUtil.floor(v.x), MathUtil.floor(v.y), MathUtil.floor(v.z)); + } + + public static Vector3d clipToZero(Vector3d vec, double epsilon) { + vec.x = MathUtil.clipToZero(vec.x, epsilon); + vec.y = MathUtil.clipToZero(vec.y, epsilon); + vec.z = MathUtil.clipToZero(vec.z, epsilon); + return vec; + } + + public static boolean closeToZero(@Nonnull Vector3d vec, double epsilon) { + return MathUtil.closeToZero(vec.x, epsilon) && MathUtil.closeToZero(vec.y, epsilon) && MathUtil.closeToZero(vec.z, epsilon); + } +} diff --git a/src/com/hypixel/hytale/math/vector/Vector3f.java b/src/com/hypixel/hytale/math/vector/Vector3f.java deleted file mode 100644 index cbcc899f..00000000 --- a/src/com/hypixel/hytale/math/vector/Vector3f.java +++ /dev/null @@ -1,616 +0,0 @@ -package com.hypixel.hytale.math.vector; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; -import com.hypixel.hytale.math.Axis; -import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.util.TrigMathUtil; -import java.util.Random; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class Vector3f { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(Vector3f.class, () -> new Vector3f(Float.NaN, Float.NaN, Float.NaN)) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.FLOAT), (o, i) -> o.x = i, o -> Float.isNaN(o.x) ? null : o.x, (o, p) -> o.x = p.x) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.FLOAT), (o, i) -> o.y = i, o -> Float.isNaN(o.y) ? null : o.y, (o, p) -> o.y = p.y) - .add() - .appendInherited(new KeyedCodec<>("Z", Codec.FLOAT), (o, i) -> o.z = i, o -> Float.isNaN(o.z) ? null : o.z, (o, p) -> o.z = p.z) - .add() - .build(); - @Nonnull - public static final BuilderCodec ROTATION = BuilderCodec.builder(Vector3f.class, () -> new Vector3f(Float.NaN, Float.NaN, Float.NaN)) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("Pitch", Codec.FLOAT), (o, i) -> o.x = i, o -> Float.isNaN(o.x) ? null : o.x, (o, p) -> o.x = p.x) - .add() - .appendInherited(new KeyedCodec<>("Yaw", Codec.FLOAT), (o, i) -> o.y = i, o -> Float.isNaN(o.y) ? null : o.y, (o, p) -> o.y = p.y) - .add() - .appendInherited(new KeyedCodec<>("Roll", Codec.FLOAT), (o, i) -> o.z = i, o -> Float.isNaN(o.z) ? null : o.z, (o, p) -> o.z = p.z) - .add() - .build(); - public static final Vector3f ZERO = new Vector3f(0.0F, 0.0F, 0.0F); - public static final Vector3f UP = new Vector3f(0.0F, 1.0F, 0.0F); - public static final Vector3f POS_Y = UP; - public static final Vector3f DOWN = new Vector3f(0.0F, -1.0F, 0.0F); - public static final Vector3f NEG_Y = DOWN; - public static final Vector3f FORWARD = new Vector3f(0.0F, 0.0F, -1.0F); - public static final Vector3f NEG_Z = FORWARD; - public static final Vector3f NORTH = FORWARD; - public static final Vector3f BACKWARD = new Vector3f(0.0F, 0.0F, 1.0F); - public static final Vector3f POS_Z = BACKWARD; - public static final Vector3f SOUTH = BACKWARD; - public static final Vector3f RIGHT = new Vector3f(1.0F, 0.0F, 0.0F); - public static final Vector3f POS_X = RIGHT; - public static final Vector3f EAST = RIGHT; - public static final Vector3f LEFT = new Vector3f(-1.0F, 0.0F, 0.0F); - public static final Vector3f NEG_X = LEFT; - public static final Vector3f WEST = LEFT; - public static final Vector3f ALL_ONES = new Vector3f(1.0F, 1.0F, 1.0F); - public static final Vector3f MIN = new Vector3f(-Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE); - public static final Vector3f MAX = new Vector3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE); - public static final Vector3f NaN = new Vector3f(Float.NaN, Float.NaN, Float.NaN); - public static final Vector3f[] BLOCK_SIDES = new Vector3f[]{UP, DOWN, FORWARD, BACKWARD, LEFT, RIGHT}; - public static final Vector3f[] BLOCK_EDGES = new Vector3f[]{ - add(UP, FORWARD), - add(DOWN, FORWARD), - add(UP, BACKWARD), - add(DOWN, BACKWARD), - add(UP, LEFT), - add(DOWN, LEFT), - add(UP, RIGHT), - add(DOWN, RIGHT), - add(FORWARD, LEFT), - add(FORWARD, RIGHT), - add(BACKWARD, LEFT), - add(BACKWARD, RIGHT) - }; - public static final Vector3f[] BLOCK_CORNERS = new Vector3f[]{ - add(UP, FORWARD, LEFT), - add(UP, FORWARD, RIGHT), - add(DOWN, FORWARD, LEFT), - add(DOWN, FORWARD, RIGHT), - add(UP, BACKWARD, LEFT), - add(UP, BACKWARD, RIGHT), - add(DOWN, BACKWARD, LEFT), - add(DOWN, BACKWARD, RIGHT) - }; - public static final Vector3f[][] BLOCK_PARTS = new Vector3f[][]{BLOCK_SIDES, BLOCK_EDGES, BLOCK_CORNERS}; - public static final Vector3f[] CARDINAL_DIRECTIONS = new Vector3f[]{NORTH, SOUTH, EAST, WEST}; - public float x; - public float y; - public float z; - private transient int hash; - - public Vector3f() { - this(0.0F, 0.0F, 0.0F); - } - - public Vector3f(@Nonnull Vector3f v) { - this(v.x, v.y, v.z); - } - - public Vector3f(@Nonnull Vector3i v) { - this(v.x, v.y, v.z); - } - - public Vector3f(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - this.hash = 0; - } - - public Vector3f(float yaw, float pitch) { - this(); - this.assign(yaw, pitch); - } - - public Vector3f(@Nonnull Random random, float length) { - this(random.nextFloat() * (float) (Math.PI * 2), random.nextFloat() * (float) (Math.PI * 2)); - this.scale(length); - } - - public float getX() { - return this.x; - } - - public float getPitch() { - return this.x; - } - - public void setX(float x) { - this.x = x; - this.hash = 0; - } - - public void setPitch(float pitch) { - this.x = pitch; - this.hash = 0; - } - - public float getY() { - return this.y; - } - - public float getYaw() { - return this.y; - } - - public void setY(float y) { - this.y = y; - this.hash = 0; - } - - public void setYaw(float yaw) { - this.y = yaw; - this.hash = 0; - } - - public float getZ() { - return this.z; - } - - public float getRoll() { - return this.z; - } - - public void setZ(float z) { - this.z = z; - this.hash = 0; - } - - public void setRoll(float roll) { - this.z = roll; - this.hash = 0; - } - - @Nonnull - public Vector3f assign(@Nonnull Vector3f v) { - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.hash = v.hash; - return this; - } - - @Nonnull - public Vector3f assign(float v) { - this.x = v; - this.y = v; - this.z = v; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f assign(@Nonnull float[] v) { - this.x = v[0]; - this.y = v[1]; - this.z = v[2]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f assign(float yaw, float pitch) { - float len = TrigMathUtil.cos(pitch); - float x = len * -TrigMathUtil.sin(yaw); - float y = TrigMathUtil.sin(pitch); - float z = len * -TrigMathUtil.cos(yaw); - return this.assign(x, y, z); - } - - @Nonnull - public Vector3f assign(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f add(@Nonnull Vector3f v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.z = this.z + v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f add(@Nonnull Vector3i v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.z = this.z + v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f add(float x, float y, float z) { - this.x += x; - this.y += y; - this.z += z; - this.hash = 0; - return this; - } - - public void addPitch(float pitch) { - this.x += pitch; - this.hash = 0; - } - - public void addYaw(float yaw) { - this.y += yaw; - this.hash = 0; - } - - public void addRoll(float roll) { - this.z += roll; - this.hash = 0; - } - - @Nonnull - public Vector3f addScaled(@Nonnull Vector3f v, float s) { - this.x = this.x + v.x * s; - this.y = this.y + v.y * s; - this.z = this.z + v.z * s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f subtract(@Nonnull Vector3f v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.z = this.z - v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f subtract(@Nonnull Vector3i v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.z = this.z - v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f subtract(float x, float y, float z) { - this.x -= x; - this.y -= y; - this.z -= z; - this.hash = 0; - return this; - } - - public void addRotationOnAxis(@Nonnull Axis axis, int angle) { - float rad = (float) (Math.PI / 180.0) * angle; - switch (axis) { - case X: - this.setPitch(this.getPitch() + rad); - break; - case Y: - this.setYaw(this.getYaw() + rad); - break; - case Z: - this.setRoll(this.getRoll() + rad); - } - } - - public void flipRotationOnAxis(@Nonnull Axis axis) { - this.addRotationOnAxis(axis, 180); - } - - @Nonnull - public Vector3f negate() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f scale(float s) { - this.x *= s; - this.y *= s; - this.z *= s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f scale(@Nonnull Vector3f p) { - this.x = this.x * p.x; - this.y = this.y * p.y; - this.z = this.z * p.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f cross(@Nonnull Vector3f v) { - float x0 = this.y * v.z - this.z * v.y; - float y0 = this.z * v.x - this.x * v.z; - float z0 = this.x * v.y - this.y * v.x; - return new Vector3f(x0, y0, z0); - } - - @Nonnull - public Vector3f cross(@Nonnull Vector3f v, @Nonnull Vector3f res) { - res.assign(this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x); - return this; - } - - public float dot(@Nonnull Vector3f other) { - return this.x * other.x + this.y * other.y + this.z * other.z; - } - - public float distanceTo(@Nonnull Vector3f v) { - return (float)Math.sqrt(this.distanceSquaredTo(v)); - } - - public float distanceTo(@Nonnull Vector3i v) { - return (float)Math.sqrt(this.distanceSquaredTo(v)); - } - - public float distanceTo(float x, float y, float z) { - return (float)Math.sqrt(this.distanceSquaredTo(x, y, z)); - } - - public float distanceSquaredTo(@Nonnull Vector3f v) { - float x0 = v.x - this.x; - float y0 = v.y - this.y; - float z0 = v.z - this.z; - return x0 * x0 + y0 * y0 + z0 * z0; - } - - public float distanceSquaredTo(@Nonnull Vector3i v) { - float x0 = v.x - this.x; - float y0 = v.y - this.y; - float z0 = v.z - this.z; - return x0 * x0 + y0 * y0 + z0 * z0; - } - - public float distanceSquaredTo(float x, float y, float z) { - float dx = x - this.x; - float dy = y - this.y; - float dz = z - this.z; - return dx * dx + dy * dy + dz * dz; - } - - @Nonnull - public Vector3f normalize() { - return this.setLength(1.0F); - } - - public float length() { - return (float)Math.sqrt(this.squaredLength()); - } - - public float squaredLength() { - return this.x * this.x + this.y * this.y + this.z * this.z; - } - - @Nonnull - public Vector3f setLength(float newLen) { - return this.scale(newLen / this.length()); - } - - @Nonnull - public Vector3f clampLength(float maxLength) { - float length = this.length(); - return maxLength > length ? this : this.scale(maxLength / length); - } - - @Nonnull - public Vector3f rotateX(float angle) { - float cos = TrigMathUtil.cos(angle); - float sin = TrigMathUtil.sin(angle); - float cy = this.y * cos - this.z * sin; - float cz = this.y * sin + this.z * cos; - this.y = cy; - this.z = cz; - return this; - } - - @Nonnull - public Vector3f rotateY(float angle) { - float cos = TrigMathUtil.cos(angle); - float sin = TrigMathUtil.sin(angle); - float cx = this.x * cos + this.z * sin; - float cz = this.x * -sin + this.z * cos; - this.x = cx; - this.z = cz; - return this; - } - - @Nonnull - public Vector3f rotateZ(float angle) { - float cos = TrigMathUtil.cos(angle); - float sin = TrigMathUtil.sin(angle); - float cx = this.x * cos - this.y * sin; - float cy = this.x * sin + this.y * cos; - this.x = cx; - this.y = cy; - return this; - } - - @Nonnull - public Vector3f floor() { - this.x = MathUtil.fastFloor(this.x); - this.y = MathUtil.fastFloor(this.y); - this.z = MathUtil.fastFloor(this.z); - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f ceil() { - this.x = MathUtil.fastCeil(this.x); - this.y = MathUtil.fastCeil(this.y); - this.z = MathUtil.fastCeil(this.z); - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f clipToZero(float epsilon) { - this.x = MathUtil.clipToZero(this.x, epsilon); - this.y = MathUtil.clipToZero(this.y, epsilon); - this.z = MathUtil.clipToZero(this.z, epsilon); - this.hash = 0; - return this; - } - - public boolean closeToZero(float epsilon) { - return MathUtil.closeToZero(this.x, epsilon) && MathUtil.closeToZero(this.y, epsilon) && MathUtil.closeToZero(this.z, epsilon); - } - - public boolean isInside(int x, int y, int z) { - float dx = this.x - x; - float dy = this.y - y; - float dz = this.z - z; - return dx >= 0.0F && dx < 1.0F && dy >= 0.0F && dy < 1.0F && dz >= 0.0F && dz < 1.0F; - } - - public boolean isFinite() { - return Float.isFinite(this.x) && Float.isFinite(this.y) && Float.isFinite(this.z); - } - - @Nonnull - public Vector3f dropHash() { - this.hash = 0; - return this; - } - - @Nonnull - public Vector3f clone() { - return new Vector3f(this.x, this.y, this.z); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - Vector3f vector3f = (Vector3f)o; - return vector3f.x == this.x && vector3f.y == this.y && vector3f.z == this.z - ? true - : Float.isNaN(vector3f.x) - && Float.isNaN(this.x) - && Float.isNaN(vector3f.y) - && Float.isNaN(this.y) - && Float.isNaN(vector3f.z) - && Float.isNaN(this.z); - } else { - return false; - } - } - - public boolean equals(@Nullable Vector3f o) { - if (o == null) { - return false; - } else { - return o.x == this.x && o.y == this.y && o.z == this.z - ? true - : Float.isNaN(o.x) && Float.isNaN(this.x) && Float.isNaN(o.y) && Float.isNaN(this.y) && Float.isNaN(o.z) && Float.isNaN(this.z); - } - } - - @Override - public int hashCode() { - if (this.hash == 0) { - this.hash = (int)HashUtil.hash(Float.floatToIntBits(this.x), Float.floatToIntBits(this.y), Float.floatToIntBits(this.z)); - } - - return this.hash; - } - - @Nonnull - @Override - public String toString() { - return "Vector3f{x=" + this.x + ", y=" + this.y + ", z=" + this.z + "}"; - } - - @Nonnull - public Vector3d toVector3d() { - return new Vector3d(this.x, this.y, this.z); - } - - @Nonnull - public static Vector3f max(@Nonnull Vector3f a, @Nonnull Vector3f b) { - return new Vector3f(Math.max(a.x, b.x), Math.max(a.y, b.y), Math.max(a.z, b.z)); - } - - @Nonnull - public static Vector3f min(@Nonnull Vector3f a, @Nonnull Vector3f b) { - return new Vector3f(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z)); - } - - @Nonnull - public static Vector3f lerp(@Nonnull Vector3f a, @Nonnull Vector3f b, float t) { - return lerpUnclamped(a, b, MathUtil.clamp(t, 0.0F, 1.0F)); - } - - @Nonnull - public static Vector3f lerpUnclamped(@Nonnull Vector3f a, @Nonnull Vector3f b, float t) { - return new Vector3f(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y), a.z + t * (b.z - a.z)); - } - - @Nonnull - public static Vector3f lerpAngle(@Nonnull Vector3f a, @Nonnull Vector3f b, float t) { - return lerpAngle(a, b, t, new Vector3f()); - } - - @Nonnull - public static Vector3f lerpAngle(@Nonnull Vector3f a, @Nonnull Vector3f b, float t, @Nonnull Vector3f target) { - target.assign(MathUtil.lerpAngle(a.x, b.x, t), MathUtil.lerpAngle(a.y, b.y, t), MathUtil.lerpAngle(a.z, b.z, t)); - return target; - } - - @Nonnull - public static Vector3f directionTo(@Nonnull Vector3f from, @Nonnull Vector3f to) { - return to.clone().subtract(from).normalize(); - } - - @Nonnull - public static Vector3f add(@Nonnull Vector3f one, @Nonnull Vector3f two) { - return new Vector3f().add(one).add(two); - } - - @Nonnull - public static Vector3f add(@Nonnull Vector3f one, @Nonnull Vector3f two, @Nonnull Vector3f three) { - return new Vector3f().add(one).add(two).add(three); - } - - @Nonnull - public static Vector3f lookAt(@Nonnull Vector3d relative) { - return lookAt(relative, new Vector3f()); - } - - @Nonnull - public static Vector3f lookAt(@Nonnull Vector3d relative, @Nonnull Vector3f result) { - if (!MathUtil.closeToZero(relative.x) || !MathUtil.closeToZero(relative.z)) { - float yaw = TrigMathUtil.atan2((float)(-relative.x), (float)(-relative.z)); - result.setY(MathUtil.wrapAngle(yaw)); - } - - double length = relative.squaredLength(); - if (length > 0.0) { - float pitch = (float) (Math.PI / 2) - (float)Math.acos(relative.y / Math.sqrt(length)); - result.setX(MathUtil.clamp(pitch, (float) (-Math.PI / 2) + MathUtil.PITCH_EDGE_PADDING, (float) (Math.PI / 2) - MathUtil.PITCH_EDGE_PADDING)); - } - - return result; - } -} diff --git a/src/com/hypixel/hytale/math/vector/Vector3fUtil.java b/src/com/hypixel/hytale/math/vector/Vector3fUtil.java new file mode 100644 index 00000000..ea534c83 --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Vector3fUtil.java @@ -0,0 +1,30 @@ +package com.hypixel.hytale.math.vector; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; +import org.joml.Vector3f; +import org.joml.Vector3fc; + +public final class Vector3fUtil { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(Vector3f.class, Vector3f::new) + .metadata(UIDisplayMode.COMPACT) + .appendInherited(new KeyedCodec<>("X", Codec.FLOAT), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Y", Codec.FLOAT), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Z", Codec.FLOAT), (o, i) -> o.z = i, o -> o.z, (o, p) -> o.z = p.z) + .addValidator(Validators.nonNull()) + .add() + .build(); + public static final Vector3fc ZERO = new Vector3f(0.0F, 0.0F, 0.0F); + + private Vector3fUtil() { + } +} diff --git a/src/com/hypixel/hytale/math/vector/Vector3i.java b/src/com/hypixel/hytale/math/vector/Vector3i.java deleted file mode 100644 index a440a013..00000000 --- a/src/com/hypixel/hytale/math/vector/Vector3i.java +++ /dev/null @@ -1,378 +0,0 @@ -package com.hypixel.hytale.math.vector; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.util.HashUtil; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class Vector3i { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(Vector3i.class, Vector3i::new) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.INTEGER), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.INTEGER), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Z", Codec.INTEGER), (o, i) -> o.z = i, o -> o.z, (o, p) -> o.z = p.z) - .addValidator(Validators.nonNull()) - .add() - .build(); - public static final Vector3i ZERO = new Vector3i(0, 0, 0); - public static final Vector3i UP = new Vector3i(0, 1, 0); - public static final Vector3i POS_Y = UP; - public static final Vector3i DOWN = new Vector3i(0, -1, 0); - public static final Vector3i NEG_Y = DOWN; - public static final Vector3i FORWARD = new Vector3i(0, 0, -1); - public static final Vector3i NEG_Z = FORWARD; - public static final Vector3i NORTH = FORWARD; - public static final Vector3i BACKWARD = new Vector3i(0, 0, 1); - public static final Vector3i POS_Z = BACKWARD; - public static final Vector3i SOUTH = BACKWARD; - public static final Vector3i RIGHT = new Vector3i(1, 0, 0); - public static final Vector3i POS_X = RIGHT; - public static final Vector3i EAST = RIGHT; - public static final Vector3i LEFT = new Vector3i(-1, 0, 0); - public static final Vector3i NEG_X = LEFT; - public static final Vector3i WEST = LEFT; - public static final Vector3i ALL_ONES = new Vector3i(1, 1, 1); - public static final Vector3i MIN = new Vector3i(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); - public static final Vector3i MAX = new Vector3i(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); - public static final Vector3i[] BLOCK_SIDES = new Vector3i[]{UP, DOWN, FORWARD, BACKWARD, LEFT, RIGHT}; - public static final Vector3i[] BLOCK_EDGES = new Vector3i[]{ - add(UP, FORWARD), - add(DOWN, FORWARD), - add(UP, BACKWARD), - add(DOWN, BACKWARD), - add(UP, LEFT), - add(DOWN, LEFT), - add(UP, RIGHT), - add(DOWN, RIGHT), - add(FORWARD, LEFT), - add(FORWARD, RIGHT), - add(BACKWARD, LEFT), - add(BACKWARD, RIGHT) - }; - public static final Vector3i[] BLOCK_CORNERS = new Vector3i[]{ - add(UP, FORWARD, LEFT), - add(UP, FORWARD, RIGHT), - add(DOWN, FORWARD, LEFT), - add(DOWN, FORWARD, RIGHT), - add(UP, BACKWARD, LEFT), - add(UP, BACKWARD, RIGHT), - add(DOWN, BACKWARD, LEFT), - add(DOWN, BACKWARD, RIGHT) - }; - public static final Vector3i[][] BLOCK_PARTS = new Vector3i[][]{BLOCK_SIDES, BLOCK_EDGES, BLOCK_CORNERS}; - public static final Vector3i[] CARDINAL_DIRECTIONS = new Vector3i[]{NORTH, SOUTH, EAST, WEST}; - public int x; - public int y; - public int z; - private transient int hash; - - public Vector3i() { - this(0, 0, 0); - } - - public Vector3i(@Nonnull Vector3i v) { - this(v.x, v.y, v.z); - } - - public Vector3i(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; - this.hash = 0; - } - - public int getX() { - return this.x; - } - - public void setX(int x) { - this.x = x; - this.hash = 0; - } - - public int getY() { - return this.y; - } - - public void setY(int y) { - this.y = y; - this.hash = 0; - } - - public int getZ() { - return this.z; - } - - public void setZ(int z) { - this.z = z; - this.hash = 0; - } - - @Nonnull - public Vector3i assign(@Nonnull Vector3i v) { - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.hash = v.hash; - return this; - } - - @Nonnull - public Vector3i assign(int v) { - this.x = v; - this.y = v; - this.z = v; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i assign(@Nonnull int[] v) { - this.x = v[0]; - this.y = v[1]; - this.z = v[2]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i assign(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i add(@Nonnull Vector3i v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.z = this.z + v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i add(int x, int y, int z) { - this.x += x; - this.y += y; - this.z += z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i addScaled(@Nonnull Vector3i v, int s) { - this.x = this.x + v.x * s; - this.y = this.y + v.y * s; - this.z = this.z + v.z * s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i subtract(@Nonnull Vector3i v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.z = this.z - v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i subtract(int x, int y, int z) { - this.x -= x; - this.y -= y; - this.z -= z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i negate() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i scale(int s) { - this.x *= s; - this.y *= s; - this.z *= s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i scale(double s) { - this.x = (int)(this.x * s); - this.y = (int)(this.y * s); - this.z = (int)(this.z * s); - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i scale(@Nonnull Vector3i p) { - this.x = this.x * p.x; - this.y = this.y * p.y; - this.z = this.z * p.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i cross(@Nonnull Vector3i v) { - int x0 = this.y * v.z - this.z * v.y; - int y0 = this.z * v.x - this.x * v.z; - int z0 = this.x * v.y - this.y * v.x; - return new Vector3i(x0, y0, z0); - } - - @Nonnull - public Vector3i cross(@Nonnull Vector3i v, @Nonnull Vector3i res) { - res.assign(this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x); - return this; - } - - public int dot(@Nonnull Vector3i other) { - return this.x * other.x + this.y * other.y + this.z * other.z; - } - - public double distanceTo(@Nonnull Vector3i v) { - return Math.sqrt(this.distanceSquaredTo(v)); - } - - public double distanceTo(int x, int y, int z) { - return Math.sqrt(this.distanceSquaredTo(x, y, z)); - } - - public int distanceSquaredTo(@Nonnull Vector3i v) { - int x0 = v.x - this.x; - int y0 = v.y - this.y; - int z0 = v.z - this.z; - return x0 * x0 + y0 * y0 + z0 * z0; - } - - public int distanceSquaredTo(int x, int y, int z) { - int dx = x - this.x; - int dy = y - this.y; - int dz = z - this.z; - return dx * dx + dy * dy + dz * dz; - } - - @Nonnull - public Vector3i normalize() { - return this.setLength(1); - } - - public double length() { - return Math.sqrt(this.squaredLength()); - } - - public int squaredLength() { - return this.x * this.x + this.y * this.y + this.z * this.z; - } - - @Nonnull - public Vector3i setLength(int newLen) { - return this.scale(newLen / this.length()); - } - - @Nonnull - public Vector3i clampLength(int maxLength) { - double length = this.length(); - return maxLength > length ? this : this.scale(maxLength / length); - } - - @Nonnull - public Vector3i dropHash() { - this.hash = 0; - return this; - } - - @Nonnull - public Vector3i clone() { - return new Vector3i(this.x, this.y, this.z); - } - - @Nonnull - public Vector3d toVector3d() { - return new Vector3d(this.x, this.y, this.z); - } - - @Nonnull - public Vector3f toVector3f() { - return new Vector3f(this.x, this.y, this.z); - } - - @Nonnull - public Vector3l toVector3l() { - return new Vector3l(this.x, this.y, this.z); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - Vector3i vector3i = (Vector3i)o; - return vector3i.x == this.x && vector3i.y == this.y && vector3i.z == this.z; - } else { - return false; - } - } - - @Override - public int hashCode() { - if (this.hash == 0) { - this.hash = (int)HashUtil.hash(this.x, this.y, this.z); - } - - return this.hash; - } - - @Nonnull - @Override - public String toString() { - return "Vector3i{x=" + this.x + ", y=" + this.y + ", z=" + this.z + "}"; - } - - @Nonnull - public static Vector3i max(@Nonnull Vector3i a, @Nonnull Vector3i b) { - return new Vector3i(Math.max(a.x, b.x), Math.max(a.y, b.y), Math.max(a.z, b.z)); - } - - @Nonnull - public static Vector3i min(@Nonnull Vector3i a, @Nonnull Vector3i b) { - return new Vector3i(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z)); - } - - @Nonnull - public static Vector3i directionTo(@Nonnull Vector3i from, @Nonnull Vector3i to) { - return to.clone().subtract(from).normalize(); - } - - @Nonnull - public static Vector3i add(@Nonnull Vector3i one, @Nonnull Vector3i two) { - return new Vector3i().add(one).add(two); - } - - @Nonnull - public static Vector3i add(@Nonnull Vector3i one, @Nonnull Vector3i two, @Nonnull Vector3i three) { - return new Vector3i().add(one).add(two).add(three); - } -} diff --git a/src/com/hypixel/hytale/math/vector/Vector3iUtil.java b/src/com/hypixel/hytale/math/vector/Vector3iUtil.java new file mode 100644 index 00000000..937cc4bc --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Vector3iUtil.java @@ -0,0 +1,100 @@ +package com.hypixel.hytale.math.vector; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.joml.Vector3ic; + +public final class Vector3iUtil { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(Vector3i.class, Vector3i::new) + .metadata(UIDisplayMode.COMPACT) + .appendInherited(new KeyedCodec<>("X", Codec.INTEGER), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Y", Codec.INTEGER), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) + .addValidator(Validators.nonNull()) + .add() + .appendInherited(new KeyedCodec<>("Z", Codec.INTEGER), (o, i) -> o.z = i, o -> o.z, (o, p) -> o.z = p.z) + .addValidator(Validators.nonNull()) + .add() + .build(); + public static final Vector3ic ZERO = new Vector3i(0, 0, 0); + public static final Vector3ic UP = new Vector3i(0, 1, 0); + public static final Vector3ic DOWN = new Vector3i(0, -1, 0); + public static final Vector3ic FORWARD = new Vector3i(0, 0, -1); + public static final Vector3ic BACKWARD = new Vector3i(0, 0, 1); + public static final Vector3ic LEFT = new Vector3i(-1, 0, 0); + public static final Vector3ic RIGHT = new Vector3i(1, 0, 0); + public static final Vector3ic POS_X = RIGHT; + public static final Vector3ic POS_Z = BACKWARD; + public static final Vector3ic POS_Y = UP; + public static final Vector3ic NEG_X = LEFT; + public static final Vector3ic NEG_Y = DOWN; + public static final Vector3ic NEG_Z = FORWARD; + public static final Vector3ic NORTH = FORWARD; + public static final Vector3ic SOUTH = BACKWARD; + public static final Vector3ic EAST = RIGHT; + public static final Vector3ic WEST = LEFT; + public static final Vector3ic[] BLOCK_CORNERS = new Vector3ic[]{ + add(UP, FORWARD, LEFT), + add(UP, FORWARD, RIGHT), + add(DOWN, FORWARD, LEFT), + add(DOWN, FORWARD, RIGHT), + add(UP, BACKWARD, LEFT), + add(UP, BACKWARD, RIGHT), + add(DOWN, BACKWARD, LEFT), + add(DOWN, BACKWARD, RIGHT) + }; + public static final Vector3ic[] BLOCK_SIDES = new Vector3ic[]{UP, DOWN, FORWARD, BACKWARD, LEFT, RIGHT}; + public static final Vector3ic[] BLOCK_EDGES = new Vector3ic[]{ + add(UP, FORWARD), + add(DOWN, FORWARD), + add(UP, BACKWARD), + add(DOWN, BACKWARD), + add(UP, LEFT), + add(DOWN, LEFT), + add(UP, RIGHT), + add(DOWN, RIGHT), + add(FORWARD, LEFT), + add(FORWARD, RIGHT), + add(BACKWARD, LEFT), + add(BACKWARD, RIGHT) + }; + public static final Vector3ic[][] BLOCK_PARTS = new Vector3ic[][]{BLOCK_SIDES, BLOCK_EDGES, BLOCK_CORNERS}; + public static final Vector3ic ALL_ONES = new Vector3i(1, 1, 1); + public static final Vector3ic MIN = new Vector3i(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); + public static final Vector3ic MAX = new Vector3i(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + + private Vector3iUtil() { + } + + @Nonnull + public static Vector3i max(@Nonnull Vector3ic a, @Nonnull Vector3ic b) { + return a.max(b, new Vector3i()); + } + + @Nonnull + public static Vector3i min(@Nonnull Vector3ic a, @Nonnull Vector3ic b) { + return a.min(b, new Vector3i()); + } + + @Nonnull + private static Vector3i add(@Nonnull Vector3ic one, @Nonnull Vector3ic two) { + return new Vector3i(one).add(two); + } + + @Nonnull + private static Vector3i add(@Nonnull Vector3ic one, @Nonnull Vector3ic two, @Nonnull Vector3ic three) { + return new Vector3i(one).add(two).add(three); + } + + public static Vector3d toVector3d(Vector3ic vec) { + return new Vector3d(vec.x(), vec.y(), vec.z()); + } +} diff --git a/src/com/hypixel/hytale/math/vector/Vector3l.java b/src/com/hypixel/hytale/math/vector/Vector3l.java deleted file mode 100644 index f5349389..00000000 --- a/src/com/hypixel/hytale/math/vector/Vector3l.java +++ /dev/null @@ -1,374 +0,0 @@ -package com.hypixel.hytale.math.vector; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.util.MathUtil; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class Vector3l { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(Vector3l.class, Vector3l::new) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.LONG), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.LONG), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) - .addValidator(Validators.nonNull()) - .add() - .appendInherited(new KeyedCodec<>("Z", Codec.LONG), (o, i) -> o.z = i, o -> o.z, (o, p) -> o.z = p.z) - .addValidator(Validators.nonNull()) - .add() - .build(); - public static final Vector3l ZERO = new Vector3l(0L, 0L, 0L); - public static final Vector3l UP = new Vector3l(0L, 1L, 0L); - public static final Vector3l POS_Y = UP; - public static final Vector3l DOWN = new Vector3l(0L, -1L, 0L); - public static final Vector3l NEG_Y = DOWN; - public static final Vector3l FORWARD = new Vector3l(0L, 0L, -1L); - public static final Vector3l NEG_Z = FORWARD; - public static final Vector3l NORTH = FORWARD; - public static final Vector3l BACKWARD = new Vector3l(0L, 0L, 1L); - public static final Vector3l POS_Z = BACKWARD; - public static final Vector3l SOUTH = BACKWARD; - public static final Vector3l RIGHT = new Vector3l(1L, 0L, 0L); - public static final Vector3l POS_X = RIGHT; - public static final Vector3l EAST = RIGHT; - public static final Vector3l LEFT = new Vector3l(-1L, 0L, 0L); - public static final Vector3l NEG_X = LEFT; - public static final Vector3l WEST = LEFT; - public static final Vector3l ALL_ONES = new Vector3l(1L, 1L, 1L); - public static final Vector3l MIN = new Vector3l(Long.MIN_VALUE, Long.MIN_VALUE, Long.MIN_VALUE); - public static final Vector3l MAX = new Vector3l(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE); - public static final Vector3l[] BLOCK_SIDES = new Vector3l[]{UP, DOWN, FORWARD, BACKWARD, LEFT, RIGHT}; - public static final Vector3l[] BLOCK_EDGES = new Vector3l[]{ - add(UP, FORWARD), - add(DOWN, FORWARD), - add(UP, BACKWARD), - add(DOWN, BACKWARD), - add(UP, LEFT), - add(DOWN, LEFT), - add(UP, RIGHT), - add(DOWN, RIGHT), - add(FORWARD, LEFT), - add(FORWARD, RIGHT), - add(BACKWARD, LEFT), - add(BACKWARD, RIGHT) - }; - public static final Vector3l[] BLOCK_CORNERS = new Vector3l[]{ - add(UP, FORWARD, LEFT), - add(UP, FORWARD, RIGHT), - add(DOWN, FORWARD, LEFT), - add(DOWN, FORWARD, RIGHT), - add(UP, BACKWARD, LEFT), - add(UP, BACKWARD, RIGHT), - add(DOWN, BACKWARD, LEFT), - add(DOWN, BACKWARD, RIGHT) - }; - public static final Vector3l[][] BLOCK_PARTS = new Vector3l[][]{BLOCK_SIDES, BLOCK_EDGES, BLOCK_CORNERS}; - public static final Vector3l[] CARDINAL_DIRECTIONS = new Vector3l[]{NORTH, SOUTH, EAST, WEST}; - public long x; - public long y; - public long z; - private transient int hash; - - public Vector3l() { - this(0L, 0L, 0L); - } - - public Vector3l(@Nonnull Vector3l v) { - this(v.x, v.y, v.z); - } - - public Vector3l(long x, long y, long z) { - this.x = x; - this.y = y; - this.z = z; - this.hash = 0; - } - - public long getX() { - return this.x; - } - - public void setX(long x) { - this.x = x; - this.hash = 0; - } - - public long getY() { - return this.y; - } - - public void setY(long y) { - this.y = y; - this.hash = 0; - } - - public long getZ() { - return this.z; - } - - public void setZ(long z) { - this.z = z; - this.hash = 0; - } - - @Nonnull - public Vector3l assign(@Nonnull Vector3l v) { - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.hash = v.hash; - return this; - } - - @Nonnull - public Vector3l assign(long v) { - this.x = v; - this.y = v; - this.z = v; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l assign(@Nonnull long[] v) { - this.x = v[0]; - this.y = v[1]; - this.z = v[2]; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l assign(long x, long y, long z) { - this.x = x; - this.y = y; - this.z = z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l add(@Nonnull Vector3l v) { - this.x = this.x + v.x; - this.y = this.y + v.y; - this.z = this.z + v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l add(long x, long y, long z) { - this.x += x; - this.y += y; - this.z += z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l addScaled(@Nonnull Vector3l v, long s) { - this.x = this.x + v.x * s; - this.y = this.y + v.y * s; - this.z = this.z + v.z * s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l subtract(@Nonnull Vector3l v) { - this.x = this.x - v.x; - this.y = this.y - v.y; - this.z = this.z - v.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l subtract(long x, long y, long z) { - this.x -= x; - this.y -= y; - this.z -= z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l negate() { - this.x = -this.x; - this.y = -this.y; - this.z = -this.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l scale(long s) { - this.x *= s; - this.y *= s; - this.z *= s; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l scale(double s) { - this.x = (long)(this.x * s); - this.y = (long)(this.y * s); - this.z = (long)(this.z * s); - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l scale(@Nonnull Vector3l p) { - this.x = this.x * p.x; - this.y = this.y * p.y; - this.z = this.z * p.z; - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l cross(@Nonnull Vector3l v) { - long x0 = this.y * v.z - this.z * v.y; - long y0 = this.z * v.x - this.x * v.z; - long z0 = this.x * v.y - this.y * v.x; - return new Vector3l(x0, y0, z0); - } - - @Nonnull - public Vector3l cross(@Nonnull Vector3l v, @Nonnull Vector3l res) { - res.assign(this.y * v.z - this.z * v.y, this.z * v.x - this.x * v.z, this.x * v.y - this.y * v.x); - return this; - } - - public long dot(@Nonnull Vector3l other) { - return this.x * other.x + this.y * other.y + this.z * other.z; - } - - public double distanceTo(@Nonnull Vector3l v) { - return Math.sqrt(this.distanceSquaredTo(v)); - } - - public double distanceTo(long x, long y, long z) { - return Math.sqrt(this.distanceSquaredTo(x, y, z)); - } - - public long distanceSquaredTo(@Nonnull Vector3l v) { - long x0 = v.x - this.x; - long y0 = v.y - this.y; - long z0 = v.z - this.z; - return x0 * x0 + y0 * y0 + z0 * z0; - } - - public long distanceSquaredTo(long x, long y, long z) { - long dx = x - this.x; - long dy = y - this.y; - long dz = z - this.z; - return dx * dx + dy * dy + dz * dz; - } - - @Nonnull - public Vector3l normalize() { - return this.setLength(1L); - } - - public double length() { - return Math.sqrt(this.squaredLength()); - } - - public long squaredLength() { - return this.x * this.x + this.y * this.y + this.z * this.z; - } - - @Nonnull - public Vector3l setLength(long newLen) { - return this.scale(newLen / this.length()); - } - - @Nonnull - public Vector3l clampLength(long maxLength) { - double length = this.length(); - return maxLength > length ? this : this.scale(maxLength / length); - } - - @Nonnull - public Vector3l dropHash() { - this.hash = 0; - return this; - } - - @Nonnull - public Vector3l clone() { - return new Vector3l(this.x, this.y, this.z); - } - - @Nonnull - public Vector3i toVector3i() { - return new Vector3i(MathUtil.floor(this.x), MathUtil.floor(this.y), MathUtil.floor(this.z)); - } - - @Nonnull - public Vector3d toVector3d() { - return new Vector3d(this.x, this.y, this.z); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - Vector3l vector3l = (Vector3l)o; - return vector3l.x == this.x && vector3l.y == this.y && vector3l.z == this.z; - } else { - return false; - } - } - - @Override - public int hashCode() { - if (this.hash == 0) { - this.hash = (int)HashUtil.hash(this.x, this.y, this.z); - } - - return this.hash; - } - - @Nonnull - @Override - public String toString() { - return "Vector3l{x=" + this.x + ", y=" + this.y + ", z=" + this.z + "}"; - } - - @Nonnull - public static Vector3l max(@Nonnull Vector3l a, @Nonnull Vector3l b) { - return new Vector3l(Math.max(a.x, b.x), Math.max(a.y, b.y), Math.max(a.z, b.z)); - } - - @Nonnull - public static Vector3l min(@Nonnull Vector3l a, @Nonnull Vector3l b) { - return new Vector3l(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z)); - } - - @Nonnull - public static Vector3l directionTo(@Nonnull Vector3l from, @Nonnull Vector3l to) { - return to.clone().subtract(from).normalize(); - } - - @Nonnull - public static Vector3l add(@Nonnull Vector3l one, @Nonnull Vector3l two) { - return new Vector3l().add(one).add(two); - } - - @Nonnull - public static Vector3l add(@Nonnull Vector3l one, @Nonnull Vector3l two, @Nonnull Vector3l three) { - return new Vector3l().add(one).add(two).add(three); - } -} diff --git a/src/com/hypixel/hytale/math/vector/Vector4d.java b/src/com/hypixel/hytale/math/vector/Vector4d.java deleted file mode 100644 index b61c400f..00000000 --- a/src/com/hypixel/hytale/math/vector/Vector4d.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.hypixel.hytale.math.vector; - -import javax.annotation.Nonnull; - -public class Vector4d { - public static final int COMPONENT_X = 0; - public static final int COMPONENT_Y = 1; - public static final int COMPONENT_Z = 2; - public static final int COMPONENT_W = 3; - public double x; - public double y; - public double z; - public double w; - - public Vector4d() { - this(0.0, 0.0, 0.0, 0.0); - } - - public Vector4d(double x, double y, double z, double w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - @Nonnull - public static Vector4d newPosition(double x, double y, double z) { - return new Vector4d(x, y, z, 1.0); - } - - @Nonnull - public static Vector4d newPosition(@Nonnull Vector3d v) { - return new Vector4d(v.x, v.y, v.z, 1.0); - } - - @Nonnull - public static Vector4d newDirection(double x, double y, double z) { - return new Vector4d(x, y, z, 0.0); - } - - @Nonnull - public Vector4d setDirection() { - this.w = 0.0; - return this; - } - - @Nonnull - public Vector4d setPosition() { - this.w = 1.0; - return this; - } - - @Nonnull - public Vector4d assign(@Nonnull Vector4d v) { - return this.assign(v.x, v.y, v.z, v.w); - } - - @Nonnull - public Vector4d assign(double x, double y, double z, double w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - return this; - } - - @Nonnull - public Vector4d lerp(@Nonnull Vector4d dest, double lerpFactor, @Nonnull Vector4d target) { - target.assign( - (dest.x - this.x) * lerpFactor + this.x, - (dest.y - this.y) * lerpFactor + this.y, - (dest.z - this.z) * lerpFactor + this.z, - (dest.w - this.w) * lerpFactor + this.w - ); - return target; - } - - public void perspectiveTransform() { - double invW = 1.0 / this.w; - this.x *= invW; - this.y *= invW; - this.z *= invW; - this.w = 1.0; - } - - public boolean isInsideFrustum() { - return Math.abs(this.x) <= Math.abs(this.w) && Math.abs(this.y) <= Math.abs(this.w) && Math.abs(this.z) <= Math.abs(this.w); - } - - public double get(int component) { - return switch (component) { - case 0 -> this.x; - case 1 -> this.y; - case 2 -> this.z; - case 3 -> this.w; - default -> throw new IllegalArgumentException("Invalid component: " + component); - }; - } - - @Nonnull - @Override - public String toString() { - return "Vector4d{x=" + this.x + ", y=" + this.y + ", z=" + this.z + ", w=" + this.w + "}"; - } -} diff --git a/src/com/hypixel/hytale/math/vector/Vector4dUtil.java b/src/com/hypixel/hytale/math/vector/Vector4dUtil.java new file mode 100644 index 00000000..55e85326 --- /dev/null +++ b/src/com/hypixel/hytale/math/vector/Vector4dUtil.java @@ -0,0 +1,20 @@ +package com.hypixel.hytale.math.vector; + +import org.joml.Vector4d; + +public final class Vector4dUtil { + public static void perspectiveTransform(Vector4d vec) { + double invW = 1.0 / vec.w; + vec.x *= invW; + vec.y *= invW; + vec.z *= invW; + vec.w = 1.0; + } + + public static boolean isInsideFrustum(Vector4d vec) { + return Math.abs(vec.x) <= Math.abs(vec.w) && Math.abs(vec.y) <= Math.abs(vec.w) && Math.abs(vec.z) <= Math.abs(vec.w); + } + + private Vector4dUtil() { + } +} diff --git a/src/com/hypixel/hytale/math/vector/VectorBoxUtil.java b/src/com/hypixel/hytale/math/vector/VectorBoxUtil.java index 6912e046..25ba0510 100644 --- a/src/com/hypixel/hytale/math/vector/VectorBoxUtil.java +++ b/src/com/hypixel/hytale/math/vector/VectorBoxUtil.java @@ -11,6 +11,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class VectorBoxUtil { public static void forEachVector(Iterable vectors, double originX, double originY, double originZ, double apothem, Consumer consumer) { @@ -419,9 +420,9 @@ public class VectorBoxUtil { public static boolean isInside( double originX, double originY, double originZ, double xMin, double yMin, double zMin, double xMax, double yMax, double zMax, @Nonnull Vector3d vector ) { - double x = vector.getX() - originX; - double y = vector.getY() - originY; - double z = vector.getZ() - originZ; + double x = vector.x() - originX; + double y = vector.y() - originY; + double z = vector.z() - originZ; return x >= xMin && x <= xMax && y >= yMin && y <= yMax && z >= zMin && z <= zMax; } } diff --git a/src/com/hypixel/hytale/math/vector/VectorSphereUtil.java b/src/com/hypixel/hytale/math/vector/VectorSphereUtil.java index c171355b..4899d45e 100644 --- a/src/com/hypixel/hytale/math/vector/VectorSphereUtil.java +++ b/src/com/hypixel/hytale/math/vector/VectorSphereUtil.java @@ -11,6 +11,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class VectorSphereUtil { public static void forEachVector(Iterable vectors, double originX, double originY, double originZ, double radius, Consumer consumer) { @@ -204,9 +205,9 @@ public class VectorSphereUtil { } public static boolean isInside(double originX, double originY, double originZ, double radiusX, double radiusY, double radiusZ, @Nonnull Vector3d vector) { - double x = vector.getX() - originX; - double y = vector.getY() - originY; - double z = vector.getZ() - originZ; + double x = vector.x() - originX; + double y = vector.y() - originY; + double z = vector.z() - originZ; double xRatio = x / radiusX; double yRatio = y / radiusY; double zRatio = z / radiusZ; diff --git a/src/com/hypixel/hytale/math/vector/relative/RelativeVector2d.java b/src/com/hypixel/hytale/math/vector/relative/RelativeVector2d.java deleted file mode 100644 index ddd48227..00000000 --- a/src/com/hypixel/hytale/math/vector/relative/RelativeVector2d.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.hypixel.hytale.math.vector.relative; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector2d; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class RelativeVector2d { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(RelativeVector2d.class, RelativeVector2d::new) - .append(new KeyedCodec<>("Vector", Vector2d.CODEC), (o, i) -> o.vector = i, RelativeVector2d::getVector) - .addValidator(Validators.nonNull()) - .add() - .append(new KeyedCodec<>("Relative", Codec.BOOLEAN), (o, i) -> o.relative = i, RelativeVector2d::isRelative) - .addValidator(Validators.nonNull()) - .add() - .build(); - private Vector2d vector; - private boolean relative; - - public RelativeVector2d(@Nonnull Vector2d vector, boolean relative) { - this.vector = vector; - this.relative = relative; - } - - protected RelativeVector2d() { - } - - @Nonnull - public Vector2d getVector() { - return this.vector; - } - - public boolean isRelative() { - return this.relative; - } - - @Nonnull - public Vector2d resolve(@Nonnull Vector2d vector) { - return this.relative ? vector.clone().add(vector) : vector.clone(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - RelativeVector2d that = (RelativeVector2d)o; - return this.relative != that.relative ? false : Objects.equals(this.vector, that.vector); - } else { - return false; - } - } - - @Override - public int hashCode() { - int result = this.vector != null ? this.vector.hashCode() : 0; - return 31 * result + (this.relative ? 1 : 0); - } - - @Nonnull - @Override - public String toString() { - return "RelativeVector2d{vector=" + this.vector + ", relative=" + this.relative + "}"; - } -} diff --git a/src/com/hypixel/hytale/math/vector/relative/RelativeVector2i.java b/src/com/hypixel/hytale/math/vector/relative/RelativeVector2i.java deleted file mode 100644 index dd61a685..00000000 --- a/src/com/hypixel/hytale/math/vector/relative/RelativeVector2i.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.hypixel.hytale.math.vector.relative; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector2i; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class RelativeVector2i { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(RelativeVector2i.class, RelativeVector2i::new) - .append(new KeyedCodec<>("Vector", Vector2i.CODEC), (o, i) -> o.vector = i, RelativeVector2i::getVector) - .addValidator(Validators.nonNull()) - .add() - .append(new KeyedCodec<>("Relative", Codec.BOOLEAN), (o, i) -> o.relative = i, RelativeVector2i::isRelative) - .addValidator(Validators.nonNull()) - .add() - .build(); - private Vector2i vector; - private boolean relative; - - public RelativeVector2i(@Nonnull Vector2i vector, boolean relative) { - this.vector = vector; - this.relative = relative; - } - - protected RelativeVector2i() { - } - - @Nonnull - public Vector2i getVector() { - return this.vector; - } - - public boolean isRelative() { - return this.relative; - } - - @Nonnull - public Vector2i resolve(@Nonnull Vector2i vector) { - return this.relative ? vector.clone().add(vector) : vector.clone(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - RelativeVector2i that = (RelativeVector2i)o; - return this.relative != that.relative ? false : Objects.equals(this.vector, that.vector); - } else { - return false; - } - } - - @Override - public int hashCode() { - int result = this.vector != null ? this.vector.hashCode() : 0; - return 31 * result + (this.relative ? 1 : 0); - } - - @Nonnull - @Override - public String toString() { - return "RelativeVector2i{vector=" + this.vector + ", relative=" + this.relative + "}"; - } -} diff --git a/src/com/hypixel/hytale/math/vector/relative/RelativeVector2l.java b/src/com/hypixel/hytale/math/vector/relative/RelativeVector2l.java deleted file mode 100644 index a96b9cac..00000000 --- a/src/com/hypixel/hytale/math/vector/relative/RelativeVector2l.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.hypixel.hytale.math.vector.relative; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector2l; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class RelativeVector2l { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(RelativeVector2l.class, RelativeVector2l::new) - .append(new KeyedCodec<>("Vector", Vector2l.CODEC), (o, i) -> o.vector = i, RelativeVector2l::getVector) - .addValidator(Validators.nonNull()) - .add() - .append(new KeyedCodec<>("Relative", Codec.BOOLEAN), (o, i) -> o.relative = i, RelativeVector2l::isRelative) - .addValidator(Validators.nonNull()) - .add() - .build(); - private Vector2l vector; - private boolean relative; - - public RelativeVector2l(@Nonnull Vector2l vector, boolean relative) { - this.vector = vector; - this.relative = relative; - } - - protected RelativeVector2l() { - } - - @Nonnull - public Vector2l getVector() { - return this.vector; - } - - public boolean isRelative() { - return this.relative; - } - - @Nonnull - public Vector2l resolve(@Nonnull Vector2l vector) { - return this.relative ? vector.clone().add(vector) : vector.clone(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - RelativeVector2l that = (RelativeVector2l)o; - return this.relative != that.relative ? false : Objects.equals(this.vector, that.vector); - } else { - return false; - } - } - - @Override - public int hashCode() { - int result = this.vector != null ? this.vector.hashCode() : 0; - return 31 * result + (this.relative ? 1 : 0); - } - - @Nonnull - @Override - public String toString() { - return "RelativeVector2l{vector=" + this.vector + ", relative=" + this.relative + "}"; - } -} diff --git a/src/com/hypixel/hytale/math/vector/relative/RelativeVector3d.java b/src/com/hypixel/hytale/math/vector/relative/RelativeVector3d.java deleted file mode 100644 index 24f74f84..00000000 --- a/src/com/hypixel/hytale/math/vector/relative/RelativeVector3d.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.hypixel.hytale.math.vector.relative; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class RelativeVector3d { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(RelativeVector3d.class, RelativeVector3d::new) - .append(new KeyedCodec<>("Vector", Vector3d.CODEC), (o, i) -> o.vector = i, RelativeVector3d::getVector) - .addValidator(Validators.nonNull()) - .add() - .append(new KeyedCodec<>("Relative", Codec.BOOLEAN), (o, i) -> o.relative = i, RelativeVector3d::isRelative) - .addValidator(Validators.nonNull()) - .add() - .build(); - private Vector3d vector; - private boolean relative; - - public RelativeVector3d(@Nonnull Vector3d vector, boolean relative) { - this.vector = vector; - this.relative = relative; - } - - protected RelativeVector3d() { - } - - @Nonnull - public Vector3d getVector() { - return this.vector; - } - - public boolean isRelative() { - return this.relative; - } - - @Nonnull - public Vector3d resolve(@Nonnull Vector3d vector) { - return this.relative ? vector.clone().add(vector) : vector.clone(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - RelativeVector3d that = (RelativeVector3d)o; - return this.relative != that.relative ? false : Objects.equals(this.vector, that.vector); - } else { - return false; - } - } - - @Override - public int hashCode() { - int result = this.vector != null ? this.vector.hashCode() : 0; - return 31 * result + (this.relative ? 1 : 0); - } - - @Nonnull - @Override - public String toString() { - return "RelativeVector3d{vector=" + this.vector + ", relative=" + this.relative + "}"; - } -} diff --git a/src/com/hypixel/hytale/math/vector/relative/RelativeVector3i.java b/src/com/hypixel/hytale/math/vector/relative/RelativeVector3i.java deleted file mode 100644 index 220dd8b1..00000000 --- a/src/com/hypixel/hytale/math/vector/relative/RelativeVector3i.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.hypixel.hytale.math.vector.relative; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class RelativeVector3i { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(RelativeVector3i.class, RelativeVector3i::new) - .append(new KeyedCodec<>("Vector", Vector3i.CODEC), (o, i) -> o.vector = i, RelativeVector3i::getVector) - .addValidator(Validators.nonNull()) - .add() - .append(new KeyedCodec<>("Relative", Codec.BOOLEAN), (o, i) -> o.relative = i, RelativeVector3i::isRelative) - .addValidator(Validators.nonNull()) - .add() - .build(); - private Vector3i vector; - private boolean relative; - - public RelativeVector3i(@Nonnull Vector3i vector, boolean relative) { - this.vector = vector; - this.relative = relative; - } - - protected RelativeVector3i() { - } - - @Nonnull - public Vector3i getVector() { - return this.vector; - } - - public boolean isRelative() { - return this.relative; - } - - @Nonnull - public Vector3i resolve(@Nonnull Vector3i vector) { - return this.relative ? vector.clone().add(vector) : vector.clone(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - RelativeVector3i that = (RelativeVector3i)o; - return this.relative != that.relative ? false : Objects.equals(this.vector, that.vector); - } else { - return false; - } - } - - @Override - public int hashCode() { - int result = this.vector != null ? this.vector.hashCode() : 0; - return 31 * result + (this.relative ? 1 : 0); - } - - @Nonnull - @Override - public String toString() { - return "RelativeVector3i{vector=" + this.vector + ", relative=" + this.relative + "}"; - } -} diff --git a/src/com/hypixel/hytale/math/vector/relative/RelativeVector3l.java b/src/com/hypixel/hytale/math/vector/relative/RelativeVector3l.java deleted file mode 100644 index 35e92c0d..00000000 --- a/src/com/hypixel/hytale/math/vector/relative/RelativeVector3l.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.hypixel.hytale.math.vector.relative; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3l; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class RelativeVector3l { - @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(RelativeVector3l.class, RelativeVector3l::new) - .append(new KeyedCodec<>("Vector", Vector3l.CODEC), (o, i) -> o.vector = i, RelativeVector3l::getVector) - .addValidator(Validators.nonNull()) - .add() - .append(new KeyedCodec<>("Relative", Codec.BOOLEAN), (o, i) -> o.relative = i, RelativeVector3l::isRelative) - .addValidator(Validators.nonNull()) - .add() - .build(); - private Vector3l vector; - private boolean relative; - - public RelativeVector3l(@Nonnull Vector3l vector, boolean relative) { - this.vector = vector; - this.relative = relative; - } - - protected RelativeVector3l() { - } - - @Nonnull - public Vector3l getVector() { - return this.vector; - } - - public boolean isRelative() { - return this.relative; - } - - @Nonnull - public Vector3l resolve(@Nonnull Vector3l vector) { - return this.relative ? vector.clone().add(vector) : vector.clone(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } else if (o != null && this.getClass() == o.getClass()) { - RelativeVector3l that = (RelativeVector3l)o; - return this.relative != that.relative ? false : Objects.equals(this.vector, that.vector); - } else { - return false; - } - } - - @Override - public int hashCode() { - int result = this.vector != null ? this.vector.hashCode() : 0; - return 31 * result + (this.relative ? 1 : 0); - } - - @Nonnull - @Override - public String toString() { - return "RelativeVector3l{vector=" + this.vector + ", relative=" + this.relative + "}"; - } -} diff --git a/src/com/hypixel/hytale/plugin/early/TransformingClassLoader.java b/src/com/hypixel/hytale/plugin/early/TransformingClassLoader.java index 3939d887..f3a66637 100644 --- a/src/com/hypixel/hytale/plugin/early/TransformingClassLoader.java +++ b/src/com/hypixel/hytale/plugin/early/TransformingClassLoader.java @@ -27,7 +27,6 @@ public final class TransformingClassLoader extends URLClassLoader { "ch.qos.logback.", "com.google.flogger.", "server.io.sentry.", - "com.hypixel.protoplus.", "com.hypixel.fastutil.", "com.hypixel.hytale.plugin.early." ); diff --git a/src/com/hypixel/hytale/procedurallib/json/BlendNoisePropertyJsonLoader.java b/src/com/hypixel/hytale/procedurallib/json/BlendNoisePropertyJsonLoader.java index c51c434a..64d3df63 100644 --- a/src/com/hypixel/hytale/procedurallib/json/BlendNoisePropertyJsonLoader.java +++ b/src/com/hypixel/hytale/procedurallib/json/BlendNoisePropertyJsonLoader.java @@ -27,7 +27,7 @@ public class BlendNoisePropertyJsonLoader extends JsonLo } protected NoiseProperty[] loadNoise() { - JsonArray noise = this.mustGetArray("Noise", BlendNoisePropertyJsonLoader.Constants.EMPTY_ARRAY); + JsonArray noise = this.mustGetArray("Noise", EMPTY_ARRAY); NoiseProperty[] noises = new NoiseProperty[noise.size()]; for (int i = 0; i < noise.size(); i++) { @@ -38,7 +38,7 @@ public class BlendNoisePropertyJsonLoader extends JsonLo } protected double[] loadThresholds() { - JsonArray thresholds = this.mustGetArray("Thresholds", BlendNoisePropertyJsonLoader.Constants.EMPTY_ARRAY); + JsonArray thresholds = this.mustGetArray("Thresholds", EMPTY_ARRAY); double[] values = new double[thresholds.size()]; for (int i = 0; i < thresholds.size(); i++) { diff --git a/src/com/hypixel/hytale/procedurallib/json/JsonLoader.java b/src/com/hypixel/hytale/procedurallib/json/JsonLoader.java index 4ca5fa8a..60169ec0 100644 --- a/src/com/hypixel/hytale/procedurallib/json/JsonLoader.java +++ b/src/com/hypixel/hytale/procedurallib/json/JsonLoader.java @@ -20,6 +20,8 @@ public abstract class JsonLoader extends Loader public static final JsonResourceLoader JSON_OBJ_LOADER = new JsonResourceLoader<>( JsonObject.class, JsonElement::isJsonObject, JsonElement::getAsJsonObject ); + protected static final JsonObject EMPTY_OBJECT = new JsonObject(); + protected static final JsonArray EMPTY_ARRAY = new JsonArray(); @Nullable protected final JsonElement json; diff --git a/src/com/hypixel/hytale/procedurallib/logic/MeshNoise.java b/src/com/hypixel/hytale/procedurallib/logic/MeshNoise.java index f060ce68..8d0dcbfc 100644 --- a/src/com/hypixel/hytale/procedurallib/logic/MeshNoise.java +++ b/src/com/hypixel/hytale/procedurallib/logic/MeshNoise.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.procedurallib.logic; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.procedurallib.NoiseFunction; import com.hypixel.hytale.procedurallib.condition.IIntCondition; import com.hypixel.hytale.procedurallib.logic.cell.GridCellDistanceFunction; +import org.joml.Vector2i; public class MeshNoise implements NoiseFunction { public static final Vector2i[] ADJACENT_CELLS = new Vector2i[]{new Vector2i(-1, 0), new Vector2i(0, -1), new Vector2i(1, 0), new Vector2i(0, 1)}; diff --git a/src/com/hypixel/hytale/procedurallib/logic/cell/evaluator/BranchEvaluator.java b/src/com/hypixel/hytale/procedurallib/logic/cell/evaluator/BranchEvaluator.java index 29c3f749..47feccc6 100644 --- a/src/com/hypixel/hytale/procedurallib/logic/cell/evaluator/BranchEvaluator.java +++ b/src/com/hypixel/hytale/procedurallib/logic/cell/evaluator/BranchEvaluator.java @@ -1,13 +1,13 @@ package com.hypixel.hytale.procedurallib.logic.cell.evaluator; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.procedurallib.logic.DoubleArray; import com.hypixel.hytale.procedurallib.logic.ResultBuffer; import com.hypixel.hytale.procedurallib.logic.cell.CellDistanceFunction; import com.hypixel.hytale.procedurallib.logic.cell.CellPointFunction; import com.hypixel.hytale.procedurallib.logic.cell.jitter.CellJitter; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class BranchEvaluator implements PointEvaluator { protected static final int CARDINAL_MASK = 1; diff --git a/src/com/hypixel/hytale/protocol/AOECircleSelector.java b/src/com/hypixel/hytale/protocol/AOECircleSelector.java index bb6bf960..4681e873 100644 --- a/src/com/hypixel/hytale/protocol/AOECircleSelector.java +++ b/src/com/hypixel/hytale/protocol/AOECircleSelector.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class AOECircleSelector extends Selector { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -14,12 +17,12 @@ public class AOECircleSelector extends Selector { public static final int MAX_SIZE = 17; public float range; @Nullable - public Vector3f offset; + public Vector3fc offset; public AOECircleSelector() { } - public AOECircleSelector(float range, @Nullable Vector3f offset) { + public AOECircleSelector(float range, @Nullable Vector3fc offset) { this.range = range; this.offset = offset; } @@ -31,14 +34,18 @@ public class AOECircleSelector extends Selector { @Nonnull public static AOECircleSelector deserialize(@Nonnull ByteBuf buf, int offset) { - AOECircleSelector obj = new AOECircleSelector(); - byte nullBits = buf.getByte(offset); - obj.range = buf.getFloatLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.offset = Vector3f.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("AOECircleSelector", 17, buf.readableBytes() - offset); + } else { + AOECircleSelector obj = new AOECircleSelector(); + byte nullBits = buf.getByte(offset); + obj.range = buf.getFloatLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.offset = PacketIO.readVector3f(buf, offset + 5); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -56,7 +63,7 @@ public class AOECircleSelector extends Selector { buf.writeByte(nullBits); buf.writeFloatLE(this.range); if (this.offset != null) { - this.offset.serialize(buf); + PacketIO.writeVector3f(buf, this.offset); } else { buf.writeZero(12); } @@ -70,13 +77,18 @@ public class AOECircleSelector extends Selector { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 17 ? ValidationResult.error("Buffer too small: expected at least 17 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public AOECircleSelector clone() { AOECircleSelector copy = new AOECircleSelector(); copy.range = this.range; - copy.offset = this.offset != null ? this.offset.clone() : null; + copy.offset = this.offset; return copy; } diff --git a/src/com/hypixel/hytale/protocol/AOECylinderSelector.java b/src/com/hypixel/hytale/protocol/AOECylinderSelector.java index c84706df..16350dce 100644 --- a/src/com/hypixel/hytale/protocol/AOECylinderSelector.java +++ b/src/com/hypixel/hytale/protocol/AOECylinderSelector.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class AOECylinderSelector extends Selector { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -15,12 +18,12 @@ public class AOECylinderSelector extends Selector { public float range; public float height; @Nullable - public Vector3f offset; + public Vector3fc offset; public AOECylinderSelector() { } - public AOECylinderSelector(float range, float height, @Nullable Vector3f offset) { + public AOECylinderSelector(float range, float height, @Nullable Vector3fc offset) { this.range = range; this.height = height; this.offset = offset; @@ -34,15 +37,19 @@ public class AOECylinderSelector extends Selector { @Nonnull public static AOECylinderSelector deserialize(@Nonnull ByteBuf buf, int offset) { - AOECylinderSelector obj = new AOECylinderSelector(); - byte nullBits = buf.getByte(offset); - obj.range = buf.getFloatLE(offset + 1); - obj.height = buf.getFloatLE(offset + 5); - if ((nullBits & 1) != 0) { - obj.offset = Vector3f.deserialize(buf, offset + 9); - } + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("AOECylinderSelector", 21, buf.readableBytes() - offset); + } else { + AOECylinderSelector obj = new AOECylinderSelector(); + byte nullBits = buf.getByte(offset); + obj.range = buf.getFloatLE(offset + 1); + obj.height = buf.getFloatLE(offset + 5); + if ((nullBits & 1) != 0) { + obj.offset = PacketIO.readVector3f(buf, offset + 9); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,7 +68,7 @@ public class AOECylinderSelector extends Selector { buf.writeFloatLE(this.range); buf.writeFloatLE(this.height); if (this.offset != null) { - this.offset.serialize(buf); + PacketIO.writeVector3f(buf, this.offset); } else { buf.writeZero(12); } @@ -75,14 +82,19 @@ public class AOECylinderSelector extends Selector { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 21 ? ValidationResult.error("Buffer too small: expected at least 21 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 21) { + return ValidationResult.error("Buffer too small: expected at least 21 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public AOECylinderSelector clone() { AOECylinderSelector copy = new AOECylinderSelector(); copy.range = this.range; copy.height = this.height; - copy.offset = this.offset != null ? this.offset.clone() : null; + copy.offset = this.offset; return copy; } diff --git a/src/com/hypixel/hytale/protocol/AbilityEffects.java b/src/com/hypixel/hytale/protocol/AbilityEffects.java index e76098d7..a2f1fada 100644 --- a/src/com/hypixel/hytale/protocol/AbilityEffects.java +++ b/src/com/hypixel/hytale/protocol/AbilityEffects.java @@ -30,34 +30,38 @@ public class AbilityEffects { @Nonnull public static AbilityEffects deserialize(@Nonnull ByteBuf buf, int offset) { - AbilityEffects obj = new AbilityEffects(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int disabledCount = VarInt.peek(buf, pos); - if (disabledCount < 0) { - throw ProtocolException.negativeLength("Disabled", disabledCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AbilityEffects", 1, buf.readableBytes() - offset); + } else { + AbilityEffects obj = new AbilityEffects(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int disabledCount = VarInt.peek(buf, pos); + if (disabledCount < 0) { + throw ProtocolException.invalidVarInt("Disabled"); + } + + int disabledVarLen = VarInt.size(disabledCount); + if (disabledCount > 4096000) { + throw ProtocolException.arrayTooLong("Disabled", disabledCount, 4096000); + } + + if (pos + disabledVarLen + disabledCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Disabled", pos + disabledVarLen + disabledCount * 1, buf.readableBytes()); + } + + pos += disabledVarLen; + obj.disabled = new InteractionType[disabledCount]; + + for (int i = 0; i < disabledCount; i++) { + obj.disabled[i] = InteractionType.fromValue(buf.getByte(pos)); + pos++; + } } - if (disabledCount > 4096000) { - throw ProtocolException.arrayTooLong("Disabled", disabledCount, 4096000); - } - - int disabledVarLen = VarInt.size(disabledCount); - if (pos + disabledVarLen + disabledCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Disabled", pos + disabledVarLen + disabledCount * 1, buf.readableBytes()); - } - - pos += disabledVarLen; - obj.disabled = new InteractionType[disabledCount]; - - for (int i = 0; i < disabledCount; i++) { - obj.disabled[i] = InteractionType.fromValue(buf.getByte(pos)); - pos++; - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -65,7 +69,7 @@ public class AbilityEffects { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -116,11 +120,19 @@ public class AbilityEffects { return ValidationResult.error("Disabled exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += disabledCount * 1; - if (pos > buffer.writerIndex()) { + pos += VarInt.size(disabledCount); + if (pos + disabledCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Disabled"); } + + for (int i = 0; i < disabledCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for Disabled[i]"); + } + + pos++; + } } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/ActiveAnimationsUpdate.java b/src/com/hypixel/hytale/protocol/ActiveAnimationsUpdate.java index 6280900a..5f5c1e17 100644 --- a/src/com/hypixel/hytale/protocol/ActiveAnimationsUpdate.java +++ b/src/com/hypixel/hytale/protocol/ActiveAnimationsUpdate.java @@ -34,41 +34,52 @@ public class ActiveAnimationsUpdate extends ComponentUpdate { int pos = offset + 0; int activeAnimationsCount = VarInt.peek(buf, pos); if (activeAnimationsCount < 0) { - throw ProtocolException.negativeLength("ActiveAnimations", activeAnimationsCount); - } else if (activeAnimationsCount > 4096000) { - throw ProtocolException.arrayTooLong("ActiveAnimations", activeAnimationsCount, 4096000); + throw ProtocolException.invalidVarInt("ActiveAnimations"); } else { - pos += VarInt.size(activeAnimationsCount); - int activeAnimationsBitfieldSize = (activeAnimationsCount + 7) / 8; - byte[] activeAnimationsBitfield = PacketIO.readBytes(buf, pos, activeAnimationsBitfieldSize); - pos += activeAnimationsBitfieldSize; - obj.activeAnimations = new String[activeAnimationsCount]; + int activeAnimationsVarLen = VarInt.size(activeAnimationsCount); + if (activeAnimationsCount > 4096000) { + throw ProtocolException.arrayTooLong("ActiveAnimations", activeAnimationsCount, 4096000); + } else { + pos += activeAnimationsVarLen; + int activeAnimationsBitfieldSize = (activeAnimationsCount + 7) / 8; + if (pos + activeAnimationsBitfieldSize > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ActiveAnimations", pos + activeAnimationsBitfieldSize, buf.readableBytes()); + } else { + byte[] activeAnimationsBitfield = PacketIO.readBytes(buf, pos, activeAnimationsBitfieldSize); + pos += activeAnimationsBitfieldSize; + obj.activeAnimations = new String[activeAnimationsCount]; - for (int i = 0; i < activeAnimationsCount; i++) { - if ((activeAnimationsBitfield[i / 8] & 1 << i % 8) != 0) { - int strLen = VarInt.peek(buf, pos); - if (strLen < 0) { - throw ProtocolException.negativeLength("activeAnimations[" + i + "]", strLen); + for (int i = 0; i < activeAnimationsCount; i++) { + if ((activeAnimationsBitfield[i / 8] & 1 << i % 8) != 0) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("activeAnimations[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("activeAnimations[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("activeAnimations[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.activeAnimations[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("activeAnimations[" + i + "]", strLen, 4096000); - } - - int strVarLen = VarInt.length(buf, pos); - obj.activeAnimations[i] = PacketIO.readVarString(buf, pos); - pos += strVarLen + strLen; + return obj; } } - - return obj; } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 0; int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); int bitfieldSize = (arrLen + 7) / 8; byte[] bitfield = PacketIO.readBytes(buf, pos, bitfieldSize); pos += bitfieldSize; @@ -76,7 +87,7 @@ public class ActiveAnimationsUpdate extends ComponentUpdate { for (int i = 0; i < arrLen; i++) { if ((bitfield[i / 8] & 1 << i % 8) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -136,22 +147,31 @@ public class ActiveAnimationsUpdate extends ComponentUpdate { } else if (activeAnimationsCount > 4096000) { return ValidationResult.error("ActiveAnimations exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(activeAnimationsCount); + int activeAnimationsBitfieldSize = (activeAnimationsCount + 7) / 8; + if (pos + activeAnimationsBitfieldSize > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading bitfield for ActiveAnimations"); + } else { + byte[] activeAnimationsBitfield = PacketIO.readBytes(buffer, pos, activeAnimationsBitfieldSize); + pos += activeAnimationsBitfieldSize; - for (int i = 0; i < activeAnimationsCount; i++) { - int strLen = VarInt.peek(buffer, pos); - if (strLen < 0) { - return ValidationResult.error("Invalid string length in ActiveAnimations"); + for (int i = 0; i < activeAnimationsCount; i++) { + if ((activeAnimationsBitfield[i / 8] & 1 << i % 8) != 0) { + int strLen = VarInt.peek(buffer, pos); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in activeAnimations"); + } + + pos += VarInt.size(strLen); + pos += strLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in activeAnimations"); + } + } } - pos += VarInt.length(buffer, pos); - pos += strLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in ActiveAnimations"); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } } diff --git a/src/com/hypixel/hytale/protocol/AmbienceFX.java b/src/com/hypixel/hytale/protocol/AmbienceFX.java index ee27bc68..c7cece1b 100644 --- a/src/com/hypixel/hytale/protocol/AmbienceFX.java +++ b/src/com/hypixel/hytale/protocol/AmbienceFX.java @@ -72,92 +72,131 @@ public class AmbienceFX { @Nonnull public static AmbienceFX deserialize(@Nonnull ByteBuf buf, int offset) { - AmbienceFX obj = new AmbienceFX(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.soundEffect = AmbienceFXSoundEffect.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 42) { + throw ProtocolException.bufferTooSmall("AmbienceFX", 42, buf.readableBytes() - offset); + } else { + AmbienceFX obj = new AmbienceFX(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.soundEffect = AmbienceFXSoundEffect.deserialize(buf, offset + 1); + } + + obj.priority = buf.getIntLE(offset + 10); + obj.audioCategoryIndex = buf.getIntLE(offset + 14); + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 18); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 42 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 22); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Conditions", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 42 + varPosBase1; + obj.conditions = AmbienceFXConditions.deserialize(buf, varPos1); + } + + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 26); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Sounds", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 42 + varPosBase2; + int soundsCount = VarInt.peek(buf, varPos2); + if (soundsCount < 0) { + throw ProtocolException.invalidVarInt("Sounds"); + } + + int varIntLen = VarInt.size(soundsCount); + if (soundsCount > 4096000) { + throw ProtocolException.arrayTooLong("Sounds", soundsCount, 4096000); + } + + if (varPos2 + varIntLen + soundsCount * 33L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Sounds", varPos2 + varIntLen + soundsCount * 33, buf.readableBytes()); + } + + obj.sounds = new AmbienceFXSound[soundsCount]; + int elemPos = varPos2 + varIntLen; + + for (int i = 0; i < soundsCount; i++) { + obj.sounds[i] = AmbienceFXSound.deserialize(buf, elemPos); + elemPos += AmbienceFXSound.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase3 = buf.getIntLE(offset + 30); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Music", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 42 + varPosBase3; + obj.music = AmbienceFXMusic.deserialize(buf, varPos3); + } + + if ((nullBits & 32) != 0) { + int varPosBase4 = buf.getIntLE(offset + 34); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("AmbientBed", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 42 + varPosBase4; + obj.ambientBed = AmbienceFXAmbientBed.deserialize(buf, varPos4); + } + + if ((nullBits & 64) != 0) { + int varPosBase5 = buf.getIntLE(offset + 38); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("BlockedAmbienceFxIndices", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 42 + varPosBase5; + int blockedAmbienceFxIndicesCount = VarInt.peek(buf, varPos5); + if (blockedAmbienceFxIndicesCount < 0) { + throw ProtocolException.invalidVarInt("BlockedAmbienceFxIndices"); + } + + int varIntLenx = VarInt.size(blockedAmbienceFxIndicesCount); + if (blockedAmbienceFxIndicesCount > 4096000) { + throw ProtocolException.arrayTooLong("BlockedAmbienceFxIndices", blockedAmbienceFxIndicesCount, 4096000); + } + + if (varPos5 + varIntLenx + blockedAmbienceFxIndicesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BlockedAmbienceFxIndices", varPos5 + varIntLenx + blockedAmbienceFxIndicesCount * 4, buf.readableBytes()); + } + + obj.blockedAmbienceFxIndices = new int[blockedAmbienceFxIndicesCount]; + + for (int i = 0; i < blockedAmbienceFxIndicesCount; i++) { + obj.blockedAmbienceFxIndices[i] = buf.getIntLE(varPos5 + varIntLenx + i * 4); + } + } + + return obj; } - - obj.priority = buf.getIntLE(offset + 10); - obj.audioCategoryIndex = buf.getIntLE(offset + 14); - if ((nullBits & 2) != 0) { - int varPos0 = offset + 42 + buf.getIntLE(offset + 18); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); - } - - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 42 + buf.getIntLE(offset + 22); - obj.conditions = AmbienceFXConditions.deserialize(buf, varPos1); - } - - if ((nullBits & 8) != 0) { - int varPos2 = offset + 42 + buf.getIntLE(offset + 26); - int soundsCount = VarInt.peek(buf, varPos2); - if (soundsCount < 0) { - throw ProtocolException.negativeLength("Sounds", soundsCount); - } - - if (soundsCount > 4096000) { - throw ProtocolException.arrayTooLong("Sounds", soundsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + soundsCount * 27L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Sounds", varPos2 + varIntLen + soundsCount * 27, buf.readableBytes()); - } - - obj.sounds = new AmbienceFXSound[soundsCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < soundsCount; i++) { - obj.sounds[i] = AmbienceFXSound.deserialize(buf, elemPos); - elemPos += AmbienceFXSound.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 16) != 0) { - int varPos3 = offset + 42 + buf.getIntLE(offset + 30); - obj.music = AmbienceFXMusic.deserialize(buf, varPos3); - } - - if ((nullBits & 32) != 0) { - int varPos4 = offset + 42 + buf.getIntLE(offset + 34); - obj.ambientBed = AmbienceFXAmbientBed.deserialize(buf, varPos4); - } - - if ((nullBits & 64) != 0) { - int varPos5 = offset + 42 + buf.getIntLE(offset + 38); - int blockedAmbienceFxIndicesCount = VarInt.peek(buf, varPos5); - if (blockedAmbienceFxIndicesCount < 0) { - throw ProtocolException.negativeLength("BlockedAmbienceFxIndices", blockedAmbienceFxIndicesCount); - } - - if (blockedAmbienceFxIndicesCount > 4096000) { - throw ProtocolException.arrayTooLong("BlockedAmbienceFxIndices", blockedAmbienceFxIndicesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + blockedAmbienceFxIndicesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("BlockedAmbienceFxIndices", varPos5 + varIntLen + blockedAmbienceFxIndicesCount * 4, buf.readableBytes()); - } - - obj.blockedAmbienceFxIndices = new int[blockedAmbienceFxIndicesCount]; - - for (int i = 0; i < blockedAmbienceFxIndicesCount; i++) { - obj.blockedAmbienceFxIndices[i] = buf.getIntLE(varPos5 + varIntLen + i * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -165,9 +204,13 @@ public class AmbienceFX { int maxEnd = 42; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 18); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 42 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -175,6 +218,10 @@ public class AmbienceFX { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 22); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Conditions", fieldOffset1, maxEnd); + } + int pos1 = offset + 42 + fieldOffset1; pos1 += AmbienceFXConditions.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -184,9 +231,13 @@ public class AmbienceFX { if ((nullBits & 8) != 0) { int fieldOffset2 = buf.getIntLE(offset + 26); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Sounds", fieldOffset2, maxEnd); + } + int pos2 = offset + 42 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += AmbienceFXSound.computeBytesConsumed(buf, pos2); @@ -199,6 +250,10 @@ public class AmbienceFX { if ((nullBits & 16) != 0) { int fieldOffset3 = buf.getIntLE(offset + 30); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Music", fieldOffset3, maxEnd); + } + int pos3 = offset + 42 + fieldOffset3; pos3 += AmbienceFXMusic.computeBytesConsumed(buf, pos3); if (pos3 - offset > maxEnd) { @@ -208,6 +263,10 @@ public class AmbienceFX { if ((nullBits & 32) != 0) { int fieldOffset4 = buf.getIntLE(offset + 34); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("AmbientBed", fieldOffset4, maxEnd); + } + int pos4 = offset + 42 + fieldOffset4; pos4 += AmbienceFXAmbientBed.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -217,9 +276,13 @@ public class AmbienceFX { if ((nullBits & 64) != 0) { int fieldOffset5 = buf.getIntLE(offset + 38); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("BlockedAmbienceFxIndices", fieldOffset5, maxEnd); + } + int pos5 = offset + 42 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + arrLen * 4; + pos5 += VarInt.size(arrLen) + arrLen * 4; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -351,7 +414,7 @@ public class AmbienceFX { } if (this.sounds != null) { - size += VarInt.size(this.sounds.length) + this.sounds.length * 27; + size += VarInt.size(this.sounds.length) + this.sounds.length * 33; } if (this.music != null) { @@ -376,15 +439,11 @@ public class AmbienceFX { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { int idOffset = buffer.getIntLE(offset + 18); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 42) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 42 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -394,7 +453,7 @@ public class AmbienceFX { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -403,15 +462,11 @@ public class AmbienceFX { if ((nullBits & 4) != 0) { int conditionsOffset = buffer.getIntLE(offset + 22); - if (conditionsOffset < 0) { + if (conditionsOffset < 0 || conditionsOffset > buffer.writerIndex() - offset - 42) { return ValidationResult.error("Invalid offset for Conditions"); } int posx = offset + 42 + conditionsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Conditions"); - } - ValidationResult conditionsResult = AmbienceFXConditions.validateStructure(buffer, posx); if (!conditionsResult.isValid()) { return ValidationResult.error("Invalid Conditions: " + conditionsResult.error()); @@ -422,16 +477,12 @@ public class AmbienceFX { if ((nullBits & 8) != 0) { int soundsOffset = buffer.getIntLE(offset + 26); - if (soundsOffset < 0) { + if (soundsOffset < 0 || soundsOffset > buffer.writerIndex() - offset - 42) { return ValidationResult.error("Invalid offset for Sounds"); } - int posxx = offset + 42 + soundsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Sounds"); - } - - int soundsCount = VarInt.peek(buffer, posxx); + int posx = offset + 42 + soundsOffset; + int soundsCount = VarInt.peek(buffer, posx); if (soundsCount < 0) { return ValidationResult.error("Invalid array count for Sounds"); } @@ -440,63 +491,51 @@ public class AmbienceFX { return ValidationResult.error("Sounds exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += soundsCount * 27; - if (posxx > buffer.writerIndex()) { + posx += VarInt.size(soundsCount); + posx += soundsCount * 33; + if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Sounds"); } } if ((nullBits & 16) != 0) { int musicOffset = buffer.getIntLE(offset + 30); - if (musicOffset < 0) { + if (musicOffset < 0 || musicOffset > buffer.writerIndex() - offset - 42) { return ValidationResult.error("Invalid offset for Music"); } - int posxxx = offset + 42 + musicOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Music"); - } - - ValidationResult musicResult = AmbienceFXMusic.validateStructure(buffer, posxxx); + int posxx = offset + 42 + musicOffset; + ValidationResult musicResult = AmbienceFXMusic.validateStructure(buffer, posxx); if (!musicResult.isValid()) { return ValidationResult.error("Invalid Music: " + musicResult.error()); } - posxxx += AmbienceFXMusic.computeBytesConsumed(buffer, posxxx); + posxx += AmbienceFXMusic.computeBytesConsumed(buffer, posxx); } if ((nullBits & 32) != 0) { int ambientBedOffset = buffer.getIntLE(offset + 34); - if (ambientBedOffset < 0) { + if (ambientBedOffset < 0 || ambientBedOffset > buffer.writerIndex() - offset - 42) { return ValidationResult.error("Invalid offset for AmbientBed"); } - int posxxxx = offset + 42 + ambientBedOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AmbientBed"); - } - - ValidationResult ambientBedResult = AmbienceFXAmbientBed.validateStructure(buffer, posxxxx); + int posxx = offset + 42 + ambientBedOffset; + ValidationResult ambientBedResult = AmbienceFXAmbientBed.validateStructure(buffer, posxx); if (!ambientBedResult.isValid()) { return ValidationResult.error("Invalid AmbientBed: " + ambientBedResult.error()); } - posxxxx += AmbienceFXAmbientBed.computeBytesConsumed(buffer, posxxxx); + posxx += AmbienceFXAmbientBed.computeBytesConsumed(buffer, posxx); } if ((nullBits & 64) != 0) { int blockedAmbienceFxIndicesOffset = buffer.getIntLE(offset + 38); - if (blockedAmbienceFxIndicesOffset < 0) { + if (blockedAmbienceFxIndicesOffset < 0 || blockedAmbienceFxIndicesOffset > buffer.writerIndex() - offset - 42) { return ValidationResult.error("Invalid offset for BlockedAmbienceFxIndices"); } - int posxxxxx = offset + 42 + blockedAmbienceFxIndicesOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockedAmbienceFxIndices"); - } - - int blockedAmbienceFxIndicesCount = VarInt.peek(buffer, posxxxxx); + int posxx = offset + 42 + blockedAmbienceFxIndicesOffset; + int blockedAmbienceFxIndicesCount = VarInt.peek(buffer, posxx); if (blockedAmbienceFxIndicesCount < 0) { return ValidationResult.error("Invalid array count for BlockedAmbienceFxIndices"); } @@ -505,9 +544,9 @@ public class AmbienceFX { return ValidationResult.error("BlockedAmbienceFxIndices exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += blockedAmbienceFxIndicesCount * 4; - if (posxxxxx > buffer.writerIndex()) { + posxx += VarInt.size(blockedAmbienceFxIndicesCount); + posxx += blockedAmbienceFxIndicesCount * 4; + if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading BlockedAmbienceFxIndices"); } } diff --git a/src/com/hypixel/hytale/protocol/AmbienceFXAmbientBed.java b/src/com/hypixel/hytale/protocol/AmbienceFXAmbientBed.java index f9602f68..8d5b61ad 100644 --- a/src/com/hypixel/hytale/protocol/AmbienceFXAmbientBed.java +++ b/src/com/hypixel/hytale/protocol/AmbienceFXAmbientBed.java @@ -38,27 +38,35 @@ public class AmbienceFXAmbientBed { @Nonnull public static AmbienceFXAmbientBed deserialize(@Nonnull ByteBuf buf, int offset) { - AmbienceFXAmbientBed obj = new AmbienceFXAmbientBed(); - byte nullBits = buf.getByte(offset); - obj.volume = buf.getFloatLE(offset + 1); - obj.transitionSpeed = AmbienceTransitionSpeed.fromValue(buf.getByte(offset + 5)); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int trackLen = VarInt.peek(buf, pos); - if (trackLen < 0) { - throw ProtocolException.negativeLength("Track", trackLen); + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("AmbienceFXAmbientBed", 6, buf.readableBytes() - offset); + } else { + AmbienceFXAmbientBed obj = new AmbienceFXAmbientBed(); + byte nullBits = buf.getByte(offset); + obj.volume = buf.getFloatLE(offset + 1); + obj.transitionSpeed = AmbienceTransitionSpeed.fromValue(buf.getByte(offset + 5)); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int trackLen = VarInt.peek(buf, pos); + if (trackLen < 0) { + throw ProtocolException.invalidVarInt("Track"); + } + + int trackVarLen = VarInt.size(trackLen); + if (trackLen > 4096000) { + throw ProtocolException.stringTooLong("Track", trackLen, 4096000); + } + + if (pos + trackVarLen + trackLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Track", pos + trackVarLen + trackLen, buf.readableBytes()); + } + + obj.track = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += trackVarLen + trackLen; } - if (trackLen > 4096000) { - throw ProtocolException.stringTooLong("Track", trackLen, 4096000); - } - - int trackVarLen = VarInt.length(buf, pos); - obj.track = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += trackVarLen + trackLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -66,7 +74,7 @@ public class AmbienceFXAmbientBed { int pos = offset + 6; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -100,25 +108,30 @@ public class AmbienceFXAmbientBed { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int trackLen = VarInt.peek(buffer, pos); - if (trackLen < 0) { - return ValidationResult.error("Invalid string length for Track"); + int v = buffer.getByte(offset + 5) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid AmbienceTransitionSpeed value for TransitionSpeed"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int trackLen = VarInt.peek(buffer, v); + if (trackLen < 0) { + return ValidationResult.error("Invalid string length for Track"); + } + + if (trackLen > 4096000) { + return ValidationResult.error("Track exceeds max length 4096000"); + } + + v += VarInt.size(trackLen); + v += trackLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Track"); + } } - if (trackLen > 4096000) { - return ValidationResult.error("Track exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += trackLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Track"); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/AmbienceFXBlockSoundSet.java b/src/com/hypixel/hytale/protocol/AmbienceFXBlockSoundSet.java index 82e7ba23..c4f229a8 100644 --- a/src/com/hypixel/hytale/protocol/AmbienceFXBlockSoundSet.java +++ b/src/com/hypixel/hytale/protocol/AmbienceFXBlockSoundSet.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -31,14 +32,18 @@ public class AmbienceFXBlockSoundSet { @Nonnull public static AmbienceFXBlockSoundSet deserialize(@Nonnull ByteBuf buf, int offset) { - AmbienceFXBlockSoundSet obj = new AmbienceFXBlockSoundSet(); - byte nullBits = buf.getByte(offset); - obj.blockSoundSetIndex = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.percent = Rangef.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("AmbienceFXBlockSoundSet", 13, buf.readableBytes() - offset); + } else { + AmbienceFXBlockSoundSet obj = new AmbienceFXBlockSoundSet(); + byte nullBits = buf.getByte(offset); + obj.blockSoundSetIndex = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.percent = Rangef.deserialize(buf, offset + 5); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -65,7 +70,12 @@ public class AmbienceFXBlockSoundSet { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 13 ? ValidationResult.error("Buffer too small: expected at least 13 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 13) { + return ValidationResult.error("Buffer too small: expected at least 13 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public AmbienceFXBlockSoundSet clone() { diff --git a/src/com/hypixel/hytale/protocol/AmbienceFXConditions.java b/src/com/hypixel/hytale/protocol/AmbienceFXConditions.java index 00d2c549..345f89ef 100644 --- a/src/com/hypixel/hytale/protocol/AmbienceFXConditions.java +++ b/src/com/hypixel/hytale/protocol/AmbienceFXConditions.java @@ -11,11 +11,11 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class AmbienceFXConditions { - public static final int NULLABLE_BIT_FIELD_SIZE = 2; - public static final int FIXED_BLOCK_SIZE = 41; - public static final int VARIABLE_FIELD_COUNT = 4; - public static final int VARIABLE_BLOCK_START = 57; - public static final int MAX_SIZE = 102400077; + public static final int NULLABLE_BIT_FIELD_SIZE = 3; + public static final int FIXED_BLOCK_SIZE = 101; + public static final int VARIABLE_FIELD_COUNT = 9; + public static final int VARIABLE_BLOCK_START = 137; + public static final int MAX_SIZE = 221184182; public boolean never; @Nullable public int[] environmentIndices; @@ -42,6 +42,34 @@ public class AmbienceFXConditions { public Rangeb globalLightLevel; @Nullable public Rangef dayTime; + @Nullable + public SpaceSize[] space; + @Nullable + public ShelterType[] shelter; + @Nullable + public SurfaceType[] surfaces; + @Nonnull + public RoofState roofState = RoofState.Any; + @Nullable + public Rangef spaceScaleRange; + @Nullable + public Rangef spaceScaleMinRange; + @Nullable + public Rangef spaceScaleMaxRange; + @Nullable + public Rangef escapedRayPercentRange; + @Nullable + public Rangef reflectionCoeffRange; + @Nullable + public Rangef absorptionCoeffRange; + @Nullable + public Rangef roofDistanceRange; + @Nullable + public AmbienceFXPhysicalMaterial[] surfacePhysicalMaterials; + public boolean surfacePhysicalMaterialsMatchAny; + @Nullable + public AmbienceFXPhysicalMaterial[] exteriorRoofPhysicalMaterials; + public boolean exteriorRoofPhysicalMaterialsMatchAny; public AmbienceFXConditions() { } @@ -62,7 +90,22 @@ public class AmbienceFXConditions { @Nullable Rangeb sunLightLevel, @Nullable Rangeb torchLightLevel, @Nullable Rangeb globalLightLevel, - @Nullable Rangef dayTime + @Nullable Rangef dayTime, + @Nullable SpaceSize[] space, + @Nullable ShelterType[] shelter, + @Nullable SurfaceType[] surfaces, + @Nonnull RoofState roofState, + @Nullable Rangef spaceScaleRange, + @Nullable Rangef spaceScaleMinRange, + @Nullable Rangef spaceScaleMaxRange, + @Nullable Rangef escapedRayPercentRange, + @Nullable Rangef reflectionCoeffRange, + @Nullable Rangef absorptionCoeffRange, + @Nullable Rangef roofDistanceRange, + @Nullable AmbienceFXPhysicalMaterial[] surfacePhysicalMaterials, + boolean surfacePhysicalMaterialsMatchAny, + @Nullable AmbienceFXPhysicalMaterial[] exteriorRoofPhysicalMaterials, + boolean exteriorRoofPhysicalMaterialsMatchAny ) { this.never = never; this.environmentIndices = environmentIndices; @@ -80,6 +123,21 @@ public class AmbienceFXConditions { this.torchLightLevel = torchLightLevel; this.globalLightLevel = globalLightLevel; this.dayTime = dayTime; + this.space = space; + this.shelter = shelter; + this.surfaces = surfaces; + this.roofState = roofState; + this.spaceScaleRange = spaceScaleRange; + this.spaceScaleMinRange = spaceScaleMinRange; + this.spaceScaleMaxRange = spaceScaleMaxRange; + this.escapedRayPercentRange = escapedRayPercentRange; + this.reflectionCoeffRange = reflectionCoeffRange; + this.absorptionCoeffRange = absorptionCoeffRange; + this.roofDistanceRange = roofDistanceRange; + this.surfacePhysicalMaterials = surfacePhysicalMaterials; + this.surfacePhysicalMaterialsMatchAny = surfacePhysicalMaterialsMatchAny; + this.exteriorRoofPhysicalMaterials = exteriorRoofPhysicalMaterials; + this.exteriorRoofPhysicalMaterialsMatchAny = exteriorRoofPhysicalMaterialsMatchAny; } public AmbienceFXConditions(@Nonnull AmbienceFXConditions other) { @@ -99,177 +157,419 @@ public class AmbienceFXConditions { this.torchLightLevel = other.torchLightLevel; this.globalLightLevel = other.globalLightLevel; this.dayTime = other.dayTime; + this.space = other.space; + this.shelter = other.shelter; + this.surfaces = other.surfaces; + this.roofState = other.roofState; + this.spaceScaleRange = other.spaceScaleRange; + this.spaceScaleMinRange = other.spaceScaleMinRange; + this.spaceScaleMaxRange = other.spaceScaleMaxRange; + this.escapedRayPercentRange = other.escapedRayPercentRange; + this.reflectionCoeffRange = other.reflectionCoeffRange; + this.absorptionCoeffRange = other.absorptionCoeffRange; + this.roofDistanceRange = other.roofDistanceRange; + this.surfacePhysicalMaterials = other.surfacePhysicalMaterials; + this.surfacePhysicalMaterialsMatchAny = other.surfacePhysicalMaterialsMatchAny; + this.exteriorRoofPhysicalMaterials = other.exteriorRoofPhysicalMaterials; + this.exteriorRoofPhysicalMaterialsMatchAny = other.exteriorRoofPhysicalMaterialsMatchAny; } @Nonnull public static AmbienceFXConditions deserialize(@Nonnull ByteBuf buf, int offset) { - AmbienceFXConditions obj = new AmbienceFXConditions(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.never = buf.getByte(offset + 2) != 0; - obj.environmentTagPatternIndex = buf.getIntLE(offset + 3); - obj.weatherTagPatternIndex = buf.getIntLE(offset + 7); - if ((nullBits[0] & 1) != 0) { - obj.altitude = Range.deserialize(buf, offset + 11); + if (buf.readableBytes() - offset < 137) { + throw ProtocolException.bufferTooSmall("AmbienceFXConditions", 137, buf.readableBytes() - offset); + } else { + AmbienceFXConditions obj = new AmbienceFXConditions(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 3); + obj.never = buf.getByte(offset + 3) != 0; + obj.environmentTagPatternIndex = buf.getIntLE(offset + 4); + obj.weatherTagPatternIndex = buf.getIntLE(offset + 8); + if ((nullBits[0] & 1) != 0) { + obj.altitude = Range.deserialize(buf, offset + 12); + } + + if ((nullBits[0] & 2) != 0) { + obj.walls = Rangeb.deserialize(buf, offset + 20); + } + + obj.roof = buf.getByte(offset + 22) != 0; + obj.roofMaterialTagPatternIndex = buf.getIntLE(offset + 23); + obj.floor = buf.getByte(offset + 27) != 0; + if ((nullBits[0] & 4) != 0) { + obj.sunLightLevel = Rangeb.deserialize(buf, offset + 28); + } + + if ((nullBits[0] & 8) != 0) { + obj.torchLightLevel = Rangeb.deserialize(buf, offset + 30); + } + + if ((nullBits[0] & 16) != 0) { + obj.globalLightLevel = Rangeb.deserialize(buf, offset + 32); + } + + if ((nullBits[0] & 32) != 0) { + obj.dayTime = Rangef.deserialize(buf, offset + 34); + } + + obj.roofState = RoofState.fromValue(buf.getByte(offset + 42)); + if ((nullBits[0] & 64) != 0) { + obj.spaceScaleRange = Rangef.deserialize(buf, offset + 43); + } + + if ((nullBits[0] & 128) != 0) { + obj.spaceScaleMinRange = Rangef.deserialize(buf, offset + 51); + } + + if ((nullBits[1] & 1) != 0) { + obj.spaceScaleMaxRange = Rangef.deserialize(buf, offset + 59); + } + + if ((nullBits[1] & 2) != 0) { + obj.escapedRayPercentRange = Rangef.deserialize(buf, offset + 67); + } + + if ((nullBits[1] & 4) != 0) { + obj.reflectionCoeffRange = Rangef.deserialize(buf, offset + 75); + } + + if ((nullBits[1] & 8) != 0) { + obj.absorptionCoeffRange = Rangef.deserialize(buf, offset + 83); + } + + if ((nullBits[1] & 16) != 0) { + obj.roofDistanceRange = Rangef.deserialize(buf, offset + 91); + } + + obj.surfacePhysicalMaterialsMatchAny = buf.getByte(offset + 99) != 0; + obj.exteriorRoofPhysicalMaterialsMatchAny = buf.getByte(offset + 100) != 0; + if ((nullBits[1] & 32) != 0) { + int varPosBase0 = buf.getIntLE(offset + 101); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("EnvironmentIndices", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 137 + varPosBase0; + int environmentIndicesCount = VarInt.peek(buf, varPos0); + if (environmentIndicesCount < 0) { + throw ProtocolException.invalidVarInt("EnvironmentIndices"); + } + + int varIntLen = VarInt.size(environmentIndicesCount); + if (environmentIndicesCount > 4096000) { + throw ProtocolException.arrayTooLong("EnvironmentIndices", environmentIndicesCount, 4096000); + } + + if (varPos0 + varIntLen + environmentIndicesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EnvironmentIndices", varPos0 + varIntLen + environmentIndicesCount * 4, buf.readableBytes()); + } + + obj.environmentIndices = new int[environmentIndicesCount]; + + for (int i = 0; i < environmentIndicesCount; i++) { + obj.environmentIndices[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); + } + } + + if ((nullBits[1] & 64) != 0) { + int varPosBase1 = buf.getIntLE(offset + 105); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("WeatherIndices", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 137 + varPosBase1; + int weatherIndicesCount = VarInt.peek(buf, varPos1); + if (weatherIndicesCount < 0) { + throw ProtocolException.invalidVarInt("WeatherIndices"); + } + + int varIntLenx = VarInt.size(weatherIndicesCount); + if (weatherIndicesCount > 4096000) { + throw ProtocolException.arrayTooLong("WeatherIndices", weatherIndicesCount, 4096000); + } + + if (varPos1 + varIntLenx + weatherIndicesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("WeatherIndices", varPos1 + varIntLenx + weatherIndicesCount * 4, buf.readableBytes()); + } + + obj.weatherIndices = new int[weatherIndicesCount]; + + for (int i = 0; i < weatherIndicesCount; i++) { + obj.weatherIndices[i] = buf.getIntLE(varPos1 + varIntLenx + i * 4); + } + } + + if ((nullBits[1] & 128) != 0) { + int varPosBase2 = buf.getIntLE(offset + 109); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("FluidFXIndices", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 137 + varPosBase2; + int fluidFXIndicesCount = VarInt.peek(buf, varPos2); + if (fluidFXIndicesCount < 0) { + throw ProtocolException.invalidVarInt("FluidFXIndices"); + } + + int varIntLenxx = VarInt.size(fluidFXIndicesCount); + if (fluidFXIndicesCount > 4096000) { + throw ProtocolException.arrayTooLong("FluidFXIndices", fluidFXIndicesCount, 4096000); + } + + if (varPos2 + varIntLenxx + fluidFXIndicesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FluidFXIndices", varPos2 + varIntLenxx + fluidFXIndicesCount * 4, buf.readableBytes()); + } + + obj.fluidFXIndices = new int[fluidFXIndicesCount]; + + for (int i = 0; i < fluidFXIndicesCount; i++) { + obj.fluidFXIndices[i] = buf.getIntLE(varPos2 + varIntLenxx + i * 4); + } + } + + if ((nullBits[2] & 1) != 0) { + int varPosBase3 = buf.getIntLE(offset + 113); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("SurroundingBlockSoundSets", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 137 + varPosBase3; + int surroundingBlockSoundSetsCount = VarInt.peek(buf, varPos3); + if (surroundingBlockSoundSetsCount < 0) { + throw ProtocolException.invalidVarInt("SurroundingBlockSoundSets"); + } + + int varIntLenxxx = VarInt.size(surroundingBlockSoundSetsCount); + if (surroundingBlockSoundSetsCount > 4096000) { + throw ProtocolException.arrayTooLong("SurroundingBlockSoundSets", surroundingBlockSoundSetsCount, 4096000); + } + + if (varPos3 + varIntLenxxx + surroundingBlockSoundSetsCount * 13L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "SurroundingBlockSoundSets", varPos3 + varIntLenxxx + surroundingBlockSoundSetsCount * 13, buf.readableBytes() + ); + } + + obj.surroundingBlockSoundSets = new AmbienceFXBlockSoundSet[surroundingBlockSoundSetsCount]; + int elemPos = varPos3 + varIntLenxxx; + + for (int i = 0; i < surroundingBlockSoundSetsCount; i++) { + obj.surroundingBlockSoundSets[i] = AmbienceFXBlockSoundSet.deserialize(buf, elemPos); + elemPos += AmbienceFXBlockSoundSet.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[2] & 2) != 0) { + int varPosBase4 = buf.getIntLE(offset + 117); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("Space", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 137 + varPosBase4; + int spaceCount = VarInt.peek(buf, varPos4); + if (spaceCount < 0) { + throw ProtocolException.invalidVarInt("Space"); + } + + int varIntLenxxxx = VarInt.size(spaceCount); + if (spaceCount > 4096000) { + throw ProtocolException.arrayTooLong("Space", spaceCount, 4096000); + } + + if (varPos4 + varIntLenxxxx + spaceCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Space", varPos4 + varIntLenxxxx + spaceCount * 1, buf.readableBytes()); + } + + obj.space = new SpaceSize[spaceCount]; + int elemPos = varPos4 + varIntLenxxxx; + + for (int i = 0; i < spaceCount; i++) { + obj.space[i] = SpaceSize.fromValue(buf.getByte(elemPos)); + elemPos++; + } + } + + if ((nullBits[2] & 4) != 0) { + int varPosBase5 = buf.getIntLE(offset + 121); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("Shelter", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 137 + varPosBase5; + int shelterCount = VarInt.peek(buf, varPos5); + if (shelterCount < 0) { + throw ProtocolException.invalidVarInt("Shelter"); + } + + int varIntLenxxxxx = VarInt.size(shelterCount); + if (shelterCount > 4096000) { + throw ProtocolException.arrayTooLong("Shelter", shelterCount, 4096000); + } + + if (varPos5 + varIntLenxxxxx + shelterCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Shelter", varPos5 + varIntLenxxxxx + shelterCount * 1, buf.readableBytes()); + } + + obj.shelter = new ShelterType[shelterCount]; + int elemPos = varPos5 + varIntLenxxxxx; + + for (int i = 0; i < shelterCount; i++) { + obj.shelter[i] = ShelterType.fromValue(buf.getByte(elemPos)); + elemPos++; + } + } + + if ((nullBits[2] & 8) != 0) { + int varPosBase6 = buf.getIntLE(offset + 125); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("Surfaces", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 137 + varPosBase6; + int surfacesCount = VarInt.peek(buf, varPos6); + if (surfacesCount < 0) { + throw ProtocolException.invalidVarInt("Surfaces"); + } + + int varIntLenxxxxxx = VarInt.size(surfacesCount); + if (surfacesCount > 4096000) { + throw ProtocolException.arrayTooLong("Surfaces", surfacesCount, 4096000); + } + + if (varPos6 + varIntLenxxxxxx + surfacesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Surfaces", varPos6 + varIntLenxxxxxx + surfacesCount * 1, buf.readableBytes()); + } + + obj.surfaces = new SurfaceType[surfacesCount]; + int elemPos = varPos6 + varIntLenxxxxxx; + + for (int i = 0; i < surfacesCount; i++) { + obj.surfaces[i] = SurfaceType.fromValue(buf.getByte(elemPos)); + elemPos++; + } + } + + if ((nullBits[2] & 16) != 0) { + int varPosBase7 = buf.getIntLE(offset + 129); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("SurfacePhysicalMaterials", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 137 + varPosBase7; + int surfacePhysicalMaterialsCount = VarInt.peek(buf, varPos7); + if (surfacePhysicalMaterialsCount < 0) { + throw ProtocolException.invalidVarInt("SurfacePhysicalMaterials"); + } + + int varIntLenxxxxxxx = VarInt.size(surfacePhysicalMaterialsCount); + if (surfacePhysicalMaterialsCount > 4096000) { + throw ProtocolException.arrayTooLong("SurfacePhysicalMaterials", surfacePhysicalMaterialsCount, 4096000); + } + + if (varPos7 + varIntLenxxxxxxx + surfacePhysicalMaterialsCount * 13L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "SurfacePhysicalMaterials", varPos7 + varIntLenxxxxxxx + surfacePhysicalMaterialsCount * 13, buf.readableBytes() + ); + } + + obj.surfacePhysicalMaterials = new AmbienceFXPhysicalMaterial[surfacePhysicalMaterialsCount]; + int elemPos = varPos7 + varIntLenxxxxxxx; + + for (int i = 0; i < surfacePhysicalMaterialsCount; i++) { + obj.surfacePhysicalMaterials[i] = AmbienceFXPhysicalMaterial.deserialize(buf, elemPos); + elemPos += AmbienceFXPhysicalMaterial.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[2] & 32) != 0) { + int varPosBase8 = buf.getIntLE(offset + 133); + if (varPosBase8 < 0 || varPosBase8 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("ExteriorRoofPhysicalMaterials", varPosBase8, buf.readableBytes()); + } + + int varPos8 = offset + 137 + varPosBase8; + int exteriorRoofPhysicalMaterialsCount = VarInt.peek(buf, varPos8); + if (exteriorRoofPhysicalMaterialsCount < 0) { + throw ProtocolException.invalidVarInt("ExteriorRoofPhysicalMaterials"); + } + + int varIntLenxxxxxxxx = VarInt.size(exteriorRoofPhysicalMaterialsCount); + if (exteriorRoofPhysicalMaterialsCount > 4096000) { + throw ProtocolException.arrayTooLong("ExteriorRoofPhysicalMaterials", exteriorRoofPhysicalMaterialsCount, 4096000); + } + + if (varPos8 + varIntLenxxxxxxxx + exteriorRoofPhysicalMaterialsCount * 13L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "ExteriorRoofPhysicalMaterials", varPos8 + varIntLenxxxxxxxx + exteriorRoofPhysicalMaterialsCount * 13, buf.readableBytes() + ); + } + + obj.exteriorRoofPhysicalMaterials = new AmbienceFXPhysicalMaterial[exteriorRoofPhysicalMaterialsCount]; + int elemPos = varPos8 + varIntLenxxxxxxxx; + + for (int i = 0; i < exteriorRoofPhysicalMaterialsCount; i++) { + obj.exteriorRoofPhysicalMaterials[i] = AmbienceFXPhysicalMaterial.deserialize(buf, elemPos); + elemPos += AmbienceFXPhysicalMaterial.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits[0] & 2) != 0) { - obj.walls = Rangeb.deserialize(buf, offset + 19); - } - - obj.roof = buf.getByte(offset + 21) != 0; - obj.roofMaterialTagPatternIndex = buf.getIntLE(offset + 22); - obj.floor = buf.getByte(offset + 26) != 0; - if ((nullBits[0] & 4) != 0) { - obj.sunLightLevel = Rangeb.deserialize(buf, offset + 27); - } - - if ((nullBits[0] & 8) != 0) { - obj.torchLightLevel = Rangeb.deserialize(buf, offset + 29); - } - - if ((nullBits[0] & 16) != 0) { - obj.globalLightLevel = Rangeb.deserialize(buf, offset + 31); - } - - if ((nullBits[0] & 32) != 0) { - obj.dayTime = Rangef.deserialize(buf, offset + 33); - } - - if ((nullBits[0] & 64) != 0) { - int varPos0 = offset + 57 + buf.getIntLE(offset + 41); - int environmentIndicesCount = VarInt.peek(buf, varPos0); - if (environmentIndicesCount < 0) { - throw ProtocolException.negativeLength("EnvironmentIndices", environmentIndicesCount); - } - - if (environmentIndicesCount > 4096000) { - throw ProtocolException.arrayTooLong("EnvironmentIndices", environmentIndicesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + environmentIndicesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("EnvironmentIndices", varPos0 + varIntLen + environmentIndicesCount * 4, buf.readableBytes()); - } - - obj.environmentIndices = new int[environmentIndicesCount]; - - for (int i = 0; i < environmentIndicesCount; i++) { - obj.environmentIndices[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); - } - } - - if ((nullBits[0] & 128) != 0) { - int varPos1 = offset + 57 + buf.getIntLE(offset + 45); - int weatherIndicesCount = VarInt.peek(buf, varPos1); - if (weatherIndicesCount < 0) { - throw ProtocolException.negativeLength("WeatherIndices", weatherIndicesCount); - } - - if (weatherIndicesCount > 4096000) { - throw ProtocolException.arrayTooLong("WeatherIndices", weatherIndicesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + weatherIndicesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("WeatherIndices", varPos1 + varIntLen + weatherIndicesCount * 4, buf.readableBytes()); - } - - obj.weatherIndices = new int[weatherIndicesCount]; - - for (int i = 0; i < weatherIndicesCount; i++) { - obj.weatherIndices[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); - } - } - - if ((nullBits[1] & 1) != 0) { - int varPos2 = offset + 57 + buf.getIntLE(offset + 49); - int fluidFXIndicesCount = VarInt.peek(buf, varPos2); - if (fluidFXIndicesCount < 0) { - throw ProtocolException.negativeLength("FluidFXIndices", fluidFXIndicesCount); - } - - if (fluidFXIndicesCount > 4096000) { - throw ProtocolException.arrayTooLong("FluidFXIndices", fluidFXIndicesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + fluidFXIndicesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FluidFXIndices", varPos2 + varIntLen + fluidFXIndicesCount * 4, buf.readableBytes()); - } - - obj.fluidFXIndices = new int[fluidFXIndicesCount]; - - for (int i = 0; i < fluidFXIndicesCount; i++) { - obj.fluidFXIndices[i] = buf.getIntLE(varPos2 + varIntLen + i * 4); - } - } - - if ((nullBits[1] & 2) != 0) { - int varPos3 = offset + 57 + buf.getIntLE(offset + 53); - int surroundingBlockSoundSetsCount = VarInt.peek(buf, varPos3); - if (surroundingBlockSoundSetsCount < 0) { - throw ProtocolException.negativeLength("SurroundingBlockSoundSets", surroundingBlockSoundSetsCount); - } - - if (surroundingBlockSoundSetsCount > 4096000) { - throw ProtocolException.arrayTooLong("SurroundingBlockSoundSets", surroundingBlockSoundSetsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + surroundingBlockSoundSetsCount * 13L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("SurroundingBlockSoundSets", varPos3 + varIntLen + surroundingBlockSoundSetsCount * 13, buf.readableBytes()); - } - - obj.surroundingBlockSoundSets = new AmbienceFXBlockSoundSet[surroundingBlockSoundSetsCount]; - int elemPos = varPos3 + varIntLen; - - for (int i = 0; i < surroundingBlockSoundSetsCount; i++) { - obj.surroundingBlockSoundSets[i] = AmbienceFXBlockSoundSet.deserialize(buf, elemPos); - elemPos += AmbienceFXBlockSoundSet.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - int maxEnd = 57; - if ((nullBits[0] & 64) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 41); - int pos0 = offset + 57 + fieldOffset0; + byte[] nullBits = PacketIO.readBytes(buf, offset, 3); + int maxEnd = 137; + if ((nullBits[1] & 32) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 101); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("EnvironmentIndices", fieldOffset0, maxEnd); + } + + int pos0 = offset + 137 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 4; + pos0 += VarInt.size(arrLen) + arrLen * 4; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } } - if ((nullBits[0] & 128) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 45); - int pos1 = offset + 57 + fieldOffset1; + if ((nullBits[1] & 64) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 105); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("WeatherIndices", fieldOffset1, maxEnd); + } + + int pos1 = offset + 137 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 4; + pos1 += VarInt.size(arrLen) + arrLen * 4; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } } - if ((nullBits[1] & 1) != 0) { - int fieldOffset2 = buf.getIntLE(offset + 49); - int pos2 = offset + 57 + fieldOffset2; + if ((nullBits[1] & 128) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 109); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("FluidFXIndices", fieldOffset2, maxEnd); + } + + int pos2 = offset + 137 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + arrLen * 4; + pos2 += VarInt.size(arrLen) + arrLen * 4; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } } - if ((nullBits[1] & 2) != 0) { - int fieldOffset3 = buf.getIntLE(offset + 53); - int pos3 = offset + 57 + fieldOffset3; + if ((nullBits[2] & 1) != 0) { + int fieldOffset3 = buf.getIntLE(offset + 113); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("SurroundingBlockSoundSets", fieldOffset3, maxEnd); + } + + int pos3 = offset + 137 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos3 += AmbienceFXBlockSoundSet.computeBytesConsumed(buf, pos3); @@ -280,12 +580,92 @@ public class AmbienceFXConditions { } } + if ((nullBits[2] & 2) != 0) { + int fieldOffset4 = buf.getIntLE(offset + 117); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("Space", fieldOffset4, maxEnd); + } + + int pos4 = offset + 137 + fieldOffset4; + int arrLen = VarInt.peek(buf, pos4); + pos4 += VarInt.size(arrLen) + arrLen * 1; + if (pos4 - offset > maxEnd) { + maxEnd = pos4 - offset; + } + } + + if ((nullBits[2] & 4) != 0) { + int fieldOffset5 = buf.getIntLE(offset + 121); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("Shelter", fieldOffset5, maxEnd); + } + + int pos5 = offset + 137 + fieldOffset5; + int arrLen = VarInt.peek(buf, pos5); + pos5 += VarInt.size(arrLen) + arrLen * 1; + if (pos5 - offset > maxEnd) { + maxEnd = pos5 - offset; + } + } + + if ((nullBits[2] & 8) != 0) { + int fieldOffset6 = buf.getIntLE(offset + 125); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("Surfaces", fieldOffset6, maxEnd); + } + + int pos6 = offset + 137 + fieldOffset6; + int arrLen = VarInt.peek(buf, pos6); + pos6 += VarInt.size(arrLen) + arrLen * 1; + if (pos6 - offset > maxEnd) { + maxEnd = pos6 - offset; + } + } + + if ((nullBits[2] & 16) != 0) { + int fieldOffset7 = buf.getIntLE(offset + 129); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("SurfacePhysicalMaterials", fieldOffset7, maxEnd); + } + + int pos7 = offset + 137 + fieldOffset7; + int arrLen = VarInt.peek(buf, pos7); + pos7 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos7 += AmbienceFXPhysicalMaterial.computeBytesConsumed(buf, pos7); + } + + if (pos7 - offset > maxEnd) { + maxEnd = pos7 - offset; + } + } + + if ((nullBits[2] & 32) != 0) { + int fieldOffset8 = buf.getIntLE(offset + 133); + if (fieldOffset8 < 0 || fieldOffset8 > buf.writerIndex() - offset - 137) { + throw ProtocolException.invalidOffset("ExteriorRoofPhysicalMaterials", fieldOffset8, maxEnd); + } + + int pos8 = offset + 137 + fieldOffset8; + int arrLen = VarInt.peek(buf, pos8); + pos8 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos8 += AmbienceFXPhysicalMaterial.computeBytesConsumed(buf, pos8); + } + + if (pos8 - offset > maxEnd) { + maxEnd = pos8 - offset; + } + } + return maxEnd; } public void serialize(@Nonnull ByteBuf buf) { int startPos = buf.writerIndex(); - byte[] nullBits = new byte[2]; + byte[] nullBits = new byte[3]; if (this.altitude != null) { nullBits[0] = (byte)(nullBits[0] | 1); } @@ -310,22 +690,70 @@ public class AmbienceFXConditions { nullBits[0] = (byte)(nullBits[0] | 32); } - if (this.environmentIndices != null) { + if (this.spaceScaleRange != null) { nullBits[0] = (byte)(nullBits[0] | 64); } - if (this.weatherIndices != null) { + if (this.spaceScaleMinRange != null) { nullBits[0] = (byte)(nullBits[0] | 128); } - if (this.fluidFXIndices != null) { + if (this.spaceScaleMaxRange != null) { nullBits[1] = (byte)(nullBits[1] | 1); } - if (this.surroundingBlockSoundSets != null) { + if (this.escapedRayPercentRange != null) { nullBits[1] = (byte)(nullBits[1] | 2); } + if (this.reflectionCoeffRange != null) { + nullBits[1] = (byte)(nullBits[1] | 4); + } + + if (this.absorptionCoeffRange != null) { + nullBits[1] = (byte)(nullBits[1] | 8); + } + + if (this.roofDistanceRange != null) { + nullBits[1] = (byte)(nullBits[1] | 16); + } + + if (this.environmentIndices != null) { + nullBits[1] = (byte)(nullBits[1] | 32); + } + + if (this.weatherIndices != null) { + nullBits[1] = (byte)(nullBits[1] | 64); + } + + if (this.fluidFXIndices != null) { + nullBits[1] = (byte)(nullBits[1] | 128); + } + + if (this.surroundingBlockSoundSets != null) { + nullBits[2] = (byte)(nullBits[2] | 1); + } + + if (this.space != null) { + nullBits[2] = (byte)(nullBits[2] | 2); + } + + if (this.shelter != null) { + nullBits[2] = (byte)(nullBits[2] | 4); + } + + if (this.surfaces != null) { + nullBits[2] = (byte)(nullBits[2] | 8); + } + + if (this.surfacePhysicalMaterials != null) { + nullBits[2] = (byte)(nullBits[2] | 16); + } + + if (this.exteriorRoofPhysicalMaterials != null) { + nullBits[2] = (byte)(nullBits[2] | 32); + } + buf.writeBytes(nullBits); buf.writeByte(this.never ? 1 : 0); buf.writeIntLE(this.environmentTagPatternIndex); @@ -369,6 +797,51 @@ public class AmbienceFXConditions { buf.writeZero(8); } + buf.writeByte(this.roofState.getValue()); + if (this.spaceScaleRange != null) { + this.spaceScaleRange.serialize(buf); + } else { + buf.writeZero(8); + } + + if (this.spaceScaleMinRange != null) { + this.spaceScaleMinRange.serialize(buf); + } else { + buf.writeZero(8); + } + + if (this.spaceScaleMaxRange != null) { + this.spaceScaleMaxRange.serialize(buf); + } else { + buf.writeZero(8); + } + + if (this.escapedRayPercentRange != null) { + this.escapedRayPercentRange.serialize(buf); + } else { + buf.writeZero(8); + } + + if (this.reflectionCoeffRange != null) { + this.reflectionCoeffRange.serialize(buf); + } else { + buf.writeZero(8); + } + + if (this.absorptionCoeffRange != null) { + this.absorptionCoeffRange.serialize(buf); + } else { + buf.writeZero(8); + } + + if (this.roofDistanceRange != null) { + this.roofDistanceRange.serialize(buf); + } else { + buf.writeZero(8); + } + + buf.writeByte(this.surfacePhysicalMaterialsMatchAny ? 1 : 0); + buf.writeByte(this.exteriorRoofPhysicalMaterialsMatchAny ? 1 : 0); int environmentIndicesOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int weatherIndicesOffsetSlot = buf.writerIndex(); @@ -377,6 +850,16 @@ public class AmbienceFXConditions { buf.writeIntLE(0); int surroundingBlockSoundSetsOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int spaceOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int shelterOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int surfacesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int surfacePhysicalMaterialsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int exteriorRoofPhysicalMaterialsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); if (this.environmentIndices != null) { buf.setIntLE(environmentIndicesOffsetSlot, buf.writerIndex() - varBlockStart); @@ -437,10 +920,85 @@ public class AmbienceFXConditions { } else { buf.setIntLE(surroundingBlockSoundSetsOffsetSlot, -1); } + + if (this.space != null) { + buf.setIntLE(spaceOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.space.length > 4096000) { + throw ProtocolException.arrayTooLong("Space", this.space.length, 4096000); + } + + VarInt.write(buf, this.space.length); + + for (SpaceSize item : this.space) { + buf.writeByte(item.getValue()); + } + } else { + buf.setIntLE(spaceOffsetSlot, -1); + } + + if (this.shelter != null) { + buf.setIntLE(shelterOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.shelter.length > 4096000) { + throw ProtocolException.arrayTooLong("Shelter", this.shelter.length, 4096000); + } + + VarInt.write(buf, this.shelter.length); + + for (ShelterType item : this.shelter) { + buf.writeByte(item.getValue()); + } + } else { + buf.setIntLE(shelterOffsetSlot, -1); + } + + if (this.surfaces != null) { + buf.setIntLE(surfacesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.surfaces.length > 4096000) { + throw ProtocolException.arrayTooLong("Surfaces", this.surfaces.length, 4096000); + } + + VarInt.write(buf, this.surfaces.length); + + for (SurfaceType item : this.surfaces) { + buf.writeByte(item.getValue()); + } + } else { + buf.setIntLE(surfacesOffsetSlot, -1); + } + + if (this.surfacePhysicalMaterials != null) { + buf.setIntLE(surfacePhysicalMaterialsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.surfacePhysicalMaterials.length > 4096000) { + throw ProtocolException.arrayTooLong("SurfacePhysicalMaterials", this.surfacePhysicalMaterials.length, 4096000); + } + + VarInt.write(buf, this.surfacePhysicalMaterials.length); + + for (AmbienceFXPhysicalMaterial item : this.surfacePhysicalMaterials) { + item.serialize(buf); + } + } else { + buf.setIntLE(surfacePhysicalMaterialsOffsetSlot, -1); + } + + if (this.exteriorRoofPhysicalMaterials != null) { + buf.setIntLE(exteriorRoofPhysicalMaterialsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.exteriorRoofPhysicalMaterials.length > 4096000) { + throw ProtocolException.arrayTooLong("ExteriorRoofPhysicalMaterials", this.exteriorRoofPhysicalMaterials.length, 4096000); + } + + VarInt.write(buf, this.exteriorRoofPhysicalMaterials.length); + + for (AmbienceFXPhysicalMaterial item : this.exteriorRoofPhysicalMaterials) { + item.serialize(buf); + } + } else { + buf.setIntLE(exteriorRoofPhysicalMaterialsOffsetSlot, -1); + } } public int computeSize() { - int size = 57; + int size = 137; if (this.environmentIndices != null) { size += VarInt.size(this.environmentIndices.length) + this.environmentIndices.length * 4; } @@ -457,123 +1015,271 @@ public class AmbienceFXConditions { size += VarInt.size(this.surroundingBlockSoundSets.length) + this.surroundingBlockSoundSets.length * 13; } + if (this.space != null) { + size += VarInt.size(this.space.length) + this.space.length * 1; + } + + if (this.shelter != null) { + size += VarInt.size(this.shelter.length) + this.shelter.length * 1; + } + + if (this.surfaces != null) { + size += VarInt.size(this.surfaces.length) + this.surfaces.length * 1; + } + + if (this.surfacePhysicalMaterials != null) { + size += VarInt.size(this.surfacePhysicalMaterials.length) + this.surfacePhysicalMaterials.length * 13; + } + + if (this.exteriorRoofPhysicalMaterials != null) { + size += VarInt.size(this.exteriorRoofPhysicalMaterials.length) + this.exteriorRoofPhysicalMaterials.length * 13; + } + return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 57) { - return ValidationResult.error("Buffer too small: expected at least 57 bytes"); + if (buffer.readableBytes() - offset < 137) { + return ValidationResult.error("Buffer too small: expected at least 137 bytes"); } else { - byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[0] & 64) != 0) { - int environmentIndicesOffset = buffer.getIntLE(offset + 41); - if (environmentIndicesOffset < 0) { - return ValidationResult.error("Invalid offset for EnvironmentIndices"); + byte[] nullBits = PacketIO.readBytes(buffer, offset, 3); + int v = buffer.getByte(offset + 42) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid RoofState value for RoofState"); + } else { + if ((nullBits[1] & 32) != 0) { + v = buffer.getIntLE(offset + 101); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for EnvironmentIndices"); + } + + int pos = offset + 137 + v; + int environmentIndicesCount = VarInt.peek(buffer, pos); + if (environmentIndicesCount < 0) { + return ValidationResult.error("Invalid array count for EnvironmentIndices"); + } + + if (environmentIndicesCount > 4096000) { + return ValidationResult.error("EnvironmentIndices exceeds max length 4096000"); + } + + pos += VarInt.size(environmentIndicesCount); + pos += environmentIndicesCount * 4; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading EnvironmentIndices"); + } } - int pos = offset + 57 + environmentIndicesOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EnvironmentIndices"); + if ((nullBits[1] & 64) != 0) { + v = buffer.getIntLE(offset + 105); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for WeatherIndices"); + } + + int posx = offset + 137 + v; + int weatherIndicesCount = VarInt.peek(buffer, posx); + if (weatherIndicesCount < 0) { + return ValidationResult.error("Invalid array count for WeatherIndices"); + } + + if (weatherIndicesCount > 4096000) { + return ValidationResult.error("WeatherIndices exceeds max length 4096000"); + } + + posx += VarInt.size(weatherIndicesCount); + posx += weatherIndicesCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading WeatherIndices"); + } } - int environmentIndicesCount = VarInt.peek(buffer, pos); - if (environmentIndicesCount < 0) { - return ValidationResult.error("Invalid array count for EnvironmentIndices"); + if ((nullBits[1] & 128) != 0) { + v = buffer.getIntLE(offset + 109); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for FluidFXIndices"); + } + + int posxx = offset + 137 + v; + int fluidFXIndicesCount = VarInt.peek(buffer, posxx); + if (fluidFXIndicesCount < 0) { + return ValidationResult.error("Invalid array count for FluidFXIndices"); + } + + if (fluidFXIndicesCount > 4096000) { + return ValidationResult.error("FluidFXIndices exceeds max length 4096000"); + } + + posxx += VarInt.size(fluidFXIndicesCount); + posxx += fluidFXIndicesCount * 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading FluidFXIndices"); + } } - if (environmentIndicesCount > 4096000) { - return ValidationResult.error("EnvironmentIndices exceeds max length 4096000"); + if ((nullBits[2] & 1) != 0) { + v = buffer.getIntLE(offset + 113); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for SurroundingBlockSoundSets"); + } + + int posxxx = offset + 137 + v; + int surroundingBlockSoundSetsCount = VarInt.peek(buffer, posxxx); + if (surroundingBlockSoundSetsCount < 0) { + return ValidationResult.error("Invalid array count for SurroundingBlockSoundSets"); + } + + if (surroundingBlockSoundSetsCount > 4096000) { + return ValidationResult.error("SurroundingBlockSoundSets exceeds max length 4096000"); + } + + posxxx += VarInt.size(surroundingBlockSoundSetsCount); + posxxx += surroundingBlockSoundSetsCount * 13; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading SurroundingBlockSoundSets"); + } } - pos += VarInt.length(buffer, pos); - pos += environmentIndicesCount * 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading EnvironmentIndices"); + if ((nullBits[2] & 2) != 0) { + v = buffer.getIntLE(offset + 117); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for Space"); + } + + int posxxxx = offset + 137 + v; + int spaceCount = VarInt.peek(buffer, posxxxx); + if (spaceCount < 0) { + return ValidationResult.error("Invalid array count for Space"); + } + + if (spaceCount > 4096000) { + return ValidationResult.error("Space exceeds max length 4096000"); + } + + posxxxx += VarInt.size(spaceCount); + if (posxxxx + spaceCount * 1L > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Space"); + } + + for (int i = 0; i < spaceCount; i++) { + int vx = buffer.getByte(posxxxx) & 255; + if (vx >= 6) { + return ValidationResult.error("Invalid SpaceSize value for Space[i]"); + } + + posxxxx++; + } } + + if ((nullBits[2] & 4) != 0) { + v = buffer.getIntLE(offset + 121); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for Shelter"); + } + + int posxxxxx = offset + 137 + v; + int shelterCount = VarInt.peek(buffer, posxxxxx); + if (shelterCount < 0) { + return ValidationResult.error("Invalid array count for Shelter"); + } + + if (shelterCount > 4096000) { + return ValidationResult.error("Shelter exceeds max length 4096000"); + } + + posxxxxx += VarInt.size(shelterCount); + if (posxxxxx + shelterCount * 1L > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Shelter"); + } + + for (int i = 0; i < shelterCount; i++) { + int vx = buffer.getByte(posxxxxx) & 255; + if (vx >= 5) { + return ValidationResult.error("Invalid ShelterType value for Shelter[i]"); + } + + posxxxxx++; + } + } + + if ((nullBits[2] & 8) != 0) { + v = buffer.getIntLE(offset + 125); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for Surfaces"); + } + + int posxxxxxx = offset + 137 + v; + int surfacesCount = VarInt.peek(buffer, posxxxxxx); + if (surfacesCount < 0) { + return ValidationResult.error("Invalid array count for Surfaces"); + } + + if (surfacesCount > 4096000) { + return ValidationResult.error("Surfaces exceeds max length 4096000"); + } + + posxxxxxx += VarInt.size(surfacesCount); + if (posxxxxxx + surfacesCount * 1L > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Surfaces"); + } + + for (int i = 0; i < surfacesCount; i++) { + int vx = buffer.getByte(posxxxxxx) & 255; + if (vx >= 4) { + return ValidationResult.error("Invalid SurfaceType value for Surfaces[i]"); + } + + posxxxxxx++; + } + } + + if ((nullBits[2] & 16) != 0) { + v = buffer.getIntLE(offset + 129); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for SurfacePhysicalMaterials"); + } + + int posxxxxxxx = offset + 137 + v; + int surfacePhysicalMaterialsCount = VarInt.peek(buffer, posxxxxxxx); + if (surfacePhysicalMaterialsCount < 0) { + return ValidationResult.error("Invalid array count for SurfacePhysicalMaterials"); + } + + if (surfacePhysicalMaterialsCount > 4096000) { + return ValidationResult.error("SurfacePhysicalMaterials exceeds max length 4096000"); + } + + posxxxxxxx += VarInt.size(surfacePhysicalMaterialsCount); + posxxxxxxx += surfacePhysicalMaterialsCount * 13; + if (posxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading SurfacePhysicalMaterials"); + } + } + + if ((nullBits[2] & 32) != 0) { + v = buffer.getIntLE(offset + 133); + if (v < 0 || v > buffer.writerIndex() - offset - 137) { + return ValidationResult.error("Invalid offset for ExteriorRoofPhysicalMaterials"); + } + + int posxxxxxxxx = offset + 137 + v; + int exteriorRoofPhysicalMaterialsCount = VarInt.peek(buffer, posxxxxxxxx); + if (exteriorRoofPhysicalMaterialsCount < 0) { + return ValidationResult.error("Invalid array count for ExteriorRoofPhysicalMaterials"); + } + + if (exteriorRoofPhysicalMaterialsCount > 4096000) { + return ValidationResult.error("ExteriorRoofPhysicalMaterials exceeds max length 4096000"); + } + + posxxxxxxxx += VarInt.size(exteriorRoofPhysicalMaterialsCount); + posxxxxxxxx += exteriorRoofPhysicalMaterialsCount * 13; + if (posxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ExteriorRoofPhysicalMaterials"); + } + } + + return ValidationResult.OK; } - - if ((nullBits[0] & 128) != 0) { - int weatherIndicesOffset = buffer.getIntLE(offset + 45); - if (weatherIndicesOffset < 0) { - return ValidationResult.error("Invalid offset for WeatherIndices"); - } - - int posx = offset + 57 + weatherIndicesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for WeatherIndices"); - } - - int weatherIndicesCount = VarInt.peek(buffer, posx); - if (weatherIndicesCount < 0) { - return ValidationResult.error("Invalid array count for WeatherIndices"); - } - - if (weatherIndicesCount > 4096000) { - return ValidationResult.error("WeatherIndices exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += weatherIndicesCount * 4; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading WeatherIndices"); - } - } - - if ((nullBits[1] & 1) != 0) { - int fluidFXIndicesOffset = buffer.getIntLE(offset + 49); - if (fluidFXIndicesOffset < 0) { - return ValidationResult.error("Invalid offset for FluidFXIndices"); - } - - int posxx = offset + 57 + fluidFXIndicesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FluidFXIndices"); - } - - int fluidFXIndicesCount = VarInt.peek(buffer, posxx); - if (fluidFXIndicesCount < 0) { - return ValidationResult.error("Invalid array count for FluidFXIndices"); - } - - if (fluidFXIndicesCount > 4096000) { - return ValidationResult.error("FluidFXIndices exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += fluidFXIndicesCount * 4; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading FluidFXIndices"); - } - } - - if ((nullBits[1] & 2) != 0) { - int surroundingBlockSoundSetsOffset = buffer.getIntLE(offset + 53); - if (surroundingBlockSoundSetsOffset < 0) { - return ValidationResult.error("Invalid offset for SurroundingBlockSoundSets"); - } - - int posxxx = offset + 57 + surroundingBlockSoundSetsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SurroundingBlockSoundSets"); - } - - int surroundingBlockSoundSetsCount = VarInt.peek(buffer, posxxx); - if (surroundingBlockSoundSetsCount < 0) { - return ValidationResult.error("Invalid array count for SurroundingBlockSoundSets"); - } - - if (surroundingBlockSoundSetsCount > 4096000) { - return ValidationResult.error("SurroundingBlockSoundSets exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += surroundingBlockSoundSetsCount * 13; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading SurroundingBlockSoundSets"); - } - } - - return ValidationResult.OK; } } @@ -597,6 +1303,25 @@ public class AmbienceFXConditions { copy.torchLightLevel = this.torchLightLevel != null ? this.torchLightLevel.clone() : null; copy.globalLightLevel = this.globalLightLevel != null ? this.globalLightLevel.clone() : null; copy.dayTime = this.dayTime != null ? this.dayTime.clone() : null; + copy.space = this.space != null ? Arrays.copyOf(this.space, this.space.length) : null; + copy.shelter = this.shelter != null ? Arrays.copyOf(this.shelter, this.shelter.length) : null; + copy.surfaces = this.surfaces != null ? Arrays.copyOf(this.surfaces, this.surfaces.length) : null; + copy.roofState = this.roofState; + copy.spaceScaleRange = this.spaceScaleRange != null ? this.spaceScaleRange.clone() : null; + copy.spaceScaleMinRange = this.spaceScaleMinRange != null ? this.spaceScaleMinRange.clone() : null; + copy.spaceScaleMaxRange = this.spaceScaleMaxRange != null ? this.spaceScaleMaxRange.clone() : null; + copy.escapedRayPercentRange = this.escapedRayPercentRange != null ? this.escapedRayPercentRange.clone() : null; + copy.reflectionCoeffRange = this.reflectionCoeffRange != null ? this.reflectionCoeffRange.clone() : null; + copy.absorptionCoeffRange = this.absorptionCoeffRange != null ? this.absorptionCoeffRange.clone() : null; + copy.roofDistanceRange = this.roofDistanceRange != null ? this.roofDistanceRange.clone() : null; + copy.surfacePhysicalMaterials = this.surfacePhysicalMaterials != null + ? Arrays.stream(this.surfacePhysicalMaterials).map(e -> e.clone()).toArray(AmbienceFXPhysicalMaterial[]::new) + : null; + copy.surfacePhysicalMaterialsMatchAny = this.surfacePhysicalMaterialsMatchAny; + copy.exteriorRoofPhysicalMaterials = this.exteriorRoofPhysicalMaterials != null + ? Arrays.stream(this.exteriorRoofPhysicalMaterials).map(e -> e.clone()).toArray(AmbienceFXPhysicalMaterial[]::new) + : null; + copy.exteriorRoofPhysicalMaterialsMatchAny = this.exteriorRoofPhysicalMaterialsMatchAny; return copy; } @@ -622,7 +1347,22 @@ public class AmbienceFXConditions { && Objects.equals(this.sunLightLevel, other.sunLightLevel) && Objects.equals(this.torchLightLevel, other.torchLightLevel) && Objects.equals(this.globalLightLevel, other.globalLightLevel) - && Objects.equals(this.dayTime, other.dayTime); + && Objects.equals(this.dayTime, other.dayTime) + && Arrays.equals((Object[])this.space, (Object[])other.space) + && Arrays.equals((Object[])this.shelter, (Object[])other.shelter) + && Arrays.equals((Object[])this.surfaces, (Object[])other.surfaces) + && Objects.equals(this.roofState, other.roofState) + && Objects.equals(this.spaceScaleRange, other.spaceScaleRange) + && Objects.equals(this.spaceScaleMinRange, other.spaceScaleMinRange) + && Objects.equals(this.spaceScaleMaxRange, other.spaceScaleMaxRange) + && Objects.equals(this.escapedRayPercentRange, other.escapedRayPercentRange) + && Objects.equals(this.reflectionCoeffRange, other.reflectionCoeffRange) + && Objects.equals(this.absorptionCoeffRange, other.absorptionCoeffRange) + && Objects.equals(this.roofDistanceRange, other.roofDistanceRange) + && Arrays.equals((Object[])this.surfacePhysicalMaterials, (Object[])other.surfacePhysicalMaterials) + && this.surfacePhysicalMaterialsMatchAny == other.surfacePhysicalMaterialsMatchAny + && Arrays.equals((Object[])this.exteriorRoofPhysicalMaterials, (Object[])other.exteriorRoofPhysicalMaterials) + && this.exteriorRoofPhysicalMaterialsMatchAny == other.exteriorRoofPhysicalMaterialsMatchAny; } } @@ -644,6 +1384,21 @@ public class AmbienceFXConditions { result = 31 * result + Objects.hashCode(this.sunLightLevel); result = 31 * result + Objects.hashCode(this.torchLightLevel); result = 31 * result + Objects.hashCode(this.globalLightLevel); - return 31 * result + Objects.hashCode(this.dayTime); + result = 31 * result + Objects.hashCode(this.dayTime); + result = 31 * result + Arrays.hashCode((Object[])this.space); + result = 31 * result + Arrays.hashCode((Object[])this.shelter); + result = 31 * result + Arrays.hashCode((Object[])this.surfaces); + result = 31 * result + Objects.hashCode(this.roofState); + result = 31 * result + Objects.hashCode(this.spaceScaleRange); + result = 31 * result + Objects.hashCode(this.spaceScaleMinRange); + result = 31 * result + Objects.hashCode(this.spaceScaleMaxRange); + result = 31 * result + Objects.hashCode(this.escapedRayPercentRange); + result = 31 * result + Objects.hashCode(this.reflectionCoeffRange); + result = 31 * result + Objects.hashCode(this.absorptionCoeffRange); + result = 31 * result + Objects.hashCode(this.roofDistanceRange); + result = 31 * result + Arrays.hashCode((Object[])this.surfacePhysicalMaterials); + result = 31 * result + Boolean.hashCode(this.surfacePhysicalMaterialsMatchAny); + result = 31 * result + Arrays.hashCode((Object[])this.exteriorRoofPhysicalMaterials); + return 31 * result + Boolean.hashCode(this.exteriorRoofPhysicalMaterialsMatchAny); } } diff --git a/src/com/hypixel/hytale/protocol/AmbienceFXMusic.java b/src/com/hypixel/hytale/protocol/AmbienceFXMusic.java index 67950acf..30925072 100644 --- a/src/com/hypixel/hytale/protocol/AmbienceFXMusic.java +++ b/src/com/hypixel/hytale/protocol/AmbienceFXMusic.java @@ -34,45 +34,53 @@ public class AmbienceFXMusic { @Nonnull public static AmbienceFXMusic deserialize(@Nonnull ByteBuf buf, int offset) { - AmbienceFXMusic obj = new AmbienceFXMusic(); - byte nullBits = buf.getByte(offset); - obj.volume = buf.getFloatLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int tracksCount = VarInt.peek(buf, pos); - if (tracksCount < 0) { - throw ProtocolException.negativeLength("Tracks", tracksCount); - } - - if (tracksCount > 4096000) { - throw ProtocolException.arrayTooLong("Tracks", tracksCount, 4096000); - } - - int tracksVarLen = VarInt.size(tracksCount); - if (pos + tracksVarLen + tracksCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tracks", pos + tracksVarLen + tracksCount * 1, buf.readableBytes()); - } - - pos += tracksVarLen; - obj.tracks = new String[tracksCount]; - - for (int i = 0; i < tracksCount; i++) { - int strLen = VarInt.peek(buf, pos); - if (strLen < 0) { - throw ProtocolException.negativeLength("tracks[" + i + "]", strLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AmbienceFXMusic", 5, buf.readableBytes() - offset); + } else { + AmbienceFXMusic obj = new AmbienceFXMusic(); + byte nullBits = buf.getByte(offset); + obj.volume = buf.getFloatLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int tracksCount = VarInt.peek(buf, pos); + if (tracksCount < 0) { + throw ProtocolException.invalidVarInt("Tracks"); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("tracks[" + i + "]", strLen, 4096000); + int tracksVarLen = VarInt.size(tracksCount); + if (tracksCount > 4096000) { + throw ProtocolException.arrayTooLong("Tracks", tracksCount, 4096000); } - int strVarLen = VarInt.length(buf, pos); - obj.tracks[i] = PacketIO.readVarString(buf, pos); - pos += strVarLen + strLen; + if (pos + tracksVarLen + tracksCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tracks", pos + tracksVarLen + tracksCount * 1, buf.readableBytes()); + } + + pos += tracksVarLen; + obj.tracks = new String[tracksCount]; + + for (int i = 0; i < tracksCount; i++) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("tracks[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("tracks[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("tracks[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.tracks[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,11 +88,11 @@ public class AmbienceFXMusic { int pos = offset + 5; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -143,7 +151,7 @@ public class AmbienceFXMusic { return ValidationResult.error("Tracks exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(tracksCount); for (int i = 0; i < tracksCount; i++) { int strLen = VarInt.peek(buffer, pos); @@ -151,7 +159,7 @@ public class AmbienceFXMusic { return ValidationResult.error("Invalid string length in Tracks"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(strLen); pos += strLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Tracks"); diff --git a/src/com/hypixel/hytale/protocol/AmbienceFXPhysicalMaterial.java b/src/com/hypixel/hytale/protocol/AmbienceFXPhysicalMaterial.java new file mode 100644 index 00000000..c7b23e98 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/AmbienceFXPhysicalMaterial.java @@ -0,0 +1,103 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class AmbienceFXPhysicalMaterial { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 13; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 13; + public static final int MAX_SIZE = 13; + public int physicalMaterialIndex; + @Nullable + public Rangef percent; + + public AmbienceFXPhysicalMaterial() { + } + + public AmbienceFXPhysicalMaterial(int physicalMaterialIndex, @Nullable Rangef percent) { + this.physicalMaterialIndex = physicalMaterialIndex; + this.percent = percent; + } + + public AmbienceFXPhysicalMaterial(@Nonnull AmbienceFXPhysicalMaterial other) { + this.physicalMaterialIndex = other.physicalMaterialIndex; + this.percent = other.percent; + } + + @Nonnull + public static AmbienceFXPhysicalMaterial deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("AmbienceFXPhysicalMaterial", 13, buf.readableBytes() - offset); + } else { + AmbienceFXPhysicalMaterial obj = new AmbienceFXPhysicalMaterial(); + byte nullBits = buf.getByte(offset); + obj.physicalMaterialIndex = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.percent = Rangef.deserialize(buf, offset + 5); + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 13; + } + + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.percent != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + buf.writeIntLE(this.physicalMaterialIndex); + if (this.percent != null) { + this.percent.serialize(buf); + } else { + buf.writeZero(8); + } + } + + public int computeSize() { + return 13; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 13) { + return ValidationResult.error("Buffer too small: expected at least 13 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } + } + + public AmbienceFXPhysicalMaterial clone() { + AmbienceFXPhysicalMaterial copy = new AmbienceFXPhysicalMaterial(); + copy.physicalMaterialIndex = this.physicalMaterialIndex; + copy.percent = this.percent != null ? this.percent.clone() : null; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof AmbienceFXPhysicalMaterial other) + ? false + : this.physicalMaterialIndex == other.physicalMaterialIndex && Objects.equals(this.percent, other.percent); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.physicalMaterialIndex, this.percent); + } +} diff --git a/src/com/hypixel/hytale/protocol/AmbienceFXSound.java b/src/com/hypixel/hytale/protocol/AmbienceFXSound.java index 3bf4f1a9..cd0e0f30 100644 --- a/src/com/hypixel/hytale/protocol/AmbienceFXSound.java +++ b/src/com/hypixel/hytale/protocol/AmbienceFXSound.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -8,10 +9,10 @@ import javax.annotation.Nullable; public class AmbienceFXSound { public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 27; + public static final int FIXED_BLOCK_SIZE = 33; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 27; - public static final int MAX_SIZE = 27; + public static final int VARIABLE_BLOCK_START = 33; + public static final int MAX_SIZE = 33; public int soundEventIndex; @Nonnull public AmbienceFXSoundPlay3D play3D = AmbienceFXSoundPlay3D.Random; @@ -22,6 +23,9 @@ public class AmbienceFXSound { public Rangef frequency; @Nullable public Range radius; + public int maxBodiesPerEmitter; + @Nullable + public Rangeb sunlightRange; public AmbienceFXSound() { } @@ -32,7 +36,9 @@ public class AmbienceFXSound { int blockSoundSetIndex, @Nonnull AmbienceFXAltitude altitude, @Nullable Rangef frequency, - @Nullable Range radius + @Nullable Range radius, + int maxBodiesPerEmitter, + @Nullable Rangeb sunlightRange ) { this.soundEventIndex = soundEventIndex; this.play3D = play3D; @@ -40,6 +46,8 @@ public class AmbienceFXSound { this.altitude = altitude; this.frequency = frequency; this.radius = radius; + this.maxBodiesPerEmitter = maxBodiesPerEmitter; + this.sunlightRange = sunlightRange; } public AmbienceFXSound(@Nonnull AmbienceFXSound other) { @@ -49,29 +57,40 @@ public class AmbienceFXSound { this.altitude = other.altitude; this.frequency = other.frequency; this.radius = other.radius; + this.maxBodiesPerEmitter = other.maxBodiesPerEmitter; + this.sunlightRange = other.sunlightRange; } @Nonnull public static AmbienceFXSound deserialize(@Nonnull ByteBuf buf, int offset) { - AmbienceFXSound obj = new AmbienceFXSound(); - byte nullBits = buf.getByte(offset); - obj.soundEventIndex = buf.getIntLE(offset + 1); - obj.play3D = AmbienceFXSoundPlay3D.fromValue(buf.getByte(offset + 5)); - obj.blockSoundSetIndex = buf.getIntLE(offset + 6); - obj.altitude = AmbienceFXAltitude.fromValue(buf.getByte(offset + 10)); - if ((nullBits & 1) != 0) { - obj.frequency = Rangef.deserialize(buf, offset + 11); - } + if (buf.readableBytes() - offset < 33) { + throw ProtocolException.bufferTooSmall("AmbienceFXSound", 33, buf.readableBytes() - offset); + } else { + AmbienceFXSound obj = new AmbienceFXSound(); + byte nullBits = buf.getByte(offset); + obj.soundEventIndex = buf.getIntLE(offset + 1); + obj.play3D = AmbienceFXSoundPlay3D.fromValue(buf.getByte(offset + 5)); + obj.blockSoundSetIndex = buf.getIntLE(offset + 6); + obj.altitude = AmbienceFXAltitude.fromValue(buf.getByte(offset + 10)); + if ((nullBits & 1) != 0) { + obj.frequency = Rangef.deserialize(buf, offset + 11); + } - if ((nullBits & 2) != 0) { - obj.radius = Range.deserialize(buf, offset + 19); - } + if ((nullBits & 2) != 0) { + obj.radius = Range.deserialize(buf, offset + 19); + } - return obj; + obj.maxBodiesPerEmitter = buf.getIntLE(offset + 27); + if ((nullBits & 4) != 0) { + obj.sunlightRange = Rangeb.deserialize(buf, offset + 31); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 27; + return 33; } public void serialize(@Nonnull ByteBuf buf) { @@ -84,6 +103,10 @@ public class AmbienceFXSound { nullBits = (byte)(nullBits | 2); } + if (this.sunlightRange != null) { + nullBits = (byte)(nullBits | 4); + } + buf.writeByte(nullBits); buf.writeIntLE(this.soundEventIndex); buf.writeByte(this.play3D.getValue()); @@ -100,14 +123,32 @@ public class AmbienceFXSound { } else { buf.writeZero(8); } + + buf.writeIntLE(this.maxBodiesPerEmitter); + if (this.sunlightRange != null) { + this.sunlightRange.serialize(buf); + } else { + buf.writeZero(2); + } } public int computeSize() { - return 27; + return 33; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 27 ? ValidationResult.error("Buffer too small: expected at least 27 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 33) { + return ValidationResult.error("Buffer too small: expected at least 33 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 5) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid AmbienceFXSoundPlay3D value for Play3D"); + } else { + v = buffer.getByte(offset + 10) & 255; + return v >= 4 ? ValidationResult.error("Invalid AmbienceFXAltitude value for Altitude") : ValidationResult.OK; + } + } } public AmbienceFXSound clone() { @@ -118,6 +159,8 @@ public class AmbienceFXSound { copy.altitude = this.altitude; copy.frequency = this.frequency != null ? this.frequency.clone() : null; copy.radius = this.radius != null ? this.radius.clone() : null; + copy.maxBodiesPerEmitter = this.maxBodiesPerEmitter; + copy.sunlightRange = this.sunlightRange != null ? this.sunlightRange.clone() : null; return copy; } @@ -133,12 +176,16 @@ public class AmbienceFXSound { && this.blockSoundSetIndex == other.blockSoundSetIndex && Objects.equals(this.altitude, other.altitude) && Objects.equals(this.frequency, other.frequency) - && Objects.equals(this.radius, other.radius); + && Objects.equals(this.radius, other.radius) + && this.maxBodiesPerEmitter == other.maxBodiesPerEmitter + && Objects.equals(this.sunlightRange, other.sunlightRange); } } @Override public int hashCode() { - return Objects.hash(this.soundEventIndex, this.play3D, this.blockSoundSetIndex, this.altitude, this.frequency, this.radius); + return Objects.hash( + this.soundEventIndex, this.play3D, this.blockSoundSetIndex, this.altitude, this.frequency, this.radius, this.maxBodiesPerEmitter, this.sunlightRange + ); } } diff --git a/src/com/hypixel/hytale/protocol/AmbienceFXSoundEffect.java b/src/com/hypixel/hytale/protocol/AmbienceFXSoundEffect.java index 3fe90960..fb4f6b2f 100644 --- a/src/com/hypixel/hytale/protocol/AmbienceFXSoundEffect.java +++ b/src/com/hypixel/hytale/protocol/AmbienceFXSoundEffect.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class AmbienceFXSoundEffect { @Nonnull public static AmbienceFXSoundEffect deserialize(@Nonnull ByteBuf buf, int offset) { - AmbienceFXSoundEffect obj = new AmbienceFXSoundEffect(); - obj.reverbEffectIndex = buf.getIntLE(offset + 0); - obj.equalizerEffectIndex = buf.getIntLE(offset + 4); - obj.isInstant = buf.getByte(offset + 8) != 0; - return obj; + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AmbienceFXSoundEffect", 9, buf.readableBytes() - offset); + } else { + AmbienceFXSoundEffect obj = new AmbienceFXSoundEffect(); + obj.reverbEffectIndex = buf.getIntLE(offset + 0); + obj.equalizerEffectIndex = buf.getIntLE(offset + 4); + obj.isInstant = buf.getByte(offset + 8) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/AmbienceFXSoundPlay3D.java b/src/com/hypixel/hytale/protocol/AmbienceFXSoundPlay3D.java index 97c25337..03ddb8b9 100644 --- a/src/com/hypixel/hytale/protocol/AmbienceFXSoundPlay3D.java +++ b/src/com/hypixel/hytale/protocol/AmbienceFXSoundPlay3D.java @@ -5,7 +5,8 @@ import com.hypixel.hytale.protocol.io.ProtocolException; public enum AmbienceFXSoundPlay3D { Random(0), LocationName(1), - No(2); + LocationNameRandom(2), + No(3); public static final AmbienceFXSoundPlay3D[] VALUES = values(); private final int value; diff --git a/src/com/hypixel/hytale/protocol/AngledDamage.java b/src/com/hypixel/hytale/protocol/AngledDamage.java index b75fd316..31983177 100644 --- a/src/com/hypixel/hytale/protocol/AngledDamage.java +++ b/src/com/hypixel/hytale/protocol/AngledDamage.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -37,18 +38,22 @@ public class AngledDamage { @Nonnull public static AngledDamage deserialize(@Nonnull ByteBuf buf, int offset) { - AngledDamage obj = new AngledDamage(); - byte nullBits = buf.getByte(offset); - obj.angle = buf.getDoubleLE(offset + 1); - obj.angleDistance = buf.getDoubleLE(offset + 9); - obj.next = buf.getIntLE(offset + 17); - int pos = offset + 21; - if ((nullBits & 1) != 0) { - obj.damageEffects = DamageEffects.deserialize(buf, pos); - pos += DamageEffects.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("AngledDamage", 21, buf.readableBytes() - offset); + } else { + AngledDamage obj = new AngledDamage(); + byte nullBits = buf.getByte(offset); + obj.angle = buf.getDoubleLE(offset + 1); + obj.angleDistance = buf.getDoubleLE(offset + 9); + obj.next = buf.getIntLE(offset + 17); + int pos = offset + 21; + if ((nullBits & 1) != 0) { + obj.damageEffects = DamageEffects.deserialize(buf, pos); + pos += DamageEffects.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/AngledWielding.java b/src/com/hypixel/hytale/protocol/AngledWielding.java index 89f9a0c7..68ee95ff 100644 --- a/src/com/hypixel/hytale/protocol/AngledWielding.java +++ b/src/com/hypixel/hytale/protocol/AngledWielding.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class AngledWielding { @Nonnull public static AngledWielding deserialize(@Nonnull ByteBuf buf, int offset) { - AngledWielding obj = new AngledWielding(); - obj.angleRad = buf.getFloatLE(offset + 0); - obj.angleDistanceRad = buf.getFloatLE(offset + 4); - obj.hasModifiers = buf.getByte(offset + 8) != 0; - return obj; + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AngledWielding", 9, buf.readableBytes() - offset); + } else { + AngledWielding obj = new AngledWielding(); + obj.angleRad = buf.getFloatLE(offset + 0); + obj.angleDistanceRad = buf.getFloatLE(offset + 4); + obj.hasModifiers = buf.getByte(offset + 8) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Animation.java b/src/com/hypixel/hytale/protocol/Animation.java index 7728c03a..34b393dd 100644 --- a/src/com/hypixel/hytale/protocol/Animation.java +++ b/src/com/hypixel/hytale/protocol/Animation.java @@ -63,52 +63,71 @@ public class Animation { @Nonnull public static Animation deserialize(@Nonnull ByteBuf buf, int offset) { - Animation obj = new Animation(); - byte nullBits = buf.getByte(offset); - obj.speed = buf.getFloatLE(offset + 1); - obj.blendingDuration = buf.getFloatLE(offset + 5); - obj.looping = buf.getByte(offset + 9) != 0; - obj.weight = buf.getFloatLE(offset + 10); - obj.soundEventIndex = buf.getIntLE(offset + 14); - obj.passiveLoopCount = buf.getIntLE(offset + 18); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 30 + buf.getIntLE(offset + 22); - int nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); + if (buf.readableBytes() - offset < 30) { + throw ProtocolException.bufferTooSmall("Animation", 30, buf.readableBytes() - offset); + } else { + Animation obj = new Animation(); + byte nullBits = buf.getByte(offset); + obj.speed = buf.getFloatLE(offset + 1); + obj.blendingDuration = buf.getFloatLE(offset + 5); + obj.looping = buf.getByte(offset + 9) != 0; + obj.weight = buf.getFloatLE(offset + 10); + obj.soundEventIndex = buf.getIntLE(offset + 14); + obj.passiveLoopCount = buf.getIntLE(offset + 18); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 22); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 30 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 26); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("FootstepIntervals", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 30 + varPosBase1; + int footstepIntervalsCount = VarInt.peek(buf, varPos1); + if (footstepIntervalsCount < 0) { + throw ProtocolException.invalidVarInt("FootstepIntervals"); + } + + int varIntLen = VarInt.size(footstepIntervalsCount); + if (footstepIntervalsCount > 4096000) { + throw ProtocolException.arrayTooLong("FootstepIntervals", footstepIntervalsCount, 4096000); + } + + if (varPos1 + varIntLen + footstepIntervalsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FootstepIntervals", varPos1 + varIntLen + footstepIntervalsCount * 4, buf.readableBytes()); + } + + obj.footstepIntervals = new int[footstepIntervalsCount]; + + for (int i = 0; i < footstepIntervalsCount; i++) { + obj.footstepIntervals[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); + } } - obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 30 + buf.getIntLE(offset + 26); - int footstepIntervalsCount = VarInt.peek(buf, varPos1); - if (footstepIntervalsCount < 0) { - throw ProtocolException.negativeLength("FootstepIntervals", footstepIntervalsCount); - } - - if (footstepIntervalsCount > 4096000) { - throw ProtocolException.arrayTooLong("FootstepIntervals", footstepIntervalsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + footstepIntervalsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FootstepIntervals", varPos1 + varIntLen + footstepIntervalsCount * 4, buf.readableBytes()); - } - - obj.footstepIntervals = new int[footstepIntervalsCount]; - - for (int i = 0; i < footstepIntervalsCount; i++) { - obj.footstepIntervals[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -116,9 +135,13 @@ public class Animation { int maxEnd = 30; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 22); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + int pos0 = offset + 30 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -126,9 +149,13 @@ public class Animation { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 26); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("FootstepIntervals", fieldOffset1, maxEnd); + } + int pos1 = offset + 30 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 4; + pos1 += VarInt.size(arrLen) + arrLen * 4; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -203,15 +230,11 @@ public class Animation { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int nameOffset = buffer.getIntLE(offset + 22); - if (nameOffset < 0) { + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Name"); } int pos = offset + 30 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - int nameLen = VarInt.peek(buffer, pos); if (nameLen < 0) { return ValidationResult.error("Invalid string length for Name"); @@ -221,7 +244,7 @@ public class Animation { return ValidationResult.error("Name exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Name"); @@ -230,15 +253,11 @@ public class Animation { if ((nullBits & 2) != 0) { int footstepIntervalsOffset = buffer.getIntLE(offset + 26); - if (footstepIntervalsOffset < 0) { + if (footstepIntervalsOffset < 0 || footstepIntervalsOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for FootstepIntervals"); } int posx = offset + 30 + footstepIntervalsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FootstepIntervals"); - } - int footstepIntervalsCount = VarInt.peek(buffer, posx); if (footstepIntervalsCount < 0) { return ValidationResult.error("Invalid array count for FootstepIntervals"); @@ -248,7 +267,7 @@ public class Animation { return ValidationResult.error("FootstepIntervals exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(footstepIntervalsCount); posx += footstepIntervalsCount * 4; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FootstepIntervals"); diff --git a/src/com/hypixel/hytale/protocol/AnimationSet.java b/src/com/hypixel/hytale/protocol/AnimationSet.java index 86fcef05..c8a84ec1 100644 --- a/src/com/hypixel/hytale/protocol/AnimationSet.java +++ b/src/com/hypixel/hytale/protocol/AnimationSet.java @@ -40,52 +40,71 @@ public class AnimationSet { @Nonnull public static AnimationSet deserialize(@Nonnull ByteBuf buf, int offset) { - AnimationSet obj = new AnimationSet(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.nextAnimationDelay = Rangef.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("AnimationSet", 17, buf.readableBytes() - offset); + } else { + AnimationSet obj = new AnimationSet(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.nextAnimationDelay = Rangef.deserialize(buf, offset + 1); + } + + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 13); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Animations", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + int animationsCount = VarInt.peek(buf, varPos1); + if (animationsCount < 0) { + throw ProtocolException.invalidVarInt("Animations"); + } + + int varIntLen = VarInt.size(animationsCount); + if (animationsCount > 4096000) { + throw ProtocolException.arrayTooLong("Animations", animationsCount, 4096000); + } + + if (varPos1 + varIntLen + animationsCount * 22L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Animations", varPos1 + varIntLen + animationsCount * 22, buf.readableBytes()); + } + + obj.animations = new Animation[animationsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < animationsCount; i++) { + obj.animations[i] = Animation.deserialize(buf, elemPos); + elemPos += Animation.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos0 = offset + 17 + buf.getIntLE(offset + 9); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); - } - - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 17 + buf.getIntLE(offset + 13); - int animationsCount = VarInt.peek(buf, varPos1); - if (animationsCount < 0) { - throw ProtocolException.negativeLength("Animations", animationsCount); - } - - if (animationsCount > 4096000) { - throw ProtocolException.arrayTooLong("Animations", animationsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + animationsCount * 22L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Animations", varPos1 + varIntLen + animationsCount * 22, buf.readableBytes()); - } - - obj.animations = new Animation[animationsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < animationsCount; i++) { - obj.animations[i] = Animation.deserialize(buf, elemPos); - elemPos += Animation.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -93,9 +112,13 @@ public class AnimationSet { int maxEnd = 17; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -103,9 +126,13 @@ public class AnimationSet { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 13); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Animations", fieldOffset1, maxEnd); + } + int pos1 = offset + 17 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += Animation.computeBytesConsumed(buf, pos1); @@ -195,15 +222,11 @@ public class AnimationSet { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { int idOffset = buffer.getIntLE(offset + 9); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 17 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -213,7 +236,7 @@ public class AnimationSet { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -222,15 +245,11 @@ public class AnimationSet { if ((nullBits & 4) != 0) { int animationsOffset = buffer.getIntLE(offset + 13); - if (animationsOffset < 0) { + if (animationsOffset < 0 || animationsOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Animations"); } int posx = offset + 17 + animationsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Animations"); - } - int animationsCount = VarInt.peek(buffer, posx); if (animationsCount < 0) { return ValidationResult.error("Invalid array count for Animations"); @@ -240,7 +259,7 @@ public class AnimationSet { return ValidationResult.error("Animations exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(animationsCount); for (int i = 0; i < animationsCount; i++) { ValidationResult structResult = Animation.validateStructure(buffer, posx); diff --git a/src/com/hypixel/hytale/protocol/ApplicationEffects.java b/src/com/hypixel/hytale/protocol/ApplicationEffects.java index 2f672a85..9eb19819 100644 --- a/src/com/hypixel/hytale/protocol/ApplicationEffects.java +++ b/src/com/hypixel/hytale/protocol/ApplicationEffects.java @@ -94,123 +94,172 @@ public class ApplicationEffects { @Nonnull public static ApplicationEffects deserialize(@Nonnull ByteBuf buf, int offset) { - ApplicationEffects obj = new ApplicationEffects(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - if ((nullBits[0] & 1) != 0) { - obj.entityBottomTint = Color.deserialize(buf, offset + 2); + if (buf.readableBytes() - offset < 59) { + throw ProtocolException.bufferTooSmall("ApplicationEffects", 59, buf.readableBytes() - offset); + } else { + ApplicationEffects obj = new ApplicationEffects(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + if ((nullBits[0] & 1) != 0) { + obj.entityBottomTint = Color.deserialize(buf, offset + 2); + } + + if ((nullBits[0] & 2) != 0) { + obj.entityTopTint = Color.deserialize(buf, offset + 5); + } + + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 8); + obj.soundEventIndexLocal = buf.getIntLE(offset + 12); + obj.soundEventIndexWorld = buf.getIntLE(offset + 16); + if ((nullBits[0] & 4) != 0) { + obj.movementEffects = MovementEffects.deserialize(buf, offset + 20); + } + + obj.mouseSensitivityAdjustmentTarget = buf.getFloatLE(offset + 27); + obj.mouseSensitivityAdjustmentDuration = buf.getFloatLE(offset + 31); + if ((nullBits[0] & 8) != 0) { + int varPosBase0 = buf.getIntLE(offset + 35); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("EntityAnimationId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 59 + varPosBase0; + int entityAnimationIdLen = VarInt.peek(buf, varPos0); + if (entityAnimationIdLen < 0) { + throw ProtocolException.invalidVarInt("EntityAnimationId"); + } + + int entityAnimationIdVarIntLen = VarInt.size(entityAnimationIdLen); + if (entityAnimationIdLen > 4096000) { + throw ProtocolException.stringTooLong("EntityAnimationId", entityAnimationIdLen, 4096000); + } + + if (varPos0 + entityAnimationIdVarIntLen + entityAnimationIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EntityAnimationId", varPos0 + entityAnimationIdVarIntLen + entityAnimationIdLen, buf.readableBytes()); + } + + obj.entityAnimationId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits[0] & 16) != 0) { + int varPosBase1 = buf.getIntLE(offset + 39); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("Particles", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 59 + varPosBase1; + int particlesCount = VarInt.peek(buf, varPos1); + if (particlesCount < 0) { + throw ProtocolException.invalidVarInt("Particles"); + } + + int varIntLen = VarInt.size(particlesCount); + if (particlesCount > 4096000) { + throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); + } + + if (varPos1 + varIntLen + particlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Particles", varPos1 + varIntLen + particlesCount * 34, buf.readableBytes()); + } + + obj.particles = new ModelParticle[particlesCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < particlesCount; i++) { + obj.particles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[0] & 32) != 0) { + int varPosBase2 = buf.getIntLE(offset + 43); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("FirstPersonParticles", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 59 + varPosBase2; + int firstPersonParticlesCount = VarInt.peek(buf, varPos2); + if (firstPersonParticlesCount < 0) { + throw ProtocolException.invalidVarInt("FirstPersonParticles"); + } + + int varIntLenx = VarInt.size(firstPersonParticlesCount); + if (firstPersonParticlesCount > 4096000) { + throw ProtocolException.arrayTooLong("FirstPersonParticles", firstPersonParticlesCount, 4096000); + } + + if (varPos2 + varIntLenx + firstPersonParticlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FirstPersonParticles", varPos2 + varIntLenx + firstPersonParticlesCount * 34, buf.readableBytes()); + } + + obj.firstPersonParticles = new ModelParticle[firstPersonParticlesCount]; + int elemPos = varPos2 + varIntLenx; + + for (int i = 0; i < firstPersonParticlesCount; i++) { + obj.firstPersonParticles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[0] & 64) != 0) { + int varPosBase3 = buf.getIntLE(offset + 47); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("ScreenEffect", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 59 + varPosBase3; + int screenEffectLen = VarInt.peek(buf, varPos3); + if (screenEffectLen < 0) { + throw ProtocolException.invalidVarInt("ScreenEffect"); + } + + int screenEffectVarIntLen = VarInt.size(screenEffectLen); + if (screenEffectLen > 4096000) { + throw ProtocolException.stringTooLong("ScreenEffect", screenEffectLen, 4096000); + } + + if (varPos3 + screenEffectVarIntLen + screenEffectLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ScreenEffect", varPos3 + screenEffectVarIntLen + screenEffectLen, buf.readableBytes()); + } + + obj.screenEffect = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase4 = buf.getIntLE(offset + 51); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("ModelVFXId", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 59 + varPosBase4; + int modelVFXIdLen = VarInt.peek(buf, varPos4); + if (modelVFXIdLen < 0) { + throw ProtocolException.invalidVarInt("ModelVFXId"); + } + + int modelVFXIdVarIntLen = VarInt.size(modelVFXIdLen); + if (modelVFXIdLen > 4096000) { + throw ProtocolException.stringTooLong("ModelVFXId", modelVFXIdLen, 4096000); + } + + if (varPos4 + modelVFXIdVarIntLen + modelVFXIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ModelVFXId", varPos4 + modelVFXIdVarIntLen + modelVFXIdLen, buf.readableBytes()); + } + + obj.modelVFXId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase5 = buf.getIntLE(offset + 55); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("AbilityEffects", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 59 + varPosBase5; + obj.abilityEffects = AbilityEffects.deserialize(buf, varPos5); + } + + return obj; } - - if ((nullBits[0] & 2) != 0) { - obj.entityTopTint = Color.deserialize(buf, offset + 5); - } - - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 8); - obj.soundEventIndexLocal = buf.getIntLE(offset + 12); - obj.soundEventIndexWorld = buf.getIntLE(offset + 16); - if ((nullBits[0] & 4) != 0) { - obj.movementEffects = MovementEffects.deserialize(buf, offset + 20); - } - - obj.mouseSensitivityAdjustmentTarget = buf.getFloatLE(offset + 27); - obj.mouseSensitivityAdjustmentDuration = buf.getFloatLE(offset + 31); - if ((nullBits[0] & 8) != 0) { - int varPos0 = offset + 59 + buf.getIntLE(offset + 35); - int entityAnimationIdLen = VarInt.peek(buf, varPos0); - if (entityAnimationIdLen < 0) { - throw ProtocolException.negativeLength("EntityAnimationId", entityAnimationIdLen); - } - - if (entityAnimationIdLen > 4096000) { - throw ProtocolException.stringTooLong("EntityAnimationId", entityAnimationIdLen, 4096000); - } - - obj.entityAnimationId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits[0] & 16) != 0) { - int varPos1 = offset + 59 + buf.getIntLE(offset + 39); - int particlesCount = VarInt.peek(buf, varPos1); - if (particlesCount < 0) { - throw ProtocolException.negativeLength("Particles", particlesCount); - } - - if (particlesCount > 4096000) { - throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + particlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Particles", varPos1 + varIntLen + particlesCount * 34, buf.readableBytes()); - } - - obj.particles = new ModelParticle[particlesCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < particlesCount; i++) { - obj.particles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[0] & 32) != 0) { - int varPos2 = offset + 59 + buf.getIntLE(offset + 43); - int firstPersonParticlesCount = VarInt.peek(buf, varPos2); - if (firstPersonParticlesCount < 0) { - throw ProtocolException.negativeLength("FirstPersonParticles", firstPersonParticlesCount); - } - - if (firstPersonParticlesCount > 4096000) { - throw ProtocolException.arrayTooLong("FirstPersonParticles", firstPersonParticlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + firstPersonParticlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FirstPersonParticles", varPos2 + varIntLen + firstPersonParticlesCount * 34, buf.readableBytes()); - } - - obj.firstPersonParticles = new ModelParticle[firstPersonParticlesCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < firstPersonParticlesCount; i++) { - obj.firstPersonParticles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[0] & 64) != 0) { - int varPos3 = offset + 59 + buf.getIntLE(offset + 47); - int screenEffectLen = VarInt.peek(buf, varPos3); - if (screenEffectLen < 0) { - throw ProtocolException.negativeLength("ScreenEffect", screenEffectLen); - } - - if (screenEffectLen > 4096000) { - throw ProtocolException.stringTooLong("ScreenEffect", screenEffectLen, 4096000); - } - - obj.screenEffect = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits[0] & 128) != 0) { - int varPos4 = offset + 59 + buf.getIntLE(offset + 51); - int modelVFXIdLen = VarInt.peek(buf, varPos4); - if (modelVFXIdLen < 0) { - throw ProtocolException.negativeLength("ModelVFXId", modelVFXIdLen); - } - - if (modelVFXIdLen > 4096000) { - throw ProtocolException.stringTooLong("ModelVFXId", modelVFXIdLen, 4096000); - } - - obj.modelVFXId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - if ((nullBits[1] & 1) != 0) { - int varPos5 = offset + 59 + buf.getIntLE(offset + 55); - obj.abilityEffects = AbilityEffects.deserialize(buf, varPos5); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -218,9 +267,13 @@ public class ApplicationEffects { int maxEnd = 59; if ((nullBits[0] & 8) != 0) { int fieldOffset0 = buf.getIntLE(offset + 35); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("EntityAnimationId", fieldOffset0, maxEnd); + } + int pos0 = offset + 59 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -228,9 +281,13 @@ public class ApplicationEffects { if ((nullBits[0] & 16) != 0) { int fieldOffset1 = buf.getIntLE(offset + 39); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("Particles", fieldOffset1, maxEnd); + } + int pos1 = offset + 59 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += ModelParticle.computeBytesConsumed(buf, pos1); @@ -243,9 +300,13 @@ public class ApplicationEffects { if ((nullBits[0] & 32) != 0) { int fieldOffset2 = buf.getIntLE(offset + 43); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("FirstPersonParticles", fieldOffset2, maxEnd); + } + int pos2 = offset + 59 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += ModelParticle.computeBytesConsumed(buf, pos2); @@ -258,9 +319,13 @@ public class ApplicationEffects { if ((nullBits[0] & 64) != 0) { int fieldOffset3 = buf.getIntLE(offset + 47); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("ScreenEffect", fieldOffset3, maxEnd); + } + int pos3 = offset + 59 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -268,9 +333,13 @@ public class ApplicationEffects { if ((nullBits[0] & 128) != 0) { int fieldOffset4 = buf.getIntLE(offset + 51); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("ModelVFXId", fieldOffset4, maxEnd); + } + int pos4 = offset + 59 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -278,6 +347,10 @@ public class ApplicationEffects { if ((nullBits[1] & 1) != 0) { int fieldOffset5 = buf.getIntLE(offset + 55); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 59) { + throw ProtocolException.invalidOffset("AbilityEffects", fieldOffset5, maxEnd); + } + int pos5 = offset + 59 + fieldOffset5; pos5 += AbilityEffects.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { @@ -471,15 +544,11 @@ public class ApplicationEffects { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); if ((nullBits[0] & 8) != 0) { int entityAnimationIdOffset = buffer.getIntLE(offset + 35); - if (entityAnimationIdOffset < 0) { + if (entityAnimationIdOffset < 0 || entityAnimationIdOffset > buffer.writerIndex() - offset - 59) { return ValidationResult.error("Invalid offset for EntityAnimationId"); } int pos = offset + 59 + entityAnimationIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EntityAnimationId"); - } - int entityAnimationIdLen = VarInt.peek(buffer, pos); if (entityAnimationIdLen < 0) { return ValidationResult.error("Invalid string length for EntityAnimationId"); @@ -489,7 +558,7 @@ public class ApplicationEffects { return ValidationResult.error("EntityAnimationId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(entityAnimationIdLen); pos += entityAnimationIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading EntityAnimationId"); @@ -498,15 +567,11 @@ public class ApplicationEffects { if ((nullBits[0] & 16) != 0) { int particlesOffset = buffer.getIntLE(offset + 39); - if (particlesOffset < 0) { + if (particlesOffset < 0 || particlesOffset > buffer.writerIndex() - offset - 59) { return ValidationResult.error("Invalid offset for Particles"); } int posx = offset + 59 + particlesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particles"); - } - int particlesCount = VarInt.peek(buffer, posx); if (particlesCount < 0) { return ValidationResult.error("Invalid array count for Particles"); @@ -516,7 +581,7 @@ public class ApplicationEffects { return ValidationResult.error("Particles exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(particlesCount); for (int i = 0; i < particlesCount; i++) { ValidationResult structResult = ModelParticle.validateStructure(buffer, posx); @@ -530,15 +595,11 @@ public class ApplicationEffects { if ((nullBits[0] & 32) != 0) { int firstPersonParticlesOffset = buffer.getIntLE(offset + 43); - if (firstPersonParticlesOffset < 0) { + if (firstPersonParticlesOffset < 0 || firstPersonParticlesOffset > buffer.writerIndex() - offset - 59) { return ValidationResult.error("Invalid offset for FirstPersonParticles"); } int posxx = offset + 59 + firstPersonParticlesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstPersonParticles"); - } - int firstPersonParticlesCount = VarInt.peek(buffer, posxx); if (firstPersonParticlesCount < 0) { return ValidationResult.error("Invalid array count for FirstPersonParticles"); @@ -548,7 +609,7 @@ public class ApplicationEffects { return ValidationResult.error("FirstPersonParticles exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(firstPersonParticlesCount); for (int i = 0; i < firstPersonParticlesCount; i++) { ValidationResult structResult = ModelParticle.validateStructure(buffer, posxx); @@ -562,15 +623,11 @@ public class ApplicationEffects { if ((nullBits[0] & 64) != 0) { int screenEffectOffset = buffer.getIntLE(offset + 47); - if (screenEffectOffset < 0) { + if (screenEffectOffset < 0 || screenEffectOffset > buffer.writerIndex() - offset - 59) { return ValidationResult.error("Invalid offset for ScreenEffect"); } int posxxx = offset + 59 + screenEffectOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ScreenEffect"); - } - int screenEffectLen = VarInt.peek(buffer, posxxx); if (screenEffectLen < 0) { return ValidationResult.error("Invalid string length for ScreenEffect"); @@ -580,7 +637,7 @@ public class ApplicationEffects { return ValidationResult.error("ScreenEffect exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(screenEffectLen); posxxx += screenEffectLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ScreenEffect"); @@ -589,15 +646,11 @@ public class ApplicationEffects { if ((nullBits[0] & 128) != 0) { int modelVFXIdOffset = buffer.getIntLE(offset + 51); - if (modelVFXIdOffset < 0) { + if (modelVFXIdOffset < 0 || modelVFXIdOffset > buffer.writerIndex() - offset - 59) { return ValidationResult.error("Invalid offset for ModelVFXId"); } int posxxxx = offset + 59 + modelVFXIdOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModelVFXId"); - } - int modelVFXIdLen = VarInt.peek(buffer, posxxxx); if (modelVFXIdLen < 0) { return ValidationResult.error("Invalid string length for ModelVFXId"); @@ -607,7 +660,7 @@ public class ApplicationEffects { return ValidationResult.error("ModelVFXId exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(modelVFXIdLen); posxxxx += modelVFXIdLen; if (posxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ModelVFXId"); @@ -616,15 +669,11 @@ public class ApplicationEffects { if ((nullBits[1] & 1) != 0) { int abilityEffectsOffset = buffer.getIntLE(offset + 55); - if (abilityEffectsOffset < 0) { + if (abilityEffectsOffset < 0 || abilityEffectsOffset > buffer.writerIndex() - offset - 59) { return ValidationResult.error("Invalid offset for AbilityEffects"); } int posxxxxx = offset + 59 + abilityEffectsOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AbilityEffects"); - } - ValidationResult abilityEffectsResult = AbilityEffects.validateStructure(buffer, posxxxxx); if (!abilityEffectsResult.isValid()) { return ValidationResult.error("Invalid AbilityEffects: " + abilityEffectsResult.error()); diff --git a/src/com/hypixel/hytale/protocol/AppliedForce.java b/src/com/hypixel/hytale/protocol/AppliedForce.java index c4350a36..af91fef7 100644 --- a/src/com/hypixel/hytale/protocol/AppliedForce.java +++ b/src/com/hypixel/hytale/protocol/AppliedForce.java @@ -1,26 +1,28 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import org.joml.Vector3fc; public class AppliedForce { - public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 18; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 17; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 18; - public static final int MAX_SIZE = 18; - @Nullable - public Vector3f direction; + public static final int VARIABLE_BLOCK_START = 17; + public static final int MAX_SIZE = 17; + @Nonnull + public Vector3fc direction = PacketIO.ZERO_VECTOR3; public boolean adjustVertical; public float force; public AppliedForce() { } - public AppliedForce(@Nullable Vector3f direction, boolean adjustVertical, float force) { + public AppliedForce(@Nonnull Vector3fc direction, boolean adjustVertical, float force) { this.direction = direction; this.adjustVertical = adjustVertical; this.force = force; @@ -34,49 +36,38 @@ public class AppliedForce { @Nonnull public static AppliedForce deserialize(@Nonnull ByteBuf buf, int offset) { - AppliedForce obj = new AppliedForce(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.direction = Vector3f.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("AppliedForce", 17, buf.readableBytes() - offset); + } else { + AppliedForce obj = new AppliedForce(); + obj.direction = PacketIO.readVector3f(buf, offset + 0); + obj.adjustVertical = buf.getByte(offset + 12) != 0; + obj.force = buf.getFloatLE(offset + 13); + return obj; } - - obj.adjustVertical = buf.getByte(offset + 13) != 0; - obj.force = buf.getFloatLE(offset + 14); - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 18; + return 17; } public void serialize(@Nonnull ByteBuf buf) { - byte nullBits = 0; - if (this.direction != null) { - nullBits = (byte)(nullBits | 1); - } - - buf.writeByte(nullBits); - if (this.direction != null) { - this.direction.serialize(buf); - } else { - buf.writeZero(12); - } - + PacketIO.writeVector3f(buf, this.direction); buf.writeByte(this.adjustVertical ? 1 : 0); buf.writeFloatLE(this.force); } public int computeSize() { - return 18; + return 17; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 18 ? ValidationResult.error("Buffer too small: expected at least 18 bytes") : ValidationResult.OK; + return buffer.readableBytes() - offset < 17 ? ValidationResult.error("Buffer too small: expected at least 17 bytes") : ValidationResult.OK; } public AppliedForce clone() { AppliedForce copy = new AppliedForce(); - copy.direction = this.direction != null ? this.direction.clone() : null; + copy.direction = this.direction; copy.adjustVertical = this.adjustVertical; copy.force = this.force; return copy; diff --git a/src/com/hypixel/hytale/protocol/ApplyEffectInteraction.java b/src/com/hypixel/hytale/protocol/ApplyEffectInteraction.java index 79653831..11ca7bcf 100644 --- a/src/com/hypixel/hytale/protocol/ApplyEffectInteraction.java +++ b/src/com/hypixel/hytale/protocol/ApplyEffectInteraction.java @@ -73,80 +73,109 @@ public class ApplyEffectInteraction extends SimpleInteraction { @Nonnull public static ApplyEffectInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ApplyEffectInteraction obj = new ApplyEffectInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.effectId = buf.getIntLE(offset + 19); - obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 23)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 44 + buf.getIntLE(offset + 24); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 44) { + throw ProtocolException.bufferTooSmall("ApplyEffectInteraction", 44, buf.readableBytes() - offset); + } else { + ApplyEffectInteraction obj = new ApplyEffectInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.effectId = buf.getIntLE(offset + 19); + obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 23)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 24); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 44 + buf.getIntLE(offset + 28); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 44 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 28); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 44 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 44 + buf.getIntLE(offset + 32); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 32); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 44 + buf.getIntLE(offset + 36); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 44 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 36); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 44 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 40); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 44 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 44 + buf.getIntLE(offset + 40); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -154,6 +183,10 @@ public class ApplyEffectInteraction extends SimpleInteraction { int maxEnd = 44; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 24); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 44 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -163,9 +196,13 @@ public class ApplyEffectInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 28); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 44 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -178,6 +215,10 @@ public class ApplyEffectInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 32); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 44 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -187,9 +228,13 @@ public class ApplyEffectInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 36); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 44 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -197,6 +242,10 @@ public class ApplyEffectInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 40); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 44 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -337,119 +386,114 @@ public class ApplyEffectInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 44 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 24); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 23) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid InteractionTarget value for EntityTarget"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 44 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 44 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Settings"); + } - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 28); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } + int pos = offset + 44 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } - int posx = offset + 44 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } + pos += VarInt.size(settingsCount); - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } - posx += VarInt.length(buffer, posx); + pos++; + pos++; + } + } - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 44 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 44 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 40); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 44 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 32); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 44 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 36); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 44 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 40); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 44 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ApplyForceInteraction.java b/src/com/hypixel/hytale/protocol/ApplyForceInteraction.java index 22041f10..7047613b 100644 --- a/src/com/hypixel/hytale/protocol/ApplyForceInteraction.java +++ b/src/com/hypixel/hytale/protocol/ApplyForceInteraction.java @@ -125,122 +125,156 @@ public class ApplyForceInteraction extends SimpleInteraction { @Nonnull public static ApplyForceInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ApplyForceInteraction obj = new ApplyForceInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - obj.velocityConfig = VelocityConfig.deserialize(buf, offset + 19); - } - - obj.changeVelocityType = ChangeVelocityType.fromValue(buf.getByte(offset + 40)); - obj.duration = buf.getFloatLE(offset + 41); - if ((nullBits & 2) != 0) { - obj.verticalClamp = FloatRange.deserialize(buf, offset + 45); - } - - obj.waitForGround = buf.getByte(offset + 53) != 0; - obj.waitForCollision = buf.getByte(offset + 54) != 0; - obj.groundCheckDelay = buf.getFloatLE(offset + 55); - obj.collisionCheckDelay = buf.getFloatLE(offset + 59); - obj.groundNext = buf.getIntLE(offset + 63); - obj.collisionNext = buf.getIntLE(offset + 67); - obj.raycastDistance = buf.getFloatLE(offset + 71); - obj.raycastHeightOffset = buf.getFloatLE(offset + 75); - obj.raycastMode = RaycastMode.fromValue(buf.getByte(offset + 79)); - if ((nullBits & 4) != 0) { - int varPos0 = offset + 104 + buf.getIntLE(offset + 80); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } - - if ((nullBits & 8) != 0) { - int varPos1 = offset + 104 + buf.getIntLE(offset + 84); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + if (buf.readableBytes() - offset < 104) { + throw ProtocolException.bufferTooSmall("ApplyForceInteraction", 104, buf.readableBytes() - offset); + } else { + ApplyForceInteraction obj = new ApplyForceInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + obj.velocityConfig = VelocityConfig.deserialize(buf, offset + 19); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + obj.changeVelocityType = ChangeVelocityType.fromValue(buf.getByte(offset + 40)); + obj.duration = buf.getFloatLE(offset + 41); + if ((nullBits & 2) != 0) { + obj.verticalClamp = FloatRange.deserialize(buf, offset + 45); } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + obj.waitForGround = buf.getByte(offset + 53) != 0; + obj.waitForCollision = buf.getByte(offset + 54) != 0; + obj.groundCheckDelay = buf.getFloatLE(offset + 55); + obj.collisionCheckDelay = buf.getFloatLE(offset + 59); + obj.groundNext = buf.getIntLE(offset + 63); + obj.collisionNext = buf.getIntLE(offset + 67); + obj.raycastDistance = buf.getFloatLE(offset + 71); + obj.raycastHeightOffset = buf.getFloatLE(offset + 75); + obj.raycastMode = RaycastMode.fromValue(buf.getByte(offset + 79)); + if ((nullBits & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 80); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varPos0 = offset + 104 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); + } + + if ((nullBits & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 84); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 104 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } + + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 16) != 0) { + int varPosBase2 = buf.getIntLE(offset + 88); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 104 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 32) != 0) { + int varPosBase3 = buf.getIntLE(offset + 92); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 104 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 64) != 0) { + int varPosBase4 = buf.getIntLE(offset + 96); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 104 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 128) != 0) { + int varPosBase5 = buf.getIntLE(offset + 100); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Forces", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 104 + varPosBase5; + int forcesCount = VarInt.peek(buf, varPos5); + if (forcesCount < 0) { + throw ProtocolException.invalidVarInt("Forces"); + } + + int varIntLenx = VarInt.size(forcesCount); + if (forcesCount > 4096000) { + throw ProtocolException.arrayTooLong("Forces", forcesCount, 4096000); + } + + if (varPos5 + varIntLenx + forcesCount * 17L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Forces", varPos5 + varIntLenx + forcesCount * 17, buf.readableBytes()); + } + + obj.forces = new AppliedForce[forcesCount]; + int elemPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < forcesCount; ix++) { + obj.forces[ix] = AppliedForce.deserialize(buf, elemPos); + elemPos += AppliedForce.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 16) != 0) { - int varPos2 = offset + 104 + buf.getIntLE(offset + 88); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 32) != 0) { - int varPos3 = offset + 104 + buf.getIntLE(offset + 92); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 64) != 0) { - int varPos4 = offset + 104 + buf.getIntLE(offset + 96); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 128) != 0) { - int varPos5 = offset + 104 + buf.getIntLE(offset + 100); - int forcesCount = VarInt.peek(buf, varPos5); - if (forcesCount < 0) { - throw ProtocolException.negativeLength("Forces", forcesCount); - } - - if (forcesCount > 4096000) { - throw ProtocolException.arrayTooLong("Forces", forcesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + forcesCount * 18L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Forces", varPos5 + varIntLen + forcesCount * 18, buf.readableBytes()); - } - - obj.forces = new AppliedForce[forcesCount]; - int elemPos = varPos5 + varIntLen; - - for (int ix = 0; ix < forcesCount; ix++) { - obj.forces[ix] = AppliedForce.deserialize(buf, elemPos); - elemPos += AppliedForce.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -248,6 +282,10 @@ public class ApplyForceInteraction extends SimpleInteraction { int maxEnd = 104; if ((nullBits & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 80); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 104 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -257,9 +295,13 @@ public class ApplyForceInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 84); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 104 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -272,6 +314,10 @@ public class ApplyForceInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset2 = buf.getIntLE(offset + 88); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 104 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -281,9 +327,13 @@ public class ApplyForceInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset3 = buf.getIntLE(offset + 92); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 104 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -291,6 +341,10 @@ public class ApplyForceInteraction extends SimpleInteraction { if ((nullBits & 64) != 0) { int fieldOffset4 = buf.getIntLE(offset + 96); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 104 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -300,9 +354,13 @@ public class ApplyForceInteraction extends SimpleInteraction { if ((nullBits & 128) != 0) { int fieldOffset5 = buf.getIntLE(offset + 100); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 104) { + throw ProtocolException.invalidOffset("Forces", fieldOffset5, maxEnd); + } + int pos5 = offset + 104 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos5 += AppliedForce.computeBytesConsumed(buf, pos5); @@ -489,7 +547,7 @@ public class ApplyForceInteraction extends SimpleInteraction { } if (this.forces != null) { - size += VarInt.size(this.forces.length) + this.forces.length * 18; + size += VarInt.size(this.forces.length) + this.forces.length * 17; } return size; @@ -500,146 +558,142 @@ public class ApplyForceInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 104 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 4) != 0) { - int effectsOffset = buffer.getIntLE(offset + 80); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 40) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ChangeVelocityType value for ChangeVelocityType"); + } else { + v = buffer.getByte(offset + 79) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid RaycastMode value for RaycastMode"); + } else { + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 80); + if (v < 0 || v > buffer.writerIndex() - offset - 104) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 104 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 104 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 84); + if (v < 0 || v > buffer.writerIndex() - offset - 104) { + return ValidationResult.error("Invalid offset for Settings"); + } - if ((nullBits & 8) != 0) { - int settingsOffset = buffer.getIntLE(offset + 84); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } + int pos = offset + 104 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } - int posx = offset + 104 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } + pos += VarInt.size(settingsCount); - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } - posx += VarInt.length(buffer, posx); + pos++; + pos++; + } + } - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 88); + if (v < 0 || v > buffer.writerIndex() - offset - 104) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 104 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 92); + if (v < 0 || v > buffer.writerIndex() - offset - 104) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 104 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 96); + if (v < 0 || v > buffer.writerIndex() - offset - 104) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 104 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 128) != 0) { + v = buffer.getIntLE(offset + 100); + if (v < 0 || v > buffer.writerIndex() - offset - 104) { + return ValidationResult.error("Invalid offset for Forces"); + } + + int posxx = offset + 104 + v; + int forcesCount = VarInt.peek(buffer, posxx); + if (forcesCount < 0) { + return ValidationResult.error("Invalid array count for Forces"); + } + + if (forcesCount > 4096000) { + return ValidationResult.error("Forces exceeds max length 4096000"); + } + + posxx += VarInt.size(forcesCount); + posxx += forcesCount * 17; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Forces"); + } + } + + return ValidationResult.OK; + } } } - - if ((nullBits & 16) != 0) { - int rulesOffset = buffer.getIntLE(offset + 88); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 104 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 32) != 0) { - int tagsOffset = buffer.getIntLE(offset + 92); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 104 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 64) != 0) { - int cameraOffset = buffer.getIntLE(offset + 96); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 104 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 128) != 0) { - int forcesOffset = buffer.getIntLE(offset + 100); - if (forcesOffset < 0) { - return ValidationResult.error("Invalid offset for Forces"); - } - - int posxxxxx = offset + 104 + forcesOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Forces"); - } - - int forcesCount = VarInt.peek(buffer, posxxxxx); - if (forcesCount < 0) { - return ValidationResult.error("Invalid array count for Forces"); - } - - if (forcesCount > 4096000) { - return ValidationResult.error("Forces exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += forcesCount * 18; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Forces"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/Asset.java b/src/com/hypixel/hytale/protocol/Asset.java index 4657bcd2..8a637241 100644 --- a/src/com/hypixel/hytale/protocol/Asset.java +++ b/src/com/hypixel/hytale/protocol/Asset.java @@ -34,26 +34,34 @@ public class Asset { @Nonnull public static Asset deserialize(@Nonnull ByteBuf buf, int offset) { - Asset obj = new Asset(); - obj.hash = PacketIO.readFixedAsciiString(buf, offset + 0, 64); - int pos = offset + 64; - int nameLen = VarInt.peek(buf, pos); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); - } else if (nameLen > 512) { - throw ProtocolException.stringTooLong("Name", nameLen, 512); + if (buf.readableBytes() - offset < 64) { + throw ProtocolException.bufferTooSmall("Asset", 64, buf.readableBytes() - offset); } else { - int nameVarLen = VarInt.length(buf, pos); - obj.name = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += nameVarLen + nameLen; - return obj; + Asset obj = new Asset(); + obj.hash = PacketIO.readFixedAsciiString(buf, offset + 0, 64); + int pos = offset + 64; + int nameLen = VarInt.peek(buf, pos); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } else { + int nameVarLen = VarInt.size(nameLen); + if (nameLen > 512) { + throw ProtocolException.stringTooLong("Name", nameLen, 512); + } else if (pos + nameVarLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", pos + nameVarLen + nameLen, buf.readableBytes()); + } else { + obj.name = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += nameVarLen + nameLen; + return obj; + } + } } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 64; int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; return pos - offset; } @@ -78,7 +86,7 @@ public class Asset { } else if (nameLen > 512) { return ValidationResult.error("Name exceeds max length 512"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Name") : ValidationResult.OK; } diff --git a/src/com/hypixel/hytale/protocol/AssetIconProperties.java b/src/com/hypixel/hytale/protocol/AssetIconProperties.java index ca1516de..42bc3cc9 100644 --- a/src/com/hypixel/hytale/protocol/AssetIconProperties.java +++ b/src/com/hypixel/hytale/protocol/AssetIconProperties.java @@ -1,10 +1,14 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2fc; +import org.joml.Vector3fc; public class AssetIconProperties { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -14,14 +18,14 @@ public class AssetIconProperties { public static final int MAX_SIZE = 25; public float scale; @Nullable - public Vector2f translation; + public Vector2fc translation; @Nullable - public Vector3f rotation; + public Vector3fc rotation; public AssetIconProperties() { } - public AssetIconProperties(float scale, @Nullable Vector2f translation, @Nullable Vector3f rotation) { + public AssetIconProperties(float scale, @Nullable Vector2fc translation, @Nullable Vector3fc rotation) { this.scale = scale; this.translation = translation; this.rotation = rotation; @@ -35,18 +39,22 @@ public class AssetIconProperties { @Nonnull public static AssetIconProperties deserialize(@Nonnull ByteBuf buf, int offset) { - AssetIconProperties obj = new AssetIconProperties(); - byte nullBits = buf.getByte(offset); - obj.scale = buf.getFloatLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.translation = Vector2f.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 25) { + throw ProtocolException.bufferTooSmall("AssetIconProperties", 25, buf.readableBytes() - offset); + } else { + AssetIconProperties obj = new AssetIconProperties(); + byte nullBits = buf.getByte(offset); + obj.scale = buf.getFloatLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.translation = PacketIO.readVector2f(buf, offset + 5); + } - if ((nullBits & 2) != 0) { - obj.rotation = Vector3f.deserialize(buf, offset + 13); - } + if ((nullBits & 2) != 0) { + obj.rotation = PacketIO.readVector3f(buf, offset + 13); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -66,13 +74,13 @@ public class AssetIconProperties { buf.writeByte(nullBits); buf.writeFloatLE(this.scale); if (this.translation != null) { - this.translation.serialize(buf); + PacketIO.writeVector2f(buf, this.translation); } else { buf.writeZero(8); } if (this.rotation != null) { - this.rotation.serialize(buf); + PacketIO.writeVector3f(buf, this.rotation); } else { buf.writeZero(12); } @@ -83,14 +91,19 @@ public class AssetIconProperties { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 25 ? ValidationResult.error("Buffer too small: expected at least 25 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 25) { + return ValidationResult.error("Buffer too small: expected at least 25 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public AssetIconProperties clone() { AssetIconProperties copy = new AssetIconProperties(); copy.scale = this.scale; - copy.translation = this.translation != null ? this.translation.clone() : null; - copy.rotation = this.rotation != null ? this.rotation.clone() : null; + copy.translation = this.translation; + copy.rotation = this.rotation; return copy; } diff --git a/src/com/hypixel/hytale/protocol/AudioCategory.java b/src/com/hypixel/hytale/protocol/AudioCategory.java index 4410bb37..4fefb194 100644 --- a/src/com/hypixel/hytale/protocol/AudioCategory.java +++ b/src/com/hypixel/hytale/protocol/AudioCategory.java @@ -34,26 +34,34 @@ public class AudioCategory { @Nonnull public static AudioCategory deserialize(@Nonnull ByteBuf buf, int offset) { - AudioCategory obj = new AudioCategory(); - byte nullBits = buf.getByte(offset); - obj.volume = buf.getFloatLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int idLen = VarInt.peek(buf, pos); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AudioCategory", 5, buf.readableBytes() - offset); + } else { + AudioCategory obj = new AudioCategory(); + byte nullBits = buf.getByte(offset); + obj.volume = buf.getFloatLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int idLen = VarInt.peek(buf, pos); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (pos + idVarLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", pos + idVarLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += idVarLen + idLen; } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - int idVarLen = VarInt.length(buf, pos); - obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += idVarLen + idLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,7 +69,7 @@ public class AudioCategory { int pos = offset + 5; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -105,7 +113,7 @@ public class AudioCategory { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); diff --git a/src/com/hypixel/hytale/protocol/AudioUpdate.java b/src/com/hypixel/hytale/protocol/AudioUpdate.java index 786e85cf..2e5d6ac4 100644 --- a/src/com/hypixel/hytale/protocol/AudioUpdate.java +++ b/src/com/hypixel/hytale/protocol/AudioUpdate.java @@ -33,12 +33,12 @@ public class AudioUpdate extends ComponentUpdate { int pos = offset + 0; int soundEventIdsCount = VarInt.peek(buf, pos); if (soundEventIdsCount < 0) { - throw ProtocolException.negativeLength("SoundEventIds", soundEventIdsCount); - } else if (soundEventIdsCount > 4096000) { - throw ProtocolException.arrayTooLong("SoundEventIds", soundEventIdsCount, 4096000); + throw ProtocolException.invalidVarInt("SoundEventIds"); } else { int soundEventIdsVarLen = VarInt.size(soundEventIdsCount); - if (pos + soundEventIdsVarLen + soundEventIdsCount * 4L > buf.readableBytes()) { + if (soundEventIdsCount > 4096000) { + throw ProtocolException.arrayTooLong("SoundEventIds", soundEventIdsCount, 4096000); + } else if (pos + soundEventIdsVarLen + soundEventIdsCount * 4L > buf.readableBytes()) { throw ProtocolException.bufferTooSmall("SoundEventIds", pos + soundEventIdsVarLen + soundEventIdsCount * 4, buf.readableBytes()); } else { pos += soundEventIdsVarLen; @@ -57,7 +57,7 @@ public class AudioUpdate extends ComponentUpdate { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 0; int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 4; + pos += VarInt.size(arrLen) + arrLen * 4; return pos - offset; } @@ -94,7 +94,7 @@ public class AudioUpdate extends ComponentUpdate { } else if (soundEventIdsCount > 4096000) { return ValidationResult.error("SoundEventIds exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(soundEventIdsCount); pos += soundEventIdsCount * 4; return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading SoundEventIds") : ValidationResult.OK; } diff --git a/src/com/hypixel/hytale/protocol/Bench.java b/src/com/hypixel/hytale/protocol/Bench.java index ce6c13e2..88ab9699 100644 --- a/src/com/hypixel/hytale/protocol/Bench.java +++ b/src/com/hypixel/hytale/protocol/Bench.java @@ -30,34 +30,38 @@ public class Bench { @Nonnull public static Bench deserialize(@Nonnull ByteBuf buf, int offset) { - Bench obj = new Bench(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int benchTierLevelsCount = VarInt.peek(buf, pos); - if (benchTierLevelsCount < 0) { - throw ProtocolException.negativeLength("BenchTierLevels", benchTierLevelsCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("Bench", 1, buf.readableBytes() - offset); + } else { + Bench obj = new Bench(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int benchTierLevelsCount = VarInt.peek(buf, pos); + if (benchTierLevelsCount < 0) { + throw ProtocolException.invalidVarInt("BenchTierLevels"); + } + + int benchTierLevelsVarLen = VarInt.size(benchTierLevelsCount); + if (benchTierLevelsCount > 4096000) { + throw ProtocolException.arrayTooLong("BenchTierLevels", benchTierLevelsCount, 4096000); + } + + if (pos + benchTierLevelsVarLen + benchTierLevelsCount * 17L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BenchTierLevels", pos + benchTierLevelsVarLen + benchTierLevelsCount * 17, buf.readableBytes()); + } + + pos += benchTierLevelsVarLen; + obj.benchTierLevels = new BenchTierLevel[benchTierLevelsCount]; + + for (int i = 0; i < benchTierLevelsCount; i++) { + obj.benchTierLevels[i] = BenchTierLevel.deserialize(buf, pos); + pos += BenchTierLevel.computeBytesConsumed(buf, pos); + } } - if (benchTierLevelsCount > 4096000) { - throw ProtocolException.arrayTooLong("BenchTierLevels", benchTierLevelsCount, 4096000); - } - - int benchTierLevelsVarLen = VarInt.size(benchTierLevelsCount); - if (pos + benchTierLevelsVarLen + benchTierLevelsCount * 17L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("BenchTierLevels", pos + benchTierLevelsVarLen + benchTierLevelsCount * 17, buf.readableBytes()); - } - - pos += benchTierLevelsVarLen; - obj.benchTierLevels = new BenchTierLevel[benchTierLevelsCount]; - - for (int i = 0; i < benchTierLevelsCount; i++) { - obj.benchTierLevels[i] = BenchTierLevel.deserialize(buf, pos); - pos += BenchTierLevel.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -65,7 +69,7 @@ public class Bench { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += BenchTierLevel.computeBytesConsumed(buf, pos); @@ -126,7 +130,7 @@ public class Bench { return ValidationResult.error("BenchTierLevels exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(benchTierLevelsCount); for (int i = 0; i < benchTierLevelsCount; i++) { ValidationResult structResult = BenchTierLevel.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/BenchRequirement.java b/src/com/hypixel/hytale/protocol/BenchRequirement.java index 81b44dd0..c4427a90 100644 --- a/src/com/hypixel/hytale/protocol/BenchRequirement.java +++ b/src/com/hypixel/hytale/protocol/BenchRequirement.java @@ -43,54 +43,77 @@ public class BenchRequirement { @Nonnull public static BenchRequirement deserialize(@Nonnull ByteBuf buf, int offset) { - BenchRequirement obj = new BenchRequirement(); - byte nullBits = buf.getByte(offset); - obj.type = BenchType.fromValue(buf.getByte(offset + 1)); - obj.requiredTierLevel = buf.getIntLE(offset + 2); - int varPos0 = offset + 14 + buf.getIntLE(offset + 6); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); - } else if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("BenchRequirement", 14, buf.readableBytes() - offset); } else { - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - if ((nullBits & 1) != 0) { - varPos0 = offset + 14 + buf.getIntLE(offset + 10); - idLen = VarInt.peek(buf, varPos0); + BenchRequirement obj = new BenchRequirement(); + byte nullBits = buf.getByte(offset); + obj.type = BenchType.fromValue(buf.getByte(offset + 1)); + obj.requiredTierLevel = buf.getIntLE(offset + 2); + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 14) { + int varPos0 = offset + 14 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); if (idLen < 0) { - throw ProtocolException.negativeLength("Categories", idLen); - } + throw ProtocolException.invalidVarInt("Id"); + } else { + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } else if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } else { + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 1) != 0) { + varPosBase0 = buf.getIntLE(offset + 10); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Categories", varPosBase0, buf.readableBytes()); + } - if (idLen > 4096000) { - throw ProtocolException.arrayTooLong("Categories", idLen, 4096000); - } + varPos0 = offset + 14 + varPosBase0; + idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Categories"); + } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + idLen * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Categories", varPos0 + varIntLen + idLen * 1, buf.readableBytes()); - } + idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.arrayTooLong("Categories", idLen, 4096000); + } - obj.categories = new String[idLen]; - int elemPos = varPos0 + varIntLen; + if (varPos0 + idVarIntLen + idLen * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Categories", varPos0 + idVarIntLen + idLen * 1, buf.readableBytes()); + } - for (int i = 0; i < idLen; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("categories[" + i + "]", strLen); + obj.categories = new String[idLen]; + int elemPos = varPos0 + idVarIntLen; + + for (int i = 0; i < idLen; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("categories[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("categories[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("categories[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.categories[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; } - - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("categories[" + i + "]", strLen, 4096000); - } - - int strVarLen = VarInt.length(buf, elemPos); - obj.categories[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; } + } else { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); } - - return obj; } } @@ -98,30 +121,38 @@ public class BenchRequirement { byte nullBits = buf.getByte(offset); int maxEnd = 14; int fieldOffset0 = buf.getIntLE(offset + 6); - int pos0 = offset + 14 + fieldOffset0; - int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - - if ((nullBits & 1) != 0) { - fieldOffset0 = buf.getIntLE(offset + 10); - pos0 = offset + 14 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); - - for (int i = 0; i < sl; i++) { - int slx = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + slx; - } - + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 14) { + int pos0 = offset + 14 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } - } - return maxEnd; + if ((nullBits & 1) != 0) { + fieldOffset0 = buf.getIntLE(offset + 10); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Categories", fieldOffset0, maxEnd); + } + + pos0 = offset + 14 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl); + + for (int i = 0; i < sl; i++) { + int slx = VarInt.peek(buf, pos0); + pos0 += VarInt.size(slx) + slx; + } + + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } } public void serialize(@Nonnull ByteBuf buf) { @@ -178,36 +209,31 @@ public class BenchRequirement { return ValidationResult.error("Buffer too small: expected at least 14 bytes"); } else { byte nullBits = buffer.getByte(offset); - int idOffset = buffer.getIntLE(offset + 6); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid BenchType value for Type"); } else { - int pos = offset + 14 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } else { + v = buffer.getIntLE(offset + 6); + if (v >= 0 && v <= buffer.writerIndex() - offset - 14) { + int pos = offset + 14 + v; int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); } else if (idLen > 4096000) { return ValidationResult.error("Id exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); } else { if ((nullBits & 1) != 0) { - idOffset = buffer.getIntLE(offset + 10); - if (idOffset < 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 14) { return ValidationResult.error("Invalid offset for Categories"); } - pos = offset + 14 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Categories"); - } - + pos = offset + 14 + v; idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid array count for Categories"); @@ -217,7 +243,7 @@ public class BenchRequirement { return ValidationResult.error("Categories exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); for (int i = 0; i < idLen; i++) { int strLen = VarInt.peek(buffer, pos); @@ -225,7 +251,7 @@ public class BenchRequirement { return ValidationResult.error("Invalid string length in Categories"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(strLen); pos += strLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Categories"); @@ -236,6 +262,8 @@ public class BenchRequirement { return ValidationResult.OK; } } + } else { + return ValidationResult.error("Invalid offset for Id"); } } } diff --git a/src/com/hypixel/hytale/protocol/BenchTierLevel.java b/src/com/hypixel/hytale/protocol/BenchTierLevel.java index ee710bfc..b1745e8a 100644 --- a/src/com/hypixel/hytale/protocol/BenchTierLevel.java +++ b/src/com/hypixel/hytale/protocol/BenchTierLevel.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -39,18 +40,22 @@ public class BenchTierLevel { @Nonnull public static BenchTierLevel deserialize(@Nonnull ByteBuf buf, int offset) { - BenchTierLevel obj = new BenchTierLevel(); - byte nullBits = buf.getByte(offset); - obj.craftingTimeReductionModifier = buf.getDoubleLE(offset + 1); - obj.extraInputSlot = buf.getIntLE(offset + 9); - obj.extraOutputSlot = buf.getIntLE(offset + 13); - int pos = offset + 17; - if ((nullBits & 1) != 0) { - obj.benchUpgradeRequirement = BenchUpgradeRequirement.deserialize(buf, pos); - pos += BenchUpgradeRequirement.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("BenchTierLevel", 17, buf.readableBytes() - offset); + } else { + BenchTierLevel obj = new BenchTierLevel(); + byte nullBits = buf.getByte(offset); + obj.craftingTimeReductionModifier = buf.getDoubleLE(offset + 1); + obj.extraInputSlot = buf.getIntLE(offset + 9); + obj.extraOutputSlot = buf.getIntLE(offset + 13); + int pos = offset + 17; + if ((nullBits & 1) != 0) { + obj.benchUpgradeRequirement = BenchUpgradeRequirement.deserialize(buf, pos); + pos += BenchUpgradeRequirement.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/BenchUpgradeRequirement.java b/src/com/hypixel/hytale/protocol/BenchUpgradeRequirement.java index 6604da3c..ea62db89 100644 --- a/src/com/hypixel/hytale/protocol/BenchUpgradeRequirement.java +++ b/src/com/hypixel/hytale/protocol/BenchUpgradeRequirement.java @@ -33,35 +33,39 @@ public class BenchUpgradeRequirement { @Nonnull public static BenchUpgradeRequirement deserialize(@Nonnull ByteBuf buf, int offset) { - BenchUpgradeRequirement obj = new BenchUpgradeRequirement(); - byte nullBits = buf.getByte(offset); - obj.timeSeconds = buf.getDoubleLE(offset + 1); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - int materialCount = VarInt.peek(buf, pos); - if (materialCount < 0) { - throw ProtocolException.negativeLength("Material", materialCount); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("BenchUpgradeRequirement", 9, buf.readableBytes() - offset); + } else { + BenchUpgradeRequirement obj = new BenchUpgradeRequirement(); + byte nullBits = buf.getByte(offset); + obj.timeSeconds = buf.getDoubleLE(offset + 1); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int materialCount = VarInt.peek(buf, pos); + if (materialCount < 0) { + throw ProtocolException.invalidVarInt("Material"); + } + + int materialVarLen = VarInt.size(materialCount); + if (materialCount > 4096000) { + throw ProtocolException.arrayTooLong("Material", materialCount, 4096000); + } + + if (pos + materialVarLen + materialCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Material", pos + materialVarLen + materialCount * 9, buf.readableBytes()); + } + + pos += materialVarLen; + obj.material = new MaterialQuantity[materialCount]; + + for (int i = 0; i < materialCount; i++) { + obj.material[i] = MaterialQuantity.deserialize(buf, pos); + pos += MaterialQuantity.computeBytesConsumed(buf, pos); + } } - if (materialCount > 4096000) { - throw ProtocolException.arrayTooLong("Material", materialCount, 4096000); - } - - int materialVarLen = VarInt.size(materialCount); - if (pos + materialVarLen + materialCount * 9L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Material", pos + materialVarLen + materialCount * 9, buf.readableBytes()); - } - - pos += materialVarLen; - obj.material = new MaterialQuantity[materialCount]; - - for (int i = 0; i < materialCount; i++) { - obj.material[i] = MaterialQuantity.deserialize(buf, pos); - pos += MaterialQuantity.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -69,7 +73,7 @@ public class BenchUpgradeRequirement { int pos = offset + 9; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += MaterialQuantity.computeBytesConsumed(buf, pos); @@ -131,7 +135,7 @@ public class BenchUpgradeRequirement { return ValidationResult.error("Material exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(materialCount); for (int i = 0; i < materialCount; i++) { ValidationResult structResult = MaterialQuantity.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/BlockBreaking.java b/src/com/hypixel/hytale/protocol/BlockBreaking.java index 42be2cea..5a51e815 100644 --- a/src/com/hypixel/hytale/protocol/BlockBreaking.java +++ b/src/com/hypixel/hytale/protocol/BlockBreaking.java @@ -48,54 +48,88 @@ public class BlockBreaking { @Nonnull public static BlockBreaking deserialize(@Nonnull ByteBuf buf, int offset) { - BlockBreaking obj = new BlockBreaking(); - byte nullBits = buf.getByte(offset); - obj.health = buf.getFloatLE(offset + 1); - obj.quantity = buf.getIntLE(offset + 5); - obj.quality = buf.getIntLE(offset + 9); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 25 + buf.getIntLE(offset + 13); - int gatherTypeLen = VarInt.peek(buf, varPos0); - if (gatherTypeLen < 0) { - throw ProtocolException.negativeLength("GatherType", gatherTypeLen); + if (buf.readableBytes() - offset < 25) { + throw ProtocolException.bufferTooSmall("BlockBreaking", 25, buf.readableBytes() - offset); + } else { + BlockBreaking obj = new BlockBreaking(); + byte nullBits = buf.getByte(offset); + obj.health = buf.getFloatLE(offset + 1); + obj.quantity = buf.getIntLE(offset + 5); + obj.quality = buf.getIntLE(offset + 9); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 13); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("GatherType", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 25 + varPosBase0; + int gatherTypeLen = VarInt.peek(buf, varPos0); + if (gatherTypeLen < 0) { + throw ProtocolException.invalidVarInt("GatherType"); + } + + int gatherTypeVarIntLen = VarInt.size(gatherTypeLen); + if (gatherTypeLen > 4096000) { + throw ProtocolException.stringTooLong("GatherType", gatherTypeLen, 4096000); + } + + if (varPos0 + gatherTypeVarIntLen + gatherTypeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("GatherType", varPos0 + gatherTypeVarIntLen + gatherTypeLen, buf.readableBytes()); + } + + obj.gatherType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (gatherTypeLen > 4096000) { - throw ProtocolException.stringTooLong("GatherType", gatherTypeLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 17); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("ItemId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 25 + varPosBase1; + int itemIdLen = VarInt.peek(buf, varPos1); + if (itemIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemId"); + } + + int itemIdVarIntLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + } + + if (varPos1 + itemIdVarIntLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemId", varPos1 + itemIdVarIntLen + itemIdLen, buf.readableBytes()); + } + + obj.itemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.gatherType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 21); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("DropListId", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 25 + varPosBase2; + int dropListIdLen = VarInt.peek(buf, varPos2); + if (dropListIdLen < 0) { + throw ProtocolException.invalidVarInt("DropListId"); + } + + int dropListIdVarIntLen = VarInt.size(dropListIdLen); + if (dropListIdLen > 4096000) { + throw ProtocolException.stringTooLong("DropListId", dropListIdLen, 4096000); + } + + if (varPos2 + dropListIdVarIntLen + dropListIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("DropListId", varPos2 + dropListIdVarIntLen + dropListIdLen, buf.readableBytes()); + } + + obj.dropListId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 25 + buf.getIntLE(offset + 17); - int itemIdLen = VarInt.peek(buf, varPos1); - if (itemIdLen < 0) { - throw ProtocolException.negativeLength("ItemId", itemIdLen); - } - - if (itemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); - } - - obj.itemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 25 + buf.getIntLE(offset + 21); - int dropListIdLen = VarInt.peek(buf, varPos2); - if (dropListIdLen < 0) { - throw ProtocolException.negativeLength("DropListId", dropListIdLen); - } - - if (dropListIdLen > 4096000) { - throw ProtocolException.stringTooLong("DropListId", dropListIdLen, 4096000); - } - - obj.dropListId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -103,9 +137,13 @@ public class BlockBreaking { int maxEnd = 25; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 13); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("GatherType", fieldOffset0, maxEnd); + } + int pos0 = offset + 25 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -113,9 +151,13 @@ public class BlockBreaking { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 17); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("ItemId", fieldOffset1, maxEnd); + } + int pos1 = offset + 25 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -123,9 +165,13 @@ public class BlockBreaking { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 21); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("DropListId", fieldOffset2, maxEnd); + } + int pos2 = offset + 25 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -206,15 +252,11 @@ public class BlockBreaking { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int gatherTypeOffset = buffer.getIntLE(offset + 13); - if (gatherTypeOffset < 0) { + if (gatherTypeOffset < 0 || gatherTypeOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for GatherType"); } int pos = offset + 25 + gatherTypeOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for GatherType"); - } - int gatherTypeLen = VarInt.peek(buffer, pos); if (gatherTypeLen < 0) { return ValidationResult.error("Invalid string length for GatherType"); @@ -224,7 +266,7 @@ public class BlockBreaking { return ValidationResult.error("GatherType exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(gatherTypeLen); pos += gatherTypeLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading GatherType"); @@ -233,15 +275,11 @@ public class BlockBreaking { if ((nullBits & 2) != 0) { int itemIdOffset = buffer.getIntLE(offset + 17); - if (itemIdOffset < 0) { + if (itemIdOffset < 0 || itemIdOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for ItemId"); } int posx = offset + 25 + itemIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemId"); - } - int itemIdLen = VarInt.peek(buffer, posx); if (itemIdLen < 0) { return ValidationResult.error("Invalid string length for ItemId"); @@ -251,7 +289,7 @@ public class BlockBreaking { return ValidationResult.error("ItemId exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(itemIdLen); posx += itemIdLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemId"); @@ -260,15 +298,11 @@ public class BlockBreaking { if ((nullBits & 4) != 0) { int dropListIdOffset = buffer.getIntLE(offset + 21); - if (dropListIdOffset < 0) { + if (dropListIdOffset < 0 || dropListIdOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for DropListId"); } int posxx = offset + 25 + dropListIdOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DropListId"); - } - int dropListIdLen = VarInt.peek(buffer, posxx); if (dropListIdLen < 0) { return ValidationResult.error("Invalid string length for DropListId"); @@ -278,7 +312,7 @@ public class BlockBreaking { return ValidationResult.error("DropListId exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(dropListIdLen); posxx += dropListIdLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading DropListId"); diff --git a/src/com/hypixel/hytale/protocol/BlockBreakingDecal.java b/src/com/hypixel/hytale/protocol/BlockBreakingDecal.java index ec8bd2ae..aec28071 100644 --- a/src/com/hypixel/hytale/protocol/BlockBreakingDecal.java +++ b/src/com/hypixel/hytale/protocol/BlockBreakingDecal.java @@ -31,44 +31,52 @@ public class BlockBreakingDecal { @Nonnull public static BlockBreakingDecal deserialize(@Nonnull ByteBuf buf, int offset) { - BlockBreakingDecal obj = new BlockBreakingDecal(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int stageTexturesCount = VarInt.peek(buf, pos); - if (stageTexturesCount < 0) { - throw ProtocolException.negativeLength("StageTextures", stageTexturesCount); - } - - if (stageTexturesCount > 4096000) { - throw ProtocolException.arrayTooLong("StageTextures", stageTexturesCount, 4096000); - } - - int stageTexturesVarLen = VarInt.size(stageTexturesCount); - if (pos + stageTexturesVarLen + stageTexturesCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("StageTextures", pos + stageTexturesVarLen + stageTexturesCount * 1, buf.readableBytes()); - } - - pos += stageTexturesVarLen; - obj.stageTextures = new String[stageTexturesCount]; - - for (int i = 0; i < stageTexturesCount; i++) { - int strLen = VarInt.peek(buf, pos); - if (strLen < 0) { - throw ProtocolException.negativeLength("stageTextures[" + i + "]", strLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BlockBreakingDecal", 1, buf.readableBytes() - offset); + } else { + BlockBreakingDecal obj = new BlockBreakingDecal(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int stageTexturesCount = VarInt.peek(buf, pos); + if (stageTexturesCount < 0) { + throw ProtocolException.invalidVarInt("StageTextures"); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("stageTextures[" + i + "]", strLen, 4096000); + int stageTexturesVarLen = VarInt.size(stageTexturesCount); + if (stageTexturesCount > 4096000) { + throw ProtocolException.arrayTooLong("StageTextures", stageTexturesCount, 4096000); } - int strVarLen = VarInt.length(buf, pos); - obj.stageTextures[i] = PacketIO.readVarString(buf, pos); - pos += strVarLen + strLen; + if (pos + stageTexturesVarLen + stageTexturesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("StageTextures", pos + stageTexturesVarLen + stageTexturesCount * 1, buf.readableBytes()); + } + + pos += stageTexturesVarLen; + obj.stageTextures = new String[stageTexturesCount]; + + for (int i = 0; i < stageTexturesCount; i++) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("stageTextures[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("stageTextures[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("stageTextures[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.stageTextures[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -76,11 +84,11 @@ public class BlockBreakingDecal { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -138,7 +146,7 @@ public class BlockBreakingDecal { return ValidationResult.error("StageTextures exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(stageTexturesCount); for (int i = 0; i < stageTexturesCount; i++) { int strLen = VarInt.peek(buffer, pos); @@ -146,7 +154,7 @@ public class BlockBreakingDecal { return ValidationResult.error("Invalid string length in StageTextures"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(strLen); pos += strLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in StageTextures"); diff --git a/src/com/hypixel/hytale/protocol/BlockConditionInteraction.java b/src/com/hypixel/hytale/protocol/BlockConditionInteraction.java index 683dade5..bee0f428 100644 --- a/src/com/hypixel/hytale/protocol/BlockConditionInteraction.java +++ b/src/com/hypixel/hytale/protocol/BlockConditionInteraction.java @@ -72,104 +72,138 @@ public class BlockConditionInteraction extends SimpleBlockInteraction { @Nonnull public static BlockConditionInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - BlockConditionInteraction obj = new BlockConditionInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.useLatestTarget = buf.getByte(offset + 19) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 44 + buf.getIntLE(offset + 20); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 44) { + throw ProtocolException.bufferTooSmall("BlockConditionInteraction", 44, buf.readableBytes() - offset); + } else { + BlockConditionInteraction obj = new BlockConditionInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.useLatestTarget = buf.getByte(offset + 19) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 20); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 44 + buf.getIntLE(offset + 24); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 44 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 24); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 44 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 28); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 44 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 32); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 44 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 36); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 44 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 40); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Matchers", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 44 + varPosBase5; + int matchersCount = VarInt.peek(buf, varPos5); + if (matchersCount < 0) { + throw ProtocolException.invalidVarInt("Matchers"); + } + + int varIntLenx = VarInt.size(matchersCount); + if (matchersCount > 4096000) { + throw ProtocolException.arrayTooLong("Matchers", matchersCount, 4096000); + } + + if (varPos5 + varIntLenx + matchersCount * 3L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Matchers", varPos5 + varIntLenx + matchersCount * 3, buf.readableBytes()); + } + + obj.matchers = new BlockMatcher[matchersCount]; + int elemPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < matchersCount; ix++) { + obj.matchers[ix] = BlockMatcher.deserialize(buf, elemPos); + elemPos += BlockMatcher.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 44 + buf.getIntLE(offset + 28); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 44 + buf.getIntLE(offset + 32); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 44 + buf.getIntLE(offset + 36); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 44 + buf.getIntLE(offset + 40); - int matchersCount = VarInt.peek(buf, varPos5); - if (matchersCount < 0) { - throw ProtocolException.negativeLength("Matchers", matchersCount); - } - - if (matchersCount > 4096000) { - throw ProtocolException.arrayTooLong("Matchers", matchersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + matchersCount * 3L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Matchers", varPos5 + varIntLen + matchersCount * 3, buf.readableBytes()); - } - - obj.matchers = new BlockMatcher[matchersCount]; - int elemPos = varPos5 + varIntLen; - - for (int ix = 0; ix < matchersCount; ix++) { - obj.matchers[ix] = BlockMatcher.deserialize(buf, elemPos); - elemPos += BlockMatcher.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -177,6 +211,10 @@ public class BlockConditionInteraction extends SimpleBlockInteraction { int maxEnd = 44; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 20); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 44 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -186,9 +224,13 @@ public class BlockConditionInteraction extends SimpleBlockInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 24); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 44 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -201,6 +243,10 @@ public class BlockConditionInteraction extends SimpleBlockInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 28); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 44 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -210,9 +256,13 @@ public class BlockConditionInteraction extends SimpleBlockInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 32); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 44 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -220,6 +270,10 @@ public class BlockConditionInteraction extends SimpleBlockInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 36); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 44 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -229,9 +283,13 @@ public class BlockConditionInteraction extends SimpleBlockInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 40); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Matchers", fieldOffset5, maxEnd); + } + int pos5 = offset + 44 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos5 += BlockMatcher.computeBytesConsumed(buf, pos5); @@ -405,151 +463,137 @@ public class BlockConditionInteraction extends SimpleBlockInteraction { return ValidationResult.error("Buffer too small: expected at least 44 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 20); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 44 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 24); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 44 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 28); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 44 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 32); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 44 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 36); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 44 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int matchersOffset = buffer.getIntLE(offset + 40); - if (matchersOffset < 0) { - return ValidationResult.error("Invalid offset for Matchers"); - } - - int posxxxxx = offset + 44 + matchersOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Matchers"); - } - - int matchersCount = VarInt.peek(buffer, posxxxxx); - if (matchersCount < 0) { - return ValidationResult.error("Invalid array count for Matchers"); - } - - if (matchersCount > 4096000) { - return ValidationResult.error("Matchers exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < matchersCount; i++) { - ValidationResult structResult = BlockMatcher.validateStructure(buffer, posxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid BlockMatcher in Matchers[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 20); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Effects"); } - posxxxxx += BlockMatcher.computeBytesConsumed(buffer, posxxxxx); - } - } + int pos = offset + 44 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - return ValidationResult.OK; + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 44 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } + } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 44 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 44 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 44 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 40); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Matchers"); + } + + int posxx = offset + 44 + v; + int matchersCount = VarInt.peek(buffer, posxx); + if (matchersCount < 0) { + return ValidationResult.error("Invalid array count for Matchers"); + } + + if (matchersCount > 4096000) { + return ValidationResult.error("Matchers exceeds max length 4096000"); + } + + posxx += VarInt.size(matchersCount); + + for (int i = 0; i < matchersCount; i++) { + ValidationResult structResult = BlockMatcher.validateStructure(buffer, posxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid BlockMatcher in Matchers[" + i + "]: " + structResult.error()); + } + + posxx += BlockMatcher.computeBytesConsumed(buffer, posxx); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/BlockFaceSupport.java b/src/com/hypixel/hytale/protocol/BlockFaceSupport.java index 74174faa..863127bb 100644 --- a/src/com/hypixel/hytale/protocol/BlockFaceSupport.java +++ b/src/com/hypixel/hytale/protocol/BlockFaceSupport.java @@ -36,48 +36,67 @@ public class BlockFaceSupport { @Nonnull public static BlockFaceSupport deserialize(@Nonnull ByteBuf buf, int offset) { - BlockFaceSupport obj = new BlockFaceSupport(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int faceTypeLen = VarInt.peek(buf, varPos0); - if (faceTypeLen < 0) { - throw ProtocolException.negativeLength("FaceType", faceTypeLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("BlockFaceSupport", 9, buf.readableBytes() - offset); + } else { + BlockFaceSupport obj = new BlockFaceSupport(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("FaceType", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int faceTypeLen = VarInt.peek(buf, varPos0); + if (faceTypeLen < 0) { + throw ProtocolException.invalidVarInt("FaceType"); + } + + int faceTypeVarIntLen = VarInt.size(faceTypeLen); + if (faceTypeLen > 4096000) { + throw ProtocolException.stringTooLong("FaceType", faceTypeLen, 4096000); + } + + if (varPos0 + faceTypeVarIntLen + faceTypeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FaceType", varPos0 + faceTypeVarIntLen + faceTypeLen, buf.readableBytes()); + } + + obj.faceType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (faceTypeLen > 4096000) { - throw ProtocolException.stringTooLong("FaceType", faceTypeLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Filler", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int fillerCount = VarInt.peek(buf, varPos1); + if (fillerCount < 0) { + throw ProtocolException.invalidVarInt("Filler"); + } + + int varIntLen = VarInt.size(fillerCount); + if (fillerCount > 4096000) { + throw ProtocolException.arrayTooLong("Filler", fillerCount, 4096000); + } + + if (varPos1 + varIntLen + fillerCount * 12L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Filler", varPos1 + varIntLen + fillerCount * 12, buf.readableBytes()); + } + + obj.filler = new Vector3i[fillerCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < fillerCount; i++) { + obj.filler[i] = Vector3i.deserialize(buf, elemPos); + elemPos += Vector3i.computeBytesConsumed(buf, elemPos); + } } - obj.faceType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int fillerCount = VarInt.peek(buf, varPos1); - if (fillerCount < 0) { - throw ProtocolException.negativeLength("Filler", fillerCount); - } - - if (fillerCount > 4096000) { - throw ProtocolException.arrayTooLong("Filler", fillerCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + fillerCount * 12L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Filler", varPos1 + varIntLen + fillerCount * 12, buf.readableBytes()); - } - - obj.filler = new Vector3i[fillerCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < fillerCount; i++) { - obj.filler[i] = Vector3i.deserialize(buf, elemPos); - elemPos += Vector3i.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -85,9 +104,13 @@ public class BlockFaceSupport { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("FaceType", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -95,9 +118,13 @@ public class BlockFaceSupport { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Filler", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += Vector3i.computeBytesConsumed(buf, pos1); @@ -171,15 +198,11 @@ public class BlockFaceSupport { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int faceTypeOffset = buffer.getIntLE(offset + 1); - if (faceTypeOffset < 0) { + if (faceTypeOffset < 0 || faceTypeOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for FaceType"); } int pos = offset + 9 + faceTypeOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FaceType"); - } - int faceTypeLen = VarInt.peek(buffer, pos); if (faceTypeLen < 0) { return ValidationResult.error("Invalid string length for FaceType"); @@ -189,7 +212,7 @@ public class BlockFaceSupport { return ValidationResult.error("FaceType exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(faceTypeLen); pos += faceTypeLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FaceType"); @@ -198,15 +221,11 @@ public class BlockFaceSupport { if ((nullBits & 2) != 0) { int fillerOffset = buffer.getIntLE(offset + 5); - if (fillerOffset < 0) { + if (fillerOffset < 0 || fillerOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Filler"); } int posx = offset + 9 + fillerOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Filler"); - } - int fillerCount = VarInt.peek(buffer, posx); if (fillerCount < 0) { return ValidationResult.error("Invalid array count for Filler"); @@ -216,7 +235,7 @@ public class BlockFaceSupport { return ValidationResult.error("Filler exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(fillerCount); posx += fillerCount * 12; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Filler"); diff --git a/src/com/hypixel/hytale/protocol/BlockFlags.java b/src/com/hypixel/hytale/protocol/BlockFlags.java index 929854c1..af11ccf1 100644 --- a/src/com/hypixel/hytale/protocol/BlockFlags.java +++ b/src/com/hypixel/hytale/protocol/BlockFlags.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class BlockFlags { @Nonnull public static BlockFlags deserialize(@Nonnull ByteBuf buf, int offset) { - BlockFlags obj = new BlockFlags(); - obj.isUsable = buf.getByte(offset + 0) != 0; - obj.isStackable = buf.getByte(offset + 1) != 0; - return obj; + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("BlockFlags", 2, buf.readableBytes() - offset); + } else { + BlockFlags obj = new BlockFlags(); + obj.isUsable = buf.getByte(offset + 0) != 0; + obj.isStackable = buf.getByte(offset + 1) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/BlockGathering.java b/src/com/hypixel/hytale/protocol/BlockGathering.java index 9edbfdd1..97562930 100644 --- a/src/com/hypixel/hytale/protocol/BlockGathering.java +++ b/src/com/hypixel/hytale/protocol/BlockGathering.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -36,24 +37,43 @@ public class BlockGathering { @Nonnull public static BlockGathering deserialize(@Nonnull ByteBuf buf, int offset) { - BlockGathering obj = new BlockGathering(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - obj.breaking = BlockBreaking.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("BlockGathering", 13, buf.readableBytes() - offset); + } else { + BlockGathering obj = new BlockGathering(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Breaking", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - obj.harvest = Harvesting.deserialize(buf, varPos1); - } + int varPos0 = offset + 13 + varPosBase0; + obj.breaking = BlockBreaking.deserialize(buf, varPos0); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - obj.soft = SoftBlock.deserialize(buf, varPos2); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Harvest", varPosBase1, buf.readableBytes()); + } - return obj; + int varPos1 = offset + 13 + varPosBase1; + obj.harvest = Harvesting.deserialize(buf, varPos1); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Soft", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + obj.soft = SoftBlock.deserialize(buf, varPos2); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,6 +81,10 @@ public class BlockGathering { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Breaking", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; pos0 += BlockBreaking.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -70,6 +94,10 @@ public class BlockGathering { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Harvest", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; pos1 += Harvesting.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -79,6 +107,10 @@ public class BlockGathering { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Soft", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; pos2 += SoftBlock.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -158,15 +190,11 @@ public class BlockGathering { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int breakingOffset = buffer.getIntLE(offset + 1); - if (breakingOffset < 0) { + if (breakingOffset < 0 || breakingOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Breaking"); } int pos = offset + 13 + breakingOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Breaking"); - } - ValidationResult breakingResult = BlockBreaking.validateStructure(buffer, pos); if (!breakingResult.isValid()) { return ValidationResult.error("Invalid Breaking: " + breakingResult.error()); @@ -177,40 +205,32 @@ public class BlockGathering { if ((nullBits & 2) != 0) { int harvestOffset = buffer.getIntLE(offset + 5); - if (harvestOffset < 0) { + if (harvestOffset < 0 || harvestOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Harvest"); } - int posx = offset + 13 + harvestOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Harvest"); - } - - ValidationResult harvestResult = Harvesting.validateStructure(buffer, posx); + int pos = offset + 13 + harvestOffset; + ValidationResult harvestResult = Harvesting.validateStructure(buffer, pos); if (!harvestResult.isValid()) { return ValidationResult.error("Invalid Harvest: " + harvestResult.error()); } - posx += Harvesting.computeBytesConsumed(buffer, posx); + pos += Harvesting.computeBytesConsumed(buffer, pos); } if ((nullBits & 4) != 0) { int softOffset = buffer.getIntLE(offset + 9); - if (softOffset < 0) { + if (softOffset < 0 || softOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Soft"); } - int posxx = offset + 13 + softOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Soft"); - } - - ValidationResult softResult = SoftBlock.validateStructure(buffer, posxx); + int pos = offset + 13 + softOffset; + ValidationResult softResult = SoftBlock.validateStructure(buffer, pos); if (!softResult.isValid()) { return ValidationResult.error("Invalid Soft: " + softResult.error()); } - posxx += SoftBlock.computeBytesConsumed(buffer, posxx); + pos += SoftBlock.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/BlockGroup.java b/src/com/hypixel/hytale/protocol/BlockGroup.java index 5ebbb66d..487340ea 100644 --- a/src/com/hypixel/hytale/protocol/BlockGroup.java +++ b/src/com/hypixel/hytale/protocol/BlockGroup.java @@ -31,44 +31,52 @@ public class BlockGroup { @Nonnull public static BlockGroup deserialize(@Nonnull ByteBuf buf, int offset) { - BlockGroup obj = new BlockGroup(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int namesCount = VarInt.peek(buf, pos); - if (namesCount < 0) { - throw ProtocolException.negativeLength("Names", namesCount); - } - - if (namesCount > 4096000) { - throw ProtocolException.arrayTooLong("Names", namesCount, 4096000); - } - - int namesVarLen = VarInt.size(namesCount); - if (pos + namesVarLen + namesCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Names", pos + namesVarLen + namesCount * 1, buf.readableBytes()); - } - - pos += namesVarLen; - obj.names = new String[namesCount]; - - for (int i = 0; i < namesCount; i++) { - int strLen = VarInt.peek(buf, pos); - if (strLen < 0) { - throw ProtocolException.negativeLength("names[" + i + "]", strLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BlockGroup", 1, buf.readableBytes() - offset); + } else { + BlockGroup obj = new BlockGroup(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int namesCount = VarInt.peek(buf, pos); + if (namesCount < 0) { + throw ProtocolException.invalidVarInt("Names"); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("names[" + i + "]", strLen, 4096000); + int namesVarLen = VarInt.size(namesCount); + if (namesCount > 4096000) { + throw ProtocolException.arrayTooLong("Names", namesCount, 4096000); } - int strVarLen = VarInt.length(buf, pos); - obj.names[i] = PacketIO.readVarString(buf, pos); - pos += strVarLen + strLen; + if (pos + namesVarLen + namesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Names", pos + namesVarLen + namesCount * 1, buf.readableBytes()); + } + + pos += namesVarLen; + obj.names = new String[namesCount]; + + for (int i = 0; i < namesCount; i++) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("names[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("names[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("names[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.names[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -76,11 +84,11 @@ public class BlockGroup { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -138,7 +146,7 @@ public class BlockGroup { return ValidationResult.error("Names exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(namesCount); for (int i = 0; i < namesCount; i++) { int strLen = VarInt.peek(buffer, pos); @@ -146,7 +154,7 @@ public class BlockGroup { return ValidationResult.error("Invalid string length in Names"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(strLen); pos += strLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Names"); diff --git a/src/com/hypixel/hytale/protocol/BlockIdMatcher.java b/src/com/hypixel/hytale/protocol/BlockIdMatcher.java index 869cc42e..bc1663e7 100644 --- a/src/com/hypixel/hytale/protocol/BlockIdMatcher.java +++ b/src/com/hypixel/hytale/protocol/BlockIdMatcher.java @@ -38,38 +38,62 @@ public class BlockIdMatcher { @Nonnull public static BlockIdMatcher deserialize(@Nonnull ByteBuf buf, int offset) { - BlockIdMatcher obj = new BlockIdMatcher(); - byte nullBits = buf.getByte(offset); - obj.tagIndex = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 5); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("BlockIdMatcher", 13, buf.readableBytes() - offset); + } else { + BlockIdMatcher obj = new BlockIdMatcher(); + byte nullBits = buf.getByte(offset); + obj.tagIndex = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("State", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int stateLen = VarInt.peek(buf, varPos1); + if (stateLen < 0) { + throw ProtocolException.invalidVarInt("State"); + } + + int stateVarIntLen = VarInt.size(stateLen); + if (stateLen > 4096000) { + throw ProtocolException.stringTooLong("State", stateLen, 4096000); + } + + if (varPos1 + stateVarIntLen + stateLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("State", varPos1 + stateVarIntLen + stateLen, buf.readableBytes()); + } + + obj.state = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 9); - int stateLen = VarInt.peek(buf, varPos1); - if (stateLen < 0) { - throw ProtocolException.negativeLength("State", stateLen); - } - - if (stateLen > 4096000) { - throw ProtocolException.stringTooLong("State", stateLen, 4096000); - } - - obj.state = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -77,9 +101,13 @@ public class BlockIdMatcher { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -87,9 +115,13 @@ public class BlockIdMatcher { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("State", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -151,15 +183,11 @@ public class BlockIdMatcher { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 5); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 13 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -169,7 +197,7 @@ public class BlockIdMatcher { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -178,15 +206,11 @@ public class BlockIdMatcher { if ((nullBits & 2) != 0) { int stateOffset = buffer.getIntLE(offset + 9); - if (stateOffset < 0) { + if (stateOffset < 0 || stateOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for State"); } int posx = offset + 13 + stateOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for State"); - } - int stateLen = VarInt.peek(buffer, posx); if (stateLen < 0) { return ValidationResult.error("Invalid string length for State"); @@ -196,7 +220,7 @@ public class BlockIdMatcher { return ValidationResult.error("State exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(stateLen); posx += stateLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading State"); diff --git a/src/com/hypixel/hytale/protocol/BlockMatcher.java b/src/com/hypixel/hytale/protocol/BlockMatcher.java index c7c4c08e..119079fe 100644 --- a/src/com/hypixel/hytale/protocol/BlockMatcher.java +++ b/src/com/hypixel/hytale/protocol/BlockMatcher.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -35,17 +36,21 @@ public class BlockMatcher { @Nonnull public static BlockMatcher deserialize(@Nonnull ByteBuf buf, int offset) { - BlockMatcher obj = new BlockMatcher(); - byte nullBits = buf.getByte(offset); - obj.face = BlockFace.fromValue(buf.getByte(offset + 1)); - obj.staticFace = buf.getByte(offset + 2) != 0; - int pos = offset + 3; - if ((nullBits & 1) != 0) { - obj.block = BlockIdMatcher.deserialize(buf, pos); - pos += BlockIdMatcher.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("BlockMatcher", 3, buf.readableBytes() - offset); + } else { + BlockMatcher obj = new BlockMatcher(); + byte nullBits = buf.getByte(offset); + obj.face = BlockFace.fromValue(buf.getByte(offset + 1)); + obj.staticFace = buf.getByte(offset + 2) != 0; + int pos = offset + 3; + if ((nullBits & 1) != 0) { + obj.block = BlockIdMatcher.deserialize(buf, pos); + pos += BlockIdMatcher.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -86,17 +91,22 @@ public class BlockMatcher { return ValidationResult.error("Buffer too small: expected at least 3 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 3; - if ((nullBits & 1) != 0) { - ValidationResult blockResult = BlockIdMatcher.validateStructure(buffer, pos); - if (!blockResult.isValid()) { - return ValidationResult.error("Invalid Block: " + blockResult.error()); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 7) { + return ValidationResult.error("Invalid BlockFace value for Face"); + } else { + v = offset + 3; + if ((nullBits & 1) != 0) { + ValidationResult blockResult = BlockIdMatcher.validateStructure(buffer, v); + if (!blockResult.isValid()) { + return ValidationResult.error("Invalid Block: " + blockResult.error()); + } + + v += BlockIdMatcher.computeBytesConsumed(buffer, v); } - pos += BlockIdMatcher.computeBytesConsumed(buffer, pos); + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/BlockMount.java b/src/com/hypixel/hytale/protocol/BlockMount.java index 5467b10e..0473a633 100644 --- a/src/com/hypixel/hytale/protocol/BlockMount.java +++ b/src/com/hypixel/hytale/protocol/BlockMount.java @@ -1,29 +1,31 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import org.joml.Vector3fc; public class BlockMount { - public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 30; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 29; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 30; - public static final int MAX_SIZE = 30; + public static final int VARIABLE_BLOCK_START = 29; + public static final int MAX_SIZE = 29; @Nonnull public BlockMountType type = BlockMountType.Seat; - @Nullable - public Vector3f position; - @Nullable - public Vector3f orientation; + @Nonnull + public Vector3fc position = PacketIO.ZERO_VECTOR3; + @Nonnull + public Vector3fc orientation = PacketIO.ZERO_VECTOR3; public int blockTypeId; public BlockMount() { } - public BlockMount(@Nonnull BlockMountType type, @Nullable Vector3f position, @Nullable Vector3f orientation, int blockTypeId) { + public BlockMount(@Nonnull BlockMountType type, @Nonnull Vector3fc position, @Nonnull Vector3fc orientation, int blockTypeId) { this.type = type; this.position = position; this.orientation = orientation; @@ -39,65 +41,47 @@ public class BlockMount { @Nonnull public static BlockMount deserialize(@Nonnull ByteBuf buf, int offset) { - BlockMount obj = new BlockMount(); - byte nullBits = buf.getByte(offset); - obj.type = BlockMountType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - obj.position = Vector3f.deserialize(buf, offset + 2); + if (buf.readableBytes() - offset < 29) { + throw ProtocolException.bufferTooSmall("BlockMount", 29, buf.readableBytes() - offset); + } else { + BlockMount obj = new BlockMount(); + obj.type = BlockMountType.fromValue(buf.getByte(offset + 0)); + obj.position = PacketIO.readVector3f(buf, offset + 1); + obj.orientation = PacketIO.readVector3f(buf, offset + 13); + obj.blockTypeId = buf.getIntLE(offset + 25); + return obj; } - - if ((nullBits & 2) != 0) { - obj.orientation = Vector3f.deserialize(buf, offset + 14); - } - - obj.blockTypeId = buf.getIntLE(offset + 26); - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 30; + return 29; } public void serialize(@Nonnull ByteBuf buf) { - byte nullBits = 0; - if (this.position != null) { - nullBits = (byte)(nullBits | 1); - } - - if (this.orientation != null) { - nullBits = (byte)(nullBits | 2); - } - - buf.writeByte(nullBits); buf.writeByte(this.type.getValue()); - if (this.position != null) { - this.position.serialize(buf); - } else { - buf.writeZero(12); - } - - if (this.orientation != null) { - this.orientation.serialize(buf); - } else { - buf.writeZero(12); - } - + PacketIO.writeVector3f(buf, this.position); + PacketIO.writeVector3f(buf, this.orientation); buf.writeIntLE(this.blockTypeId); } public int computeSize() { - return 30; + return 29; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 30 ? ValidationResult.error("Buffer too small: expected at least 30 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 29) { + return ValidationResult.error("Buffer too small: expected at least 29 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 2 ? ValidationResult.error("Invalid BlockMountType value for Type") : ValidationResult.OK; + } } public BlockMount clone() { BlockMount copy = new BlockMount(); copy.type = this.type; - copy.position = this.position != null ? this.position.clone() : null; - copy.orientation = this.orientation != null ? this.orientation.clone() : null; + copy.position = this.position; + copy.orientation = this.orientation; copy.blockTypeId = this.blockTypeId; return copy; } diff --git a/src/com/hypixel/hytale/protocol/BlockMovementSettings.java b/src/com/hypixel/hytale/protocol/BlockMovementSettings.java index 68cae8b8..0d7341da 100644 --- a/src/com/hypixel/hytale/protocol/BlockMovementSettings.java +++ b/src/com/hypixel/hytale/protocol/BlockMovementSettings.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -72,20 +73,24 @@ public class BlockMovementSettings { @Nonnull public static BlockMovementSettings deserialize(@Nonnull ByteBuf buf, int offset) { - BlockMovementSettings obj = new BlockMovementSettings(); - obj.isClimbable = buf.getByte(offset + 0) != 0; - obj.climbUpSpeedMultiplier = buf.getFloatLE(offset + 1); - obj.climbDownSpeedMultiplier = buf.getFloatLE(offset + 5); - obj.climbLateralSpeedMultiplier = buf.getFloatLE(offset + 9); - obj.isBouncy = buf.getByte(offset + 13) != 0; - obj.bounceVelocity = buf.getFloatLE(offset + 14); - obj.drag = buf.getFloatLE(offset + 18); - obj.friction = buf.getFloatLE(offset + 22); - obj.terminalVelocityModifier = buf.getFloatLE(offset + 26); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 30); - obj.acceleration = buf.getFloatLE(offset + 34); - obj.jumpForceMultiplier = buf.getFloatLE(offset + 38); - return obj; + if (buf.readableBytes() - offset < 42) { + throw ProtocolException.bufferTooSmall("BlockMovementSettings", 42, buf.readableBytes() - offset); + } else { + BlockMovementSettings obj = new BlockMovementSettings(); + obj.isClimbable = buf.getByte(offset + 0) != 0; + obj.climbUpSpeedMultiplier = buf.getFloatLE(offset + 1); + obj.climbDownSpeedMultiplier = buf.getFloatLE(offset + 5); + obj.climbLateralSpeedMultiplier = buf.getFloatLE(offset + 9); + obj.isBouncy = buf.getByte(offset + 13) != 0; + obj.bounceVelocity = buf.getFloatLE(offset + 14); + obj.drag = buf.getFloatLE(offset + 18); + obj.friction = buf.getFloatLE(offset + 22); + obj.terminalVelocityModifier = buf.getFloatLE(offset + 26); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 30); + obj.acceleration = buf.getFloatLE(offset + 34); + obj.jumpForceMultiplier = buf.getFloatLE(offset + 38); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/BlockParticleSet.java b/src/com/hypixel/hytale/protocol/BlockParticleSet.java index 8e398f75..5bd9ba28 100644 --- a/src/com/hypixel/hytale/protocol/BlockParticleSet.java +++ b/src/com/hypixel/hytale/protocol/BlockParticleSet.java @@ -11,6 +11,7 @@ import java.util.Objects; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class BlockParticleSet { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -24,7 +25,7 @@ public class BlockParticleSet { public Color color; public float scale; @Nullable - public Vector3f positionOffset; + public Vector3fc positionOffset; @Nullable public Direction rotationOffset; @Nullable @@ -37,7 +38,7 @@ public class BlockParticleSet { @Nullable String id, @Nullable Color color, float scale, - @Nullable Vector3f positionOffset, + @Nullable Vector3fc positionOffset, @Nullable Direction rotationOffset, @Nullable Map particleSystemIds ) { @@ -60,71 +61,94 @@ public class BlockParticleSet { @Nonnull public static BlockParticleSet deserialize(@Nonnull ByteBuf buf, int offset) { - BlockParticleSet obj = new BlockParticleSet(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.color = Color.deserialize(buf, offset + 1); - } - - obj.scale = buf.getFloatLE(offset + 4); - if ((nullBits & 2) != 0) { - obj.positionOffset = Vector3f.deserialize(buf, offset + 8); - } - - if ((nullBits & 4) != 0) { - obj.rotationOffset = Direction.deserialize(buf, offset + 20); - } - - if ((nullBits & 8) != 0) { - int varPos0 = offset + 40 + buf.getIntLE(offset + 32); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 40) { + throw ProtocolException.bufferTooSmall("BlockParticleSet", 40, buf.readableBytes() - offset); + } else { + BlockParticleSet obj = new BlockParticleSet(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.color = Color.deserialize(buf, offset + 1); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + obj.scale = buf.getFloatLE(offset + 4); + if ((nullBits & 2) != 0) { + obj.positionOffset = PacketIO.readVector3f(buf, offset + 8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 16) != 0) { - int varPos1 = offset + 40 + buf.getIntLE(offset + 36); - int particleSystemIdsCount = VarInt.peek(buf, varPos1); - if (particleSystemIdsCount < 0) { - throw ProtocolException.negativeLength("ParticleSystemIds", particleSystemIdsCount); + if ((nullBits & 4) != 0) { + obj.rotationOffset = Direction.deserialize(buf, offset + 20); } - if (particleSystemIdsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ParticleSystemIds", particleSystemIdsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.particleSystemIds = new HashMap<>(particleSystemIdsCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < particleSystemIdsCount; i++) { - BlockParticleEvent key = BlockParticleEvent.fromValue(buf.getByte(dictPos)); - int valLen = VarInt.peek(buf, ++dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); + if ((nullBits & 8) != 0) { + int varPosBase0 = buf.getIntLE(offset + 32); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); } - if (valLen > 4096000) { - throw ProtocolException.stringTooLong("val", valLen, 4096000); + int varPos0 = offset + 40 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); } - int valVarLen = VarInt.length(buf, dictPos); - String val = PacketIO.readVarString(buf, dictPos); - dictPos += valVarLen + valLen; - if (obj.particleSystemIds.put(key, val) != null) { - throw ProtocolException.duplicateKey("particleSystemIds", key); + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase1 = buf.getIntLE(offset + 36); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("ParticleSystemIds", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 40 + varPosBase1; + int particleSystemIdsCount = VarInt.peek(buf, varPos1); + if (particleSystemIdsCount < 0) { + throw ProtocolException.invalidVarInt("ParticleSystemIds"); + } + + int varIntLen = VarInt.size(particleSystemIdsCount); + if (particleSystemIdsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ParticleSystemIds", particleSystemIdsCount, 4096000); + } + + obj.particleSystemIds = new HashMap<>(particleSystemIdsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < particleSystemIdsCount; i++) { + BlockParticleEvent key = BlockParticleEvent.fromValue(buf.getByte(dictPos)); + int valLen = VarInt.peek(buf, ++dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 4096000) { + throw ProtocolException.stringTooLong("val", valLen, 4096000); + } + + if (dictPos + valVarLen + valLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen, buf.readableBytes()); + } + + String val = PacketIO.readVarString(buf, dictPos); + dictPos += valVarLen + valLen; + if (obj.particleSystemIds.put(key, val) != null) { + throw ProtocolException.duplicateKey("particleSystemIds", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -132,9 +156,13 @@ public class BlockParticleSet { int maxEnd = 40; if ((nullBits & 8) != 0) { int fieldOffset0 = buf.getIntLE(offset + 32); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 40 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -142,13 +170,17 @@ public class BlockParticleSet { if ((nullBits & 16) != 0) { int fieldOffset1 = buf.getIntLE(offset + 36); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("ParticleSystemIds", fieldOffset1, maxEnd); + } + int pos1 = offset + 40 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, ++pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -191,7 +223,7 @@ public class BlockParticleSet { buf.writeFloatLE(this.scale); if (this.positionOffset != null) { - this.positionOffset.serialize(buf); + PacketIO.writeVector3f(buf, this.positionOffset); } else { buf.writeZero(12); } @@ -257,15 +289,11 @@ public class BlockParticleSet { byte nullBits = buffer.getByte(offset); if ((nullBits & 8) != 0) { int idOffset = buffer.getIntLE(offset + 32); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 40) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 40 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -275,7 +303,7 @@ public class BlockParticleSet { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -284,15 +312,11 @@ public class BlockParticleSet { if ((nullBits & 16) != 0) { int particleSystemIdsOffset = buffer.getIntLE(offset + 36); - if (particleSystemIdsOffset < 0) { + if (particleSystemIdsOffset < 0 || particleSystemIdsOffset > buffer.writerIndex() - offset - 40) { return ValidationResult.error("Invalid offset for ParticleSystemIds"); } int posx = offset + 40 + particleSystemIdsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ParticleSystemIds"); - } - int particleSystemIdsCount = VarInt.peek(buffer, posx); if (particleSystemIdsCount < 0) { return ValidationResult.error("Invalid dictionary count for ParticleSystemIds"); @@ -302,20 +326,25 @@ public class BlockParticleSet { return ValidationResult.error("ParticleSystemIds exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(particleSystemIdsCount); for (int i = 0; i < particleSystemIdsCount; i++) { - int valueLen = VarInt.peek(buffer, ++posx); - if (valueLen < 0) { + int v = buffer.getByte(posx) & 255; + if (v >= 10) { + return ValidationResult.error("Invalid BlockParticleEvent value for key"); + } + + v = VarInt.peek(buffer, ++posx); + if (v < 0) { return ValidationResult.error("Invalid string length for value"); } - if (valueLen > 4096000) { + if (v > 4096000) { return ValidationResult.error("value exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); - posx += valueLen; + posx += VarInt.size(v); + posx += v; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); } @@ -331,7 +360,7 @@ public class BlockParticleSet { copy.id = this.id; copy.color = this.color != null ? this.color.clone() : null; copy.scale = this.scale; - copy.positionOffset = this.positionOffset != null ? this.positionOffset.clone() : null; + copy.positionOffset = this.positionOffset; copy.rotationOffset = this.rotationOffset != null ? this.rotationOffset.clone() : null; copy.particleSystemIds = this.particleSystemIds != null ? new HashMap<>(this.particleSystemIds) : null; return copy; diff --git a/src/com/hypixel/hytale/protocol/BlockPlacementSettings.java b/src/com/hypixel/hytale/protocol/BlockPlacementSettings.java index f7084450..c51655e2 100644 --- a/src/com/hypixel/hytale/protocol/BlockPlacementSettings.java +++ b/src/com/hypixel/hytale/protocol/BlockPlacementSettings.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -58,16 +59,20 @@ public class BlockPlacementSettings { @Nonnull public static BlockPlacementSettings deserialize(@Nonnull ByteBuf buf, int offset) { - BlockPlacementSettings obj = new BlockPlacementSettings(); - obj.allowRotationKey = buf.getByte(offset + 0) != 0; - obj.placeInEmptyBlocks = buf.getByte(offset + 1) != 0; - obj.previewVisibility = BlockPreviewVisibility.fromValue(buf.getByte(offset + 2)); - obj.rotationMode = BlockPlacementRotationMode.fromValue(buf.getByte(offset + 3)); - obj.wallPlacementOverrideBlockId = buf.getIntLE(offset + 4); - obj.floorPlacementOverrideBlockId = buf.getIntLE(offset + 8); - obj.ceilingPlacementOverrideBlockId = buf.getIntLE(offset + 12); - obj.allowBreakReplace = buf.getByte(offset + 16) != 0; - return obj; + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("BlockPlacementSettings", 17, buf.readableBytes() - offset); + } else { + BlockPlacementSettings obj = new BlockPlacementSettings(); + obj.allowRotationKey = buf.getByte(offset + 0) != 0; + obj.placeInEmptyBlocks = buf.getByte(offset + 1) != 0; + obj.previewVisibility = BlockPreviewVisibility.fromValue(buf.getByte(offset + 2)); + obj.rotationMode = BlockPlacementRotationMode.fromValue(buf.getByte(offset + 3)); + obj.wallPlacementOverrideBlockId = buf.getIntLE(offset + 4); + obj.floorPlacementOverrideBlockId = buf.getIntLE(offset + 8); + obj.ceilingPlacementOverrideBlockId = buf.getIntLE(offset + 12); + obj.allowBreakReplace = buf.getByte(offset + 16) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,7 +95,17 @@ public class BlockPlacementSettings { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 17 ? ValidationResult.error("Buffer too small: expected at least 17 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); + } else { + int v = buffer.getByte(offset + 2) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid BlockPreviewVisibility value for PreviewVisibility"); + } else { + v = buffer.getByte(offset + 3) & 255; + return v >= 4 ? ValidationResult.error("Invalid BlockPlacementRotationMode value for RotationMode") : ValidationResult.OK; + } + } } public BlockPlacementSettings clone() { diff --git a/src/com/hypixel/hytale/protocol/BlockPosition.java b/src/com/hypixel/hytale/protocol/BlockPosition.java index bffa0bcf..f5cf147e 100644 --- a/src/com/hypixel/hytale/protocol/BlockPosition.java +++ b/src/com/hypixel/hytale/protocol/BlockPosition.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class BlockPosition { @Nonnull public static BlockPosition deserialize(@Nonnull ByteBuf buf, int offset) { - BlockPosition obj = new BlockPosition(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("BlockPosition", 12, buf.readableBytes() - offset); + } else { + BlockPosition obj = new BlockPosition(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/BlockRotation.java b/src/com/hypixel/hytale/protocol/BlockRotation.java index 71cbabcb..cf1db372 100644 --- a/src/com/hypixel/hytale/protocol/BlockRotation.java +++ b/src/com/hypixel/hytale/protocol/BlockRotation.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -35,11 +36,15 @@ public class BlockRotation { @Nonnull public static BlockRotation deserialize(@Nonnull ByteBuf buf, int offset) { - BlockRotation obj = new BlockRotation(); - obj.rotationYaw = Rotation.fromValue(buf.getByte(offset + 0)); - obj.rotationPitch = Rotation.fromValue(buf.getByte(offset + 1)); - obj.rotationRoll = Rotation.fromValue(buf.getByte(offset + 2)); - return obj; + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("BlockRotation", 3, buf.readableBytes() - offset); + } else { + BlockRotation obj = new BlockRotation(); + obj.rotationYaw = Rotation.fromValue(buf.getByte(offset + 0)); + obj.rotationPitch = Rotation.fromValue(buf.getByte(offset + 1)); + obj.rotationRoll = Rotation.fromValue(buf.getByte(offset + 2)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -57,7 +62,22 @@ public class BlockRotation { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 3 ? ValidationResult.error("Buffer too small: expected at least 3 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 3) { + return ValidationResult.error("Buffer too small: expected at least 3 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid Rotation value for RotationYaw"); + } else { + v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid Rotation value for RotationPitch"); + } else { + v = buffer.getByte(offset + 2) & 255; + return v >= 4 ? ValidationResult.error("Invalid Rotation value for RotationRoll") : ValidationResult.OK; + } + } + } } public BlockRotation clone() { diff --git a/src/com/hypixel/hytale/protocol/BlockSelectorToolData.java b/src/com/hypixel/hytale/protocol/BlockSelectorToolData.java index bae12a90..1425738d 100644 --- a/src/com/hypixel/hytale/protocol/BlockSelectorToolData.java +++ b/src/com/hypixel/hytale/protocol/BlockSelectorToolData.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class BlockSelectorToolData { @Nonnull public static BlockSelectorToolData deserialize(@Nonnull ByteBuf buf, int offset) { - BlockSelectorToolData obj = new BlockSelectorToolData(); - obj.durabilityLossOnUse = buf.getFloatLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("BlockSelectorToolData", 4, buf.readableBytes() - offset); + } else { + BlockSelectorToolData obj = new BlockSelectorToolData(); + obj.durabilityLossOnUse = buf.getFloatLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/BlockSet.java b/src/com/hypixel/hytale/protocol/BlockSet.java index c5ca7aae..4470753e 100644 --- a/src/com/hypixel/hytale/protocol/BlockSet.java +++ b/src/com/hypixel/hytale/protocol/BlockSet.java @@ -36,46 +36,65 @@ public class BlockSet { @Nonnull public static BlockSet deserialize(@Nonnull ByteBuf buf, int offset) { - BlockSet obj = new BlockSet(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("BlockSet", 9, buf.readableBytes() - offset); + } else { + BlockSet obj = new BlockSet(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Blocks", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int blocksCount = VarInt.peek(buf, varPos1); + if (blocksCount < 0) { + throw ProtocolException.invalidVarInt("Blocks"); + } + + int varIntLen = VarInt.size(blocksCount); + if (blocksCount > 4096000) { + throw ProtocolException.arrayTooLong("Blocks", blocksCount, 4096000); + } + + if (varPos1 + varIntLen + blocksCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Blocks", varPos1 + varIntLen + blocksCount * 4, buf.readableBytes()); + } + + obj.blocks = new int[blocksCount]; + + for (int i = 0; i < blocksCount; i++) { + obj.blocks[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); + } } - obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int blocksCount = VarInt.peek(buf, varPos1); - if (blocksCount < 0) { - throw ProtocolException.negativeLength("Blocks", blocksCount); - } - - if (blocksCount > 4096000) { - throw ProtocolException.arrayTooLong("Blocks", blocksCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + blocksCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Blocks", varPos1 + varIntLen + blocksCount * 4, buf.readableBytes()); - } - - obj.blocks = new int[blocksCount]; - - for (int i = 0; i < blocksCount; i++) { - obj.blocks[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -83,9 +102,13 @@ public class BlockSet { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -93,9 +116,13 @@ public class BlockSet { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Blocks", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 4; + pos1 += VarInt.size(arrLen) + arrLen * 4; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -164,15 +191,11 @@ public class BlockSet { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int nameOffset = buffer.getIntLE(offset + 1); - if (nameOffset < 0) { + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Name"); } int pos = offset + 9 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - int nameLen = VarInt.peek(buffer, pos); if (nameLen < 0) { return ValidationResult.error("Invalid string length for Name"); @@ -182,7 +205,7 @@ public class BlockSet { return ValidationResult.error("Name exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Name"); @@ -191,15 +214,11 @@ public class BlockSet { if ((nullBits & 2) != 0) { int blocksOffset = buffer.getIntLE(offset + 5); - if (blocksOffset < 0) { + if (blocksOffset < 0 || blocksOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Blocks"); } int posx = offset + 9 + blocksOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Blocks"); - } - int blocksCount = VarInt.peek(buffer, posx); if (blocksCount < 0) { return ValidationResult.error("Invalid array count for Blocks"); @@ -209,7 +228,7 @@ public class BlockSet { return ValidationResult.error("Blocks exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(blocksCount); posx += blocksCount * 4; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Blocks"); diff --git a/src/com/hypixel/hytale/protocol/BlockSoundSet.java b/src/com/hypixel/hytale/protocol/BlockSoundSet.java index 5c2425ea..dc21c6a7 100644 --- a/src/com/hypixel/hytale/protocol/BlockSoundSet.java +++ b/src/com/hypixel/hytale/protocol/BlockSoundSet.java @@ -42,52 +42,71 @@ public class BlockSoundSet { @Nonnull public static BlockSoundSet deserialize(@Nonnull ByteBuf buf, int offset) { - BlockSoundSet obj = new BlockSoundSet(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.moveInRepeatRange = FloatRange.deserialize(buf, offset + 1); - } - - if ((nullBits & 2) != 0) { - int varPos0 = offset + 17 + buf.getIntLE(offset + 9); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("BlockSoundSet", 17, buf.readableBytes() - offset); + } else { + BlockSoundSet obj = new BlockSoundSet(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.moveInRepeatRange = FloatRange.deserialize(buf, offset + 1); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 13); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("SoundEventIndices", varPosBase1, buf.readableBytes()); + } - if ((nullBits & 4) != 0) { - int varPos1 = offset + 17 + buf.getIntLE(offset + 13); - int soundEventIndicesCount = VarInt.peek(buf, varPos1); - if (soundEventIndicesCount < 0) { - throw ProtocolException.negativeLength("SoundEventIndices", soundEventIndicesCount); - } + int varPos1 = offset + 17 + varPosBase1; + int soundEventIndicesCount = VarInt.peek(buf, varPos1); + if (soundEventIndicesCount < 0) { + throw ProtocolException.invalidVarInt("SoundEventIndices"); + } - if (soundEventIndicesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SoundEventIndices", soundEventIndicesCount, 4096000); - } + int varIntLen = VarInt.size(soundEventIndicesCount); + if (soundEventIndicesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SoundEventIndices", soundEventIndicesCount, 4096000); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.soundEventIndices = new HashMap<>(soundEventIndicesCount); - int dictPos = varPos1 + varIntLen; + obj.soundEventIndices = new HashMap<>(soundEventIndicesCount); + int dictPos = varPos1 + varIntLen; - for (int i = 0; i < soundEventIndicesCount; i++) { - BlockSoundEvent key = BlockSoundEvent.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.soundEventIndices.put(key, val) != null) { - throw ProtocolException.duplicateKey("soundEventIndices", key); + for (int i = 0; i < soundEventIndicesCount; i++) { + BlockSoundEvent key = BlockSoundEvent.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.soundEventIndices.put(key, val) != null) { + throw ProtocolException.duplicateKey("soundEventIndices", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -95,9 +114,13 @@ public class BlockSoundSet { int maxEnd = 17; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -105,9 +128,13 @@ public class BlockSoundSet { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 13); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("SoundEventIndices", fieldOffset1, maxEnd); + } + int pos1 = offset + 17 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + 4; @@ -192,15 +219,11 @@ public class BlockSoundSet { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { int idOffset = buffer.getIntLE(offset + 9); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 17 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -210,7 +233,7 @@ public class BlockSoundSet { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -219,15 +242,11 @@ public class BlockSoundSet { if ((nullBits & 4) != 0) { int soundEventIndicesOffset = buffer.getIntLE(offset + 13); - if (soundEventIndicesOffset < 0) { + if (soundEventIndicesOffset < 0 || soundEventIndicesOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for SoundEventIndices"); } int posx = offset + 17 + soundEventIndicesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SoundEventIndices"); - } - int soundEventIndicesCount = VarInt.peek(buffer, posx); if (soundEventIndicesCount < 0) { return ValidationResult.error("Invalid dictionary count for SoundEventIndices"); @@ -237,9 +256,14 @@ public class BlockSoundSet { return ValidationResult.error("SoundEventIndices exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(soundEventIndicesCount); for (int i = 0; i < soundEventIndicesCount; i++) { + int v = buffer.getByte(posx) & 255; + if (v >= 9) { + return ValidationResult.error("Invalid BlockSoundEvent value for key"); + } + posx = ++posx + 4; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); diff --git a/src/com/hypixel/hytale/protocol/BlockTextures.java b/src/com/hypixel/hytale/protocol/BlockTextures.java index 023838d3..6f334117 100644 --- a/src/com/hypixel/hytale/protocol/BlockTextures.java +++ b/src/com/hypixel/hytale/protocol/BlockTextures.java @@ -56,94 +56,158 @@ public class BlockTextures { @Nonnull public static BlockTextures deserialize(@Nonnull ByteBuf buf, int offset) { - BlockTextures obj = new BlockTextures(); - byte nullBits = buf.getByte(offset); - obj.weight = buf.getFloatLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 29 + buf.getIntLE(offset + 5); - int topLen = VarInt.peek(buf, varPos0); - if (topLen < 0) { - throw ProtocolException.negativeLength("Top", topLen); + if (buf.readableBytes() - offset < 29) { + throw ProtocolException.bufferTooSmall("BlockTextures", 29, buf.readableBytes() - offset); + } else { + BlockTextures obj = new BlockTextures(); + byte nullBits = buf.getByte(offset); + obj.weight = buf.getFloatLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Top", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 29 + varPosBase0; + int topLen = VarInt.peek(buf, varPos0); + if (topLen < 0) { + throw ProtocolException.invalidVarInt("Top"); + } + + int topVarIntLen = VarInt.size(topLen); + if (topLen > 4096000) { + throw ProtocolException.stringTooLong("Top", topLen, 4096000); + } + + if (varPos0 + topVarIntLen + topLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Top", varPos0 + topVarIntLen + topLen, buf.readableBytes()); + } + + obj.top = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (topLen > 4096000) { - throw ProtocolException.stringTooLong("Top", topLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Bottom", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 29 + varPosBase1; + int bottomLen = VarInt.peek(buf, varPos1); + if (bottomLen < 0) { + throw ProtocolException.invalidVarInt("Bottom"); + } + + int bottomVarIntLen = VarInt.size(bottomLen); + if (bottomLen > 4096000) { + throw ProtocolException.stringTooLong("Bottom", bottomLen, 4096000); + } + + if (varPos1 + bottomVarIntLen + bottomLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Bottom", varPos1 + bottomVarIntLen + bottomLen, buf.readableBytes()); + } + + obj.bottom = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.top = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 13); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Front", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 29 + varPosBase2; + int frontLen = VarInt.peek(buf, varPos2); + if (frontLen < 0) { + throw ProtocolException.invalidVarInt("Front"); + } + + int frontVarIntLen = VarInt.size(frontLen); + if (frontLen > 4096000) { + throw ProtocolException.stringTooLong("Front", frontLen, 4096000); + } + + if (varPos2 + frontVarIntLen + frontLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Front", varPos2 + frontVarIntLen + frontLen, buf.readableBytes()); + } + + obj.front = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 17); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Back", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 29 + varPosBase3; + int backLen = VarInt.peek(buf, varPos3); + if (backLen < 0) { + throw ProtocolException.invalidVarInt("Back"); + } + + int backVarIntLen = VarInt.size(backLen); + if (backLen > 4096000) { + throw ProtocolException.stringTooLong("Back", backLen, 4096000); + } + + if (varPos3 + backVarIntLen + backLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Back", varPos3 + backVarIntLen + backLen, buf.readableBytes()); + } + + obj.back = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 21); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Left", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 29 + varPosBase4; + int leftLen = VarInt.peek(buf, varPos4); + if (leftLen < 0) { + throw ProtocolException.invalidVarInt("Left"); + } + + int leftVarIntLen = VarInt.size(leftLen); + if (leftLen > 4096000) { + throw ProtocolException.stringTooLong("Left", leftLen, 4096000); + } + + if (varPos4 + leftVarIntLen + leftLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Left", varPos4 + leftVarIntLen + leftLen, buf.readableBytes()); + } + + obj.left = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 25); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Right", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 29 + varPosBase5; + int rightLen = VarInt.peek(buf, varPos5); + if (rightLen < 0) { + throw ProtocolException.invalidVarInt("Right"); + } + + int rightVarIntLen = VarInt.size(rightLen); + if (rightLen > 4096000) { + throw ProtocolException.stringTooLong("Right", rightLen, 4096000); + } + + if (varPos5 + rightVarIntLen + rightLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Right", varPos5 + rightVarIntLen + rightLen, buf.readableBytes()); + } + + obj.right = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 29 + buf.getIntLE(offset + 9); - int bottomLen = VarInt.peek(buf, varPos1); - if (bottomLen < 0) { - throw ProtocolException.negativeLength("Bottom", bottomLen); - } - - if (bottomLen > 4096000) { - throw ProtocolException.stringTooLong("Bottom", bottomLen, 4096000); - } - - obj.bottom = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 29 + buf.getIntLE(offset + 13); - int frontLen = VarInt.peek(buf, varPos2); - if (frontLen < 0) { - throw ProtocolException.negativeLength("Front", frontLen); - } - - if (frontLen > 4096000) { - throw ProtocolException.stringTooLong("Front", frontLen, 4096000); - } - - obj.front = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 29 + buf.getIntLE(offset + 17); - int backLen = VarInt.peek(buf, varPos3); - if (backLen < 0) { - throw ProtocolException.negativeLength("Back", backLen); - } - - if (backLen > 4096000) { - throw ProtocolException.stringTooLong("Back", backLen, 4096000); - } - - obj.back = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 29 + buf.getIntLE(offset + 21); - int leftLen = VarInt.peek(buf, varPos4); - if (leftLen < 0) { - throw ProtocolException.negativeLength("Left", leftLen); - } - - if (leftLen > 4096000) { - throw ProtocolException.stringTooLong("Left", leftLen, 4096000); - } - - obj.left = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 29 + buf.getIntLE(offset + 25); - int rightLen = VarInt.peek(buf, varPos5); - if (rightLen < 0) { - throw ProtocolException.negativeLength("Right", rightLen); - } - - if (rightLen > 4096000) { - throw ProtocolException.stringTooLong("Right", rightLen, 4096000); - } - - obj.right = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -151,9 +215,13 @@ public class BlockTextures { int maxEnd = 29; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Top", fieldOffset0, maxEnd); + } + int pos0 = offset + 29 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -161,9 +229,13 @@ public class BlockTextures { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Bottom", fieldOffset1, maxEnd); + } + int pos1 = offset + 29 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -171,9 +243,13 @@ public class BlockTextures { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 13); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Front", fieldOffset2, maxEnd); + } + int pos2 = offset + 29 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -181,9 +257,13 @@ public class BlockTextures { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 17); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Back", fieldOffset3, maxEnd); + } + int pos3 = offset + 29 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -191,9 +271,13 @@ public class BlockTextures { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 21); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Left", fieldOffset4, maxEnd); + } + int pos4 = offset + 29 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -201,9 +285,13 @@ public class BlockTextures { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 25); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Right", fieldOffset5, maxEnd); + } + int pos5 = offset + 29 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -333,15 +421,11 @@ public class BlockTextures { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int topOffset = buffer.getIntLE(offset + 5); - if (topOffset < 0) { + if (topOffset < 0 || topOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Top"); } int pos = offset + 29 + topOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Top"); - } - int topLen = VarInt.peek(buffer, pos); if (topLen < 0) { return ValidationResult.error("Invalid string length for Top"); @@ -351,7 +435,7 @@ public class BlockTextures { return ValidationResult.error("Top exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(topLen); pos += topLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Top"); @@ -360,15 +444,11 @@ public class BlockTextures { if ((nullBits & 2) != 0) { int bottomOffset = buffer.getIntLE(offset + 9); - if (bottomOffset < 0) { + if (bottomOffset < 0 || bottomOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Bottom"); } int posx = offset + 29 + bottomOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Bottom"); - } - int bottomLen = VarInt.peek(buffer, posx); if (bottomLen < 0) { return ValidationResult.error("Invalid string length for Bottom"); @@ -378,7 +458,7 @@ public class BlockTextures { return ValidationResult.error("Bottom exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(bottomLen); posx += bottomLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Bottom"); @@ -387,15 +467,11 @@ public class BlockTextures { if ((nullBits & 4) != 0) { int frontOffset = buffer.getIntLE(offset + 13); - if (frontOffset < 0) { + if (frontOffset < 0 || frontOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Front"); } int posxx = offset + 29 + frontOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Front"); - } - int frontLen = VarInt.peek(buffer, posxx); if (frontLen < 0) { return ValidationResult.error("Invalid string length for Front"); @@ -405,7 +481,7 @@ public class BlockTextures { return ValidationResult.error("Front exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(frontLen); posxx += frontLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Front"); @@ -414,15 +490,11 @@ public class BlockTextures { if ((nullBits & 8) != 0) { int backOffset = buffer.getIntLE(offset + 17); - if (backOffset < 0) { + if (backOffset < 0 || backOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Back"); } int posxxx = offset + 29 + backOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Back"); - } - int backLen = VarInt.peek(buffer, posxxx); if (backLen < 0) { return ValidationResult.error("Invalid string length for Back"); @@ -432,7 +504,7 @@ public class BlockTextures { return ValidationResult.error("Back exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(backLen); posxxx += backLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Back"); @@ -441,15 +513,11 @@ public class BlockTextures { if ((nullBits & 16) != 0) { int leftOffset = buffer.getIntLE(offset + 21); - if (leftOffset < 0) { + if (leftOffset < 0 || leftOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Left"); } int posxxxx = offset + 29 + leftOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Left"); - } - int leftLen = VarInt.peek(buffer, posxxxx); if (leftLen < 0) { return ValidationResult.error("Invalid string length for Left"); @@ -459,7 +527,7 @@ public class BlockTextures { return ValidationResult.error("Left exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(leftLen); posxxxx += leftLen; if (posxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Left"); @@ -468,15 +536,11 @@ public class BlockTextures { if ((nullBits & 32) != 0) { int rightOffset = buffer.getIntLE(offset + 25); - if (rightOffset < 0) { + if (rightOffset < 0 || rightOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Right"); } int posxxxxx = offset + 29 + rightOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Right"); - } - int rightLen = VarInt.peek(buffer, posxxxxx); if (rightLen < 0) { return ValidationResult.error("Invalid string length for Right"); @@ -486,7 +550,7 @@ public class BlockTextures { return ValidationResult.error("Right exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); + posxxxxx += VarInt.size(rightLen); posxxxxx += rightLen; if (posxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Right"); diff --git a/src/com/hypixel/hytale/protocol/BlockType.java b/src/com/hypixel/hytale/protocol/BlockType.java index 70e3aaa3..720c4e83 100644 --- a/src/com/hypixel/hytale/protocol/BlockType.java +++ b/src/com/hypixel/hytale/protocol/BlockType.java @@ -15,9 +15,9 @@ import javax.annotation.Nullable; public class BlockType { public static final int NULLABLE_BIT_FIELD_SIZE = 4; - public static final int FIXED_BLOCK_SIZE = 164; - public static final int VARIABLE_FIELD_COUNT = 24; - public static final int VARIABLE_BLOCK_START = 260; + public static final int FIXED_BLOCK_SIZE = 168; + public static final int VARIABLE_FIELD_COUNT = 25; + public static final int VARIABLE_BLOCK_START = 268; public static final int MAX_SIZE = 1677721600; @Nullable public String item; @@ -63,8 +63,11 @@ public class BlockType { @Nonnull public Rotation rotationYawPlacementOffset = Rotation.None; public int blockSoundSetIndex; + public int physicalMaterialIndex; public int ambientSoundEventIndex; @Nullable + public ConditionalBlockSound[] conditionalSounds; + @Nullable public ModelParticle[] particles; @Nullable public String blockParticleSetId; @@ -140,7 +143,9 @@ public class BlockType { @Nonnull VariantRotation variantRotation, @Nonnull Rotation rotationYawPlacementOffset, int blockSoundSetIndex, + int physicalMaterialIndex, int ambientSoundEventIndex, + @Nullable ConditionalBlockSound[] conditionalSounds, @Nullable ModelParticle[] particles, @Nullable String blockParticleSetId, @Nullable String blockBreakingDecalId, @@ -192,7 +197,9 @@ public class BlockType { this.variantRotation = variantRotation; this.rotationYawPlacementOffset = rotationYawPlacementOffset; this.blockSoundSetIndex = blockSoundSetIndex; + this.physicalMaterialIndex = physicalMaterialIndex; this.ambientSoundEventIndex = ambientSoundEventIndex; + this.conditionalSounds = conditionalSounds; this.particles = particles; this.blockParticleSetId = blockParticleSetId; this.blockBreakingDecalId = blockBreakingDecalId; @@ -246,7 +253,9 @@ public class BlockType { this.variantRotation = other.variantRotation; this.rotationYawPlacementOffset = other.rotationYawPlacementOffset; this.blockSoundSetIndex = other.blockSoundSetIndex; + this.physicalMaterialIndex = other.physicalMaterialIndex; this.ambientSoundEventIndex = other.ambientSoundEventIndex; + this.conditionalSounds = other.conditionalSounds; this.particles = other.particles; this.blockParticleSetId = other.blockParticleSetId; this.blockBreakingDecalId = other.blockBreakingDecalId; @@ -275,555 +284,783 @@ public class BlockType { @Nonnull public static BlockType deserialize(@Nonnull ByteBuf buf, int offset) { - BlockType obj = new BlockType(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 4); - obj.unknown = buf.getByte(offset + 4) != 0; - obj.drawType = DrawType.fromValue(buf.getByte(offset + 5)); - obj.material = BlockMaterial.fromValue(buf.getByte(offset + 6)); - obj.opacity = Opacity.fromValue(buf.getByte(offset + 7)); - obj.hitbox = buf.getIntLE(offset + 8); - obj.interactionHitbox = buf.getIntLE(offset + 12); - obj.modelScale = buf.getFloatLE(offset + 16); - obj.looping = buf.getByte(offset + 20) != 0; - obj.maxSupportDistance = buf.getIntLE(offset + 21); - obj.blockSupportsRequiredFor = BlockSupportsRequiredForType.fromValue(buf.getByte(offset + 25)); - obj.requiresAlphaBlending = buf.getByte(offset + 26) != 0; - obj.cubeShadingMode = ShadingMode.fromValue(buf.getByte(offset + 27)); - obj.randomRotation = RandomRotation.fromValue(buf.getByte(offset + 28)); - obj.variantRotation = VariantRotation.fromValue(buf.getByte(offset + 29)); - obj.rotationYawPlacementOffset = Rotation.fromValue(buf.getByte(offset + 30)); - obj.blockSoundSetIndex = buf.getIntLE(offset + 31); - obj.ambientSoundEventIndex = buf.getIntLE(offset + 35); - if ((nullBits[0] & 1) != 0) { - obj.particleColor = Color.deserialize(buf, offset + 39); - } - - if ((nullBits[0] & 2) != 0) { - obj.light = ColorLight.deserialize(buf, offset + 42); - } - - if ((nullBits[0] & 4) != 0) { - obj.tint = Tint.deserialize(buf, offset + 46); - } - - if ((nullBits[0] & 8) != 0) { - obj.biomeTint = Tint.deserialize(buf, offset + 70); - } - - obj.group = buf.getIntLE(offset + 94); - if ((nullBits[0] & 16) != 0) { - obj.movementSettings = BlockMovementSettings.deserialize(buf, offset + 98); - } - - if ((nullBits[0] & 32) != 0) { - obj.flags = BlockFlags.deserialize(buf, offset + 140); - } - - if ((nullBits[0] & 64) != 0) { - obj.placementSettings = BlockPlacementSettings.deserialize(buf, offset + 142); - } - - obj.ignoreSupportWhenPlaced = buf.getByte(offset + 159) != 0; - obj.transitionToTag = buf.getIntLE(offset + 160); - if ((nullBits[0] & 128) != 0) { - int varPos0 = offset + 260 + buf.getIntLE(offset + 164); - int itemLen = VarInt.peek(buf, varPos0); - if (itemLen < 0) { - throw ProtocolException.negativeLength("Item", itemLen); + if (buf.readableBytes() - offset < 268) { + throw ProtocolException.bufferTooSmall("BlockType", 268, buf.readableBytes() - offset); + } else { + BlockType obj = new BlockType(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 4); + obj.unknown = buf.getByte(offset + 4) != 0; + obj.drawType = DrawType.fromValue(buf.getByte(offset + 5)); + obj.material = BlockMaterial.fromValue(buf.getByte(offset + 6)); + obj.opacity = Opacity.fromValue(buf.getByte(offset + 7)); + obj.hitbox = buf.getIntLE(offset + 8); + obj.interactionHitbox = buf.getIntLE(offset + 12); + obj.modelScale = buf.getFloatLE(offset + 16); + obj.looping = buf.getByte(offset + 20) != 0; + obj.maxSupportDistance = buf.getIntLE(offset + 21); + obj.blockSupportsRequiredFor = BlockSupportsRequiredForType.fromValue(buf.getByte(offset + 25)); + obj.requiresAlphaBlending = buf.getByte(offset + 26) != 0; + obj.cubeShadingMode = ShadingMode.fromValue(buf.getByte(offset + 27)); + obj.randomRotation = RandomRotation.fromValue(buf.getByte(offset + 28)); + obj.variantRotation = VariantRotation.fromValue(buf.getByte(offset + 29)); + obj.rotationYawPlacementOffset = Rotation.fromValue(buf.getByte(offset + 30)); + obj.blockSoundSetIndex = buf.getIntLE(offset + 31); + obj.physicalMaterialIndex = buf.getIntLE(offset + 35); + obj.ambientSoundEventIndex = buf.getIntLE(offset + 39); + if ((nullBits[0] & 1) != 0) { + obj.particleColor = Color.deserialize(buf, offset + 43); } - if (itemLen > 4096000) { - throw ProtocolException.stringTooLong("Item", itemLen, 4096000); + if ((nullBits[0] & 2) != 0) { + obj.light = ColorLight.deserialize(buf, offset + 46); } - obj.item = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits[1] & 1) != 0) { - int varPos1 = offset + 260 + buf.getIntLE(offset + 168); - int nameLen = VarInt.peek(buf, varPos1); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); + if ((nullBits[0] & 4) != 0) { + obj.tint = Tint.deserialize(buf, offset + 50); } - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + if ((nullBits[0] & 8) != 0) { + obj.biomeTint = Tint.deserialize(buf, offset + 74); } - obj.name = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits[1] & 2) != 0) { - int varPos2 = offset + 260 + buf.getIntLE(offset + 172); - int shaderEffectCount = VarInt.peek(buf, varPos2); - if (shaderEffectCount < 0) { - throw ProtocolException.negativeLength("ShaderEffect", shaderEffectCount); + obj.group = buf.getIntLE(offset + 98); + if ((nullBits[0] & 16) != 0) { + obj.movementSettings = BlockMovementSettings.deserialize(buf, offset + 102); } - if (shaderEffectCount > 4096000) { - throw ProtocolException.arrayTooLong("ShaderEffect", shaderEffectCount, 4096000); + if ((nullBits[0] & 32) != 0) { + obj.flags = BlockFlags.deserialize(buf, offset + 144); } - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + shaderEffectCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ShaderEffect", varPos2 + varIntLen + shaderEffectCount * 1, buf.readableBytes()); + if ((nullBits[0] & 64) != 0) { + obj.placementSettings = BlockPlacementSettings.deserialize(buf, offset + 146); } - obj.shaderEffect = new ShaderType[shaderEffectCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < shaderEffectCount; i++) { - obj.shaderEffect[i] = ShaderType.fromValue(buf.getByte(elemPos)); - elemPos++; - } - } - - if ((nullBits[1] & 4) != 0) { - int varPos3 = offset + 260 + buf.getIntLE(offset + 176); - int modelLen = VarInt.peek(buf, varPos3); - if (modelLen < 0) { - throw ProtocolException.negativeLength("Model", modelLen); - } - - if (modelLen > 4096000) { - throw ProtocolException.stringTooLong("Model", modelLen, 4096000); - } - - obj.model = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits[1] & 8) != 0) { - int varPos4 = offset + 260 + buf.getIntLE(offset + 180); - int modelTextureCount = VarInt.peek(buf, varPos4); - if (modelTextureCount < 0) { - throw ProtocolException.negativeLength("ModelTexture", modelTextureCount); - } - - if (modelTextureCount > 4096000) { - throw ProtocolException.arrayTooLong("ModelTexture", modelTextureCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos4); - if (varPos4 + varIntLen + modelTextureCount * 5L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ModelTexture", varPos4 + varIntLen + modelTextureCount * 5, buf.readableBytes()); - } - - obj.modelTexture = new ModelTexture[modelTextureCount]; - int elemPos = varPos4 + varIntLen; - - for (int i = 0; i < modelTextureCount; i++) { - obj.modelTexture[i] = ModelTexture.deserialize(buf, elemPos); - elemPos += ModelTexture.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[1] & 16) != 0) { - int varPos5 = offset + 260 + buf.getIntLE(offset + 184); - int modelAnimationLen = VarInt.peek(buf, varPos5); - if (modelAnimationLen < 0) { - throw ProtocolException.negativeLength("ModelAnimation", modelAnimationLen); - } - - if (modelAnimationLen > 4096000) { - throw ProtocolException.stringTooLong("ModelAnimation", modelAnimationLen, 4096000); - } - - obj.modelAnimation = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - if ((nullBits[1] & 32) != 0) { - int varPos6 = offset + 260 + buf.getIntLE(offset + 188); - int supportCount = VarInt.peek(buf, varPos6); - if (supportCount < 0) { - throw ProtocolException.negativeLength("Support", supportCount); - } - - if (supportCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Support", supportCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos6); - obj.support = new HashMap<>(supportCount); - int dictPos = varPos6 + varIntLen; - - for (int i = 0; i < supportCount; i++) { - BlockNeighbor key = BlockNeighbor.fromValue(buf.getByte(dictPos)); - int valLen = VarInt.peek(buf, ++dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); + obj.ignoreSupportWhenPlaced = buf.getByte(offset + 163) != 0; + obj.transitionToTag = buf.getIntLE(offset + 164); + if ((nullBits[0] & 128) != 0) { + int varPosBase0 = buf.getIntLE(offset + 168); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Item", varPosBase0, buf.readableBytes()); } - if (valLen > 64) { - throw ProtocolException.arrayTooLong("val", valLen, 64); + int varPos0 = offset + 268 + varPosBase0; + int itemLen = VarInt.peek(buf, varPos0); + if (itemLen < 0) { + throw ProtocolException.invalidVarInt("Item"); } - int valVarLen = VarInt.length(buf, dictPos); - if (dictPos + valVarLen + valLen * 17L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 17, buf.readableBytes()); + int itemVarIntLen = VarInt.size(itemLen); + if (itemLen > 4096000) { + throw ProtocolException.stringTooLong("Item", itemLen, 4096000); } - dictPos += valVarLen; - RequiredBlockFaceSupport[] val = new RequiredBlockFaceSupport[valLen]; - - for (int valIdx = 0; valIdx < valLen; valIdx++) { - val[valIdx] = RequiredBlockFaceSupport.deserialize(buf, dictPos); - dictPos += RequiredBlockFaceSupport.computeBytesConsumed(buf, dictPos); + if (varPos0 + itemVarIntLen + itemLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Item", varPos0 + itemVarIntLen + itemLen, buf.readableBytes()); } - if (obj.support.put(key, val) != null) { - throw ProtocolException.duplicateKey("support", key); - } - } - } - - if ((nullBits[1] & 64) != 0) { - int varPos7 = offset + 260 + buf.getIntLE(offset + 192); - int supportingCount = VarInt.peek(buf, varPos7); - if (supportingCount < 0) { - throw ProtocolException.negativeLength("Supporting", supportingCount); + obj.item = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (supportingCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Supporting", supportingCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos7); - obj.supporting = new HashMap<>(supportingCount); - int dictPos = varPos7 + varIntLen; - - for (int i = 0; i < supportingCount; i++) { - BlockNeighbor keyx = BlockNeighbor.fromValue(buf.getByte(dictPos)); - int valLenx = VarInt.peek(buf, ++dictPos); - if (valLenx < 0) { - throw ProtocolException.negativeLength("val", valLenx); + if ((nullBits[1] & 1) != 0) { + int varPosBase1 = buf.getIntLE(offset + 172); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Name", varPosBase1, buf.readableBytes()); } - if (valLenx > 64) { - throw ProtocolException.arrayTooLong("val", valLenx, 64); + int varPos1 = offset + 268 + varPosBase1; + int nameLen = VarInt.peek(buf, varPos1); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); } - int valVarLenx = VarInt.length(buf, dictPos); - if (dictPos + valVarLenx + valLenx * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLenx + valLenx * 1, buf.readableBytes()); + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); } - dictPos += valVarLenx; - BlockFaceSupport[] val = new BlockFaceSupport[valLenx]; - - for (int valIdx = 0; valIdx < valLenx; valIdx++) { - val[valIdx] = BlockFaceSupport.deserialize(buf, dictPos); - dictPos += BlockFaceSupport.computeBytesConsumed(buf, dictPos); + if (varPos1 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos1 + nameVarIntLen + nameLen, buf.readableBytes()); } - if (obj.supporting.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("supporting", keyx); - } - } - } - - if ((nullBits[1] & 128) != 0) { - int varPos8 = offset + 260 + buf.getIntLE(offset + 196); - int cubeTexturesCount = VarInt.peek(buf, varPos8); - if (cubeTexturesCount < 0) { - throw ProtocolException.negativeLength("CubeTextures", cubeTexturesCount); + obj.name = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - if (cubeTexturesCount > 4096000) { - throw ProtocolException.arrayTooLong("CubeTextures", cubeTexturesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos8); - if (varPos8 + varIntLen + cubeTexturesCount * 5L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("CubeTextures", varPos8 + varIntLen + cubeTexturesCount * 5, buf.readableBytes()); - } - - obj.cubeTextures = new BlockTextures[cubeTexturesCount]; - int elemPos = varPos8 + varIntLen; - - for (int i = 0; i < cubeTexturesCount; i++) { - obj.cubeTextures[i] = BlockTextures.deserialize(buf, elemPos); - elemPos += BlockTextures.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[2] & 1) != 0) { - int varPos9 = offset + 260 + buf.getIntLE(offset + 200); - int cubeSideMaskTextureLen = VarInt.peek(buf, varPos9); - if (cubeSideMaskTextureLen < 0) { - throw ProtocolException.negativeLength("CubeSideMaskTexture", cubeSideMaskTextureLen); - } - - if (cubeSideMaskTextureLen > 4096000) { - throw ProtocolException.stringTooLong("CubeSideMaskTexture", cubeSideMaskTextureLen, 4096000); - } - - obj.cubeSideMaskTexture = PacketIO.readVarString(buf, varPos9, PacketIO.UTF8); - } - - if ((nullBits[2] & 2) != 0) { - int varPos10 = offset + 260 + buf.getIntLE(offset + 204); - int particlesCount = VarInt.peek(buf, varPos10); - if (particlesCount < 0) { - throw ProtocolException.negativeLength("Particles", particlesCount); - } - - if (particlesCount > 4096000) { - throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos10); - if (varPos10 + varIntLen + particlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Particles", varPos10 + varIntLen + particlesCount * 34, buf.readableBytes()); - } - - obj.particles = new ModelParticle[particlesCount]; - int elemPos = varPos10 + varIntLen; - - for (int i = 0; i < particlesCount; i++) { - obj.particles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[2] & 4) != 0) { - int varPos11 = offset + 260 + buf.getIntLE(offset + 208); - int blockParticleSetIdLen = VarInt.peek(buf, varPos11); - if (blockParticleSetIdLen < 0) { - throw ProtocolException.negativeLength("BlockParticleSetId", blockParticleSetIdLen); - } - - if (blockParticleSetIdLen > 4096000) { - throw ProtocolException.stringTooLong("BlockParticleSetId", blockParticleSetIdLen, 4096000); - } - - obj.blockParticleSetId = PacketIO.readVarString(buf, varPos11, PacketIO.UTF8); - } - - if ((nullBits[2] & 8) != 0) { - int varPos12 = offset + 260 + buf.getIntLE(offset + 212); - int blockBreakingDecalIdLen = VarInt.peek(buf, varPos12); - if (blockBreakingDecalIdLen < 0) { - throw ProtocolException.negativeLength("BlockBreakingDecalId", blockBreakingDecalIdLen); - } - - if (blockBreakingDecalIdLen > 4096000) { - throw ProtocolException.stringTooLong("BlockBreakingDecalId", blockBreakingDecalIdLen, 4096000); - } - - obj.blockBreakingDecalId = PacketIO.readVarString(buf, varPos12, PacketIO.UTF8); - } - - if ((nullBits[2] & 16) != 0) { - int varPos13 = offset + 260 + buf.getIntLE(offset + 216); - int transitionTextureLen = VarInt.peek(buf, varPos13); - if (transitionTextureLen < 0) { - throw ProtocolException.negativeLength("TransitionTexture", transitionTextureLen); - } - - if (transitionTextureLen > 4096000) { - throw ProtocolException.stringTooLong("TransitionTexture", transitionTextureLen, 4096000); - } - - obj.transitionTexture = PacketIO.readVarString(buf, varPos13, PacketIO.UTF8); - } - - if ((nullBits[2] & 32) != 0) { - int varPos14 = offset + 260 + buf.getIntLE(offset + 220); - int transitionToGroupsCount = VarInt.peek(buf, varPos14); - if (transitionToGroupsCount < 0) { - throw ProtocolException.negativeLength("TransitionToGroups", transitionToGroupsCount); - } - - if (transitionToGroupsCount > 4096000) { - throw ProtocolException.arrayTooLong("TransitionToGroups", transitionToGroupsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos14); - if (varPos14 + varIntLen + transitionToGroupsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("TransitionToGroups", varPos14 + varIntLen + transitionToGroupsCount * 4, buf.readableBytes()); - } - - obj.transitionToGroups = new int[transitionToGroupsCount]; - - for (int i = 0; i < transitionToGroupsCount; i++) { - obj.transitionToGroups[i] = buf.getIntLE(varPos14 + varIntLen + i * 4); - } - } - - if ((nullBits[2] & 64) != 0) { - int varPos15 = offset + 260 + buf.getIntLE(offset + 224); - int interactionHintLen = VarInt.peek(buf, varPos15); - if (interactionHintLen < 0) { - throw ProtocolException.negativeLength("InteractionHint", interactionHintLen); - } - - if (interactionHintLen > 4096000) { - throw ProtocolException.stringTooLong("InteractionHint", interactionHintLen, 4096000); - } - - obj.interactionHint = PacketIO.readVarString(buf, varPos15, PacketIO.UTF8); - } - - if ((nullBits[2] & 128) != 0) { - int varPos16 = offset + 260 + buf.getIntLE(offset + 228); - obj.gathering = BlockGathering.deserialize(buf, varPos16); - } - - if ((nullBits[3] & 1) != 0) { - int varPos17 = offset + 260 + buf.getIntLE(offset + 232); - obj.display = ModelDisplay.deserialize(buf, varPos17); - } - - if ((nullBits[3] & 2) != 0) { - int varPos18 = offset + 260 + buf.getIntLE(offset + 236); - obj.rail = RailConfig.deserialize(buf, varPos18); - } - - if ((nullBits[3] & 4) != 0) { - int varPos19 = offset + 260 + buf.getIntLE(offset + 240); - int interactionsCount = VarInt.peek(buf, varPos19); - if (interactionsCount < 0) { - throw ProtocolException.negativeLength("Interactions", interactionsCount); - } - - if (interactionsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos19); - obj.interactions = new HashMap<>(interactionsCount); - int dictPos = varPos19 + varIntLen; - - for (int i = 0; i < interactionsCount; i++) { - InteractionType keyxx = InteractionType.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.interactions.put(keyxx, val) != null) { - throw ProtocolException.duplicateKey("interactions", keyxx); - } - } - } - - if ((nullBits[3] & 8) != 0) { - int varPos20 = offset + 260 + buf.getIntLE(offset + 244); - int statesCount = VarInt.peek(buf, varPos20); - if (statesCount < 0) { - throw ProtocolException.negativeLength("States", statesCount); - } - - if (statesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("States", statesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos20); - obj.states = new HashMap<>(statesCount); - int dictPos = varPos20 + varIntLen; - - for (int ix = 0; ix < statesCount; ix++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if ((nullBits[1] & 2) != 0) { + int varPosBase2 = buf.getIntLE(offset + 176); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ShaderEffect", varPosBase2, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos2 = offset + 268 + varPosBase2; + int shaderEffectCount = VarInt.peek(buf, varPos2); + if (shaderEffectCount < 0) { + throw ProtocolException.invalidVarInt("ShaderEffect"); } - int keyVarLen = VarInt.length(buf, dictPos); - String keyxx = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - int val = buf.getIntLE(dictPos); - dictPos += 4; - if (obj.states.put(keyxx, val) != null) { - throw ProtocolException.duplicateKey("states", keyxx); + int varIntLen = VarInt.size(shaderEffectCount); + if (shaderEffectCount > 4096000) { + throw ProtocolException.arrayTooLong("ShaderEffect", shaderEffectCount, 4096000); + } + + if (varPos2 + varIntLen + shaderEffectCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ShaderEffect", varPos2 + varIntLen + shaderEffectCount * 1, buf.readableBytes()); + } + + obj.shaderEffect = new ShaderType[shaderEffectCount]; + int elemPos = varPos2 + varIntLen; + + for (int i = 0; i < shaderEffectCount; i++) { + obj.shaderEffect[i] = ShaderType.fromValue(buf.getByte(elemPos)); + elemPos++; } } - } - if ((nullBits[3] & 16) != 0) { - int varPos21 = offset + 260 + buf.getIntLE(offset + 248); - int tagIndexesCount = VarInt.peek(buf, varPos21); - if (tagIndexesCount < 0) { - throw ProtocolException.negativeLength("TagIndexes", tagIndexesCount); + if ((nullBits[1] & 4) != 0) { + int varPosBase3 = buf.getIntLE(offset + 180); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Model", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 268 + varPosBase3; + int modelLen = VarInt.peek(buf, varPos3); + if (modelLen < 0) { + throw ProtocolException.invalidVarInt("Model"); + } + + int modelVarIntLen = VarInt.size(modelLen); + if (modelLen > 4096000) { + throw ProtocolException.stringTooLong("Model", modelLen, 4096000); + } + + if (varPos3 + modelVarIntLen + modelLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Model", varPos3 + modelVarIntLen + modelLen, buf.readableBytes()); + } + + obj.model = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); } - if (tagIndexesCount > 4096000) { - throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); + if ((nullBits[1] & 8) != 0) { + int varPosBase4 = buf.getIntLE(offset + 184); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ModelTexture", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 268 + varPosBase4; + int modelTextureCount = VarInt.peek(buf, varPos4); + if (modelTextureCount < 0) { + throw ProtocolException.invalidVarInt("ModelTexture"); + } + + int varIntLenx = VarInt.size(modelTextureCount); + if (modelTextureCount > 4096000) { + throw ProtocolException.arrayTooLong("ModelTexture", modelTextureCount, 4096000); + } + + if (varPos4 + varIntLenx + modelTextureCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ModelTexture", varPos4 + varIntLenx + modelTextureCount * 5, buf.readableBytes()); + } + + obj.modelTexture = new ModelTexture[modelTextureCount]; + int elemPos = varPos4 + varIntLenx; + + for (int i = 0; i < modelTextureCount; i++) { + obj.modelTexture[i] = ModelTexture.deserialize(buf, elemPos); + elemPos += ModelTexture.computeBytesConsumed(buf, elemPos); + } } - int varIntLen = VarInt.length(buf, varPos21); - if (varPos21 + varIntLen + tagIndexesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("TagIndexes", varPos21 + varIntLen + tagIndexesCount * 4, buf.readableBytes()); + if ((nullBits[1] & 16) != 0) { + int varPosBase5 = buf.getIntLE(offset + 188); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ModelAnimation", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 268 + varPosBase5; + int modelAnimationLen = VarInt.peek(buf, varPos5); + if (modelAnimationLen < 0) { + throw ProtocolException.invalidVarInt("ModelAnimation"); + } + + int modelAnimationVarIntLen = VarInt.size(modelAnimationLen); + if (modelAnimationLen > 4096000) { + throw ProtocolException.stringTooLong("ModelAnimation", modelAnimationLen, 4096000); + } + + if (varPos5 + modelAnimationVarIntLen + modelAnimationLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ModelAnimation", varPos5 + modelAnimationVarIntLen + modelAnimationLen, buf.readableBytes()); + } + + obj.modelAnimation = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); } - obj.tagIndexes = new int[tagIndexesCount]; + if ((nullBits[1] & 32) != 0) { + int varPosBase6 = buf.getIntLE(offset + 192); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Support", varPosBase6, buf.readableBytes()); + } - for (int ix = 0; ix < tagIndexesCount; ix++) { - obj.tagIndexes[ix] = buf.getIntLE(varPos21 + varIntLen + ix * 4); + int varPos6 = offset + 268 + varPosBase6; + int supportCount = VarInt.peek(buf, varPos6); + if (supportCount < 0) { + throw ProtocolException.invalidVarInt("Support"); + } + + int varIntLenxx = VarInt.size(supportCount); + if (supportCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Support", supportCount, 4096000); + } + + obj.support = new HashMap<>(supportCount); + int dictPos = varPos6 + varIntLenxx; + + for (int i = 0; i < supportCount; i++) { + BlockNeighbor key = BlockNeighbor.fromValue(buf.getByte(dictPos)); + int valLen = VarInt.peek(buf, ++dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 64) { + throw ProtocolException.arrayTooLong("val", valLen, 64); + } + + if (dictPos + valVarLen + valLen * 17L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 17, buf.readableBytes()); + } + + dictPos += valVarLen; + RequiredBlockFaceSupport[] val = new RequiredBlockFaceSupport[valLen]; + + for (int valIdx = 0; valIdx < valLen; valIdx++) { + val[valIdx] = RequiredBlockFaceSupport.deserialize(buf, dictPos); + dictPos += RequiredBlockFaceSupport.computeBytesConsumed(buf, dictPos); + } + + if (obj.support.put(key, val) != null) { + throw ProtocolException.duplicateKey("support", key); + } + } } - } - if ((nullBits[3] & 32) != 0) { - int varPos22 = offset + 260 + buf.getIntLE(offset + 252); - obj.bench = Bench.deserialize(buf, varPos22); - } + if ((nullBits[1] & 64) != 0) { + int varPosBase7 = buf.getIntLE(offset + 196); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Supporting", varPosBase7, buf.readableBytes()); + } - if ((nullBits[3] & 64) != 0) { - int varPos23 = offset + 260 + buf.getIntLE(offset + 256); - obj.connectedBlockRuleSet = ConnectedBlockRuleSet.deserialize(buf, varPos23); - } + int varPos7 = offset + 268 + varPosBase7; + int supportingCount = VarInt.peek(buf, varPos7); + if (supportingCount < 0) { + throw ProtocolException.invalidVarInt("Supporting"); + } - return obj; + int varIntLenxx = VarInt.size(supportingCount); + if (supportingCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Supporting", supportingCount, 4096000); + } + + obj.supporting = new HashMap<>(supportingCount); + int dictPos = varPos7 + varIntLenxx; + + for (int i = 0; i < supportingCount; i++) { + BlockNeighbor keyx = BlockNeighbor.fromValue(buf.getByte(dictPos)); + int valLenx = VarInt.peek(buf, ++dictPos); + if (valLenx < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLenx = VarInt.size(valLenx); + if (valLenx > 64) { + throw ProtocolException.arrayTooLong("val", valLenx, 64); + } + + if (dictPos + valVarLenx + valLenx * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLenx + valLenx * 1, buf.readableBytes()); + } + + dictPos += valVarLenx; + BlockFaceSupport[] val = new BlockFaceSupport[valLenx]; + + for (int valIdx = 0; valIdx < valLenx; valIdx++) { + val[valIdx] = BlockFaceSupport.deserialize(buf, dictPos); + dictPos += BlockFaceSupport.computeBytesConsumed(buf, dictPos); + } + + if (obj.supporting.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("supporting", keyx); + } + } + } + + if ((nullBits[1] & 128) != 0) { + int varPosBase8 = buf.getIntLE(offset + 200); + if (varPosBase8 < 0 || varPosBase8 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("CubeTextures", varPosBase8, buf.readableBytes()); + } + + int varPos8 = offset + 268 + varPosBase8; + int cubeTexturesCount = VarInt.peek(buf, varPos8); + if (cubeTexturesCount < 0) { + throw ProtocolException.invalidVarInt("CubeTextures"); + } + + int varIntLenxx = VarInt.size(cubeTexturesCount); + if (cubeTexturesCount > 4096000) { + throw ProtocolException.arrayTooLong("CubeTextures", cubeTexturesCount, 4096000); + } + + if (varPos8 + varIntLenxx + cubeTexturesCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CubeTextures", varPos8 + varIntLenxx + cubeTexturesCount * 5, buf.readableBytes()); + } + + obj.cubeTextures = new BlockTextures[cubeTexturesCount]; + int elemPos = varPos8 + varIntLenxx; + + for (int i = 0; i < cubeTexturesCount; i++) { + obj.cubeTextures[i] = BlockTextures.deserialize(buf, elemPos); + elemPos += BlockTextures.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[2] & 1) != 0) { + int varPosBase9 = buf.getIntLE(offset + 204); + if (varPosBase9 < 0 || varPosBase9 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("CubeSideMaskTexture", varPosBase9, buf.readableBytes()); + } + + int varPos9 = offset + 268 + varPosBase9; + int cubeSideMaskTextureLen = VarInt.peek(buf, varPos9); + if (cubeSideMaskTextureLen < 0) { + throw ProtocolException.invalidVarInt("CubeSideMaskTexture"); + } + + int cubeSideMaskTextureVarIntLen = VarInt.size(cubeSideMaskTextureLen); + if (cubeSideMaskTextureLen > 4096000) { + throw ProtocolException.stringTooLong("CubeSideMaskTexture", cubeSideMaskTextureLen, 4096000); + } + + if (varPos9 + cubeSideMaskTextureVarIntLen + cubeSideMaskTextureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "CubeSideMaskTexture", varPos9 + cubeSideMaskTextureVarIntLen + cubeSideMaskTextureLen, buf.readableBytes() + ); + } + + obj.cubeSideMaskTexture = PacketIO.readVarString(buf, varPos9, PacketIO.UTF8); + } + + if ((nullBits[2] & 2) != 0) { + int varPosBase10 = buf.getIntLE(offset + 208); + if (varPosBase10 < 0 || varPosBase10 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ConditionalSounds", varPosBase10, buf.readableBytes()); + } + + int varPos10 = offset + 268 + varPosBase10; + int conditionalSoundsCount = VarInt.peek(buf, varPos10); + if (conditionalSoundsCount < 0) { + throw ProtocolException.invalidVarInt("ConditionalSounds"); + } + + int varIntLenxxx = VarInt.size(conditionalSoundsCount); + if (conditionalSoundsCount > 4096000) { + throw ProtocolException.arrayTooLong("ConditionalSounds", conditionalSoundsCount, 4096000); + } + + if (varPos10 + varIntLenxxx + conditionalSoundsCount * 8L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ConditionalSounds", varPos10 + varIntLenxxx + conditionalSoundsCount * 8, buf.readableBytes()); + } + + obj.conditionalSounds = new ConditionalBlockSound[conditionalSoundsCount]; + int elemPos = varPos10 + varIntLenxxx; + + for (int i = 0; i < conditionalSoundsCount; i++) { + obj.conditionalSounds[i] = ConditionalBlockSound.deserialize(buf, elemPos); + elemPos += ConditionalBlockSound.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[2] & 4) != 0) { + int varPosBase11 = buf.getIntLE(offset + 212); + if (varPosBase11 < 0 || varPosBase11 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Particles", varPosBase11, buf.readableBytes()); + } + + int varPos11 = offset + 268 + varPosBase11; + int particlesCount = VarInt.peek(buf, varPos11); + if (particlesCount < 0) { + throw ProtocolException.invalidVarInt("Particles"); + } + + int varIntLenxxxx = VarInt.size(particlesCount); + if (particlesCount > 4096000) { + throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); + } + + if (varPos11 + varIntLenxxxx + particlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Particles", varPos11 + varIntLenxxxx + particlesCount * 34, buf.readableBytes()); + } + + obj.particles = new ModelParticle[particlesCount]; + int elemPos = varPos11 + varIntLenxxxx; + + for (int i = 0; i < particlesCount; i++) { + obj.particles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[2] & 8) != 0) { + int varPosBase12 = buf.getIntLE(offset + 216); + if (varPosBase12 < 0 || varPosBase12 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("BlockParticleSetId", varPosBase12, buf.readableBytes()); + } + + int varPos12 = offset + 268 + varPosBase12; + int blockParticleSetIdLen = VarInt.peek(buf, varPos12); + if (blockParticleSetIdLen < 0) { + throw ProtocolException.invalidVarInt("BlockParticleSetId"); + } + + int blockParticleSetIdVarIntLen = VarInt.size(blockParticleSetIdLen); + if (blockParticleSetIdLen > 4096000) { + throw ProtocolException.stringTooLong("BlockParticleSetId", blockParticleSetIdLen, 4096000); + } + + if (varPos12 + blockParticleSetIdVarIntLen + blockParticleSetIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BlockParticleSetId", varPos12 + blockParticleSetIdVarIntLen + blockParticleSetIdLen, buf.readableBytes()); + } + + obj.blockParticleSetId = PacketIO.readVarString(buf, varPos12, PacketIO.UTF8); + } + + if ((nullBits[2] & 16) != 0) { + int varPosBase13 = buf.getIntLE(offset + 220); + if (varPosBase13 < 0 || varPosBase13 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("BlockBreakingDecalId", varPosBase13, buf.readableBytes()); + } + + int varPos13 = offset + 268 + varPosBase13; + int blockBreakingDecalIdLen = VarInt.peek(buf, varPos13); + if (blockBreakingDecalIdLen < 0) { + throw ProtocolException.invalidVarInt("BlockBreakingDecalId"); + } + + int blockBreakingDecalIdVarIntLen = VarInt.size(blockBreakingDecalIdLen); + if (blockBreakingDecalIdLen > 4096000) { + throw ProtocolException.stringTooLong("BlockBreakingDecalId", blockBreakingDecalIdLen, 4096000); + } + + if (varPos13 + blockBreakingDecalIdVarIntLen + blockBreakingDecalIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "BlockBreakingDecalId", varPos13 + blockBreakingDecalIdVarIntLen + blockBreakingDecalIdLen, buf.readableBytes() + ); + } + + obj.blockBreakingDecalId = PacketIO.readVarString(buf, varPos13, PacketIO.UTF8); + } + + if ((nullBits[2] & 32) != 0) { + int varPosBase14 = buf.getIntLE(offset + 224); + if (varPosBase14 < 0 || varPosBase14 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("TransitionTexture", varPosBase14, buf.readableBytes()); + } + + int varPos14 = offset + 268 + varPosBase14; + int transitionTextureLen = VarInt.peek(buf, varPos14); + if (transitionTextureLen < 0) { + throw ProtocolException.invalidVarInt("TransitionTexture"); + } + + int transitionTextureVarIntLen = VarInt.size(transitionTextureLen); + if (transitionTextureLen > 4096000) { + throw ProtocolException.stringTooLong("TransitionTexture", transitionTextureLen, 4096000); + } + + if (varPos14 + transitionTextureVarIntLen + transitionTextureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TransitionTexture", varPos14 + transitionTextureVarIntLen + transitionTextureLen, buf.readableBytes()); + } + + obj.transitionTexture = PacketIO.readVarString(buf, varPos14, PacketIO.UTF8); + } + + if ((nullBits[2] & 64) != 0) { + int varPosBase15 = buf.getIntLE(offset + 228); + if (varPosBase15 < 0 || varPosBase15 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("TransitionToGroups", varPosBase15, buf.readableBytes()); + } + + int varPos15 = offset + 268 + varPosBase15; + int transitionToGroupsCount = VarInt.peek(buf, varPos15); + if (transitionToGroupsCount < 0) { + throw ProtocolException.invalidVarInt("TransitionToGroups"); + } + + int varIntLenxxxxx = VarInt.size(transitionToGroupsCount); + if (transitionToGroupsCount > 4096000) { + throw ProtocolException.arrayTooLong("TransitionToGroups", transitionToGroupsCount, 4096000); + } + + if (varPos15 + varIntLenxxxxx + transitionToGroupsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TransitionToGroups", varPos15 + varIntLenxxxxx + transitionToGroupsCount * 4, buf.readableBytes()); + } + + obj.transitionToGroups = new int[transitionToGroupsCount]; + + for (int i = 0; i < transitionToGroupsCount; i++) { + obj.transitionToGroups[i] = buf.getIntLE(varPos15 + varIntLenxxxxx + i * 4); + } + } + + if ((nullBits[2] & 128) != 0) { + int varPosBase16 = buf.getIntLE(offset + 232); + if (varPosBase16 < 0 || varPosBase16 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("InteractionHint", varPosBase16, buf.readableBytes()); + } + + int varPos16 = offset + 268 + varPosBase16; + int interactionHintLen = VarInt.peek(buf, varPos16); + if (interactionHintLen < 0) { + throw ProtocolException.invalidVarInt("InteractionHint"); + } + + int interactionHintVarIntLen = VarInt.size(interactionHintLen); + if (interactionHintLen > 4096000) { + throw ProtocolException.stringTooLong("InteractionHint", interactionHintLen, 4096000); + } + + if (varPos16 + interactionHintVarIntLen + interactionHintLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("InteractionHint", varPos16 + interactionHintVarIntLen + interactionHintLen, buf.readableBytes()); + } + + obj.interactionHint = PacketIO.readVarString(buf, varPos16, PacketIO.UTF8); + } + + if ((nullBits[3] & 1) != 0) { + int varPosBase17 = buf.getIntLE(offset + 236); + if (varPosBase17 < 0 || varPosBase17 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Gathering", varPosBase17, buf.readableBytes()); + } + + int varPos17 = offset + 268 + varPosBase17; + obj.gathering = BlockGathering.deserialize(buf, varPos17); + } + + if ((nullBits[3] & 2) != 0) { + int varPosBase18 = buf.getIntLE(offset + 240); + if (varPosBase18 < 0 || varPosBase18 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Display", varPosBase18, buf.readableBytes()); + } + + int varPos18 = offset + 268 + varPosBase18; + obj.display = ModelDisplay.deserialize(buf, varPos18); + } + + if ((nullBits[3] & 4) != 0) { + int varPosBase19 = buf.getIntLE(offset + 244); + if (varPosBase19 < 0 || varPosBase19 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Rail", varPosBase19, buf.readableBytes()); + } + + int varPos19 = offset + 268 + varPosBase19; + obj.rail = RailConfig.deserialize(buf, varPos19); + } + + if ((nullBits[3] & 8) != 0) { + int varPosBase20 = buf.getIntLE(offset + 248); + if (varPosBase20 < 0 || varPosBase20 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Interactions", varPosBase20, buf.readableBytes()); + } + + int varPos20 = offset + 268 + varPosBase20; + int interactionsCount = VarInt.peek(buf, varPos20); + if (interactionsCount < 0) { + throw ProtocolException.invalidVarInt("Interactions"); + } + + int varIntLenxxxxxx = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + } + + obj.interactions = new HashMap<>(interactionsCount); + int dictPos = varPos20 + varIntLenxxxxxx; + + for (int i = 0; i < interactionsCount; i++) { + InteractionType keyxx = InteractionType.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.interactions.put(keyxx, val) != null) { + throw ProtocolException.duplicateKey("interactions", keyxx); + } + } + } + + if ((nullBits[3] & 16) != 0) { + int varPosBase21 = buf.getIntLE(offset + 252); + if (varPosBase21 < 0 || varPosBase21 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("States", varPosBase21, buf.readableBytes()); + } + + int varPos21 = offset + 268 + varPosBase21; + int statesCount = VarInt.peek(buf, varPos21); + if (statesCount < 0) { + throw ProtocolException.invalidVarInt("States"); + } + + int varIntLenxxxxxx = VarInt.size(statesCount); + if (statesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("States", statesCount, 4096000); + } + + obj.states = new HashMap<>(statesCount); + int dictPos = varPos21 + varIntLenxxxxxx; + + for (int ix = 0; ix < statesCount; ix++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String keyxx = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + int val = buf.getIntLE(dictPos); + dictPos += 4; + if (obj.states.put(keyxx, val) != null) { + throw ProtocolException.duplicateKey("states", keyxx); + } + } + } + + if ((nullBits[3] & 32) != 0) { + int varPosBase22 = buf.getIntLE(offset + 256); + if (varPosBase22 < 0 || varPosBase22 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("TagIndexes", varPosBase22, buf.readableBytes()); + } + + int varPos22 = offset + 268 + varPosBase22; + int tagIndexesCount = VarInt.peek(buf, varPos22); + if (tagIndexesCount < 0) { + throw ProtocolException.invalidVarInt("TagIndexes"); + } + + int varIntLenxxxxxx = VarInt.size(tagIndexesCount); + if (tagIndexesCount > 4096000) { + throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); + } + + if (varPos22 + varIntLenxxxxxx + tagIndexesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TagIndexes", varPos22 + varIntLenxxxxxx + tagIndexesCount * 4, buf.readableBytes()); + } + + obj.tagIndexes = new int[tagIndexesCount]; + + for (int ix = 0; ix < tagIndexesCount; ix++) { + obj.tagIndexes[ix] = buf.getIntLE(varPos22 + varIntLenxxxxxx + ix * 4); + } + } + + if ((nullBits[3] & 64) != 0) { + int varPosBase23 = buf.getIntLE(offset + 260); + if (varPosBase23 < 0 || varPosBase23 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Bench", varPosBase23, buf.readableBytes()); + } + + int varPos23 = offset + 268 + varPosBase23; + obj.bench = Bench.deserialize(buf, varPos23); + } + + if ((nullBits[3] & 128) != 0) { + int varPosBase24 = buf.getIntLE(offset + 264); + if (varPosBase24 < 0 || varPosBase24 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ConnectedBlockRuleSet", varPosBase24, buf.readableBytes()); + } + + int varPos24 = offset + 268 + varPosBase24; + obj.connectedBlockRuleSet = ConnectedBlockRuleSet.deserialize(buf, varPos24); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte[] nullBits = PacketIO.readBytes(buf, offset, 4); - int maxEnd = 260; + int maxEnd = 268; if ((nullBits[0] & 128) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 164); - int pos0 = offset + 260 + fieldOffset0; + int fieldOffset0 = buf.getIntLE(offset + 168); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Item", fieldOffset0, maxEnd); + } + + int pos0 = offset + 268 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } } if ((nullBits[1] & 1) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 168); - int pos1 = offset + 260 + fieldOffset1; + int fieldOffset1 = buf.getIntLE(offset + 172); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Name", fieldOffset1, maxEnd); + } + + int pos1 = offset + 268 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } } if ((nullBits[1] & 2) != 0) { - int fieldOffset2 = buf.getIntLE(offset + 172); - int pos2 = offset + 260 + fieldOffset2; + int fieldOffset2 = buf.getIntLE(offset + 176); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ShaderEffect", fieldOffset2, maxEnd); + } + + int pos2 = offset + 268 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + arrLen * 1; + pos2 += VarInt.size(arrLen) + arrLen * 1; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } } if ((nullBits[1] & 4) != 0) { - int fieldOffset3 = buf.getIntLE(offset + 176); - int pos3 = offset + 260 + fieldOffset3; + int fieldOffset3 = buf.getIntLE(offset + 180); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Model", fieldOffset3, maxEnd); + } + + int pos3 = offset + 268 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } } if ((nullBits[1] & 8) != 0) { - int fieldOffset4 = buf.getIntLE(offset + 180); - int pos4 = offset + 260 + fieldOffset4; + int fieldOffset4 = buf.getIntLE(offset + 184); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ModelTexture", fieldOffset4, maxEnd); + } + + int pos4 = offset + 268 + fieldOffset4; int arrLen = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4); + pos4 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos4 += ModelTexture.computeBytesConsumed(buf, pos4); @@ -835,24 +1072,32 @@ public class BlockType { } if ((nullBits[1] & 16) != 0) { - int fieldOffset5 = buf.getIntLE(offset + 184); - int pos5 = offset + 260 + fieldOffset5; + int fieldOffset5 = buf.getIntLE(offset + 188); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ModelAnimation", fieldOffset5, maxEnd); + } + + int pos5 = offset + 268 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } } if ((nullBits[1] & 32) != 0) { - int fieldOffset6 = buf.getIntLE(offset + 188); - int pos6 = offset + 260 + fieldOffset6; + int fieldOffset6 = buf.getIntLE(offset + 192); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Support", fieldOffset6, maxEnd); + } + + int pos6 = offset + 268 + fieldOffset6; int dictLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int al = VarInt.peek(buf, ++pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(al); for (int j = 0; j < al; j++) { pos6 += RequiredBlockFaceSupport.computeBytesConsumed(buf, pos6); @@ -865,14 +1110,18 @@ public class BlockType { } if ((nullBits[1] & 64) != 0) { - int fieldOffset7 = buf.getIntLE(offset + 192); - int pos7 = offset + 260 + fieldOffset7; + int fieldOffset7 = buf.getIntLE(offset + 196); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Supporting", fieldOffset7, maxEnd); + } + + int pos7 = offset + 268 + fieldOffset7; int dictLen = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7); + pos7 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int al = VarInt.peek(buf, ++pos7); - pos7 += VarInt.length(buf, pos7); + pos7 += VarInt.size(al); for (int j = 0; j < al; j++) { pos7 += BlockFaceSupport.computeBytesConsumed(buf, pos7); @@ -885,10 +1134,14 @@ public class BlockType { } if ((nullBits[1] & 128) != 0) { - int fieldOffset8 = buf.getIntLE(offset + 196); - int pos8 = offset + 260 + fieldOffset8; + int fieldOffset8 = buf.getIntLE(offset + 200); + if (fieldOffset8 < 0 || fieldOffset8 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("CubeTextures", fieldOffset8, maxEnd); + } + + int pos8 = offset + 268 + fieldOffset8; int arrLen = VarInt.peek(buf, pos8); - pos8 += VarInt.length(buf, pos8); + pos8 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos8 += BlockTextures.computeBytesConsumed(buf, pos8); @@ -900,23 +1153,31 @@ public class BlockType { } if ((nullBits[2] & 1) != 0) { - int fieldOffset9 = buf.getIntLE(offset + 200); - int pos9 = offset + 260 + fieldOffset9; + int fieldOffset9 = buf.getIntLE(offset + 204); + if (fieldOffset9 < 0 || fieldOffset9 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("CubeSideMaskTexture", fieldOffset9, maxEnd); + } + + int pos9 = offset + 268 + fieldOffset9; int sl = VarInt.peek(buf, pos9); - pos9 += VarInt.length(buf, pos9) + sl; + pos9 += VarInt.size(sl) + sl; if (pos9 - offset > maxEnd) { maxEnd = pos9 - offset; } } if ((nullBits[2] & 2) != 0) { - int fieldOffset10 = buf.getIntLE(offset + 204); - int pos10 = offset + 260 + fieldOffset10; + int fieldOffset10 = buf.getIntLE(offset + 208); + if (fieldOffset10 < 0 || fieldOffset10 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ConditionalSounds", fieldOffset10, maxEnd); + } + + int pos10 = offset + 268 + fieldOffset10; int arrLen = VarInt.peek(buf, pos10); - pos10 += VarInt.length(buf, pos10); + pos10 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { - pos10 += ModelParticle.computeBytesConsumed(buf, pos10); + pos10 += ConditionalBlockSound.computeBytesConsumed(buf, pos10); } if (pos10 - offset > maxEnd) { @@ -925,107 +1186,145 @@ public class BlockType { } if ((nullBits[2] & 4) != 0) { - int fieldOffset11 = buf.getIntLE(offset + 208); - int pos11 = offset + 260 + fieldOffset11; - int sl = VarInt.peek(buf, pos11); - pos11 += VarInt.length(buf, pos11) + sl; + int fieldOffset11 = buf.getIntLE(offset + 212); + if (fieldOffset11 < 0 || fieldOffset11 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Particles", fieldOffset11, maxEnd); + } + + int pos11 = offset + 268 + fieldOffset11; + int arrLen = VarInt.peek(buf, pos11); + pos11 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos11 += ModelParticle.computeBytesConsumed(buf, pos11); + } + if (pos11 - offset > maxEnd) { maxEnd = pos11 - offset; } } if ((nullBits[2] & 8) != 0) { - int fieldOffset12 = buf.getIntLE(offset + 212); - int pos12 = offset + 260 + fieldOffset12; + int fieldOffset12 = buf.getIntLE(offset + 216); + if (fieldOffset12 < 0 || fieldOffset12 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("BlockParticleSetId", fieldOffset12, maxEnd); + } + + int pos12 = offset + 268 + fieldOffset12; int sl = VarInt.peek(buf, pos12); - pos12 += VarInt.length(buf, pos12) + sl; + pos12 += VarInt.size(sl) + sl; if (pos12 - offset > maxEnd) { maxEnd = pos12 - offset; } } if ((nullBits[2] & 16) != 0) { - int fieldOffset13 = buf.getIntLE(offset + 216); - int pos13 = offset + 260 + fieldOffset13; + int fieldOffset13 = buf.getIntLE(offset + 220); + if (fieldOffset13 < 0 || fieldOffset13 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("BlockBreakingDecalId", fieldOffset13, maxEnd); + } + + int pos13 = offset + 268 + fieldOffset13; int sl = VarInt.peek(buf, pos13); - pos13 += VarInt.length(buf, pos13) + sl; + pos13 += VarInt.size(sl) + sl; if (pos13 - offset > maxEnd) { maxEnd = pos13 - offset; } } if ((nullBits[2] & 32) != 0) { - int fieldOffset14 = buf.getIntLE(offset + 220); - int pos14 = offset + 260 + fieldOffset14; - int arrLen = VarInt.peek(buf, pos14); - pos14 += VarInt.length(buf, pos14) + arrLen * 4; + int fieldOffset14 = buf.getIntLE(offset + 224); + if (fieldOffset14 < 0 || fieldOffset14 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("TransitionTexture", fieldOffset14, maxEnd); + } + + int pos14 = offset + 268 + fieldOffset14; + int sl = VarInt.peek(buf, pos14); + pos14 += VarInt.size(sl) + sl; if (pos14 - offset > maxEnd) { maxEnd = pos14 - offset; } } if ((nullBits[2] & 64) != 0) { - int fieldOffset15 = buf.getIntLE(offset + 224); - int pos15 = offset + 260 + fieldOffset15; - int sl = VarInt.peek(buf, pos15); - pos15 += VarInt.length(buf, pos15) + sl; + int fieldOffset15 = buf.getIntLE(offset + 228); + if (fieldOffset15 < 0 || fieldOffset15 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("TransitionToGroups", fieldOffset15, maxEnd); + } + + int pos15 = offset + 268 + fieldOffset15; + int arrLen = VarInt.peek(buf, pos15); + pos15 += VarInt.size(arrLen) + arrLen * 4; if (pos15 - offset > maxEnd) { maxEnd = pos15 - offset; } } if ((nullBits[2] & 128) != 0) { - int fieldOffset16 = buf.getIntLE(offset + 228); - int pos16 = offset + 260 + fieldOffset16; - pos16 += BlockGathering.computeBytesConsumed(buf, pos16); + int fieldOffset16 = buf.getIntLE(offset + 232); + if (fieldOffset16 < 0 || fieldOffset16 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("InteractionHint", fieldOffset16, maxEnd); + } + + int pos16 = offset + 268 + fieldOffset16; + int sl = VarInt.peek(buf, pos16); + pos16 += VarInt.size(sl) + sl; if (pos16 - offset > maxEnd) { maxEnd = pos16 - offset; } } if ((nullBits[3] & 1) != 0) { - int fieldOffset17 = buf.getIntLE(offset + 232); - int pos17 = offset + 260 + fieldOffset17; - pos17 += ModelDisplay.computeBytesConsumed(buf, pos17); + int fieldOffset17 = buf.getIntLE(offset + 236); + if (fieldOffset17 < 0 || fieldOffset17 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Gathering", fieldOffset17, maxEnd); + } + + int pos17 = offset + 268 + fieldOffset17; + pos17 += BlockGathering.computeBytesConsumed(buf, pos17); if (pos17 - offset > maxEnd) { maxEnd = pos17 - offset; } } if ((nullBits[3] & 2) != 0) { - int fieldOffset18 = buf.getIntLE(offset + 236); - int pos18 = offset + 260 + fieldOffset18; - pos18 += RailConfig.computeBytesConsumed(buf, pos18); + int fieldOffset18 = buf.getIntLE(offset + 240); + if (fieldOffset18 < 0 || fieldOffset18 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Display", fieldOffset18, maxEnd); + } + + int pos18 = offset + 268 + fieldOffset18; + pos18 += ModelDisplay.computeBytesConsumed(buf, pos18); if (pos18 - offset > maxEnd) { maxEnd = pos18 - offset; } } if ((nullBits[3] & 4) != 0) { - int fieldOffset19 = buf.getIntLE(offset + 240); - int pos19 = offset + 260 + fieldOffset19; - int dictLen = VarInt.peek(buf, pos19); - pos19 += VarInt.length(buf, pos19); - - for (int i = 0; i < dictLen; i++) { - pos19 = ++pos19 + 4; + int fieldOffset19 = buf.getIntLE(offset + 244); + if (fieldOffset19 < 0 || fieldOffset19 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Rail", fieldOffset19, maxEnd); } + int pos19 = offset + 268 + fieldOffset19; + pos19 += RailConfig.computeBytesConsumed(buf, pos19); if (pos19 - offset > maxEnd) { maxEnd = pos19 - offset; } } if ((nullBits[3] & 8) != 0) { - int fieldOffset20 = buf.getIntLE(offset + 244); - int pos20 = offset + 260 + fieldOffset20; + int fieldOffset20 = buf.getIntLE(offset + 248); + if (fieldOffset20 < 0 || fieldOffset20 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Interactions", fieldOffset20, maxEnd); + } + + int pos20 = offset + 268 + fieldOffset20; int dictLen = VarInt.peek(buf, pos20); - pos20 += VarInt.length(buf, pos20); + pos20 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { - int sl = VarInt.peek(buf, pos20); - pos20 += VarInt.length(buf, pos20) + sl; - pos20 += 4; + pos20 = ++pos20 + 4; } if (pos20 - offset > maxEnd) { @@ -1034,33 +1333,66 @@ public class BlockType { } if ((nullBits[3] & 16) != 0) { - int fieldOffset21 = buf.getIntLE(offset + 248); - int pos21 = offset + 260 + fieldOffset21; - int arrLen = VarInt.peek(buf, pos21); - pos21 += VarInt.length(buf, pos21) + arrLen * 4; + int fieldOffset21 = buf.getIntLE(offset + 252); + if (fieldOffset21 < 0 || fieldOffset21 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("States", fieldOffset21, maxEnd); + } + + int pos21 = offset + 268 + fieldOffset21; + int dictLen = VarInt.peek(buf, pos21); + pos21 += VarInt.size(dictLen); + + for (int i = 0; i < dictLen; i++) { + int sl = VarInt.peek(buf, pos21); + pos21 += VarInt.size(sl) + sl; + pos21 += 4; + } + if (pos21 - offset > maxEnd) { maxEnd = pos21 - offset; } } if ((nullBits[3] & 32) != 0) { - int fieldOffset22 = buf.getIntLE(offset + 252); - int pos22 = offset + 260 + fieldOffset22; - pos22 += Bench.computeBytesConsumed(buf, pos22); + int fieldOffset22 = buf.getIntLE(offset + 256); + if (fieldOffset22 < 0 || fieldOffset22 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("TagIndexes", fieldOffset22, maxEnd); + } + + int pos22 = offset + 268 + fieldOffset22; + int arrLen = VarInt.peek(buf, pos22); + pos22 += VarInt.size(arrLen) + arrLen * 4; if (pos22 - offset > maxEnd) { maxEnd = pos22 - offset; } } if ((nullBits[3] & 64) != 0) { - int fieldOffset23 = buf.getIntLE(offset + 256); - int pos23 = offset + 260 + fieldOffset23; - pos23 += ConnectedBlockRuleSet.computeBytesConsumed(buf, pos23); + int fieldOffset23 = buf.getIntLE(offset + 260); + if (fieldOffset23 < 0 || fieldOffset23 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("Bench", fieldOffset23, maxEnd); + } + + int pos23 = offset + 268 + fieldOffset23; + pos23 += Bench.computeBytesConsumed(buf, pos23); if (pos23 - offset > maxEnd) { maxEnd = pos23 - offset; } } + if ((nullBits[3] & 128) != 0) { + int fieldOffset24 = buf.getIntLE(offset + 264); + if (fieldOffset24 < 0 || fieldOffset24 > buf.writerIndex() - offset - 268) { + throw ProtocolException.invalidOffset("ConnectedBlockRuleSet", fieldOffset24, maxEnd); + } + + int pos24 = offset + 268 + fieldOffset24; + pos24 += ConnectedBlockRuleSet.computeBytesConsumed(buf, pos24); + if (pos24 - offset > maxEnd) { + maxEnd = pos24 - offset; + } + } + return maxEnd; } @@ -1135,62 +1467,66 @@ public class BlockType { nullBits[2] = (byte)(nullBits[2] | 1); } - if (this.particles != null) { + if (this.conditionalSounds != null) { nullBits[2] = (byte)(nullBits[2] | 2); } - if (this.blockParticleSetId != null) { + if (this.particles != null) { nullBits[2] = (byte)(nullBits[2] | 4); } - if (this.blockBreakingDecalId != null) { + if (this.blockParticleSetId != null) { nullBits[2] = (byte)(nullBits[2] | 8); } - if (this.transitionTexture != null) { + if (this.blockBreakingDecalId != null) { nullBits[2] = (byte)(nullBits[2] | 16); } - if (this.transitionToGroups != null) { + if (this.transitionTexture != null) { nullBits[2] = (byte)(nullBits[2] | 32); } - if (this.interactionHint != null) { + if (this.transitionToGroups != null) { nullBits[2] = (byte)(nullBits[2] | 64); } - if (this.gathering != null) { + if (this.interactionHint != null) { nullBits[2] = (byte)(nullBits[2] | 128); } - if (this.display != null) { + if (this.gathering != null) { nullBits[3] = (byte)(nullBits[3] | 1); } - if (this.rail != null) { + if (this.display != null) { nullBits[3] = (byte)(nullBits[3] | 2); } - if (this.interactions != null) { + if (this.rail != null) { nullBits[3] = (byte)(nullBits[3] | 4); } - if (this.states != null) { + if (this.interactions != null) { nullBits[3] = (byte)(nullBits[3] | 8); } - if (this.tagIndexes != null) { + if (this.states != null) { nullBits[3] = (byte)(nullBits[3] | 16); } - if (this.bench != null) { + if (this.tagIndexes != null) { nullBits[3] = (byte)(nullBits[3] | 32); } - if (this.connectedBlockRuleSet != null) { + if (this.bench != null) { nullBits[3] = (byte)(nullBits[3] | 64); } + if (this.connectedBlockRuleSet != null) { + nullBits[3] = (byte)(nullBits[3] | 128); + } + buf.writeBytes(nullBits); buf.writeByte(this.unknown ? 1 : 0); buf.writeByte(this.drawType.getValue()); @@ -1208,6 +1544,7 @@ public class BlockType { buf.writeByte(this.variantRotation.getValue()); buf.writeByte(this.rotationYawPlacementOffset.getValue()); buf.writeIntLE(this.blockSoundSetIndex); + buf.writeIntLE(this.physicalMaterialIndex); buf.writeIntLE(this.ambientSoundEventIndex); if (this.particleColor != null) { this.particleColor.serialize(buf); @@ -1274,6 +1611,8 @@ public class BlockType { buf.writeIntLE(0); int cubeSideMaskTextureOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int conditionalSoundsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int particlesOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int blockParticleSetIdOffsetSlot = buf.writerIndex(); @@ -1423,6 +1762,21 @@ public class BlockType { buf.setIntLE(cubeSideMaskTextureOffsetSlot, -1); } + if (this.conditionalSounds != null) { + buf.setIntLE(conditionalSoundsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.conditionalSounds.length > 4096000) { + throw ProtocolException.arrayTooLong("ConditionalSounds", this.conditionalSounds.length, 4096000); + } + + VarInt.write(buf, this.conditionalSounds.length); + + for (ConditionalBlockSound item : this.conditionalSounds) { + item.serialize(buf); + } + } else { + buf.setIntLE(conditionalSoundsOffsetSlot, -1); + } + if (this.particles != null) { buf.setIntLE(particlesOffsetSlot, buf.writerIndex() - varBlockStart); if (this.particles.length > 4096000) { @@ -1565,7 +1919,7 @@ public class BlockType { } public int computeSize() { - int size = 260; + int size = 268; if (this.item != null) { size += PacketIO.stringSize(this.item); } @@ -1630,6 +1984,10 @@ public class BlockType { size += PacketIO.stringSize(this.cubeSideMaskTexture); } + if (this.conditionalSounds != null) { + size += VarInt.size(this.conditionalSounds.length) + this.conditionalSounds.length * 8; + } + if (this.particles != null) { int particlesSize = 0; @@ -1702,673 +2060,663 @@ public class BlockType { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 260) { - return ValidationResult.error("Buffer too small: expected at least 260 bytes"); + if (buffer.readableBytes() - offset < 268) { + return ValidationResult.error("Buffer too small: expected at least 268 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 4); - if ((nullBits[0] & 128) != 0) { - int itemOffset = buffer.getIntLE(offset + 164); - if (itemOffset < 0) { - return ValidationResult.error("Invalid offset for Item"); - } + int v = buffer.getByte(offset + 5) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid DrawType value for DrawType"); + } else { + v = buffer.getByte(offset + 6) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid BlockMaterial value for Material"); + } else { + v = buffer.getByte(offset + 7) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid Opacity value for Opacity"); + } else { + v = buffer.getByte(offset + 25) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid BlockSupportsRequiredForType value for BlockSupportsRequiredFor"); + } else { + v = buffer.getByte(offset + 27) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid ShadingMode value for CubeShadingMode"); + } else { + v = buffer.getByte(offset + 28) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid RandomRotation value for RandomRotation"); + } else { + v = buffer.getByte(offset + 29) & 255; + if (v >= 8) { + return ValidationResult.error("Invalid VariantRotation value for VariantRotation"); + } else { + v = buffer.getByte(offset + 30) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid Rotation value for RotationYawPlacementOffset"); + } else { + if ((nullBits[0] & 128) != 0) { + v = buffer.getIntLE(offset + 168); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Item"); + } - int pos = offset + 260 + itemOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Item"); - } + int pos = offset + 268 + v; + int itemLen = VarInt.peek(buffer, pos); + if (itemLen < 0) { + return ValidationResult.error("Invalid string length for Item"); + } - int itemLen = VarInt.peek(buffer, pos); - if (itemLen < 0) { - return ValidationResult.error("Invalid string length for Item"); - } + if (itemLen > 4096000) { + return ValidationResult.error("Item exceeds max length 4096000"); + } - if (itemLen > 4096000) { - return ValidationResult.error("Item exceeds max length 4096000"); - } + pos += VarInt.size(itemLen); + pos += itemLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Item"); + } + } - pos += VarInt.length(buffer, pos); - pos += itemLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Item"); - } - } + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 172); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Name"); + } - if ((nullBits[1] & 1) != 0) { - int nameOffset = buffer.getIntLE(offset + 168); - if (nameOffset < 0) { - return ValidationResult.error("Invalid offset for Name"); - } + int posx = offset + 268 + v; + int nameLen = VarInt.peek(buffer, posx); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } - int posx = offset + 260 + nameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } + if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); + } - int nameLen = VarInt.peek(buffer, posx); - if (nameLen < 0) { - return ValidationResult.error("Invalid string length for Name"); - } + posx += VarInt.size(nameLen); + posx += nameLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); + } + } - if (nameLen > 4096000) { - return ValidationResult.error("Name exceeds max length 4096000"); - } + if ((nullBits[1] & 2) != 0) { + v = buffer.getIntLE(offset + 176); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for ShaderEffect"); + } - posx += VarInt.length(buffer, posx); - posx += nameLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Name"); - } - } + int posxx = offset + 268 + v; + int shaderEffectCount = VarInt.peek(buffer, posxx); + if (shaderEffectCount < 0) { + return ValidationResult.error("Invalid array count for ShaderEffect"); + } - if ((nullBits[1] & 2) != 0) { - int shaderEffectOffset = buffer.getIntLE(offset + 172); - if (shaderEffectOffset < 0) { - return ValidationResult.error("Invalid offset for ShaderEffect"); - } + if (shaderEffectCount > 4096000) { + return ValidationResult.error("ShaderEffect exceeds max length 4096000"); + } - int posxx = offset + 260 + shaderEffectOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ShaderEffect"); - } + posxx += VarInt.size(shaderEffectCount); + if (posxx + shaderEffectCount * 1L > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ShaderEffect"); + } - int shaderEffectCount = VarInt.peek(buffer, posxx); - if (shaderEffectCount < 0) { - return ValidationResult.error("Invalid array count for ShaderEffect"); - } + for (int i = 0; i < shaderEffectCount; i++) { + int vx = buffer.getByte(posxx) & 255; + if (vx >= 10) { + return ValidationResult.error("Invalid ShaderType value for ShaderEffect[i]"); + } - if (shaderEffectCount > 4096000) { - return ValidationResult.error("ShaderEffect exceeds max length 4096000"); - } + posxx++; + } + } - posxx += VarInt.length(buffer, posxx); - posxx += shaderEffectCount * 1; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ShaderEffect"); - } - } + if ((nullBits[1] & 4) != 0) { + v = buffer.getIntLE(offset + 180); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Model"); + } - if ((nullBits[1] & 4) != 0) { - int modelOffset = buffer.getIntLE(offset + 176); - if (modelOffset < 0) { - return ValidationResult.error("Invalid offset for Model"); - } + int posxxx = offset + 268 + v; + int modelLen = VarInt.peek(buffer, posxxx); + if (modelLen < 0) { + return ValidationResult.error("Invalid string length for Model"); + } - int posxxx = offset + 260 + modelOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } + if (modelLen > 4096000) { + return ValidationResult.error("Model exceeds max length 4096000"); + } - int modelLen = VarInt.peek(buffer, posxxx); - if (modelLen < 0) { - return ValidationResult.error("Invalid string length for Model"); - } + posxxx += VarInt.size(modelLen); + posxxx += modelLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Model"); + } + } - if (modelLen > 4096000) { - return ValidationResult.error("Model exceeds max length 4096000"); - } + if ((nullBits[1] & 8) != 0) { + v = buffer.getIntLE(offset + 184); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for ModelTexture"); + } - posxxx += VarInt.length(buffer, posxxx); - posxxx += modelLen; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Model"); - } - } + int posxxxx = offset + 268 + v; + int modelTextureCount = VarInt.peek(buffer, posxxxx); + if (modelTextureCount < 0) { + return ValidationResult.error("Invalid array count for ModelTexture"); + } - if ((nullBits[1] & 8) != 0) { - int modelTextureOffset = buffer.getIntLE(offset + 180); - if (modelTextureOffset < 0) { - return ValidationResult.error("Invalid offset for ModelTexture"); - } + if (modelTextureCount > 4096000) { + return ValidationResult.error("ModelTexture exceeds max length 4096000"); + } - int posxxxx = offset + 260 + modelTextureOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModelTexture"); - } + posxxxx += VarInt.size(modelTextureCount); - int modelTextureCount = VarInt.peek(buffer, posxxxx); - if (modelTextureCount < 0) { - return ValidationResult.error("Invalid array count for ModelTexture"); - } + for (int i = 0; i < modelTextureCount; i++) { + ValidationResult structResult = ModelTexture.validateStructure(buffer, posxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ModelTexture in ModelTexture[" + i + "]: " + structResult.error()); + } - if (modelTextureCount > 4096000) { - return ValidationResult.error("ModelTexture exceeds max length 4096000"); - } + posxxxx += ModelTexture.computeBytesConsumed(buffer, posxxxx); + } + } - posxxxx += VarInt.length(buffer, posxxxx); + if ((nullBits[1] & 16) != 0) { + v = buffer.getIntLE(offset + 188); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for ModelAnimation"); + } - for (int i = 0; i < modelTextureCount; i++) { - ValidationResult structResult = ModelTexture.validateStructure(buffer, posxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ModelTexture in ModelTexture[" + i + "]: " + structResult.error()); - } + int posxxxxx = offset + 268 + v; + int modelAnimationLen = VarInt.peek(buffer, posxxxxx); + if (modelAnimationLen < 0) { + return ValidationResult.error("Invalid string length for ModelAnimation"); + } - posxxxx += ModelTexture.computeBytesConsumed(buffer, posxxxx); - } - } + if (modelAnimationLen > 4096000) { + return ValidationResult.error("ModelAnimation exceeds max length 4096000"); + } - if ((nullBits[1] & 16) != 0) { - int modelAnimationOffset = buffer.getIntLE(offset + 184); - if (modelAnimationOffset < 0) { - return ValidationResult.error("Invalid offset for ModelAnimation"); - } + posxxxxx += VarInt.size(modelAnimationLen); + posxxxxx += modelAnimationLen; + if (posxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ModelAnimation"); + } + } - int posxxxxx = offset + 260 + modelAnimationOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModelAnimation"); - } + if ((nullBits[1] & 32) != 0) { + v = buffer.getIntLE(offset + 192); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Support"); + } - int modelAnimationLen = VarInt.peek(buffer, posxxxxx); - if (modelAnimationLen < 0) { - return ValidationResult.error("Invalid string length for ModelAnimation"); - } + int posxxxxxx = offset + 268 + v; + int supportCount = VarInt.peek(buffer, posxxxxxx); + if (supportCount < 0) { + return ValidationResult.error("Invalid dictionary count for Support"); + } - if (modelAnimationLen > 4096000) { - return ValidationResult.error("ModelAnimation exceeds max length 4096000"); - } + if (supportCount > 4096000) { + return ValidationResult.error("Support exceeds max length 4096000"); + } - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += modelAnimationLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ModelAnimation"); - } - } + posxxxxxx += VarInt.size(supportCount); - if ((nullBits[1] & 32) != 0) { - int supportOffset = buffer.getIntLE(offset + 188); - if (supportOffset < 0) { - return ValidationResult.error("Invalid offset for Support"); - } + for (int i = 0; i < supportCount; i++) { + int vx = buffer.getByte(posxxxxxx) & 255; + if (vx >= 26) { + return ValidationResult.error("Invalid BlockNeighbor value for key"); + } - int posxxxxxx = offset + 260 + supportOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Support"); - } + vx = VarInt.peek(buffer, ++posxxxxxx); + if (vx < 0) { + return ValidationResult.error("Invalid array count for value"); + } - int supportCount = VarInt.peek(buffer, posxxxxxx); - if (supportCount < 0) { - return ValidationResult.error("Invalid dictionary count for Support"); - } + posxxxxxx += VarInt.size(vx); - if (supportCount > 4096000) { - return ValidationResult.error("Support exceeds max length 4096000"); - } + for (int valueArrIdx = 0; valueArrIdx < vx; valueArrIdx++) { + posxxxxxx += RequiredBlockFaceSupport.computeBytesConsumed(buffer, posxxxxxx); + } + } + } - posxxxxxx += VarInt.length(buffer, posxxxxxx); + if ((nullBits[1] & 64) != 0) { + v = buffer.getIntLE(offset + 196); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Supporting"); + } - for (int i = 0; i < supportCount; i++) { - int valueArrCount = VarInt.peek(buffer, ++posxxxxxx); - if (valueArrCount < 0) { - return ValidationResult.error("Invalid array count for value"); - } + int posxxxxxxx = offset + 268 + v; + int supportingCount = VarInt.peek(buffer, posxxxxxxx); + if (supportingCount < 0) { + return ValidationResult.error("Invalid dictionary count for Supporting"); + } - posxxxxxx += VarInt.length(buffer, posxxxxxx); + if (supportingCount > 4096000) { + return ValidationResult.error("Supporting exceeds max length 4096000"); + } - for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - posxxxxxx += RequiredBlockFaceSupport.computeBytesConsumed(buffer, posxxxxxx); + posxxxxxxx += VarInt.size(supportingCount); + + for (int i = 0; i < supportingCount; i++) { + int vxx = buffer.getByte(posxxxxxxx) & 255; + if (vxx >= 26) { + return ValidationResult.error("Invalid BlockNeighbor value for key"); + } + + vxx = VarInt.peek(buffer, ++posxxxxxxx); + if (vxx < 0) { + return ValidationResult.error("Invalid array count for value"); + } + + posxxxxxxx += VarInt.size(vxx); + + for (int valueArrIdx = 0; valueArrIdx < vxx; valueArrIdx++) { + posxxxxxxx += BlockFaceSupport.computeBytesConsumed(buffer, posxxxxxxx); + } + } + } + + if ((nullBits[1] & 128) != 0) { + v = buffer.getIntLE(offset + 200); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for CubeTextures"); + } + + int posxxxxxxxx = offset + 268 + v; + int cubeTexturesCount = VarInt.peek(buffer, posxxxxxxxx); + if (cubeTexturesCount < 0) { + return ValidationResult.error("Invalid array count for CubeTextures"); + } + + if (cubeTexturesCount > 4096000) { + return ValidationResult.error("CubeTextures exceeds max length 4096000"); + } + + posxxxxxxxx += VarInt.size(cubeTexturesCount); + + for (int i = 0; i < cubeTexturesCount; i++) { + ValidationResult structResult = BlockTextures.validateStructure(buffer, posxxxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid BlockTextures in CubeTextures[" + i + "]: " + structResult.error()); + } + + posxxxxxxxx += BlockTextures.computeBytesConsumed(buffer, posxxxxxxxx); + } + } + + if ((nullBits[2] & 1) != 0) { + v = buffer.getIntLE(offset + 204); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for CubeSideMaskTexture"); + } + + int posxxxxxxxxx = offset + 268 + v; + int cubeSideMaskTextureLen = VarInt.peek(buffer, posxxxxxxxxx); + if (cubeSideMaskTextureLen < 0) { + return ValidationResult.error("Invalid string length for CubeSideMaskTexture"); + } + + if (cubeSideMaskTextureLen > 4096000) { + return ValidationResult.error("CubeSideMaskTexture exceeds max length 4096000"); + } + + posxxxxxxxxx += VarInt.size(cubeSideMaskTextureLen); + posxxxxxxxxx += cubeSideMaskTextureLen; + if (posxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading CubeSideMaskTexture"); + } + } + + if ((nullBits[2] & 2) != 0) { + v = buffer.getIntLE(offset + 208); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for ConditionalSounds"); + } + + int posxxxxxxxxxx = offset + 268 + v; + int conditionalSoundsCount = VarInt.peek(buffer, posxxxxxxxxxx); + if (conditionalSoundsCount < 0) { + return ValidationResult.error("Invalid array count for ConditionalSounds"); + } + + if (conditionalSoundsCount > 4096000) { + return ValidationResult.error("ConditionalSounds exceeds max length 4096000"); + } + + posxxxxxxxxxx += VarInt.size(conditionalSoundsCount); + posxxxxxxxxxx += conditionalSoundsCount * 8; + if (posxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ConditionalSounds"); + } + } + + if ((nullBits[2] & 4) != 0) { + v = buffer.getIntLE(offset + 212); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Particles"); + } + + int posxxxxxxxxxxx = offset + 268 + v; + int particlesCount = VarInt.peek(buffer, posxxxxxxxxxxx); + if (particlesCount < 0) { + return ValidationResult.error("Invalid array count for Particles"); + } + + if (particlesCount > 4096000) { + return ValidationResult.error("Particles exceeds max length 4096000"); + } + + posxxxxxxxxxxx += VarInt.size(particlesCount); + + for (int i = 0; i < particlesCount; i++) { + ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxxxxxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); + } + + posxxxxxxxxxxx += ModelParticle.computeBytesConsumed(buffer, posxxxxxxxxxxx); + } + } + + if ((nullBits[2] & 8) != 0) { + v = buffer.getIntLE(offset + 216); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for BlockParticleSetId"); + } + + int posxxxxxxxxxxxx = offset + 268 + v; + int blockParticleSetIdLen = VarInt.peek(buffer, posxxxxxxxxxxxx); + if (blockParticleSetIdLen < 0) { + return ValidationResult.error("Invalid string length for BlockParticleSetId"); + } + + if (blockParticleSetIdLen > 4096000) { + return ValidationResult.error("BlockParticleSetId exceeds max length 4096000"); + } + + posxxxxxxxxxxxx += VarInt.size(blockParticleSetIdLen); + posxxxxxxxxxxxx += blockParticleSetIdLen; + if (posxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading BlockParticleSetId"); + } + } + + if ((nullBits[2] & 16) != 0) { + v = buffer.getIntLE(offset + 220); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for BlockBreakingDecalId"); + } + + int posxxxxxxxxxxxxx = offset + 268 + v; + int blockBreakingDecalIdLen = VarInt.peek(buffer, posxxxxxxxxxxxxx); + if (blockBreakingDecalIdLen < 0) { + return ValidationResult.error("Invalid string length for BlockBreakingDecalId"); + } + + if (blockBreakingDecalIdLen > 4096000) { + return ValidationResult.error("BlockBreakingDecalId exceeds max length 4096000"); + } + + posxxxxxxxxxxxxx += VarInt.size(blockBreakingDecalIdLen); + posxxxxxxxxxxxxx += blockBreakingDecalIdLen; + if (posxxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading BlockBreakingDecalId"); + } + } + + if ((nullBits[2] & 32) != 0) { + v = buffer.getIntLE(offset + 224); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for TransitionTexture"); + } + + int posxxxxxxxxxxxxxx = offset + 268 + v; + int transitionTextureLen = VarInt.peek(buffer, posxxxxxxxxxxxxxx); + if (transitionTextureLen < 0) { + return ValidationResult.error("Invalid string length for TransitionTexture"); + } + + if (transitionTextureLen > 4096000) { + return ValidationResult.error("TransitionTexture exceeds max length 4096000"); + } + + posxxxxxxxxxxxxxx += VarInt.size(transitionTextureLen); + posxxxxxxxxxxxxxx += transitionTextureLen; + if (posxxxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TransitionTexture"); + } + } + + if ((nullBits[2] & 64) != 0) { + v = buffer.getIntLE(offset + 228); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for TransitionToGroups"); + } + + int posxxxxxxxxxxxxxxx = offset + 268 + v; + int transitionToGroupsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxx); + if (transitionToGroupsCount < 0) { + return ValidationResult.error("Invalid array count for TransitionToGroups"); + } + + if (transitionToGroupsCount > 4096000) { + return ValidationResult.error("TransitionToGroups exceeds max length 4096000"); + } + + posxxxxxxxxxxxxxxx += VarInt.size(transitionToGroupsCount); + posxxxxxxxxxxxxxxx += transitionToGroupsCount * 4; + if (posxxxxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TransitionToGroups"); + } + } + + if ((nullBits[2] & 128) != 0) { + v = buffer.getIntLE(offset + 232); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for InteractionHint"); + } + + int posxxxxxxxxxxxxxxxx = offset + 268 + v; + int interactionHintLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxx); + if (interactionHintLen < 0) { + return ValidationResult.error("Invalid string length for InteractionHint"); + } + + if (interactionHintLen > 4096000) { + return ValidationResult.error("InteractionHint exceeds max length 4096000"); + } + + posxxxxxxxxxxxxxxxx += VarInt.size(interactionHintLen); + posxxxxxxxxxxxxxxxx += interactionHintLen; + if (posxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading InteractionHint"); + } + } + + if ((nullBits[3] & 1) != 0) { + v = buffer.getIntLE(offset + 236); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Gathering"); + } + + int posxxxxxxxxxxxxxxxxx = offset + 268 + v; + ValidationResult gatheringResult = BlockGathering.validateStructure(buffer, posxxxxxxxxxxxxxxxxx); + if (!gatheringResult.isValid()) { + return ValidationResult.error("Invalid Gathering: " + gatheringResult.error()); + } + + posxxxxxxxxxxxxxxxxx += BlockGathering.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxx); + } + + if ((nullBits[3] & 2) != 0) { + v = buffer.getIntLE(offset + 240); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Display"); + } + + int posxxxxxxxxxxxxxxxxx = offset + 268 + v; + ValidationResult displayResult = ModelDisplay.validateStructure(buffer, posxxxxxxxxxxxxxxxxx); + if (!displayResult.isValid()) { + return ValidationResult.error("Invalid Display: " + displayResult.error()); + } + + posxxxxxxxxxxxxxxxxx += ModelDisplay.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxx); + } + + if ((nullBits[3] & 4) != 0) { + v = buffer.getIntLE(offset + 244); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Rail"); + } + + int posxxxxxxxxxxxxxxxxx = offset + 268 + v; + ValidationResult railResult = RailConfig.validateStructure(buffer, posxxxxxxxxxxxxxxxxx); + if (!railResult.isValid()) { + return ValidationResult.error("Invalid Rail: " + railResult.error()); + } + + posxxxxxxxxxxxxxxxxx += RailConfig.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxx); + } + + if ((nullBits[3] & 8) != 0) { + v = buffer.getIntLE(offset + 248); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Interactions"); + } + + int posxxxxxxxxxxxxxxxxx = offset + 268 + v; + int interactionsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxx); + if (interactionsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Interactions"); + } + + if (interactionsCount > 4096000) { + return ValidationResult.error("Interactions exceeds max length 4096000"); + } + + posxxxxxxxxxxxxxxxxx += VarInt.size(interactionsCount); + + for (int i = 0; i < interactionsCount; i++) { + int vxxx = buffer.getByte(posxxxxxxxxxxxxxxxxx) & 255; + if (vxxx >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); + } + + posxxxxxxxxxxxxxxxxx = ++posxxxxxxxxxxxxxxxxx + 4; + if (posxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + if ((nullBits[3] & 16) != 0) { + v = buffer.getIntLE(offset + 252); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for States"); + } + + int posxxxxxxxxxxxxxxxxxx = offset + 268 + v; + int statesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxx); + if (statesCount < 0) { + return ValidationResult.error("Invalid dictionary count for States"); + } + + if (statesCount > 4096000) { + return ValidationResult.error("States exceeds max length 4096000"); + } + + posxxxxxxxxxxxxxxxxxx += VarInt.size(statesCount); + + for (int i = 0; i < statesCount; i++) { + int keyLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxx); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxxxxxxxxxxxxxxxxx += VarInt.size(keyLen); + posxxxxxxxxxxxxxxxxxx += keyLen; + if (posxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxxxxxxxxxxxxxxxxxx += 4; + if (posxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + if ((nullBits[3] & 32) != 0) { + v = buffer.getIntLE(offset + 256); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for TagIndexes"); + } + + int posxxxxxxxxxxxxxxxxxxx = offset + 268 + v; + int tagIndexesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxx); + if (tagIndexesCount < 0) { + return ValidationResult.error("Invalid array count for TagIndexes"); + } + + if (tagIndexesCount > 4096000) { + return ValidationResult.error("TagIndexes exceeds max length 4096000"); + } + + posxxxxxxxxxxxxxxxxxxx += VarInt.size(tagIndexesCount); + posxxxxxxxxxxxxxxxxxxx += tagIndexesCount * 4; + if (posxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TagIndexes"); + } + } + + if ((nullBits[3] & 64) != 0) { + v = buffer.getIntLE(offset + 260); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for Bench"); + } + + int posxxxxxxxxxxxxxxxxxxxx = offset + 268 + v; + ValidationResult benchResult = Bench.validateStructure(buffer, posxxxxxxxxxxxxxxxxxxxx); + if (!benchResult.isValid()) { + return ValidationResult.error("Invalid Bench: " + benchResult.error()); + } + + posxxxxxxxxxxxxxxxxxxxx += Bench.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxxxx); + } + + if ((nullBits[3] & 128) != 0) { + v = buffer.getIntLE(offset + 264); + if (v < 0 || v > buffer.writerIndex() - offset - 268) { + return ValidationResult.error("Invalid offset for ConnectedBlockRuleSet"); + } + + int posxxxxxxxxxxxxxxxxxxxx = offset + 268 + v; + ValidationResult connectedBlockRuleSetResult = ConnectedBlockRuleSet.validateStructure(buffer, posxxxxxxxxxxxxxxxxxxxx); + if (!connectedBlockRuleSetResult.isValid()) { + return ValidationResult.error("Invalid ConnectedBlockRuleSet: " + connectedBlockRuleSetResult.error()); + } + + posxxxxxxxxxxxxxxxxxxxx += ConnectedBlockRuleSet.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxxxx); + } + + return ValidationResult.OK; + } + } + } + } + } } } } - - if ((nullBits[1] & 64) != 0) { - int supportingOffset = buffer.getIntLE(offset + 192); - if (supportingOffset < 0) { - return ValidationResult.error("Invalid offset for Supporting"); - } - - int posxxxxxxx = offset + 260 + supportingOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Supporting"); - } - - int supportingCount = VarInt.peek(buffer, posxxxxxxx); - if (supportingCount < 0) { - return ValidationResult.error("Invalid dictionary count for Supporting"); - } - - if (supportingCount > 4096000) { - return ValidationResult.error("Supporting exceeds max length 4096000"); - } - - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); - - for (int i = 0; i < supportingCount; i++) { - int valueArrCount = VarInt.peek(buffer, ++posxxxxxxx); - if (valueArrCount < 0) { - return ValidationResult.error("Invalid array count for value"); - } - - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); - - for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - posxxxxxxx += BlockFaceSupport.computeBytesConsumed(buffer, posxxxxxxx); - } - } - } - - if ((nullBits[1] & 128) != 0) { - int cubeTexturesOffset = buffer.getIntLE(offset + 196); - if (cubeTexturesOffset < 0) { - return ValidationResult.error("Invalid offset for CubeTextures"); - } - - int posxxxxxxxx = offset + 260 + cubeTexturesOffset; - if (posxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for CubeTextures"); - } - - int cubeTexturesCount = VarInt.peek(buffer, posxxxxxxxx); - if (cubeTexturesCount < 0) { - return ValidationResult.error("Invalid array count for CubeTextures"); - } - - if (cubeTexturesCount > 4096000) { - return ValidationResult.error("CubeTextures exceeds max length 4096000"); - } - - posxxxxxxxx += VarInt.length(buffer, posxxxxxxxx); - - for (int i = 0; i < cubeTexturesCount; i++) { - ValidationResult structResult = BlockTextures.validateStructure(buffer, posxxxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid BlockTextures in CubeTextures[" + i + "]: " + structResult.error()); - } - - posxxxxxxxx += BlockTextures.computeBytesConsumed(buffer, posxxxxxxxx); - } - } - - if ((nullBits[2] & 1) != 0) { - int cubeSideMaskTextureOffset = buffer.getIntLE(offset + 200); - if (cubeSideMaskTextureOffset < 0) { - return ValidationResult.error("Invalid offset for CubeSideMaskTexture"); - } - - int posxxxxxxxxx = offset + 260 + cubeSideMaskTextureOffset; - if (posxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for CubeSideMaskTexture"); - } - - int cubeSideMaskTextureLen = VarInt.peek(buffer, posxxxxxxxxx); - if (cubeSideMaskTextureLen < 0) { - return ValidationResult.error("Invalid string length for CubeSideMaskTexture"); - } - - if (cubeSideMaskTextureLen > 4096000) { - return ValidationResult.error("CubeSideMaskTexture exceeds max length 4096000"); - } - - posxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxx); - posxxxxxxxxx += cubeSideMaskTextureLen; - if (posxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading CubeSideMaskTexture"); - } - } - - if ((nullBits[2] & 2) != 0) { - int particlesOffset = buffer.getIntLE(offset + 204); - if (particlesOffset < 0) { - return ValidationResult.error("Invalid offset for Particles"); - } - - int posxxxxxxxxxx = offset + 260 + particlesOffset; - if (posxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particles"); - } - - int particlesCount = VarInt.peek(buffer, posxxxxxxxxxx); - if (particlesCount < 0) { - return ValidationResult.error("Invalid array count for Particles"); - } - - if (particlesCount > 4096000) { - return ValidationResult.error("Particles exceeds max length 4096000"); - } - - posxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxx); - - for (int i = 0; i < particlesCount; i++) { - ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxxxxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); - } - - posxxxxxxxxxx += ModelParticle.computeBytesConsumed(buffer, posxxxxxxxxxx); - } - } - - if ((nullBits[2] & 4) != 0) { - int blockParticleSetIdOffset = buffer.getIntLE(offset + 208); - if (blockParticleSetIdOffset < 0) { - return ValidationResult.error("Invalid offset for BlockParticleSetId"); - } - - int posxxxxxxxxxxx = offset + 260 + blockParticleSetIdOffset; - if (posxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockParticleSetId"); - } - - int blockParticleSetIdLen = VarInt.peek(buffer, posxxxxxxxxxxx); - if (blockParticleSetIdLen < 0) { - return ValidationResult.error("Invalid string length for BlockParticleSetId"); - } - - if (blockParticleSetIdLen > 4096000) { - return ValidationResult.error("BlockParticleSetId exceeds max length 4096000"); - } - - posxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxx); - posxxxxxxxxxxx += blockParticleSetIdLen; - if (posxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading BlockParticleSetId"); - } - } - - if ((nullBits[2] & 8) != 0) { - int blockBreakingDecalIdOffset = buffer.getIntLE(offset + 212); - if (blockBreakingDecalIdOffset < 0) { - return ValidationResult.error("Invalid offset for BlockBreakingDecalId"); - } - - int posxxxxxxxxxxxx = offset + 260 + blockBreakingDecalIdOffset; - if (posxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockBreakingDecalId"); - } - - int blockBreakingDecalIdLen = VarInt.peek(buffer, posxxxxxxxxxxxx); - if (blockBreakingDecalIdLen < 0) { - return ValidationResult.error("Invalid string length for BlockBreakingDecalId"); - } - - if (blockBreakingDecalIdLen > 4096000) { - return ValidationResult.error("BlockBreakingDecalId exceeds max length 4096000"); - } - - posxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxx); - posxxxxxxxxxxxx += blockBreakingDecalIdLen; - if (posxxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading BlockBreakingDecalId"); - } - } - - if ((nullBits[2] & 16) != 0) { - int transitionTextureOffset = buffer.getIntLE(offset + 216); - if (transitionTextureOffset < 0) { - return ValidationResult.error("Invalid offset for TransitionTexture"); - } - - int posxxxxxxxxxxxxx = offset + 260 + transitionTextureOffset; - if (posxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TransitionTexture"); - } - - int transitionTextureLen = VarInt.peek(buffer, posxxxxxxxxxxxxx); - if (transitionTextureLen < 0) { - return ValidationResult.error("Invalid string length for TransitionTexture"); - } - - if (transitionTextureLen > 4096000) { - return ValidationResult.error("TransitionTexture exceeds max length 4096000"); - } - - posxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxx); - posxxxxxxxxxxxxx += transitionTextureLen; - if (posxxxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading TransitionTexture"); - } - } - - if ((nullBits[2] & 32) != 0) { - int transitionToGroupsOffset = buffer.getIntLE(offset + 220); - if (transitionToGroupsOffset < 0) { - return ValidationResult.error("Invalid offset for TransitionToGroups"); - } - - int posxxxxxxxxxxxxxx = offset + 260 + transitionToGroupsOffset; - if (posxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TransitionToGroups"); - } - - int transitionToGroupsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxx); - if (transitionToGroupsCount < 0) { - return ValidationResult.error("Invalid array count for TransitionToGroups"); - } - - if (transitionToGroupsCount > 4096000) { - return ValidationResult.error("TransitionToGroups exceeds max length 4096000"); - } - - posxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxx += transitionToGroupsCount * 4; - if (posxxxxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading TransitionToGroups"); - } - } - - if ((nullBits[2] & 64) != 0) { - int interactionHintOffset = buffer.getIntLE(offset + 224); - if (interactionHintOffset < 0) { - return ValidationResult.error("Invalid offset for InteractionHint"); - } - - int posxxxxxxxxxxxxxxx = offset + 260 + interactionHintOffset; - if (posxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for InteractionHint"); - } - - int interactionHintLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxx); - if (interactionHintLen < 0) { - return ValidationResult.error("Invalid string length for InteractionHint"); - } - - if (interactionHintLen > 4096000) { - return ValidationResult.error("InteractionHint exceeds max length 4096000"); - } - - posxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxxx += interactionHintLen; - if (posxxxxxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading InteractionHint"); - } - } - - if ((nullBits[2] & 128) != 0) { - int gatheringOffset = buffer.getIntLE(offset + 228); - if (gatheringOffset < 0) { - return ValidationResult.error("Invalid offset for Gathering"); - } - - int posxxxxxxxxxxxxxxxx = offset + 260 + gatheringOffset; - if (posxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Gathering"); - } - - ValidationResult gatheringResult = BlockGathering.validateStructure(buffer, posxxxxxxxxxxxxxxxx); - if (!gatheringResult.isValid()) { - return ValidationResult.error("Invalid Gathering: " + gatheringResult.error()); - } - - posxxxxxxxxxxxxxxxx += BlockGathering.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxx); - } - - if ((nullBits[3] & 1) != 0) { - int displayOffset = buffer.getIntLE(offset + 232); - if (displayOffset < 0) { - return ValidationResult.error("Invalid offset for Display"); - } - - int posxxxxxxxxxxxxxxxxx = offset + 260 + displayOffset; - if (posxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Display"); - } - - ValidationResult displayResult = ModelDisplay.validateStructure(buffer, posxxxxxxxxxxxxxxxxx); - if (!displayResult.isValid()) { - return ValidationResult.error("Invalid Display: " + displayResult.error()); - } - - posxxxxxxxxxxxxxxxxx += ModelDisplay.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxx); - } - - if ((nullBits[3] & 2) != 0) { - int railOffset = buffer.getIntLE(offset + 236); - if (railOffset < 0) { - return ValidationResult.error("Invalid offset for Rail"); - } - - int posxxxxxxxxxxxxxxxxxx = offset + 260 + railOffset; - if (posxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rail"); - } - - ValidationResult railResult = RailConfig.validateStructure(buffer, posxxxxxxxxxxxxxxxxxx); - if (!railResult.isValid()) { - return ValidationResult.error("Invalid Rail: " + railResult.error()); - } - - posxxxxxxxxxxxxxxxxxx += RailConfig.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxx); - } - - if ((nullBits[3] & 4) != 0) { - int interactionsOffset = buffer.getIntLE(offset + 240); - if (interactionsOffset < 0) { - return ValidationResult.error("Invalid offset for Interactions"); - } - - int posxxxxxxxxxxxxxxxxxxx = offset + 260 + interactionsOffset; - if (posxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Interactions"); - } - - int interactionsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxx); - if (interactionsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Interactions"); - } - - if (interactionsCount > 4096000) { - return ValidationResult.error("Interactions exceeds max length 4096000"); - } - - posxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxx); - - for (int i = 0; i < interactionsCount; i++) { - posxxxxxxxxxxxxxxxxxxx = ++posxxxxxxxxxxxxxxxxxxx + 4; - if (posxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); - } - } - } - - if ((nullBits[3] & 8) != 0) { - int statesOffset = buffer.getIntLE(offset + 244); - if (statesOffset < 0) { - return ValidationResult.error("Invalid offset for States"); - } - - int posxxxxxxxxxxxxxxxxxxxx = offset + 260 + statesOffset; - if (posxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for States"); - } - - int statesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxx); - if (statesCount < 0) { - return ValidationResult.error("Invalid dictionary count for States"); - } - - if (statesCount > 4096000) { - return ValidationResult.error("States exceeds max length 4096000"); - } - - posxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxx); - - for (int ix = 0; ix < statesCount; ix++) { - int keyLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxxxxxxxx += keyLen; - if (posxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - posxxxxxxxxxxxxxxxxxxxx += 4; - if (posxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); - } - } - } - - if ((nullBits[3] & 16) != 0) { - int tagIndexesOffset = buffer.getIntLE(offset + 248); - if (tagIndexesOffset < 0) { - return ValidationResult.error("Invalid offset for TagIndexes"); - } - - int posxxxxxxxxxxxxxxxxxxxxx = offset + 260 + tagIndexesOffset; - if (posxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TagIndexes"); - } - - int tagIndexesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxxx); - if (tagIndexesCount < 0) { - return ValidationResult.error("Invalid array count for TagIndexes"); - } - - if (tagIndexesCount > 4096000) { - return ValidationResult.error("TagIndexes exceeds max length 4096000"); - } - - posxxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxxxxxxxxx += tagIndexesCount * 4; - if (posxxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading TagIndexes"); - } - } - - if ((nullBits[3] & 32) != 0) { - int benchOffset = buffer.getIntLE(offset + 252); - if (benchOffset < 0) { - return ValidationResult.error("Invalid offset for Bench"); - } - - int posxxxxxxxxxxxxxxxxxxxxxx = offset + 260 + benchOffset; - if (posxxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Bench"); - } - - ValidationResult benchResult = Bench.validateStructure(buffer, posxxxxxxxxxxxxxxxxxxxxxx); - if (!benchResult.isValid()) { - return ValidationResult.error("Invalid Bench: " + benchResult.error()); - } - - posxxxxxxxxxxxxxxxxxxxxxx += Bench.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxxxxxx); - } - - if ((nullBits[3] & 64) != 0) { - int connectedBlockRuleSetOffset = buffer.getIntLE(offset + 256); - if (connectedBlockRuleSetOffset < 0) { - return ValidationResult.error("Invalid offset for ConnectedBlockRuleSet"); - } - - int posxxxxxxxxxxxxxxxxxxxxxxx = offset + 260 + connectedBlockRuleSetOffset; - if (posxxxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ConnectedBlockRuleSet"); - } - - ValidationResult connectedBlockRuleSetResult = ConnectedBlockRuleSet.validateStructure(buffer, posxxxxxxxxxxxxxxxxxxxxxxx); - if (!connectedBlockRuleSetResult.isValid()) { - return ValidationResult.error("Invalid ConnectedBlockRuleSet: " + connectedBlockRuleSetResult.error()); - } - - posxxxxxxxxxxxxxxxxxxxxxxx += ConnectedBlockRuleSet.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxxxxxxx); - } - - return ValidationResult.OK; } } @@ -2418,7 +2766,11 @@ public class BlockType { copy.variantRotation = this.variantRotation; copy.rotationYawPlacementOffset = this.rotationYawPlacementOffset; copy.blockSoundSetIndex = this.blockSoundSetIndex; + copy.physicalMaterialIndex = this.physicalMaterialIndex; copy.ambientSoundEventIndex = this.ambientSoundEventIndex; + copy.conditionalSounds = this.conditionalSounds != null + ? Arrays.stream(this.conditionalSounds).map(ex -> ex.clone()).toArray(ConditionalBlockSound[]::new) + : null; copy.particles = this.particles != null ? Arrays.stream(this.particles).map(ex -> ex.clone()).toArray(ModelParticle[]::new) : null; copy.blockParticleSetId = this.blockParticleSetId; copy.blockBreakingDecalId = this.blockBreakingDecalId; @@ -2479,7 +2831,9 @@ public class BlockType { && Objects.equals(this.variantRotation, other.variantRotation) && Objects.equals(this.rotationYawPlacementOffset, other.rotationYawPlacementOffset) && this.blockSoundSetIndex == other.blockSoundSetIndex + && this.physicalMaterialIndex == other.physicalMaterialIndex && this.ambientSoundEventIndex == other.ambientSoundEventIndex + && Arrays.equals((Object[])this.conditionalSounds, (Object[])other.conditionalSounds) && Arrays.equals((Object[])this.particles, (Object[])other.particles) && Objects.equals(this.blockParticleSetId, other.blockParticleSetId) && Objects.equals(this.blockBreakingDecalId, other.blockBreakingDecalId) @@ -2536,7 +2890,9 @@ public class BlockType { result = 31 * result + Objects.hashCode(this.variantRotation); result = 31 * result + Objects.hashCode(this.rotationYawPlacementOffset); result = 31 * result + Integer.hashCode(this.blockSoundSetIndex); + result = 31 * result + Integer.hashCode(this.physicalMaterialIndex); result = 31 * result + Integer.hashCode(this.ambientSoundEventIndex); + result = 31 * result + Arrays.hashCode((Object[])this.conditionalSounds); result = 31 * result + Arrays.hashCode((Object[])this.particles); result = 31 * result + Objects.hashCode(this.blockParticleSetId); result = 31 * result + Objects.hashCode(this.blockBreakingDecalId); diff --git a/src/com/hypixel/hytale/protocol/BlockUpdate.java b/src/com/hypixel/hytale/protocol/BlockUpdate.java index 44c27935..ddee0c82 100644 --- a/src/com/hypixel/hytale/protocol/BlockUpdate.java +++ b/src/com/hypixel/hytale/protocol/BlockUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class BlockUpdate extends ComponentUpdate { @Nonnull public static BlockUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - BlockUpdate obj = new BlockUpdate(); - obj.blockId = buf.getIntLE(offset + 0); - obj.entityScale = buf.getFloatLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("BlockUpdate", 8, buf.readableBytes() - offset); + } else { + BlockUpdate obj = new BlockUpdate(); + obj.blockId = buf.getIntLE(offset + 0); + obj.entityScale = buf.getFloatLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/BoolParamValue.java b/src/com/hypixel/hytale/protocol/BoolParamValue.java index 38f41e00..6083f01f 100644 --- a/src/com/hypixel/hytale/protocol/BoolParamValue.java +++ b/src/com/hypixel/hytale/protocol/BoolParamValue.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class BoolParamValue extends ParamValue { @Nonnull public static BoolParamValue deserialize(@Nonnull ByteBuf buf, int offset) { - BoolParamValue obj = new BoolParamValue(); - obj.value = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BoolParamValue", 1, buf.readableBytes() - offset); + } else { + BoolParamValue obj = new BoolParamValue(); + obj.value = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/BreakBlockInteraction.java b/src/com/hypixel/hytale/protocol/BreakBlockInteraction.java index b10ba379..325a8f24 100644 --- a/src/com/hypixel/hytale/protocol/BreakBlockInteraction.java +++ b/src/com/hypixel/hytale/protocol/BreakBlockInteraction.java @@ -71,80 +71,109 @@ public class BreakBlockInteraction extends SimpleBlockInteraction { @Nonnull public static BreakBlockInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - BreakBlockInteraction obj = new BreakBlockInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.useLatestTarget = buf.getByte(offset + 19) != 0; - obj.harvest = buf.getByte(offset + 20) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 41 + buf.getIntLE(offset + 21); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 41) { + throw ProtocolException.bufferTooSmall("BreakBlockInteraction", 41, buf.readableBytes() - offset); + } else { + BreakBlockInteraction obj = new BreakBlockInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.useLatestTarget = buf.getByte(offset + 19) != 0; + obj.harvest = buf.getByte(offset + 20) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 21); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 41 + buf.getIntLE(offset + 25); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 41 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 25); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 41 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 41 + buf.getIntLE(offset + 29); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 29); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 41 + buf.getIntLE(offset + 33); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 41 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 33); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 41 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 37); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 41 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 41 + buf.getIntLE(offset + 37); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -152,6 +181,10 @@ public class BreakBlockInteraction extends SimpleBlockInteraction { int maxEnd = 41; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 21); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 41 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -161,9 +194,13 @@ public class BreakBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 25); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 41 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -176,6 +213,10 @@ public class BreakBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 29); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 41 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -185,9 +226,13 @@ public class BreakBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 33); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 41 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -195,6 +240,10 @@ public class BreakBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 37); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 41 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -335,119 +384,109 @@ public class BreakBlockInteraction extends SimpleBlockInteraction { return ValidationResult.error("Buffer too small: expected at least 41 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 21); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 21); + if (v < 0 || v > buffer.writerIndex() - offset - 41) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 41 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 41 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 25); + if (v < 0 || v > buffer.writerIndex() - offset - 41) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 41 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 29); + if (v < 0 || v > buffer.writerIndex() - offset - 41) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 41 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 41) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 41 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 41) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 41 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 25); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 41 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 29); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 41 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 33); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 41 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 37); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 41 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/BuilderToolInteraction.java b/src/com/hypixel/hytale/protocol/BuilderToolInteraction.java index 6ba2524e..9b006920 100644 --- a/src/com/hypixel/hytale/protocol/BuilderToolInteraction.java +++ b/src/com/hypixel/hytale/protocol/BuilderToolInteraction.java @@ -64,78 +64,107 @@ public class BuilderToolInteraction extends SimpleInteraction { @Nonnull public static BuilderToolInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolInteraction obj = new BuilderToolInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 39 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 39) { + throw ProtocolException.bufferTooSmall("BuilderToolInteraction", 39, buf.readableBytes() - offset); + } else { + BuilderToolInteraction obj = new BuilderToolInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 39 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 39 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 39 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 39 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 39 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 39 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 39 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 39 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 39 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -143,6 +172,10 @@ public class BuilderToolInteraction extends SimpleInteraction { int maxEnd = 39; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 39 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -152,9 +185,13 @@ public class BuilderToolInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 39 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -167,6 +204,10 @@ public class BuilderToolInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 39 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -176,9 +217,13 @@ public class BuilderToolInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 39 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -186,6 +231,10 @@ public class BuilderToolInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 39 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -324,119 +373,109 @@ public class BuilderToolInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 39 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 39 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 39 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 39 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 39 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 39 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 39 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 39 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 39 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 39 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 39 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/CameraAxis.java b/src/com/hypixel/hytale/protocol/CameraAxis.java index 2eb6c9cd..876e42b0 100644 --- a/src/com/hypixel/hytale/protocol/CameraAxis.java +++ b/src/com/hypixel/hytale/protocol/CameraAxis.java @@ -35,38 +35,42 @@ public class CameraAxis { @Nonnull public static CameraAxis deserialize(@Nonnull ByteBuf buf, int offset) { - CameraAxis obj = new CameraAxis(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.angleRange = Rangef.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("CameraAxis", 9, buf.readableBytes() - offset); + } else { + CameraAxis obj = new CameraAxis(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.angleRange = Rangef.deserialize(buf, offset + 1); + } + + int pos = offset + 9; + if ((nullBits & 2) != 0) { + int targetNodesCount = VarInt.peek(buf, pos); + if (targetNodesCount < 0) { + throw ProtocolException.invalidVarInt("TargetNodes"); + } + + int targetNodesVarLen = VarInt.size(targetNodesCount); + if (targetNodesCount > 4096000) { + throw ProtocolException.arrayTooLong("TargetNodes", targetNodesCount, 4096000); + } + + if (pos + targetNodesVarLen + targetNodesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TargetNodes", pos + targetNodesVarLen + targetNodesCount * 1, buf.readableBytes()); + } + + pos += targetNodesVarLen; + obj.targetNodes = new CameraNode[targetNodesCount]; + + for (int i = 0; i < targetNodesCount; i++) { + obj.targetNodes[i] = CameraNode.fromValue(buf.getByte(pos)); + pos++; + } + } + + return obj; } - - int pos = offset + 9; - if ((nullBits & 2) != 0) { - int targetNodesCount = VarInt.peek(buf, pos); - if (targetNodesCount < 0) { - throw ProtocolException.negativeLength("TargetNodes", targetNodesCount); - } - - if (targetNodesCount > 4096000) { - throw ProtocolException.arrayTooLong("TargetNodes", targetNodesCount, 4096000); - } - - int targetNodesVarLen = VarInt.size(targetNodesCount); - if (pos + targetNodesVarLen + targetNodesCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("TargetNodes", pos + targetNodesVarLen + targetNodesCount * 1, buf.readableBytes()); - } - - pos += targetNodesVarLen; - obj.targetNodes = new CameraNode[targetNodesCount]; - - for (int i = 0; i < targetNodesCount; i++) { - obj.targetNodes[i] = CameraNode.fromValue(buf.getByte(pos)); - pos++; - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -74,7 +78,7 @@ public class CameraAxis { int pos = offset + 9; if ((nullBits & 2) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -135,11 +139,19 @@ public class CameraAxis { return ValidationResult.error("TargetNodes exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += targetNodesCount * 1; - if (pos > buffer.writerIndex()) { + pos += VarInt.size(targetNodesCount); + if (pos + targetNodesCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading TargetNodes"); } + + for (int i = 0; i < targetNodesCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid CameraNode value for TargetNodes[i]"); + } + + pos++; + } } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/CameraInteraction.java b/src/com/hypixel/hytale/protocol/CameraInteraction.java index 53d92556..81a868e4 100644 --- a/src/com/hypixel/hytale/protocol/CameraInteraction.java +++ b/src/com/hypixel/hytale/protocol/CameraInteraction.java @@ -82,82 +82,111 @@ public class CameraInteraction extends SimpleInteraction { @Nonnull public static CameraInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - CameraInteraction obj = new CameraInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.cameraAction = CameraActionType.fromValue(buf.getByte(offset + 19)); - obj.cameraPerspective = CameraPerspectiveType.fromValue(buf.getByte(offset + 20)); - obj.cameraPersist = buf.getByte(offset + 21) != 0; - obj.cameraInteractionTime = buf.getFloatLE(offset + 22); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 46 + buf.getIntLE(offset + 26); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 46) { + throw ProtocolException.bufferTooSmall("CameraInteraction", 46, buf.readableBytes() - offset); + } else { + CameraInteraction obj = new CameraInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.cameraAction = CameraActionType.fromValue(buf.getByte(offset + 19)); + obj.cameraPerspective = CameraPerspectiveType.fromValue(buf.getByte(offset + 20)); + obj.cameraPersist = buf.getByte(offset + 21) != 0; + obj.cameraInteractionTime = buf.getFloatLE(offset + 22); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 26); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 46 + buf.getIntLE(offset + 30); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 46 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 30); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 46 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 46 + buf.getIntLE(offset + 34); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 34); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 46 + buf.getIntLE(offset + 38); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 46 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 38); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 46 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 42); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 46 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 46 + buf.getIntLE(offset + 42); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -165,6 +194,10 @@ public class CameraInteraction extends SimpleInteraction { int maxEnd = 46; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 26); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 46 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -174,9 +207,13 @@ public class CameraInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 30); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 46 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -189,6 +226,10 @@ public class CameraInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 34); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 46 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -198,9 +239,13 @@ public class CameraInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 38); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 46 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -208,6 +253,10 @@ public class CameraInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 42); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 46 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -350,119 +399,119 @@ public class CameraInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 46 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 26); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 19) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid CameraActionType value for CameraAction"); + } else { + v = buffer.getByte(offset + 20) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid CameraPerspectiveType value for CameraPerspective"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 26); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 46 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 46 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 30); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Settings"); + } - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 30); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } + int pos = offset + 46 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } - int posx = offset + 46 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } + pos += VarInt.size(settingsCount); - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } - posx += VarInt.length(buffer, posx); + pos++; + pos++; + } + } - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 34); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 46 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 38); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 46 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 42); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 46 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; + } } } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 34); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 46 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 38); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 46 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 42); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 46 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/CameraSettings.java b/src/com/hypixel/hytale/protocol/CameraSettings.java index eb33b734..217d6afa 100644 --- a/src/com/hypixel/hytale/protocol/CameraSettings.java +++ b/src/com/hypixel/hytale/protocol/CameraSettings.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class CameraSettings { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -13,7 +16,7 @@ public class CameraSettings { public static final int VARIABLE_BLOCK_START = 21; public static final int MAX_SIZE = 8192049; @Nullable - public Vector3f positionOffset; + public Vector3fc positionOffset; @Nullable public CameraAxis yaw; @Nullable @@ -22,7 +25,7 @@ public class CameraSettings { public CameraSettings() { } - public CameraSettings(@Nullable Vector3f positionOffset, @Nullable CameraAxis yaw, @Nullable CameraAxis pitch) { + public CameraSettings(@Nullable Vector3fc positionOffset, @Nullable CameraAxis yaw, @Nullable CameraAxis pitch) { this.positionOffset = positionOffset; this.yaw = yaw; this.pitch = pitch; @@ -36,23 +39,37 @@ public class CameraSettings { @Nonnull public static CameraSettings deserialize(@Nonnull ByteBuf buf, int offset) { - CameraSettings obj = new CameraSettings(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.positionOffset = Vector3f.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("CameraSettings", 21, buf.readableBytes() - offset); + } else { + CameraSettings obj = new CameraSettings(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.positionOffset = PacketIO.readVector3f(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - int varPos0 = offset + 21 + buf.getIntLE(offset + 13); - obj.yaw = CameraAxis.deserialize(buf, varPos0); - } + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 13); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Yaw", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 4) != 0) { - int varPos1 = offset + 21 + buf.getIntLE(offset + 17); - obj.pitch = CameraAxis.deserialize(buf, varPos1); - } + int varPos0 = offset + 21 + varPosBase0; + obj.yaw = CameraAxis.deserialize(buf, varPos0); + } - return obj; + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 17); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Pitch", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + obj.pitch = CameraAxis.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -60,6 +77,10 @@ public class CameraSettings { int maxEnd = 21; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 13); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Yaw", fieldOffset0, maxEnd); + } + int pos0 = offset + 21 + fieldOffset0; pos0 += CameraAxis.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -69,6 +90,10 @@ public class CameraSettings { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 17); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Pitch", fieldOffset1, maxEnd); + } + int pos1 = offset + 21 + fieldOffset1; pos1 += CameraAxis.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -96,7 +121,7 @@ public class CameraSettings { buf.writeByte(nullBits); if (this.positionOffset != null) { - this.positionOffset.serialize(buf); + PacketIO.writeVector3f(buf, this.positionOffset); } else { buf.writeZero(12); } @@ -141,15 +166,11 @@ public class CameraSettings { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { int yawOffset = buffer.getIntLE(offset + 13); - if (yawOffset < 0) { + if (yawOffset < 0 || yawOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Yaw"); } int pos = offset + 21 + yawOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Yaw"); - } - ValidationResult yawResult = CameraAxis.validateStructure(buffer, pos); if (!yawResult.isValid()) { return ValidationResult.error("Invalid Yaw: " + yawResult.error()); @@ -160,21 +181,17 @@ public class CameraSettings { if ((nullBits & 4) != 0) { int pitchOffset = buffer.getIntLE(offset + 17); - if (pitchOffset < 0) { + if (pitchOffset < 0 || pitchOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Pitch"); } - int posx = offset + 21 + pitchOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Pitch"); - } - - ValidationResult pitchResult = CameraAxis.validateStructure(buffer, posx); + int pos = offset + 21 + pitchOffset; + ValidationResult pitchResult = CameraAxis.validateStructure(buffer, pos); if (!pitchResult.isValid()) { return ValidationResult.error("Invalid Pitch: " + pitchResult.error()); } - posx += CameraAxis.computeBytesConsumed(buffer, posx); + pos += CameraAxis.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; @@ -183,7 +200,7 @@ public class CameraSettings { public CameraSettings clone() { CameraSettings copy = new CameraSettings(); - copy.positionOffset = this.positionOffset != null ? this.positionOffset.clone() : null; + copy.positionOffset = this.positionOffset; copy.yaw = this.yaw != null ? this.yaw.clone() : null; copy.pitch = this.pitch != null ? this.pitch.clone() : null; return copy; diff --git a/src/com/hypixel/hytale/protocol/CameraShake.java b/src/com/hypixel/hytale/protocol/CameraShake.java index 9937ce1c..3639cfad 100644 --- a/src/com/hypixel/hytale/protocol/CameraShake.java +++ b/src/com/hypixel/hytale/protocol/CameraShake.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,19 +33,33 @@ public class CameraShake { @Nonnull public static CameraShake deserialize(@Nonnull ByteBuf buf, int offset) { - CameraShake obj = new CameraShake(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - obj.firstPerson = CameraShakeConfig.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("CameraShake", 9, buf.readableBytes() - offset); + } else { + CameraShake obj = new CameraShake(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("FirstPerson", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - obj.thirdPerson = CameraShakeConfig.deserialize(buf, varPos1); - } + int varPos0 = offset + 9 + varPosBase0; + obj.firstPerson = CameraShakeConfig.deserialize(buf, varPos0); + } - return obj; + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ThirdPerson", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + obj.thirdPerson = CameraShakeConfig.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -52,6 +67,10 @@ public class CameraShake { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("FirstPerson", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; pos0 += CameraShakeConfig.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -61,6 +80,10 @@ public class CameraShake { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ThirdPerson", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; pos1 += CameraShakeConfig.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -123,15 +146,11 @@ public class CameraShake { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int firstPersonOffset = buffer.getIntLE(offset + 1); - if (firstPersonOffset < 0) { + if (firstPersonOffset < 0 || firstPersonOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for FirstPerson"); } int pos = offset + 9 + firstPersonOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstPerson"); - } - ValidationResult firstPersonResult = CameraShakeConfig.validateStructure(buffer, pos); if (!firstPersonResult.isValid()) { return ValidationResult.error("Invalid FirstPerson: " + firstPersonResult.error()); @@ -142,21 +161,17 @@ public class CameraShake { if ((nullBits & 2) != 0) { int thirdPersonOffset = buffer.getIntLE(offset + 5); - if (thirdPersonOffset < 0) { + if (thirdPersonOffset < 0 || thirdPersonOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for ThirdPerson"); } - int posx = offset + 9 + thirdPersonOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ThirdPerson"); - } - - ValidationResult thirdPersonResult = CameraShakeConfig.validateStructure(buffer, posx); + int pos = offset + 9 + thirdPersonOffset; + ValidationResult thirdPersonResult = CameraShakeConfig.validateStructure(buffer, pos); if (!thirdPersonResult.isValid()) { return ValidationResult.error("Invalid ThirdPerson: " + thirdPersonResult.error()); } - posx += CameraShakeConfig.computeBytesConsumed(buffer, posx); + pos += CameraShakeConfig.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/CameraShakeConfig.java b/src/com/hypixel/hytale/protocol/CameraShakeConfig.java index a8fdc902..b873809f 100644 --- a/src/com/hypixel/hytale/protocol/CameraShakeConfig.java +++ b/src/com/hypixel/hytale/protocol/CameraShakeConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -57,30 +58,44 @@ public class CameraShakeConfig { @Nonnull public static CameraShakeConfig deserialize(@Nonnull ByteBuf buf, int offset) { - CameraShakeConfig obj = new CameraShakeConfig(); - byte nullBits = buf.getByte(offset); - obj.duration = buf.getFloatLE(offset + 1); - obj.startTime = buf.getFloatLE(offset + 5); - obj.continuous = buf.getByte(offset + 9) != 0; - if ((nullBits & 1) != 0) { - obj.easeIn = EasingConfig.deserialize(buf, offset + 10); - } + if (buf.readableBytes() - offset < 28) { + throw ProtocolException.bufferTooSmall("CameraShakeConfig", 28, buf.readableBytes() - offset); + } else { + CameraShakeConfig obj = new CameraShakeConfig(); + byte nullBits = buf.getByte(offset); + obj.duration = buf.getFloatLE(offset + 1); + obj.startTime = buf.getFloatLE(offset + 5); + obj.continuous = buf.getByte(offset + 9) != 0; + if ((nullBits & 1) != 0) { + obj.easeIn = EasingConfig.deserialize(buf, offset + 10); + } - if ((nullBits & 2) != 0) { - obj.easeOut = EasingConfig.deserialize(buf, offset + 15); - } + if ((nullBits & 2) != 0) { + obj.easeOut = EasingConfig.deserialize(buf, offset + 15); + } - if ((nullBits & 4) != 0) { - int varPos0 = offset + 28 + buf.getIntLE(offset + 20); - obj.offset = OffsetNoise.deserialize(buf, varPos0); - } + if ((nullBits & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 20); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 28) { + throw ProtocolException.invalidOffset("Offset", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos1 = offset + 28 + buf.getIntLE(offset + 24); - obj.rotation = RotationNoise.deserialize(buf, varPos1); - } + int varPos0 = offset + 28 + varPosBase0; + obj.offset = OffsetNoise.deserialize(buf, varPos0); + } - return obj; + if ((nullBits & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 24); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 28) { + throw ProtocolException.invalidOffset("Rotation", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 28 + varPosBase1; + obj.rotation = RotationNoise.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -88,6 +103,10 @@ public class CameraShakeConfig { int maxEnd = 28; if ((nullBits & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 20); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 28) { + throw ProtocolException.invalidOffset("Offset", fieldOffset0, maxEnd); + } + int pos0 = offset + 28 + fieldOffset0; pos0 += OffsetNoise.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -97,6 +116,10 @@ public class CameraShakeConfig { if ((nullBits & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 24); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 28) { + throw ProtocolException.invalidOffset("Rotation", fieldOffset1, maxEnd); + } + int pos1 = offset + 28 + fieldOffset1; pos1 += RotationNoise.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -182,15 +205,11 @@ public class CameraShakeConfig { byte nullBits = buffer.getByte(offset); if ((nullBits & 4) != 0) { int offsetOffset = buffer.getIntLE(offset + 20); - if (offsetOffset < 0) { + if (offsetOffset < 0 || offsetOffset > buffer.writerIndex() - offset - 28) { return ValidationResult.error("Invalid offset for Offset"); } int pos = offset + 28 + offsetOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Offset"); - } - ValidationResult offsetResult = OffsetNoise.validateStructure(buffer, pos); if (!offsetResult.isValid()) { return ValidationResult.error("Invalid Offset: " + offsetResult.error()); @@ -201,21 +220,17 @@ public class CameraShakeConfig { if ((nullBits & 8) != 0) { int rotationOffset = buffer.getIntLE(offset + 24); - if (rotationOffset < 0) { + if (rotationOffset < 0 || rotationOffset > buffer.writerIndex() - offset - 28) { return ValidationResult.error("Invalid offset for Rotation"); } - int posx = offset + 28 + rotationOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rotation"); - } - - ValidationResult rotationResult = RotationNoise.validateStructure(buffer, posx); + int pos = offset + 28 + rotationOffset; + ValidationResult rotationResult = RotationNoise.validateStructure(buffer, pos); if (!rotationResult.isValid()) { return ValidationResult.error("Invalid Rotation: " + rotationResult.error()); } - posx += RotationNoise.computeBytesConsumed(buffer, posx); + pos += RotationNoise.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/CancelChainInteraction.java b/src/com/hypixel/hytale/protocol/CancelChainInteraction.java index 317c1d44..35eb26f7 100644 --- a/src/com/hypixel/hytale/protocol/CancelChainInteraction.java +++ b/src/com/hypixel/hytale/protocol/CancelChainInteraction.java @@ -70,92 +70,131 @@ public class CancelChainInteraction extends SimpleInteraction { @Nonnull public static CancelChainInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - CancelChainInteraction obj = new CancelChainInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 43 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 43) { + throw ProtocolException.bufferTooSmall("CancelChainInteraction", 43, buf.readableBytes() - offset); + } else { + CancelChainInteraction obj = new CancelChainInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 43 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 43 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 43 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 43 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 43 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 43 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 39); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("ChainId", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 43 + varPosBase5; + int chainIdLen = VarInt.peek(buf, varPos5); + if (chainIdLen < 0) { + throw ProtocolException.invalidVarInt("ChainId"); + } + + int chainIdVarIntLen = VarInt.size(chainIdLen); + if (chainIdLen > 4096000) { + throw ProtocolException.stringTooLong("ChainId", chainIdLen, 4096000); + } + + if (varPos5 + chainIdVarIntLen + chainIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ChainId", varPos5 + chainIdVarIntLen + chainIdLen, buf.readableBytes()); + } + + obj.chainId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 43 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 43 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 43 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 43 + buf.getIntLE(offset + 39); - int chainIdLen = VarInt.peek(buf, varPos5); - if (chainIdLen < 0) { - throw ProtocolException.negativeLength("ChainId", chainIdLen); - } - - if (chainIdLen > 4096000) { - throw ProtocolException.stringTooLong("ChainId", chainIdLen, 4096000); - } - - obj.chainId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -163,6 +202,10 @@ public class CancelChainInteraction extends SimpleInteraction { int maxEnd = 43; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 43 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -172,9 +215,13 @@ public class CancelChainInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 43 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -187,6 +234,10 @@ public class CancelChainInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 43 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -196,9 +247,13 @@ public class CancelChainInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 43 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -206,6 +261,10 @@ public class CancelChainInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 43 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -215,9 +274,13 @@ public class CancelChainInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 39); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("ChainId", fieldOffset5, maxEnd); + } + int pos5 = offset + 43 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -371,146 +434,132 @@ public class CancelChainInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 43 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 43 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 43 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 43 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 43 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 43 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 43 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for ChainId"); + } + + int posxx = offset + 43 + v; + int chainIdLen = VarInt.peek(buffer, posxx); + if (chainIdLen < 0) { + return ValidationResult.error("Invalid string length for ChainId"); + } + + if (chainIdLen > 4096000) { + return ValidationResult.error("ChainId exceeds max length 4096000"); + } + + posxx += VarInt.size(chainIdLen); + posxx += chainIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ChainId"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 43 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 43 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 43 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 43 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int chainIdOffset = buffer.getIntLE(offset + 39); - if (chainIdOffset < 0) { - return ValidationResult.error("Invalid offset for ChainId"); - } - - int posxxxxx = offset + 43 + chainIdOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ChainId"); - } - - int chainIdLen = VarInt.peek(buffer, posxxxxx); - if (chainIdLen < 0) { - return ValidationResult.error("Invalid string length for ChainId"); - } - - if (chainIdLen > 4096000) { - return ValidationResult.error("ChainId exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += chainIdLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ChainId"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ChainFlagInteraction.java b/src/com/hypixel/hytale/protocol/ChainFlagInteraction.java index 337ffa68..fe71ed1c 100644 --- a/src/com/hypixel/hytale/protocol/ChainFlagInteraction.java +++ b/src/com/hypixel/hytale/protocol/ChainFlagInteraction.java @@ -75,106 +75,155 @@ public class ChainFlagInteraction extends SimpleInteraction { @Nonnull public static ChainFlagInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ChainFlagInteraction obj = new ChainFlagInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 47 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 47) { + throw ProtocolException.bufferTooSmall("ChainFlagInteraction", 47, buf.readableBytes() - offset); + } else { + ChainFlagInteraction obj = new ChainFlagInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 47 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 47 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 47 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 47 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 47 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 47 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 39); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("ChainId", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 47 + varPosBase5; + int chainIdLen = VarInt.peek(buf, varPos5); + if (chainIdLen < 0) { + throw ProtocolException.invalidVarInt("ChainId"); + } + + int chainIdVarIntLen = VarInt.size(chainIdLen); + if (chainIdLen > 4096000) { + throw ProtocolException.stringTooLong("ChainId", chainIdLen, 4096000); + } + + if (varPos5 + chainIdVarIntLen + chainIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ChainId", varPos5 + chainIdVarIntLen + chainIdLen, buf.readableBytes()); + } + + obj.chainId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + if ((nullBits & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 43); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Flag", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 47 + varPosBase6; + int flagLen = VarInt.peek(buf, varPos6); + if (flagLen < 0) { + throw ProtocolException.invalidVarInt("Flag"); + } + + int flagVarIntLen = VarInt.size(flagLen); + if (flagLen > 4096000) { + throw ProtocolException.stringTooLong("Flag", flagLen, 4096000); + } + + if (varPos6 + flagVarIntLen + flagLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Flag", varPos6 + flagVarIntLen + flagLen, buf.readableBytes()); + } + + obj.flag = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 47 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 47 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 47 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 47 + buf.getIntLE(offset + 39); - int chainIdLen = VarInt.peek(buf, varPos5); - if (chainIdLen < 0) { - throw ProtocolException.negativeLength("ChainId", chainIdLen); - } - - if (chainIdLen > 4096000) { - throw ProtocolException.stringTooLong("ChainId", chainIdLen, 4096000); - } - - obj.chainId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - if ((nullBits & 64) != 0) { - int varPos6 = offset + 47 + buf.getIntLE(offset + 43); - int flagLen = VarInt.peek(buf, varPos6); - if (flagLen < 0) { - throw ProtocolException.negativeLength("Flag", flagLen); - } - - if (flagLen > 4096000) { - throw ProtocolException.stringTooLong("Flag", flagLen, 4096000); - } - - obj.flag = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -182,6 +231,10 @@ public class ChainFlagInteraction extends SimpleInteraction { int maxEnd = 47; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 47 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -191,9 +244,13 @@ public class ChainFlagInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 47 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -206,6 +263,10 @@ public class ChainFlagInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 47 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -215,9 +276,13 @@ public class ChainFlagInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 47 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -225,6 +290,10 @@ public class ChainFlagInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 47 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -234,9 +303,13 @@ public class ChainFlagInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 39); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("ChainId", fieldOffset5, maxEnd); + } + int pos5 = offset + 47 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -244,9 +317,13 @@ public class ChainFlagInteraction extends SimpleInteraction { if ((nullBits & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 43); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Flag", fieldOffset6, maxEnd); + } + int pos6 = offset + 47 + fieldOffset6; int sl = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6) + sl; + pos6 += VarInt.size(sl) + sl; if (pos6 - offset > maxEnd) { maxEnd = pos6 - offset; } @@ -417,173 +494,155 @@ public class ChainFlagInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 47 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 47 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 47 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 47 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 47 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 47 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 47 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for ChainId"); + } + + int posxx = offset + 47 + v; + int chainIdLen = VarInt.peek(buffer, posxx); + if (chainIdLen < 0) { + return ValidationResult.error("Invalid string length for ChainId"); + } + + if (chainIdLen > 4096000) { + return ValidationResult.error("ChainId exceeds max length 4096000"); + } + + posxx += VarInt.size(chainIdLen); + posxx += chainIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ChainId"); + } + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 43); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Flag"); + } + + int posxxx = offset + 47 + v; + int flagLen = VarInt.peek(buffer, posxxx); + if (flagLen < 0) { + return ValidationResult.error("Invalid string length for Flag"); + } + + if (flagLen > 4096000) { + return ValidationResult.error("Flag exceeds max length 4096000"); + } + + posxxx += VarInt.size(flagLen); + posxxx += flagLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Flag"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 47 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 47 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 47 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 47 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int chainIdOffset = buffer.getIntLE(offset + 39); - if (chainIdOffset < 0) { - return ValidationResult.error("Invalid offset for ChainId"); - } - - int posxxxxx = offset + 47 + chainIdOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ChainId"); - } - - int chainIdLen = VarInt.peek(buffer, posxxxxx); - if (chainIdLen < 0) { - return ValidationResult.error("Invalid string length for ChainId"); - } - - if (chainIdLen > 4096000) { - return ValidationResult.error("ChainId exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += chainIdLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ChainId"); - } - } - - if ((nullBits & 64) != 0) { - int flagOffset = buffer.getIntLE(offset + 43); - if (flagOffset < 0) { - return ValidationResult.error("Invalid offset for Flag"); - } - - int posxxxxxx = offset + 47 + flagOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Flag"); - } - - int flagLen = VarInt.peek(buffer, posxxxxxx); - if (flagLen < 0) { - return ValidationResult.error("Invalid string length for Flag"); - } - - if (flagLen > 4096000) { - return ValidationResult.error("Flag exceeds max length 4096000"); - } - - posxxxxxx += VarInt.length(buffer, posxxxxxx); - posxxxxxx += flagLen; - if (posxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Flag"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ChainingInteraction.java b/src/com/hypixel/hytale/protocol/ChainingInteraction.java index 84c2d4af..9aba7c94 100644 --- a/src/com/hypixel/hytale/protocol/ChainingInteraction.java +++ b/src/com/hypixel/hytale/protocol/ChainingInteraction.java @@ -78,150 +78,203 @@ public class ChainingInteraction extends Interaction { @Nonnull public static ChainingInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ChainingInteraction obj = new ChainingInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.chainingAllowance = buf.getFloatLE(offset + 11); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 47 + buf.getIntLE(offset + 15); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 47 + buf.getIntLE(offset + 19); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); - } - - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); - } - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 47 + buf.getIntLE(offset + 23); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 47 + buf.getIntLE(offset + 27); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 47 + buf.getIntLE(offset + 31); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 47 + buf.getIntLE(offset + 35); - int chainIdLen = VarInt.peek(buf, varPos5); - if (chainIdLen < 0) { - throw ProtocolException.negativeLength("ChainId", chainIdLen); - } - - if (chainIdLen > 4096000) { - throw ProtocolException.stringTooLong("ChainId", chainIdLen, 4096000); - } - - obj.chainId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - if ((nullBits & 64) != 0) { - int varPos6 = offset + 47 + buf.getIntLE(offset + 39); - int chainingNextCount = VarInt.peek(buf, varPos6); - if (chainingNextCount < 0) { - throw ProtocolException.negativeLength("ChainingNext", chainingNextCount); - } - - if (chainingNextCount > 4096000) { - throw ProtocolException.arrayTooLong("ChainingNext", chainingNextCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos6); - if (varPos6 + varIntLen + chainingNextCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ChainingNext", varPos6 + varIntLen + chainingNextCount * 4, buf.readableBytes()); - } - - obj.chainingNext = new int[chainingNextCount]; - - for (int ix = 0; ix < chainingNextCount; ix++) { - obj.chainingNext[ix] = buf.getIntLE(varPos6 + varIntLen + ix * 4); - } - } - - if ((nullBits & 128) != 0) { - int varPos7 = offset + 47 + buf.getIntLE(offset + 43); - int flagsCount = VarInt.peek(buf, varPos7); - if (flagsCount < 0) { - throw ProtocolException.negativeLength("Flags", flagsCount); - } - - if (flagsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Flags", flagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos7); - obj.flags = new HashMap<>(flagsCount); - int dictPos = varPos7 + varIntLen; - - for (int ix = 0; ix < flagsCount; ix++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 47) { + throw ProtocolException.bufferTooSmall("ChainingInteraction", 47, buf.readableBytes() - offset); + } else { + ChainingInteraction obj = new ChainingInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.chainingAllowance = buf.getFloatLE(offset + 11); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 15); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 47 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 19); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - int val = buf.getIntLE(dictPos); - dictPos += 4; - if (obj.flags.put(key, val) != null) { - throw ProtocolException.duplicateKey("flags", key); + int varPos1 = offset + 47 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } + + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - return obj; + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 23); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 47 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 27); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 47 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 31); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 47 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 35); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("ChainId", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 47 + varPosBase5; + int chainIdLen = VarInt.peek(buf, varPos5); + if (chainIdLen < 0) { + throw ProtocolException.invalidVarInt("ChainId"); + } + + int chainIdVarIntLen = VarInt.size(chainIdLen); + if (chainIdLen > 4096000) { + throw ProtocolException.stringTooLong("ChainId", chainIdLen, 4096000); + } + + if (varPos5 + chainIdVarIntLen + chainIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ChainId", varPos5 + chainIdVarIntLen + chainIdLen, buf.readableBytes()); + } + + obj.chainId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + if ((nullBits & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 39); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("ChainingNext", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 47 + varPosBase6; + int chainingNextCount = VarInt.peek(buf, varPos6); + if (chainingNextCount < 0) { + throw ProtocolException.invalidVarInt("ChainingNext"); + } + + int varIntLenx = VarInt.size(chainingNextCount); + if (chainingNextCount > 4096000) { + throw ProtocolException.arrayTooLong("ChainingNext", chainingNextCount, 4096000); + } + + if (varPos6 + varIntLenx + chainingNextCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ChainingNext", varPos6 + varIntLenx + chainingNextCount * 4, buf.readableBytes()); + } + + obj.chainingNext = new int[chainingNextCount]; + + for (int ix = 0; ix < chainingNextCount; ix++) { + obj.chainingNext[ix] = buf.getIntLE(varPos6 + varIntLenx + ix * 4); + } + } + + if ((nullBits & 128) != 0) { + int varPosBase7 = buf.getIntLE(offset + 43); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Flags", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 47 + varPosBase7; + int flagsCount = VarInt.peek(buf, varPos7); + if (flagsCount < 0) { + throw ProtocolException.invalidVarInt("Flags"); + } + + int varIntLenxx = VarInt.size(flagsCount); + if (flagsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Flags", flagsCount, 4096000); + } + + obj.flags = new HashMap<>(flagsCount); + int dictPos = varPos7 + varIntLenxx; + + for (int ix = 0; ix < flagsCount; ix++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + int val = buf.getIntLE(dictPos); + dictPos += 4; + if (obj.flags.put(key, val) != null) { + throw ProtocolException.duplicateKey("flags", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -229,6 +282,10 @@ public class ChainingInteraction extends Interaction { int maxEnd = 47; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 15); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 47 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -238,9 +295,13 @@ public class ChainingInteraction extends Interaction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 19); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 47 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -253,6 +314,10 @@ public class ChainingInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 23); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 47 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -262,9 +327,13 @@ public class ChainingInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 27); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 47 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -272,6 +341,10 @@ public class ChainingInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 31); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 47 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -281,9 +354,13 @@ public class ChainingInteraction extends Interaction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 35); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("ChainId", fieldOffset5, maxEnd); + } + int pos5 = offset + 47 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -291,9 +368,13 @@ public class ChainingInteraction extends Interaction { if ((nullBits & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 39); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("ChainingNext", fieldOffset6, maxEnd); + } + int pos6 = offset + 47 + fieldOffset6; int arrLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6) + arrLen * 4; + pos6 += VarInt.size(arrLen) + arrLen * 4; if (pos6 - offset > maxEnd) { maxEnd = pos6 - offset; } @@ -301,13 +382,17 @@ public class ChainingInteraction extends Interaction { if ((nullBits & 128) != 0) { int fieldOffset7 = buf.getIntLE(offset + 43); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Flags", fieldOffset7, maxEnd); + } + int pos7 = offset + 47 + fieldOffset7; int dictLen = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7); + pos7 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7) + sl; + pos7 += VarInt.size(sl) + sl; pos7 += 4; } @@ -520,218 +605,196 @@ public class ChainingInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 47 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 15); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 47 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 19); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 47 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 23); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 47 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 27); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 47 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 31); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 47 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int chainIdOffset = buffer.getIntLE(offset + 35); - if (chainIdOffset < 0) { - return ValidationResult.error("Invalid offset for ChainId"); - } - - int posxxxxx = offset + 47 + chainIdOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ChainId"); - } - - int chainIdLen = VarInt.peek(buffer, posxxxxx); - if (chainIdLen < 0) { - return ValidationResult.error("Invalid string length for ChainId"); - } - - if (chainIdLen > 4096000) { - return ValidationResult.error("ChainId exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += chainIdLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ChainId"); - } - } - - if ((nullBits & 64) != 0) { - int chainingNextOffset = buffer.getIntLE(offset + 39); - if (chainingNextOffset < 0) { - return ValidationResult.error("Invalid offset for ChainingNext"); - } - - int posxxxxxx = offset + 47 + chainingNextOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ChainingNext"); - } - - int chainingNextCount = VarInt.peek(buffer, posxxxxxx); - if (chainingNextCount < 0) { - return ValidationResult.error("Invalid array count for ChainingNext"); - } - - if (chainingNextCount > 4096000) { - return ValidationResult.error("ChainingNext exceeds max length 4096000"); - } - - posxxxxxx += VarInt.length(buffer, posxxxxxx); - posxxxxxx += chainingNextCount * 4; - if (posxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ChainingNext"); - } - } - - if ((nullBits & 128) != 0) { - int flagsOffset = buffer.getIntLE(offset + 43); - if (flagsOffset < 0) { - return ValidationResult.error("Invalid offset for Flags"); - } - - int posxxxxxxx = offset + 47 + flagsOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Flags"); - } - - int flagsCount = VarInt.peek(buffer, posxxxxxxx); - if (flagsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Flags"); - } - - if (flagsCount > 4096000) { - return ValidationResult.error("Flags exceeds max length 4096000"); - } - - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); - - for (int i = 0; i < flagsCount; i++) { - int keyLen = VarInt.peek(buffer, posxxxxxxx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Effects"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 47 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); } - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); - posxxxxxxx += keyLen; - if (posxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Settings"); } - posxxxxxxx += 4; - if (posxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + int pos = offset + 47 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; } } - } - return ValidationResult.OK; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 47 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 47 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 47 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for ChainId"); + } + + int posxx = offset + 47 + v; + int chainIdLen = VarInt.peek(buffer, posxx); + if (chainIdLen < 0) { + return ValidationResult.error("Invalid string length for ChainId"); + } + + if (chainIdLen > 4096000) { + return ValidationResult.error("ChainId exceeds max length 4096000"); + } + + posxx += VarInt.size(chainIdLen); + posxx += chainIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ChainId"); + } + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for ChainingNext"); + } + + int posxxx = offset + 47 + v; + int chainingNextCount = VarInt.peek(buffer, posxxx); + if (chainingNextCount < 0) { + return ValidationResult.error("Invalid array count for ChainingNext"); + } + + if (chainingNextCount > 4096000) { + return ValidationResult.error("ChainingNext exceeds max length 4096000"); + } + + posxxx += VarInt.size(chainingNextCount); + posxxx += chainingNextCount * 4; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ChainingNext"); + } + } + + if ((nullBits & 128) != 0) { + v = buffer.getIntLE(offset + 43); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Flags"); + } + + int posxxxx = offset + 47 + v; + int flagsCount = VarInt.peek(buffer, posxxxx); + if (flagsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Flags"); + } + + if (flagsCount > 4096000) { + return ValidationResult.error("Flags exceeds max length 4096000"); + } + + posxxxx += VarInt.size(flagsCount); + + for (int i = 0; i < flagsCount; i++) { + int keyLen = VarInt.peek(buffer, posxxxx); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxxx += VarInt.size(keyLen); + posxxxx += keyLen; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxxxx += 4; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/ChangeActiveSlotInteraction.java b/src/com/hypixel/hytale/protocol/ChangeActiveSlotInteraction.java index 0d72bd91..625b9d8c 100644 --- a/src/com/hypixel/hytale/protocol/ChangeActiveSlotInteraction.java +++ b/src/com/hypixel/hytale/protocol/ChangeActiveSlotInteraction.java @@ -62,77 +62,106 @@ public class ChangeActiveSlotInteraction extends Interaction { @Nonnull public static ChangeActiveSlotInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ChangeActiveSlotInteraction obj = new ChangeActiveSlotInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.targetSlot = buf.getIntLE(offset + 11); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 35 + buf.getIntLE(offset + 15); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 35) { + throw ProtocolException.bufferTooSmall("ChangeActiveSlotInteraction", 35, buf.readableBytes() - offset); + } else { + ChangeActiveSlotInteraction obj = new ChangeActiveSlotInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.targetSlot = buf.getIntLE(offset + 11); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 15); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 35 + buf.getIntLE(offset + 19); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 35 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 19); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 35 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 35 + buf.getIntLE(offset + 23); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 23); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 35 + buf.getIntLE(offset + 27); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 35 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 27); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 35 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 31); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 35 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 35 + buf.getIntLE(offset + 31); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -140,6 +169,10 @@ public class ChangeActiveSlotInteraction extends Interaction { int maxEnd = 35; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 15); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 35 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -149,9 +182,13 @@ public class ChangeActiveSlotInteraction extends Interaction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 19); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 35 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -164,6 +201,10 @@ public class ChangeActiveSlotInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 23); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 35 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -173,9 +214,13 @@ public class ChangeActiveSlotInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 27); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 35 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -183,6 +228,10 @@ public class ChangeActiveSlotInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 31); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 35 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -320,119 +369,109 @@ public class ChangeActiveSlotInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 35 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 15); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 35 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 35 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 35 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 35 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 35 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 35 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 19); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 35 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 23); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 35 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 27); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 35 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 31); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 35 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ChangeBlockInteraction.java b/src/com/hypixel/hytale/protocol/ChangeBlockInteraction.java index 66fe8126..4400c26e 100644 --- a/src/com/hypixel/hytale/protocol/ChangeBlockInteraction.java +++ b/src/com/hypixel/hytale/protocol/ChangeBlockInteraction.java @@ -80,107 +80,141 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { @Nonnull public static ChangeBlockInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ChangeBlockInteraction obj = new ChangeBlockInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.useLatestTarget = buf.getByte(offset + 19) != 0; - obj.worldSoundEventIndex = buf.getIntLE(offset + 20); - obj.requireNotBroken = buf.getByte(offset + 24) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 49 + buf.getIntLE(offset + 25); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 49) { + throw ProtocolException.bufferTooSmall("ChangeBlockInteraction", 49, buf.readableBytes() - offset); + } else { + ChangeBlockInteraction obj = new ChangeBlockInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.useLatestTarget = buf.getByte(offset + 19) != 0; + obj.worldSoundEventIndex = buf.getIntLE(offset + 20); + obj.requireNotBroken = buf.getByte(offset + 24) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 25); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 49 + buf.getIntLE(offset + 29); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 49 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 29); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 49 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 49 + buf.getIntLE(offset + 33); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 33); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 49 + buf.getIntLE(offset + 37); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 49 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 37); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } + int varPos3 = offset + 49 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } - obj.tags = new int[tagsCount]; + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 49 + buf.getIntLE(offset + 41); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } + obj.tags = new int[tagsCount]; - if ((nullBits & 32) != 0) { - int varPos5 = offset + 49 + buf.getIntLE(offset + 45); - int blockChangesCount = VarInt.peek(buf, varPos5); - if (blockChangesCount < 0) { - throw ProtocolException.negativeLength("BlockChanges", blockChangesCount); - } - - if (blockChangesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("BlockChanges", blockChangesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - obj.blockChanges = new HashMap<>(blockChangesCount); - int dictPos = varPos5 + varIntLen; - - for (int ix = 0; ix < blockChangesCount; ix++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - int val = buf.getIntLE(dictPos); - dictPos += 4; - if (obj.blockChanges.put(key, val) != null) { - throw ProtocolException.duplicateKey("blockChanges", key); + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); } } - } - return obj; + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 41); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 49 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 45); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("BlockChanges", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 49 + varPosBase5; + int blockChangesCount = VarInt.peek(buf, varPos5); + if (blockChangesCount < 0) { + throw ProtocolException.invalidVarInt("BlockChanges"); + } + + int varIntLenx = VarInt.size(blockChangesCount); + if (blockChangesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("BlockChanges", blockChangesCount, 4096000); + } + + obj.blockChanges = new HashMap<>(blockChangesCount); + int dictPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < blockChangesCount; ix++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + int val = buf.getIntLE(dictPos); + dictPos += 4; + if (obj.blockChanges.put(key, val) != null) { + throw ProtocolException.duplicateKey("blockChanges", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -188,6 +222,10 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { int maxEnd = 49; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 25); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 49 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -197,9 +235,13 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 29); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 49 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -212,6 +254,10 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 33); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 49 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -221,9 +267,13 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 37); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 49 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -231,6 +281,10 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 41); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 49 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -240,9 +294,13 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 45); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("BlockChanges", fieldOffset5, maxEnd); + } + int pos5 = offset + 49 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos5 += 4; @@ -414,154 +472,140 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { return ValidationResult.error("Buffer too small: expected at least 49 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 25); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 49 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 29); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 49 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 33); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 49 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 37); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 49 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 41); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 49 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int blockChangesOffset = buffer.getIntLE(offset + 45); - if (blockChangesOffset < 0) { - return ValidationResult.error("Invalid offset for BlockChanges"); - } - - int posxxxxx = offset + 49 + blockChangesOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockChanges"); - } - - int blockChangesCount = VarInt.peek(buffer, posxxxxx); - if (blockChangesCount < 0) { - return ValidationResult.error("Invalid dictionary count for BlockChanges"); - } - - if (blockChangesCount > 4096000) { - return ValidationResult.error("BlockChanges exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < blockChangesCount; i++) { - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 25); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Effects"); } - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + int pos = offset + 49 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 29); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 49 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; } } - } - return ValidationResult.OK; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 49 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 49 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 41); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 49 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 45); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for BlockChanges"); + } + + int posxx = offset + 49 + v; + int blockChangesCount = VarInt.peek(buffer, posxx); + if (blockChangesCount < 0) { + return ValidationResult.error("Invalid dictionary count for BlockChanges"); + } + + if (blockChangesCount > 4096000) { + return ValidationResult.error("BlockChanges exceeds max length 4096000"); + } + + posxx += VarInt.size(blockChangesCount); + + for (int i = 0; i < blockChangesCount; i++) { + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/ChangeStatInteraction.java b/src/com/hypixel/hytale/protocol/ChangeStatInteraction.java index 73a6a12a..7b4c8b78 100644 --- a/src/com/hypixel/hytale/protocol/ChangeStatInteraction.java +++ b/src/com/hypixel/hytale/protocol/ChangeStatInteraction.java @@ -84,107 +84,141 @@ public class ChangeStatInteraction extends SimpleInteraction { @Nonnull public static ChangeStatInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ChangeStatInteraction obj = new ChangeStatInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 19)); - obj.valueType = ValueType.fromValue(buf.getByte(offset + 20)); - obj.changeStatBehaviour = ChangeStatBehaviour.fromValue(buf.getByte(offset + 21)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 46 + buf.getIntLE(offset + 22); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 46) { + throw ProtocolException.bufferTooSmall("ChangeStatInteraction", 46, buf.readableBytes() - offset); + } else { + ChangeStatInteraction obj = new ChangeStatInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 19)); + obj.valueType = ValueType.fromValue(buf.getByte(offset + 20)); + obj.changeStatBehaviour = ChangeStatBehaviour.fromValue(buf.getByte(offset + 21)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 22); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 46 + buf.getIntLE(offset + 26); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 46 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 26); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 46 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 46 + buf.getIntLE(offset + 30); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 30); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 46 + buf.getIntLE(offset + 34); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 46 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 34); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } + int varPos3 = offset + 46 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } - obj.tags = new int[tagsCount]; + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 46 + buf.getIntLE(offset + 38); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } + obj.tags = new int[tagsCount]; - if ((nullBits & 32) != 0) { - int varPos5 = offset + 46 + buf.getIntLE(offset + 42); - int statModifiersCount = VarInt.peek(buf, varPos5); - if (statModifiersCount < 0) { - throw ProtocolException.negativeLength("StatModifiers", statModifiersCount); - } - - if (statModifiersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - obj.statModifiers = new HashMap<>(statModifiersCount); - int dictPos = varPos5 + varIntLen; - - for (int ix = 0; ix < statModifiersCount; ix++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.statModifiers.put(key, val) != null) { - throw ProtocolException.duplicateKey("statModifiers", key); + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); } } - } - return obj; + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 38); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 46 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 42); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("StatModifiers", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 46 + varPosBase5; + int statModifiersCount = VarInt.peek(buf, varPos5); + if (statModifiersCount < 0) { + throw ProtocolException.invalidVarInt("StatModifiers"); + } + + int varIntLenx = VarInt.size(statModifiersCount); + if (statModifiersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); + } + + obj.statModifiers = new HashMap<>(statModifiersCount); + int dictPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < statModifiersCount; ix++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.statModifiers.put(key, val) != null) { + throw ProtocolException.duplicateKey("statModifiers", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -192,6 +226,10 @@ public class ChangeStatInteraction extends SimpleInteraction { int maxEnd = 46; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 22); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 46 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -201,9 +239,13 @@ public class ChangeStatInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 26); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 46 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -216,6 +258,10 @@ public class ChangeStatInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 30); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 46 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -225,9 +271,13 @@ public class ChangeStatInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 34); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 46 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -235,6 +285,10 @@ public class ChangeStatInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 38); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 46 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -244,9 +298,13 @@ public class ChangeStatInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 42); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("StatModifiers", fieldOffset5, maxEnd); + } + int pos5 = offset + 46 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos5 += 4; @@ -418,154 +476,155 @@ public class ChangeStatInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 46 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 22); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 19) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid InteractionTarget value for EntityTarget"); + } else { + v = buffer.getByte(offset + 20) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ValueType value for ValueType"); + } else { + v = buffer.getByte(offset + 21) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ChangeStatBehaviour value for ChangeStatBehaviour"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 22); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 46 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 46 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 26); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Settings"); + } - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 26); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } + int pos = offset + 46 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } - int posx = offset + 46 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } + pos += VarInt.size(settingsCount); - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } - posx += VarInt.length(buffer, posx); + pos++; + pos++; + } + } - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 30); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Rules"); + } - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 30); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } + int posx = offset + 46 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } - int posxx = offset + 46 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 34); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Tags"); + } - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } + int posx = offset + 46 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 34); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } - int posxxx = offset + 46 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 38); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Camera"); + } - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } + int posxx = offset + 46 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 38); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 42); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for StatModifiers"); + } - int posxxxx = offset + 46 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } + int posxx = offset + 46 + v; + int statModifiersCount = VarInt.peek(buffer, posxx); + if (statModifiersCount < 0) { + return ValidationResult.error("Invalid dictionary count for StatModifiers"); + } - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } + if (statModifiersCount > 4096000) { + return ValidationResult.error("StatModifiers exceeds max length 4096000"); + } - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } + posxx += VarInt.size(statModifiersCount); - if ((nullBits & 32) != 0) { - int statModifiersOffset = buffer.getIntLE(offset + 42); - if (statModifiersOffset < 0) { - return ValidationResult.error("Invalid offset for StatModifiers"); - } + for (int i = 0; i < statModifiersCount; i++) { + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } - int posxxxxx = offset + 46 + statModifiersOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for StatModifiers"); - } + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } - int statModifiersCount = VarInt.peek(buffer, posxxxxx); - if (statModifiersCount < 0) { - return ValidationResult.error("Invalid dictionary count for StatModifiers"); - } - - if (statModifiersCount > 4096000) { - return ValidationResult.error("StatModifiers exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < statModifiersCount; i++) { - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + return ValidationResult.OK; + } } } } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ChangeStateInteraction.java b/src/com/hypixel/hytale/protocol/ChangeStateInteraction.java index 0a8ed0d3..3e5cb20c 100644 --- a/src/com/hypixel/hytale/protocol/ChangeStateInteraction.java +++ b/src/com/hypixel/hytale/protocol/ChangeStateInteraction.java @@ -73,125 +73,167 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { @Nonnull public static ChangeStateInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ChangeStateInteraction obj = new ChangeStateInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.useLatestTarget = buf.getByte(offset + 19) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 44 + buf.getIntLE(offset + 20); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 44 + buf.getIntLE(offset + 24); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); - } - - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); - } - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 44 + buf.getIntLE(offset + 28); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 44 + buf.getIntLE(offset + 32); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 44 + buf.getIntLE(offset + 36); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 44 + buf.getIntLE(offset + 40); - int stateChangesCount = VarInt.peek(buf, varPos5); - if (stateChangesCount < 0) { - throw ProtocolException.negativeLength("StateChanges", stateChangesCount); - } - - if (stateChangesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("StateChanges", stateChangesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - obj.stateChanges = new HashMap<>(stateChangesCount); - int dictPos = varPos5 + varIntLen; - - for (int ix = 0; ix < stateChangesCount; ix++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 44) { + throw ProtocolException.bufferTooSmall("ChangeStateInteraction", 44, buf.readableBytes() - offset); + } else { + ChangeStateInteraction obj = new ChangeStateInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.useLatestTarget = buf.getByte(offset + 19) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 20); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 44 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 24); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - int valLen = VarInt.peek(buf, dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); + int varPos1 = offset + 44 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); } - if (valLen > 4096000) { - throw ProtocolException.stringTooLong("val", valLen, 4096000); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); } - int valVarLen = VarInt.length(buf, dictPos); - String val = PacketIO.readVarString(buf, dictPos); - dictPos += valVarLen + valLen; - if (obj.stateChanges.put(key, val) != null) { - throw ProtocolException.duplicateKey("stateChanges", key); + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - return obj; + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 28); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 44 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 32); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 44 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 36); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 44 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 40); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("StateChanges", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 44 + varPosBase5; + int stateChangesCount = VarInt.peek(buf, varPos5); + if (stateChangesCount < 0) { + throw ProtocolException.invalidVarInt("StateChanges"); + } + + int varIntLenx = VarInt.size(stateChangesCount); + if (stateChangesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("StateChanges", stateChangesCount, 4096000); + } + + obj.stateChanges = new HashMap<>(stateChangesCount); + int dictPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < stateChangesCount; ix++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + int valLen = VarInt.peek(buf, dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 4096000) { + throw ProtocolException.stringTooLong("val", valLen, 4096000); + } + + if (dictPos + valVarLen + valLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen, buf.readableBytes()); + } + + String val = PacketIO.readVarString(buf, dictPos); + dictPos += valVarLen + valLen; + if (obj.stateChanges.put(key, val) != null) { + throw ProtocolException.duplicateKey("stateChanges", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -199,6 +241,10 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { int maxEnd = 44; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 20); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 44 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -208,9 +254,13 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 24); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 44 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -223,6 +273,10 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 28); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 44 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -232,9 +286,13 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 32); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 44 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -242,6 +300,10 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 36); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 44 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -251,15 +313,19 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 40); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("StateChanges", fieldOffset5, maxEnd); + } + int pos5 = offset + 44 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; } if (pos5 - offset > maxEnd) { @@ -431,174 +497,160 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { return ValidationResult.error("Buffer too small: expected at least 44 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 20); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 44 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 24); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 44 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 28); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 44 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 32); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 44 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 36); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 44 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int stateChangesOffset = buffer.getIntLE(offset + 40); - if (stateChangesOffset < 0) { - return ValidationResult.error("Invalid offset for StateChanges"); - } - - int posxxxxx = offset + 44 + stateChangesOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for StateChanges"); - } - - int stateChangesCount = VarInt.peek(buffer, posxxxxx); - if (stateChangesCount < 0) { - return ValidationResult.error("Invalid dictionary count for StateChanges"); - } - - if (stateChangesCount > 4096000) { - return ValidationResult.error("StateChanges exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < stateChangesCount; i++) { - int keyLen = VarInt.peek(buffer, posxxxxx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 20); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Effects"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 44 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); } - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += keyLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Settings"); } - int valueLen = VarInt.peek(buffer, posxxxxx); - if (valueLen < 0) { - return ValidationResult.error("Invalid string length for value"); + int pos = offset + 44 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); } - if (valueLen > 4096000) { - return ValidationResult.error("value exceeds max length 4096000"); + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += valueLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; } } - } - return ValidationResult.OK; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 44 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 44 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 44 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 40); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for StateChanges"); + } + + int posxx = offset + 44 + v; + int stateChangesCount = VarInt.peek(buffer, posxx); + if (stateChangesCount < 0) { + return ValidationResult.error("Invalid dictionary count for StateChanges"); + } + + if (stateChangesCount > 4096000) { + return ValidationResult.error("StateChanges exceeds max length 4096000"); + } + + posxx += VarInt.size(stateChangesCount); + + for (int i = 0; i < stateChangesCount; i++) { + int keyLen = VarInt.peek(buffer, posxx); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxx += VarInt.size(keyLen); + posxx += keyLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + int valueLen = VarInt.peek(buffer, posxx); + if (valueLen < 0) { + return ValidationResult.error("Invalid string length for value"); + } + + if (valueLen > 4096000) { + return ValidationResult.error("value exceeds max length 4096000"); + } + + posxx += VarInt.size(valueLen); + posxx += valueLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/ChargingDelay.java b/src/com/hypixel/hytale/protocol/ChargingDelay.java index 8eaef9ca..1364358f 100644 --- a/src/com/hypixel/hytale/protocol/ChargingDelay.java +++ b/src/com/hypixel/hytale/protocol/ChargingDelay.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -38,13 +39,17 @@ public class ChargingDelay { @Nonnull public static ChargingDelay deserialize(@Nonnull ByteBuf buf, int offset) { - ChargingDelay obj = new ChargingDelay(); - obj.minDelay = buf.getFloatLE(offset + 0); - obj.maxDelay = buf.getFloatLE(offset + 4); - obj.maxTotalDelay = buf.getFloatLE(offset + 8); - obj.minHealth = buf.getFloatLE(offset + 12); - obj.maxHealth = buf.getFloatLE(offset + 16); - return obj; + if (buf.readableBytes() - offset < 20) { + throw ProtocolException.bufferTooSmall("ChargingDelay", 20, buf.readableBytes() - offset); + } else { + ChargingDelay obj = new ChargingDelay(); + obj.minDelay = buf.getFloatLE(offset + 0); + obj.maxDelay = buf.getFloatLE(offset + 4); + obj.maxTotalDelay = buf.getFloatLE(offset + 8); + obj.minHealth = buf.getFloatLE(offset + 12); + obj.maxHealth = buf.getFloatLE(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ChargingInteraction.java b/src/com/hypixel/hytale/protocol/ChargingInteraction.java index b409d315..49c34d52 100644 --- a/src/com/hypixel/hytale/protocol/ChargingInteraction.java +++ b/src/com/hypixel/hytale/protocol/ChargingInteraction.java @@ -101,138 +101,177 @@ public class ChargingInteraction extends Interaction { @Nonnull public static ChargingInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ChargingInteraction obj = new ChargingInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.failed = buf.getIntLE(offset + 11); - obj.allowIndefiniteHold = buf.getByte(offset + 15) != 0; - obj.displayProgress = buf.getByte(offset + 16) != 0; - obj.cancelOnOtherClick = buf.getByte(offset + 17) != 0; - obj.failOnDamage = buf.getByte(offset + 18) != 0; - obj.mouseSensitivityAdjustmentTarget = buf.getFloatLE(offset + 19); - obj.mouseSensitivityAdjustmentDuration = buf.getFloatLE(offset + 23); - if ((nullBits & 1) != 0) { - obj.chargingDelay = ChargingDelay.deserialize(buf, offset + 27); - } - - if ((nullBits & 2) != 0) { - int varPos0 = offset + 75 + buf.getIntLE(offset + 47); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 75 + buf.getIntLE(offset + 51); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + if (buf.readableBytes() - offset < 75) { + throw ProtocolException.bufferTooSmall("ChargingInteraction", 75, buf.readableBytes() - offset); + } else { + ChargingInteraction obj = new ChargingInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.failed = buf.getIntLE(offset + 11); + obj.allowIndefiniteHold = buf.getByte(offset + 15) != 0; + obj.displayProgress = buf.getByte(offset + 16) != 0; + obj.cancelOnOtherClick = buf.getByte(offset + 17) != 0; + obj.failOnDamage = buf.getByte(offset + 18) != 0; + obj.mouseSensitivityAdjustmentTarget = buf.getFloatLE(offset + 19); + obj.mouseSensitivityAdjustmentDuration = buf.getFloatLE(offset + 23); + if ((nullBits & 1) != 0) { + obj.chargingDelay = ChargingDelay.deserialize(buf, offset + 27); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 47); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 75 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 51); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varPos1 = offset + 75 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } + + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 8) != 0) { - int varPos2 = offset + 75 + buf.getIntLE(offset + 55); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 55); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 16) != 0) { - int varPos3 = offset + 75 + buf.getIntLE(offset + 59); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 75 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } + if ((nullBits & 16) != 0) { + int varPosBase3 = buf.getIntLE(offset + 59); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } + int varPos3 = offset + 75 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } - obj.tags = new int[tagsCount]; + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } - if ((nullBits & 32) != 0) { - int varPos4 = offset + 75 + buf.getIntLE(offset + 63); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } + obj.tags = new int[tagsCount]; - if ((nullBits & 64) != 0) { - int varPos5 = offset + 75 + buf.getIntLE(offset + 67); - int chargedNextCount = VarInt.peek(buf, varPos5); - if (chargedNextCount < 0) { - throw ProtocolException.negativeLength("ChargedNext", chargedNextCount); - } - - if (chargedNextCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ChargedNext", chargedNextCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - obj.chargedNext = new HashMap<>(chargedNextCount); - int dictPos = varPos5 + varIntLen; - - for (int ix = 0; ix < chargedNextCount; ix++) { - float key = buf.getFloatLE(dictPos); - dictPos += 4; - int val = buf.getIntLE(dictPos); - dictPos += 4; - if (obj.chargedNext.put(key, val) != null) { - throw ProtocolException.duplicateKey("chargedNext", key); + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); } } - } - if ((nullBits & 128) != 0) { - int varPos6 = offset + 75 + buf.getIntLE(offset + 71); - int forksCount = VarInt.peek(buf, varPos6); - if (forksCount < 0) { - throw ProtocolException.negativeLength("Forks", forksCount); + if ((nullBits & 32) != 0) { + int varPosBase4 = buf.getIntLE(offset + 63); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 75 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - if (forksCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Forks", forksCount, 4096000); - } + if ((nullBits & 64) != 0) { + int varPosBase5 = buf.getIntLE(offset + 67); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("ChargedNext", varPosBase5, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos6); - obj.forks = new HashMap<>(forksCount); - int dictPos = varPos6 + varIntLen; + int varPos5 = offset + 75 + varPosBase5; + int chargedNextCount = VarInt.peek(buf, varPos5); + if (chargedNextCount < 0) { + throw ProtocolException.invalidVarInt("ChargedNext"); + } - for (int ixx = 0; ixx < forksCount; ixx++) { - InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.forks.put(key, val) != null) { - throw ProtocolException.duplicateKey("forks", key); + int varIntLenx = VarInt.size(chargedNextCount); + if (chargedNextCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ChargedNext", chargedNextCount, 4096000); + } + + obj.chargedNext = new HashMap<>(chargedNextCount); + int dictPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < chargedNextCount; ix++) { + float key = buf.getFloatLE(dictPos); + dictPos += 4; + int val = buf.getIntLE(dictPos); + dictPos += 4; + if (obj.chargedNext.put(key, val) != null) { + throw ProtocolException.duplicateKey("chargedNext", key); + } } } - } - return obj; + if ((nullBits & 128) != 0) { + int varPosBase6 = buf.getIntLE(offset + 71); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Forks", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 75 + varPosBase6; + int forksCount = VarInt.peek(buf, varPos6); + if (forksCount < 0) { + throw ProtocolException.invalidVarInt("Forks"); + } + + int varIntLenx = VarInt.size(forksCount); + if (forksCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Forks", forksCount, 4096000); + } + + obj.forks = new HashMap<>(forksCount); + int dictPos = varPos6 + varIntLenx; + + for (int ixx = 0; ixx < forksCount; ixx++) { + InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.forks.put(key, val) != null) { + throw ProtocolException.duplicateKey("forks", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -240,6 +279,10 @@ public class ChargingInteraction extends Interaction { int maxEnd = 75; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 47); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 75 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -249,9 +292,13 @@ public class ChargingInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 51); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 75 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -264,6 +311,10 @@ public class ChargingInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset2 = buf.getIntLE(offset + 55); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 75 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -273,9 +324,13 @@ public class ChargingInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset3 = buf.getIntLE(offset + 59); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 75 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -283,6 +338,10 @@ public class ChargingInteraction extends Interaction { if ((nullBits & 32) != 0) { int fieldOffset4 = buf.getIntLE(offset + 63); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 75 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -292,9 +351,13 @@ public class ChargingInteraction extends Interaction { if ((nullBits & 64) != 0) { int fieldOffset5 = buf.getIntLE(offset + 67); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("ChargedNext", fieldOffset5, maxEnd); + } + int pos5 = offset + 75 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos5 += 4; @@ -308,9 +371,13 @@ public class ChargingInteraction extends Interaction { if ((nullBits & 128) != 0) { int fieldOffset6 = buf.getIntLE(offset + 71); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 75) { + throw ProtocolException.invalidOffset("Forks", fieldOffset6, maxEnd); + } + int pos6 = offset + 75 + fieldOffset6; int dictLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos6 = ++pos6 + 4; @@ -519,184 +586,171 @@ public class ChargingInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 75 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 2) != 0) { - int effectsOffset = buffer.getIntLE(offset + 47); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 75 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 4) != 0) { - int settingsOffset = buffer.getIntLE(offset + 51); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 75 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 8) != 0) { - int rulesOffset = buffer.getIntLE(offset + 55); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 75 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 16) != 0) { - int tagsOffset = buffer.getIntLE(offset + 59); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 75 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 32) != 0) { - int cameraOffset = buffer.getIntLE(offset + 63); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 75 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 64) != 0) { - int chargedNextOffset = buffer.getIntLE(offset + 67); - if (chargedNextOffset < 0) { - return ValidationResult.error("Invalid offset for ChargedNext"); - } - - int posxxxxx = offset + 75 + chargedNextOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ChargedNext"); - } - - int chargedNextCount = VarInt.peek(buffer, posxxxxx); - if (chargedNextCount < 0) { - return ValidationResult.error("Invalid dictionary count for ChargedNext"); - } - - if (chargedNextCount > 4096000) { - return ValidationResult.error("ChargedNext exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < chargedNextCount; i++) { - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 47); + if (v < 0 || v > buffer.writerIndex() - offset - 75) { + return ValidationResult.error("Invalid offset for Effects"); } - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + int pos = offset + 75 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 51); + if (v < 0 || v > buffer.writerIndex() - offset - 75) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 75 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; } } - } - if ((nullBits & 128) != 0) { - int forksOffset = buffer.getIntLE(offset + 71); - if (forksOffset < 0) { - return ValidationResult.error("Invalid offset for Forks"); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 55); + if (v < 0 || v > buffer.writerIndex() - offset - 75) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 75 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - int posxxxxxx = offset + 75 + forksOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Forks"); - } + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 59); + if (v < 0 || v > buffer.writerIndex() - offset - 75) { + return ValidationResult.error("Invalid offset for Tags"); + } - int forksCount = VarInt.peek(buffer, posxxxxxx); - if (forksCount < 0) { - return ValidationResult.error("Invalid dictionary count for Forks"); - } + int posx = offset + 75 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } - if (forksCount > 4096000) { - return ValidationResult.error("Forks exceeds max length 4096000"); - } + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } - posxxxxxx += VarInt.length(buffer, posxxxxxx); - - for (int i = 0; i < forksCount; i++) { - posxxxxxx = ++posxxxxxx + 4; - if (posxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); } } - } - return ValidationResult.OK; + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 63); + if (v < 0 || v > buffer.writerIndex() - offset - 75) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 75 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 67); + if (v < 0 || v > buffer.writerIndex() - offset - 75) { + return ValidationResult.error("Invalid offset for ChargedNext"); + } + + int posxx = offset + 75 + v; + int chargedNextCount = VarInt.peek(buffer, posxx); + if (chargedNextCount < 0) { + return ValidationResult.error("Invalid dictionary count for ChargedNext"); + } + + if (chargedNextCount > 4096000) { + return ValidationResult.error("ChargedNext exceeds max length 4096000"); + } + + posxx += VarInt.size(chargedNextCount); + + for (int i = 0; i < chargedNextCount; i++) { + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + if ((nullBits & 128) != 0) { + v = buffer.getIntLE(offset + 71); + if (v < 0 || v > buffer.writerIndex() - offset - 75) { + return ValidationResult.error("Invalid offset for Forks"); + } + + int posxxx = offset + 75 + v; + int forksCount = VarInt.peek(buffer, posxxx); + if (forksCount < 0) { + return ValidationResult.error("Invalid dictionary count for Forks"); + } + + if (forksCount > 4096000) { + return ValidationResult.error("Forks exceeds max length 4096000"); + } + + posxxx += VarInt.size(forksCount); + + for (int i = 0; i < forksCount; i++) { + int vx = buffer.getByte(posxxx) & 255; + if (vx >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); + } + + posxxx = ++posxxx + 4; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/ClampConfig.java b/src/com/hypixel/hytale/protocol/ClampConfig.java index 1ec61e8e..59fb5c54 100644 --- a/src/com/hypixel/hytale/protocol/ClampConfig.java +++ b/src/com/hypixel/hytale/protocol/ClampConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class ClampConfig { @Nonnull public static ClampConfig deserialize(@Nonnull ByteBuf buf, int offset) { - ClampConfig obj = new ClampConfig(); - obj.min = buf.getFloatLE(offset + 0); - obj.max = buf.getFloatLE(offset + 4); - obj.normalize = buf.getByte(offset + 8) != 0; - return obj; + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ClampConfig", 9, buf.readableBytes() - offset); + } else { + ClampConfig obj = new ClampConfig(); + obj.min = buf.getFloatLE(offset + 0); + obj.max = buf.getFloatLE(offset + 4); + obj.normalize = buf.getByte(offset + 8) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ClearEntityEffectInteraction.java b/src/com/hypixel/hytale/protocol/ClearEntityEffectInteraction.java index bd34f6a9..ca6f706c 100644 --- a/src/com/hypixel/hytale/protocol/ClearEntityEffectInteraction.java +++ b/src/com/hypixel/hytale/protocol/ClearEntityEffectInteraction.java @@ -73,80 +73,109 @@ public class ClearEntityEffectInteraction extends SimpleInteraction { @Nonnull public static ClearEntityEffectInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ClearEntityEffectInteraction obj = new ClearEntityEffectInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.effectId = buf.getIntLE(offset + 19); - obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 23)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 44 + buf.getIntLE(offset + 24); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 44) { + throw ProtocolException.bufferTooSmall("ClearEntityEffectInteraction", 44, buf.readableBytes() - offset); + } else { + ClearEntityEffectInteraction obj = new ClearEntityEffectInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.effectId = buf.getIntLE(offset + 19); + obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 23)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 24); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 44 + buf.getIntLE(offset + 28); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 44 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 28); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 44 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 44 + buf.getIntLE(offset + 32); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 32); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 44 + buf.getIntLE(offset + 36); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 44 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 36); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 44 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 40); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 44 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 44 + buf.getIntLE(offset + 40); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -154,6 +183,10 @@ public class ClearEntityEffectInteraction extends SimpleInteraction { int maxEnd = 44; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 24); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 44 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -163,9 +196,13 @@ public class ClearEntityEffectInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 28); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 44 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -178,6 +215,10 @@ public class ClearEntityEffectInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 32); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 44 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -187,9 +228,13 @@ public class ClearEntityEffectInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 36); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 44 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -197,6 +242,10 @@ public class ClearEntityEffectInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 40); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 44) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 44 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -337,119 +386,114 @@ public class ClearEntityEffectInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 44 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 24); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 23) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid InteractionTarget value for EntityTarget"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 44 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 44 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Settings"); + } - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 28); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } + int pos = offset + 44 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } - int posx = offset + 44 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } + pos += VarInt.size(settingsCount); - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } - posx += VarInt.length(buffer, posx); + pos++; + pos++; + } + } - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 44 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 44 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 40); + if (v < 0 || v > buffer.writerIndex() - offset - 44) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 44 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 32); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 44 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 36); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 44 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 40); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 44 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/Cloud.java b/src/com/hypixel/hytale/protocol/Cloud.java index 386a255b..6031163d 100644 --- a/src/com/hypixel/hytale/protocol/Cloud.java +++ b/src/com/hypixel/hytale/protocol/Cloud.java @@ -42,75 +42,99 @@ public class Cloud { @Nonnull public static Cloud deserialize(@Nonnull ByteBuf buf, int offset) { - Cloud obj = new Cloud(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int textureLen = VarInt.peek(buf, varPos0); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("Cloud", 13, buf.readableBytes() - offset); + } else { + Cloud obj = new Cloud(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Texture", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int textureLen = VarInt.peek(buf, varPos0); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarIntLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (varPos0 + textureVarIntLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", varPos0 + textureVarIntLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Speeds", varPosBase1, buf.readableBytes()); + } - obj.texture = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + int varPos1 = offset + 13 + varPosBase1; + int speedsCount = VarInt.peek(buf, varPos1); + if (speedsCount < 0) { + throw ProtocolException.invalidVarInt("Speeds"); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int speedsCount = VarInt.peek(buf, varPos1); - if (speedsCount < 0) { - throw ProtocolException.negativeLength("Speeds", speedsCount); - } + int varIntLen = VarInt.size(speedsCount); + if (speedsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Speeds", speedsCount, 4096000); + } - if (speedsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Speeds", speedsCount, 4096000); - } + obj.speeds = new HashMap<>(speedsCount); + int dictPos = varPos1 + varIntLen; - int varIntLen = VarInt.length(buf, varPos1); - obj.speeds = new HashMap<>(speedsCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < speedsCount; i++) { - float key = buf.getFloatLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.speeds.put(key, val) != null) { - throw ProtocolException.duplicateKey("speeds", key); + for (int i = 0; i < speedsCount; i++) { + float key = buf.getFloatLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.speeds.put(key, val) != null) { + throw ProtocolException.duplicateKey("speeds", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int colorsCount = VarInt.peek(buf, varPos2); - if (colorsCount < 0) { - throw ProtocolException.negativeLength("Colors", colorsCount); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Colors", varPosBase2, buf.readableBytes()); + } - if (colorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Colors", colorsCount, 4096000); - } + int varPos2 = offset + 13 + varPosBase2; + int colorsCount = VarInt.peek(buf, varPos2); + if (colorsCount < 0) { + throw ProtocolException.invalidVarInt("Colors"); + } - int varIntLen = VarInt.length(buf, varPos2); - obj.colors = new HashMap<>(colorsCount); - int dictPos = varPos2 + varIntLen; + int varIntLen = VarInt.size(colorsCount); + if (colorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Colors", colorsCount, 4096000); + } - for (int ix = 0; ix < colorsCount; ix++) { - float key = buf.getFloatLE(dictPos); - dictPos += 4; - ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); - dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); - if (obj.colors.put(key, val) != null) { - throw ProtocolException.duplicateKey("colors", key); + obj.colors = new HashMap<>(colorsCount); + int dictPos = varPos2 + varIntLen; + + for (int ix = 0; ix < colorsCount; ix++) { + float key = buf.getFloatLE(dictPos); + dictPos += 4; + ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); + dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); + if (obj.colors.put(key, val) != null) { + throw ProtocolException.duplicateKey("colors", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -118,9 +142,13 @@ public class Cloud { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Texture", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -128,9 +156,13 @@ public class Cloud { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Speeds", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 += 4; @@ -144,9 +176,13 @@ public class Cloud { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Colors", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int dictLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos2 += 4; @@ -248,15 +284,11 @@ public class Cloud { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int textureOffset = buffer.getIntLE(offset + 1); - if (textureOffset < 0) { + if (textureOffset < 0 || textureOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Texture"); } int pos = offset + 13 + textureOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Texture"); - } - int textureLen = VarInt.peek(buffer, pos); if (textureLen < 0) { return ValidationResult.error("Invalid string length for Texture"); @@ -266,7 +298,7 @@ public class Cloud { return ValidationResult.error("Texture exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(textureLen); pos += textureLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Texture"); @@ -275,15 +307,11 @@ public class Cloud { if ((nullBits & 2) != 0) { int speedsOffset = buffer.getIntLE(offset + 5); - if (speedsOffset < 0) { + if (speedsOffset < 0 || speedsOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Speeds"); } int posx = offset + 13 + speedsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Speeds"); - } - int speedsCount = VarInt.peek(buffer, posx); if (speedsCount < 0) { return ValidationResult.error("Invalid dictionary count for Speeds"); @@ -293,7 +321,7 @@ public class Cloud { return ValidationResult.error("Speeds exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(speedsCount); for (int i = 0; i < speedsCount; i++) { posx += 4; @@ -310,15 +338,11 @@ public class Cloud { if ((nullBits & 4) != 0) { int colorsOffset = buffer.getIntLE(offset + 9); - if (colorsOffset < 0) { + if (colorsOffset < 0 || colorsOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Colors"); } int posxx = offset + 13 + colorsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Colors"); - } - int colorsCount = VarInt.peek(buffer, posxx); if (colorsCount < 0) { return ValidationResult.error("Invalid dictionary count for Colors"); @@ -328,7 +352,7 @@ public class Cloud { return ValidationResult.error("Colors exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(colorsCount); for (int i = 0; i < colorsCount; i++) { posxx += 4; diff --git a/src/com/hypixel/hytale/protocol/Color.java b/src/com/hypixel/hytale/protocol/Color.java index a908fbe3..314cd855 100644 --- a/src/com/hypixel/hytale/protocol/Color.java +++ b/src/com/hypixel/hytale/protocol/Color.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class Color { @Nonnull public static Color deserialize(@Nonnull ByteBuf buf, int offset) { - Color obj = new Color(); - obj.red = buf.getByte(offset + 0); - obj.green = buf.getByte(offset + 1); - obj.blue = buf.getByte(offset + 2); - return obj; + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("Color", 3, buf.readableBytes() - offset); + } else { + Color obj = new Color(); + obj.red = buf.getByte(offset + 0); + obj.green = buf.getByte(offset + 1); + obj.blue = buf.getByte(offset + 2); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ColorAlpha.java b/src/com/hypixel/hytale/protocol/ColorAlpha.java index 232ee6a1..3f682ccc 100644 --- a/src/com/hypixel/hytale/protocol/ColorAlpha.java +++ b/src/com/hypixel/hytale/protocol/ColorAlpha.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -35,12 +36,16 @@ public class ColorAlpha { @Nonnull public static ColorAlpha deserialize(@Nonnull ByteBuf buf, int offset) { - ColorAlpha obj = new ColorAlpha(); - obj.alpha = buf.getByte(offset + 0); - obj.red = buf.getByte(offset + 1); - obj.green = buf.getByte(offset + 2); - obj.blue = buf.getByte(offset + 3); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("ColorAlpha", 4, buf.readableBytes() - offset); + } else { + ColorAlpha obj = new ColorAlpha(); + obj.alpha = buf.getByte(offset + 0); + obj.red = buf.getByte(offset + 1); + obj.green = buf.getByte(offset + 2); + obj.blue = buf.getByte(offset + 3); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ColorLight.java b/src/com/hypixel/hytale/protocol/ColorLight.java index f6f7f35f..3792d300 100644 --- a/src/com/hypixel/hytale/protocol/ColorLight.java +++ b/src/com/hypixel/hytale/protocol/ColorLight.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -35,12 +36,16 @@ public class ColorLight { @Nonnull public static ColorLight deserialize(@Nonnull ByteBuf buf, int offset) { - ColorLight obj = new ColorLight(); - obj.radius = buf.getByte(offset + 0); - obj.red = buf.getByte(offset + 1); - obj.green = buf.getByte(offset + 2); - obj.blue = buf.getByte(offset + 3); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("ColorLight", 4, buf.readableBytes() - offset); + } else { + ColorLight obj = new ColorLight(); + obj.radius = buf.getByte(offset + 0); + obj.red = buf.getByte(offset + 1); + obj.green = buf.getByte(offset + 2); + obj.blue = buf.getByte(offset + 3); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/CombatTextEntityUIComponentAnimationEvent.java b/src/com/hypixel/hytale/protocol/CombatTextEntityUIComponentAnimationEvent.java index adb7eafe..dbf13363 100644 --- a/src/com/hypixel/hytale/protocol/CombatTextEntityUIComponentAnimationEvent.java +++ b/src/com/hypixel/hytale/protocol/CombatTextEntityUIComponentAnimationEvent.java @@ -1,25 +1,27 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import org.joml.Vector2fc; public class CombatTextEntityUIComponentAnimationEvent { - public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 34; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 33; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 34; - public static final int MAX_SIZE = 34; + public static final int VARIABLE_BLOCK_START = 33; + public static final int MAX_SIZE = 33; @Nonnull public CombatTextEntityUIAnimationEventType type = CombatTextEntityUIAnimationEventType.Scale; public float startAt; public float endAt; public float startScale; public float endScale; - @Nullable - public Vector2f positionOffset; + @Nonnull + public Vector2fc positionOffset = PacketIO.ZERO_VECTOR2; public float startOpacity; public float endOpacity; @@ -32,7 +34,7 @@ public class CombatTextEntityUIComponentAnimationEvent { float endAt, float startScale, float endScale, - @Nullable Vector2f positionOffset, + @Nonnull Vector2fc positionOffset, float startOpacity, float endOpacity ) { @@ -59,54 +61,48 @@ public class CombatTextEntityUIComponentAnimationEvent { @Nonnull public static CombatTextEntityUIComponentAnimationEvent deserialize(@Nonnull ByteBuf buf, int offset) { - CombatTextEntityUIComponentAnimationEvent obj = new CombatTextEntityUIComponentAnimationEvent(); - byte nullBits = buf.getByte(offset); - obj.type = CombatTextEntityUIAnimationEventType.fromValue(buf.getByte(offset + 1)); - obj.startAt = buf.getFloatLE(offset + 2); - obj.endAt = buf.getFloatLE(offset + 6); - obj.startScale = buf.getFloatLE(offset + 10); - obj.endScale = buf.getFloatLE(offset + 14); - if ((nullBits & 1) != 0) { - obj.positionOffset = Vector2f.deserialize(buf, offset + 18); + if (buf.readableBytes() - offset < 33) { + throw ProtocolException.bufferTooSmall("CombatTextEntityUIComponentAnimationEvent", 33, buf.readableBytes() - offset); + } else { + CombatTextEntityUIComponentAnimationEvent obj = new CombatTextEntityUIComponentAnimationEvent(); + obj.type = CombatTextEntityUIAnimationEventType.fromValue(buf.getByte(offset + 0)); + obj.startAt = buf.getFloatLE(offset + 1); + obj.endAt = buf.getFloatLE(offset + 5); + obj.startScale = buf.getFloatLE(offset + 9); + obj.endScale = buf.getFloatLE(offset + 13); + obj.positionOffset = PacketIO.readVector2f(buf, offset + 17); + obj.startOpacity = buf.getFloatLE(offset + 25); + obj.endOpacity = buf.getFloatLE(offset + 29); + return obj; } - - obj.startOpacity = buf.getFloatLE(offset + 26); - obj.endOpacity = buf.getFloatLE(offset + 30); - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 34; + return 33; } public void serialize(@Nonnull ByteBuf buf) { - byte nullBits = 0; - if (this.positionOffset != null) { - nullBits = (byte)(nullBits | 1); - } - - buf.writeByte(nullBits); buf.writeByte(this.type.getValue()); buf.writeFloatLE(this.startAt); buf.writeFloatLE(this.endAt); buf.writeFloatLE(this.startScale); buf.writeFloatLE(this.endScale); - if (this.positionOffset != null) { - this.positionOffset.serialize(buf); - } else { - buf.writeZero(8); - } - + PacketIO.writeVector2f(buf, this.positionOffset); buf.writeFloatLE(this.startOpacity); buf.writeFloatLE(this.endOpacity); } public int computeSize() { - return 34; + return 33; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 34 ? ValidationResult.error("Buffer too small: expected at least 34 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 33) { + return ValidationResult.error("Buffer too small: expected at least 33 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 3 ? ValidationResult.error("Invalid CombatTextEntityUIAnimationEventType value for Type") : ValidationResult.OK; + } } public CombatTextEntityUIComponentAnimationEvent clone() { @@ -116,7 +112,7 @@ public class CombatTextEntityUIComponentAnimationEvent { copy.endAt = this.endAt; copy.startScale = this.startScale; copy.endScale = this.endScale; - copy.positionOffset = this.positionOffset != null ? this.positionOffset.clone() : null; + copy.positionOffset = this.positionOffset; copy.startOpacity = this.startOpacity; copy.endOpacity = this.endOpacity; return copy; diff --git a/src/com/hypixel/hytale/protocol/CombatTextUpdate.java b/src/com/hypixel/hytale/protocol/CombatTextUpdate.java index 64226c32..d371d230 100644 --- a/src/com/hypixel/hytale/protocol/CombatTextUpdate.java +++ b/src/com/hypixel/hytale/protocol/CombatTextUpdate.java @@ -33,26 +33,34 @@ public class CombatTextUpdate extends ComponentUpdate { @Nonnull public static CombatTextUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - CombatTextUpdate obj = new CombatTextUpdate(); - obj.hitAngleDeg = buf.getFloatLE(offset + 0); - int pos = offset + 4; - int textLen = VarInt.peek(buf, pos); - if (textLen < 0) { - throw ProtocolException.negativeLength("Text", textLen); - } else if (textLen > 4096000) { - throw ProtocolException.stringTooLong("Text", textLen, 4096000); + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("CombatTextUpdate", 4, buf.readableBytes() - offset); } else { - int textVarLen = VarInt.length(buf, pos); - obj.text = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += textVarLen + textLen; - return obj; + CombatTextUpdate obj = new CombatTextUpdate(); + obj.hitAngleDeg = buf.getFloatLE(offset + 0); + int pos = offset + 4; + int textLen = VarInt.peek(buf, pos); + if (textLen < 0) { + throw ProtocolException.invalidVarInt("Text"); + } else { + int textVarLen = VarInt.size(textLen); + if (textLen > 4096000) { + throw ProtocolException.stringTooLong("Text", textLen, 4096000); + } else if (pos + textVarLen + textLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Text", pos + textVarLen + textLen, buf.readableBytes()); + } else { + obj.text = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += textVarLen + textLen; + return obj; + } + } } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 4; int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; return pos - offset; } @@ -81,7 +89,7 @@ public class CombatTextUpdate extends ComponentUpdate { } else if (textLen > 4096000) { return ValidationResult.error("Text exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(textLen); pos += textLen; return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Text") : ValidationResult.OK; } diff --git a/src/com/hypixel/hytale/protocol/ComponentUpdate.java b/src/com/hypixel/hytale/protocol/ComponentUpdate.java index 618cf515..321ac79f 100644 --- a/src/com/hypixel/hytale/protocol/ComponentUpdate.java +++ b/src/com/hypixel/hytale/protocol/ComponentUpdate.java @@ -12,7 +12,7 @@ public abstract class ComponentUpdate { @Nonnull public static ComponentUpdate deserialize(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return (ComponentUpdate)(switch (typeId) { case 0 -> NameplateUpdate.deserialize(buf, offset + typeIdLen); @@ -47,7 +47,7 @@ public abstract class ComponentUpdate { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return typeIdLen + switch (typeId) { case 0 -> NameplateUpdate.computeBytesConsumed(buf, offset + typeIdLen); @@ -155,7 +155,7 @@ public abstract class ComponentUpdate { public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { int typeId = VarInt.peek(buffer, offset); - int typeIdLen = VarInt.length(buffer, offset); + int typeIdLen = VarInt.size(typeId); return switch (typeId) { case 0 -> NameplateUpdate.validateStructure(buffer, offset + typeIdLen); diff --git a/src/com/hypixel/hytale/protocol/ConditionInteraction.java b/src/com/hypixel/hytale/protocol/ConditionInteraction.java index 9dd6c5ea..1713edab 100644 --- a/src/com/hypixel/hytale/protocol/ConditionInteraction.java +++ b/src/com/hypixel/hytale/protocol/ConditionInteraction.java @@ -95,102 +95,131 @@ public class ConditionInteraction extends SimpleInteraction { @Nonnull public static ConditionInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ConditionInteraction obj = new ConditionInteraction(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 2)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 3); - obj.runTime = buf.getFloatLE(offset + 7); - obj.cancelOnItemChange = buf.getByte(offset + 11) != 0; - obj.next = buf.getIntLE(offset + 12); - obj.failed = buf.getIntLE(offset + 16); - if ((nullBits[0] & 1) != 0) { - obj.requiredGameMode = GameMode.fromValue(buf.getByte(offset + 20)); - } - - if ((nullBits[0] & 2) != 0) { - obj.jumping = buf.getByte(offset + 21) != 0; - } - - if ((nullBits[0] & 4) != 0) { - obj.swimming = buf.getByte(offset + 22) != 0; - } - - if ((nullBits[0] & 8) != 0) { - obj.crouching = buf.getByte(offset + 23) != 0; - } - - if ((nullBits[0] & 16) != 0) { - obj.running = buf.getByte(offset + 24) != 0; - } - - if ((nullBits[0] & 32) != 0) { - obj.flying = buf.getByte(offset + 25) != 0; - } - - if ((nullBits[0] & 64) != 0) { - int varPos0 = offset + 46 + buf.getIntLE(offset + 26); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } - - if ((nullBits[0] & 128) != 0) { - int varPos1 = offset + 46 + buf.getIntLE(offset + 30); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + if (buf.readableBytes() - offset < 46) { + throw ProtocolException.bufferTooSmall("ConditionInteraction", 46, buf.readableBytes() - offset); + } else { + ConditionInteraction obj = new ConditionInteraction(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 2)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 3); + obj.runTime = buf.getFloatLE(offset + 7); + obj.cancelOnItemChange = buf.getByte(offset + 11) != 0; + obj.next = buf.getIntLE(offset + 12); + obj.failed = buf.getIntLE(offset + 16); + if ((nullBits[0] & 1) != 0) { + obj.requiredGameMode = GameMode.fromValue(buf.getByte(offset + 20)); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + if ((nullBits[0] & 2) != 0) { + obj.jumping = buf.getByte(offset + 21) != 0; } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + if ((nullBits[0] & 4) != 0) { + obj.swimming = buf.getByte(offset + 22) != 0; + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + if ((nullBits[0] & 8) != 0) { + obj.crouching = buf.getByte(offset + 23) != 0; + } + + if ((nullBits[0] & 16) != 0) { + obj.running = buf.getByte(offset + 24) != 0; + } + + if ((nullBits[0] & 32) != 0) { + obj.flying = buf.getByte(offset + 25) != 0; + } + + if ((nullBits[0] & 64) != 0) { + int varPosBase0 = buf.getIntLE(offset + 26); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 46 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase1 = buf.getIntLE(offset + 30); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 46 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } + + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits[1] & 1) != 0) { - int varPos2 = offset + 46 + buf.getIntLE(offset + 34); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits[1] & 1) != 0) { + int varPosBase2 = buf.getIntLE(offset + 34); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits[1] & 2) != 0) { - int varPos3 = offset + 46 + buf.getIntLE(offset + 38); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 46 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits[1] & 2) != 0) { + int varPosBase3 = buf.getIntLE(offset + 38); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 46 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits[1] & 4) != 0) { + int varPosBase4 = buf.getIntLE(offset + 42); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 46 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits[1] & 4) != 0) { - int varPos4 = offset + 46 + buf.getIntLE(offset + 42); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -198,6 +227,10 @@ public class ConditionInteraction extends SimpleInteraction { int maxEnd = 46; if ((nullBits[0] & 64) != 0) { int fieldOffset0 = buf.getIntLE(offset + 26); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 46 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -207,9 +240,13 @@ public class ConditionInteraction extends SimpleInteraction { if ((nullBits[0] & 128) != 0) { int fieldOffset1 = buf.getIntLE(offset + 30); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 46 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -222,6 +259,10 @@ public class ConditionInteraction extends SimpleInteraction { if ((nullBits[1] & 1) != 0) { int fieldOffset2 = buf.getIntLE(offset + 34); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 46 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -231,9 +272,13 @@ public class ConditionInteraction extends SimpleInteraction { if ((nullBits[1] & 2) != 0) { int fieldOffset3 = buf.getIntLE(offset + 38); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 46 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -241,6 +286,10 @@ public class ConditionInteraction extends SimpleInteraction { if ((nullBits[1] & 4) != 0) { int fieldOffset4 = buf.getIntLE(offset + 42); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 46 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -439,119 +488,116 @@ public class ConditionInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 46 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[0] & 64) != 0) { - int effectsOffset = buffer.getIntLE(offset + 26); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 2) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits[0] & 1) != 0) { + v = buffer.getByte(offset + 20) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid GameMode value for RequiredGameMode"); + } } - int pos = offset + 46 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits[0] & 64) != 0) { + v = buffer.getIntLE(offset + 26); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 46 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits[0] & 128) != 0) { + v = buffer.getIntLE(offset + 30); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 46 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 34); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 46 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits[1] & 2) != 0) { + v = buffer.getIntLE(offset + 38); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 46 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits[1] & 4) != 0) { + v = buffer.getIntLE(offset + 42); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 46 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits[0] & 128) != 0) { - int settingsOffset = buffer.getIntLE(offset + 30); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 46 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits[1] & 1) != 0) { - int rulesOffset = buffer.getIntLE(offset + 34); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 46 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits[1] & 2) != 0) { - int tagsOffset = buffer.getIntLE(offset + 38); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 46 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits[1] & 4) != 0) { - int cameraOffset = buffer.getIntLE(offset + 42); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 46 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ConditionalBlockSound.java b/src/com/hypixel/hytale/protocol/ConditionalBlockSound.java new file mode 100644 index 00000000..98b14ddd --- /dev/null +++ b/src/com/hypixel/hytale/protocol/ConditionalBlockSound.java @@ -0,0 +1,82 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class ConditionalBlockSound { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 8; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 8; + public int soundEventIndex; + public int ambienceFXIndex; + + public ConditionalBlockSound() { + } + + public ConditionalBlockSound(int soundEventIndex, int ambienceFXIndex) { + this.soundEventIndex = soundEventIndex; + this.ambienceFXIndex = ambienceFXIndex; + } + + public ConditionalBlockSound(@Nonnull ConditionalBlockSound other) { + this.soundEventIndex = other.soundEventIndex; + this.ambienceFXIndex = other.ambienceFXIndex; + } + + @Nonnull + public static ConditionalBlockSound deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("ConditionalBlockSound", 8, buf.readableBytes() - offset); + } else { + ConditionalBlockSound obj = new ConditionalBlockSound(); + obj.soundEventIndex = buf.getIntLE(offset + 0); + obj.ambienceFXIndex = buf.getIntLE(offset + 4); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 8; + } + + public void serialize(@Nonnull ByteBuf buf) { + buf.writeIntLE(this.soundEventIndex); + buf.writeIntLE(this.ambienceFXIndex); + } + + public int computeSize() { + return 8; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 8 ? ValidationResult.error("Buffer too small: expected at least 8 bytes") : ValidationResult.OK; + } + + public ConditionalBlockSound clone() { + ConditionalBlockSound copy = new ConditionalBlockSound(); + copy.soundEventIndex = this.soundEventIndex; + copy.ambienceFXIndex = this.ambienceFXIndex; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof ConditionalBlockSound other) + ? false + : this.soundEventIndex == other.soundEventIndex && this.ambienceFXIndex == other.ambienceFXIndex; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.soundEventIndex, this.ambienceFXIndex); + } +} diff --git a/src/com/hypixel/hytale/protocol/ConnectedBlockRuleSet.java b/src/com/hypixel/hytale/protocol/ConnectedBlockRuleSet.java index c5b5dced..633d41e4 100644 --- a/src/com/hypixel/hytale/protocol/ConnectedBlockRuleSet.java +++ b/src/com/hypixel/hytale/protocol/ConnectedBlockRuleSet.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -36,20 +37,34 @@ public class ConnectedBlockRuleSet { @Nonnull public static ConnectedBlockRuleSet deserialize(@Nonnull ByteBuf buf, int offset) { - ConnectedBlockRuleSet obj = new ConnectedBlockRuleSet(); - byte nullBits = buf.getByte(offset); - obj.type = ConnectedBlockRuleSetType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - obj.stair = StairConnectedBlockRuleSet.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("ConnectedBlockRuleSet", 10, buf.readableBytes() - offset); + } else { + ConnectedBlockRuleSet obj = new ConnectedBlockRuleSet(); + byte nullBits = buf.getByte(offset); + obj.type = ConnectedBlockRuleSetType.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Stair", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - obj.roof = RoofConnectedBlockRuleSet.deserialize(buf, varPos1); - } + int varPos0 = offset + 10 + varPosBase0; + obj.stair = StairConnectedBlockRuleSet.deserialize(buf, varPos0); + } - return obj; + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Roof", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + obj.roof = RoofConnectedBlockRuleSet.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -57,6 +72,10 @@ public class ConnectedBlockRuleSet { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Stair", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; pos0 += StairConnectedBlockRuleSet.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -66,6 +85,10 @@ public class ConnectedBlockRuleSet { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Roof", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; pos1 += RoofConnectedBlockRuleSet.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -127,45 +150,42 @@ public class ConnectedBlockRuleSet { return ValidationResult.error("Buffer too small: expected at least 10 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int stairOffset = buffer.getIntLE(offset + 2); - if (stairOffset < 0) { - return ValidationResult.error("Invalid offset for Stair"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ConnectedBlockRuleSetType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 2); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for Stair"); + } + + int pos = offset + 10 + v; + ValidationResult stairResult = StairConnectedBlockRuleSet.validateStructure(buffer, pos); + if (!stairResult.isValid()) { + return ValidationResult.error("Invalid Stair: " + stairResult.error()); + } + + pos += StairConnectedBlockRuleSet.computeBytesConsumed(buffer, pos); } - int pos = offset + 10 + stairOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Stair"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for Roof"); + } + + int pos = offset + 10 + v; + ValidationResult roofResult = RoofConnectedBlockRuleSet.validateStructure(buffer, pos); + if (!roofResult.isValid()) { + return ValidationResult.error("Invalid Roof: " + roofResult.error()); + } + + pos += RoofConnectedBlockRuleSet.computeBytesConsumed(buffer, pos); } - ValidationResult stairResult = StairConnectedBlockRuleSet.validateStructure(buffer, pos); - if (!stairResult.isValid()) { - return ValidationResult.error("Invalid Stair: " + stairResult.error()); - } - - pos += StairConnectedBlockRuleSet.computeBytesConsumed(buffer, pos); + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int roofOffset = buffer.getIntLE(offset + 6); - if (roofOffset < 0) { - return ValidationResult.error("Invalid offset for Roof"); - } - - int posx = offset + 10 + roofOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Roof"); - } - - ValidationResult roofResult = RoofConnectedBlockRuleSet.validateStructure(buffer, posx); - if (!roofResult.isValid()) { - return ValidationResult.error("Invalid Roof: " + roofResult.error()); - } - - posx += RoofConnectedBlockRuleSet.computeBytesConsumed(buffer, posx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/CooldownConditionInteraction.java b/src/com/hypixel/hytale/protocol/CooldownConditionInteraction.java index 5bcded38..6f452360 100644 --- a/src/com/hypixel/hytale/protocol/CooldownConditionInteraction.java +++ b/src/com/hypixel/hytale/protocol/CooldownConditionInteraction.java @@ -70,92 +70,131 @@ public class CooldownConditionInteraction extends SimpleInteraction { @Nonnull public static CooldownConditionInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - CooldownConditionInteraction obj = new CooldownConditionInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 43 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 43) { + throw ProtocolException.bufferTooSmall("CooldownConditionInteraction", 43, buf.readableBytes() - offset); + } else { + CooldownConditionInteraction obj = new CooldownConditionInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 43 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 43 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 43 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 43 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 43 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 43 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 39); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("CooldownId", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 43 + varPosBase5; + int cooldownIdLen = VarInt.peek(buf, varPos5); + if (cooldownIdLen < 0) { + throw ProtocolException.invalidVarInt("CooldownId"); + } + + int cooldownIdVarIntLen = VarInt.size(cooldownIdLen); + if (cooldownIdLen > 4096000) { + throw ProtocolException.stringTooLong("CooldownId", cooldownIdLen, 4096000); + } + + if (varPos5 + cooldownIdVarIntLen + cooldownIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CooldownId", varPos5 + cooldownIdVarIntLen + cooldownIdLen, buf.readableBytes()); + } + + obj.cooldownId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 43 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 43 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 43 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 43 + buf.getIntLE(offset + 39); - int cooldownIdLen = VarInt.peek(buf, varPos5); - if (cooldownIdLen < 0) { - throw ProtocolException.negativeLength("CooldownId", cooldownIdLen); - } - - if (cooldownIdLen > 4096000) { - throw ProtocolException.stringTooLong("CooldownId", cooldownIdLen, 4096000); - } - - obj.cooldownId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -163,6 +202,10 @@ public class CooldownConditionInteraction extends SimpleInteraction { int maxEnd = 43; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 43 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -172,9 +215,13 @@ public class CooldownConditionInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 43 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -187,6 +234,10 @@ public class CooldownConditionInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 43 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -196,9 +247,13 @@ public class CooldownConditionInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 43 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -206,6 +261,10 @@ public class CooldownConditionInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 43 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -215,9 +274,13 @@ public class CooldownConditionInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 39); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("CooldownId", fieldOffset5, maxEnd); + } + int pos5 = offset + 43 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -371,146 +434,132 @@ public class CooldownConditionInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 43 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 43 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 43 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 43 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 43 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 43 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 43 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for CooldownId"); + } + + int posxx = offset + 43 + v; + int cooldownIdLen = VarInt.peek(buffer, posxx); + if (cooldownIdLen < 0) { + return ValidationResult.error("Invalid string length for CooldownId"); + } + + if (cooldownIdLen > 4096000) { + return ValidationResult.error("CooldownId exceeds max length 4096000"); + } + + posxx += VarInt.size(cooldownIdLen); + posxx += cooldownIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading CooldownId"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 43 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 43 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 43 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 43 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int cooldownIdOffset = buffer.getIntLE(offset + 39); - if (cooldownIdOffset < 0) { - return ValidationResult.error("Invalid offset for CooldownId"); - } - - int posxxxxx = offset + 43 + cooldownIdOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for CooldownId"); - } - - int cooldownIdLen = VarInt.peek(buffer, posxxxxx); - if (cooldownIdLen < 0) { - return ValidationResult.error("Invalid string length for CooldownId"); - } - - if (cooldownIdLen > 4096000) { - return ValidationResult.error("CooldownId exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += cooldownIdLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading CooldownId"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/CraftingRecipe.java b/src/com/hypixel/hytale/protocol/CraftingRecipe.java index 47029cee..5dcef22f 100644 --- a/src/com/hypixel/hytale/protocol/CraftingRecipe.java +++ b/src/com/hypixel/hytale/protocol/CraftingRecipe.java @@ -66,106 +66,140 @@ public class CraftingRecipe { @Nonnull public static CraftingRecipe deserialize(@Nonnull ByteBuf buf, int offset) { - CraftingRecipe obj = new CraftingRecipe(); - byte nullBits = buf.getByte(offset); - obj.knowledgeRequired = buf.getByte(offset + 1) != 0; - obj.timeSeconds = buf.getFloatLE(offset + 2); - obj.requiredMemoriesLevel = buf.getIntLE(offset + 6); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 30 + buf.getIntLE(offset + 10); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 30) { + throw ProtocolException.bufferTooSmall("CraftingRecipe", 30, buf.readableBytes() - offset); + } else { + CraftingRecipe obj = new CraftingRecipe(); + byte nullBits = buf.getByte(offset); + obj.knowledgeRequired = buf.getByte(offset + 1) != 0; + obj.timeSeconds = buf.getFloatLE(offset + 2); + obj.requiredMemoriesLevel = buf.getIntLE(offset + 6); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 10); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 30 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 14); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Inputs", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 30 + varPosBase1; + int inputsCount = VarInt.peek(buf, varPos1); + if (inputsCount < 0) { + throw ProtocolException.invalidVarInt("Inputs"); + } + + int varIntLen = VarInt.size(inputsCount); + if (inputsCount > 4096000) { + throw ProtocolException.arrayTooLong("Inputs", inputsCount, 4096000); + } + + if (varPos1 + varIntLen + inputsCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Inputs", varPos1 + varIntLen + inputsCount * 9, buf.readableBytes()); + } + + obj.inputs = new MaterialQuantity[inputsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < inputsCount; i++) { + obj.inputs[i] = MaterialQuantity.deserialize(buf, elemPos); + elemPos += MaterialQuantity.computeBytesConsumed(buf, elemPos); + } } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 18); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Outputs", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 30 + varPosBase2; + int outputsCount = VarInt.peek(buf, varPos2); + if (outputsCount < 0) { + throw ProtocolException.invalidVarInt("Outputs"); + } + + int varIntLenx = VarInt.size(outputsCount); + if (outputsCount > 4096000) { + throw ProtocolException.arrayTooLong("Outputs", outputsCount, 4096000); + } + + if (varPos2 + varIntLenx + outputsCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Outputs", varPos2 + varIntLenx + outputsCount * 9, buf.readableBytes()); + } + + obj.outputs = new MaterialQuantity[outputsCount]; + int elemPos = varPos2 + varIntLenx; + + for (int i = 0; i < outputsCount; i++) { + obj.outputs[i] = MaterialQuantity.deserialize(buf, elemPos); + elemPos += MaterialQuantity.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 22); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("PrimaryOutput", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 30 + varPosBase3; + obj.primaryOutput = MaterialQuantity.deserialize(buf, varPos3); + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 26); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("BenchRequirement", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 30 + varPosBase4; + int benchRequirementCount = VarInt.peek(buf, varPos4); + if (benchRequirementCount < 0) { + throw ProtocolException.invalidVarInt("BenchRequirement"); + } + + int varIntLenxx = VarInt.size(benchRequirementCount); + if (benchRequirementCount > 4096000) { + throw ProtocolException.arrayTooLong("BenchRequirement", benchRequirementCount, 4096000); + } + + if (varPos4 + varIntLenxx + benchRequirementCount * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BenchRequirement", varPos4 + varIntLenxx + benchRequirementCount * 6, buf.readableBytes()); + } + + obj.benchRequirement = new BenchRequirement[benchRequirementCount]; + int elemPos = varPos4 + varIntLenxx; + + for (int i = 0; i < benchRequirementCount; i++) { + obj.benchRequirement[i] = BenchRequirement.deserialize(buf, elemPos); + elemPos += BenchRequirement.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 30 + buf.getIntLE(offset + 14); - int inputsCount = VarInt.peek(buf, varPos1); - if (inputsCount < 0) { - throw ProtocolException.negativeLength("Inputs", inputsCount); - } - - if (inputsCount > 4096000) { - throw ProtocolException.arrayTooLong("Inputs", inputsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + inputsCount * 9L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Inputs", varPos1 + varIntLen + inputsCount * 9, buf.readableBytes()); - } - - obj.inputs = new MaterialQuantity[inputsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < inputsCount; i++) { - obj.inputs[i] = MaterialQuantity.deserialize(buf, elemPos); - elemPos += MaterialQuantity.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 30 + buf.getIntLE(offset + 18); - int outputsCount = VarInt.peek(buf, varPos2); - if (outputsCount < 0) { - throw ProtocolException.negativeLength("Outputs", outputsCount); - } - - if (outputsCount > 4096000) { - throw ProtocolException.arrayTooLong("Outputs", outputsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + outputsCount * 9L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Outputs", varPos2 + varIntLen + outputsCount * 9, buf.readableBytes()); - } - - obj.outputs = new MaterialQuantity[outputsCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < outputsCount; i++) { - obj.outputs[i] = MaterialQuantity.deserialize(buf, elemPos); - elemPos += MaterialQuantity.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 30 + buf.getIntLE(offset + 22); - obj.primaryOutput = MaterialQuantity.deserialize(buf, varPos3); - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 30 + buf.getIntLE(offset + 26); - int benchRequirementCount = VarInt.peek(buf, varPos4); - if (benchRequirementCount < 0) { - throw ProtocolException.negativeLength("BenchRequirement", benchRequirementCount); - } - - if (benchRequirementCount > 4096000) { - throw ProtocolException.arrayTooLong("BenchRequirement", benchRequirementCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos4); - if (varPos4 + varIntLen + benchRequirementCount * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("BenchRequirement", varPos4 + varIntLen + benchRequirementCount * 6, buf.readableBytes()); - } - - obj.benchRequirement = new BenchRequirement[benchRequirementCount]; - int elemPos = varPos4 + varIntLen; - - for (int i = 0; i < benchRequirementCount; i++) { - obj.benchRequirement[i] = BenchRequirement.deserialize(buf, elemPos); - elemPos += BenchRequirement.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -173,9 +207,13 @@ public class CraftingRecipe { int maxEnd = 30; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 10); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 30 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -183,9 +221,13 @@ public class CraftingRecipe { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 14); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Inputs", fieldOffset1, maxEnd); + } + int pos1 = offset + 30 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += MaterialQuantity.computeBytesConsumed(buf, pos1); @@ -198,9 +240,13 @@ public class CraftingRecipe { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 18); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Outputs", fieldOffset2, maxEnd); + } + int pos2 = offset + 30 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += MaterialQuantity.computeBytesConsumed(buf, pos2); @@ -213,6 +259,10 @@ public class CraftingRecipe { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 22); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("PrimaryOutput", fieldOffset3, maxEnd); + } + int pos3 = offset + 30 + fieldOffset3; pos3 += MaterialQuantity.computeBytesConsumed(buf, pos3); if (pos3 - offset > maxEnd) { @@ -222,9 +272,13 @@ public class CraftingRecipe { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 26); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("BenchRequirement", fieldOffset4, maxEnd); + } + int pos4 = offset + 30 + fieldOffset4; int arrLen = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4); + pos4 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos4 += BenchRequirement.computeBytesConsumed(buf, pos4); @@ -386,15 +440,11 @@ public class CraftingRecipe { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 10); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 30 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -404,7 +454,7 @@ public class CraftingRecipe { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -413,15 +463,11 @@ public class CraftingRecipe { if ((nullBits & 2) != 0) { int inputsOffset = buffer.getIntLE(offset + 14); - if (inputsOffset < 0) { + if (inputsOffset < 0 || inputsOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Inputs"); } int posx = offset + 30 + inputsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Inputs"); - } - int inputsCount = VarInt.peek(buffer, posx); if (inputsCount < 0) { return ValidationResult.error("Invalid array count for Inputs"); @@ -431,7 +477,7 @@ public class CraftingRecipe { return ValidationResult.error("Inputs exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(inputsCount); for (int i = 0; i < inputsCount; i++) { ValidationResult structResult = MaterialQuantity.validateStructure(buffer, posx); @@ -445,15 +491,11 @@ public class CraftingRecipe { if ((nullBits & 4) != 0) { int outputsOffset = buffer.getIntLE(offset + 18); - if (outputsOffset < 0) { + if (outputsOffset < 0 || outputsOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Outputs"); } int posxx = offset + 30 + outputsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Outputs"); - } - int outputsCount = VarInt.peek(buffer, posxx); if (outputsCount < 0) { return ValidationResult.error("Invalid array count for Outputs"); @@ -463,7 +505,7 @@ public class CraftingRecipe { return ValidationResult.error("Outputs exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(outputsCount); for (int i = 0; i < outputsCount; i++) { ValidationResult structResult = MaterialQuantity.validateStructure(buffer, posxx); @@ -477,15 +519,11 @@ public class CraftingRecipe { if ((nullBits & 8) != 0) { int primaryOutputOffset = buffer.getIntLE(offset + 22); - if (primaryOutputOffset < 0) { + if (primaryOutputOffset < 0 || primaryOutputOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for PrimaryOutput"); } int posxxx = offset + 30 + primaryOutputOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for PrimaryOutput"); - } - ValidationResult primaryOutputResult = MaterialQuantity.validateStructure(buffer, posxxx); if (!primaryOutputResult.isValid()) { return ValidationResult.error("Invalid PrimaryOutput: " + primaryOutputResult.error()); @@ -496,16 +534,12 @@ public class CraftingRecipe { if ((nullBits & 16) != 0) { int benchRequirementOffset = buffer.getIntLE(offset + 26); - if (benchRequirementOffset < 0) { + if (benchRequirementOffset < 0 || benchRequirementOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for BenchRequirement"); } - int posxxxx = offset + 30 + benchRequirementOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BenchRequirement"); - } - - int benchRequirementCount = VarInt.peek(buffer, posxxxx); + int posxxx = offset + 30 + benchRequirementOffset; + int benchRequirementCount = VarInt.peek(buffer, posxxx); if (benchRequirementCount < 0) { return ValidationResult.error("Invalid array count for BenchRequirement"); } @@ -514,15 +548,15 @@ public class CraftingRecipe { return ValidationResult.error("BenchRequirement exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxx += VarInt.size(benchRequirementCount); for (int i = 0; i < benchRequirementCount; i++) { - ValidationResult structResult = BenchRequirement.validateStructure(buffer, posxxxx); + ValidationResult structResult = BenchRequirement.validateStructure(buffer, posxxx); if (!structResult.isValid()) { return ValidationResult.error("Invalid BenchRequirement in BenchRequirement[" + i + "]: " + structResult.error()); } - posxxxx += BenchRequirement.computeBytesConsumed(buffer, posxxxx); + posxxx += BenchRequirement.computeBytesConsumed(buffer, posxxx); } } diff --git a/src/com/hypixel/hytale/protocol/DamageCause.java b/src/com/hypixel/hytale/protocol/DamageCause.java index da7052b5..0f26ddd1 100644 --- a/src/com/hypixel/hytale/protocol/DamageCause.java +++ b/src/com/hypixel/hytale/protocol/DamageCause.java @@ -35,37 +35,61 @@ public class DamageCause { @Nonnull public static DamageCause deserialize(@Nonnull ByteBuf buf, int offset) { - DamageCause obj = new DamageCause(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("DamageCause", 9, buf.readableBytes() - offset); + } else { + DamageCause obj = new DamageCause(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("DamageTextColor", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int damageTextColorLen = VarInt.peek(buf, varPos1); + if (damageTextColorLen < 0) { + throw ProtocolException.invalidVarInt("DamageTextColor"); + } + + int damageTextColorVarIntLen = VarInt.size(damageTextColorLen); + if (damageTextColorLen > 4096000) { + throw ProtocolException.stringTooLong("DamageTextColor", damageTextColorLen, 4096000); + } + + if (varPos1 + damageTextColorVarIntLen + damageTextColorLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("DamageTextColor", varPos1 + damageTextColorVarIntLen + damageTextColorLen, buf.readableBytes()); + } + + obj.damageTextColor = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int damageTextColorLen = VarInt.peek(buf, varPos1); - if (damageTextColorLen < 0) { - throw ProtocolException.negativeLength("DamageTextColor", damageTextColorLen); - } - - if (damageTextColorLen > 4096000) { - throw ProtocolException.stringTooLong("DamageTextColor", damageTextColorLen, 4096000); - } - - obj.damageTextColor = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,9 +97,13 @@ public class DamageCause { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -83,9 +111,13 @@ public class DamageCause { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("DamageTextColor", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -146,15 +178,11 @@ public class DamageCause { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 1); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 9 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -164,7 +192,7 @@ public class DamageCause { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -173,15 +201,11 @@ public class DamageCause { if ((nullBits & 2) != 0) { int damageTextColorOffset = buffer.getIntLE(offset + 5); - if (damageTextColorOffset < 0) { + if (damageTextColorOffset < 0 || damageTextColorOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for DamageTextColor"); } int posx = offset + 9 + damageTextColorOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DamageTextColor"); - } - int damageTextColorLen = VarInt.peek(buffer, posx); if (damageTextColorLen < 0) { return ValidationResult.error("Invalid string length for DamageTextColor"); @@ -191,7 +215,7 @@ public class DamageCause { return ValidationResult.error("DamageTextColor exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(damageTextColorLen); posx += damageTextColorLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading DamageTextColor"); diff --git a/src/com/hypixel/hytale/protocol/DamageEffects.java b/src/com/hypixel/hytale/protocol/DamageEffects.java index aa964d6c..3c39c768 100644 --- a/src/com/hypixel/hytale/protocol/DamageEffects.java +++ b/src/com/hypixel/hytale/protocol/DamageEffects.java @@ -37,60 +37,74 @@ public class DamageEffects { @Nonnull public static DamageEffects deserialize(@Nonnull ByteBuf buf, int offset) { - DamageEffects obj = new DamageEffects(); - byte nullBits = buf.getByte(offset); - obj.soundEventIndex = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 5); - int modelParticlesCount = VarInt.peek(buf, varPos0); - if (modelParticlesCount < 0) { - throw ProtocolException.negativeLength("ModelParticles", modelParticlesCount); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("DamageEffects", 13, buf.readableBytes() - offset); + } else { + DamageEffects obj = new DamageEffects(); + byte nullBits = buf.getByte(offset); + obj.soundEventIndex = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ModelParticles", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int modelParticlesCount = VarInt.peek(buf, varPos0); + if (modelParticlesCount < 0) { + throw ProtocolException.invalidVarInt("ModelParticles"); + } + + int varIntLen = VarInt.size(modelParticlesCount); + if (modelParticlesCount > 4096000) { + throw ProtocolException.arrayTooLong("ModelParticles", modelParticlesCount, 4096000); + } + + if (varPos0 + varIntLen + modelParticlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ModelParticles", varPos0 + varIntLen + modelParticlesCount * 34, buf.readableBytes()); + } + + obj.modelParticles = new ModelParticle[modelParticlesCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < modelParticlesCount; i++) { + obj.modelParticles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } } - if (modelParticlesCount > 4096000) { - throw ProtocolException.arrayTooLong("ModelParticles", modelParticlesCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("WorldParticles", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int worldParticlesCount = VarInt.peek(buf, varPos1); + if (worldParticlesCount < 0) { + throw ProtocolException.invalidVarInt("WorldParticles"); + } + + int varIntLenx = VarInt.size(worldParticlesCount); + if (worldParticlesCount > 4096000) { + throw ProtocolException.arrayTooLong("WorldParticles", worldParticlesCount, 4096000); + } + + if (varPos1 + varIntLenx + worldParticlesCount * 32L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("WorldParticles", varPos1 + varIntLenx + worldParticlesCount * 32, buf.readableBytes()); + } + + obj.worldParticles = new WorldParticle[worldParticlesCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < worldParticlesCount; i++) { + obj.worldParticles[i] = WorldParticle.deserialize(buf, elemPos); + elemPos += WorldParticle.computeBytesConsumed(buf, elemPos); + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + modelParticlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ModelParticles", varPos0 + varIntLen + modelParticlesCount * 34, buf.readableBytes()); - } - - obj.modelParticles = new ModelParticle[modelParticlesCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < modelParticlesCount; i++) { - obj.modelParticles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 9); - int worldParticlesCount = VarInt.peek(buf, varPos1); - if (worldParticlesCount < 0) { - throw ProtocolException.negativeLength("WorldParticles", worldParticlesCount); - } - - if (worldParticlesCount > 4096000) { - throw ProtocolException.arrayTooLong("WorldParticles", worldParticlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + worldParticlesCount * 32L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("WorldParticles", varPos1 + varIntLen + worldParticlesCount * 32, buf.readableBytes()); - } - - obj.worldParticles = new WorldParticle[worldParticlesCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < worldParticlesCount; i++) { - obj.worldParticles[i] = WorldParticle.deserialize(buf, elemPos); - elemPos += WorldParticle.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -98,9 +112,13 @@ public class DamageEffects { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ModelParticles", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += ModelParticle.computeBytesConsumed(buf, pos0); @@ -113,9 +131,13 @@ public class DamageEffects { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("WorldParticles", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += WorldParticle.computeBytesConsumed(buf, pos1); @@ -210,15 +232,11 @@ public class DamageEffects { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int modelParticlesOffset = buffer.getIntLE(offset + 5); - if (modelParticlesOffset < 0) { + if (modelParticlesOffset < 0 || modelParticlesOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for ModelParticles"); } int pos = offset + 13 + modelParticlesOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModelParticles"); - } - int modelParticlesCount = VarInt.peek(buffer, pos); if (modelParticlesCount < 0) { return ValidationResult.error("Invalid array count for ModelParticles"); @@ -228,7 +246,7 @@ public class DamageEffects { return ValidationResult.error("ModelParticles exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(modelParticlesCount); for (int i = 0; i < modelParticlesCount; i++) { ValidationResult structResult = ModelParticle.validateStructure(buffer, pos); @@ -242,15 +260,11 @@ public class DamageEffects { if ((nullBits & 2) != 0) { int worldParticlesOffset = buffer.getIntLE(offset + 9); - if (worldParticlesOffset < 0) { + if (worldParticlesOffset < 0 || worldParticlesOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for WorldParticles"); } int posx = offset + 13 + worldParticlesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for WorldParticles"); - } - int worldParticlesCount = VarInt.peek(buffer, posx); if (worldParticlesCount < 0) { return ValidationResult.error("Invalid array count for WorldParticles"); @@ -260,7 +274,7 @@ public class DamageEffects { return ValidationResult.error("WorldParticles exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(worldParticlesCount); for (int i = 0; i < worldParticlesCount; i++) { ValidationResult structResult = WorldParticle.validateStructure(buffer, posx); diff --git a/src/com/hypixel/hytale/protocol/DamageEntityInteraction.java b/src/com/hypixel/hytale/protocol/DamageEntityInteraction.java index 7dab7a97..19f9d8eb 100644 --- a/src/com/hypixel/hytale/protocol/DamageEntityInteraction.java +++ b/src/com/hypixel/hytale/protocol/DamageEntityInteraction.java @@ -91,170 +91,223 @@ public class DamageEntityInteraction extends Interaction { @Nonnull public static DamageEntityInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - DamageEntityInteraction obj = new DamageEntityInteraction(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 2)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 3); - obj.runTime = buf.getFloatLE(offset + 7); - obj.cancelOnItemChange = buf.getByte(offset + 11) != 0; - obj.next = buf.getIntLE(offset + 12); - obj.failed = buf.getIntLE(offset + 16); - obj.blocked = buf.getIntLE(offset + 20); - if ((nullBits[0] & 1) != 0) { - int varPos0 = offset + 60 + buf.getIntLE(offset + 24); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } - - if ((nullBits[0] & 2) != 0) { - int varPos1 = offset + 60 + buf.getIntLE(offset + 28); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); - } - - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); - } - } - } - - if ((nullBits[0] & 4) != 0) { - int varPos2 = offset + 60 + buf.getIntLE(offset + 32); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits[0] & 8) != 0) { - int varPos3 = offset + 60 + buf.getIntLE(offset + 36); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits[0] & 16) != 0) { - int varPos4 = offset + 60 + buf.getIntLE(offset + 40); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits[0] & 32) != 0) { - int varPos5 = offset + 60 + buf.getIntLE(offset + 44); - obj.damageEffects = DamageEffects.deserialize(buf, varPos5); - } - - if ((nullBits[0] & 64) != 0) { - int varPos6 = offset + 60 + buf.getIntLE(offset + 48); - int angledDamageCount = VarInt.peek(buf, varPos6); - if (angledDamageCount < 0) { - throw ProtocolException.negativeLength("AngledDamage", angledDamageCount); - } - - if (angledDamageCount > 4096000) { - throw ProtocolException.arrayTooLong("AngledDamage", angledDamageCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos6); - if (varPos6 + varIntLen + angledDamageCount * 21L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("AngledDamage", varPos6 + varIntLen + angledDamageCount * 21, buf.readableBytes()); - } - - obj.angledDamage = new AngledDamage[angledDamageCount]; - int elemPos = varPos6 + varIntLen; - - for (int ix = 0; ix < angledDamageCount; ix++) { - obj.angledDamage[ix] = AngledDamage.deserialize(buf, elemPos); - elemPos += AngledDamage.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[0] & 128) != 0) { - int varPos7 = offset + 60 + buf.getIntLE(offset + 52); - int targetedDamageCount = VarInt.peek(buf, varPos7); - if (targetedDamageCount < 0) { - throw ProtocolException.negativeLength("TargetedDamage", targetedDamageCount); - } - - if (targetedDamageCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("TargetedDamage", targetedDamageCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos7); - obj.targetedDamage = new HashMap<>(targetedDamageCount); - int dictPos = varPos7 + varIntLen; - - for (int ix = 0; ix < targetedDamageCount; ix++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 60) { + throw ProtocolException.bufferTooSmall("DamageEntityInteraction", 60, buf.readableBytes() - offset); + } else { + DamageEntityInteraction obj = new DamageEntityInteraction(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 2)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 3); + obj.runTime = buf.getFloatLE(offset + 7); + obj.cancelOnItemChange = buf.getByte(offset + 11) != 0; + obj.next = buf.getIntLE(offset + 12); + obj.failed = buf.getIntLE(offset + 16); + obj.blocked = buf.getIntLE(offset + 20); + if ((nullBits[0] & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 24); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 60 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); + } + + if ((nullBits[0] & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 28); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - TargetedDamage val = TargetedDamage.deserialize(buf, dictPos); - dictPos += TargetedDamage.computeBytesConsumed(buf, dictPos); - if (obj.targetedDamage.put(key, val) != null) { - throw ProtocolException.duplicateKey("targetedDamage", key); + int varPos1 = offset + 60 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } + + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits[0] & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 32); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 60 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits[0] & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 36); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 60 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits[0] & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 40); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 60 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits[0] & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 44); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("DamageEffects", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 60 + varPosBase5; + obj.damageEffects = DamageEffects.deserialize(buf, varPos5); + } + + if ((nullBits[0] & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 48); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("AngledDamage", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 60 + varPosBase6; + int angledDamageCount = VarInt.peek(buf, varPos6); + if (angledDamageCount < 0) { + throw ProtocolException.invalidVarInt("AngledDamage"); + } + + int varIntLenx = VarInt.size(angledDamageCount); + if (angledDamageCount > 4096000) { + throw ProtocolException.arrayTooLong("AngledDamage", angledDamageCount, 4096000); + } + + if (varPos6 + varIntLenx + angledDamageCount * 21L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AngledDamage", varPos6 + varIntLenx + angledDamageCount * 21, buf.readableBytes()); + } + + obj.angledDamage = new AngledDamage[angledDamageCount]; + int elemPos = varPos6 + varIntLenx; + + for (int ix = 0; ix < angledDamageCount; ix++) { + obj.angledDamage[ix] = AngledDamage.deserialize(buf, elemPos); + elemPos += AngledDamage.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase7 = buf.getIntLE(offset + 52); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("TargetedDamage", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 60 + varPosBase7; + int targetedDamageCount = VarInt.peek(buf, varPos7); + if (targetedDamageCount < 0) { + throw ProtocolException.invalidVarInt("TargetedDamage"); + } + + int varIntLenxx = VarInt.size(targetedDamageCount); + if (targetedDamageCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("TargetedDamage", targetedDamageCount, 4096000); + } + + obj.targetedDamage = new HashMap<>(targetedDamageCount); + int dictPos = varPos7 + varIntLenxx; + + for (int ix = 0; ix < targetedDamageCount; ix++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + TargetedDamage val = TargetedDamage.deserialize(buf, dictPos); + dictPos += TargetedDamage.computeBytesConsumed(buf, dictPos); + if (obj.targetedDamage.put(key, val) != null) { + throw ProtocolException.duplicateKey("targetedDamage", key); + } + } + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase8 = buf.getIntLE(offset + 56); + if (varPosBase8 < 0 || varPosBase8 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("EntityStatsOnHit", varPosBase8, buf.readableBytes()); + } + + int varPos8 = offset + 60 + varPosBase8; + int entityStatsOnHitCount = VarInt.peek(buf, varPos8); + if (entityStatsOnHitCount < 0) { + throw ProtocolException.invalidVarInt("EntityStatsOnHit"); + } + + int varIntLenxx = VarInt.size(entityStatsOnHitCount); + if (entityStatsOnHitCount > 4096000) { + throw ProtocolException.arrayTooLong("EntityStatsOnHit", entityStatsOnHitCount, 4096000); + } + + if (varPos8 + varIntLenxx + entityStatsOnHitCount * 13L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EntityStatsOnHit", varPos8 + varIntLenxx + entityStatsOnHitCount * 13, buf.readableBytes()); + } + + obj.entityStatsOnHit = new EntityStatOnHit[entityStatsOnHitCount]; + int elemPos = varPos8 + varIntLenxx; + + for (int ix = 0; ix < entityStatsOnHitCount; ix++) { + obj.entityStatsOnHit[ix] = EntityStatOnHit.deserialize(buf, elemPos); + elemPos += EntityStatOnHit.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits[1] & 1) != 0) { - int varPos8 = offset + 60 + buf.getIntLE(offset + 56); - int entityStatsOnHitCount = VarInt.peek(buf, varPos8); - if (entityStatsOnHitCount < 0) { - throw ProtocolException.negativeLength("EntityStatsOnHit", entityStatsOnHitCount); - } - - if (entityStatsOnHitCount > 4096000) { - throw ProtocolException.arrayTooLong("EntityStatsOnHit", entityStatsOnHitCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos8); - if (varPos8 + varIntLen + entityStatsOnHitCount * 13L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("EntityStatsOnHit", varPos8 + varIntLen + entityStatsOnHitCount * 13, buf.readableBytes()); - } - - obj.entityStatsOnHit = new EntityStatOnHit[entityStatsOnHitCount]; - int elemPos = varPos8 + varIntLen; - - for (int ix = 0; ix < entityStatsOnHitCount; ix++) { - obj.entityStatsOnHit[ix] = EntityStatOnHit.deserialize(buf, elemPos); - elemPos += EntityStatOnHit.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -262,6 +315,10 @@ public class DamageEntityInteraction extends Interaction { int maxEnd = 60; if ((nullBits[0] & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 24); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 60 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -271,9 +328,13 @@ public class DamageEntityInteraction extends Interaction { if ((nullBits[0] & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 28); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 60 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -286,6 +347,10 @@ public class DamageEntityInteraction extends Interaction { if ((nullBits[0] & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 32); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 60 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -295,9 +360,13 @@ public class DamageEntityInteraction extends Interaction { if ((nullBits[0] & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 36); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 60 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -305,6 +374,10 @@ public class DamageEntityInteraction extends Interaction { if ((nullBits[0] & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 40); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 60 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -314,6 +387,10 @@ public class DamageEntityInteraction extends Interaction { if ((nullBits[0] & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 44); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("DamageEffects", fieldOffset5, maxEnd); + } + int pos5 = offset + 60 + fieldOffset5; pos5 += DamageEffects.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { @@ -323,9 +400,13 @@ public class DamageEntityInteraction extends Interaction { if ((nullBits[0] & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 48); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("AngledDamage", fieldOffset6, maxEnd); + } + int pos6 = offset + 60 + fieldOffset6; int arrLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos6 += AngledDamage.computeBytesConsumed(buf, pos6); @@ -338,13 +419,17 @@ public class DamageEntityInteraction extends Interaction { if ((nullBits[0] & 128) != 0) { int fieldOffset7 = buf.getIntLE(offset + 52); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("TargetedDamage", fieldOffset7, maxEnd); + } + int pos7 = offset + 60 + fieldOffset7; int dictLen = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7); + pos7 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7) + sl; + pos7 += VarInt.size(sl) + sl; pos7 += TargetedDamage.computeBytesConsumed(buf, pos7); } @@ -355,9 +440,13 @@ public class DamageEntityInteraction extends Interaction { if ((nullBits[1] & 1) != 0) { int fieldOffset8 = buf.getIntLE(offset + 56); + if (fieldOffset8 < 0 || fieldOffset8 > buf.writerIndex() - offset - 60) { + throw ProtocolException.invalidOffset("EntityStatsOnHit", fieldOffset8, maxEnd); + } + int pos8 = offset + 60 + fieldOffset8; int arrLen = VarInt.peek(buf, pos8); - pos8 += VarInt.length(buf, pos8); + pos8 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos8 += EntityStatOnHit.computeBytesConsumed(buf, pos8); @@ -611,244 +700,218 @@ public class DamageEntityInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 60 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[0] & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 24); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 60 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits[0] & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 28); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 60 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits[0] & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 32); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 60 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits[0] & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 36); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 60 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits[0] & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 40); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 60 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits[0] & 32) != 0) { - int damageEffectsOffset = buffer.getIntLE(offset + 44); - if (damageEffectsOffset < 0) { - return ValidationResult.error("Invalid offset for DamageEffects"); - } - - int posxxxxx = offset + 60 + damageEffectsOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DamageEffects"); - } - - ValidationResult damageEffectsResult = DamageEffects.validateStructure(buffer, posxxxxx); - if (!damageEffectsResult.isValid()) { - return ValidationResult.error("Invalid DamageEffects: " + damageEffectsResult.error()); - } - - posxxxxx += DamageEffects.computeBytesConsumed(buffer, posxxxxx); - } - - if ((nullBits[0] & 64) != 0) { - int angledDamageOffset = buffer.getIntLE(offset + 48); - if (angledDamageOffset < 0) { - return ValidationResult.error("Invalid offset for AngledDamage"); - } - - int posxxxxxx = offset + 60 + angledDamageOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AngledDamage"); - } - - int angledDamageCount = VarInt.peek(buffer, posxxxxxx); - if (angledDamageCount < 0) { - return ValidationResult.error("Invalid array count for AngledDamage"); - } - - if (angledDamageCount > 4096000) { - return ValidationResult.error("AngledDamage exceeds max length 4096000"); - } - - posxxxxxx += VarInt.length(buffer, posxxxxxx); - - for (int i = 0; i < angledDamageCount; i++) { - ValidationResult structResult = AngledDamage.validateStructure(buffer, posxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid AngledDamage in AngledDamage[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 2) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits[0] & 1) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for Effects"); } - posxxxxxx += AngledDamage.computeBytesConsumed(buffer, posxxxxxx); + int pos = offset + 60 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } + + if ((nullBits[0] & 2) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 60 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } + } + + if ((nullBits[0] & 4) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 60 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits[0] & 8) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 60 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits[0] & 16) != 0) { + v = buffer.getIntLE(offset + 40); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 60 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits[0] & 32) != 0) { + v = buffer.getIntLE(offset + 44); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for DamageEffects"); + } + + int posxx = offset + 60 + v; + ValidationResult damageEffectsResult = DamageEffects.validateStructure(buffer, posxx); + if (!damageEffectsResult.isValid()) { + return ValidationResult.error("Invalid DamageEffects: " + damageEffectsResult.error()); + } + + posxx += DamageEffects.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits[0] & 64) != 0) { + v = buffer.getIntLE(offset + 48); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for AngledDamage"); + } + + int posxx = offset + 60 + v; + int angledDamageCount = VarInt.peek(buffer, posxx); + if (angledDamageCount < 0) { + return ValidationResult.error("Invalid array count for AngledDamage"); + } + + if (angledDamageCount > 4096000) { + return ValidationResult.error("AngledDamage exceeds max length 4096000"); + } + + posxx += VarInt.size(angledDamageCount); + + for (int i = 0; i < angledDamageCount; i++) { + ValidationResult structResult = AngledDamage.validateStructure(buffer, posxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid AngledDamage in AngledDamage[" + i + "]: " + structResult.error()); + } + + posxx += AngledDamage.computeBytesConsumed(buffer, posxx); + } + } + + if ((nullBits[0] & 128) != 0) { + v = buffer.getIntLE(offset + 52); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for TargetedDamage"); + } + + int posxxx = offset + 60 + v; + int targetedDamageCount = VarInt.peek(buffer, posxxx); + if (targetedDamageCount < 0) { + return ValidationResult.error("Invalid dictionary count for TargetedDamage"); + } + + if (targetedDamageCount > 4096000) { + return ValidationResult.error("TargetedDamage exceeds max length 4096000"); + } + + posxxx += VarInt.size(targetedDamageCount); + + for (int i = 0; i < targetedDamageCount; i++) { + int keyLen = VarInt.peek(buffer, posxxx); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxx += VarInt.size(keyLen); + posxxx += keyLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxxx += TargetedDamage.computeBytesConsumed(buffer, posxxx); + } + } + + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 56); + if (v < 0 || v > buffer.writerIndex() - offset - 60) { + return ValidationResult.error("Invalid offset for EntityStatsOnHit"); + } + + int posxxxx = offset + 60 + v; + int entityStatsOnHitCount = VarInt.peek(buffer, posxxxx); + if (entityStatsOnHitCount < 0) { + return ValidationResult.error("Invalid array count for EntityStatsOnHit"); + } + + if (entityStatsOnHitCount > 4096000) { + return ValidationResult.error("EntityStatsOnHit exceeds max length 4096000"); + } + + posxxxx += VarInt.size(entityStatsOnHitCount); + + for (int i = 0; i < entityStatsOnHitCount; i++) { + ValidationResult structResult = EntityStatOnHit.validateStructure(buffer, posxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid EntityStatOnHit in EntityStatsOnHit[" + i + "]: " + structResult.error()); + } + + posxxxx += EntityStatOnHit.computeBytesConsumed(buffer, posxxxx); + } + } + + return ValidationResult.OK; } - - if ((nullBits[0] & 128) != 0) { - int targetedDamageOffset = buffer.getIntLE(offset + 52); - if (targetedDamageOffset < 0) { - return ValidationResult.error("Invalid offset for TargetedDamage"); - } - - int posxxxxxxx = offset + 60 + targetedDamageOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TargetedDamage"); - } - - int targetedDamageCount = VarInt.peek(buffer, posxxxxxxx); - if (targetedDamageCount < 0) { - return ValidationResult.error("Invalid dictionary count for TargetedDamage"); - } - - if (targetedDamageCount > 4096000) { - return ValidationResult.error("TargetedDamage exceeds max length 4096000"); - } - - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); - - for (int i = 0; i < targetedDamageCount; i++) { - int keyLen = VarInt.peek(buffer, posxxxxxxx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); - posxxxxxxx += keyLen; - if (posxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - posxxxxxxx += TargetedDamage.computeBytesConsumed(buffer, posxxxxxxx); - } - } - - if ((nullBits[1] & 1) != 0) { - int entityStatsOnHitOffset = buffer.getIntLE(offset + 56); - if (entityStatsOnHitOffset < 0) { - return ValidationResult.error("Invalid offset for EntityStatsOnHit"); - } - - int posxxxxxxxx = offset + 60 + entityStatsOnHitOffset; - if (posxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EntityStatsOnHit"); - } - - int entityStatsOnHitCount = VarInt.peek(buffer, posxxxxxxxx); - if (entityStatsOnHitCount < 0) { - return ValidationResult.error("Invalid array count for EntityStatsOnHit"); - } - - if (entityStatsOnHitCount > 4096000) { - return ValidationResult.error("EntityStatsOnHit exceeds max length 4096000"); - } - - posxxxxxxxx += VarInt.length(buffer, posxxxxxxxx); - - for (int i = 0; i < entityStatsOnHitCount; i++) { - ValidationResult structResult = EntityStatOnHit.validateStructure(buffer, posxxxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid EntityStatOnHit in EntityStatsOnHit[" + i + "]: " + structResult.error()); - } - - posxxxxxxxx += EntityStatOnHit.computeBytesConsumed(buffer, posxxxxxxxx); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/DebugFlags.java b/src/com/hypixel/hytale/protocol/DebugFlags.java new file mode 100644 index 00000000..618220a8 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/DebugFlags.java @@ -0,0 +1,28 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum DebugFlags { + Fade(0), + NoWireframe(1), + NoSolid(2); + + public static final DebugFlags[] VALUES = values(); + private final int value; + + private DebugFlags(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static DebugFlags fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("DebugFlags", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/DeployableConfig.java b/src/com/hypixel/hytale/protocol/DeployableConfig.java index b9601466..907fa6d6 100644 --- a/src/com/hypixel/hytale/protocol/DeployableConfig.java +++ b/src/com/hypixel/hytale/protocol/DeployableConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -35,20 +36,34 @@ public class DeployableConfig { @Nonnull public static DeployableConfig deserialize(@Nonnull ByteBuf buf, int offset) { - DeployableConfig obj = new DeployableConfig(); - byte nullBits = buf.getByte(offset); - obj.allowPlaceOnWalls = buf.getByte(offset + 1) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - obj.model = Model.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("DeployableConfig", 10, buf.readableBytes() - offset); + } else { + DeployableConfig obj = new DeployableConfig(); + byte nullBits = buf.getByte(offset); + obj.allowPlaceOnWalls = buf.getByte(offset + 1) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Model", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - obj.modelPreview = Model.deserialize(buf, varPos1); - } + int varPos0 = offset + 10 + varPosBase0; + obj.model = Model.deserialize(buf, varPos0); + } - return obj; + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("ModelPreview", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + obj.modelPreview = Model.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -56,6 +71,10 @@ public class DeployableConfig { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Model", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; pos0 += Model.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -65,6 +84,10 @@ public class DeployableConfig { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("ModelPreview", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; pos1 += Model.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -128,15 +151,11 @@ public class DeployableConfig { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int modelOffset = buffer.getIntLE(offset + 2); - if (modelOffset < 0) { + if (modelOffset < 0 || modelOffset > buffer.writerIndex() - offset - 10) { return ValidationResult.error("Invalid offset for Model"); } int pos = offset + 10 + modelOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } - ValidationResult modelResult = Model.validateStructure(buffer, pos); if (!modelResult.isValid()) { return ValidationResult.error("Invalid Model: " + modelResult.error()); @@ -147,21 +166,17 @@ public class DeployableConfig { if ((nullBits & 2) != 0) { int modelPreviewOffset = buffer.getIntLE(offset + 6); - if (modelPreviewOffset < 0) { + if (modelPreviewOffset < 0 || modelPreviewOffset > buffer.writerIndex() - offset - 10) { return ValidationResult.error("Invalid offset for ModelPreview"); } - int posx = offset + 10 + modelPreviewOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModelPreview"); - } - - ValidationResult modelPreviewResult = Model.validateStructure(buffer, posx); + int pos = offset + 10 + modelPreviewOffset; + ValidationResult modelPreviewResult = Model.validateStructure(buffer, pos); if (!modelPreviewResult.isValid()) { return ValidationResult.error("Invalid ModelPreview: " + modelPreviewResult.error()); } - posx += Model.computeBytesConsumed(buffer, posx); + pos += Model.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/DetailBox.java b/src/com/hypixel/hytale/protocol/DetailBox.java index 9addf0d3..5296e2e6 100644 --- a/src/com/hypixel/hytale/protocol/DetailBox.java +++ b/src/com/hypixel/hytale/protocol/DetailBox.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class DetailBox { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -12,15 +15,15 @@ public class DetailBox { public static final int VARIABLE_FIELD_COUNT = 0; public static final int VARIABLE_BLOCK_START = 37; public static final int MAX_SIZE = 37; - @Nullable - public Vector3f offset; + @Nonnull + public Vector3fc offset = PacketIO.ZERO_VECTOR3; @Nullable public Hitbox box; public DetailBox() { } - public DetailBox(@Nullable Vector3f offset, @Nullable Hitbox box) { + public DetailBox(@Nonnull Vector3fc offset, @Nullable Hitbox box) { this.offset = offset; this.box = box; } @@ -32,17 +35,18 @@ public class DetailBox { @Nonnull public static DetailBox deserialize(@Nonnull ByteBuf buf, int offset) { - DetailBox obj = new DetailBox(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.offset = Vector3f.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 37) { + throw ProtocolException.bufferTooSmall("DetailBox", 37, buf.readableBytes() - offset); + } else { + DetailBox obj = new DetailBox(); + byte nullBits = buf.getByte(offset); + obj.offset = PacketIO.readVector3f(buf, offset + 1); + if ((nullBits & 1) != 0) { + obj.box = Hitbox.deserialize(buf, offset + 13); + } - if ((nullBits & 2) != 0) { - obj.box = Hitbox.deserialize(buf, offset + 13); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -51,21 +55,12 @@ public class DetailBox { public void serialize(@Nonnull ByteBuf buf) { byte nullBits = 0; - if (this.offset != null) { + if (this.box != null) { nullBits = (byte)(nullBits | 1); } - if (this.box != null) { - nullBits = (byte)(nullBits | 2); - } - buf.writeByte(nullBits); - if (this.offset != null) { - this.offset.serialize(buf); - } else { - buf.writeZero(12); - } - + PacketIO.writeVector3f(buf, this.offset); if (this.box != null) { this.box.serialize(buf); } else { @@ -78,12 +73,17 @@ public class DetailBox { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 37 ? ValidationResult.error("Buffer too small: expected at least 37 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 37) { + return ValidationResult.error("Buffer too small: expected at least 37 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public DetailBox clone() { DetailBox copy = new DetailBox(); - copy.offset = this.offset != null ? this.offset.clone() : null; + copy.offset = this.offset; copy.box = this.box != null ? this.box.clone() : null; return copy; } diff --git a/src/com/hypixel/hytale/protocol/Direction.java b/src/com/hypixel/hytale/protocol/Direction.java index 96f1d02e..103d5175 100644 --- a/src/com/hypixel/hytale/protocol/Direction.java +++ b/src/com/hypixel/hytale/protocol/Direction.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class Direction { @Nonnull public static Direction deserialize(@Nonnull ByteBuf buf, int offset) { - Direction obj = new Direction(); - obj.yaw = buf.getFloatLE(offset + 0); - obj.pitch = buf.getFloatLE(offset + 4); - obj.roll = buf.getFloatLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("Direction", 12, buf.readableBytes() - offset); + } else { + Direction obj = new Direction(); + obj.yaw = buf.getFloatLE(offset + 0); + obj.pitch = buf.getFloatLE(offset + 4); + obj.roll = buf.getFloatLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/DoubleParamValue.java b/src/com/hypixel/hytale/protocol/DoubleParamValue.java index 7f17bc3d..308c4878 100644 --- a/src/com/hypixel/hytale/protocol/DoubleParamValue.java +++ b/src/com/hypixel/hytale/protocol/DoubleParamValue.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class DoubleParamValue extends ParamValue { @Nonnull public static DoubleParamValue deserialize(@Nonnull ByteBuf buf, int offset) { - DoubleParamValue obj = new DoubleParamValue(); - obj.value = buf.getDoubleLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("DoubleParamValue", 8, buf.readableBytes() - offset); + } else { + DoubleParamValue obj = new DoubleParamValue(); + obj.value = buf.getDoubleLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/DynamicLightUpdate.java b/src/com/hypixel/hytale/protocol/DynamicLightUpdate.java index dad76a9d..a4b99b13 100644 --- a/src/com/hypixel/hytale/protocol/DynamicLightUpdate.java +++ b/src/com/hypixel/hytale/protocol/DynamicLightUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -27,9 +28,13 @@ public class DynamicLightUpdate extends ComponentUpdate { @Nonnull public static DynamicLightUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - DynamicLightUpdate obj = new DynamicLightUpdate(); - obj.dynamicLight = ColorLight.deserialize(buf, offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("DynamicLightUpdate", 4, buf.readableBytes() - offset); + } else { + DynamicLightUpdate obj = new DynamicLightUpdate(); + obj.dynamicLight = ColorLight.deserialize(buf, offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/EasingConfig.java b/src/com/hypixel/hytale/protocol/EasingConfig.java index 52a4537e..745501b7 100644 --- a/src/com/hypixel/hytale/protocol/EasingConfig.java +++ b/src/com/hypixel/hytale/protocol/EasingConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -30,10 +31,14 @@ public class EasingConfig { @Nonnull public static EasingConfig deserialize(@Nonnull ByteBuf buf, int offset) { - EasingConfig obj = new EasingConfig(); - obj.time = buf.getFloatLE(offset + 0); - obj.type = EasingType.fromValue(buf.getByte(offset + 4)); - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("EasingConfig", 5, buf.readableBytes() - offset); + } else { + EasingConfig obj = new EasingConfig(); + obj.time = buf.getFloatLE(offset + 0); + obj.type = EasingType.fromValue(buf.getByte(offset + 4)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -50,7 +55,12 @@ public class EasingConfig { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 5 ? ValidationResult.error("Buffer too small: expected at least 5 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 5) { + return ValidationResult.error("Buffer too small: expected at least 5 bytes"); + } else { + int v = buffer.getByte(offset + 4) & 255; + return v >= 31 ? ValidationResult.error("Invalid EasingType value for Type") : ValidationResult.OK; + } } public EasingConfig clone() { diff --git a/src/com/hypixel/hytale/protocol/Edge.java b/src/com/hypixel/hytale/protocol/Edge.java index 60c529a4..ecc2e26e 100644 --- a/src/com/hypixel/hytale/protocol/Edge.java +++ b/src/com/hypixel/hytale/protocol/Edge.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -31,14 +32,18 @@ public class Edge { @Nonnull public static Edge deserialize(@Nonnull ByteBuf buf, int offset) { - Edge obj = new Edge(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.color = ColorAlpha.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("Edge", 9, buf.readableBytes() - offset); + } else { + Edge obj = new Edge(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.color = ColorAlpha.deserialize(buf, offset + 1); + } - obj.width = buf.getFloatLE(offset + 5); - return obj; + obj.width = buf.getFloatLE(offset + 5); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -66,7 +71,12 @@ public class Edge { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 9 ? ValidationResult.error("Buffer too small: expected at least 9 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 9) { + return ValidationResult.error("Buffer too small: expected at least 9 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public Edge clone() { diff --git a/src/com/hypixel/hytale/protocol/EffectConditionInteraction.java b/src/com/hypixel/hytale/protocol/EffectConditionInteraction.java index 94a68729..bcde7b21 100644 --- a/src/com/hypixel/hytale/protocol/EffectConditionInteraction.java +++ b/src/com/hypixel/hytale/protocol/EffectConditionInteraction.java @@ -79,103 +79,137 @@ public class EffectConditionInteraction extends SimpleInteraction { @Nonnull public static EffectConditionInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - EffectConditionInteraction obj = new EffectConditionInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.match = Match.fromValue(buf.getByte(offset + 19)); - obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 20)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 45 + buf.getIntLE(offset + 21); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 45) { + throw ProtocolException.bufferTooSmall("EffectConditionInteraction", 45, buf.readableBytes() - offset); + } else { + EffectConditionInteraction obj = new EffectConditionInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.match = Match.fromValue(buf.getByte(offset + 19)); + obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 20)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 21); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 45 + buf.getIntLE(offset + 25); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 45 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 25); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 45 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 29); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 45 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 33); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 45 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 37); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 45 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 41); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("EntityEffects", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 45 + varPosBase5; + int entityEffectsCount = VarInt.peek(buf, varPos5); + if (entityEffectsCount < 0) { + throw ProtocolException.invalidVarInt("EntityEffects"); + } + + int varIntLenx = VarInt.size(entityEffectsCount); + if (entityEffectsCount > 4096000) { + throw ProtocolException.arrayTooLong("EntityEffects", entityEffectsCount, 4096000); + } + + if (varPos5 + varIntLenx + entityEffectsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EntityEffects", varPos5 + varIntLenx + entityEffectsCount * 4, buf.readableBytes()); + } + + obj.entityEffects = new int[entityEffectsCount]; + + for (int ix = 0; ix < entityEffectsCount; ix++) { + obj.entityEffects[ix] = buf.getIntLE(varPos5 + varIntLenx + ix * 4); + } + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 45 + buf.getIntLE(offset + 29); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 45 + buf.getIntLE(offset + 33); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 45 + buf.getIntLE(offset + 37); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 45 + buf.getIntLE(offset + 41); - int entityEffectsCount = VarInt.peek(buf, varPos5); - if (entityEffectsCount < 0) { - throw ProtocolException.negativeLength("EntityEffects", entityEffectsCount); - } - - if (entityEffectsCount > 4096000) { - throw ProtocolException.arrayTooLong("EntityEffects", entityEffectsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + entityEffectsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("EntityEffects", varPos5 + varIntLen + entityEffectsCount * 4, buf.readableBytes()); - } - - obj.entityEffects = new int[entityEffectsCount]; - - for (int ix = 0; ix < entityEffectsCount; ix++) { - obj.entityEffects[ix] = buf.getIntLE(varPos5 + varIntLen + ix * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -183,6 +217,10 @@ public class EffectConditionInteraction extends SimpleInteraction { int maxEnd = 45; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 21); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 45 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -192,9 +230,13 @@ public class EffectConditionInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 25); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 45 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -207,6 +249,10 @@ public class EffectConditionInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 29); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 45 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -216,9 +262,13 @@ public class EffectConditionInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 33); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 45 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -226,6 +276,10 @@ public class EffectConditionInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 37); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 45 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -235,9 +289,13 @@ public class EffectConditionInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 41); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("EntityEffects", fieldOffset5, maxEnd); + } + int pos5 = offset + 45 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + arrLen * 4; + pos5 += VarInt.size(arrLen) + arrLen * 4; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -401,146 +459,142 @@ public class EffectConditionInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 45 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 21); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 19) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid Match value for Match"); + } else { + v = buffer.getByte(offset + 20) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid InteractionTarget value for EntityTarget"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 21); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 45 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 45 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 25); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Settings"); + } - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 25); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } + int pos = offset + 45 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } - int posx = offset + 45 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } + pos += VarInt.size(settingsCount); - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } - posx += VarInt.length(buffer, posx); + pos++; + pos++; + } + } - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 29); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 45 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 45 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 45 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 41); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for EntityEffects"); + } + + int posxx = offset + 45 + v; + int entityEffectsCount = VarInt.peek(buffer, posxx); + if (entityEffectsCount < 0) { + return ValidationResult.error("Invalid array count for EntityEffects"); + } + + if (entityEffectsCount > 4096000) { + return ValidationResult.error("EntityEffects exceeds max length 4096000"); + } + + posxx += VarInt.size(entityEffectsCount); + posxx += entityEffectsCount * 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading EntityEffects"); + } + } + + return ValidationResult.OK; + } } } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 29); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 45 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 33); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 45 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 37); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 45 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int entityEffectsOffset = buffer.getIntLE(offset + 41); - if (entityEffectsOffset < 0) { - return ValidationResult.error("Invalid offset for EntityEffects"); - } - - int posxxxxx = offset + 45 + entityEffectsOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EntityEffects"); - } - - int entityEffectsCount = VarInt.peek(buffer, posxxxxx); - if (entityEffectsCount < 0) { - return ValidationResult.error("Invalid array count for EntityEffects"); - } - - if (entityEffectsCount > 4096000) { - return ValidationResult.error("EntityEffects exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += entityEffectsCount * 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading EntityEffects"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/EntityEffect.java b/src/com/hypixel/hytale/protocol/EntityEffect.java index 2dd07106..54c864b6 100644 --- a/src/com/hypixel/hytale/protocol/EntityEffect.java +++ b/src/com/hypixel/hytale/protocol/EntityEffect.java @@ -95,95 +95,144 @@ public class EntityEffect { @Nonnull public static EntityEffect deserialize(@Nonnull ByteBuf buf, int offset) { - EntityEffect obj = new EntityEffect(); - byte nullBits = buf.getByte(offset); - obj.worldRemovalSoundEventIndex = buf.getIntLE(offset + 1); - obj.localRemovalSoundEventIndex = buf.getIntLE(offset + 5); - obj.duration = buf.getFloatLE(offset + 9); - obj.infinite = buf.getByte(offset + 13) != 0; - obj.debuff = buf.getByte(offset + 14) != 0; - obj.overlapBehavior = OverlapBehavior.fromValue(buf.getByte(offset + 15)); - obj.damageCalculatorCooldown = buf.getDoubleLE(offset + 16); - obj.valueType = ValueType.fromValue(buf.getByte(offset + 24)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 49 + buf.getIntLE(offset + 25); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 49) { + throw ProtocolException.bufferTooSmall("EntityEffect", 49, buf.readableBytes() - offset); + } else { + EntityEffect obj = new EntityEffect(); + byte nullBits = buf.getByte(offset); + obj.worldRemovalSoundEventIndex = buf.getIntLE(offset + 1); + obj.localRemovalSoundEventIndex = buf.getIntLE(offset + 5); + obj.duration = buf.getFloatLE(offset + 9); + obj.infinite = buf.getByte(offset + 13) != 0; + obj.debuff = buf.getByte(offset + 14) != 0; + obj.overlapBehavior = OverlapBehavior.fromValue(buf.getByte(offset + 15)); + obj.damageCalculatorCooldown = buf.getDoubleLE(offset + 16); + obj.valueType = ValueType.fromValue(buf.getByte(offset + 24)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 25); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 49 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 29); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Name", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 49 + varPosBase1; + int nameLen = VarInt.peek(buf, varPos1); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos1 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos1 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 33); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("ApplicationEffects", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 49 + buf.getIntLE(offset + 29); - int nameLen = VarInt.peek(buf, varPos1); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); + int varPos2 = offset + 49 + varPosBase2; + obj.applicationEffects = ApplicationEffects.deserialize(buf, varPos2); } - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 37); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("ModelOverride", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 49 + varPosBase3; + obj.modelOverride = ModelOverride.deserialize(buf, varPos3); } - obj.name = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 41); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("StatusEffectIcon", varPosBase4, buf.readableBytes()); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 49 + buf.getIntLE(offset + 33); - obj.applicationEffects = ApplicationEffects.deserialize(buf, varPos2); - } + int varPos4 = offset + 49 + varPosBase4; + int statusEffectIconLen = VarInt.peek(buf, varPos4); + if (statusEffectIconLen < 0) { + throw ProtocolException.invalidVarInt("StatusEffectIcon"); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 49 + buf.getIntLE(offset + 37); - obj.modelOverride = ModelOverride.deserialize(buf, varPos3); - } + int statusEffectIconVarIntLen = VarInt.size(statusEffectIconLen); + if (statusEffectIconLen > 4096000) { + throw ProtocolException.stringTooLong("StatusEffectIcon", statusEffectIconLen, 4096000); + } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 49 + buf.getIntLE(offset + 41); - int statusEffectIconLen = VarInt.peek(buf, varPos4); - if (statusEffectIconLen < 0) { - throw ProtocolException.negativeLength("StatusEffectIcon", statusEffectIconLen); + if (varPos4 + statusEffectIconVarIntLen + statusEffectIconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("StatusEffectIcon", varPos4 + statusEffectIconVarIntLen + statusEffectIconLen, buf.readableBytes()); + } + + obj.statusEffectIcon = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); } - if (statusEffectIconLen > 4096000) { - throw ProtocolException.stringTooLong("StatusEffectIcon", statusEffectIconLen, 4096000); - } + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 45); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("StatModifiers", varPosBase5, buf.readableBytes()); + } - obj.statusEffectIcon = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } + int varPos5 = offset + 49 + varPosBase5; + int statModifiersCount = VarInt.peek(buf, varPos5); + if (statModifiersCount < 0) { + throw ProtocolException.invalidVarInt("StatModifiers"); + } - if ((nullBits & 32) != 0) { - int varPos5 = offset + 49 + buf.getIntLE(offset + 45); - int statModifiersCount = VarInt.peek(buf, varPos5); - if (statModifiersCount < 0) { - throw ProtocolException.negativeLength("StatModifiers", statModifiersCount); - } + int varIntLen = VarInt.size(statModifiersCount); + if (statModifiersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); + } - if (statModifiersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); - } + obj.statModifiers = new HashMap<>(statModifiersCount); + int dictPos = varPos5 + varIntLen; - int varIntLen = VarInt.length(buf, varPos5); - obj.statModifiers = new HashMap<>(statModifiersCount); - int dictPos = varPos5 + varIntLen; - - for (int i = 0; i < statModifiersCount; i++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.statModifiers.put(key, val) != null) { - throw ProtocolException.duplicateKey("statModifiers", key); + for (int i = 0; i < statModifiersCount; i++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.statModifiers.put(key, val) != null) { + throw ProtocolException.duplicateKey("statModifiers", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -191,9 +240,13 @@ public class EntityEffect { int maxEnd = 49; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 25); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 49 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -201,9 +254,13 @@ public class EntityEffect { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 29); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Name", fieldOffset1, maxEnd); + } + int pos1 = offset + 49 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -211,6 +268,10 @@ public class EntityEffect { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 33); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("ApplicationEffects", fieldOffset2, maxEnd); + } + int pos2 = offset + 49 + fieldOffset2; pos2 += ApplicationEffects.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -220,6 +281,10 @@ public class EntityEffect { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 37); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("ModelOverride", fieldOffset3, maxEnd); + } + int pos3 = offset + 49 + fieldOffset3; pos3 += ModelOverride.computeBytesConsumed(buf, pos3); if (pos3 - offset > maxEnd) { @@ -229,9 +294,13 @@ public class EntityEffect { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 41); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("StatusEffectIcon", fieldOffset4, maxEnd); + } + int pos4 = offset + 49 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -239,9 +308,13 @@ public class EntityEffect { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 45); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("StatModifiers", fieldOffset5, maxEnd); + } + int pos5 = offset + 49 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos5 += 4; @@ -391,161 +464,147 @@ public class EntityEffect { return ValidationResult.error("Buffer too small: expected at least 49 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int idOffset = buffer.getIntLE(offset + 25); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); - } + int v = buffer.getByte(offset + 15) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid OverlapBehavior value for OverlapBehavior"); + } else { + v = buffer.getByte(offset + 24) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ValueType value for ValueType"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 25); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Id"); + } - int pos = offset + 49 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } + int pos = offset + 49 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); - } - } - - if ((nullBits & 2) != 0) { - int nameOffset = buffer.getIntLE(offset + 29); - if (nameOffset < 0) { - return ValidationResult.error("Invalid offset for Name"); - } - - int posx = offset + 49 + nameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - - int nameLen = VarInt.peek(buffer, posx); - if (nameLen < 0) { - return ValidationResult.error("Invalid string length for Name"); - } - - if (nameLen > 4096000) { - return ValidationResult.error("Name exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += nameLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Name"); - } - } - - if ((nullBits & 4) != 0) { - int applicationEffectsOffset = buffer.getIntLE(offset + 33); - if (applicationEffectsOffset < 0) { - return ValidationResult.error("Invalid offset for ApplicationEffects"); - } - - int posxx = offset + 49 + applicationEffectsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ApplicationEffects"); - } - - ValidationResult applicationEffectsResult = ApplicationEffects.validateStructure(buffer, posxx); - if (!applicationEffectsResult.isValid()) { - return ValidationResult.error("Invalid ApplicationEffects: " + applicationEffectsResult.error()); - } - - posxx += ApplicationEffects.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int modelOverrideOffset = buffer.getIntLE(offset + 37); - if (modelOverrideOffset < 0) { - return ValidationResult.error("Invalid offset for ModelOverride"); - } - - int posxxx = offset + 49 + modelOverrideOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModelOverride"); - } - - ValidationResult modelOverrideResult = ModelOverride.validateStructure(buffer, posxxx); - if (!modelOverrideResult.isValid()) { - return ValidationResult.error("Invalid ModelOverride: " + modelOverrideResult.error()); - } - - posxxx += ModelOverride.computeBytesConsumed(buffer, posxxx); - } - - if ((nullBits & 16) != 0) { - int statusEffectIconOffset = buffer.getIntLE(offset + 41); - if (statusEffectIconOffset < 0) { - return ValidationResult.error("Invalid offset for StatusEffectIcon"); - } - - int posxxxx = offset + 49 + statusEffectIconOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for StatusEffectIcon"); - } - - int statusEffectIconLen = VarInt.peek(buffer, posxxxx); - if (statusEffectIconLen < 0) { - return ValidationResult.error("Invalid string length for StatusEffectIcon"); - } - - if (statusEffectIconLen > 4096000) { - return ValidationResult.error("StatusEffectIcon exceeds max length 4096000"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - posxxxx += statusEffectIconLen; - if (posxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading StatusEffectIcon"); - } - } - - if ((nullBits & 32) != 0) { - int statModifiersOffset = buffer.getIntLE(offset + 45); - if (statModifiersOffset < 0) { - return ValidationResult.error("Invalid offset for StatModifiers"); - } - - int posxxxxx = offset + 49 + statModifiersOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for StatModifiers"); - } - - int statModifiersCount = VarInt.peek(buffer, posxxxxx); - if (statModifiersCount < 0) { - return ValidationResult.error("Invalid dictionary count for StatModifiers"); - } - - if (statModifiersCount > 4096000) { - return ValidationResult.error("StatModifiers exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < statModifiersCount; i++) { - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } } - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 29); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Name"); + } + + int posx = offset + 49 + v; + int nameLen = VarInt.peek(buffer, posx); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } + + if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); + } + + posx += VarInt.size(nameLen); + posx += nameLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); + } } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for ApplicationEffects"); + } + + int posxx = offset + 49 + v; + ValidationResult applicationEffectsResult = ApplicationEffects.validateStructure(buffer, posxx); + if (!applicationEffectsResult.isValid()) { + return ValidationResult.error("Invalid ApplicationEffects: " + applicationEffectsResult.error()); + } + + posxx += ApplicationEffects.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for ModelOverride"); + } + + int posxx = offset + 49 + v; + ValidationResult modelOverrideResult = ModelOverride.validateStructure(buffer, posxx); + if (!modelOverrideResult.isValid()) { + return ValidationResult.error("Invalid ModelOverride: " + modelOverrideResult.error()); + } + + posxx += ModelOverride.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 41); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for StatusEffectIcon"); + } + + int posxx = offset + 49 + v; + int statusEffectIconLen = VarInt.peek(buffer, posxx); + if (statusEffectIconLen < 0) { + return ValidationResult.error("Invalid string length for StatusEffectIcon"); + } + + if (statusEffectIconLen > 4096000) { + return ValidationResult.error("StatusEffectIcon exceeds max length 4096000"); + } + + posxx += VarInt.size(statusEffectIconLen); + posxx += statusEffectIconLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading StatusEffectIcon"); + } + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 45); + if (v < 0 || v > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for StatModifiers"); + } + + int posxxx = offset + 49 + v; + int statModifiersCount = VarInt.peek(buffer, posxxx); + if (statModifiersCount < 0) { + return ValidationResult.error("Invalid dictionary count for StatModifiers"); + } + + if (statModifiersCount > 4096000) { + return ValidationResult.error("StatModifiers exceeds max length 4096000"); + } + + posxxx += VarInt.size(statModifiersCount); + + for (int i = 0; i < statModifiersCount; i++) { + posxxx += 4; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxxx += 4; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + return ValidationResult.OK; } } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/EntityEffectUpdate.java b/src/com/hypixel/hytale/protocol/EntityEffectUpdate.java index ffd6c071..af00ffcc 100644 --- a/src/com/hypixel/hytale/protocol/EntityEffectUpdate.java +++ b/src/com/hypixel/hytale/protocol/EntityEffectUpdate.java @@ -47,30 +47,38 @@ public class EntityEffectUpdate { @Nonnull public static EntityEffectUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - EntityEffectUpdate obj = new EntityEffectUpdate(); - byte nullBits = buf.getByte(offset); - obj.type = EffectOp.fromValue(buf.getByte(offset + 1)); - obj.id = buf.getIntLE(offset + 2); - obj.remainingTime = buf.getFloatLE(offset + 6); - obj.infinite = buf.getByte(offset + 10) != 0; - obj.debuff = buf.getByte(offset + 11) != 0; - int pos = offset + 12; - if ((nullBits & 1) != 0) { - int statusEffectIconLen = VarInt.peek(buf, pos); - if (statusEffectIconLen < 0) { - throw ProtocolException.negativeLength("StatusEffectIcon", statusEffectIconLen); + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("EntityEffectUpdate", 12, buf.readableBytes() - offset); + } else { + EntityEffectUpdate obj = new EntityEffectUpdate(); + byte nullBits = buf.getByte(offset); + obj.type = EffectOp.fromValue(buf.getByte(offset + 1)); + obj.id = buf.getIntLE(offset + 2); + obj.remainingTime = buf.getFloatLE(offset + 6); + obj.infinite = buf.getByte(offset + 10) != 0; + obj.debuff = buf.getByte(offset + 11) != 0; + int pos = offset + 12; + if ((nullBits & 1) != 0) { + int statusEffectIconLen = VarInt.peek(buf, pos); + if (statusEffectIconLen < 0) { + throw ProtocolException.invalidVarInt("StatusEffectIcon"); + } + + int statusEffectIconVarLen = VarInt.size(statusEffectIconLen); + if (statusEffectIconLen > 4096000) { + throw ProtocolException.stringTooLong("StatusEffectIcon", statusEffectIconLen, 4096000); + } + + if (pos + statusEffectIconVarLen + statusEffectIconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("StatusEffectIcon", pos + statusEffectIconVarLen + statusEffectIconLen, buf.readableBytes()); + } + + obj.statusEffectIcon = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += statusEffectIconVarLen + statusEffectIconLen; } - if (statusEffectIconLen > 4096000) { - throw ProtocolException.stringTooLong("StatusEffectIcon", statusEffectIconLen, 4096000); - } - - int statusEffectIconVarLen = VarInt.length(buf, pos); - obj.statusEffectIcon = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += statusEffectIconVarLen + statusEffectIconLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,7 +86,7 @@ public class EntityEffectUpdate { int pos = offset + 12; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -115,25 +123,30 @@ public class EntityEffectUpdate { return ValidationResult.error("Buffer too small: expected at least 12 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 12; - if ((nullBits & 1) != 0) { - int statusEffectIconLen = VarInt.peek(buffer, pos); - if (statusEffectIconLen < 0) { - return ValidationResult.error("Invalid string length for StatusEffectIcon"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid EffectOp value for Type"); + } else { + v = offset + 12; + if ((nullBits & 1) != 0) { + int statusEffectIconLen = VarInt.peek(buffer, v); + if (statusEffectIconLen < 0) { + return ValidationResult.error("Invalid string length for StatusEffectIcon"); + } + + if (statusEffectIconLen > 4096000) { + return ValidationResult.error("StatusEffectIcon exceeds max length 4096000"); + } + + v += VarInt.size(statusEffectIconLen); + v += statusEffectIconLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading StatusEffectIcon"); + } } - if (statusEffectIconLen > 4096000) { - return ValidationResult.error("StatusEffectIcon exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += statusEffectIconLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading StatusEffectIcon"); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/EntityEffectsUpdate.java b/src/com/hypixel/hytale/protocol/EntityEffectsUpdate.java index 9cb51e13..fe1dec9d 100644 --- a/src/com/hypixel/hytale/protocol/EntityEffectsUpdate.java +++ b/src/com/hypixel/hytale/protocol/EntityEffectsUpdate.java @@ -33,12 +33,12 @@ public class EntityEffectsUpdate extends ComponentUpdate { int pos = offset + 0; int entityEffectUpdatesCount = VarInt.peek(buf, pos); if (entityEffectUpdatesCount < 0) { - throw ProtocolException.negativeLength("EntityEffectUpdates", entityEffectUpdatesCount); - } else if (entityEffectUpdatesCount > 4096000) { - throw ProtocolException.arrayTooLong("EntityEffectUpdates", entityEffectUpdatesCount, 4096000); + throw ProtocolException.invalidVarInt("EntityEffectUpdates"); } else { int entityEffectUpdatesVarLen = VarInt.size(entityEffectUpdatesCount); - if (pos + entityEffectUpdatesVarLen + entityEffectUpdatesCount * 12L > buf.readableBytes()) { + if (entityEffectUpdatesCount > 4096000) { + throw ProtocolException.arrayTooLong("EntityEffectUpdates", entityEffectUpdatesCount, 4096000); + } else if (pos + entityEffectUpdatesVarLen + entityEffectUpdatesCount * 12L > buf.readableBytes()) { throw ProtocolException.bufferTooSmall("EntityEffectUpdates", pos + entityEffectUpdatesVarLen + entityEffectUpdatesCount * 12, buf.readableBytes()); } else { pos += entityEffectUpdatesVarLen; @@ -57,7 +57,7 @@ public class EntityEffectsUpdate extends ComponentUpdate { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 0; int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += EntityEffectUpdate.computeBytesConsumed(buf, pos); @@ -105,7 +105,7 @@ public class EntityEffectsUpdate extends ComponentUpdate { } else if (entityEffectUpdatesCount > 4096000) { return ValidationResult.error("EntityEffectUpdates exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(entityEffectUpdatesCount); for (int i = 0; i < entityEffectUpdatesCount; i++) { ValidationResult structResult = EntityEffectUpdate.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/EntityMatcher.java b/src/com/hypixel/hytale/protocol/EntityMatcher.java index 36349693..e941c32b 100644 --- a/src/com/hypixel/hytale/protocol/EntityMatcher.java +++ b/src/com/hypixel/hytale/protocol/EntityMatcher.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -30,10 +31,14 @@ public class EntityMatcher { @Nonnull public static EntityMatcher deserialize(@Nonnull ByteBuf buf, int offset) { - EntityMatcher obj = new EntityMatcher(); - obj.type = EntityMatcherType.fromValue(buf.getByte(offset + 0)); - obj.invert = buf.getByte(offset + 1) != 0; - return obj; + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("EntityMatcher", 2, buf.readableBytes() - offset); + } else { + EntityMatcher obj = new EntityMatcher(); + obj.type = EntityMatcherType.fromValue(buf.getByte(offset + 0)); + obj.invert = buf.getByte(offset + 1) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -50,7 +55,12 @@ public class EntityMatcher { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 2 ? ValidationResult.error("Buffer too small: expected at least 2 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 2) { + return ValidationResult.error("Buffer too small: expected at least 2 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 3 ? ValidationResult.error("Invalid EntityMatcherType value for Type") : ValidationResult.OK; + } } public EntityMatcher clone() { diff --git a/src/com/hypixel/hytale/protocol/EntityStatEffects.java b/src/com/hypixel/hytale/protocol/EntityStatEffects.java index 906ec4dd..d6ad7a34 100644 --- a/src/com/hypixel/hytale/protocol/EntityStatEffects.java +++ b/src/com/hypixel/hytale/protocol/EntityStatEffects.java @@ -36,36 +36,40 @@ public class EntityStatEffects { @Nonnull public static EntityStatEffects deserialize(@Nonnull ByteBuf buf, int offset) { - EntityStatEffects obj = new EntityStatEffects(); - byte nullBits = buf.getByte(offset); - obj.triggerAtZero = buf.getByte(offset + 1) != 0; - obj.soundEventIndex = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int particlesCount = VarInt.peek(buf, pos); - if (particlesCount < 0) { - throw ProtocolException.negativeLength("Particles", particlesCount); + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("EntityStatEffects", 6, buf.readableBytes() - offset); + } else { + EntityStatEffects obj = new EntityStatEffects(); + byte nullBits = buf.getByte(offset); + obj.triggerAtZero = buf.getByte(offset + 1) != 0; + obj.soundEventIndex = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int particlesCount = VarInt.peek(buf, pos); + if (particlesCount < 0) { + throw ProtocolException.invalidVarInt("Particles"); + } + + int particlesVarLen = VarInt.size(particlesCount); + if (particlesCount > 4096000) { + throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); + } + + if (pos + particlesVarLen + particlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Particles", pos + particlesVarLen + particlesCount * 34, buf.readableBytes()); + } + + pos += particlesVarLen; + obj.particles = new ModelParticle[particlesCount]; + + for (int i = 0; i < particlesCount; i++) { + obj.particles[i] = ModelParticle.deserialize(buf, pos); + pos += ModelParticle.computeBytesConsumed(buf, pos); + } } - if (particlesCount > 4096000) { - throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); - } - - int particlesVarLen = VarInt.size(particlesCount); - if (pos + particlesVarLen + particlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Particles", pos + particlesVarLen + particlesCount * 34, buf.readableBytes()); - } - - pos += particlesVarLen; - obj.particles = new ModelParticle[particlesCount]; - - for (int i = 0; i < particlesCount; i++) { - obj.particles[i] = ModelParticle.deserialize(buf, pos); - pos += ModelParticle.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,7 +77,7 @@ public class EntityStatEffects { int pos = offset + 6; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += ModelParticle.computeBytesConsumed(buf, pos); @@ -136,7 +140,7 @@ public class EntityStatEffects { return ValidationResult.error("Particles exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(particlesCount); for (int i = 0; i < particlesCount; i++) { ValidationResult structResult = ModelParticle.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/EntityStatOnHit.java b/src/com/hypixel/hytale/protocol/EntityStatOnHit.java index 9c00c852..c409155c 100644 --- a/src/com/hypixel/hytale/protocol/EntityStatOnHit.java +++ b/src/com/hypixel/hytale/protocol/EntityStatOnHit.java @@ -39,40 +39,44 @@ public class EntityStatOnHit { @Nonnull public static EntityStatOnHit deserialize(@Nonnull ByteBuf buf, int offset) { - EntityStatOnHit obj = new EntityStatOnHit(); - byte nullBits = buf.getByte(offset); - obj.entityStatIndex = buf.getIntLE(offset + 1); - obj.amount = buf.getFloatLE(offset + 5); - obj.multiplierPerExtraEntityHit = buf.getFloatLE(offset + 9); - int pos = offset + 13; - if ((nullBits & 1) != 0) { - int multipliersPerEntitiesHitCount = VarInt.peek(buf, pos); - if (multipliersPerEntitiesHitCount < 0) { - throw ProtocolException.negativeLength("MultipliersPerEntitiesHit", multipliersPerEntitiesHitCount); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("EntityStatOnHit", 13, buf.readableBytes() - offset); + } else { + EntityStatOnHit obj = new EntityStatOnHit(); + byte nullBits = buf.getByte(offset); + obj.entityStatIndex = buf.getIntLE(offset + 1); + obj.amount = buf.getFloatLE(offset + 5); + obj.multiplierPerExtraEntityHit = buf.getFloatLE(offset + 9); + int pos = offset + 13; + if ((nullBits & 1) != 0) { + int multipliersPerEntitiesHitCount = VarInt.peek(buf, pos); + if (multipliersPerEntitiesHitCount < 0) { + throw ProtocolException.invalidVarInt("MultipliersPerEntitiesHit"); + } + + int multipliersPerEntitiesHitVarLen = VarInt.size(multipliersPerEntitiesHitCount); + if (multipliersPerEntitiesHitCount > 4096000) { + throw ProtocolException.arrayTooLong("MultipliersPerEntitiesHit", multipliersPerEntitiesHitCount, 4096000); + } + + if (pos + multipliersPerEntitiesHitVarLen + multipliersPerEntitiesHitCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "MultipliersPerEntitiesHit", pos + multipliersPerEntitiesHitVarLen + multipliersPerEntitiesHitCount * 4, buf.readableBytes() + ); + } + + pos += multipliersPerEntitiesHitVarLen; + obj.multipliersPerEntitiesHit = new float[multipliersPerEntitiesHitCount]; + + for (int i = 0; i < multipliersPerEntitiesHitCount; i++) { + obj.multipliersPerEntitiesHit[i] = buf.getFloatLE(pos + i * 4); + } + + pos += multipliersPerEntitiesHitCount * 4; } - if (multipliersPerEntitiesHitCount > 4096000) { - throw ProtocolException.arrayTooLong("MultipliersPerEntitiesHit", multipliersPerEntitiesHitCount, 4096000); - } - - int multipliersPerEntitiesHitVarLen = VarInt.size(multipliersPerEntitiesHitCount); - if (pos + multipliersPerEntitiesHitVarLen + multipliersPerEntitiesHitCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall( - "MultipliersPerEntitiesHit", pos + multipliersPerEntitiesHitVarLen + multipliersPerEntitiesHitCount * 4, buf.readableBytes() - ); - } - - pos += multipliersPerEntitiesHitVarLen; - obj.multipliersPerEntitiesHit = new float[multipliersPerEntitiesHitCount]; - - for (int i = 0; i < multipliersPerEntitiesHitCount; i++) { - obj.multipliersPerEntitiesHit[i] = buf.getFloatLE(pos + i * 4); - } - - pos += multipliersPerEntitiesHitCount * 4; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class EntityStatOnHit { int pos = offset + 13; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 4; + pos += VarInt.size(arrLen) + arrLen * 4; } return pos - offset; @@ -134,7 +138,7 @@ public class EntityStatOnHit { return ValidationResult.error("MultipliersPerEntitiesHit exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(multipliersPerEntitiesHitCount); pos += multipliersPerEntitiesHitCount * 4; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading MultipliersPerEntitiesHit"); diff --git a/src/com/hypixel/hytale/protocol/EntityStatType.java b/src/com/hypixel/hytale/protocol/EntityStatType.java index 42e0b972..686996e6 100644 --- a/src/com/hypixel/hytale/protocol/EntityStatType.java +++ b/src/com/hypixel/hytale/protocol/EntityStatType.java @@ -64,38 +64,62 @@ public class EntityStatType { @Nonnull public static EntityStatType deserialize(@Nonnull ByteBuf buf, int offset) { - EntityStatType obj = new EntityStatType(); - byte nullBits = buf.getByte(offset); - obj.value = buf.getFloatLE(offset + 1); - obj.min = buf.getFloatLE(offset + 5); - obj.max = buf.getFloatLE(offset + 9); - obj.resetBehavior = EntityStatResetBehavior.fromValue(buf.getByte(offset + 13)); - obj.hideFromTooltip = buf.getByte(offset + 14) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 27 + buf.getIntLE(offset + 15); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 27) { + throw ProtocolException.bufferTooSmall("EntityStatType", 27, buf.readableBytes() - offset); + } else { + EntityStatType obj = new EntityStatType(); + byte nullBits = buf.getByte(offset); + obj.value = buf.getFloatLE(offset + 1); + obj.min = buf.getFloatLE(offset + 5); + obj.max = buf.getFloatLE(offset + 9); + obj.resetBehavior = EntityStatResetBehavior.fromValue(buf.getByte(offset + 13)); + obj.hideFromTooltip = buf.getByte(offset + 14) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 15); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 27 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 19); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("MinValueEffects", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 27 + varPosBase1; + obj.minValueEffects = EntityStatEffects.deserialize(buf, varPos1); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 23); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("MaxValueEffects", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 27 + buf.getIntLE(offset + 19); - obj.minValueEffects = EntityStatEffects.deserialize(buf, varPos1); - } + int varPos2 = offset + 27 + varPosBase2; + obj.maxValueEffects = EntityStatEffects.deserialize(buf, varPos2); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 27 + buf.getIntLE(offset + 23); - obj.maxValueEffects = EntityStatEffects.deserialize(buf, varPos2); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -103,9 +127,13 @@ public class EntityStatType { int maxEnd = 27; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 15); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 27 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -113,6 +141,10 @@ public class EntityStatType { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 19); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("MinValueEffects", fieldOffset1, maxEnd); + } + int pos1 = offset + 27 + fieldOffset1; pos1 += EntityStatEffects.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -122,6 +154,10 @@ public class EntityStatType { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 23); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("MaxValueEffects", fieldOffset2, maxEnd); + } + int pos2 = offset + 27 + fieldOffset2; pos2 += EntityStatEffects.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -204,72 +240,65 @@ public class EntityStatType { return ValidationResult.error("Buffer too small: expected at least 27 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int idOffset = buffer.getIntLE(offset + 15); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); + int v = buffer.getByte(offset + 13) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid EntityStatResetBehavior value for ResetBehavior"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 27) { + return ValidationResult.error("Invalid offset for Id"); + } + + int pos = offset + 27 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } } - int pos = offset + 27 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 27) { + return ValidationResult.error("Invalid offset for MinValueEffects"); + } + + int posx = offset + 27 + v; + ValidationResult minValueEffectsResult = EntityStatEffects.validateStructure(buffer, posx); + if (!minValueEffectsResult.isValid()) { + return ValidationResult.error("Invalid MinValueEffects: " + minValueEffectsResult.error()); + } + + posx += EntityStatEffects.computeBytesConsumed(buffer, posx); } - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 27) { + return ValidationResult.error("Invalid offset for MaxValueEffects"); + } + + int posx = offset + 27 + v; + ValidationResult maxValueEffectsResult = EntityStatEffects.validateStructure(buffer, posx); + if (!maxValueEffectsResult.isValid()) { + return ValidationResult.error("Invalid MaxValueEffects: " + maxValueEffectsResult.error()); + } + + posx += EntityStatEffects.computeBytesConsumed(buffer, posx); } - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); - } + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int minValueEffectsOffset = buffer.getIntLE(offset + 19); - if (minValueEffectsOffset < 0) { - return ValidationResult.error("Invalid offset for MinValueEffects"); - } - - int posx = offset + 27 + minValueEffectsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MinValueEffects"); - } - - ValidationResult minValueEffectsResult = EntityStatEffects.validateStructure(buffer, posx); - if (!minValueEffectsResult.isValid()) { - return ValidationResult.error("Invalid MinValueEffects: " + minValueEffectsResult.error()); - } - - posx += EntityStatEffects.computeBytesConsumed(buffer, posx); - } - - if ((nullBits & 4) != 0) { - int maxValueEffectsOffset = buffer.getIntLE(offset + 23); - if (maxValueEffectsOffset < 0) { - return ValidationResult.error("Invalid offset for MaxValueEffects"); - } - - int posxx = offset + 27 + maxValueEffectsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaxValueEffects"); - } - - ValidationResult maxValueEffectsResult = EntityStatEffects.validateStructure(buffer, posxx); - if (!maxValueEffectsResult.isValid()) { - return ValidationResult.error("Invalid MaxValueEffects: " + maxValueEffectsResult.error()); - } - - posxx += EntityStatEffects.computeBytesConsumed(buffer, posxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/EntityStatUpdate.java b/src/com/hypixel/hytale/protocol/EntityStatUpdate.java index 6a2b5ce8..a54f2da3 100644 --- a/src/com/hypixel/hytale/protocol/EntityStatUpdate.java +++ b/src/com/hypixel/hytale/protocol/EntityStatUpdate.java @@ -59,66 +59,89 @@ public class EntityStatUpdate { @Nonnull public static EntityStatUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - EntityStatUpdate obj = new EntityStatUpdate(); - byte nullBits = buf.getByte(offset); - obj.op = EntityStatOp.fromValue(buf.getByte(offset + 1)); - obj.predictable = buf.getByte(offset + 2) != 0; - obj.value = buf.getFloatLE(offset + 3); - if ((nullBits & 1) != 0) { - obj.modifier = Modifier.deserialize(buf, offset + 7); - } - - if ((nullBits & 2) != 0) { - int varPos0 = offset + 21 + buf.getIntLE(offset + 13); - int modifiersCount = VarInt.peek(buf, varPos0); - if (modifiersCount < 0) { - throw ProtocolException.negativeLength("Modifiers", modifiersCount); + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("EntityStatUpdate", 21, buf.readableBytes() - offset); + } else { + EntityStatUpdate obj = new EntityStatUpdate(); + byte nullBits = buf.getByte(offset); + obj.op = EntityStatOp.fromValue(buf.getByte(offset + 1)); + obj.predictable = buf.getByte(offset + 2) != 0; + obj.value = buf.getFloatLE(offset + 3); + if ((nullBits & 1) != 0) { + obj.modifier = Modifier.deserialize(buf, offset + 7); } - if (modifiersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Modifiers", modifiersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - obj.modifiers = new HashMap<>(modifiersCount); - int dictPos = varPos0 + varIntLen; - - for (int i = 0; i < modifiersCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 13); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Modifiers", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 21 + varPosBase0; + int modifiersCount = VarInt.peek(buf, varPos0); + if (modifiersCount < 0) { + throw ProtocolException.invalidVarInt("Modifiers"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - Modifier val = Modifier.deserialize(buf, dictPos); - dictPos += Modifier.computeBytesConsumed(buf, dictPos); - if (obj.modifiers.put(key, val) != null) { - throw ProtocolException.duplicateKey("modifiers", key); + int varIntLen = VarInt.size(modifiersCount); + if (modifiersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Modifiers", modifiersCount, 4096000); + } + + obj.modifiers = new HashMap<>(modifiersCount); + int dictPos = varPos0 + varIntLen; + + for (int i = 0; i < modifiersCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + Modifier val = Modifier.deserialize(buf, dictPos); + dictPos += Modifier.computeBytesConsumed(buf, dictPos); + if (obj.modifiers.put(key, val) != null) { + throw ProtocolException.duplicateKey("modifiers", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos1 = offset + 21 + buf.getIntLE(offset + 17); - int modifierKeyLen = VarInt.peek(buf, varPos1); - if (modifierKeyLen < 0) { - throw ProtocolException.negativeLength("ModifierKey", modifierKeyLen); + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 17); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("ModifierKey", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + int modifierKeyLen = VarInt.peek(buf, varPos1); + if (modifierKeyLen < 0) { + throw ProtocolException.invalidVarInt("ModifierKey"); + } + + int modifierKeyVarIntLen = VarInt.size(modifierKeyLen); + if (modifierKeyLen > 4096000) { + throw ProtocolException.stringTooLong("ModifierKey", modifierKeyLen, 4096000); + } + + if (varPos1 + modifierKeyVarIntLen + modifierKeyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ModifierKey", varPos1 + modifierKeyVarIntLen + modifierKeyLen, buf.readableBytes()); + } + + obj.modifierKey = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - if (modifierKeyLen > 4096000) { - throw ProtocolException.stringTooLong("ModifierKey", modifierKeyLen, 4096000); - } - - obj.modifierKey = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -126,13 +149,17 @@ public class EntityStatUpdate { int maxEnd = 21; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 13); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Modifiers", fieldOffset0, maxEnd); + } + int pos0 = offset + 21 + fieldOffset0; int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; pos0 += Modifier.computeBytesConsumed(buf, pos0); } @@ -143,9 +170,13 @@ public class EntityStatUpdate { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 17); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("ModifierKey", fieldOffset1, maxEnd); + } + int pos1 = offset + 21 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -232,76 +263,73 @@ public class EntityStatUpdate { return ValidationResult.error("Buffer too small: expected at least 21 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 2) != 0) { - int modifiersOffset = buffer.getIntLE(offset + 13); - if (modifiersOffset < 0) { - return ValidationResult.error("Invalid offset for Modifiers"); - } - - int pos = offset + 21 + modifiersOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Modifiers"); - } - - int modifiersCount = VarInt.peek(buffer, pos); - if (modifiersCount < 0) { - return ValidationResult.error("Invalid dictionary count for Modifiers"); - } - - if (modifiersCount > 4096000) { - return ValidationResult.error("Modifiers exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < modifiersCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 9) { + return ValidationResult.error("Invalid EntityStatOp value for Op"); + } else { + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 13); + if (v < 0 || v > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for Modifiers"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 21 + v; + int modifiersCount = VarInt.peek(buffer, pos); + if (modifiersCount < 0) { + return ValidationResult.error("Invalid dictionary count for Modifiers"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + if (modifiersCount > 4096000) { + return ValidationResult.error("Modifiers exceeds max length 4096000"); } - pos += 6; + pos += VarInt.size(modifiersCount); + + for (int i = 0; i < modifiersCount; i++) { + int keyLen = VarInt.peek(buffer, pos); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + pos += VarInt.size(keyLen); + pos += keyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + pos += 6; + } } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 17); + if (v < 0 || v > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for ModifierKey"); + } + + int posx = offset + 21 + v; + int modifierKeyLen = VarInt.peek(buffer, posx); + if (modifierKeyLen < 0) { + return ValidationResult.error("Invalid string length for ModifierKey"); + } + + if (modifierKeyLen > 4096000) { + return ValidationResult.error("ModifierKey exceeds max length 4096000"); + } + + posx += VarInt.size(modifierKeyLen); + posx += modifierKeyLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ModifierKey"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 4) != 0) { - int modifierKeyOffset = buffer.getIntLE(offset + 17); - if (modifierKeyOffset < 0) { - return ValidationResult.error("Invalid offset for ModifierKey"); - } - - int posx = offset + 21 + modifierKeyOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModifierKey"); - } - - int modifierKeyLen = VarInt.peek(buffer, posx); - if (modifierKeyLen < 0) { - return ValidationResult.error("Invalid string length for ModifierKey"); - } - - if (modifierKeyLen > 4096000) { - return ValidationResult.error("ModifierKey exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += modifierKeyLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ModifierKey"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/EntityStatsUpdate.java b/src/com/hypixel/hytale/protocol/EntityStatsUpdate.java index 1a762299..9e45554a 100644 --- a/src/com/hypixel/hytale/protocol/EntityStatsUpdate.java +++ b/src/com/hypixel/hytale/protocol/EntityStatsUpdate.java @@ -37,56 +37,59 @@ public class EntityStatsUpdate extends ComponentUpdate { int pos = offset + 0; int entityStatUpdatesCount = VarInt.peek(buf, pos); if (entityStatUpdatesCount < 0) { - throw ProtocolException.negativeLength("EntityStatUpdates", entityStatUpdatesCount); - } else if (entityStatUpdatesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("EntityStatUpdates", entityStatUpdatesCount, 4096000); + throw ProtocolException.invalidVarInt("EntityStatUpdates"); } else { - pos += VarInt.size(entityStatUpdatesCount); - obj.entityStatUpdates = new HashMap<>(entityStatUpdatesCount); + int entityStatUpdatesVarLen = VarInt.size(entityStatUpdatesCount); + if (entityStatUpdatesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("EntityStatUpdates", entityStatUpdatesCount, 4096000); + } else { + pos += entityStatUpdatesVarLen; + obj.entityStatUpdates = new HashMap<>(entityStatUpdatesCount); - for (int i = 0; i < entityStatUpdatesCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - int valLen = VarInt.peek(buf, pos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); + for (int i = 0; i < entityStatUpdatesCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + int valLen = VarInt.peek(buf, pos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 64) { + throw ProtocolException.arrayTooLong("val", valLen, 64); + } + + if (pos + valVarLen + valLen * 13L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", pos + valVarLen + valLen * 13, buf.readableBytes()); + } + + pos += valVarLen; + EntityStatUpdate[] val = new EntityStatUpdate[valLen]; + + for (int valIdx = 0; valIdx < valLen; valIdx++) { + val[valIdx] = EntityStatUpdate.deserialize(buf, pos); + pos += EntityStatUpdate.computeBytesConsumed(buf, pos); + } + + if (obj.entityStatUpdates.put(key, val) != null) { + throw ProtocolException.duplicateKey("entityStatUpdates", key); + } } - if (valLen > 64) { - throw ProtocolException.arrayTooLong("val", valLen, 64); - } - - int valVarLen = VarInt.length(buf, pos); - if (pos + valVarLen + valLen * 13L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", pos + valVarLen + valLen * 13, buf.readableBytes()); - } - - pos += valVarLen; - EntityStatUpdate[] val = new EntityStatUpdate[valLen]; - - for (int valIdx = 0; valIdx < valLen; valIdx++) { - val[valIdx] = EntityStatUpdate.deserialize(buf, pos); - pos += EntityStatUpdate.computeBytesConsumed(buf, pos); - } - - if (obj.entityStatUpdates.put(key, val) != null) { - throw ProtocolException.duplicateKey("entityStatUpdates", key); - } + return obj; } - - return obj; } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 0; int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; int al = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(al); for (int j = 0; j < al; j++) { pos += EntityStatUpdate.computeBytesConsumed(buf, pos); @@ -140,7 +143,7 @@ public class EntityStatsUpdate extends ComponentUpdate { } else if (entityStatUpdatesCount > 4096000) { return ValidationResult.error("EntityStatUpdates exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(entityStatUpdatesCount); for (int i = 0; i < entityStatUpdatesCount; i++) { pos += 4; @@ -153,7 +156,7 @@ public class EntityStatsUpdate extends ComponentUpdate { return ValidationResult.error("Invalid array count for value"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(valueArrCount); for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { pos += EntityStatUpdate.computeBytesConsumed(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/EntityUIComponent.java b/src/com/hypixel/hytale/protocol/EntityUIComponent.java index ce34f6d9..e3a3e067 100644 --- a/src/com/hypixel/hytale/protocol/EntityUIComponent.java +++ b/src/com/hypixel/hytale/protocol/EntityUIComponent.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.VarInt; @@ -8,17 +9,18 @@ import java.util.Arrays; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2fc; public class EntityUIComponent { public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 51; public static final int VARIABLE_FIELD_COUNT = 1; public static final int VARIABLE_BLOCK_START = 51; - public static final int MAX_SIZE = 139264056; + public static final int MAX_SIZE = 135168056; @Nonnull public EntityUIType type = EntityUIType.EntityStat; - @Nullable - public Vector2f hitboxOffset; + @Nonnull + public Vector2fc hitboxOffset = PacketIO.ZERO_VECTOR2; public boolean unknown; public int entityStatIndex; @Nullable @@ -37,7 +39,7 @@ public class EntityUIComponent { public EntityUIComponent( @Nonnull EntityUIType type, - @Nullable Vector2f hitboxOffset, + @Nonnull Vector2fc hitboxOffset, boolean unknown, int entityStatIndex, @Nullable RangeVector2f combatTextRandomPositionOffsetRange, @@ -77,63 +79,64 @@ public class EntityUIComponent { @Nonnull public static EntityUIComponent deserialize(@Nonnull ByteBuf buf, int offset) { - EntityUIComponent obj = new EntityUIComponent(); - byte nullBits = buf.getByte(offset); - obj.type = EntityUIType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - obj.hitboxOffset = Vector2f.deserialize(buf, offset + 2); - } - - obj.unknown = buf.getByte(offset + 10) != 0; - obj.entityStatIndex = buf.getIntLE(offset + 11); - if ((nullBits & 2) != 0) { - obj.combatTextRandomPositionOffsetRange = RangeVector2f.deserialize(buf, offset + 15); - } - - obj.combatTextViewportMargin = buf.getFloatLE(offset + 32); - obj.combatTextDuration = buf.getFloatLE(offset + 36); - obj.combatTextHitAngleModifierStrength = buf.getFloatLE(offset + 40); - obj.combatTextFontSize = buf.getFloatLE(offset + 44); - if ((nullBits & 4) != 0) { - obj.combatTextColor = Color.deserialize(buf, offset + 48); - } - - int pos = offset + 51; - if ((nullBits & 8) != 0) { - int combatTextAnimationEventsCount = VarInt.peek(buf, pos); - if (combatTextAnimationEventsCount < 0) { - throw ProtocolException.negativeLength("CombatTextAnimationEvents", combatTextAnimationEventsCount); + if (buf.readableBytes() - offset < 51) { + throw ProtocolException.bufferTooSmall("EntityUIComponent", 51, buf.readableBytes() - offset); + } else { + EntityUIComponent obj = new EntityUIComponent(); + byte nullBits = buf.getByte(offset); + obj.type = EntityUIType.fromValue(buf.getByte(offset + 1)); + obj.hitboxOffset = PacketIO.readVector2f(buf, offset + 2); + obj.unknown = buf.getByte(offset + 10) != 0; + obj.entityStatIndex = buf.getIntLE(offset + 11); + if ((nullBits & 1) != 0) { + obj.combatTextRandomPositionOffsetRange = RangeVector2f.deserialize(buf, offset + 15); } - if (combatTextAnimationEventsCount > 4096000) { - throw ProtocolException.arrayTooLong("CombatTextAnimationEvents", combatTextAnimationEventsCount, 4096000); + obj.combatTextViewportMargin = buf.getFloatLE(offset + 32); + obj.combatTextDuration = buf.getFloatLE(offset + 36); + obj.combatTextHitAngleModifierStrength = buf.getFloatLE(offset + 40); + obj.combatTextFontSize = buf.getFloatLE(offset + 44); + if ((nullBits & 2) != 0) { + obj.combatTextColor = Color.deserialize(buf, offset + 48); } - int combatTextAnimationEventsVarLen = VarInt.size(combatTextAnimationEventsCount); - if (pos + combatTextAnimationEventsVarLen + combatTextAnimationEventsCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall( - "CombatTextAnimationEvents", pos + combatTextAnimationEventsVarLen + combatTextAnimationEventsCount * 34, buf.readableBytes() - ); + int pos = offset + 51; + if ((nullBits & 4) != 0) { + int combatTextAnimationEventsCount = VarInt.peek(buf, pos); + if (combatTextAnimationEventsCount < 0) { + throw ProtocolException.invalidVarInt("CombatTextAnimationEvents"); + } + + int combatTextAnimationEventsVarLen = VarInt.size(combatTextAnimationEventsCount); + if (combatTextAnimationEventsCount > 4096000) { + throw ProtocolException.arrayTooLong("CombatTextAnimationEvents", combatTextAnimationEventsCount, 4096000); + } + + if (pos + combatTextAnimationEventsVarLen + combatTextAnimationEventsCount * 33L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "CombatTextAnimationEvents", pos + combatTextAnimationEventsVarLen + combatTextAnimationEventsCount * 33, buf.readableBytes() + ); + } + + pos += combatTextAnimationEventsVarLen; + obj.combatTextAnimationEvents = new CombatTextEntityUIComponentAnimationEvent[combatTextAnimationEventsCount]; + + for (int i = 0; i < combatTextAnimationEventsCount; i++) { + obj.combatTextAnimationEvents[i] = CombatTextEntityUIComponentAnimationEvent.deserialize(buf, pos); + pos += CombatTextEntityUIComponentAnimationEvent.computeBytesConsumed(buf, pos); + } } - pos += combatTextAnimationEventsVarLen; - obj.combatTextAnimationEvents = new CombatTextEntityUIComponentAnimationEvent[combatTextAnimationEventsCount]; - - for (int i = 0; i < combatTextAnimationEventsCount; i++) { - obj.combatTextAnimationEvents[i] = CombatTextEntityUIComponentAnimationEvent.deserialize(buf, pos); - pos += CombatTextEntityUIComponentAnimationEvent.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); int pos = offset + 51; - if ((nullBits & 8) != 0) { + if ((nullBits & 4) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += CombatTextEntityUIComponentAnimationEvent.computeBytesConsumed(buf, pos); @@ -145,30 +148,21 @@ public class EntityUIComponent { public void serialize(@Nonnull ByteBuf buf) { byte nullBits = 0; - if (this.hitboxOffset != null) { + if (this.combatTextRandomPositionOffsetRange != null) { nullBits = (byte)(nullBits | 1); } - if (this.combatTextRandomPositionOffsetRange != null) { + if (this.combatTextColor != null) { nullBits = (byte)(nullBits | 2); } - if (this.combatTextColor != null) { - nullBits = (byte)(nullBits | 4); - } - if (this.combatTextAnimationEvents != null) { - nullBits = (byte)(nullBits | 8); + nullBits = (byte)(nullBits | 4); } buf.writeByte(nullBits); buf.writeByte(this.type.getValue()); - if (this.hitboxOffset != null) { - this.hitboxOffset.serialize(buf); - } else { - buf.writeZero(8); - } - + PacketIO.writeVector2f(buf, this.hitboxOffset); buf.writeByte(this.unknown ? 1 : 0); buf.writeIntLE(this.entityStatIndex); if (this.combatTextRandomPositionOffsetRange != null) { @@ -203,7 +197,7 @@ public class EntityUIComponent { public int computeSize() { int size = 51; if (this.combatTextAnimationEvents != null) { - size += VarInt.size(this.combatTextAnimationEvents.length) + this.combatTextAnimationEvents.length * 34; + size += VarInt.size(this.combatTextAnimationEvents.length) + this.combatTextAnimationEvents.length * 33; } return size; @@ -214,32 +208,37 @@ public class EntityUIComponent { return ValidationResult.error("Buffer too small: expected at least 51 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 51; - if ((nullBits & 8) != 0) { - int combatTextAnimationEventsCount = VarInt.peek(buffer, pos); - if (combatTextAnimationEventsCount < 0) { - return ValidationResult.error("Invalid array count for CombatTextAnimationEvents"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid EntityUIType value for Type"); + } else { + v = offset + 51; + if ((nullBits & 4) != 0) { + int combatTextAnimationEventsCount = VarInt.peek(buffer, v); + if (combatTextAnimationEventsCount < 0) { + return ValidationResult.error("Invalid array count for CombatTextAnimationEvents"); + } + + if (combatTextAnimationEventsCount > 4096000) { + return ValidationResult.error("CombatTextAnimationEvents exceeds max length 4096000"); + } + + v += VarInt.size(combatTextAnimationEventsCount); + v += combatTextAnimationEventsCount * 33; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading CombatTextAnimationEvents"); + } } - if (combatTextAnimationEventsCount > 4096000) { - return ValidationResult.error("CombatTextAnimationEvents exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += combatTextAnimationEventsCount * 34; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading CombatTextAnimationEvents"); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } public EntityUIComponent clone() { EntityUIComponent copy = new EntityUIComponent(); copy.type = this.type; - copy.hitboxOffset = this.hitboxOffset != null ? this.hitboxOffset.clone() : null; + copy.hitboxOffset = this.hitboxOffset; copy.unknown = this.unknown; copy.entityStatIndex = this.entityStatIndex; copy.combatTextRandomPositionOffsetRange = this.combatTextRandomPositionOffsetRange != null ? this.combatTextRandomPositionOffsetRange.clone() : null; diff --git a/src/com/hypixel/hytale/protocol/EntityUpdate.java b/src/com/hypixel/hytale/protocol/EntityUpdate.java index a9fbd756..5ddf3b20 100644 --- a/src/com/hypixel/hytale/protocol/EntityUpdate.java +++ b/src/com/hypixel/hytale/protocol/EntityUpdate.java @@ -37,60 +37,74 @@ public class EntityUpdate { @Nonnull public static EntityUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - EntityUpdate obj = new EntityUpdate(); - byte nullBits = buf.getByte(offset); - obj.networkId = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 5); - int removedCount = VarInt.peek(buf, varPos0); - if (removedCount < 0) { - throw ProtocolException.negativeLength("Removed", removedCount); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("EntityUpdate", 13, buf.readableBytes() - offset); + } else { + EntityUpdate obj = new EntityUpdate(); + byte nullBits = buf.getByte(offset); + obj.networkId = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Removed", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int removedCount = VarInt.peek(buf, varPos0); + if (removedCount < 0) { + throw ProtocolException.invalidVarInt("Removed"); + } + + int varIntLen = VarInt.size(removedCount); + if (removedCount > 4096000) { + throw ProtocolException.arrayTooLong("Removed", removedCount, 4096000); + } + + if (varPos0 + varIntLen + removedCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Removed", varPos0 + varIntLen + removedCount * 1, buf.readableBytes()); + } + + obj.removed = new ComponentUpdateType[removedCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < removedCount; i++) { + obj.removed[i] = ComponentUpdateType.fromValue(buf.getByte(elemPos)); + elemPos++; + } } - if (removedCount > 4096000) { - throw ProtocolException.arrayTooLong("Removed", removedCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Updates", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int updatesCount = VarInt.peek(buf, varPos1); + if (updatesCount < 0) { + throw ProtocolException.invalidVarInt("Updates"); + } + + int varIntLenx = VarInt.size(updatesCount); + if (updatesCount > 4096000) { + throw ProtocolException.arrayTooLong("Updates", updatesCount, 4096000); + } + + if (varPos1 + varIntLenx + updatesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Updates", varPos1 + varIntLenx + updatesCount * 1, buf.readableBytes()); + } + + obj.updates = new ComponentUpdate[updatesCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < updatesCount; i++) { + obj.updates[i] = ComponentUpdate.deserialize(buf, elemPos); + elemPos += ComponentUpdate.computeBytesConsumed(buf, elemPos); + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + removedCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Removed", varPos0 + varIntLen + removedCount * 1, buf.readableBytes()); - } - - obj.removed = new ComponentUpdateType[removedCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < removedCount; i++) { - obj.removed[i] = ComponentUpdateType.fromValue(buf.getByte(elemPos)); - elemPos++; - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 9); - int updatesCount = VarInt.peek(buf, varPos1); - if (updatesCount < 0) { - throw ProtocolException.negativeLength("Updates", updatesCount); - } - - if (updatesCount > 4096000) { - throw ProtocolException.arrayTooLong("Updates", updatesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + updatesCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Updates", varPos1 + varIntLen + updatesCount * 1, buf.readableBytes()); - } - - obj.updates = new ComponentUpdate[updatesCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < updatesCount; i++) { - obj.updates[i] = ComponentUpdate.deserialize(buf, elemPos); - elemPos += ComponentUpdate.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -98,9 +112,13 @@ public class EntityUpdate { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Removed", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 1; + pos0 += VarInt.size(arrLen) + arrLen * 1; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -108,9 +126,13 @@ public class EntityUpdate { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Updates", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += ComponentUpdate.computeBytesConsumed(buf, pos1); @@ -199,15 +221,11 @@ public class EntityUpdate { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int removedOffset = buffer.getIntLE(offset + 5); - if (removedOffset < 0) { + if (removedOffset < 0 || removedOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Removed"); } int pos = offset + 13 + removedOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Removed"); - } - int removedCount = VarInt.peek(buffer, pos); if (removedCount < 0) { return ValidationResult.error("Invalid array count for Removed"); @@ -217,24 +235,28 @@ public class EntityUpdate { return ValidationResult.error("Removed exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += removedCount * 1; - if (pos > buffer.writerIndex()) { + pos += VarInt.size(removedCount); + if (pos + removedCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Removed"); } + + for (int i = 0; i < removedCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 26) { + return ValidationResult.error("Invalid ComponentUpdateType value for Removed[i]"); + } + + pos++; + } } if ((nullBits & 2) != 0) { int updatesOffset = buffer.getIntLE(offset + 9); - if (updatesOffset < 0) { + if (updatesOffset < 0 || updatesOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Updates"); } int posx = offset + 13 + updatesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Updates"); - } - int updatesCount = VarInt.peek(buffer, posx); if (updatesCount < 0) { return ValidationResult.error("Invalid array count for Updates"); @@ -244,7 +266,7 @@ public class EntityUpdate { return ValidationResult.error("Updates exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(updatesCount); for (int i = 0; i < updatesCount; i++) { ValidationResult structResult = ComponentUpdate.validateStructure(buffer, posx); diff --git a/src/com/hypixel/hytale/protocol/EqualizerEffect.java b/src/com/hypixel/hytale/protocol/EqualizerEffect.java index 5a6035f6..a3df6b55 100644 --- a/src/com/hypixel/hytale/protocol/EqualizerEffect.java +++ b/src/com/hypixel/hytale/protocol/EqualizerEffect.java @@ -73,35 +73,43 @@ public class EqualizerEffect { @Nonnull public static EqualizerEffect deserialize(@Nonnull ByteBuf buf, int offset) { - EqualizerEffect obj = new EqualizerEffect(); - byte nullBits = buf.getByte(offset); - obj.lowGain = buf.getFloatLE(offset + 1); - obj.lowCutOff = buf.getFloatLE(offset + 5); - obj.lowMidGain = buf.getFloatLE(offset + 9); - obj.lowMidCenter = buf.getFloatLE(offset + 13); - obj.lowMidWidth = buf.getFloatLE(offset + 17); - obj.highMidGain = buf.getFloatLE(offset + 21); - obj.highMidCenter = buf.getFloatLE(offset + 25); - obj.highMidWidth = buf.getFloatLE(offset + 29); - obj.highGain = buf.getFloatLE(offset + 33); - obj.highCutOff = buf.getFloatLE(offset + 37); - int pos = offset + 41; - if ((nullBits & 1) != 0) { - int idLen = VarInt.peek(buf, pos); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 41) { + throw ProtocolException.bufferTooSmall("EqualizerEffect", 41, buf.readableBytes() - offset); + } else { + EqualizerEffect obj = new EqualizerEffect(); + byte nullBits = buf.getByte(offset); + obj.lowGain = buf.getFloatLE(offset + 1); + obj.lowCutOff = buf.getFloatLE(offset + 5); + obj.lowMidGain = buf.getFloatLE(offset + 9); + obj.lowMidCenter = buf.getFloatLE(offset + 13); + obj.lowMidWidth = buf.getFloatLE(offset + 17); + obj.highMidGain = buf.getFloatLE(offset + 21); + obj.highMidCenter = buf.getFloatLE(offset + 25); + obj.highMidWidth = buf.getFloatLE(offset + 29); + obj.highGain = buf.getFloatLE(offset + 33); + obj.highCutOff = buf.getFloatLE(offset + 37); + int pos = offset + 41; + if ((nullBits & 1) != 0) { + int idLen = VarInt.peek(buf, pos); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (pos + idVarLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", pos + idVarLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += idVarLen + idLen; } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - int idVarLen = VarInt.length(buf, pos); - obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += idVarLen + idLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -109,7 +117,7 @@ public class EqualizerEffect { int pos = offset + 41; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -162,7 +170,7 @@ public class EqualizerEffect { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); diff --git a/src/com/hypixel/hytale/protocol/EquipmentUpdate.java b/src/com/hypixel/hytale/protocol/EquipmentUpdate.java index 6b2763aa..9b4178fe 100644 --- a/src/com/hypixel/hytale/protocol/EquipmentUpdate.java +++ b/src/com/hypixel/hytale/protocol/EquipmentUpdate.java @@ -40,72 +40,105 @@ public class EquipmentUpdate extends ComponentUpdate { @Nonnull public static EquipmentUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - EquipmentUpdate obj = new EquipmentUpdate(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int armorIdsCount = VarInt.peek(buf, varPos0); - if (armorIdsCount < 0) { - throw ProtocolException.negativeLength("ArmorIds", armorIdsCount); - } - - if (armorIdsCount > 4096000) { - throw ProtocolException.arrayTooLong("ArmorIds", armorIdsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + armorIdsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ArmorIds", varPos0 + varIntLen + armorIdsCount * 1, buf.readableBytes()); - } - - obj.armorIds = new String[armorIdsCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < armorIdsCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("armorIds[" + i + "]", strLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("EquipmentUpdate", 13, buf.readableBytes() - offset); + } else { + EquipmentUpdate obj = new EquipmentUpdate(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ArmorIds", varPosBase0, buf.readableBytes()); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("armorIds[" + i + "]", strLen, 4096000); + int varPos0 = offset + 13 + varPosBase0; + int armorIdsCount = VarInt.peek(buf, varPos0); + if (armorIdsCount < 0) { + throw ProtocolException.invalidVarInt("ArmorIds"); } - int strVarLen = VarInt.length(buf, elemPos); - obj.armorIds[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; + int varIntLen = VarInt.size(armorIdsCount); + if (armorIdsCount > 4096000) { + throw ProtocolException.arrayTooLong("ArmorIds", armorIdsCount, 4096000); + } + + if (varPos0 + varIntLen + armorIdsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ArmorIds", varPos0 + varIntLen + armorIdsCount * 1, buf.readableBytes()); + } + + obj.armorIds = new String[armorIdsCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < armorIdsCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("armorIds[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("armorIds[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("armorIds[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.armorIds[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("RightHandItemId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int rightHandItemIdLen = VarInt.peek(buf, varPos1); + if (rightHandItemIdLen < 0) { + throw ProtocolException.invalidVarInt("RightHandItemId"); + } + + int rightHandItemIdVarIntLen = VarInt.size(rightHandItemIdLen); + if (rightHandItemIdLen > 4096000) { + throw ProtocolException.stringTooLong("RightHandItemId", rightHandItemIdLen, 4096000); + } + + if (varPos1 + rightHandItemIdVarIntLen + rightHandItemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RightHandItemId", varPos1 + rightHandItemIdVarIntLen + rightHandItemIdLen, buf.readableBytes()); + } + + obj.rightHandItemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("LeftHandItemId", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int leftHandItemIdLen = VarInt.peek(buf, varPos2); + if (leftHandItemIdLen < 0) { + throw ProtocolException.invalidVarInt("LeftHandItemId"); + } + + int leftHandItemIdVarIntLen = VarInt.size(leftHandItemIdLen); + if (leftHandItemIdLen > 4096000) { + throw ProtocolException.stringTooLong("LeftHandItemId", leftHandItemIdLen, 4096000); + } + + if (varPos2 + leftHandItemIdVarIntLen + leftHandItemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("LeftHandItemId", varPos2 + leftHandItemIdVarIntLen + leftHandItemIdLen, buf.readableBytes()); + } + + obj.leftHandItemId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int rightHandItemIdLen = VarInt.peek(buf, varPos1); - if (rightHandItemIdLen < 0) { - throw ProtocolException.negativeLength("RightHandItemId", rightHandItemIdLen); - } - - if (rightHandItemIdLen > 4096000) { - throw ProtocolException.stringTooLong("RightHandItemId", rightHandItemIdLen, 4096000); - } - - obj.rightHandItemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int leftHandItemIdLen = VarInt.peek(buf, varPos2); - if (leftHandItemIdLen < 0) { - throw ProtocolException.negativeLength("LeftHandItemId", leftHandItemIdLen); - } - - if (leftHandItemIdLen > 4096000) { - throw ProtocolException.stringTooLong("LeftHandItemId", leftHandItemIdLen, 4096000); - } - - obj.leftHandItemId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -113,13 +146,17 @@ public class EquipmentUpdate extends ComponentUpdate { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ArmorIds", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; } if (pos0 - offset > maxEnd) { @@ -129,9 +166,13 @@ public class EquipmentUpdate extends ComponentUpdate { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("RightHandItemId", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -139,9 +180,13 @@ public class EquipmentUpdate extends ComponentUpdate { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("LeftHandItemId", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -237,15 +282,11 @@ public class EquipmentUpdate extends ComponentUpdate { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int armorIdsOffset = buffer.getIntLE(offset + 1); - if (armorIdsOffset < 0) { + if (armorIdsOffset < 0 || armorIdsOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for ArmorIds"); } int pos = offset + 13 + armorIdsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ArmorIds"); - } - int armorIdsCount = VarInt.peek(buffer, pos); if (armorIdsCount < 0) { return ValidationResult.error("Invalid array count for ArmorIds"); @@ -255,7 +296,7 @@ public class EquipmentUpdate extends ComponentUpdate { return ValidationResult.error("ArmorIds exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(armorIdsCount); for (int i = 0; i < armorIdsCount; i++) { int strLen = VarInt.peek(buffer, pos); @@ -263,7 +304,7 @@ public class EquipmentUpdate extends ComponentUpdate { return ValidationResult.error("Invalid string length in ArmorIds"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(strLen); pos += strLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in ArmorIds"); @@ -273,15 +314,11 @@ public class EquipmentUpdate extends ComponentUpdate { if ((nullBits & 2) != 0) { int rightHandItemIdOffset = buffer.getIntLE(offset + 5); - if (rightHandItemIdOffset < 0) { + if (rightHandItemIdOffset < 0 || rightHandItemIdOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for RightHandItemId"); } int posx = offset + 13 + rightHandItemIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for RightHandItemId"); - } - int rightHandItemIdLen = VarInt.peek(buffer, posx); if (rightHandItemIdLen < 0) { return ValidationResult.error("Invalid string length for RightHandItemId"); @@ -291,7 +328,7 @@ public class EquipmentUpdate extends ComponentUpdate { return ValidationResult.error("RightHandItemId exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(rightHandItemIdLen); posx += rightHandItemIdLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading RightHandItemId"); @@ -300,15 +337,11 @@ public class EquipmentUpdate extends ComponentUpdate { if ((nullBits & 4) != 0) { int leftHandItemIdOffset = buffer.getIntLE(offset + 9); - if (leftHandItemIdOffset < 0) { + if (leftHandItemIdOffset < 0 || leftHandItemIdOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for LeftHandItemId"); } int posxx = offset + 13 + leftHandItemIdOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for LeftHandItemId"); - } - int leftHandItemIdLen = VarInt.peek(buffer, posxx); if (leftHandItemIdLen < 0) { return ValidationResult.error("Invalid string length for LeftHandItemId"); @@ -318,7 +351,7 @@ public class EquipmentUpdate extends ComponentUpdate { return ValidationResult.error("LeftHandItemId exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(leftHandItemIdLen); posxx += leftHandItemIdLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading LeftHandItemId"); diff --git a/src/com/hypixel/hytale/protocol/ExtraResources.java b/src/com/hypixel/hytale/protocol/ExtraResources.java index f3cdb4b9..6e5505a2 100644 --- a/src/com/hypixel/hytale/protocol/ExtraResources.java +++ b/src/com/hypixel/hytale/protocol/ExtraResources.java @@ -30,34 +30,38 @@ public class ExtraResources { @Nonnull public static ExtraResources deserialize(@Nonnull ByteBuf buf, int offset) { - ExtraResources obj = new ExtraResources(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int resourcesCount = VarInt.peek(buf, pos); - if (resourcesCount < 0) { - throw ProtocolException.negativeLength("Resources", resourcesCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ExtraResources", 1, buf.readableBytes() - offset); + } else { + ExtraResources obj = new ExtraResources(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int resourcesCount = VarInt.peek(buf, pos); + if (resourcesCount < 0) { + throw ProtocolException.invalidVarInt("Resources"); + } + + int resourcesVarLen = VarInt.size(resourcesCount); + if (resourcesCount > 4096000) { + throw ProtocolException.arrayTooLong("Resources", resourcesCount, 4096000); + } + + if (pos + resourcesVarLen + resourcesCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Resources", pos + resourcesVarLen + resourcesCount * 5, buf.readableBytes()); + } + + pos += resourcesVarLen; + obj.resources = new ItemQuantity[resourcesCount]; + + for (int i = 0; i < resourcesCount; i++) { + obj.resources[i] = ItemQuantity.deserialize(buf, pos); + pos += ItemQuantity.computeBytesConsumed(buf, pos); + } } - if (resourcesCount > 4096000) { - throw ProtocolException.arrayTooLong("Resources", resourcesCount, 4096000); - } - - int resourcesVarLen = VarInt.size(resourcesCount); - if (pos + resourcesVarLen + resourcesCount * 5L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Resources", pos + resourcesVarLen + resourcesCount * 5, buf.readableBytes()); - } - - pos += resourcesVarLen; - obj.resources = new ItemQuantity[resourcesCount]; - - for (int i = 0; i < resourcesCount; i++) { - obj.resources[i] = ItemQuantity.deserialize(buf, pos); - pos += ItemQuantity.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -65,7 +69,7 @@ public class ExtraResources { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += ItemQuantity.computeBytesConsumed(buf, pos); @@ -126,7 +130,7 @@ public class ExtraResources { return ValidationResult.error("Resources exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(resourcesCount); for (int i = 0; i < resourcesCount; i++) { ValidationResult structResult = ItemQuantity.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/FirstClickInteraction.java b/src/com/hypixel/hytale/protocol/FirstClickInteraction.java index a1623abf..7d6e11a7 100644 --- a/src/com/hypixel/hytale/protocol/FirstClickInteraction.java +++ b/src/com/hypixel/hytale/protocol/FirstClickInteraction.java @@ -66,78 +66,107 @@ public class FirstClickInteraction extends Interaction { @Nonnull public static FirstClickInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - FirstClickInteraction obj = new FirstClickInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.click = buf.getIntLE(offset + 11); - obj.held = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 39 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 39) { + throw ProtocolException.bufferTooSmall("FirstClickInteraction", 39, buf.readableBytes() - offset); + } else { + FirstClickInteraction obj = new FirstClickInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.click = buf.getIntLE(offset + 11); + obj.held = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 39 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 39 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 39 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 39 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 39 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 39 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 39 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 39 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 39 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -145,6 +174,10 @@ public class FirstClickInteraction extends Interaction { int maxEnd = 39; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 39 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -154,9 +187,13 @@ public class FirstClickInteraction extends Interaction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 39 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -169,6 +206,10 @@ public class FirstClickInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 39 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -178,9 +219,13 @@ public class FirstClickInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 39 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -188,6 +233,10 @@ public class FirstClickInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 39 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -326,119 +375,109 @@ public class FirstClickInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 39 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 39 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 39 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 39 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 39 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 39 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 39 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 39 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 39 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 39 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 39 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/FloatRange.java b/src/com/hypixel/hytale/protocol/FloatRange.java index d95d1910..dcc5ab64 100644 --- a/src/com/hypixel/hytale/protocol/FloatRange.java +++ b/src/com/hypixel/hytale/protocol/FloatRange.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class FloatRange { @Nonnull public static FloatRange deserialize(@Nonnull ByteBuf buf, int offset) { - FloatRange obj = new FloatRange(); - obj.inclusiveMin = buf.getFloatLE(offset + 0); - obj.inclusiveMax = buf.getFloatLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("FloatRange", 8, buf.readableBytes() - offset); + } else { + FloatRange obj = new FloatRange(); + obj.inclusiveMin = buf.getFloatLE(offset + 0); + obj.inclusiveMax = buf.getFloatLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Fluid.java b/src/com/hypixel/hytale/protocol/Fluid.java index 881fe2d0..3dc8c6fd 100644 --- a/src/com/hypixel/hytale/protocol/Fluid.java +++ b/src/com/hypixel/hytale/protocol/Fluid.java @@ -95,149 +95,193 @@ public class Fluid { @Nonnull public static Fluid deserialize(@Nonnull ByteBuf buf, int offset) { - Fluid obj = new Fluid(); - byte nullBits = buf.getByte(offset); - obj.maxFluidLevel = buf.getIntLE(offset + 1); - obj.requiresAlphaBlending = buf.getByte(offset + 5) != 0; - obj.opacity = Opacity.fromValue(buf.getByte(offset + 6)); - if ((nullBits & 1) != 0) { - obj.light = ColorLight.deserialize(buf, offset + 7); + if (buf.readableBytes() - offset < 47) { + throw ProtocolException.bufferTooSmall("Fluid", 47, buf.readableBytes() - offset); + } else { + Fluid obj = new Fluid(); + byte nullBits = buf.getByte(offset); + obj.maxFluidLevel = buf.getIntLE(offset + 1); + obj.requiresAlphaBlending = buf.getByte(offset + 5) != 0; + obj.opacity = Opacity.fromValue(buf.getByte(offset + 6)); + if ((nullBits & 1) != 0) { + obj.light = ColorLight.deserialize(buf, offset + 7); + } + + obj.drawType = FluidDrawType.fromValue(buf.getByte(offset + 11)); + obj.fluidFXIndex = buf.getIntLE(offset + 12); + obj.blockSoundSetIndex = buf.getIntLE(offset + 16); + if ((nullBits & 2) != 0) { + obj.particleColor = Color.deserialize(buf, offset + 20); + } + + if ((nullBits & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 23); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 47 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 27); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("CubeTextures", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 47 + varPosBase1; + int cubeTexturesCount = VarInt.peek(buf, varPos1); + if (cubeTexturesCount < 0) { + throw ProtocolException.invalidVarInt("CubeTextures"); + } + + int varIntLen = VarInt.size(cubeTexturesCount); + if (cubeTexturesCount > 4096000) { + throw ProtocolException.arrayTooLong("CubeTextures", cubeTexturesCount, 4096000); + } + + if (varPos1 + varIntLen + cubeTexturesCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CubeTextures", varPos1 + varIntLen + cubeTexturesCount * 5, buf.readableBytes()); + } + + obj.cubeTextures = new BlockTextures[cubeTexturesCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < cubeTexturesCount; i++) { + obj.cubeTextures[i] = BlockTextures.deserialize(buf, elemPos); + elemPos += BlockTextures.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase2 = buf.getIntLE(offset + 31); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("ShaderEffect", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 47 + varPosBase2; + int shaderEffectCount = VarInt.peek(buf, varPos2); + if (shaderEffectCount < 0) { + throw ProtocolException.invalidVarInt("ShaderEffect"); + } + + int varIntLenx = VarInt.size(shaderEffectCount); + if (shaderEffectCount > 4096000) { + throw ProtocolException.arrayTooLong("ShaderEffect", shaderEffectCount, 4096000); + } + + if (varPos2 + varIntLenx + shaderEffectCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ShaderEffect", varPos2 + varIntLenx + shaderEffectCount * 1, buf.readableBytes()); + } + + obj.shaderEffect = new ShaderType[shaderEffectCount]; + int elemPos = varPos2 + varIntLenx; + + for (int i = 0; i < shaderEffectCount; i++) { + obj.shaderEffect[i] = ShaderType.fromValue(buf.getByte(elemPos)); + elemPos++; + } + } + + if ((nullBits & 32) != 0) { + int varPosBase3 = buf.getIntLE(offset + 35); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Particles", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 47 + varPosBase3; + int particlesCount = VarInt.peek(buf, varPos3); + if (particlesCount < 0) { + throw ProtocolException.invalidVarInt("Particles"); + } + + int varIntLenxx = VarInt.size(particlesCount); + if (particlesCount > 4096000) { + throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); + } + + if (varPos3 + varIntLenxx + particlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Particles", varPos3 + varIntLenxx + particlesCount * 34, buf.readableBytes()); + } + + obj.particles = new ModelParticle[particlesCount]; + int elemPos = varPos3 + varIntLenxx; + + for (int i = 0; i < particlesCount; i++) { + obj.particles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 64) != 0) { + int varPosBase4 = buf.getIntLE(offset + 39); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("BlockParticleSetId", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 47 + varPosBase4; + int blockParticleSetIdLen = VarInt.peek(buf, varPos4); + if (blockParticleSetIdLen < 0) { + throw ProtocolException.invalidVarInt("BlockParticleSetId"); + } + + int blockParticleSetIdVarIntLen = VarInt.size(blockParticleSetIdLen); + if (blockParticleSetIdLen > 4096000) { + throw ProtocolException.stringTooLong("BlockParticleSetId", blockParticleSetIdLen, 4096000); + } + + if (varPos4 + blockParticleSetIdVarIntLen + blockParticleSetIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BlockParticleSetId", varPos4 + blockParticleSetIdVarIntLen + blockParticleSetIdLen, buf.readableBytes()); + } + + obj.blockParticleSetId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + if ((nullBits & 128) != 0) { + int varPosBase5 = buf.getIntLE(offset + 43); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("TagIndexes", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 47 + varPosBase5; + int tagIndexesCount = VarInt.peek(buf, varPos5); + if (tagIndexesCount < 0) { + throw ProtocolException.invalidVarInt("TagIndexes"); + } + + int varIntLenxxx = VarInt.size(tagIndexesCount); + if (tagIndexesCount > 4096000) { + throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); + } + + if (varPos5 + varIntLenxxx + tagIndexesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TagIndexes", varPos5 + varIntLenxxx + tagIndexesCount * 4, buf.readableBytes()); + } + + obj.tagIndexes = new int[tagIndexesCount]; + + for (int i = 0; i < tagIndexesCount; i++) { + obj.tagIndexes[i] = buf.getIntLE(varPos5 + varIntLenxxx + i * 4); + } + } + + return obj; } - - obj.drawType = FluidDrawType.fromValue(buf.getByte(offset + 11)); - obj.fluidFXIndex = buf.getIntLE(offset + 12); - obj.blockSoundSetIndex = buf.getIntLE(offset + 16); - if ((nullBits & 2) != 0) { - obj.particleColor = Color.deserialize(buf, offset + 20); - } - - if ((nullBits & 4) != 0) { - int varPos0 = offset + 47 + buf.getIntLE(offset + 23); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); - } - - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos1 = offset + 47 + buf.getIntLE(offset + 27); - int cubeTexturesCount = VarInt.peek(buf, varPos1); - if (cubeTexturesCount < 0) { - throw ProtocolException.negativeLength("CubeTextures", cubeTexturesCount); - } - - if (cubeTexturesCount > 4096000) { - throw ProtocolException.arrayTooLong("CubeTextures", cubeTexturesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + cubeTexturesCount * 5L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("CubeTextures", varPos1 + varIntLen + cubeTexturesCount * 5, buf.readableBytes()); - } - - obj.cubeTextures = new BlockTextures[cubeTexturesCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < cubeTexturesCount; i++) { - obj.cubeTextures[i] = BlockTextures.deserialize(buf, elemPos); - elemPos += BlockTextures.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 16) != 0) { - int varPos2 = offset + 47 + buf.getIntLE(offset + 31); - int shaderEffectCount = VarInt.peek(buf, varPos2); - if (shaderEffectCount < 0) { - throw ProtocolException.negativeLength("ShaderEffect", shaderEffectCount); - } - - if (shaderEffectCount > 4096000) { - throw ProtocolException.arrayTooLong("ShaderEffect", shaderEffectCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + shaderEffectCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ShaderEffect", varPos2 + varIntLen + shaderEffectCount * 1, buf.readableBytes()); - } - - obj.shaderEffect = new ShaderType[shaderEffectCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < shaderEffectCount; i++) { - obj.shaderEffect[i] = ShaderType.fromValue(buf.getByte(elemPos)); - elemPos++; - } - } - - if ((nullBits & 32) != 0) { - int varPos3 = offset + 47 + buf.getIntLE(offset + 35); - int particlesCount = VarInt.peek(buf, varPos3); - if (particlesCount < 0) { - throw ProtocolException.negativeLength("Particles", particlesCount); - } - - if (particlesCount > 4096000) { - throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + particlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Particles", varPos3 + varIntLen + particlesCount * 34, buf.readableBytes()); - } - - obj.particles = new ModelParticle[particlesCount]; - int elemPos = varPos3 + varIntLen; - - for (int i = 0; i < particlesCount; i++) { - obj.particles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 64) != 0) { - int varPos4 = offset + 47 + buf.getIntLE(offset + 39); - int blockParticleSetIdLen = VarInt.peek(buf, varPos4); - if (blockParticleSetIdLen < 0) { - throw ProtocolException.negativeLength("BlockParticleSetId", blockParticleSetIdLen); - } - - if (blockParticleSetIdLen > 4096000) { - throw ProtocolException.stringTooLong("BlockParticleSetId", blockParticleSetIdLen, 4096000); - } - - obj.blockParticleSetId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - if ((nullBits & 128) != 0) { - int varPos5 = offset + 47 + buf.getIntLE(offset + 43); - int tagIndexesCount = VarInt.peek(buf, varPos5); - if (tagIndexesCount < 0) { - throw ProtocolException.negativeLength("TagIndexes", tagIndexesCount); - } - - if (tagIndexesCount > 4096000) { - throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + tagIndexesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("TagIndexes", varPos5 + varIntLen + tagIndexesCount * 4, buf.readableBytes()); - } - - obj.tagIndexes = new int[tagIndexesCount]; - - for (int i = 0; i < tagIndexesCount; i++) { - obj.tagIndexes[i] = buf.getIntLE(varPos5 + varIntLen + i * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -245,9 +289,13 @@ public class Fluid { int maxEnd = 47; if ((nullBits & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 23); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 47 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -255,9 +303,13 @@ public class Fluid { if ((nullBits & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 27); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("CubeTextures", fieldOffset1, maxEnd); + } + int pos1 = offset + 47 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += BlockTextures.computeBytesConsumed(buf, pos1); @@ -270,9 +322,13 @@ public class Fluid { if ((nullBits & 16) != 0) { int fieldOffset2 = buf.getIntLE(offset + 31); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("ShaderEffect", fieldOffset2, maxEnd); + } + int pos2 = offset + 47 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + arrLen * 1; + pos2 += VarInt.size(arrLen) + arrLen * 1; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -280,9 +336,13 @@ public class Fluid { if ((nullBits & 32) != 0) { int fieldOffset3 = buf.getIntLE(offset + 35); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Particles", fieldOffset3, maxEnd); + } + int pos3 = offset + 47 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos3 += ModelParticle.computeBytesConsumed(buf, pos3); @@ -295,9 +355,13 @@ public class Fluid { if ((nullBits & 64) != 0) { int fieldOffset4 = buf.getIntLE(offset + 39); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("BlockParticleSetId", fieldOffset4, maxEnd); + } + int pos4 = offset + 47 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -305,9 +369,13 @@ public class Fluid { if ((nullBits & 128) != 0) { int fieldOffset5 = buf.getIntLE(offset + 43); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("TagIndexes", fieldOffset5, maxEnd); + } + int pos5 = offset + 47 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + arrLen * 4; + pos5 += VarInt.size(arrLen) + arrLen * 4; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -504,179 +572,173 @@ public class Fluid { return ValidationResult.error("Buffer too small: expected at least 47 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 4) != 0) { - int idOffset = buffer.getIntLE(offset + 23); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); - } + int v = buffer.getByte(offset + 6) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid Opacity value for Opacity"); + } else { + v = buffer.getByte(offset + 11) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid FluidDrawType value for DrawType"); + } else { + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Id"); + } - int pos = offset + 47 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } + int pos = offset + 47 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); - } - } - - if ((nullBits & 8) != 0) { - int cubeTexturesOffset = buffer.getIntLE(offset + 27); - if (cubeTexturesOffset < 0) { - return ValidationResult.error("Invalid offset for CubeTextures"); - } - - int posx = offset + 47 + cubeTexturesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for CubeTextures"); - } - - int cubeTexturesCount = VarInt.peek(buffer, posx); - if (cubeTexturesCount < 0) { - return ValidationResult.error("Invalid array count for CubeTextures"); - } - - if (cubeTexturesCount > 4096000) { - return ValidationResult.error("CubeTextures exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < cubeTexturesCount; i++) { - ValidationResult structResult = BlockTextures.validateStructure(buffer, posx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid BlockTextures in CubeTextures[" + i + "]: " + structResult.error()); + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } } - posx += BlockTextures.computeBytesConsumed(buffer, posx); - } - } + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for CubeTextures"); + } - if ((nullBits & 16) != 0) { - int shaderEffectOffset = buffer.getIntLE(offset + 31); - if (shaderEffectOffset < 0) { - return ValidationResult.error("Invalid offset for ShaderEffect"); - } + int posx = offset + 47 + v; + int cubeTexturesCount = VarInt.peek(buffer, posx); + if (cubeTexturesCount < 0) { + return ValidationResult.error("Invalid array count for CubeTextures"); + } - int posxx = offset + 47 + shaderEffectOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ShaderEffect"); - } + if (cubeTexturesCount > 4096000) { + return ValidationResult.error("CubeTextures exceeds max length 4096000"); + } - int shaderEffectCount = VarInt.peek(buffer, posxx); - if (shaderEffectCount < 0) { - return ValidationResult.error("Invalid array count for ShaderEffect"); - } + posx += VarInt.size(cubeTexturesCount); - if (shaderEffectCount > 4096000) { - return ValidationResult.error("ShaderEffect exceeds max length 4096000"); - } + for (int i = 0; i < cubeTexturesCount; i++) { + ValidationResult structResult = BlockTextures.validateStructure(buffer, posx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid BlockTextures in CubeTextures[" + i + "]: " + structResult.error()); + } - posxx += VarInt.length(buffer, posxx); - posxx += shaderEffectCount * 1; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ShaderEffect"); - } - } - - if ((nullBits & 32) != 0) { - int particlesOffset = buffer.getIntLE(offset + 35); - if (particlesOffset < 0) { - return ValidationResult.error("Invalid offset for Particles"); - } - - int posxxx = offset + 47 + particlesOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particles"); - } - - int particlesCount = VarInt.peek(buffer, posxxx); - if (particlesCount < 0) { - return ValidationResult.error("Invalid array count for Particles"); - } - - if (particlesCount > 4096000) { - return ValidationResult.error("Particles exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - - for (int i = 0; i < particlesCount; i++) { - ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); + posx += BlockTextures.computeBytesConsumed(buffer, posx); + } } - posxxx += ModelParticle.computeBytesConsumed(buffer, posxxx); + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for ShaderEffect"); + } + + int posxx = offset + 47 + v; + int shaderEffectCount = VarInt.peek(buffer, posxx); + if (shaderEffectCount < 0) { + return ValidationResult.error("Invalid array count for ShaderEffect"); + } + + if (shaderEffectCount > 4096000) { + return ValidationResult.error("ShaderEffect exceeds max length 4096000"); + } + + posxx += VarInt.size(shaderEffectCount); + if (posxx + shaderEffectCount * 1L > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ShaderEffect"); + } + + for (int i = 0; i < shaderEffectCount; i++) { + int vx = buffer.getByte(posxx) & 255; + if (vx >= 10) { + return ValidationResult.error("Invalid ShaderType value for ShaderEffect[i]"); + } + + posxx++; + } + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Particles"); + } + + int posxxx = offset + 47 + v; + int particlesCount = VarInt.peek(buffer, posxxx); + if (particlesCount < 0) { + return ValidationResult.error("Invalid array count for Particles"); + } + + if (particlesCount > 4096000) { + return ValidationResult.error("Particles exceeds max length 4096000"); + } + + posxxx += VarInt.size(particlesCount); + + for (int i = 0; i < particlesCount; i++) { + ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); + } + + posxxx += ModelParticle.computeBytesConsumed(buffer, posxxx); + } + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for BlockParticleSetId"); + } + + int posxxxx = offset + 47 + v; + int blockParticleSetIdLen = VarInt.peek(buffer, posxxxx); + if (blockParticleSetIdLen < 0) { + return ValidationResult.error("Invalid string length for BlockParticleSetId"); + } + + if (blockParticleSetIdLen > 4096000) { + return ValidationResult.error("BlockParticleSetId exceeds max length 4096000"); + } + + posxxxx += VarInt.size(blockParticleSetIdLen); + posxxxx += blockParticleSetIdLen; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading BlockParticleSetId"); + } + } + + if ((nullBits & 128) != 0) { + v = buffer.getIntLE(offset + 43); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for TagIndexes"); + } + + int posxxxxx = offset + 47 + v; + int tagIndexesCount = VarInt.peek(buffer, posxxxxx); + if (tagIndexesCount < 0) { + return ValidationResult.error("Invalid array count for TagIndexes"); + } + + if (tagIndexesCount > 4096000) { + return ValidationResult.error("TagIndexes exceeds max length 4096000"); + } + + posxxxxx += VarInt.size(tagIndexesCount); + posxxxxx += tagIndexesCount * 4; + if (posxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TagIndexes"); + } + } + + return ValidationResult.OK; } } - - if ((nullBits & 64) != 0) { - int blockParticleSetIdOffset = buffer.getIntLE(offset + 39); - if (blockParticleSetIdOffset < 0) { - return ValidationResult.error("Invalid offset for BlockParticleSetId"); - } - - int posxxxx = offset + 47 + blockParticleSetIdOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockParticleSetId"); - } - - int blockParticleSetIdLen = VarInt.peek(buffer, posxxxx); - if (blockParticleSetIdLen < 0) { - return ValidationResult.error("Invalid string length for BlockParticleSetId"); - } - - if (blockParticleSetIdLen > 4096000) { - return ValidationResult.error("BlockParticleSetId exceeds max length 4096000"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - posxxxx += blockParticleSetIdLen; - if (posxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading BlockParticleSetId"); - } - } - - if ((nullBits & 128) != 0) { - int tagIndexesOffset = buffer.getIntLE(offset + 43); - if (tagIndexesOffset < 0) { - return ValidationResult.error("Invalid offset for TagIndexes"); - } - - int posxxxxx = offset + 47 + tagIndexesOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TagIndexes"); - } - - int tagIndexesCount = VarInt.peek(buffer, posxxxxx); - if (tagIndexesCount < 0) { - return ValidationResult.error("Invalid array count for TagIndexes"); - } - - if (tagIndexesCount > 4096000) { - return ValidationResult.error("TagIndexes exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += tagIndexesCount * 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading TagIndexes"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/FluidFX.java b/src/com/hypixel/hytale/protocol/FluidFX.java index dea3e88d..b23f8f8b 100644 --- a/src/com/hypixel/hytale/protocol/FluidFX.java +++ b/src/com/hypixel/hytale/protocol/FluidFX.java @@ -88,51 +88,70 @@ public class FluidFX { @Nonnull public static FluidFX deserialize(@Nonnull ByteBuf buf, int offset) { - FluidFX obj = new FluidFX(); - byte nullBits = buf.getByte(offset); - obj.shader = ShaderType.fromValue(buf.getByte(offset + 1)); - obj.fogMode = FluidFog.fromValue(buf.getByte(offset + 2)); - if ((nullBits & 1) != 0) { - obj.fogColor = Color.deserialize(buf, offset + 3); - } - - if ((nullBits & 2) != 0) { - obj.fogDistance = NearFar.deserialize(buf, offset + 6); - } - - obj.fogDepthStart = buf.getFloatLE(offset + 14); - obj.fogDepthFalloff = buf.getFloatLE(offset + 18); - if ((nullBits & 4) != 0) { - obj.colorFilter = Color.deserialize(buf, offset + 22); - } - - obj.colorSaturation = buf.getFloatLE(offset + 25); - obj.distortionAmplitude = buf.getFloatLE(offset + 29); - obj.distortionFrequency = buf.getFloatLE(offset + 33); - if ((nullBits & 8) != 0) { - obj.movementSettings = FluidFXMovementSettings.deserialize(buf, offset + 37); - } - - if ((nullBits & 16) != 0) { - int varPos0 = offset + 69 + buf.getIntLE(offset + 61); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 69) { + throw ProtocolException.bufferTooSmall("FluidFX", 69, buf.readableBytes() - offset); + } else { + FluidFX obj = new FluidFX(); + byte nullBits = buf.getByte(offset); + obj.shader = ShaderType.fromValue(buf.getByte(offset + 1)); + obj.fogMode = FluidFog.fromValue(buf.getByte(offset + 2)); + if ((nullBits & 1) != 0) { + obj.fogColor = Color.deserialize(buf, offset + 3); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + obj.fogDistance = NearFar.deserialize(buf, offset + 6); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + obj.fogDepthStart = buf.getFloatLE(offset + 14); + obj.fogDepthFalloff = buf.getFloatLE(offset + 18); + if ((nullBits & 4) != 0) { + obj.colorFilter = Color.deserialize(buf, offset + 22); + } - if ((nullBits & 32) != 0) { - int varPos1 = offset + 69 + buf.getIntLE(offset + 65); - obj.particle = FluidParticle.deserialize(buf, varPos1); - } + obj.colorSaturation = buf.getFloatLE(offset + 25); + obj.distortionAmplitude = buf.getFloatLE(offset + 29); + obj.distortionFrequency = buf.getFloatLE(offset + 33); + if ((nullBits & 8) != 0) { + obj.movementSettings = FluidFXMovementSettings.deserialize(buf, offset + 37); + } - return obj; + if ((nullBits & 16) != 0) { + int varPosBase0 = buf.getIntLE(offset + 61); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 69) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 69 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 32) != 0) { + int varPosBase1 = buf.getIntLE(offset + 65); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 69) { + throw ProtocolException.invalidOffset("Particle", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 69 + varPosBase1; + obj.particle = FluidParticle.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -140,9 +159,13 @@ public class FluidFX { int maxEnd = 69; if ((nullBits & 16) != 0) { int fieldOffset0 = buf.getIntLE(offset + 61); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 69) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 69 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -150,6 +173,10 @@ public class FluidFX { if ((nullBits & 32) != 0) { int fieldOffset1 = buf.getIntLE(offset + 65); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 69) { + throw ProtocolException.invalidOffset("Particle", fieldOffset1, maxEnd); + } + int pos1 = offset + 69 + fieldOffset1; pos1 += FluidParticle.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -257,53 +284,55 @@ public class FluidFX { return ValidationResult.error("Buffer too small: expected at least 69 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 16) != 0) { - int idOffset = buffer.getIntLE(offset + 61); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 10) { + return ValidationResult.error("Invalid ShaderType value for Shader"); + } else { + v = buffer.getByte(offset + 2) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid FluidFog value for FogMode"); + } else { + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 61); + if (v < 0 || v > buffer.writerIndex() - offset - 69) { + return ValidationResult.error("Invalid offset for Id"); + } - int pos = offset + 69 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } + int pos = offset + 69 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } + } - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 65); + if (v < 0 || v > buffer.writerIndex() - offset - 69) { + return ValidationResult.error("Invalid offset for Particle"); + } + + int posx = offset + 69 + v; + ValidationResult particleResult = FluidParticle.validateStructure(buffer, posx); + if (!particleResult.isValid()) { + return ValidationResult.error("Invalid Particle: " + particleResult.error()); + } + + posx += FluidParticle.computeBytesConsumed(buffer, posx); + } + + return ValidationResult.OK; } } - - if ((nullBits & 32) != 0) { - int particleOffset = buffer.getIntLE(offset + 65); - if (particleOffset < 0) { - return ValidationResult.error("Invalid offset for Particle"); - } - - int posx = offset + 69 + particleOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particle"); - } - - ValidationResult particleResult = FluidParticle.validateStructure(buffer, posx); - if (!particleResult.isValid()) { - return ValidationResult.error("Invalid Particle: " + particleResult.error()); - } - - posx += FluidParticle.computeBytesConsumed(buffer, posx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/FluidFXMovementSettings.java b/src/com/hypixel/hytale/protocol/FluidFXMovementSettings.java index 18220768..94261b23 100644 --- a/src/com/hypixel/hytale/protocol/FluidFXMovementSettings.java +++ b/src/com/hypixel/hytale/protocol/FluidFXMovementSettings.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -43,14 +44,18 @@ public class FluidFXMovementSettings { @Nonnull public static FluidFXMovementSettings deserialize(@Nonnull ByteBuf buf, int offset) { - FluidFXMovementSettings obj = new FluidFXMovementSettings(); - obj.swimUpSpeed = buf.getFloatLE(offset + 0); - obj.swimDownSpeed = buf.getFloatLE(offset + 4); - obj.sinkSpeed = buf.getFloatLE(offset + 8); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 12); - obj.fieldOfViewMultiplier = buf.getFloatLE(offset + 16); - obj.entryVelocityMultiplier = buf.getFloatLE(offset + 20); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("FluidFXMovementSettings", 24, buf.readableBytes() - offset); + } else { + FluidFXMovementSettings obj = new FluidFXMovementSettings(); + obj.swimUpSpeed = buf.getFloatLE(offset + 0); + obj.swimDownSpeed = buf.getFloatLE(offset + 4); + obj.sinkSpeed = buf.getFloatLE(offset + 8); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 12); + obj.fieldOfViewMultiplier = buf.getFloatLE(offset + 16); + obj.entryVelocityMultiplier = buf.getFloatLE(offset + 20); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/FluidParticle.java b/src/com/hypixel/hytale/protocol/FluidParticle.java index 517553dd..76e6dbbe 100644 --- a/src/com/hypixel/hytale/protocol/FluidParticle.java +++ b/src/com/hypixel/hytale/protocol/FluidParticle.java @@ -38,30 +38,38 @@ public class FluidParticle { @Nonnull public static FluidParticle deserialize(@Nonnull ByteBuf buf, int offset) { - FluidParticle obj = new FluidParticle(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.color = Color.deserialize(buf, offset + 1); - } - - obj.scale = buf.getFloatLE(offset + 4); - int pos = offset + 8; - if ((nullBits & 2) != 0) { - int systemIdLen = VarInt.peek(buf, pos); - if (systemIdLen < 0) { - throw ProtocolException.negativeLength("SystemId", systemIdLen); + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("FluidParticle", 8, buf.readableBytes() - offset); + } else { + FluidParticle obj = new FluidParticle(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.color = Color.deserialize(buf, offset + 1); } - if (systemIdLen > 4096000) { - throw ProtocolException.stringTooLong("SystemId", systemIdLen, 4096000); + obj.scale = buf.getFloatLE(offset + 4); + int pos = offset + 8; + if ((nullBits & 2) != 0) { + int systemIdLen = VarInt.peek(buf, pos); + if (systemIdLen < 0) { + throw ProtocolException.invalidVarInt("SystemId"); + } + + int systemIdVarLen = VarInt.size(systemIdLen); + if (systemIdLen > 4096000) { + throw ProtocolException.stringTooLong("SystemId", systemIdLen, 4096000); + } + + if (pos + systemIdVarLen + systemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SystemId", pos + systemIdVarLen + systemIdLen, buf.readableBytes()); + } + + obj.systemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += systemIdVarLen + systemIdLen; } - int systemIdVarLen = VarInt.length(buf, pos); - obj.systemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += systemIdVarLen + systemIdLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -69,7 +77,7 @@ public class FluidParticle { int pos = offset + 8; if ((nullBits & 2) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -123,7 +131,7 @@ public class FluidParticle { return ValidationResult.error("SystemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(systemIdLen); pos += systemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SystemId"); diff --git a/src/com/hypixel/hytale/protocol/FogOptions.java b/src/com/hypixel/hytale/protocol/FogOptions.java index 3e7cff68..5331c243 100644 --- a/src/com/hypixel/hytale/protocol/FogOptions.java +++ b/src/com/hypixel/hytale/protocol/FogOptions.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -48,14 +49,18 @@ public class FogOptions { @Nonnull public static FogOptions deserialize(@Nonnull ByteBuf buf, int offset) { - FogOptions obj = new FogOptions(); - obj.ignoreFogLimits = buf.getByte(offset + 0) != 0; - obj.effectiveViewDistanceMultiplier = buf.getFloatLE(offset + 1); - obj.fogFarViewDistance = buf.getFloatLE(offset + 5); - obj.fogHeightCameraOffset = buf.getFloatLE(offset + 9); - obj.fogHeightCameraOverriden = buf.getByte(offset + 13) != 0; - obj.fogHeightCameraFixed = buf.getFloatLE(offset + 14); - return obj; + if (buf.readableBytes() - offset < 18) { + throw ProtocolException.bufferTooSmall("FogOptions", 18, buf.readableBytes() - offset); + } else { + FogOptions obj = new FogOptions(); + obj.ignoreFogLimits = buf.getByte(offset + 0) != 0; + obj.effectiveViewDistanceMultiplier = buf.getFloatLE(offset + 1); + obj.fogFarViewDistance = buf.getFloatLE(offset + 5); + obj.fogHeightCameraOffset = buf.getFloatLE(offset + 9); + obj.fogHeightCameraOverriden = buf.getByte(offset + 13) != 0; + obj.fogHeightCameraFixed = buf.getFloatLE(offset + 14); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ForkedChainId.java b/src/com/hypixel/hytale/protocol/ForkedChainId.java index 6913f2a9..5f9df4bf 100644 --- a/src/com/hypixel/hytale/protocol/ForkedChainId.java +++ b/src/com/hypixel/hytale/protocol/ForkedChainId.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -34,17 +35,21 @@ public class ForkedChainId { @Nonnull public static ForkedChainId deserialize(@Nonnull ByteBuf buf, int offset) { - ForkedChainId obj = new ForkedChainId(); - byte nullBits = buf.getByte(offset); - obj.entryIndex = buf.getIntLE(offset + 1); - obj.subIndex = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - obj.forkedId = deserialize(buf, pos); - pos += computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ForkedChainId", 9, buf.readableBytes() - offset); + } else { + ForkedChainId obj = new ForkedChainId(); + byte nullBits = buf.getByte(offset); + obj.entryIndex = buf.getIntLE(offset + 1); + obj.subIndex = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + obj.forkedId = deserialize(buf, pos); + pos += computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/FormattedMessage.java b/src/com/hypixel/hytale/protocol/FormattedMessage.java index 4b96fc31..b57dfef4 100644 --- a/src/com/hypixel/hytale/protocol/FormattedMessage.java +++ b/src/com/hypixel/hytale/protocol/FormattedMessage.java @@ -16,8 +16,8 @@ import javax.annotation.Nullable; public class FormattedMessage { public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 6; - public static final int VARIABLE_FIELD_COUNT = 7; - public static final int VARIABLE_BLOCK_START = 34; + public static final int VARIABLE_FIELD_COUNT = 8; + public static final int VARIABLE_BLOCK_START = 38; public static final int MAX_SIZE = 1677721600; @Nullable public String rawText; @@ -42,6 +42,8 @@ public class FormattedMessage { @Nullable public String link; public boolean markupEnabled; + @Nullable + public FormattedMessageImage image; public FormattedMessage() { } @@ -58,7 +60,8 @@ public class FormattedMessage { @Nonnull MaybeBool monospace, @Nonnull MaybeBool underlined, @Nullable String link, - boolean markupEnabled + boolean markupEnabled, + @Nullable FormattedMessageImage image ) { this.rawText = rawText; this.messageId = messageId; @@ -72,6 +75,7 @@ public class FormattedMessage { this.underlined = underlined; this.link = link; this.markupEnabled = markupEnabled; + this.image = image; } public FormattedMessage(@Nonnull FormattedMessage other) { @@ -87,181 +91,263 @@ public class FormattedMessage { this.underlined = other.underlined; this.link = other.link; this.markupEnabled = other.markupEnabled; + this.image = other.image; } @Nonnull public static FormattedMessage deserialize(@Nonnull ByteBuf buf, int offset) { - FormattedMessage obj = new FormattedMessage(); - byte nullBits = buf.getByte(offset); - obj.bold = MaybeBool.fromValue(buf.getByte(offset + 1)); - obj.italic = MaybeBool.fromValue(buf.getByte(offset + 2)); - obj.monospace = MaybeBool.fromValue(buf.getByte(offset + 3)); - obj.underlined = MaybeBool.fromValue(buf.getByte(offset + 4)); - obj.markupEnabled = buf.getByte(offset + 5) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 34 + buf.getIntLE(offset + 6); - int rawTextLen = VarInt.peek(buf, varPos0); - if (rawTextLen < 0) { - throw ProtocolException.negativeLength("RawText", rawTextLen); - } - - if (rawTextLen > 4096000) { - throw ProtocolException.stringTooLong("RawText", rawTextLen, 4096000); - } - - obj.rawText = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 34 + buf.getIntLE(offset + 10); - int messageIdLen = VarInt.peek(buf, varPos1); - if (messageIdLen < 0) { - throw ProtocolException.negativeLength("MessageId", messageIdLen); - } - - if (messageIdLen > 4096000) { - throw ProtocolException.stringTooLong("MessageId", messageIdLen, 4096000); - } - - obj.messageId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 34 + buf.getIntLE(offset + 14); - int childrenCount = VarInt.peek(buf, varPos2); - if (childrenCount < 0) { - throw ProtocolException.negativeLength("Children", childrenCount); - } - - if (childrenCount > 4096000) { - throw ProtocolException.arrayTooLong("Children", childrenCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + childrenCount * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Children", varPos2 + varIntLen + childrenCount * 6, buf.readableBytes()); - } - - obj.children = new FormattedMessage[childrenCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < childrenCount; i++) { - obj.children[i] = deserialize(buf, elemPos); - elemPos += computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 34 + buf.getIntLE(offset + 18); - int paramsCount = VarInt.peek(buf, varPos3); - if (paramsCount < 0) { - throw ProtocolException.negativeLength("Params", paramsCount); - } - - if (paramsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Params", paramsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - obj.params = new HashMap<>(paramsCount); - int dictPos = varPos3 + varIntLen; - - for (int i = 0; i < paramsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 38) { + throw ProtocolException.bufferTooSmall("FormattedMessage", 38, buf.readableBytes() - offset); + } else { + FormattedMessage obj = new FormattedMessage(); + byte nullBits = buf.getByte(offset); + obj.bold = MaybeBool.fromValue(buf.getByte(offset + 1)); + obj.italic = MaybeBool.fromValue(buf.getByte(offset + 2)); + obj.monospace = MaybeBool.fromValue(buf.getByte(offset + 3)); + obj.underlined = MaybeBool.fromValue(buf.getByte(offset + 4)); + obj.markupEnabled = buf.getByte(offset + 5) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("RawText", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 38 + varPosBase0; + int rawTextLen = VarInt.peek(buf, varPos0); + if (rawTextLen < 0) { + throw ProtocolException.invalidVarInt("RawText"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - ParamValue val = ParamValue.deserialize(buf, dictPos); - dictPos += ParamValue.computeBytesConsumed(buf, dictPos); - if (obj.params.put(key, val) != null) { - throw ProtocolException.duplicateKey("params", key); + int rawTextVarIntLen = VarInt.size(rawTextLen); + if (rawTextLen > 4096000) { + throw ProtocolException.stringTooLong("RawText", rawTextLen, 4096000); + } + + if (varPos0 + rawTextVarIntLen + rawTextLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RawText", varPos0 + rawTextVarIntLen + rawTextLen, buf.readableBytes()); + } + + obj.rawText = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 10); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("MessageId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 38 + varPosBase1; + int messageIdLen = VarInt.peek(buf, varPos1); + if (messageIdLen < 0) { + throw ProtocolException.invalidVarInt("MessageId"); + } + + int messageIdVarIntLen = VarInt.size(messageIdLen); + if (messageIdLen > 4096000) { + throw ProtocolException.stringTooLong("MessageId", messageIdLen, 4096000); + } + + if (varPos1 + messageIdVarIntLen + messageIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("MessageId", varPos1 + messageIdVarIntLen + messageIdLen, buf.readableBytes()); + } + + obj.messageId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 14); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Children", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 38 + varPosBase2; + int childrenCount = VarInt.peek(buf, varPos2); + if (childrenCount < 0) { + throw ProtocolException.invalidVarInt("Children"); + } + + int varIntLen = VarInt.size(childrenCount); + if (childrenCount > 4096000) { + throw ProtocolException.arrayTooLong("Children", childrenCount, 4096000); + } + + if (varPos2 + varIntLen + childrenCount * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Children", varPos2 + varIntLen + childrenCount * 6, buf.readableBytes()); + } + + obj.children = new FormattedMessage[childrenCount]; + int elemPos = varPos2 + varIntLen; + + for (int i = 0; i < childrenCount; i++) { + obj.children[i] = deserialize(buf, elemPos); + elemPos += computeBytesConsumed(buf, elemPos); } } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 18); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Params", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 38 + varPosBase3; + int paramsCount = VarInt.peek(buf, varPos3); + if (paramsCount < 0) { + throw ProtocolException.invalidVarInt("Params"); + } + + int varIntLenx = VarInt.size(paramsCount); + if (paramsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Params", paramsCount, 4096000); + } + + obj.params = new HashMap<>(paramsCount); + int dictPos = varPos3 + varIntLenx; + + for (int i = 0; i < paramsCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + ParamValue val = ParamValue.deserialize(buf, dictPos); + dictPos += ParamValue.computeBytesConsumed(buf, dictPos); + if (obj.params.put(key, val) != null) { + throw ProtocolException.duplicateKey("params", key); + } + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 22); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("MessageParams", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 38 + varPosBase4; + int messageParamsCount = VarInt.peek(buf, varPos4); + if (messageParamsCount < 0) { + throw ProtocolException.invalidVarInt("MessageParams"); + } + + int varIntLenx = VarInt.size(messageParamsCount); + if (messageParamsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("MessageParams", messageParamsCount, 4096000); + } + + obj.messageParams = new HashMap<>(messageParamsCount); + int dictPos = varPos4 + varIntLenx; + + for (int i = 0; i < messageParamsCount; i++) { + int keyLenx = VarInt.peek(buf, dictPos); + if (keyLenx < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLenx = VarInt.size(keyLenx); + if (keyLenx > 4096000) { + throw ProtocolException.stringTooLong("key", keyLenx, 4096000); + } + + if (dictPos + keyVarLenx + keyLenx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLenx + keyLenx, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLenx + keyLenx; + FormattedMessage val = deserialize(buf, dictPos); + dictPos += computeBytesConsumed(buf, dictPos); + if (obj.messageParams.put(key, val) != null) { + throw ProtocolException.duplicateKey("messageParams", key); + } + } + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 26); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Color", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 38 + varPosBase5; + int colorLen = VarInt.peek(buf, varPos5); + if (colorLen < 0) { + throw ProtocolException.invalidVarInt("Color"); + } + + int colorVarIntLen = VarInt.size(colorLen); + if (colorLen > 4096000) { + throw ProtocolException.stringTooLong("Color", colorLen, 4096000); + } + + if (varPos5 + colorVarIntLen + colorLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Color", varPos5 + colorVarIntLen + colorLen, buf.readableBytes()); + } + + obj.color = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + if ((nullBits & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 30); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Link", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 38 + varPosBase6; + int linkLen = VarInt.peek(buf, varPos6); + if (linkLen < 0) { + throw ProtocolException.invalidVarInt("Link"); + } + + int linkVarIntLen = VarInt.size(linkLen); + if (linkLen > 4096000) { + throw ProtocolException.stringTooLong("Link", linkLen, 4096000); + } + + if (varPos6 + linkVarIntLen + linkLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Link", varPos6 + linkVarIntLen + linkLen, buf.readableBytes()); + } + + obj.link = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); + } + + if ((nullBits & 128) != 0) { + int varPosBase7 = buf.getIntLE(offset + 34); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Image", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 38 + varPosBase7; + obj.image = FormattedMessageImage.deserialize(buf, varPos7); + } + + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 34 + buf.getIntLE(offset + 22); - int messageParamsCount = VarInt.peek(buf, varPos4); - if (messageParamsCount < 0) { - throw ProtocolException.negativeLength("MessageParams", messageParamsCount); - } - - if (messageParamsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("MessageParams", messageParamsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos4); - obj.messageParams = new HashMap<>(messageParamsCount); - int dictPos = varPos4 + varIntLen; - - for (int i = 0; i < messageParamsCount; i++) { - int keyLenx = VarInt.peek(buf, dictPos); - if (keyLenx < 0) { - throw ProtocolException.negativeLength("key", keyLenx); - } - - if (keyLenx > 4096000) { - throw ProtocolException.stringTooLong("key", keyLenx, 4096000); - } - - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLenx; - FormattedMessage val = deserialize(buf, dictPos); - dictPos += computeBytesConsumed(buf, dictPos); - if (obj.messageParams.put(key, val) != null) { - throw ProtocolException.duplicateKey("messageParams", key); - } - } - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 34 + buf.getIntLE(offset + 26); - int colorLen = VarInt.peek(buf, varPos5); - if (colorLen < 0) { - throw ProtocolException.negativeLength("Color", colorLen); - } - - if (colorLen > 4096000) { - throw ProtocolException.stringTooLong("Color", colorLen, 4096000); - } - - obj.color = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - if ((nullBits & 64) != 0) { - int varPos6 = offset + 34 + buf.getIntLE(offset + 30); - int linkLen = VarInt.peek(buf, varPos6); - if (linkLen < 0) { - throw ProtocolException.negativeLength("Link", linkLen); - } - - if (linkLen > 4096000) { - throw ProtocolException.stringTooLong("Link", linkLen, 4096000); - } - - obj.link = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 34; + int maxEnd = 38; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6); - int pos0 = offset + 34 + fieldOffset0; + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("RawText", fieldOffset0, maxEnd); + } + + int pos0 = offset + 38 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -269,9 +355,13 @@ public class FormattedMessage { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 10); - int pos1 = offset + 34 + fieldOffset1; + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("MessageId", fieldOffset1, maxEnd); + } + + int pos1 = offset + 38 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -279,9 +369,13 @@ public class FormattedMessage { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 14); - int pos2 = offset + 34 + fieldOffset2; + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Children", fieldOffset2, maxEnd); + } + + int pos2 = offset + 38 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += computeBytesConsumed(buf, pos2); @@ -294,13 +388,17 @@ public class FormattedMessage { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 18); - int pos3 = offset + 34 + fieldOffset3; + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Params", fieldOffset3, maxEnd); + } + + int pos3 = offset + 38 + fieldOffset3; int dictLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; pos3 += ParamValue.computeBytesConsumed(buf, pos3); } @@ -311,13 +409,17 @@ public class FormattedMessage { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 22); - int pos4 = offset + 34 + fieldOffset4; + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("MessageParams", fieldOffset4, maxEnd); + } + + int pos4 = offset + 38 + fieldOffset4; int dictLen = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4); + pos4 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; pos4 += computeBytesConsumed(buf, pos4); } @@ -328,9 +430,13 @@ public class FormattedMessage { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 26); - int pos5 = offset + 34 + fieldOffset5; + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Color", fieldOffset5, maxEnd); + } + + int pos5 = offset + 38 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -338,14 +444,31 @@ public class FormattedMessage { if ((nullBits & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 30); - int pos6 = offset + 34 + fieldOffset6; + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Link", fieldOffset6, maxEnd); + } + + int pos6 = offset + 38 + fieldOffset6; int sl = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6) + sl; + pos6 += VarInt.size(sl) + sl; if (pos6 - offset > maxEnd) { maxEnd = pos6 - offset; } } + if ((nullBits & 128) != 0) { + int fieldOffset7 = buf.getIntLE(offset + 34); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Image", fieldOffset7, maxEnd); + } + + int pos7 = offset + 38 + fieldOffset7; + pos7 += FormattedMessageImage.computeBytesConsumed(buf, pos7); + if (pos7 - offset > maxEnd) { + maxEnd = pos7 - offset; + } + } + return maxEnd; } @@ -380,6 +503,10 @@ public class FormattedMessage { nullBits = (byte)(nullBits | 64); } + if (this.image != null) { + nullBits = (byte)(nullBits | 128); + } + buf.writeByte(nullBits); buf.writeByte(this.bold.getValue()); buf.writeByte(this.italic.getValue()); @@ -400,6 +527,8 @@ public class FormattedMessage { buf.writeIntLE(0); int linkOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int imageOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); if (this.rawText != null) { buf.setIntLE(rawTextOffsetSlot, buf.writerIndex() - varBlockStart); @@ -475,10 +604,17 @@ public class FormattedMessage { } else { buf.setIntLE(linkOffsetSlot, -1); } + + if (this.image != null) { + buf.setIntLE(imageOffsetSlot, buf.writerIndex() - varBlockStart); + this.image.serialize(buf); + } else { + buf.setIntLE(imageOffsetSlot, -1); + } } public int computeSize() { - int size = 34; + int size = 38; if (this.rawText != null) { size += PacketIO.stringSize(this.rawText); } @@ -525,239 +661,250 @@ public class FormattedMessage { size += PacketIO.stringSize(this.link); } + if (this.image != null) { + size += this.image.computeSize(); + } + return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 34) { - return ValidationResult.error("Buffer too small: expected at least 34 bytes"); + if (buffer.readableBytes() - offset < 38) { + return ValidationResult.error("Buffer too small: expected at least 38 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int rawTextOffset = buffer.getIntLE(offset + 6); - if (rawTextOffset < 0) { - return ValidationResult.error("Invalid offset for RawText"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid MaybeBool value for Bold"); + } else { + v = buffer.getByte(offset + 2) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid MaybeBool value for Italic"); + } else { + v = buffer.getByte(offset + 3) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid MaybeBool value for Monospace"); + } else { + v = buffer.getByte(offset + 4) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid MaybeBool value for Underlined"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for RawText"); + } - int pos = offset + 34 + rawTextOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for RawText"); - } + int pos = offset + 38 + v; + int rawTextLen = VarInt.peek(buffer, pos); + if (rawTextLen < 0) { + return ValidationResult.error("Invalid string length for RawText"); + } - int rawTextLen = VarInt.peek(buffer, pos); - if (rawTextLen < 0) { - return ValidationResult.error("Invalid string length for RawText"); - } + if (rawTextLen > 4096000) { + return ValidationResult.error("RawText exceeds max length 4096000"); + } - if (rawTextLen > 4096000) { - return ValidationResult.error("RawText exceeds max length 4096000"); - } + pos += VarInt.size(rawTextLen); + pos += rawTextLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading RawText"); + } + } - pos += VarInt.length(buffer, pos); - pos += rawTextLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading RawText"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for MessageId"); + } + + int posx = offset + 38 + v; + int messageIdLen = VarInt.peek(buffer, posx); + if (messageIdLen < 0) { + return ValidationResult.error("Invalid string length for MessageId"); + } + + if (messageIdLen > 4096000) { + return ValidationResult.error("MessageId exceeds max length 4096000"); + } + + posx += VarInt.size(messageIdLen); + posx += messageIdLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading MessageId"); + } + } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 14); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for Children"); + } + + int posxx = offset + 38 + v; + int childrenCount = VarInt.peek(buffer, posxx); + if (childrenCount < 0) { + return ValidationResult.error("Invalid array count for Children"); + } + + if (childrenCount > 4096000) { + return ValidationResult.error("Children exceeds max length 4096000"); + } + + posxx += VarInt.size(childrenCount); + + for (int i = 0; i < childrenCount; i++) { + ValidationResult structResult = validateStructure(buffer, posxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid FormattedMessage in Children[" + i + "]: " + structResult.error()); + } + + posxx += computeBytesConsumed(buffer, posxx); + } + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 18); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for Params"); + } + + int posxxx = offset + 38 + v; + int paramsCount = VarInt.peek(buffer, posxxx); + if (paramsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Params"); + } + + if (paramsCount > 4096000) { + return ValidationResult.error("Params exceeds max length 4096000"); + } + + posxxx += VarInt.size(paramsCount); + + for (int i = 0; i < paramsCount; i++) { + int keyLen = VarInt.peek(buffer, posxxx); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxx += VarInt.size(keyLen); + posxxx += keyLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxxx += ParamValue.computeBytesConsumed(buffer, posxxx); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 22); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for MessageParams"); + } + + int posxxxx = offset + 38 + v; + int messageParamsCount = VarInt.peek(buffer, posxxxx); + if (messageParamsCount < 0) { + return ValidationResult.error("Invalid dictionary count for MessageParams"); + } + + if (messageParamsCount > 4096000) { + return ValidationResult.error("MessageParams exceeds max length 4096000"); + } + + posxxxx += VarInt.size(messageParamsCount); + + for (int i = 0; i < messageParamsCount; i++) { + int keyLenx = VarInt.peek(buffer, posxxxx); + if (keyLenx < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLenx > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxxx += VarInt.size(keyLenx); + posxxxx += keyLenx; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxxxx += computeBytesConsumed(buffer, posxxxx); + } + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 26); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for Color"); + } + + int posxxxxx = offset + 38 + v; + int colorLen = VarInt.peek(buffer, posxxxxx); + if (colorLen < 0) { + return ValidationResult.error("Invalid string length for Color"); + } + + if (colorLen > 4096000) { + return ValidationResult.error("Color exceeds max length 4096000"); + } + + posxxxxx += VarInt.size(colorLen); + posxxxxx += colorLen; + if (posxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Color"); + } + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 30); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for Link"); + } + + int posxxxxxx = offset + 38 + v; + int linkLen = VarInt.peek(buffer, posxxxxxx); + if (linkLen < 0) { + return ValidationResult.error("Invalid string length for Link"); + } + + if (linkLen > 4096000) { + return ValidationResult.error("Link exceeds max length 4096000"); + } + + posxxxxxx += VarInt.size(linkLen); + posxxxxxx += linkLen; + if (posxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Link"); + } + } + + if ((nullBits & 128) != 0) { + v = buffer.getIntLE(offset + 34); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for Image"); + } + + int posxxxxxxx = offset + 38 + v; + ValidationResult imageResult = FormattedMessageImage.validateStructure(buffer, posxxxxxxx); + if (!imageResult.isValid()) { + return ValidationResult.error("Invalid Image: " + imageResult.error()); + } + + posxxxxxxx += FormattedMessageImage.computeBytesConsumed(buffer, posxxxxxxx); + } + + return ValidationResult.OK; + } + } } } - - if ((nullBits & 2) != 0) { - int messageIdOffset = buffer.getIntLE(offset + 10); - if (messageIdOffset < 0) { - return ValidationResult.error("Invalid offset for MessageId"); - } - - int posx = offset + 34 + messageIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MessageId"); - } - - int messageIdLen = VarInt.peek(buffer, posx); - if (messageIdLen < 0) { - return ValidationResult.error("Invalid string length for MessageId"); - } - - if (messageIdLen > 4096000) { - return ValidationResult.error("MessageId exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += messageIdLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading MessageId"); - } - } - - if ((nullBits & 4) != 0) { - int childrenOffset = buffer.getIntLE(offset + 14); - if (childrenOffset < 0) { - return ValidationResult.error("Invalid offset for Children"); - } - - int posxx = offset + 34 + childrenOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Children"); - } - - int childrenCount = VarInt.peek(buffer, posxx); - if (childrenCount < 0) { - return ValidationResult.error("Invalid array count for Children"); - } - - if (childrenCount > 4096000) { - return ValidationResult.error("Children exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - - for (int i = 0; i < childrenCount; i++) { - ValidationResult structResult = validateStructure(buffer, posxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid FormattedMessage in Children[" + i + "]: " + structResult.error()); - } - - posxx += computeBytesConsumed(buffer, posxx); - } - } - - if ((nullBits & 8) != 0) { - int paramsOffset = buffer.getIntLE(offset + 18); - if (paramsOffset < 0) { - return ValidationResult.error("Invalid offset for Params"); - } - - int posxxx = offset + 34 + paramsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Params"); - } - - int paramsCount = VarInt.peek(buffer, posxxx); - if (paramsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Params"); - } - - if (paramsCount > 4096000) { - return ValidationResult.error("Params exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - - for (int i = 0; i < paramsCount; i++) { - int keyLen = VarInt.peek(buffer, posxxx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += keyLen; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - posxxx += ParamValue.computeBytesConsumed(buffer, posxxx); - } - } - - if ((nullBits & 16) != 0) { - int messageParamsOffset = buffer.getIntLE(offset + 22); - if (messageParamsOffset < 0) { - return ValidationResult.error("Invalid offset for MessageParams"); - } - - int posxxxx = offset + 34 + messageParamsOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MessageParams"); - } - - int messageParamsCount = VarInt.peek(buffer, posxxxx); - if (messageParamsCount < 0) { - return ValidationResult.error("Invalid dictionary count for MessageParams"); - } - - if (messageParamsCount > 4096000) { - return ValidationResult.error("MessageParams exceeds max length 4096000"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - - for (int i = 0; i < messageParamsCount; i++) { - int keyLenx = VarInt.peek(buffer, posxxxx); - if (keyLenx < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLenx > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - posxxxx += keyLenx; - if (posxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - posxxxx += computeBytesConsumed(buffer, posxxxx); - } - } - - if ((nullBits & 32) != 0) { - int colorOffset = buffer.getIntLE(offset + 26); - if (colorOffset < 0) { - return ValidationResult.error("Invalid offset for Color"); - } - - int posxxxxx = offset + 34 + colorOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Color"); - } - - int colorLen = VarInt.peek(buffer, posxxxxx); - if (colorLen < 0) { - return ValidationResult.error("Invalid string length for Color"); - } - - if (colorLen > 4096000) { - return ValidationResult.error("Color exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += colorLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Color"); - } - } - - if ((nullBits & 64) != 0) { - int linkOffset = buffer.getIntLE(offset + 30); - if (linkOffset < 0) { - return ValidationResult.error("Invalid offset for Link"); - } - - int posxxxxxx = offset + 34 + linkOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Link"); - } - - int linkLen = VarInt.peek(buffer, posxxxxxx); - if (linkLen < 0) { - return ValidationResult.error("Invalid string length for Link"); - } - - if (linkLen > 4096000) { - return ValidationResult.error("Link exceeds max length 4096000"); - } - - posxxxxxx += VarInt.length(buffer, posxxxxxx); - posxxxxxx += linkLen; - if (posxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Link"); - } - } - - return ValidationResult.OK; } } @@ -784,6 +931,7 @@ public class FormattedMessage { copy.underlined = this.underlined; copy.link = this.link; copy.markupEnabled = this.markupEnabled; + copy.image = this.image != null ? this.image.clone() : null; return copy; } @@ -805,7 +953,8 @@ public class FormattedMessage { && Objects.equals(this.monospace, other.monospace) && Objects.equals(this.underlined, other.underlined) && Objects.equals(this.link, other.link) - && this.markupEnabled == other.markupEnabled; + && this.markupEnabled == other.markupEnabled + && Objects.equals(this.image, other.image); } } @@ -823,6 +972,7 @@ public class FormattedMessage { result = 31 * result + Objects.hashCode(this.monospace); result = 31 * result + Objects.hashCode(this.underlined); result = 31 * result + Objects.hashCode(this.link); - return 31 * result + Boolean.hashCode(this.markupEnabled); + result = 31 * result + Boolean.hashCode(this.markupEnabled); + return 31 * result + Objects.hashCode(this.image); } } diff --git a/src/com/hypixel/hytale/protocol/FormattedMessageImage.java b/src/com/hypixel/hytale/protocol/FormattedMessageImage.java new file mode 100644 index 00000000..7a7dfea9 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/FormattedMessageImage.java @@ -0,0 +1,123 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class FormattedMessageImage { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 8; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 16384013; + @Nonnull + public String filePath = ""; + public int width; + public int height; + + public FormattedMessageImage() { + } + + public FormattedMessageImage(@Nonnull String filePath, int width, int height) { + this.filePath = filePath; + this.width = width; + this.height = height; + } + + public FormattedMessageImage(@Nonnull FormattedMessageImage other) { + this.filePath = other.filePath; + this.width = other.width; + this.height = other.height; + } + + @Nonnull + public static FormattedMessageImage deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("FormattedMessageImage", 8, buf.readableBytes() - offset); + } else { + FormattedMessageImage obj = new FormattedMessageImage(); + obj.width = buf.getIntLE(offset + 0); + obj.height = buf.getIntLE(offset + 4); + int pos = offset + 8; + int filePathLen = VarInt.peek(buf, pos); + if (filePathLen < 0) { + throw ProtocolException.invalidVarInt("FilePath"); + } else { + int filePathVarLen = VarInt.size(filePathLen); + if (filePathLen > 4096000) { + throw ProtocolException.stringTooLong("FilePath", filePathLen, 4096000); + } else if (pos + filePathVarLen + filePathLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FilePath", pos + filePathVarLen + filePathLen, buf.readableBytes()); + } else { + obj.filePath = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += filePathVarLen + filePathLen; + return obj; + } + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int pos = offset + 8; + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + return pos - offset; + } + + public void serialize(@Nonnull ByteBuf buf) { + buf.writeIntLE(this.width); + buf.writeIntLE(this.height); + PacketIO.writeVarString(buf, this.filePath, 4096000); + } + + public int computeSize() { + int size = 8; + return size + PacketIO.stringSize(this.filePath); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 8) { + return ValidationResult.error("Buffer too small: expected at least 8 bytes"); + } else { + int pos = offset + 8; + int filePathLen = VarInt.peek(buffer, pos); + if (filePathLen < 0) { + return ValidationResult.error("Invalid string length for FilePath"); + } else if (filePathLen > 4096000) { + return ValidationResult.error("FilePath exceeds max length 4096000"); + } else { + pos += VarInt.size(filePathLen); + pos += filePathLen; + return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading FilePath") : ValidationResult.OK; + } + } + } + + public FormattedMessageImage clone() { + FormattedMessageImage copy = new FormattedMessageImage(); + copy.filePath = this.filePath; + copy.width = this.width; + copy.height = this.height; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof FormattedMessageImage other) + ? false + : Objects.equals(this.filePath, other.filePath) && this.width == other.width && this.height == other.height; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.filePath, this.width, this.height); + } +} diff --git a/src/com/hypixel/hytale/protocol/HalfFloatPosition.java b/src/com/hypixel/hytale/protocol/HalfFloatPosition.java index 0c79b48d..d4b66982 100644 --- a/src/com/hypixel/hytale/protocol/HalfFloatPosition.java +++ b/src/com/hypixel/hytale/protocol/HalfFloatPosition.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class HalfFloatPosition { @Nonnull public static HalfFloatPosition deserialize(@Nonnull ByteBuf buf, int offset) { - HalfFloatPosition obj = new HalfFloatPosition(); - obj.x = buf.getShortLE(offset + 0); - obj.y = buf.getShortLE(offset + 2); - obj.z = buf.getShortLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("HalfFloatPosition", 6, buf.readableBytes() - offset); + } else { + HalfFloatPosition obj = new HalfFloatPosition(); + obj.x = buf.getShortLE(offset + 0); + obj.y = buf.getShortLE(offset + 2); + obj.z = buf.getShortLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Harvesting.java b/src/com/hypixel/hytale/protocol/Harvesting.java index 64afc065..6ae302db 100644 --- a/src/com/hypixel/hytale/protocol/Harvesting.java +++ b/src/com/hypixel/hytale/protocol/Harvesting.java @@ -35,37 +35,61 @@ public class Harvesting { @Nonnull public static Harvesting deserialize(@Nonnull ByteBuf buf, int offset) { - Harvesting obj = new Harvesting(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int itemIdLen = VarInt.peek(buf, varPos0); - if (itemIdLen < 0) { - throw ProtocolException.negativeLength("ItemId", itemIdLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("Harvesting", 9, buf.readableBytes() - offset); + } else { + Harvesting obj = new Harvesting(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ItemId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int itemIdLen = VarInt.peek(buf, varPos0); + if (itemIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemId"); + } + + int itemIdVarIntLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + } + + if (varPos0 + itemIdVarIntLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemId", varPos0 + itemIdVarIntLen + itemIdLen, buf.readableBytes()); + } + + obj.itemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (itemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("DropListId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int dropListIdLen = VarInt.peek(buf, varPos1); + if (dropListIdLen < 0) { + throw ProtocolException.invalidVarInt("DropListId"); + } + + int dropListIdVarIntLen = VarInt.size(dropListIdLen); + if (dropListIdLen > 4096000) { + throw ProtocolException.stringTooLong("DropListId", dropListIdLen, 4096000); + } + + if (varPos1 + dropListIdVarIntLen + dropListIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("DropListId", varPos1 + dropListIdVarIntLen + dropListIdLen, buf.readableBytes()); + } + + obj.dropListId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.itemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int dropListIdLen = VarInt.peek(buf, varPos1); - if (dropListIdLen < 0) { - throw ProtocolException.negativeLength("DropListId", dropListIdLen); - } - - if (dropListIdLen > 4096000) { - throw ProtocolException.stringTooLong("DropListId", dropListIdLen, 4096000); - } - - obj.dropListId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,9 +97,13 @@ public class Harvesting { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ItemId", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -83,9 +111,13 @@ public class Harvesting { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("DropListId", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -146,15 +178,11 @@ public class Harvesting { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int itemIdOffset = buffer.getIntLE(offset + 1); - if (itemIdOffset < 0) { + if (itemIdOffset < 0 || itemIdOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for ItemId"); } int pos = offset + 9 + itemIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemId"); - } - int itemIdLen = VarInt.peek(buffer, pos); if (itemIdLen < 0) { return ValidationResult.error("Invalid string length for ItemId"); @@ -164,7 +192,7 @@ public class Harvesting { return ValidationResult.error("ItemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(itemIdLen); pos += itemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemId"); @@ -173,15 +201,11 @@ public class Harvesting { if ((nullBits & 2) != 0) { int dropListIdOffset = buffer.getIntLE(offset + 5); - if (dropListIdOffset < 0) { + if (dropListIdOffset < 0 || dropListIdOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for DropListId"); } int posx = offset + 9 + dropListIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DropListId"); - } - int dropListIdLen = VarInt.peek(buffer, posx); if (dropListIdLen < 0) { return ValidationResult.error("Invalid string length for DropListId"); @@ -191,7 +215,7 @@ public class Harvesting { return ValidationResult.error("DropListId exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(dropListIdLen); posx += dropListIdLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading DropListId"); diff --git a/src/com/hypixel/hytale/protocol/HitEntity.java b/src/com/hypixel/hytale/protocol/HitEntity.java index 5edab1fe..3e3808ff 100644 --- a/src/com/hypixel/hytale/protocol/HitEntity.java +++ b/src/com/hypixel/hytale/protocol/HitEntity.java @@ -33,35 +33,39 @@ public class HitEntity { @Nonnull public static HitEntity deserialize(@Nonnull ByteBuf buf, int offset) { - HitEntity obj = new HitEntity(); - byte nullBits = buf.getByte(offset); - obj.next = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int matchersCount = VarInt.peek(buf, pos); - if (matchersCount < 0) { - throw ProtocolException.negativeLength("Matchers", matchersCount); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("HitEntity", 5, buf.readableBytes() - offset); + } else { + HitEntity obj = new HitEntity(); + byte nullBits = buf.getByte(offset); + obj.next = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int matchersCount = VarInt.peek(buf, pos); + if (matchersCount < 0) { + throw ProtocolException.invalidVarInt("Matchers"); + } + + int matchersVarLen = VarInt.size(matchersCount); + if (matchersCount > 4096000) { + throw ProtocolException.arrayTooLong("Matchers", matchersCount, 4096000); + } + + if (pos + matchersVarLen + matchersCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Matchers", pos + matchersVarLen + matchersCount * 2, buf.readableBytes()); + } + + pos += matchersVarLen; + obj.matchers = new EntityMatcher[matchersCount]; + + for (int i = 0; i < matchersCount; i++) { + obj.matchers[i] = EntityMatcher.deserialize(buf, pos); + pos += EntityMatcher.computeBytesConsumed(buf, pos); + } } - if (matchersCount > 4096000) { - throw ProtocolException.arrayTooLong("Matchers", matchersCount, 4096000); - } - - int matchersVarLen = VarInt.size(matchersCount); - if (pos + matchersVarLen + matchersCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Matchers", pos + matchersVarLen + matchersCount * 2, buf.readableBytes()); - } - - pos += matchersVarLen; - obj.matchers = new EntityMatcher[matchersCount]; - - for (int i = 0; i < matchersCount; i++) { - obj.matchers[i] = EntityMatcher.deserialize(buf, pos); - pos += EntityMatcher.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -69,7 +73,7 @@ public class HitEntity { int pos = offset + 5; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += EntityMatcher.computeBytesConsumed(buf, pos); @@ -125,7 +129,7 @@ public class HitEntity { return ValidationResult.error("Matchers exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(matchersCount); pos += matchersCount * 2; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Matchers"); diff --git a/src/com/hypixel/hytale/protocol/Hitbox.java b/src/com/hypixel/hytale/protocol/Hitbox.java index 5b3310ef..e7b7128e 100644 --- a/src/com/hypixel/hytale/protocol/Hitbox.java +++ b/src/com/hypixel/hytale/protocol/Hitbox.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,14 +42,18 @@ public class Hitbox { @Nonnull public static Hitbox deserialize(@Nonnull ByteBuf buf, int offset) { - Hitbox obj = new Hitbox(); - obj.minX = buf.getFloatLE(offset + 0); - obj.minY = buf.getFloatLE(offset + 4); - obj.minZ = buf.getFloatLE(offset + 8); - obj.maxX = buf.getFloatLE(offset + 12); - obj.maxY = buf.getFloatLE(offset + 16); - obj.maxZ = buf.getFloatLE(offset + 20); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("Hitbox", 24, buf.readableBytes() - offset); + } else { + Hitbox obj = new Hitbox(); + obj.minX = buf.getFloatLE(offset + 0); + obj.minY = buf.getFloatLE(offset + 4); + obj.minZ = buf.getFloatLE(offset + 8); + obj.maxX = buf.getFloatLE(offset + 12); + obj.maxY = buf.getFloatLE(offset + 16); + obj.maxZ = buf.getFloatLE(offset + 20); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/HitboxCollisionConfig.java b/src/com/hypixel/hytale/protocol/HitboxCollisionConfig.java index a11c00bd..519fbc86 100644 --- a/src/com/hypixel/hytale/protocol/HitboxCollisionConfig.java +++ b/src/com/hypixel/hytale/protocol/HitboxCollisionConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -30,10 +31,14 @@ public class HitboxCollisionConfig { @Nonnull public static HitboxCollisionConfig deserialize(@Nonnull ByteBuf buf, int offset) { - HitboxCollisionConfig obj = new HitboxCollisionConfig(); - obj.collisionType = CollisionType.fromValue(buf.getByte(offset + 0)); - obj.softCollisionOffsetRatio = buf.getFloatLE(offset + 1); - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("HitboxCollisionConfig", 5, buf.readableBytes() - offset); + } else { + HitboxCollisionConfig obj = new HitboxCollisionConfig(); + obj.collisionType = CollisionType.fromValue(buf.getByte(offset + 0)); + obj.softCollisionOffsetRatio = buf.getFloatLE(offset + 1); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -50,7 +55,12 @@ public class HitboxCollisionConfig { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 5 ? ValidationResult.error("Buffer too small: expected at least 5 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 5) { + return ValidationResult.error("Buffer too small: expected at least 5 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 2 ? ValidationResult.error("Invalid CollisionType value for CollisionType") : ValidationResult.OK; + } } public HitboxCollisionConfig clone() { diff --git a/src/com/hypixel/hytale/protocol/HitboxCollisionUpdate.java b/src/com/hypixel/hytale/protocol/HitboxCollisionUpdate.java index afd4cc40..f44d0227 100644 --- a/src/com/hypixel/hytale/protocol/HitboxCollisionUpdate.java +++ b/src/com/hypixel/hytale/protocol/HitboxCollisionUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class HitboxCollisionUpdate extends ComponentUpdate { @Nonnull public static HitboxCollisionUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - HitboxCollisionUpdate obj = new HitboxCollisionUpdate(); - obj.hitboxCollisionConfigIndex = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("HitboxCollisionUpdate", 4, buf.readableBytes() - offset); + } else { + HitboxCollisionUpdate obj = new HitboxCollisionUpdate(); + obj.hitboxCollisionConfigIndex = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/HorizontalSelector.java b/src/com/hypixel/hytale/protocol/HorizontalSelector.java index c2a9c8ba..169bebee 100644 --- a/src/com/hypixel/hytale/protocol/HorizontalSelector.java +++ b/src/com/hypixel/hytale/protocol/HorizontalSelector.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -65,18 +66,22 @@ public class HorizontalSelector extends Selector { @Nonnull public static HorizontalSelector deserialize(@Nonnull ByteBuf buf, int offset) { - HorizontalSelector obj = new HorizontalSelector(); - obj.extendTop = buf.getFloatLE(offset + 0); - obj.extendBottom = buf.getFloatLE(offset + 4); - obj.yawLength = buf.getFloatLE(offset + 8); - obj.yawStartOffset = buf.getFloatLE(offset + 12); - obj.pitchOffset = buf.getFloatLE(offset + 16); - obj.rollOffset = buf.getFloatLE(offset + 20); - obj.startDistance = buf.getFloatLE(offset + 24); - obj.endDistance = buf.getFloatLE(offset + 28); - obj.direction = HorizontalSelectorDirection.fromValue(buf.getByte(offset + 32)); - obj.testLineOfSight = buf.getByte(offset + 33) != 0; - return obj; + if (buf.readableBytes() - offset < 34) { + throw ProtocolException.bufferTooSmall("HorizontalSelector", 34, buf.readableBytes() - offset); + } else { + HorizontalSelector obj = new HorizontalSelector(); + obj.extendTop = buf.getFloatLE(offset + 0); + obj.extendBottom = buf.getFloatLE(offset + 4); + obj.yawLength = buf.getFloatLE(offset + 8); + obj.yawStartOffset = buf.getFloatLE(offset + 12); + obj.pitchOffset = buf.getFloatLE(offset + 16); + obj.rollOffset = buf.getFloatLE(offset + 20); + obj.startDistance = buf.getFloatLE(offset + 24); + obj.endDistance = buf.getFloatLE(offset + 28); + obj.direction = HorizontalSelectorDirection.fromValue(buf.getByte(offset + 32)); + obj.testLineOfSight = buf.getByte(offset + 33) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -105,7 +110,12 @@ public class HorizontalSelector extends Selector { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 34 ? ValidationResult.error("Buffer too small: expected at least 34 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 34) { + return ValidationResult.error("Buffer too small: expected at least 34 bytes"); + } else { + int v = buffer.getByte(offset + 32) & 255; + return v >= 2 ? ValidationResult.error("Invalid HorizontalSelectorDirection value for Direction") : ValidationResult.OK; + } } public HorizontalSelector clone() { diff --git a/src/com/hypixel/hytale/protocol/HostAddress.java b/src/com/hypixel/hytale/protocol/HostAddress.java index 8bdfde3f..cd6b3cca 100644 --- a/src/com/hypixel/hytale/protocol/HostAddress.java +++ b/src/com/hypixel/hytale/protocol/HostAddress.java @@ -33,26 +33,34 @@ public class HostAddress { @Nonnull public static HostAddress deserialize(@Nonnull ByteBuf buf, int offset) { - HostAddress obj = new HostAddress(); - obj.port = buf.getShortLE(offset + 0); - int pos = offset + 2; - int hostLen = VarInt.peek(buf, pos); - if (hostLen < 0) { - throw ProtocolException.negativeLength("Host", hostLen); - } else if (hostLen > 256) { - throw ProtocolException.stringTooLong("Host", hostLen, 256); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("HostAddress", 2, buf.readableBytes() - offset); } else { - int hostVarLen = VarInt.length(buf, pos); - obj.host = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += hostVarLen + hostLen; - return obj; + HostAddress obj = new HostAddress(); + obj.port = buf.getShortLE(offset + 0); + int pos = offset + 2; + int hostLen = VarInt.peek(buf, pos); + if (hostLen < 0) { + throw ProtocolException.invalidVarInt("Host"); + } else { + int hostVarLen = VarInt.size(hostLen); + if (hostLen > 256) { + throw ProtocolException.stringTooLong("Host", hostLen, 256); + } else if (pos + hostVarLen + hostLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Host", pos + hostVarLen + hostLen, buf.readableBytes()); + } else { + obj.host = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += hostVarLen + hostLen; + return obj; + } + } } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 2; int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; return pos - offset; } @@ -77,7 +85,7 @@ public class HostAddress { } else if (hostLen > 256) { return ValidationResult.error("Host exceeds max length 256"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(hostLen); pos += hostLen; return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Host") : ValidationResult.OK; } diff --git a/src/com/hypixel/hytale/protocol/IncrementCooldownInteraction.java b/src/com/hypixel/hytale/protocol/IncrementCooldownInteraction.java index 1817a601..ec92d4ad 100644 --- a/src/com/hypixel/hytale/protocol/IncrementCooldownInteraction.java +++ b/src/com/hypixel/hytale/protocol/IncrementCooldownInteraction.java @@ -86,96 +86,135 @@ public class IncrementCooldownInteraction extends SimpleInteraction { @Nonnull public static IncrementCooldownInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - IncrementCooldownInteraction obj = new IncrementCooldownInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.cooldownIncrementTime = buf.getFloatLE(offset + 19); - obj.cooldownIncrementCharge = buf.getIntLE(offset + 23); - obj.cooldownIncrementChargeTime = buf.getFloatLE(offset + 27); - obj.cooldownIncrementInterrupt = buf.getByte(offset + 31) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 56 + buf.getIntLE(offset + 32); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 56) { + throw ProtocolException.bufferTooSmall("IncrementCooldownInteraction", 56, buf.readableBytes() - offset); + } else { + IncrementCooldownInteraction obj = new IncrementCooldownInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.cooldownIncrementTime = buf.getFloatLE(offset + 19); + obj.cooldownIncrementCharge = buf.getIntLE(offset + 23); + obj.cooldownIncrementChargeTime = buf.getFloatLE(offset + 27); + obj.cooldownIncrementInterrupt = buf.getByte(offset + 31) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 32); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 56 + buf.getIntLE(offset + 36); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 56 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 36); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 56 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 40); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 56 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 44); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 56 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 48); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 56 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 52); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("CooldownId", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 56 + varPosBase5; + int cooldownIdLen = VarInt.peek(buf, varPos5); + if (cooldownIdLen < 0) { + throw ProtocolException.invalidVarInt("CooldownId"); + } + + int cooldownIdVarIntLen = VarInt.size(cooldownIdLen); + if (cooldownIdLen > 4096000) { + throw ProtocolException.stringTooLong("CooldownId", cooldownIdLen, 4096000); + } + + if (varPos5 + cooldownIdVarIntLen + cooldownIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CooldownId", varPos5 + cooldownIdVarIntLen + cooldownIdLen, buf.readableBytes()); + } + + obj.cooldownId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 56 + buf.getIntLE(offset + 40); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 56 + buf.getIntLE(offset + 44); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 56 + buf.getIntLE(offset + 48); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 56 + buf.getIntLE(offset + 52); - int cooldownIdLen = VarInt.peek(buf, varPos5); - if (cooldownIdLen < 0) { - throw ProtocolException.negativeLength("CooldownId", cooldownIdLen); - } - - if (cooldownIdLen > 4096000) { - throw ProtocolException.stringTooLong("CooldownId", cooldownIdLen, 4096000); - } - - obj.cooldownId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -183,6 +222,10 @@ public class IncrementCooldownInteraction extends SimpleInteraction { int maxEnd = 56; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 32); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 56 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -192,9 +235,13 @@ public class IncrementCooldownInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 36); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 56 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -207,6 +254,10 @@ public class IncrementCooldownInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 40); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 56 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -216,9 +267,13 @@ public class IncrementCooldownInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 44); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 56 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -226,6 +281,10 @@ public class IncrementCooldownInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 48); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 56 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -235,9 +294,13 @@ public class IncrementCooldownInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 52); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 56) { + throw ProtocolException.invalidOffset("CooldownId", fieldOffset5, maxEnd); + } + int pos5 = offset + 56 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -395,146 +458,132 @@ public class IncrementCooldownInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 56 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 32); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 56) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 56 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 56 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 56) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 56 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 40); + if (v < 0 || v > buffer.writerIndex() - offset - 56) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 56 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 44); + if (v < 0 || v > buffer.writerIndex() - offset - 56) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 56 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 48); + if (v < 0 || v > buffer.writerIndex() - offset - 56) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 56 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 52); + if (v < 0 || v > buffer.writerIndex() - offset - 56) { + return ValidationResult.error("Invalid offset for CooldownId"); + } + + int posxx = offset + 56 + v; + int cooldownIdLen = VarInt.peek(buffer, posxx); + if (cooldownIdLen < 0) { + return ValidationResult.error("Invalid string length for CooldownId"); + } + + if (cooldownIdLen > 4096000) { + return ValidationResult.error("CooldownId exceeds max length 4096000"); + } + + posxx += VarInt.size(cooldownIdLen); + posxx += cooldownIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading CooldownId"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 36); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 56 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 40); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 56 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 44); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 56 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 48); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 56 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int cooldownIdOffset = buffer.getIntLE(offset + 52); - if (cooldownIdOffset < 0) { - return ValidationResult.error("Invalid offset for CooldownId"); - } - - int posxxxxx = offset + 56 + cooldownIdOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for CooldownId"); - } - - int cooldownIdLen = VarInt.peek(buffer, posxxxxx); - if (cooldownIdLen < 0) { - return ValidationResult.error("Invalid string length for CooldownId"); - } - - if (cooldownIdLen > 4096000) { - return ValidationResult.error("CooldownId exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += cooldownIdLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading CooldownId"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/InitialVelocity.java b/src/com/hypixel/hytale/protocol/InitialVelocity.java index c8533683..24439e72 100644 --- a/src/com/hypixel/hytale/protocol/InitialVelocity.java +++ b/src/com/hypixel/hytale/protocol/InitialVelocity.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -36,21 +37,25 @@ public class InitialVelocity { @Nonnull public static InitialVelocity deserialize(@Nonnull ByteBuf buf, int offset) { - InitialVelocity obj = new InitialVelocity(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.yaw = Rangef.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 25) { + throw ProtocolException.bufferTooSmall("InitialVelocity", 25, buf.readableBytes() - offset); + } else { + InitialVelocity obj = new InitialVelocity(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.yaw = Rangef.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.pitch = Rangef.deserialize(buf, offset + 9); - } + if ((nullBits & 2) != 0) { + obj.pitch = Rangef.deserialize(buf, offset + 9); + } - if ((nullBits & 4) != 0) { - obj.speed = Rangef.deserialize(buf, offset + 17); - } + if ((nullBits & 4) != 0) { + obj.speed = Rangef.deserialize(buf, offset + 17); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -96,7 +101,12 @@ public class InitialVelocity { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 25 ? ValidationResult.error("Buffer too small: expected at least 25 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 25) { + return ValidationResult.error("Buffer too small: expected at least 25 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public InitialVelocity clone() { diff --git a/src/com/hypixel/hytale/protocol/InstantData.java b/src/com/hypixel/hytale/protocol/InstantData.java index 73fe7753..92470b67 100644 --- a/src/com/hypixel/hytale/protocol/InstantData.java +++ b/src/com/hypixel/hytale/protocol/InstantData.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class InstantData { @Nonnull public static InstantData deserialize(@Nonnull ByteBuf buf, int offset) { - InstantData obj = new InstantData(); - obj.seconds = buf.getLongLE(offset + 0); - obj.nanos = buf.getIntLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("InstantData", 12, buf.readableBytes() - offset); + } else { + InstantData obj = new InstantData(); + obj.seconds = buf.getLongLE(offset + 0); + obj.nanos = buf.getIntLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/IntParamValue.java b/src/com/hypixel/hytale/protocol/IntParamValue.java index a3630058..117ade99 100644 --- a/src/com/hypixel/hytale/protocol/IntParamValue.java +++ b/src/com/hypixel/hytale/protocol/IntParamValue.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class IntParamValue extends ParamValue { @Nonnull public static IntParamValue deserialize(@Nonnull ByteBuf buf, int offset) { - IntParamValue obj = new IntParamValue(); - obj.value = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("IntParamValue", 4, buf.readableBytes() - offset); + } else { + IntParamValue obj = new IntParamValue(); + obj.value = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/InteractableUpdate.java b/src/com/hypixel/hytale/protocol/InteractableUpdate.java index bc9f101f..15dc4200 100644 --- a/src/com/hypixel/hytale/protocol/InteractableUpdate.java +++ b/src/com/hypixel/hytale/protocol/InteractableUpdate.java @@ -31,25 +31,33 @@ public class InteractableUpdate extends ComponentUpdate { @Nonnull public static InteractableUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - InteractableUpdate obj = new InteractableUpdate(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int interactionHintLen = VarInt.peek(buf, pos); - if (interactionHintLen < 0) { - throw ProtocolException.negativeLength("InteractionHint", interactionHintLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("InteractableUpdate", 1, buf.readableBytes() - offset); + } else { + InteractableUpdate obj = new InteractableUpdate(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int interactionHintLen = VarInt.peek(buf, pos); + if (interactionHintLen < 0) { + throw ProtocolException.invalidVarInt("InteractionHint"); + } + + int interactionHintVarLen = VarInt.size(interactionHintLen); + if (interactionHintLen > 4096000) { + throw ProtocolException.stringTooLong("InteractionHint", interactionHintLen, 4096000); + } + + if (pos + interactionHintVarLen + interactionHintLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("InteractionHint", pos + interactionHintVarLen + interactionHintLen, buf.readableBytes()); + } + + obj.interactionHint = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += interactionHintVarLen + interactionHintLen; } - if (interactionHintLen > 4096000) { - throw ProtocolException.stringTooLong("InteractionHint", interactionHintLen, 4096000); - } - - int interactionHintVarLen = VarInt.length(buf, pos); - obj.interactionHint = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += interactionHintVarLen + interactionHintLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -57,7 +65,7 @@ public class InteractableUpdate extends ComponentUpdate { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -105,7 +113,7 @@ public class InteractableUpdate extends ComponentUpdate { return ValidationResult.error("InteractionHint exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(interactionHintLen); pos += interactionHintLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading InteractionHint"); diff --git a/src/com/hypixel/hytale/protocol/Interaction.java b/src/com/hypixel/hytale/protocol/Interaction.java index 73600eb3..abfe1c17 100644 --- a/src/com/hypixel/hytale/protocol/Interaction.java +++ b/src/com/hypixel/hytale/protocol/Interaction.java @@ -29,7 +29,7 @@ public abstract class Interaction { @Nonnull public static Interaction deserialize(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return (Interaction)(switch (typeId) { case 0 -> SimpleBlockInteraction.deserialize(buf, offset + typeIdLen); @@ -82,7 +82,7 @@ public abstract class Interaction { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return typeIdLen + switch (typeId) { case 0 -> SimpleBlockInteraction.computeBytesConsumed(buf, offset + typeIdLen); @@ -244,7 +244,7 @@ public abstract class Interaction { public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { int typeId = VarInt.peek(buffer, offset); - int typeIdLen = VarInt.length(buffer, offset); + int typeIdLen = VarInt.size(typeId); return switch (typeId) { case 0 -> SimpleBlockInteraction.validateStructure(buffer, offset + typeIdLen); diff --git a/src/com/hypixel/hytale/protocol/InteractionCamera.java b/src/com/hypixel/hytale/protocol/InteractionCamera.java index 1514d4d8..2f521310 100644 --- a/src/com/hypixel/hytale/protocol/InteractionCamera.java +++ b/src/com/hypixel/hytale/protocol/InteractionCamera.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class InteractionCamera { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -13,15 +16,15 @@ public class InteractionCamera { public static final int VARIABLE_BLOCK_START = 29; public static final int MAX_SIZE = 29; public float time; - @Nullable - public Vector3f position; + @Nonnull + public Vector3fc position = PacketIO.ZERO_VECTOR3; @Nullable public Direction rotation; public InteractionCamera() { } - public InteractionCamera(float time, @Nullable Vector3f position, @Nullable Direction rotation) { + public InteractionCamera(float time, @Nonnull Vector3fc position, @Nullable Direction rotation) { this.time = time; this.position = position; this.rotation = rotation; @@ -35,18 +38,19 @@ public class InteractionCamera { @Nonnull public static InteractionCamera deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionCamera obj = new InteractionCamera(); - byte nullBits = buf.getByte(offset); - obj.time = buf.getFloatLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.position = Vector3f.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 29) { + throw ProtocolException.bufferTooSmall("InteractionCamera", 29, buf.readableBytes() - offset); + } else { + InteractionCamera obj = new InteractionCamera(); + byte nullBits = buf.getByte(offset); + obj.time = buf.getFloatLE(offset + 1); + obj.position = PacketIO.readVector3f(buf, offset + 5); + if ((nullBits & 1) != 0) { + obj.rotation = Direction.deserialize(buf, offset + 17); + } - if ((nullBits & 2) != 0) { - obj.rotation = Direction.deserialize(buf, offset + 17); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -55,22 +59,13 @@ public class InteractionCamera { public void serialize(@Nonnull ByteBuf buf) { byte nullBits = 0; - if (this.position != null) { - nullBits = (byte)(nullBits | 1); - } - if (this.rotation != null) { - nullBits = (byte)(nullBits | 2); + nullBits = (byte)(nullBits | 1); } buf.writeByte(nullBits); buf.writeFloatLE(this.time); - if (this.position != null) { - this.position.serialize(buf); - } else { - buf.writeZero(12); - } - + PacketIO.writeVector3f(buf, this.position); if (this.rotation != null) { this.rotation.serialize(buf); } else { @@ -83,13 +78,18 @@ public class InteractionCamera { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 29 ? ValidationResult.error("Buffer too small: expected at least 29 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 29) { + return ValidationResult.error("Buffer too small: expected at least 29 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public InteractionCamera clone() { InteractionCamera copy = new InteractionCamera(); copy.time = this.time; - copy.position = this.position != null ? this.position.clone() : null; + copy.position = this.position; copy.rotation = this.rotation != null ? this.rotation.clone() : null; return copy; } diff --git a/src/com/hypixel/hytale/protocol/InteractionCameraSettings.java b/src/com/hypixel/hytale/protocol/InteractionCameraSettings.java index a5a31614..e72ec4a3 100644 --- a/src/com/hypixel/hytale/protocol/InteractionCameraSettings.java +++ b/src/com/hypixel/hytale/protocol/InteractionCameraSettings.java @@ -34,59 +34,73 @@ public class InteractionCameraSettings { @Nonnull public static InteractionCameraSettings deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionCameraSettings obj = new InteractionCameraSettings(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int firstPersonCount = VarInt.peek(buf, varPos0); - if (firstPersonCount < 0) { - throw ProtocolException.negativeLength("FirstPerson", firstPersonCount); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("InteractionCameraSettings", 9, buf.readableBytes() - offset); + } else { + InteractionCameraSettings obj = new InteractionCameraSettings(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("FirstPerson", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int firstPersonCount = VarInt.peek(buf, varPos0); + if (firstPersonCount < 0) { + throw ProtocolException.invalidVarInt("FirstPerson"); + } + + int varIntLen = VarInt.size(firstPersonCount); + if (firstPersonCount > 4096000) { + throw ProtocolException.arrayTooLong("FirstPerson", firstPersonCount, 4096000); + } + + if (varPos0 + varIntLen + firstPersonCount * 29L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FirstPerson", varPos0 + varIntLen + firstPersonCount * 29, buf.readableBytes()); + } + + obj.firstPerson = new InteractionCamera[firstPersonCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < firstPersonCount; i++) { + obj.firstPerson[i] = InteractionCamera.deserialize(buf, elemPos); + elemPos += InteractionCamera.computeBytesConsumed(buf, elemPos); + } } - if (firstPersonCount > 4096000) { - throw ProtocolException.arrayTooLong("FirstPerson", firstPersonCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ThirdPerson", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int thirdPersonCount = VarInt.peek(buf, varPos1); + if (thirdPersonCount < 0) { + throw ProtocolException.invalidVarInt("ThirdPerson"); + } + + int varIntLenx = VarInt.size(thirdPersonCount); + if (thirdPersonCount > 4096000) { + throw ProtocolException.arrayTooLong("ThirdPerson", thirdPersonCount, 4096000); + } + + if (varPos1 + varIntLenx + thirdPersonCount * 29L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ThirdPerson", varPos1 + varIntLenx + thirdPersonCount * 29, buf.readableBytes()); + } + + obj.thirdPerson = new InteractionCamera[thirdPersonCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < thirdPersonCount; i++) { + obj.thirdPerson[i] = InteractionCamera.deserialize(buf, elemPos); + elemPos += InteractionCamera.computeBytesConsumed(buf, elemPos); + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + firstPersonCount * 29L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FirstPerson", varPos0 + varIntLen + firstPersonCount * 29, buf.readableBytes()); - } - - obj.firstPerson = new InteractionCamera[firstPersonCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < firstPersonCount; i++) { - obj.firstPerson[i] = InteractionCamera.deserialize(buf, elemPos); - elemPos += InteractionCamera.computeBytesConsumed(buf, elemPos); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int thirdPersonCount = VarInt.peek(buf, varPos1); - if (thirdPersonCount < 0) { - throw ProtocolException.negativeLength("ThirdPerson", thirdPersonCount); - } - - if (thirdPersonCount > 4096000) { - throw ProtocolException.arrayTooLong("ThirdPerson", thirdPersonCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + thirdPersonCount * 29L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ThirdPerson", varPos1 + varIntLen + thirdPersonCount * 29, buf.readableBytes()); - } - - obj.thirdPerson = new InteractionCamera[thirdPersonCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < thirdPersonCount; i++) { - obj.thirdPerson[i] = InteractionCamera.deserialize(buf, elemPos); - elemPos += InteractionCamera.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,9 +108,13 @@ public class InteractionCameraSettings { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("FirstPerson", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += InteractionCamera.computeBytesConsumed(buf, pos0); @@ -109,9 +127,13 @@ public class InteractionCameraSettings { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ThirdPerson", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += InteractionCamera.computeBytesConsumed(buf, pos1); @@ -193,15 +215,11 @@ public class InteractionCameraSettings { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int firstPersonOffset = buffer.getIntLE(offset + 1); - if (firstPersonOffset < 0) { + if (firstPersonOffset < 0 || firstPersonOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for FirstPerson"); } int pos = offset + 9 + firstPersonOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstPerson"); - } - int firstPersonCount = VarInt.peek(buffer, pos); if (firstPersonCount < 0) { return ValidationResult.error("Invalid array count for FirstPerson"); @@ -211,7 +229,7 @@ public class InteractionCameraSettings { return ValidationResult.error("FirstPerson exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(firstPersonCount); pos += firstPersonCount * 29; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FirstPerson"); @@ -220,15 +238,11 @@ public class InteractionCameraSettings { if ((nullBits & 2) != 0) { int thirdPersonOffset = buffer.getIntLE(offset + 5); - if (thirdPersonOffset < 0) { + if (thirdPersonOffset < 0 || thirdPersonOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for ThirdPerson"); } int posx = offset + 9 + thirdPersonOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ThirdPerson"); - } - int thirdPersonCount = VarInt.peek(buffer, posx); if (thirdPersonCount < 0) { return ValidationResult.error("Invalid array count for ThirdPerson"); @@ -238,7 +252,7 @@ public class InteractionCameraSettings { return ValidationResult.error("ThirdPerson exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(thirdPersonCount); posx += thirdPersonCount * 29; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ThirdPerson"); diff --git a/src/com/hypixel/hytale/protocol/InteractionChainData.java b/src/com/hypixel/hytale/protocol/InteractionChainData.java index ec52bb4d..79a65ff1 100644 --- a/src/com/hypixel/hytale/protocol/InteractionChainData.java +++ b/src/com/hypixel/hytale/protocol/InteractionChainData.java @@ -9,6 +9,7 @@ import java.util.Objects; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class InteractionChainData { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -20,14 +21,14 @@ public class InteractionChainData { @Nonnull public UUID proxyId = new UUID(0L, 0L); @Nullable - public Vector3f hitLocation; + public Vector3fc hitLocation; @Nullable public String hitDetail; @Nullable public BlockPosition blockPosition; public int targetSlot = Integer.MIN_VALUE; @Nullable - public Vector3f hitNormal; + public Vector3fc hitNormal; public InteractionChainData() { } @@ -35,11 +36,11 @@ public class InteractionChainData { public InteractionChainData( int entityId, @Nonnull UUID proxyId, - @Nullable Vector3f hitLocation, + @Nullable Vector3fc hitLocation, @Nullable String hitDetail, @Nullable BlockPosition blockPosition, int targetSlot, - @Nullable Vector3f hitNormal + @Nullable Vector3fc hitNormal ) { this.entityId = entityId; this.proxyId = proxyId; @@ -62,40 +63,48 @@ public class InteractionChainData { @Nonnull public static InteractionChainData deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionChainData obj = new InteractionChainData(); - byte nullBits = buf.getByte(offset); - obj.entityId = buf.getIntLE(offset + 1); - obj.proxyId = PacketIO.readUUID(buf, offset + 5); - if ((nullBits & 1) != 0) { - obj.hitLocation = Vector3f.deserialize(buf, offset + 21); - } - - if ((nullBits & 2) != 0) { - obj.blockPosition = BlockPosition.deserialize(buf, offset + 33); - } - - obj.targetSlot = buf.getIntLE(offset + 45); - if ((nullBits & 4) != 0) { - obj.hitNormal = Vector3f.deserialize(buf, offset + 49); - } - - int pos = offset + 61; - if ((nullBits & 8) != 0) { - int hitDetailLen = VarInt.peek(buf, pos); - if (hitDetailLen < 0) { - throw ProtocolException.negativeLength("HitDetail", hitDetailLen); + if (buf.readableBytes() - offset < 61) { + throw ProtocolException.bufferTooSmall("InteractionChainData", 61, buf.readableBytes() - offset); + } else { + InteractionChainData obj = new InteractionChainData(); + byte nullBits = buf.getByte(offset); + obj.entityId = buf.getIntLE(offset + 1); + obj.proxyId = PacketIO.readUUID(buf, offset + 5); + if ((nullBits & 1) != 0) { + obj.hitLocation = PacketIO.readVector3f(buf, offset + 21); } - if (hitDetailLen > 4096000) { - throw ProtocolException.stringTooLong("HitDetail", hitDetailLen, 4096000); + if ((nullBits & 2) != 0) { + obj.blockPosition = BlockPosition.deserialize(buf, offset + 33); } - int hitDetailVarLen = VarInt.length(buf, pos); - obj.hitDetail = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += hitDetailVarLen + hitDetailLen; - } + obj.targetSlot = buf.getIntLE(offset + 45); + if ((nullBits & 4) != 0) { + obj.hitNormal = PacketIO.readVector3f(buf, offset + 49); + } - return obj; + int pos = offset + 61; + if ((nullBits & 8) != 0) { + int hitDetailLen = VarInt.peek(buf, pos); + if (hitDetailLen < 0) { + throw ProtocolException.invalidVarInt("HitDetail"); + } + + int hitDetailVarLen = VarInt.size(hitDetailLen); + if (hitDetailLen > 4096000) { + throw ProtocolException.stringTooLong("HitDetail", hitDetailLen, 4096000); + } + + if (pos + hitDetailVarLen + hitDetailLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("HitDetail", pos + hitDetailVarLen + hitDetailLen, buf.readableBytes()); + } + + obj.hitDetail = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += hitDetailVarLen + hitDetailLen; + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -103,7 +112,7 @@ public class InteractionChainData { int pos = offset + 61; if ((nullBits & 8) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -131,7 +140,7 @@ public class InteractionChainData { buf.writeIntLE(this.entityId); PacketIO.writeUUID(buf, this.proxyId); if (this.hitLocation != null) { - this.hitLocation.serialize(buf); + PacketIO.writeVector3f(buf, this.hitLocation); } else { buf.writeZero(12); } @@ -144,7 +153,7 @@ public class InteractionChainData { buf.writeIntLE(this.targetSlot); if (this.hitNormal != null) { - this.hitNormal.serialize(buf); + PacketIO.writeVector3f(buf, this.hitNormal); } else { buf.writeZero(12); } @@ -179,7 +188,7 @@ public class InteractionChainData { return ValidationResult.error("HitDetail exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(hitDetailLen); pos += hitDetailLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading HitDetail"); @@ -194,11 +203,11 @@ public class InteractionChainData { InteractionChainData copy = new InteractionChainData(); copy.entityId = this.entityId; copy.proxyId = this.proxyId; - copy.hitLocation = this.hitLocation != null ? this.hitLocation.clone() : null; + copy.hitLocation = this.hitLocation; copy.hitDetail = this.hitDetail; copy.blockPosition = this.blockPosition != null ? this.blockPosition.clone() : null; copy.targetSlot = this.targetSlot; - copy.hitNormal = this.hitNormal != null ? this.hitNormal.clone() : null; + copy.hitNormal = this.hitNormal; return copy; } diff --git a/src/com/hypixel/hytale/protocol/InteractionConfiguration.java b/src/com/hypixel/hytale/protocol/InteractionConfiguration.java index a914dae3..9d3595e0 100644 --- a/src/com/hypixel/hytale/protocol/InteractionConfiguration.java +++ b/src/com/hypixel/hytale/protocol/InteractionConfiguration.java @@ -52,62 +52,76 @@ public class InteractionConfiguration { @Nonnull public static InteractionConfiguration deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionConfiguration obj = new InteractionConfiguration(); - byte nullBits = buf.getByte(offset); - obj.displayOutlines = buf.getByte(offset + 1) != 0; - obj.debugOutlines = buf.getByte(offset + 2) != 0; - obj.allEntities = buf.getByte(offset + 3) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 12 + buf.getIntLE(offset + 4); - int useDistanceCount = VarInt.peek(buf, varPos0); - if (useDistanceCount < 0) { - throw ProtocolException.negativeLength("UseDistance", useDistanceCount); - } + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("InteractionConfiguration", 12, buf.readableBytes() - offset); + } else { + InteractionConfiguration obj = new InteractionConfiguration(); + byte nullBits = buf.getByte(offset); + obj.displayOutlines = buf.getByte(offset + 1) != 0; + obj.debugOutlines = buf.getByte(offset + 2) != 0; + obj.allEntities = buf.getByte(offset + 3) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("UseDistance", varPosBase0, buf.readableBytes()); + } - if (useDistanceCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("UseDistance", useDistanceCount, 4096000); - } + int varPos0 = offset + 12 + varPosBase0; + int useDistanceCount = VarInt.peek(buf, varPos0); + if (useDistanceCount < 0) { + throw ProtocolException.invalidVarInt("UseDistance"); + } - int varIntLen = VarInt.length(buf, varPos0); - obj.useDistance = new HashMap<>(useDistanceCount); - int dictPos = varPos0 + varIntLen; + int varIntLen = VarInt.size(useDistanceCount); + if (useDistanceCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("UseDistance", useDistanceCount, 4096000); + } - for (int i = 0; i < useDistanceCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - float val = buf.getFloatLE(++dictPos); - dictPos += 4; - if (obj.useDistance.put(key, val) != null) { - throw ProtocolException.duplicateKey("useDistance", key); + obj.useDistance = new HashMap<>(useDistanceCount); + int dictPos = varPos0 + varIntLen; + + for (int i = 0; i < useDistanceCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + float val = buf.getFloatLE(++dictPos); + dictPos += 4; + if (obj.useDistance.put(key, val) != null) { + throw ProtocolException.duplicateKey("useDistance", key); + } } } - } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 12 + buf.getIntLE(offset + 8); - int prioritiesCount = VarInt.peek(buf, varPos1); - if (prioritiesCount < 0) { - throw ProtocolException.negativeLength("Priorities", prioritiesCount); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 8); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("Priorities", varPosBase1, buf.readableBytes()); + } - if (prioritiesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Priorities", prioritiesCount, 4096000); - } + int varPos1 = offset + 12 + varPosBase1; + int prioritiesCount = VarInt.peek(buf, varPos1); + if (prioritiesCount < 0) { + throw ProtocolException.invalidVarInt("Priorities"); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.priorities = new HashMap<>(prioritiesCount); - int dictPos = varPos1 + varIntLen; + int varIntLen = VarInt.size(prioritiesCount); + if (prioritiesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Priorities", prioritiesCount, 4096000); + } - for (int ix = 0; ix < prioritiesCount; ix++) { - InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); - InteractionPriority val = InteractionPriority.deserialize(buf, ++dictPos); - dictPos += InteractionPriority.computeBytesConsumed(buf, dictPos); - if (obj.priorities.put(key, val) != null) { - throw ProtocolException.duplicateKey("priorities", key); + obj.priorities = new HashMap<>(prioritiesCount); + int dictPos = varPos1 + varIntLen; + + for (int ix = 0; ix < prioritiesCount; ix++) { + InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); + InteractionPriority val = InteractionPriority.deserialize(buf, ++dictPos); + dictPos += InteractionPriority.computeBytesConsumed(buf, dictPos); + if (obj.priorities.put(key, val) != null) { + throw ProtocolException.duplicateKey("priorities", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -115,9 +129,13 @@ public class InteractionConfiguration { int maxEnd = 12; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("UseDistance", fieldOffset0, maxEnd); + } + int pos0 = offset + 12 + fieldOffset0; int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos0 = ++pos0 + 4; @@ -130,9 +148,13 @@ public class InteractionConfiguration { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 8); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("Priorities", fieldOffset1, maxEnd); + } + int pos1 = offset + 12 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionPriority.computeBytesConsumed(buf, pos1); @@ -225,15 +247,11 @@ public class InteractionConfiguration { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int useDistanceOffset = buffer.getIntLE(offset + 4); - if (useDistanceOffset < 0) { + if (useDistanceOffset < 0 || useDistanceOffset > buffer.writerIndex() - offset - 12) { return ValidationResult.error("Invalid offset for UseDistance"); } int pos = offset + 12 + useDistanceOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for UseDistance"); - } - int useDistanceCount = VarInt.peek(buffer, pos); if (useDistanceCount < 0) { return ValidationResult.error("Invalid dictionary count for UseDistance"); @@ -243,9 +261,14 @@ public class InteractionConfiguration { return ValidationResult.error("UseDistance exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(useDistanceCount); for (int i = 0; i < useDistanceCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + pos = ++pos + 4; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); @@ -255,15 +278,11 @@ public class InteractionConfiguration { if ((nullBits & 2) != 0) { int prioritiesOffset = buffer.getIntLE(offset + 8); - if (prioritiesOffset < 0) { + if (prioritiesOffset < 0 || prioritiesOffset > buffer.writerIndex() - offset - 12) { return ValidationResult.error("Invalid offset for Priorities"); } int posx = offset + 12 + prioritiesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Priorities"); - } - int prioritiesCount = VarInt.peek(buffer, posx); if (prioritiesCount < 0) { return ValidationResult.error("Invalid dictionary count for Priorities"); @@ -273,9 +292,14 @@ public class InteractionConfiguration { return ValidationResult.error("Priorities exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(prioritiesCount); + + for (int i = 0; i < prioritiesCount; i++) { + int vx = buffer.getByte(posx) & 255; + if (vx >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); + } - for (int ix = 0; ix < prioritiesCount; ix++) { posx = ++posx + InteractionPriority.computeBytesConsumed(buffer, posx); } } diff --git a/src/com/hypixel/hytale/protocol/InteractionCooldown.java b/src/com/hypixel/hytale/protocol/InteractionCooldown.java index d7be03f3..9c45b266 100644 --- a/src/com/hypixel/hytale/protocol/InteractionCooldown.java +++ b/src/com/hypixel/hytale/protocol/InteractionCooldown.java @@ -50,50 +50,69 @@ public class InteractionCooldown { @Nonnull public static InteractionCooldown deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionCooldown obj = new InteractionCooldown(); - byte nullBits = buf.getByte(offset); - obj.cooldown = buf.getFloatLE(offset + 1); - obj.clickBypass = buf.getByte(offset + 5) != 0; - obj.skipCooldownReset = buf.getByte(offset + 6) != 0; - obj.interruptRecharge = buf.getByte(offset + 7) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 16 + buf.getIntLE(offset + 8); - int cooldownIdLen = VarInt.peek(buf, varPos0); - if (cooldownIdLen < 0) { - throw ProtocolException.negativeLength("CooldownId", cooldownIdLen); + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("InteractionCooldown", 16, buf.readableBytes() - offset); + } else { + InteractionCooldown obj = new InteractionCooldown(); + byte nullBits = buf.getByte(offset); + obj.cooldown = buf.getFloatLE(offset + 1); + obj.clickBypass = buf.getByte(offset + 5) != 0; + obj.skipCooldownReset = buf.getByte(offset + 6) != 0; + obj.interruptRecharge = buf.getByte(offset + 7) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 8); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("CooldownId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 16 + varPosBase0; + int cooldownIdLen = VarInt.peek(buf, varPos0); + if (cooldownIdLen < 0) { + throw ProtocolException.invalidVarInt("CooldownId"); + } + + int cooldownIdVarIntLen = VarInt.size(cooldownIdLen); + if (cooldownIdLen > 4096000) { + throw ProtocolException.stringTooLong("CooldownId", cooldownIdLen, 4096000); + } + + if (varPos0 + cooldownIdVarIntLen + cooldownIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CooldownId", varPos0 + cooldownIdVarIntLen + cooldownIdLen, buf.readableBytes()); + } + + obj.cooldownId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (cooldownIdLen > 4096000) { - throw ProtocolException.stringTooLong("CooldownId", cooldownIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 12); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("ChargeTimes", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 16 + varPosBase1; + int chargeTimesCount = VarInt.peek(buf, varPos1); + if (chargeTimesCount < 0) { + throw ProtocolException.invalidVarInt("ChargeTimes"); + } + + int varIntLen = VarInt.size(chargeTimesCount); + if (chargeTimesCount > 4096000) { + throw ProtocolException.arrayTooLong("ChargeTimes", chargeTimesCount, 4096000); + } + + if (varPos1 + varIntLen + chargeTimesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ChargeTimes", varPos1 + varIntLen + chargeTimesCount * 4, buf.readableBytes()); + } + + obj.chargeTimes = new float[chargeTimesCount]; + + for (int i = 0; i < chargeTimesCount; i++) { + obj.chargeTimes[i] = buf.getFloatLE(varPos1 + varIntLen + i * 4); + } } - obj.cooldownId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 16 + buf.getIntLE(offset + 12); - int chargeTimesCount = VarInt.peek(buf, varPos1); - if (chargeTimesCount < 0) { - throw ProtocolException.negativeLength("ChargeTimes", chargeTimesCount); - } - - if (chargeTimesCount > 4096000) { - throw ProtocolException.arrayTooLong("ChargeTimes", chargeTimesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + chargeTimesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ChargeTimes", varPos1 + varIntLen + chargeTimesCount * 4, buf.readableBytes()); - } - - obj.chargeTimes = new float[chargeTimesCount]; - - for (int i = 0; i < chargeTimesCount; i++) { - obj.chargeTimes[i] = buf.getFloatLE(varPos1 + varIntLen + i * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,9 +120,13 @@ public class InteractionCooldown { int maxEnd = 16; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 8); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("CooldownId", fieldOffset0, maxEnd); + } + int pos0 = offset + 16 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -111,9 +134,13 @@ public class InteractionCooldown { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 12); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("ChargeTimes", fieldOffset1, maxEnd); + } + int pos1 = offset + 16 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 4; + pos1 += VarInt.size(arrLen) + arrLen * 4; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -186,15 +213,11 @@ public class InteractionCooldown { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int cooldownIdOffset = buffer.getIntLE(offset + 8); - if (cooldownIdOffset < 0) { + if (cooldownIdOffset < 0 || cooldownIdOffset > buffer.writerIndex() - offset - 16) { return ValidationResult.error("Invalid offset for CooldownId"); } int pos = offset + 16 + cooldownIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for CooldownId"); - } - int cooldownIdLen = VarInt.peek(buffer, pos); if (cooldownIdLen < 0) { return ValidationResult.error("Invalid string length for CooldownId"); @@ -204,7 +227,7 @@ public class InteractionCooldown { return ValidationResult.error("CooldownId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(cooldownIdLen); pos += cooldownIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading CooldownId"); @@ -213,15 +236,11 @@ public class InteractionCooldown { if ((nullBits & 2) != 0) { int chargeTimesOffset = buffer.getIntLE(offset + 12); - if (chargeTimesOffset < 0) { + if (chargeTimesOffset < 0 || chargeTimesOffset > buffer.writerIndex() - offset - 16) { return ValidationResult.error("Invalid offset for ChargeTimes"); } int posx = offset + 16 + chargeTimesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ChargeTimes"); - } - int chargeTimesCount = VarInt.peek(buffer, posx); if (chargeTimesCount < 0) { return ValidationResult.error("Invalid array count for ChargeTimes"); @@ -231,7 +250,7 @@ public class InteractionCooldown { return ValidationResult.error("ChargeTimes exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(chargeTimesCount); posx += chargeTimesCount * 4; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ChargeTimes"); diff --git a/src/com/hypixel/hytale/protocol/InteractionEffects.java b/src/com/hypixel/hytale/protocol/InteractionEffects.java index 08b4f5f5..9bf9aa9f 100644 --- a/src/com/hypixel/hytale/protocol/InteractionEffects.java +++ b/src/com/hypixel/hytale/protocol/InteractionEffects.java @@ -89,126 +89,167 @@ public class InteractionEffects { @Nonnull public static InteractionEffects deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionEffects obj = new InteractionEffects(); - byte nullBits = buf.getByte(offset); - obj.worldSoundEventIndex = buf.getIntLE(offset + 1); - obj.localSoundEventIndex = buf.getIntLE(offset + 5); - obj.waitForAnimationToFinish = buf.getByte(offset + 9) != 0; - obj.clearAnimationOnFinish = buf.getByte(offset + 10) != 0; - obj.clearSoundEventOnFinish = buf.getByte(offset + 11) != 0; - if ((nullBits & 1) != 0) { - obj.cameraShake = CameraShakeEffect.deserialize(buf, offset + 12); + if (buf.readableBytes() - offset < 52) { + throw ProtocolException.bufferTooSmall("InteractionEffects", 52, buf.readableBytes() - offset); + } else { + InteractionEffects obj = new InteractionEffects(); + byte nullBits = buf.getByte(offset); + obj.worldSoundEventIndex = buf.getIntLE(offset + 1); + obj.localSoundEventIndex = buf.getIntLE(offset + 5); + obj.waitForAnimationToFinish = buf.getByte(offset + 9) != 0; + obj.clearAnimationOnFinish = buf.getByte(offset + 10) != 0; + obj.clearSoundEventOnFinish = buf.getByte(offset + 11) != 0; + if ((nullBits & 1) != 0) { + obj.cameraShake = CameraShakeEffect.deserialize(buf, offset + 12); + } + + if ((nullBits & 2) != 0) { + obj.movementEffects = MovementEffects.deserialize(buf, offset + 21); + } + + obj.startDelay = buf.getFloatLE(offset + 28); + if ((nullBits & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 32); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("Particles", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 52 + varPosBase0; + int particlesCount = VarInt.peek(buf, varPos0); + if (particlesCount < 0) { + throw ProtocolException.invalidVarInt("Particles"); + } + + int varIntLen = VarInt.size(particlesCount); + if (particlesCount > 4096000) { + throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); + } + + if (varPos0 + varIntLen + particlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Particles", varPos0 + varIntLen + particlesCount * 34, buf.readableBytes()); + } + + obj.particles = new ModelParticle[particlesCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < particlesCount; i++) { + obj.particles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 36); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("FirstPersonParticles", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 52 + varPosBase1; + int firstPersonParticlesCount = VarInt.peek(buf, varPos1); + if (firstPersonParticlesCount < 0) { + throw ProtocolException.invalidVarInt("FirstPersonParticles"); + } + + int varIntLenx = VarInt.size(firstPersonParticlesCount); + if (firstPersonParticlesCount > 4096000) { + throw ProtocolException.arrayTooLong("FirstPersonParticles", firstPersonParticlesCount, 4096000); + } + + if (varPos1 + varIntLenx + firstPersonParticlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FirstPersonParticles", varPos1 + varIntLenx + firstPersonParticlesCount * 34, buf.readableBytes()); + } + + obj.firstPersonParticles = new ModelParticle[firstPersonParticlesCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < firstPersonParticlesCount; i++) { + obj.firstPersonParticles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase2 = buf.getIntLE(offset + 40); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("Trails", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 52 + varPosBase2; + int trailsCount = VarInt.peek(buf, varPos2); + if (trailsCount < 0) { + throw ProtocolException.invalidVarInt("Trails"); + } + + int varIntLenxx = VarInt.size(trailsCount); + if (trailsCount > 4096000) { + throw ProtocolException.arrayTooLong("Trails", trailsCount, 4096000); + } + + if (varPos2 + varIntLenxx + trailsCount * 27L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Trails", varPos2 + varIntLenxx + trailsCount * 27, buf.readableBytes()); + } + + obj.trails = new ModelTrail[trailsCount]; + int elemPos = varPos2 + varIntLenxx; + + for (int i = 0; i < trailsCount; i++) { + obj.trails[i] = ModelTrail.deserialize(buf, elemPos); + elemPos += ModelTrail.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 32) != 0) { + int varPosBase3 = buf.getIntLE(offset + 44); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("ItemPlayerAnimationsId", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 52 + varPosBase3; + int itemPlayerAnimationsIdLen = VarInt.peek(buf, varPos3); + if (itemPlayerAnimationsIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemPlayerAnimationsId"); + } + + int itemPlayerAnimationsIdVarIntLen = VarInt.size(itemPlayerAnimationsIdLen); + if (itemPlayerAnimationsIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemPlayerAnimationsId", itemPlayerAnimationsIdLen, 4096000); + } + + if (varPos3 + itemPlayerAnimationsIdVarIntLen + itemPlayerAnimationsIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "ItemPlayerAnimationsId", varPos3 + itemPlayerAnimationsIdVarIntLen + itemPlayerAnimationsIdLen, buf.readableBytes() + ); + } + + obj.itemPlayerAnimationsId = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits & 64) != 0) { + int varPosBase4 = buf.getIntLE(offset + 48); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("ItemAnimationId", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 52 + varPosBase4; + int itemAnimationIdLen = VarInt.peek(buf, varPos4); + if (itemAnimationIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemAnimationId"); + } + + int itemAnimationIdVarIntLen = VarInt.size(itemAnimationIdLen); + if (itemAnimationIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemAnimationId", itemAnimationIdLen, 4096000); + } + + if (varPos4 + itemAnimationIdVarIntLen + itemAnimationIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemAnimationId", varPos4 + itemAnimationIdVarIntLen + itemAnimationIdLen, buf.readableBytes()); + } + + obj.itemAnimationId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - obj.movementEffects = MovementEffects.deserialize(buf, offset + 21); - } - - obj.startDelay = buf.getFloatLE(offset + 28); - if ((nullBits & 4) != 0) { - int varPos0 = offset + 52 + buf.getIntLE(offset + 32); - int particlesCount = VarInt.peek(buf, varPos0); - if (particlesCount < 0) { - throw ProtocolException.negativeLength("Particles", particlesCount); - } - - if (particlesCount > 4096000) { - throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + particlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Particles", varPos0 + varIntLen + particlesCount * 34, buf.readableBytes()); - } - - obj.particles = new ModelParticle[particlesCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < particlesCount; i++) { - obj.particles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 8) != 0) { - int varPos1 = offset + 52 + buf.getIntLE(offset + 36); - int firstPersonParticlesCount = VarInt.peek(buf, varPos1); - if (firstPersonParticlesCount < 0) { - throw ProtocolException.negativeLength("FirstPersonParticles", firstPersonParticlesCount); - } - - if (firstPersonParticlesCount > 4096000) { - throw ProtocolException.arrayTooLong("FirstPersonParticles", firstPersonParticlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + firstPersonParticlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FirstPersonParticles", varPos1 + varIntLen + firstPersonParticlesCount * 34, buf.readableBytes()); - } - - obj.firstPersonParticles = new ModelParticle[firstPersonParticlesCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < firstPersonParticlesCount; i++) { - obj.firstPersonParticles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 16) != 0) { - int varPos2 = offset + 52 + buf.getIntLE(offset + 40); - int trailsCount = VarInt.peek(buf, varPos2); - if (trailsCount < 0) { - throw ProtocolException.negativeLength("Trails", trailsCount); - } - - if (trailsCount > 4096000) { - throw ProtocolException.arrayTooLong("Trails", trailsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + trailsCount * 27L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Trails", varPos2 + varIntLen + trailsCount * 27, buf.readableBytes()); - } - - obj.trails = new ModelTrail[trailsCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < trailsCount; i++) { - obj.trails[i] = ModelTrail.deserialize(buf, elemPos); - elemPos += ModelTrail.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 32) != 0) { - int varPos3 = offset + 52 + buf.getIntLE(offset + 44); - int itemPlayerAnimationsIdLen = VarInt.peek(buf, varPos3); - if (itemPlayerAnimationsIdLen < 0) { - throw ProtocolException.negativeLength("ItemPlayerAnimationsId", itemPlayerAnimationsIdLen); - } - - if (itemPlayerAnimationsIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemPlayerAnimationsId", itemPlayerAnimationsIdLen, 4096000); - } - - obj.itemPlayerAnimationsId = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits & 64) != 0) { - int varPos4 = offset + 52 + buf.getIntLE(offset + 48); - int itemAnimationIdLen = VarInt.peek(buf, varPos4); - if (itemAnimationIdLen < 0) { - throw ProtocolException.negativeLength("ItemAnimationId", itemAnimationIdLen); - } - - if (itemAnimationIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemAnimationId", itemAnimationIdLen, 4096000); - } - - obj.itemAnimationId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -216,9 +257,13 @@ public class InteractionEffects { int maxEnd = 52; if ((nullBits & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 32); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("Particles", fieldOffset0, maxEnd); + } + int pos0 = offset + 52 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += ModelParticle.computeBytesConsumed(buf, pos0); @@ -231,9 +276,13 @@ public class InteractionEffects { if ((nullBits & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 36); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("FirstPersonParticles", fieldOffset1, maxEnd); + } + int pos1 = offset + 52 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += ModelParticle.computeBytesConsumed(buf, pos1); @@ -246,9 +295,13 @@ public class InteractionEffects { if ((nullBits & 16) != 0) { int fieldOffset2 = buf.getIntLE(offset + 40); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("Trails", fieldOffset2, maxEnd); + } + int pos2 = offset + 52 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += ModelTrail.computeBytesConsumed(buf, pos2); @@ -261,9 +314,13 @@ public class InteractionEffects { if ((nullBits & 32) != 0) { int fieldOffset3 = buf.getIntLE(offset + 44); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("ItemPlayerAnimationsId", fieldOffset3, maxEnd); + } + int pos3 = offset + 52 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -271,9 +328,13 @@ public class InteractionEffects { if ((nullBits & 64) != 0) { int fieldOffset4 = buf.getIntLE(offset + 48); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("ItemAnimationId", fieldOffset4, maxEnd); + } + int pos4 = offset + 52 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -453,15 +514,11 @@ public class InteractionEffects { byte nullBits = buffer.getByte(offset); if ((nullBits & 4) != 0) { int particlesOffset = buffer.getIntLE(offset + 32); - if (particlesOffset < 0) { + if (particlesOffset < 0 || particlesOffset > buffer.writerIndex() - offset - 52) { return ValidationResult.error("Invalid offset for Particles"); } int pos = offset + 52 + particlesOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particles"); - } - int particlesCount = VarInt.peek(buffer, pos); if (particlesCount < 0) { return ValidationResult.error("Invalid array count for Particles"); @@ -471,7 +528,7 @@ public class InteractionEffects { return ValidationResult.error("Particles exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(particlesCount); for (int i = 0; i < particlesCount; i++) { ValidationResult structResult = ModelParticle.validateStructure(buffer, pos); @@ -485,15 +542,11 @@ public class InteractionEffects { if ((nullBits & 8) != 0) { int firstPersonParticlesOffset = buffer.getIntLE(offset + 36); - if (firstPersonParticlesOffset < 0) { + if (firstPersonParticlesOffset < 0 || firstPersonParticlesOffset > buffer.writerIndex() - offset - 52) { return ValidationResult.error("Invalid offset for FirstPersonParticles"); } int posx = offset + 52 + firstPersonParticlesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstPersonParticles"); - } - int firstPersonParticlesCount = VarInt.peek(buffer, posx); if (firstPersonParticlesCount < 0) { return ValidationResult.error("Invalid array count for FirstPersonParticles"); @@ -503,7 +556,7 @@ public class InteractionEffects { return ValidationResult.error("FirstPersonParticles exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(firstPersonParticlesCount); for (int i = 0; i < firstPersonParticlesCount; i++) { ValidationResult structResult = ModelParticle.validateStructure(buffer, posx); @@ -517,15 +570,11 @@ public class InteractionEffects { if ((nullBits & 16) != 0) { int trailsOffset = buffer.getIntLE(offset + 40); - if (trailsOffset < 0) { + if (trailsOffset < 0 || trailsOffset > buffer.writerIndex() - offset - 52) { return ValidationResult.error("Invalid offset for Trails"); } int posxx = offset + 52 + trailsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Trails"); - } - int trailsCount = VarInt.peek(buffer, posxx); if (trailsCount < 0) { return ValidationResult.error("Invalid array count for Trails"); @@ -535,7 +584,7 @@ public class InteractionEffects { return ValidationResult.error("Trails exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(trailsCount); for (int i = 0; i < trailsCount; i++) { ValidationResult structResult = ModelTrail.validateStructure(buffer, posxx); @@ -549,15 +598,11 @@ public class InteractionEffects { if ((nullBits & 32) != 0) { int itemPlayerAnimationsIdOffset = buffer.getIntLE(offset + 44); - if (itemPlayerAnimationsIdOffset < 0) { + if (itemPlayerAnimationsIdOffset < 0 || itemPlayerAnimationsIdOffset > buffer.writerIndex() - offset - 52) { return ValidationResult.error("Invalid offset for ItemPlayerAnimationsId"); } int posxxx = offset + 52 + itemPlayerAnimationsIdOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemPlayerAnimationsId"); - } - int itemPlayerAnimationsIdLen = VarInt.peek(buffer, posxxx); if (itemPlayerAnimationsIdLen < 0) { return ValidationResult.error("Invalid string length for ItemPlayerAnimationsId"); @@ -567,7 +612,7 @@ public class InteractionEffects { return ValidationResult.error("ItemPlayerAnimationsId exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(itemPlayerAnimationsIdLen); posxxx += itemPlayerAnimationsIdLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemPlayerAnimationsId"); @@ -576,15 +621,11 @@ public class InteractionEffects { if ((nullBits & 64) != 0) { int itemAnimationIdOffset = buffer.getIntLE(offset + 48); - if (itemAnimationIdOffset < 0) { + if (itemAnimationIdOffset < 0 || itemAnimationIdOffset > buffer.writerIndex() - offset - 52) { return ValidationResult.error("Invalid offset for ItemAnimationId"); } int posxxxx = offset + 52 + itemAnimationIdOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemAnimationId"); - } - int itemAnimationIdLen = VarInt.peek(buffer, posxxxx); if (itemAnimationIdLen < 0) { return ValidationResult.error("Invalid string length for ItemAnimationId"); @@ -594,7 +635,7 @@ public class InteractionEffects { return ValidationResult.error("ItemAnimationId exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(itemAnimationIdLen); posxxxx += itemAnimationIdLen; if (posxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemAnimationId"); diff --git a/src/com/hypixel/hytale/protocol/InteractionPriority.java b/src/com/hypixel/hytale/protocol/InteractionPriority.java index 717e1396..1529a024 100644 --- a/src/com/hypixel/hytale/protocol/InteractionPriority.java +++ b/src/com/hypixel/hytale/protocol/InteractionPriority.java @@ -33,33 +33,38 @@ public class InteractionPriority { @Nonnull public static InteractionPriority deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionPriority obj = new InteractionPriority(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int valuesCount = VarInt.peek(buf, pos); - if (valuesCount < 0) { - throw ProtocolException.negativeLength("Values", valuesCount); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("InteractionPriority", 1, buf.readableBytes() - offset); + } else { + InteractionPriority obj = new InteractionPriority(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int valuesCount = VarInt.peek(buf, pos); + if (valuesCount < 0) { + throw ProtocolException.invalidVarInt("Values"); + } - if (valuesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Values", valuesCount, 4096000); - } + int valuesVarLen = VarInt.size(valuesCount); + if (valuesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Values", valuesCount, 4096000); + } - pos += VarInt.size(valuesCount); - obj.values = new HashMap<>(valuesCount); + pos += valuesVarLen; + obj.values = new HashMap<>(valuesCount); - for (int i = 0; i < valuesCount; i++) { - PrioritySlot key = PrioritySlot.fromValue(buf.getByte(pos)); - int val = buf.getIntLE(++pos); - pos += 4; - if (obj.values.put(key, val) != null) { - throw ProtocolException.duplicateKey("values", key); + for (int i = 0; i < valuesCount; i++) { + PrioritySlot key = PrioritySlot.fromValue(buf.getByte(pos)); + int val = buf.getIntLE(++pos); + pos += 4; + if (obj.values.put(key, val) != null) { + throw ProtocolException.duplicateKey("values", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -67,7 +72,7 @@ public class InteractionPriority { int pos = offset + 1; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos = ++pos + 4; @@ -123,9 +128,14 @@ public class InteractionPriority { return ValidationResult.error("Values exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(valuesCount); for (int i = 0; i < valuesCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid PrioritySlot value for key"); + } + pos = ++pos + 4; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); diff --git a/src/com/hypixel/hytale/protocol/InteractionRules.java b/src/com/hypixel/hytale/protocol/InteractionRules.java index d83c5bf9..49237b7f 100644 --- a/src/com/hypixel/hytale/protocol/InteractionRules.java +++ b/src/com/hypixel/hytale/protocol/InteractionRules.java @@ -63,113 +63,137 @@ public class InteractionRules { @Nonnull public static InteractionRules deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionRules obj = new InteractionRules(); - byte nullBits = buf.getByte(offset); - obj.blockedByBypassIndex = buf.getIntLE(offset + 1); - obj.blockingBypassIndex = buf.getIntLE(offset + 5); - obj.interruptedByBypassIndex = buf.getIntLE(offset + 9); - obj.interruptingBypassIndex = buf.getIntLE(offset + 13); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 33 + buf.getIntLE(offset + 17); - int blockedByCount = VarInt.peek(buf, varPos0); - if (blockedByCount < 0) { - throw ProtocolException.negativeLength("BlockedBy", blockedByCount); + if (buf.readableBytes() - offset < 33) { + throw ProtocolException.bufferTooSmall("InteractionRules", 33, buf.readableBytes() - offset); + } else { + InteractionRules obj = new InteractionRules(); + byte nullBits = buf.getByte(offset); + obj.blockedByBypassIndex = buf.getIntLE(offset + 1); + obj.blockingBypassIndex = buf.getIntLE(offset + 5); + obj.interruptedByBypassIndex = buf.getIntLE(offset + 9); + obj.interruptingBypassIndex = buf.getIntLE(offset + 13); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 17); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("BlockedBy", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 33 + varPosBase0; + int blockedByCount = VarInt.peek(buf, varPos0); + if (blockedByCount < 0) { + throw ProtocolException.invalidVarInt("BlockedBy"); + } + + int varIntLen = VarInt.size(blockedByCount); + if (blockedByCount > 4096000) { + throw ProtocolException.arrayTooLong("BlockedBy", blockedByCount, 4096000); + } + + if (varPos0 + varIntLen + blockedByCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BlockedBy", varPos0 + varIntLen + blockedByCount * 1, buf.readableBytes()); + } + + obj.blockedBy = new InteractionType[blockedByCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < blockedByCount; i++) { + obj.blockedBy[i] = InteractionType.fromValue(buf.getByte(elemPos)); + elemPos++; + } } - if (blockedByCount > 4096000) { - throw ProtocolException.arrayTooLong("BlockedBy", blockedByCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 21); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("Blocking", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 33 + varPosBase1; + int blockingCount = VarInt.peek(buf, varPos1); + if (blockingCount < 0) { + throw ProtocolException.invalidVarInt("Blocking"); + } + + int varIntLenx = VarInt.size(blockingCount); + if (blockingCount > 4096000) { + throw ProtocolException.arrayTooLong("Blocking", blockingCount, 4096000); + } + + if (varPos1 + varIntLenx + blockingCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Blocking", varPos1 + varIntLenx + blockingCount * 1, buf.readableBytes()); + } + + obj.blocking = new InteractionType[blockingCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < blockingCount; i++) { + obj.blocking[i] = InteractionType.fromValue(buf.getByte(elemPos)); + elemPos++; + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + blockedByCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("BlockedBy", varPos0 + varIntLen + blockedByCount * 1, buf.readableBytes()); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 25); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("InterruptedBy", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 33 + varPosBase2; + int interruptedByCount = VarInt.peek(buf, varPos2); + if (interruptedByCount < 0) { + throw ProtocolException.invalidVarInt("InterruptedBy"); + } + + int varIntLenxx = VarInt.size(interruptedByCount); + if (interruptedByCount > 4096000) { + throw ProtocolException.arrayTooLong("InterruptedBy", interruptedByCount, 4096000); + } + + if (varPos2 + varIntLenxx + interruptedByCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("InterruptedBy", varPos2 + varIntLenxx + interruptedByCount * 1, buf.readableBytes()); + } + + obj.interruptedBy = new InteractionType[interruptedByCount]; + int elemPos = varPos2 + varIntLenxx; + + for (int i = 0; i < interruptedByCount; i++) { + obj.interruptedBy[i] = InteractionType.fromValue(buf.getByte(elemPos)); + elemPos++; + } } - obj.blockedBy = new InteractionType[blockedByCount]; - int elemPos = varPos0 + varIntLen; + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 29); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("Interrupting", varPosBase3, buf.readableBytes()); + } - for (int i = 0; i < blockedByCount; i++) { - obj.blockedBy[i] = InteractionType.fromValue(buf.getByte(elemPos)); - elemPos++; + int varPos3 = offset + 33 + varPosBase3; + int interruptingCount = VarInt.peek(buf, varPos3); + if (interruptingCount < 0) { + throw ProtocolException.invalidVarInt("Interrupting"); + } + + int varIntLenxxx = VarInt.size(interruptingCount); + if (interruptingCount > 4096000) { + throw ProtocolException.arrayTooLong("Interrupting", interruptingCount, 4096000); + } + + if (varPos3 + varIntLenxxx + interruptingCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Interrupting", varPos3 + varIntLenxxx + interruptingCount * 1, buf.readableBytes()); + } + + obj.interrupting = new InteractionType[interruptingCount]; + int elemPos = varPos3 + varIntLenxxx; + + for (int i = 0; i < interruptingCount; i++) { + obj.interrupting[i] = InteractionType.fromValue(buf.getByte(elemPos)); + elemPos++; + } } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 33 + buf.getIntLE(offset + 21); - int blockingCount = VarInt.peek(buf, varPos1); - if (blockingCount < 0) { - throw ProtocolException.negativeLength("Blocking", blockingCount); - } - - if (blockingCount > 4096000) { - throw ProtocolException.arrayTooLong("Blocking", blockingCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + blockingCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Blocking", varPos1 + varIntLen + blockingCount * 1, buf.readableBytes()); - } - - obj.blocking = new InteractionType[blockingCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < blockingCount; i++) { - obj.blocking[i] = InteractionType.fromValue(buf.getByte(elemPos)); - elemPos++; - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 33 + buf.getIntLE(offset + 25); - int interruptedByCount = VarInt.peek(buf, varPos2); - if (interruptedByCount < 0) { - throw ProtocolException.negativeLength("InterruptedBy", interruptedByCount); - } - - if (interruptedByCount > 4096000) { - throw ProtocolException.arrayTooLong("InterruptedBy", interruptedByCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + interruptedByCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("InterruptedBy", varPos2 + varIntLen + interruptedByCount * 1, buf.readableBytes()); - } - - obj.interruptedBy = new InteractionType[interruptedByCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < interruptedByCount; i++) { - obj.interruptedBy[i] = InteractionType.fromValue(buf.getByte(elemPos)); - elemPos++; - } - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 33 + buf.getIntLE(offset + 29); - int interruptingCount = VarInt.peek(buf, varPos3); - if (interruptingCount < 0) { - throw ProtocolException.negativeLength("Interrupting", interruptingCount); - } - - if (interruptingCount > 4096000) { - throw ProtocolException.arrayTooLong("Interrupting", interruptingCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + interruptingCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Interrupting", varPos3 + varIntLen + interruptingCount * 1, buf.readableBytes()); - } - - obj.interrupting = new InteractionType[interruptingCount]; - int elemPos = varPos3 + varIntLen; - - for (int i = 0; i < interruptingCount; i++) { - obj.interrupting[i] = InteractionType.fromValue(buf.getByte(elemPos)); - elemPos++; - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -177,9 +201,13 @@ public class InteractionRules { int maxEnd = 33; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 17); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("BlockedBy", fieldOffset0, maxEnd); + } + int pos0 = offset + 33 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 1; + pos0 += VarInt.size(arrLen) + arrLen * 1; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -187,9 +215,13 @@ public class InteractionRules { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 21); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("Blocking", fieldOffset1, maxEnd); + } + int pos1 = offset + 33 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 1; + pos1 += VarInt.size(arrLen) + arrLen * 1; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -197,9 +229,13 @@ public class InteractionRules { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 25); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("InterruptedBy", fieldOffset2, maxEnd); + } + int pos2 = offset + 33 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + arrLen * 1; + pos2 += VarInt.size(arrLen) + arrLen * 1; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -207,9 +243,13 @@ public class InteractionRules { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 29); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("Interrupting", fieldOffset3, maxEnd); + } + int pos3 = offset + 33 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 1; + pos3 += VarInt.size(arrLen) + arrLen * 1; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -340,15 +380,11 @@ public class InteractionRules { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int blockedByOffset = buffer.getIntLE(offset + 17); - if (blockedByOffset < 0) { + if (blockedByOffset < 0 || blockedByOffset > buffer.writerIndex() - offset - 33) { return ValidationResult.error("Invalid offset for BlockedBy"); } int pos = offset + 33 + blockedByOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockedBy"); - } - int blockedByCount = VarInt.peek(buffer, pos); if (blockedByCount < 0) { return ValidationResult.error("Invalid array count for BlockedBy"); @@ -358,24 +394,28 @@ public class InteractionRules { return ValidationResult.error("BlockedBy exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += blockedByCount * 1; - if (pos > buffer.writerIndex()) { + pos += VarInt.size(blockedByCount); + if (pos + blockedByCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading BlockedBy"); } + + for (int i = 0; i < blockedByCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for BlockedBy[i]"); + } + + pos++; + } } if ((nullBits & 2) != 0) { int blockingOffset = buffer.getIntLE(offset + 21); - if (blockingOffset < 0) { + if (blockingOffset < 0 || blockingOffset > buffer.writerIndex() - offset - 33) { return ValidationResult.error("Invalid offset for Blocking"); } int posx = offset + 33 + blockingOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Blocking"); - } - int blockingCount = VarInt.peek(buffer, posx); if (blockingCount < 0) { return ValidationResult.error("Invalid array count for Blocking"); @@ -385,24 +425,28 @@ public class InteractionRules { return ValidationResult.error("Blocking exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); - posx += blockingCount * 1; - if (posx > buffer.writerIndex()) { + posx += VarInt.size(blockingCount); + if (posx + blockingCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Blocking"); } + + for (int i = 0; i < blockingCount; i++) { + int v = buffer.getByte(posx) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for Blocking[i]"); + } + + posx++; + } } if ((nullBits & 4) != 0) { int interruptedByOffset = buffer.getIntLE(offset + 25); - if (interruptedByOffset < 0) { + if (interruptedByOffset < 0 || interruptedByOffset > buffer.writerIndex() - offset - 33) { return ValidationResult.error("Invalid offset for InterruptedBy"); } int posxx = offset + 33 + interruptedByOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for InterruptedBy"); - } - int interruptedByCount = VarInt.peek(buffer, posxx); if (interruptedByCount < 0) { return ValidationResult.error("Invalid array count for InterruptedBy"); @@ -412,24 +456,28 @@ public class InteractionRules { return ValidationResult.error("InterruptedBy exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += interruptedByCount * 1; - if (posxx > buffer.writerIndex()) { + posxx += VarInt.size(interruptedByCount); + if (posxx + interruptedByCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading InterruptedBy"); } + + for (int i = 0; i < interruptedByCount; i++) { + int v = buffer.getByte(posxx) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for InterruptedBy[i]"); + } + + posxx++; + } } if ((nullBits & 8) != 0) { int interruptingOffset = buffer.getIntLE(offset + 29); - if (interruptingOffset < 0) { + if (interruptingOffset < 0 || interruptingOffset > buffer.writerIndex() - offset - 33) { return ValidationResult.error("Invalid offset for Interrupting"); } int posxxx = offset + 33 + interruptingOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Interrupting"); - } - int interruptingCount = VarInt.peek(buffer, posxxx); if (interruptingCount < 0) { return ValidationResult.error("Invalid array count for Interrupting"); @@ -439,11 +487,19 @@ public class InteractionRules { return ValidationResult.error("Interrupting exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); - posxxx += interruptingCount * 1; - if (posxxx > buffer.writerIndex()) { + posxxx += VarInt.size(interruptingCount); + if (posxxx + interruptingCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Interrupting"); } + + for (int i = 0; i < interruptingCount; i++) { + int v = buffer.getByte(posxxx) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for Interrupting[i]"); + } + + posxxx++; + } } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/InteractionSettings.java b/src/com/hypixel/hytale/protocol/InteractionSettings.java index fb2a8a98..ccee001f 100644 --- a/src/com/hypixel/hytale/protocol/InteractionSettings.java +++ b/src/com/hypixel/hytale/protocol/InteractionSettings.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class InteractionSettings { @Nonnull public static InteractionSettings deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionSettings obj = new InteractionSettings(); - obj.allowSkipOnClick = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("InteractionSettings", 1, buf.readableBytes() - offset); + } else { + InteractionSettings obj = new InteractionSettings(); + obj.allowSkipOnClick = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/InteractionSyncData.java b/src/com/hypixel/hytale/protocol/InteractionSyncData.java index 7188545e..6b986479 100644 --- a/src/com/hypixel/hytale/protocol/InteractionSyncData.java +++ b/src/com/hypixel/hytale/protocol/InteractionSyncData.java @@ -13,6 +13,7 @@ import java.util.UUID; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class InteractionSyncData { public static final int NULLABLE_BIT_FIELD_SIZE = 2; @@ -50,7 +51,7 @@ public class InteractionSyncData { public Position raycastHit; public float raycastDistance; @Nullable - public Vector3f raycastNormal; + public Vector3fc raycastNormal; @Nonnull public MovementDirection movementDirection = MovementDirection.None; @Nonnull @@ -83,7 +84,7 @@ public class InteractionSyncData { @Nullable Direction attackerRot, @Nullable Position raycastHit, float raycastDistance, - @Nullable Vector3f raycastNormal, + @Nullable Vector3fc raycastNormal, @Nonnull MovementDirection movementDirection, @Nonnull ApplyForceState applyForceState, int nextLabel, @@ -146,103 +147,117 @@ public class InteractionSyncData { @Nonnull public static InteractionSyncData deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionSyncData obj = new InteractionSyncData(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.state = InteractionState.fromValue(buf.getByte(offset + 2)); - obj.progress = buf.getFloatLE(offset + 3); - obj.operationCounter = buf.getIntLE(offset + 7); - obj.rootInteraction = buf.getIntLE(offset + 11); - obj.totalForks = buf.getIntLE(offset + 15); - obj.entityId = buf.getIntLE(offset + 19); - obj.enteredRootInteraction = buf.getIntLE(offset + 23); - if ((nullBits[0] & 1) != 0) { - obj.blockPosition = BlockPosition.deserialize(buf, offset + 27); - } - - obj.blockFace = BlockFace.fromValue(buf.getByte(offset + 39)); - if ((nullBits[0] & 2) != 0) { - obj.blockRotation = BlockRotation.deserialize(buf, offset + 40); - } - - obj.placedBlockId = buf.getIntLE(offset + 43); - obj.chargeValue = buf.getFloatLE(offset + 47); - obj.chainingIndex = buf.getIntLE(offset + 51); - obj.flagIndex = buf.getIntLE(offset + 55); - if ((nullBits[0] & 4) != 0) { - obj.attackerPos = Position.deserialize(buf, offset + 59); - } - - if ((nullBits[0] & 8) != 0) { - obj.attackerRot = Direction.deserialize(buf, offset + 83); - } - - if ((nullBits[0] & 16) != 0) { - obj.raycastHit = Position.deserialize(buf, offset + 95); - } - - obj.raycastDistance = buf.getFloatLE(offset + 119); - if ((nullBits[0] & 32) != 0) { - obj.raycastNormal = Vector3f.deserialize(buf, offset + 123); - } - - obj.movementDirection = MovementDirection.fromValue(buf.getByte(offset + 135)); - obj.applyForceState = ApplyForceState.fromValue(buf.getByte(offset + 136)); - obj.nextLabel = buf.getIntLE(offset + 137); - if ((nullBits[0] & 64) != 0) { - obj.generatedUUID = PacketIO.readUUID(buf, offset + 141); - } - - if ((nullBits[0] & 128) != 0) { - int varPos0 = offset + 165 + buf.getIntLE(offset + 157); - int forkCountsCount = VarInt.peek(buf, varPos0); - if (forkCountsCount < 0) { - throw ProtocolException.negativeLength("ForkCounts", forkCountsCount); + if (buf.readableBytes() - offset < 165) { + throw ProtocolException.bufferTooSmall("InteractionSyncData", 165, buf.readableBytes() - offset); + } else { + InteractionSyncData obj = new InteractionSyncData(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + obj.state = InteractionState.fromValue(buf.getByte(offset + 2)); + obj.progress = buf.getFloatLE(offset + 3); + obj.operationCounter = buf.getIntLE(offset + 7); + obj.rootInteraction = buf.getIntLE(offset + 11); + obj.totalForks = buf.getIntLE(offset + 15); + obj.entityId = buf.getIntLE(offset + 19); + obj.enteredRootInteraction = buf.getIntLE(offset + 23); + if ((nullBits[0] & 1) != 0) { + obj.blockPosition = BlockPosition.deserialize(buf, offset + 27); } - if (forkCountsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ForkCounts", forkCountsCount, 4096000); + obj.blockFace = BlockFace.fromValue(buf.getByte(offset + 39)); + if ((nullBits[0] & 2) != 0) { + obj.blockRotation = BlockRotation.deserialize(buf, offset + 40); } - int varIntLen = VarInt.length(buf, varPos0); - obj.forkCounts = new HashMap<>(forkCountsCount); - int dictPos = varPos0 + varIntLen; + obj.placedBlockId = buf.getIntLE(offset + 43); + obj.chargeValue = buf.getFloatLE(offset + 47); + obj.chainingIndex = buf.getIntLE(offset + 51); + obj.flagIndex = buf.getIntLE(offset + 55); + if ((nullBits[0] & 4) != 0) { + obj.attackerPos = Position.deserialize(buf, offset + 59); + } - for (int i = 0; i < forkCountsCount; i++) { - InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.forkCounts.put(key, val) != null) { - throw ProtocolException.duplicateKey("forkCounts", key); + if ((nullBits[0] & 8) != 0) { + obj.attackerRot = Direction.deserialize(buf, offset + 83); + } + + if ((nullBits[0] & 16) != 0) { + obj.raycastHit = Position.deserialize(buf, offset + 95); + } + + obj.raycastDistance = buf.getFloatLE(offset + 119); + if ((nullBits[0] & 32) != 0) { + obj.raycastNormal = PacketIO.readVector3f(buf, offset + 123); + } + + obj.movementDirection = MovementDirection.fromValue(buf.getByte(offset + 135)); + obj.applyForceState = ApplyForceState.fromValue(buf.getByte(offset + 136)); + obj.nextLabel = buf.getIntLE(offset + 137); + if ((nullBits[0] & 64) != 0) { + obj.generatedUUID = PacketIO.readUUID(buf, offset + 141); + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase0 = buf.getIntLE(offset + 157); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 165) { + throw ProtocolException.invalidOffset("ForkCounts", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 165 + varPosBase0; + int forkCountsCount = VarInt.peek(buf, varPos0); + if (forkCountsCount < 0) { + throw ProtocolException.invalidVarInt("ForkCounts"); + } + + int varIntLen = VarInt.size(forkCountsCount); + if (forkCountsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ForkCounts", forkCountsCount, 4096000); + } + + obj.forkCounts = new HashMap<>(forkCountsCount); + int dictPos = varPos0 + varIntLen; + + for (int i = 0; i < forkCountsCount; i++) { + InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.forkCounts.put(key, val) != null) { + throw ProtocolException.duplicateKey("forkCounts", key); + } } } + + if ((nullBits[1] & 1) != 0) { + int varPosBase1 = buf.getIntLE(offset + 161); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 165) { + throw ProtocolException.invalidOffset("HitEntities", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 165 + varPosBase1; + int hitEntitiesCount = VarInt.peek(buf, varPos1); + if (hitEntitiesCount < 0) { + throw ProtocolException.invalidVarInt("HitEntities"); + } + + int varIntLen = VarInt.size(hitEntitiesCount); + if (hitEntitiesCount > 4096000) { + throw ProtocolException.arrayTooLong("HitEntities", hitEntitiesCount, 4096000); + } + + if (varPos1 + varIntLen + hitEntitiesCount * 53L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("HitEntities", varPos1 + varIntLen + hitEntitiesCount * 53, buf.readableBytes()); + } + + obj.hitEntities = new SelectedHitEntity[hitEntitiesCount]; + int elemPos = varPos1 + varIntLen; + + for (int ix = 0; ix < hitEntitiesCount; ix++) { + obj.hitEntities[ix] = SelectedHitEntity.deserialize(buf, elemPos); + elemPos += SelectedHitEntity.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits[1] & 1) != 0) { - int varPos1 = offset + 165 + buf.getIntLE(offset + 161); - int hitEntitiesCount = VarInt.peek(buf, varPos1); - if (hitEntitiesCount < 0) { - throw ProtocolException.negativeLength("HitEntities", hitEntitiesCount); - } - - if (hitEntitiesCount > 4096000) { - throw ProtocolException.arrayTooLong("HitEntities", hitEntitiesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + hitEntitiesCount * 53L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("HitEntities", varPos1 + varIntLen + hitEntitiesCount * 53, buf.readableBytes()); - } - - obj.hitEntities = new SelectedHitEntity[hitEntitiesCount]; - int elemPos = varPos1 + varIntLen; - - for (int ix = 0; ix < hitEntitiesCount; ix++) { - obj.hitEntities[ix] = SelectedHitEntity.deserialize(buf, elemPos); - elemPos += SelectedHitEntity.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -250,9 +265,13 @@ public class InteractionSyncData { int maxEnd = 165; if ((nullBits[0] & 128) != 0) { int fieldOffset0 = buf.getIntLE(offset + 157); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 165) { + throw ProtocolException.invalidOffset("ForkCounts", fieldOffset0, maxEnd); + } + int pos0 = offset + 165 + fieldOffset0; int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos0 = ++pos0 + 4; @@ -265,9 +284,13 @@ public class InteractionSyncData { if ((nullBits[1] & 1) != 0) { int fieldOffset1 = buf.getIntLE(offset + 161); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 165) { + throw ProtocolException.invalidOffset("HitEntities", fieldOffset1, maxEnd); + } + int pos1 = offset + 165 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += SelectedHitEntity.computeBytesConsumed(buf, pos1); @@ -365,7 +388,7 @@ public class InteractionSyncData { buf.writeFloatLE(this.raycastDistance); if (this.raycastNormal != null) { - this.raycastNormal.serialize(buf); + PacketIO.writeVector3f(buf, this.raycastNormal); } else { buf.writeZero(12); } @@ -434,64 +457,81 @@ public class InteractionSyncData { return ValidationResult.error("Buffer too small: expected at least 165 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[0] & 128) != 0) { - int forkCountsOffset = buffer.getIntLE(offset + 157); - if (forkCountsOffset < 0) { - return ValidationResult.error("Invalid offset for ForkCounts"); - } + int v = buffer.getByte(offset + 2) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid InteractionState value for State"); + } else { + v = buffer.getByte(offset + 39) & 255; + if (v >= 7) { + return ValidationResult.error("Invalid BlockFace value for BlockFace"); + } else { + v = buffer.getByte(offset + 135) & 255; + if (v >= 9) { + return ValidationResult.error("Invalid MovementDirection value for MovementDirection"); + } else { + v = buffer.getByte(offset + 136) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid ApplyForceState value for ApplyForceState"); + } else { + if ((nullBits[0] & 128) != 0) { + v = buffer.getIntLE(offset + 157); + if (v < 0 || v > buffer.writerIndex() - offset - 165) { + return ValidationResult.error("Invalid offset for ForkCounts"); + } - int pos = offset + 165 + forkCountsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ForkCounts"); - } + int pos = offset + 165 + v; + int forkCountsCount = VarInt.peek(buffer, pos); + if (forkCountsCount < 0) { + return ValidationResult.error("Invalid dictionary count for ForkCounts"); + } - int forkCountsCount = VarInt.peek(buffer, pos); - if (forkCountsCount < 0) { - return ValidationResult.error("Invalid dictionary count for ForkCounts"); - } + if (forkCountsCount > 4096000) { + return ValidationResult.error("ForkCounts exceeds max length 4096000"); + } - if (forkCountsCount > 4096000) { - return ValidationResult.error("ForkCounts exceeds max length 4096000"); - } + pos += VarInt.size(forkCountsCount); - pos += VarInt.length(buffer, pos); + for (int i = 0; i < forkCountsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); + } - for (int i = 0; i < forkCountsCount; i++) { - pos = ++pos + 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + pos = ++pos + 4; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 161); + if (v < 0 || v > buffer.writerIndex() - offset - 165) { + return ValidationResult.error("Invalid offset for HitEntities"); + } + + int posx = offset + 165 + v; + int hitEntitiesCount = VarInt.peek(buffer, posx); + if (hitEntitiesCount < 0) { + return ValidationResult.error("Invalid array count for HitEntities"); + } + + if (hitEntitiesCount > 4096000) { + return ValidationResult.error("HitEntities exceeds max length 4096000"); + } + + posx += VarInt.size(hitEntitiesCount); + posx += hitEntitiesCount * 53; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading HitEntities"); + } + } + + return ValidationResult.OK; + } } } } - - if ((nullBits[1] & 1) != 0) { - int hitEntitiesOffset = buffer.getIntLE(offset + 161); - if (hitEntitiesOffset < 0) { - return ValidationResult.error("Invalid offset for HitEntities"); - } - - int posx = offset + 165 + hitEntitiesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for HitEntities"); - } - - int hitEntitiesCount = VarInt.peek(buffer, posx); - if (hitEntitiesCount < 0) { - return ValidationResult.error("Invalid array count for HitEntities"); - } - - if (hitEntitiesCount > 4096000) { - return ValidationResult.error("HitEntities exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += hitEntitiesCount * 53; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading HitEntities"); - } - } - - return ValidationResult.OK; } } @@ -517,7 +557,7 @@ public class InteractionSyncData { copy.attackerRot = this.attackerRot != null ? this.attackerRot.clone() : null; copy.raycastHit = this.raycastHit != null ? this.raycastHit.clone() : null; copy.raycastDistance = this.raycastDistance; - copy.raycastNormal = this.raycastNormal != null ? this.raycastNormal.clone() : null; + copy.raycastNormal = this.raycastNormal; copy.movementDirection = this.movementDirection; copy.applyForceState = this.applyForceState; copy.nextLabel = this.nextLabel; diff --git a/src/com/hypixel/hytale/protocol/InteractionsUpdate.java b/src/com/hypixel/hytale/protocol/InteractionsUpdate.java index e4b1ff8c..2adc784d 100644 --- a/src/com/hypixel/hytale/protocol/InteractionsUpdate.java +++ b/src/com/hypixel/hytale/protocol/InteractionsUpdate.java @@ -38,43 +38,64 @@ public class InteractionsUpdate extends ComponentUpdate { @Nonnull public static InteractionsUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - InteractionsUpdate obj = new InteractionsUpdate(); - byte nullBits = buf.getByte(offset); - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int interactionsCount = VarInt.peek(buf, varPos0); - if (interactionsCount < 0) { - throw ProtocolException.negativeLength("Interactions", interactionsCount); - } else if (interactionsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("InteractionsUpdate", 9, buf.readableBytes() - offset); } else { - int varIntLen = VarInt.length(buf, varPos0); - obj.interactions = new HashMap<>(interactionsCount); - int dictPos = varPos0 + varIntLen; - - for (int i = 0; i < interactionsCount; i++) { - InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.interactions.put(key, val) != null) { - throw ProtocolException.duplicateKey("interactions", key); - } - } - - if ((nullBits & 1) != 0) { - varPos0 = offset + 9 + buf.getIntLE(offset + 5); - interactionsCount = VarInt.peek(buf, varPos0); + InteractionsUpdate obj = new InteractionsUpdate(); + byte nullBits = buf.getByte(offset); + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 9) { + int varPos0 = offset + 9 + varPosBase0; + int interactionsCount = VarInt.peek(buf, varPos0); if (interactionsCount < 0) { - throw ProtocolException.negativeLength("InteractionHint", interactionsCount); - } + throw ProtocolException.invalidVarInt("Interactions"); + } else { + int varIntLen = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + } else { + obj.interactions = new HashMap<>(interactionsCount); + int dictPos = varPos0 + varIntLen; - if (interactionsCount > 4096000) { - throw ProtocolException.stringTooLong("InteractionHint", interactionsCount, 4096000); - } + for (int i = 0; i < interactionsCount; i++) { + InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.interactions.put(key, val) != null) { + throw ProtocolException.duplicateKey("interactions", key); + } + } - obj.interactionHint = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 1) != 0) { + varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("InteractionHint", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 9 + varPosBase0; + interactionsCount = VarInt.peek(buf, varPos0); + if (interactionsCount < 0) { + throw ProtocolException.invalidVarInt("InteractionHint"); + } + + varIntLen = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.stringTooLong("InteractionHint", interactionsCount, 4096000); + } + + if (varPos0 + varIntLen + interactionsCount > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("InteractionHint", varPos0 + varIntLen + interactionsCount, buf.readableBytes()); + } + + obj.interactionHint = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("Interactions", varPosBase0, buf.readableBytes()); } - - return obj; } } @@ -82,29 +103,37 @@ public class InteractionsUpdate extends ComponentUpdate { byte nullBits = buf.getByte(offset); int maxEnd = 9; int fieldOffset0 = buf.getIntLE(offset + 1); - int pos0 = offset + 9 + fieldOffset0; - int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 9) { + int pos0 = offset + 9 + fieldOffset0; + int dictLen = VarInt.peek(buf, pos0); + pos0 += VarInt.size(dictLen); - for (int i = 0; i < dictLen; i++) { - pos0 = ++pos0 + 4; - } + for (int i = 0; i < dictLen; i++) { + pos0 = ++pos0 + 4; + } - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - - if ((nullBits & 1) != 0) { - fieldOffset0 = buf.getIntLE(offset + 5); - pos0 = offset + 9 + fieldOffset0; - dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + dictLen; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } - } - return maxEnd; + if ((nullBits & 1) != 0) { + fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("InteractionHint", fieldOffset0, maxEnd); + } + + pos0 = offset + 9 + fieldOffset0; + dictLen = VarInt.peek(buf, pos0); + pos0 += VarInt.size(dictLen) + dictLen; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Interactions", fieldOffset0, maxEnd); + } } @Override @@ -160,58 +189,55 @@ public class InteractionsUpdate extends ComponentUpdate { } else { byte nullBits = buffer.getByte(offset); int interactionsOffset = buffer.getIntLE(offset + 1); - if (interactionsOffset < 0) { - return ValidationResult.error("Invalid offset for Interactions"); - } else { + if (interactionsOffset >= 0 && interactionsOffset <= buffer.writerIndex() - offset - 9) { int pos = offset + 9 + interactionsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Interactions"); + int interactionsCount = VarInt.peek(buffer, pos); + if (interactionsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Interactions"); + } else if (interactionsCount > 4096000) { + return ValidationResult.error("Interactions exceeds max length 4096000"); } else { - int interactionsCount = VarInt.peek(buffer, pos); - if (interactionsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Interactions"); - } else if (interactionsCount > 4096000) { - return ValidationResult.error("Interactions exceeds max length 4096000"); - } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(interactionsCount); - for (int i = 0; i < interactionsCount; i++) { - pos = ++pos + 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); - } + for (int i = 0; i < interactionsCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); } - if ((nullBits & 1) != 0) { - interactionsOffset = buffer.getIntLE(offset + 5); - if (interactionsOffset < 0) { - return ValidationResult.error("Invalid offset for InteractionHint"); - } - - pos = offset + 9 + interactionsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for InteractionHint"); - } - - interactionsCount = VarInt.peek(buffer, pos); - if (interactionsCount < 0) { - return ValidationResult.error("Invalid string length for InteractionHint"); - } - - if (interactionsCount > 4096000) { - return ValidationResult.error("InteractionHint exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += interactionsCount; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading InteractionHint"); - } + pos = ++pos + 4; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); } - - return ValidationResult.OK; } + + if ((nullBits & 1) != 0) { + interactionsOffset = buffer.getIntLE(offset + 5); + if (interactionsOffset < 0 || interactionsOffset > buffer.writerIndex() - offset - 9) { + return ValidationResult.error("Invalid offset for InteractionHint"); + } + + pos = offset + 9 + interactionsOffset; + interactionsCount = VarInt.peek(buffer, pos); + if (interactionsCount < 0) { + return ValidationResult.error("Invalid string length for InteractionHint"); + } + + if (interactionsCount > 4096000) { + return ValidationResult.error("InteractionHint exceeds max length 4096000"); + } + + pos += VarInt.size(interactionsCount); + pos += interactionsCount; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading InteractionHint"); + } + } + + return ValidationResult.OK; } + } else { + return ValidationResult.error("Invalid offset for Interactions"); } } } diff --git a/src/com/hypixel/hytale/protocol/IntersectionHighlight.java b/src/com/hypixel/hytale/protocol/IntersectionHighlight.java index daa14f4f..e9c5fe58 100644 --- a/src/com/hypixel/hytale/protocol/IntersectionHighlight.java +++ b/src/com/hypixel/hytale/protocol/IntersectionHighlight.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -31,14 +32,18 @@ public class IntersectionHighlight { @Nonnull public static IntersectionHighlight deserialize(@Nonnull ByteBuf buf, int offset) { - IntersectionHighlight obj = new IntersectionHighlight(); - byte nullBits = buf.getByte(offset); - obj.highlightThreshold = buf.getFloatLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.highlightColor = Color.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("IntersectionHighlight", 8, buf.readableBytes() - offset); + } else { + IntersectionHighlight obj = new IntersectionHighlight(); + byte nullBits = buf.getByte(offset); + obj.highlightThreshold = buf.getFloatLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.highlightColor = Color.deserialize(buf, offset + 5); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -65,7 +70,12 @@ public class IntersectionHighlight { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 8 ? ValidationResult.error("Buffer too small: expected at least 8 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 8) { + return ValidationResult.error("Buffer too small: expected at least 8 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public IntersectionHighlight clone() { diff --git a/src/com/hypixel/hytale/protocol/InventorySection.java b/src/com/hypixel/hytale/protocol/InventorySection.java index 9e5835e9..d460ae8d 100644 --- a/src/com/hypixel/hytale/protocol/InventorySection.java +++ b/src/com/hypixel/hytale/protocol/InventorySection.java @@ -36,35 +36,40 @@ public class InventorySection { @Nonnull public static InventorySection deserialize(@Nonnull ByteBuf buf, int offset) { - InventorySection obj = new InventorySection(); - byte nullBits = buf.getByte(offset); - obj.capacity = buf.getShortLE(offset + 1); - int pos = offset + 3; - if ((nullBits & 1) != 0) { - int itemsCount = VarInt.peek(buf, pos); - if (itemsCount < 0) { - throw ProtocolException.negativeLength("Items", itemsCount); - } + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("InventorySection", 3, buf.readableBytes() - offset); + } else { + InventorySection obj = new InventorySection(); + byte nullBits = buf.getByte(offset); + obj.capacity = buf.getShortLE(offset + 1); + int pos = offset + 3; + if ((nullBits & 1) != 0) { + int itemsCount = VarInt.peek(buf, pos); + if (itemsCount < 0) { + throw ProtocolException.invalidVarInt("Items"); + } - if (itemsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Items", itemsCount, 4096000); - } + int itemsVarLen = VarInt.size(itemsCount); + if (itemsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Items", itemsCount, 4096000); + } - pos += VarInt.size(itemsCount); - obj.items = new HashMap<>(itemsCount); + pos += itemsVarLen; + obj.items = new HashMap<>(itemsCount); - for (int i = 0; i < itemsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - ItemWithAllMetadata val = ItemWithAllMetadata.deserialize(buf, pos); - pos += ItemWithAllMetadata.computeBytesConsumed(buf, pos); - if (obj.items.put(key, val) != null) { - throw ProtocolException.duplicateKey("items", key); + for (int i = 0; i < itemsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + ItemWithAllMetadata val = ItemWithAllMetadata.deserialize(buf, pos); + pos += ItemWithAllMetadata.computeBytesConsumed(buf, pos); + if (obj.items.put(key, val) != null) { + throw ProtocolException.duplicateKey("items", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +77,7 @@ public class InventorySection { int pos = offset + 3; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -136,7 +141,7 @@ public class InventorySection { return ValidationResult.error("Items exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(itemsCount); for (int i = 0; i < itemsCount; i++) { pos += 4; diff --git a/src/com/hypixel/hytale/protocol/ItemAnimation.java b/src/com/hypixel/hytale/protocol/ItemAnimation.java index b110f006..cf61ba7c 100644 --- a/src/com/hypixel/hytale/protocol/ItemAnimation.java +++ b/src/com/hypixel/hytale/protocol/ItemAnimation.java @@ -73,84 +73,140 @@ public class ItemAnimation { @Nonnull public static ItemAnimation deserialize(@Nonnull ByteBuf buf, int offset) { - ItemAnimation obj = new ItemAnimation(); - byte nullBits = buf.getByte(offset); - obj.keepPreviousFirstPersonAnimation = buf.getByte(offset + 1) != 0; - obj.speed = buf.getFloatLE(offset + 2); - obj.blendingDuration = buf.getFloatLE(offset + 6); - obj.looping = buf.getByte(offset + 10) != 0; - obj.clipsGeometry = buf.getByte(offset + 11) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 32 + buf.getIntLE(offset + 12); - int thirdPersonLen = VarInt.peek(buf, varPos0); - if (thirdPersonLen < 0) { - throw ProtocolException.negativeLength("ThirdPerson", thirdPersonLen); + if (buf.readableBytes() - offset < 32) { + throw ProtocolException.bufferTooSmall("ItemAnimation", 32, buf.readableBytes() - offset); + } else { + ItemAnimation obj = new ItemAnimation(); + byte nullBits = buf.getByte(offset); + obj.keepPreviousFirstPersonAnimation = buf.getByte(offset + 1) != 0; + obj.speed = buf.getFloatLE(offset + 2); + obj.blendingDuration = buf.getFloatLE(offset + 6); + obj.looping = buf.getByte(offset + 10) != 0; + obj.clipsGeometry = buf.getByte(offset + 11) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 12); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("ThirdPerson", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 32 + varPosBase0; + int thirdPersonLen = VarInt.peek(buf, varPos0); + if (thirdPersonLen < 0) { + throw ProtocolException.invalidVarInt("ThirdPerson"); + } + + int thirdPersonVarIntLen = VarInt.size(thirdPersonLen); + if (thirdPersonLen > 4096000) { + throw ProtocolException.stringTooLong("ThirdPerson", thirdPersonLen, 4096000); + } + + if (varPos0 + thirdPersonVarIntLen + thirdPersonLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ThirdPerson", varPos0 + thirdPersonVarIntLen + thirdPersonLen, buf.readableBytes()); + } + + obj.thirdPerson = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (thirdPersonLen > 4096000) { - throw ProtocolException.stringTooLong("ThirdPerson", thirdPersonLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 16); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("ThirdPersonMoving", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 32 + varPosBase1; + int thirdPersonMovingLen = VarInt.peek(buf, varPos1); + if (thirdPersonMovingLen < 0) { + throw ProtocolException.invalidVarInt("ThirdPersonMoving"); + } + + int thirdPersonMovingVarIntLen = VarInt.size(thirdPersonMovingLen); + if (thirdPersonMovingLen > 4096000) { + throw ProtocolException.stringTooLong("ThirdPersonMoving", thirdPersonMovingLen, 4096000); + } + + if (varPos1 + thirdPersonMovingVarIntLen + thirdPersonMovingLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ThirdPersonMoving", varPos1 + thirdPersonMovingVarIntLen + thirdPersonMovingLen, buf.readableBytes()); + } + + obj.thirdPersonMoving = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.thirdPerson = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 20); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("ThirdPersonFace", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 32 + varPosBase2; + int thirdPersonFaceLen = VarInt.peek(buf, varPos2); + if (thirdPersonFaceLen < 0) { + throw ProtocolException.invalidVarInt("ThirdPersonFace"); + } + + int thirdPersonFaceVarIntLen = VarInt.size(thirdPersonFaceLen); + if (thirdPersonFaceLen > 4096000) { + throw ProtocolException.stringTooLong("ThirdPersonFace", thirdPersonFaceLen, 4096000); + } + + if (varPos2 + thirdPersonFaceVarIntLen + thirdPersonFaceLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ThirdPersonFace", varPos2 + thirdPersonFaceVarIntLen + thirdPersonFaceLen, buf.readableBytes()); + } + + obj.thirdPersonFace = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 24); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("FirstPerson", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 32 + varPosBase3; + int firstPersonLen = VarInt.peek(buf, varPos3); + if (firstPersonLen < 0) { + throw ProtocolException.invalidVarInt("FirstPerson"); + } + + int firstPersonVarIntLen = VarInt.size(firstPersonLen); + if (firstPersonLen > 4096000) { + throw ProtocolException.stringTooLong("FirstPerson", firstPersonLen, 4096000); + } + + if (varPos3 + firstPersonVarIntLen + firstPersonLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FirstPerson", varPos3 + firstPersonVarIntLen + firstPersonLen, buf.readableBytes()); + } + + obj.firstPerson = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 28); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("FirstPersonOverride", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 32 + varPosBase4; + int firstPersonOverrideLen = VarInt.peek(buf, varPos4); + if (firstPersonOverrideLen < 0) { + throw ProtocolException.invalidVarInt("FirstPersonOverride"); + } + + int firstPersonOverrideVarIntLen = VarInt.size(firstPersonOverrideLen); + if (firstPersonOverrideLen > 4096000) { + throw ProtocolException.stringTooLong("FirstPersonOverride", firstPersonOverrideLen, 4096000); + } + + if (varPos4 + firstPersonOverrideVarIntLen + firstPersonOverrideLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "FirstPersonOverride", varPos4 + firstPersonOverrideVarIntLen + firstPersonOverrideLen, buf.readableBytes() + ); + } + + obj.firstPersonOverride = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 32 + buf.getIntLE(offset + 16); - int thirdPersonMovingLen = VarInt.peek(buf, varPos1); - if (thirdPersonMovingLen < 0) { - throw ProtocolException.negativeLength("ThirdPersonMoving", thirdPersonMovingLen); - } - - if (thirdPersonMovingLen > 4096000) { - throw ProtocolException.stringTooLong("ThirdPersonMoving", thirdPersonMovingLen, 4096000); - } - - obj.thirdPersonMoving = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 32 + buf.getIntLE(offset + 20); - int thirdPersonFaceLen = VarInt.peek(buf, varPos2); - if (thirdPersonFaceLen < 0) { - throw ProtocolException.negativeLength("ThirdPersonFace", thirdPersonFaceLen); - } - - if (thirdPersonFaceLen > 4096000) { - throw ProtocolException.stringTooLong("ThirdPersonFace", thirdPersonFaceLen, 4096000); - } - - obj.thirdPersonFace = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 32 + buf.getIntLE(offset + 24); - int firstPersonLen = VarInt.peek(buf, varPos3); - if (firstPersonLen < 0) { - throw ProtocolException.negativeLength("FirstPerson", firstPersonLen); - } - - if (firstPersonLen > 4096000) { - throw ProtocolException.stringTooLong("FirstPerson", firstPersonLen, 4096000); - } - - obj.firstPerson = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 32 + buf.getIntLE(offset + 28); - int firstPersonOverrideLen = VarInt.peek(buf, varPos4); - if (firstPersonOverrideLen < 0) { - throw ProtocolException.negativeLength("FirstPersonOverride", firstPersonOverrideLen); - } - - if (firstPersonOverrideLen > 4096000) { - throw ProtocolException.stringTooLong("FirstPersonOverride", firstPersonOverrideLen, 4096000); - } - - obj.firstPersonOverride = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -158,9 +214,13 @@ public class ItemAnimation { int maxEnd = 32; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 12); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("ThirdPerson", fieldOffset0, maxEnd); + } + int pos0 = offset + 32 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -168,9 +228,13 @@ public class ItemAnimation { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 16); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("ThirdPersonMoving", fieldOffset1, maxEnd); + } + int pos1 = offset + 32 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -178,9 +242,13 @@ public class ItemAnimation { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 20); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("ThirdPersonFace", fieldOffset2, maxEnd); + } + int pos2 = offset + 32 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -188,9 +256,13 @@ public class ItemAnimation { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 24); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("FirstPerson", fieldOffset3, maxEnd); + } + int pos3 = offset + 32 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -198,9 +270,13 @@ public class ItemAnimation { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 28); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 32) { + throw ProtocolException.invalidOffset("FirstPersonOverride", fieldOffset4, maxEnd); + } + int pos4 = offset + 32 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -317,15 +393,11 @@ public class ItemAnimation { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int thirdPersonOffset = buffer.getIntLE(offset + 12); - if (thirdPersonOffset < 0) { + if (thirdPersonOffset < 0 || thirdPersonOffset > buffer.writerIndex() - offset - 32) { return ValidationResult.error("Invalid offset for ThirdPerson"); } int pos = offset + 32 + thirdPersonOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ThirdPerson"); - } - int thirdPersonLen = VarInt.peek(buffer, pos); if (thirdPersonLen < 0) { return ValidationResult.error("Invalid string length for ThirdPerson"); @@ -335,7 +407,7 @@ public class ItemAnimation { return ValidationResult.error("ThirdPerson exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(thirdPersonLen); pos += thirdPersonLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ThirdPerson"); @@ -344,15 +416,11 @@ public class ItemAnimation { if ((nullBits & 2) != 0) { int thirdPersonMovingOffset = buffer.getIntLE(offset + 16); - if (thirdPersonMovingOffset < 0) { + if (thirdPersonMovingOffset < 0 || thirdPersonMovingOffset > buffer.writerIndex() - offset - 32) { return ValidationResult.error("Invalid offset for ThirdPersonMoving"); } int posx = offset + 32 + thirdPersonMovingOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ThirdPersonMoving"); - } - int thirdPersonMovingLen = VarInt.peek(buffer, posx); if (thirdPersonMovingLen < 0) { return ValidationResult.error("Invalid string length for ThirdPersonMoving"); @@ -362,7 +430,7 @@ public class ItemAnimation { return ValidationResult.error("ThirdPersonMoving exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(thirdPersonMovingLen); posx += thirdPersonMovingLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ThirdPersonMoving"); @@ -371,15 +439,11 @@ public class ItemAnimation { if ((nullBits & 4) != 0) { int thirdPersonFaceOffset = buffer.getIntLE(offset + 20); - if (thirdPersonFaceOffset < 0) { + if (thirdPersonFaceOffset < 0 || thirdPersonFaceOffset > buffer.writerIndex() - offset - 32) { return ValidationResult.error("Invalid offset for ThirdPersonFace"); } int posxx = offset + 32 + thirdPersonFaceOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ThirdPersonFace"); - } - int thirdPersonFaceLen = VarInt.peek(buffer, posxx); if (thirdPersonFaceLen < 0) { return ValidationResult.error("Invalid string length for ThirdPersonFace"); @@ -389,7 +453,7 @@ public class ItemAnimation { return ValidationResult.error("ThirdPersonFace exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(thirdPersonFaceLen); posxx += thirdPersonFaceLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ThirdPersonFace"); @@ -398,15 +462,11 @@ public class ItemAnimation { if ((nullBits & 8) != 0) { int firstPersonOffset = buffer.getIntLE(offset + 24); - if (firstPersonOffset < 0) { + if (firstPersonOffset < 0 || firstPersonOffset > buffer.writerIndex() - offset - 32) { return ValidationResult.error("Invalid offset for FirstPerson"); } int posxxx = offset + 32 + firstPersonOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstPerson"); - } - int firstPersonLen = VarInt.peek(buffer, posxxx); if (firstPersonLen < 0) { return ValidationResult.error("Invalid string length for FirstPerson"); @@ -416,7 +476,7 @@ public class ItemAnimation { return ValidationResult.error("FirstPerson exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(firstPersonLen); posxxx += firstPersonLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FirstPerson"); @@ -425,15 +485,11 @@ public class ItemAnimation { if ((nullBits & 16) != 0) { int firstPersonOverrideOffset = buffer.getIntLE(offset + 28); - if (firstPersonOverrideOffset < 0) { + if (firstPersonOverrideOffset < 0 || firstPersonOverrideOffset > buffer.writerIndex() - offset - 32) { return ValidationResult.error("Invalid offset for FirstPersonOverride"); } int posxxxx = offset + 32 + firstPersonOverrideOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstPersonOverride"); - } - int firstPersonOverrideLen = VarInt.peek(buffer, posxxxx); if (firstPersonOverrideLen < 0) { return ValidationResult.error("Invalid string length for FirstPersonOverride"); @@ -443,7 +499,7 @@ public class ItemAnimation { return ValidationResult.error("FirstPersonOverride exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(firstPersonOverrideLen); posxxxx += firstPersonOverrideLen; if (posxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FirstPersonOverride"); diff --git a/src/com/hypixel/hytale/protocol/ItemAppearanceCondition.java b/src/com/hypixel/hytale/protocol/ItemAppearanceCondition.java index 8aba744a..c2347b37 100644 --- a/src/com/hypixel/hytale/protocol/ItemAppearanceCondition.java +++ b/src/com/hypixel/hytale/protocol/ItemAppearanceCondition.java @@ -72,108 +72,152 @@ public class ItemAppearanceCondition { @Nonnull public static ItemAppearanceCondition deserialize(@Nonnull ByteBuf buf, int offset) { - ItemAppearanceCondition obj = new ItemAppearanceCondition(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.condition = FloatRange.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 38) { + throw ProtocolException.bufferTooSmall("ItemAppearanceCondition", 38, buf.readableBytes() - offset); + } else { + ItemAppearanceCondition obj = new ItemAppearanceCondition(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.condition = FloatRange.deserialize(buf, offset + 1); + } + + obj.conditionValueType = ValueType.fromValue(buf.getByte(offset + 9)); + obj.localSoundEventId = buf.getIntLE(offset + 10); + obj.worldSoundEventId = buf.getIntLE(offset + 14); + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 18); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Particles", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 38 + varPosBase0; + int particlesCount = VarInt.peek(buf, varPos0); + if (particlesCount < 0) { + throw ProtocolException.invalidVarInt("Particles"); + } + + int varIntLen = VarInt.size(particlesCount); + if (particlesCount > 4096000) { + throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); + } + + if (varPos0 + varIntLen + particlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Particles", varPos0 + varIntLen + particlesCount * 34, buf.readableBytes()); + } + + obj.particles = new ModelParticle[particlesCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < particlesCount; i++) { + obj.particles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 22); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("FirstPersonParticles", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 38 + varPosBase1; + int firstPersonParticlesCount = VarInt.peek(buf, varPos1); + if (firstPersonParticlesCount < 0) { + throw ProtocolException.invalidVarInt("FirstPersonParticles"); + } + + int varIntLenx = VarInt.size(firstPersonParticlesCount); + if (firstPersonParticlesCount > 4096000) { + throw ProtocolException.arrayTooLong("FirstPersonParticles", firstPersonParticlesCount, 4096000); + } + + if (varPos1 + varIntLenx + firstPersonParticlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FirstPersonParticles", varPos1 + varIntLenx + firstPersonParticlesCount * 34, buf.readableBytes()); + } + + obj.firstPersonParticles = new ModelParticle[firstPersonParticlesCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < firstPersonParticlesCount; i++) { + obj.firstPersonParticles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 26); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Model", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 38 + varPosBase2; + int modelLen = VarInt.peek(buf, varPos2); + if (modelLen < 0) { + throw ProtocolException.invalidVarInt("Model"); + } + + int modelVarIntLen = VarInt.size(modelLen); + if (modelLen > 4096000) { + throw ProtocolException.stringTooLong("Model", modelLen, 4096000); + } + + if (varPos2 + modelVarIntLen + modelLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Model", varPos2 + modelVarIntLen + modelLen, buf.readableBytes()); + } + + obj.model = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase3 = buf.getIntLE(offset + 30); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Texture", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 38 + varPosBase3; + int textureLen = VarInt.peek(buf, varPos3); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarIntLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (varPos3 + textureVarIntLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", varPos3 + textureVarIntLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits & 32) != 0) { + int varPosBase4 = buf.getIntLE(offset + 34); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("ModelVFXId", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 38 + varPosBase4; + int modelVFXIdLen = VarInt.peek(buf, varPos4); + if (modelVFXIdLen < 0) { + throw ProtocolException.invalidVarInt("ModelVFXId"); + } + + int modelVFXIdVarIntLen = VarInt.size(modelVFXIdLen); + if (modelVFXIdLen > 4096000) { + throw ProtocolException.stringTooLong("ModelVFXId", modelVFXIdLen, 4096000); + } + + if (varPos4 + modelVFXIdVarIntLen + modelVFXIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ModelVFXId", varPos4 + modelVFXIdVarIntLen + modelVFXIdLen, buf.readableBytes()); + } + + obj.modelVFXId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + return obj; } - - obj.conditionValueType = ValueType.fromValue(buf.getByte(offset + 9)); - obj.localSoundEventId = buf.getIntLE(offset + 10); - obj.worldSoundEventId = buf.getIntLE(offset + 14); - if ((nullBits & 2) != 0) { - int varPos0 = offset + 38 + buf.getIntLE(offset + 18); - int particlesCount = VarInt.peek(buf, varPos0); - if (particlesCount < 0) { - throw ProtocolException.negativeLength("Particles", particlesCount); - } - - if (particlesCount > 4096000) { - throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + particlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Particles", varPos0 + varIntLen + particlesCount * 34, buf.readableBytes()); - } - - obj.particles = new ModelParticle[particlesCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < particlesCount; i++) { - obj.particles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 38 + buf.getIntLE(offset + 22); - int firstPersonParticlesCount = VarInt.peek(buf, varPos1); - if (firstPersonParticlesCount < 0) { - throw ProtocolException.negativeLength("FirstPersonParticles", firstPersonParticlesCount); - } - - if (firstPersonParticlesCount > 4096000) { - throw ProtocolException.arrayTooLong("FirstPersonParticles", firstPersonParticlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + firstPersonParticlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FirstPersonParticles", varPos1 + varIntLen + firstPersonParticlesCount * 34, buf.readableBytes()); - } - - obj.firstPersonParticles = new ModelParticle[firstPersonParticlesCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < firstPersonParticlesCount; i++) { - obj.firstPersonParticles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 8) != 0) { - int varPos2 = offset + 38 + buf.getIntLE(offset + 26); - int modelLen = VarInt.peek(buf, varPos2); - if (modelLen < 0) { - throw ProtocolException.negativeLength("Model", modelLen); - } - - if (modelLen > 4096000) { - throw ProtocolException.stringTooLong("Model", modelLen, 4096000); - } - - obj.model = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 16) != 0) { - int varPos3 = offset + 38 + buf.getIntLE(offset + 30); - int textureLen = VarInt.peek(buf, varPos3); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); - } - - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } - - obj.texture = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits & 32) != 0) { - int varPos4 = offset + 38 + buf.getIntLE(offset + 34); - int modelVFXIdLen = VarInt.peek(buf, varPos4); - if (modelVFXIdLen < 0) { - throw ProtocolException.negativeLength("ModelVFXId", modelVFXIdLen); - } - - if (modelVFXIdLen > 4096000) { - throw ProtocolException.stringTooLong("ModelVFXId", modelVFXIdLen, 4096000); - } - - obj.modelVFXId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -181,9 +225,13 @@ public class ItemAppearanceCondition { int maxEnd = 38; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 18); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Particles", fieldOffset0, maxEnd); + } + int pos0 = offset + 38 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += ModelParticle.computeBytesConsumed(buf, pos0); @@ -196,9 +244,13 @@ public class ItemAppearanceCondition { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 22); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("FirstPersonParticles", fieldOffset1, maxEnd); + } + int pos1 = offset + 38 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += ModelParticle.computeBytesConsumed(buf, pos1); @@ -211,9 +263,13 @@ public class ItemAppearanceCondition { if ((nullBits & 8) != 0) { int fieldOffset2 = buf.getIntLE(offset + 26); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Model", fieldOffset2, maxEnd); + } + int pos2 = offset + 38 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -221,9 +277,13 @@ public class ItemAppearanceCondition { if ((nullBits & 16) != 0) { int fieldOffset3 = buf.getIntLE(offset + 30); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("Texture", fieldOffset3, maxEnd); + } + int pos3 = offset + 38 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -231,9 +291,13 @@ public class ItemAppearanceCondition { if ((nullBits & 32) != 0) { int fieldOffset4 = buf.getIntLE(offset + 34); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 38) { + throw ProtocolException.invalidOffset("ModelVFXId", fieldOffset4, maxEnd); + } + int pos4 = offset + 38 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -384,152 +448,137 @@ public class ItemAppearanceCondition { return ValidationResult.error("Buffer too small: expected at least 38 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 2) != 0) { - int particlesOffset = buffer.getIntLE(offset + 18); - if (particlesOffset < 0) { - return ValidationResult.error("Invalid offset for Particles"); - } - - int pos = offset + 38 + particlesOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particles"); - } - - int particlesCount = VarInt.peek(buffer, pos); - if (particlesCount < 0) { - return ValidationResult.error("Invalid array count for Particles"); - } - - if (particlesCount > 4096000) { - return ValidationResult.error("Particles exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < particlesCount; i++) { - ValidationResult structResult = ModelParticle.validateStructure(buffer, pos); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 9) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ValueType value for ConditionValueType"); + } else { + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 18); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for Particles"); } - pos += ModelParticle.computeBytesConsumed(buffer, pos); - } - } - - if ((nullBits & 4) != 0) { - int firstPersonParticlesOffset = buffer.getIntLE(offset + 22); - if (firstPersonParticlesOffset < 0) { - return ValidationResult.error("Invalid offset for FirstPersonParticles"); - } - - int posx = offset + 38 + firstPersonParticlesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstPersonParticles"); - } - - int firstPersonParticlesCount = VarInt.peek(buffer, posx); - if (firstPersonParticlesCount < 0) { - return ValidationResult.error("Invalid array count for FirstPersonParticles"); - } - - if (firstPersonParticlesCount > 4096000) { - return ValidationResult.error("FirstPersonParticles exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < firstPersonParticlesCount; i++) { - ValidationResult structResult = ModelParticle.validateStructure(buffer, posx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ModelParticle in FirstPersonParticles[" + i + "]: " + structResult.error()); + int pos = offset + 38 + v; + int particlesCount = VarInt.peek(buffer, pos); + if (particlesCount < 0) { + return ValidationResult.error("Invalid array count for Particles"); } - posx += ModelParticle.computeBytesConsumed(buffer, posx); + if (particlesCount > 4096000) { + return ValidationResult.error("Particles exceeds max length 4096000"); + } + + pos += VarInt.size(particlesCount); + + for (int i = 0; i < particlesCount; i++) { + ValidationResult structResult = ModelParticle.validateStructure(buffer, pos); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); + } + + pos += ModelParticle.computeBytesConsumed(buffer, pos); + } } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 22); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for FirstPersonParticles"); + } + + int posx = offset + 38 + v; + int firstPersonParticlesCount = VarInt.peek(buffer, posx); + if (firstPersonParticlesCount < 0) { + return ValidationResult.error("Invalid array count for FirstPersonParticles"); + } + + if (firstPersonParticlesCount > 4096000) { + return ValidationResult.error("FirstPersonParticles exceeds max length 4096000"); + } + + posx += VarInt.size(firstPersonParticlesCount); + + for (int i = 0; i < firstPersonParticlesCount; i++) { + ValidationResult structResult = ModelParticle.validateStructure(buffer, posx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ModelParticle in FirstPersonParticles[" + i + "]: " + structResult.error()); + } + + posx += ModelParticle.computeBytesConsumed(buffer, posx); + } + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 26); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for Model"); + } + + int posxx = offset + 38 + v; + int modelLen = VarInt.peek(buffer, posxx); + if (modelLen < 0) { + return ValidationResult.error("Invalid string length for Model"); + } + + if (modelLen > 4096000) { + return ValidationResult.error("Model exceeds max length 4096000"); + } + + posxx += VarInt.size(modelLen); + posxx += modelLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Model"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 30); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for Texture"); + } + + int posxxx = offset + 38 + v; + int textureLen = VarInt.peek(buffer, posxxx); + if (textureLen < 0) { + return ValidationResult.error("Invalid string length for Texture"); + } + + if (textureLen > 4096000) { + return ValidationResult.error("Texture exceeds max length 4096000"); + } + + posxxx += VarInt.size(textureLen); + posxxx += textureLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Texture"); + } + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 34); + if (v < 0 || v > buffer.writerIndex() - offset - 38) { + return ValidationResult.error("Invalid offset for ModelVFXId"); + } + + int posxxxx = offset + 38 + v; + int modelVFXIdLen = VarInt.peek(buffer, posxxxx); + if (modelVFXIdLen < 0) { + return ValidationResult.error("Invalid string length for ModelVFXId"); + } + + if (modelVFXIdLen > 4096000) { + return ValidationResult.error("ModelVFXId exceeds max length 4096000"); + } + + posxxxx += VarInt.size(modelVFXIdLen); + posxxxx += modelVFXIdLen; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ModelVFXId"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 8) != 0) { - int modelOffset = buffer.getIntLE(offset + 26); - if (modelOffset < 0) { - return ValidationResult.error("Invalid offset for Model"); - } - - int posxx = offset + 38 + modelOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } - - int modelLen = VarInt.peek(buffer, posxx); - if (modelLen < 0) { - return ValidationResult.error("Invalid string length for Model"); - } - - if (modelLen > 4096000) { - return ValidationResult.error("Model exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += modelLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Model"); - } - } - - if ((nullBits & 16) != 0) { - int textureOffset = buffer.getIntLE(offset + 30); - if (textureOffset < 0) { - return ValidationResult.error("Invalid offset for Texture"); - } - - int posxxx = offset + 38 + textureOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Texture"); - } - - int textureLen = VarInt.peek(buffer, posxxx); - if (textureLen < 0) { - return ValidationResult.error("Invalid string length for Texture"); - } - - if (textureLen > 4096000) { - return ValidationResult.error("Texture exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += textureLen; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Texture"); - } - } - - if ((nullBits & 32) != 0) { - int modelVFXIdOffset = buffer.getIntLE(offset + 34); - if (modelVFXIdOffset < 0) { - return ValidationResult.error("Invalid offset for ModelVFXId"); - } - - int posxxxx = offset + 38 + modelVFXIdOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModelVFXId"); - } - - int modelVFXIdLen = VarInt.peek(buffer, posxxxx); - if (modelVFXIdLen < 0) { - return ValidationResult.error("Invalid string length for ModelVFXId"); - } - - if (modelVFXIdLen > 4096000) { - return ValidationResult.error("ModelVFXId exceeds max length 4096000"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - posxxxx += modelVFXIdLen; - if (posxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ModelVFXId"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ItemArmor.java b/src/com/hypixel/hytale/protocol/ItemArmor.java index af3bdb70..755e0ecf 100644 --- a/src/com/hypixel/hytale/protocol/ItemArmor.java +++ b/src/com/hypixel/hytale/protocol/ItemArmor.java @@ -66,250 +66,291 @@ public class ItemArmor { @Nonnull public static ItemArmor deserialize(@Nonnull ByteBuf buf, int offset) { - ItemArmor obj = new ItemArmor(); - byte nullBits = buf.getByte(offset); - obj.armorSlot = ItemArmorSlot.fromValue(buf.getByte(offset + 1)); - obj.baseDamageResistance = buf.getDoubleLE(offset + 2); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 30 + buf.getIntLE(offset + 10); - int cosmeticsToHideCount = VarInt.peek(buf, varPos0); - if (cosmeticsToHideCount < 0) { - throw ProtocolException.negativeLength("CosmeticsToHide", cosmeticsToHideCount); + if (buf.readableBytes() - offset < 30) { + throw ProtocolException.bufferTooSmall("ItemArmor", 30, buf.readableBytes() - offset); + } else { + ItemArmor obj = new ItemArmor(); + byte nullBits = buf.getByte(offset); + obj.armorSlot = ItemArmorSlot.fromValue(buf.getByte(offset + 1)); + obj.baseDamageResistance = buf.getDoubleLE(offset + 2); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 10); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("CosmeticsToHide", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 30 + varPosBase0; + int cosmeticsToHideCount = VarInt.peek(buf, varPos0); + if (cosmeticsToHideCount < 0) { + throw ProtocolException.invalidVarInt("CosmeticsToHide"); + } + + int varIntLen = VarInt.size(cosmeticsToHideCount); + if (cosmeticsToHideCount > 4096000) { + throw ProtocolException.arrayTooLong("CosmeticsToHide", cosmeticsToHideCount, 4096000); + } + + if (varPos0 + varIntLen + cosmeticsToHideCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CosmeticsToHide", varPos0 + varIntLen + cosmeticsToHideCount * 1, buf.readableBytes()); + } + + obj.cosmeticsToHide = new Cosmetic[cosmeticsToHideCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < cosmeticsToHideCount; i++) { + obj.cosmeticsToHide[i] = Cosmetic.fromValue(buf.getByte(elemPos)); + elemPos++; + } } - if (cosmeticsToHideCount > 4096000) { - throw ProtocolException.arrayTooLong("CosmeticsToHide", cosmeticsToHideCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 14); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("StatModifiers", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 30 + varPosBase1; + int statModifiersCount = VarInt.peek(buf, varPos1); + if (statModifiersCount < 0) { + throw ProtocolException.invalidVarInt("StatModifiers"); + } + + int varIntLenx = VarInt.size(statModifiersCount); + if (statModifiersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); + } + + obj.statModifiers = new HashMap<>(statModifiersCount); + int dictPos = varPos1 + varIntLenx; + + for (int i = 0; i < statModifiersCount; i++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + int valLen = VarInt.peek(buf, dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 64) { + throw ProtocolException.arrayTooLong("val", valLen, 64); + } + + if (dictPos + valVarLen + valLen * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 6, buf.readableBytes()); + } + + dictPos += valVarLen; + Modifier[] val = new Modifier[valLen]; + + for (int valIdx = 0; valIdx < valLen; valIdx++) { + val[valIdx] = Modifier.deserialize(buf, dictPos); + dictPos += Modifier.computeBytesConsumed(buf, dictPos); + } + + if (obj.statModifiers.put(key, val) != null) { + throw ProtocolException.duplicateKey("statModifiers", key); + } + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + cosmeticsToHideCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("CosmeticsToHide", varPos0 + varIntLen + cosmeticsToHideCount * 1, buf.readableBytes()); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 18); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("DamageResistance", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 30 + varPosBase2; + int damageResistanceCount = VarInt.peek(buf, varPos2); + if (damageResistanceCount < 0) { + throw ProtocolException.invalidVarInt("DamageResistance"); + } + + int varIntLenx = VarInt.size(damageResistanceCount); + if (damageResistanceCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("DamageResistance", damageResistanceCount, 4096000); + } + + obj.damageResistance = new HashMap<>(damageResistanceCount); + int dictPos = varPos2 + varIntLenx; + + for (int i = 0; i < damageResistanceCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String keyx = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + int valLenx = VarInt.peek(buf, dictPos); + if (valLenx < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLenx = VarInt.size(valLenx); + if (valLenx > 64) { + throw ProtocolException.arrayTooLong("val", valLenx, 64); + } + + if (dictPos + valVarLenx + valLenx * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLenx + valLenx * 6, buf.readableBytes()); + } + + dictPos += valVarLenx; + Modifier[] val = new Modifier[valLenx]; + + for (int valIdx = 0; valIdx < valLenx; valIdx++) { + val[valIdx] = Modifier.deserialize(buf, dictPos); + dictPos += Modifier.computeBytesConsumed(buf, dictPos); + } + + if (obj.damageResistance.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("damageResistance", keyx); + } + } } - obj.cosmeticsToHide = new Cosmetic[cosmeticsToHideCount]; - int elemPos = varPos0 + varIntLen; + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 22); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("DamageEnhancement", varPosBase3, buf.readableBytes()); + } - for (int i = 0; i < cosmeticsToHideCount; i++) { - obj.cosmeticsToHide[i] = Cosmetic.fromValue(buf.getByte(elemPos)); - elemPos++; + int varPos3 = offset + 30 + varPosBase3; + int damageEnhancementCount = VarInt.peek(buf, varPos3); + if (damageEnhancementCount < 0) { + throw ProtocolException.invalidVarInt("DamageEnhancement"); + } + + int varIntLenx = VarInt.size(damageEnhancementCount); + if (damageEnhancementCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("DamageEnhancement", damageEnhancementCount, 4096000); + } + + obj.damageEnhancement = new HashMap<>(damageEnhancementCount); + int dictPos = varPos3 + varIntLenx; + + for (int i = 0; i < damageEnhancementCount; i++) { + int keyLenx = VarInt.peek(buf, dictPos); + if (keyLenx < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLenx = VarInt.size(keyLenx); + if (keyLenx > 4096000) { + throw ProtocolException.stringTooLong("key", keyLenx, 4096000); + } + + if (dictPos + keyVarLenx + keyLenx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLenx + keyLenx, buf.readableBytes()); + } + + String keyxx = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLenx + keyLenx; + int valLenxx = VarInt.peek(buf, dictPos); + if (valLenxx < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLenxx = VarInt.size(valLenxx); + if (valLenxx > 64) { + throw ProtocolException.arrayTooLong("val", valLenxx, 64); + } + + if (dictPos + valVarLenxx + valLenxx * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLenxx + valLenxx * 6, buf.readableBytes()); + } + + dictPos += valVarLenxx; + Modifier[] val = new Modifier[valLenxx]; + + for (int valIdx = 0; valIdx < valLenxx; valIdx++) { + val[valIdx] = Modifier.deserialize(buf, dictPos); + dictPos += Modifier.computeBytesConsumed(buf, dictPos); + } + + if (obj.damageEnhancement.put(keyxx, val) != null) { + throw ProtocolException.duplicateKey("damageEnhancement", keyxx); + } + } } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 26); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("DamageClassEnhancement", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 30 + varPosBase4; + int damageClassEnhancementCount = VarInt.peek(buf, varPos4); + if (damageClassEnhancementCount < 0) { + throw ProtocolException.invalidVarInt("DamageClassEnhancement"); + } + + int varIntLenx = VarInt.size(damageClassEnhancementCount); + if (damageClassEnhancementCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("DamageClassEnhancement", damageClassEnhancementCount, 4096000); + } + + obj.damageClassEnhancement = new HashMap<>(damageClassEnhancementCount); + int dictPos = varPos4 + varIntLenx; + + for (int i = 0; i < damageClassEnhancementCount; i++) { + int keyLenxx = VarInt.peek(buf, dictPos); + if (keyLenxx < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLenxx = VarInt.size(keyLenxx); + if (keyLenxx > 4096000) { + throw ProtocolException.stringTooLong("key", keyLenxx, 4096000); + } + + if (dictPos + keyVarLenxx + keyLenxx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLenxx + keyLenxx, buf.readableBytes()); + } + + String keyxxx = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLenxx + keyLenxx; + int valLenxxx = VarInt.peek(buf, dictPos); + if (valLenxxx < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLenxxx = VarInt.size(valLenxxx); + if (valLenxxx > 64) { + throw ProtocolException.arrayTooLong("val", valLenxxx, 64); + } + + if (dictPos + valVarLenxxx + valLenxxx * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLenxxx + valLenxxx * 6, buf.readableBytes()); + } + + dictPos += valVarLenxxx; + Modifier[] val = new Modifier[valLenxxx]; + + for (int valIdx = 0; valIdx < valLenxxx; valIdx++) { + val[valIdx] = Modifier.deserialize(buf, dictPos); + dictPos += Modifier.computeBytesConsumed(buf, dictPos); + } + + if (obj.damageClassEnhancement.put(keyxxx, val) != null) { + throw ProtocolException.duplicateKey("damageClassEnhancement", keyxxx); + } + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 30 + buf.getIntLE(offset + 14); - int statModifiersCount = VarInt.peek(buf, varPos1); - if (statModifiersCount < 0) { - throw ProtocolException.negativeLength("StatModifiers", statModifiersCount); - } - - if (statModifiersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.statModifiers = new HashMap<>(statModifiersCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < statModifiersCount; i++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - int valLen = VarInt.peek(buf, dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); - } - - if (valLen > 64) { - throw ProtocolException.arrayTooLong("val", valLen, 64); - } - - int valVarLen = VarInt.length(buf, dictPos); - if (dictPos + valVarLen + valLen * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 6, buf.readableBytes()); - } - - dictPos += valVarLen; - Modifier[] val = new Modifier[valLen]; - - for (int valIdx = 0; valIdx < valLen; valIdx++) { - val[valIdx] = Modifier.deserialize(buf, dictPos); - dictPos += Modifier.computeBytesConsumed(buf, dictPos); - } - - if (obj.statModifiers.put(key, val) != null) { - throw ProtocolException.duplicateKey("statModifiers", key); - } - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 30 + buf.getIntLE(offset + 18); - int damageResistanceCount = VarInt.peek(buf, varPos2); - if (damageResistanceCount < 0) { - throw ProtocolException.negativeLength("DamageResistance", damageResistanceCount); - } - - if (damageResistanceCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("DamageResistance", damageResistanceCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - obj.damageResistance = new HashMap<>(damageResistanceCount); - int dictPos = varPos2 + varIntLen; - - for (int i = 0; i < damageResistanceCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); - } - - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); - } - - int keyVarLen = VarInt.length(buf, dictPos); - String keyx = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - int valLenx = VarInt.peek(buf, dictPos); - if (valLenx < 0) { - throw ProtocolException.negativeLength("val", valLenx); - } - - if (valLenx > 64) { - throw ProtocolException.arrayTooLong("val", valLenx, 64); - } - - int valVarLenx = VarInt.length(buf, dictPos); - if (dictPos + valVarLenx + valLenx * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLenx + valLenx * 6, buf.readableBytes()); - } - - dictPos += valVarLenx; - Modifier[] val = new Modifier[valLenx]; - - for (int valIdx = 0; valIdx < valLenx; valIdx++) { - val[valIdx] = Modifier.deserialize(buf, dictPos); - dictPos += Modifier.computeBytesConsumed(buf, dictPos); - } - - if (obj.damageResistance.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("damageResistance", keyx); - } - } - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 30 + buf.getIntLE(offset + 22); - int damageEnhancementCount = VarInt.peek(buf, varPos3); - if (damageEnhancementCount < 0) { - throw ProtocolException.negativeLength("DamageEnhancement", damageEnhancementCount); - } - - if (damageEnhancementCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("DamageEnhancement", damageEnhancementCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - obj.damageEnhancement = new HashMap<>(damageEnhancementCount); - int dictPos = varPos3 + varIntLen; - - for (int i = 0; i < damageEnhancementCount; i++) { - int keyLenx = VarInt.peek(buf, dictPos); - if (keyLenx < 0) { - throw ProtocolException.negativeLength("key", keyLenx); - } - - if (keyLenx > 4096000) { - throw ProtocolException.stringTooLong("key", keyLenx, 4096000); - } - - int keyVarLenx = VarInt.length(buf, dictPos); - String keyxx = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLenx + keyLenx; - int valLenxx = VarInt.peek(buf, dictPos); - if (valLenxx < 0) { - throw ProtocolException.negativeLength("val", valLenxx); - } - - if (valLenxx > 64) { - throw ProtocolException.arrayTooLong("val", valLenxx, 64); - } - - int valVarLenxx = VarInt.length(buf, dictPos); - if (dictPos + valVarLenxx + valLenxx * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLenxx + valLenxx * 6, buf.readableBytes()); - } - - dictPos += valVarLenxx; - Modifier[] val = new Modifier[valLenxx]; - - for (int valIdx = 0; valIdx < valLenxx; valIdx++) { - val[valIdx] = Modifier.deserialize(buf, dictPos); - dictPos += Modifier.computeBytesConsumed(buf, dictPos); - } - - if (obj.damageEnhancement.put(keyxx, val) != null) { - throw ProtocolException.duplicateKey("damageEnhancement", keyxx); - } - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 30 + buf.getIntLE(offset + 26); - int damageClassEnhancementCount = VarInt.peek(buf, varPos4); - if (damageClassEnhancementCount < 0) { - throw ProtocolException.negativeLength("DamageClassEnhancement", damageClassEnhancementCount); - } - - if (damageClassEnhancementCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("DamageClassEnhancement", damageClassEnhancementCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos4); - obj.damageClassEnhancement = new HashMap<>(damageClassEnhancementCount); - int dictPos = varPos4 + varIntLen; - - for (int i = 0; i < damageClassEnhancementCount; i++) { - int keyLenxx = VarInt.peek(buf, dictPos); - if (keyLenxx < 0) { - throw ProtocolException.negativeLength("key", keyLenxx); - } - - if (keyLenxx > 4096000) { - throw ProtocolException.stringTooLong("key", keyLenxx, 4096000); - } - - int keyVarLenxx = VarInt.length(buf, dictPos); - String keyxxx = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLenxx + keyLenxx; - int valLenxxx = VarInt.peek(buf, dictPos); - if (valLenxxx < 0) { - throw ProtocolException.negativeLength("val", valLenxxx); - } - - if (valLenxxx > 64) { - throw ProtocolException.arrayTooLong("val", valLenxxx, 64); - } - - int valVarLenxxx = VarInt.length(buf, dictPos); - if (dictPos + valVarLenxxx + valLenxxx * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLenxxx + valLenxxx * 6, buf.readableBytes()); - } - - dictPos += valVarLenxxx; - Modifier[] val = new Modifier[valLenxxx]; - - for (int valIdx = 0; valIdx < valLenxxx; valIdx++) { - val[valIdx] = Modifier.deserialize(buf, dictPos); - dictPos += Modifier.computeBytesConsumed(buf, dictPos); - } - - if (obj.damageClassEnhancement.put(keyxxx, val) != null) { - throw ProtocolException.duplicateKey("damageClassEnhancement", keyxxx); - } - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -317,9 +358,13 @@ public class ItemArmor { int maxEnd = 30; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 10); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("CosmeticsToHide", fieldOffset0, maxEnd); + } + int pos0 = offset + 30 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 1; + pos0 += VarInt.size(arrLen) + arrLen * 1; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -327,14 +372,18 @@ public class ItemArmor { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 14); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("StatModifiers", fieldOffset1, maxEnd); + } + int pos1 = offset + 30 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 += 4; int al = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(al); for (int j = 0; j < al; j++) { pos1 += Modifier.computeBytesConsumed(buf, pos1); @@ -348,15 +397,19 @@ public class ItemArmor { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 18); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("DamageResistance", fieldOffset2, maxEnd); + } + int pos2 = offset + 30 + fieldOffset2; int dictLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(sl); for (int j = 0; j < sl; j++) { pos2 += Modifier.computeBytesConsumed(buf, pos2); @@ -370,15 +423,19 @@ public class ItemArmor { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 22); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("DamageEnhancement", fieldOffset3, maxEnd); + } + int pos3 = offset + 30 + fieldOffset3; int dictLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(sl); for (int j = 0; j < sl; j++) { pos3 += Modifier.computeBytesConsumed(buf, pos3); @@ -392,15 +449,19 @@ public class ItemArmor { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 26); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("DamageClassEnhancement", fieldOffset4, maxEnd); + } + int pos4 = offset + 30 + fieldOffset4; int dictLen = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4); + pos4 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4); + pos4 += VarInt.size(sl); for (int j = 0; j < sl; j++) { pos4 += Modifier.computeBytesConsumed(buf, pos4); @@ -602,228 +663,221 @@ public class ItemArmor { return ValidationResult.error("Buffer too small: expected at least 30 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int cosmeticsToHideOffset = buffer.getIntLE(offset + 10); - if (cosmeticsToHideOffset < 0) { - return ValidationResult.error("Invalid offset for CosmeticsToHide"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid ItemArmorSlot value for ArmorSlot"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 30) { + return ValidationResult.error("Invalid offset for CosmeticsToHide"); + } + + int pos = offset + 30 + v; + int cosmeticsToHideCount = VarInt.peek(buffer, pos); + if (cosmeticsToHideCount < 0) { + return ValidationResult.error("Invalid array count for CosmeticsToHide"); + } + + if (cosmeticsToHideCount > 4096000) { + return ValidationResult.error("CosmeticsToHide exceeds max length 4096000"); + } + + pos += VarInt.size(cosmeticsToHideCount); + if (pos + cosmeticsToHideCount * 1L > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading CosmeticsToHide"); + } + + for (int i = 0; i < cosmeticsToHideCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 13) { + return ValidationResult.error("Invalid Cosmetic value for CosmeticsToHide[i]"); + } + + pos++; + } } - int pos = offset + 30 + cosmeticsToHideOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for CosmeticsToHide"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 14); + if (v < 0 || v > buffer.writerIndex() - offset - 30) { + return ValidationResult.error("Invalid offset for StatModifiers"); + } + + int posx = offset + 30 + v; + int statModifiersCount = VarInt.peek(buffer, posx); + if (statModifiersCount < 0) { + return ValidationResult.error("Invalid dictionary count for StatModifiers"); + } + + if (statModifiersCount > 4096000) { + return ValidationResult.error("StatModifiers exceeds max length 4096000"); + } + + posx += VarInt.size(statModifiersCount); + + for (int i = 0; i < statModifiersCount; i++) { + posx += 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + int valueArrCount = VarInt.peek(buffer, posx); + if (valueArrCount < 0) { + return ValidationResult.error("Invalid array count for value"); + } + + posx += VarInt.size(valueArrCount); + + for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { + posx += 6; + } + } } - int cosmeticsToHideCount = VarInt.peek(buffer, pos); - if (cosmeticsToHideCount < 0) { - return ValidationResult.error("Invalid array count for CosmeticsToHide"); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 18); + if (v < 0 || v > buffer.writerIndex() - offset - 30) { + return ValidationResult.error("Invalid offset for DamageResistance"); + } + + int posxx = offset + 30 + v; + int damageResistanceCount = VarInt.peek(buffer, posxx); + if (damageResistanceCount < 0) { + return ValidationResult.error("Invalid dictionary count for DamageResistance"); + } + + if (damageResistanceCount > 4096000) { + return ValidationResult.error("DamageResistance exceeds max length 4096000"); + } + + posxx += VarInt.size(damageResistanceCount); + + for (int i = 0; i < damageResistanceCount; i++) { + int keyLen = VarInt.peek(buffer, posxx); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxx += VarInt.size(keyLen); + posxx += keyLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + int valueArrCount = VarInt.peek(buffer, posxx); + if (valueArrCount < 0) { + return ValidationResult.error("Invalid array count for value"); + } + + posxx += VarInt.size(valueArrCount); + + for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { + posxx += 6; + } + } } - if (cosmeticsToHideCount > 4096000) { - return ValidationResult.error("CosmeticsToHide exceeds max length 4096000"); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 22); + if (v < 0 || v > buffer.writerIndex() - offset - 30) { + return ValidationResult.error("Invalid offset for DamageEnhancement"); + } + + int posxxx = offset + 30 + v; + int damageEnhancementCount = VarInt.peek(buffer, posxxx); + if (damageEnhancementCount < 0) { + return ValidationResult.error("Invalid dictionary count for DamageEnhancement"); + } + + if (damageEnhancementCount > 4096000) { + return ValidationResult.error("DamageEnhancement exceeds max length 4096000"); + } + + posxxx += VarInt.size(damageEnhancementCount); + + for (int i = 0; i < damageEnhancementCount; i++) { + int keyLenx = VarInt.peek(buffer, posxxx); + if (keyLenx < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLenx > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxx += VarInt.size(keyLenx); + posxxx += keyLenx; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + int valueArrCount = VarInt.peek(buffer, posxxx); + if (valueArrCount < 0) { + return ValidationResult.error("Invalid array count for value"); + } + + posxxx += VarInt.size(valueArrCount); + + for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { + posxxx += 6; + } + } } - pos += VarInt.length(buffer, pos); - pos += cosmeticsToHideCount * 1; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading CosmeticsToHide"); + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 26); + if (v < 0 || v > buffer.writerIndex() - offset - 30) { + return ValidationResult.error("Invalid offset for DamageClassEnhancement"); + } + + int posxxxx = offset + 30 + v; + int damageClassEnhancementCount = VarInt.peek(buffer, posxxxx); + if (damageClassEnhancementCount < 0) { + return ValidationResult.error("Invalid dictionary count for DamageClassEnhancement"); + } + + if (damageClassEnhancementCount > 4096000) { + return ValidationResult.error("DamageClassEnhancement exceeds max length 4096000"); + } + + posxxxx += VarInt.size(damageClassEnhancementCount); + + for (int i = 0; i < damageClassEnhancementCount; i++) { + int keyLenxx = VarInt.peek(buffer, posxxxx); + if (keyLenxx < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLenxx > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxxx += VarInt.size(keyLenxx); + posxxxx += keyLenxx; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + int valueArrCount = VarInt.peek(buffer, posxxxx); + if (valueArrCount < 0) { + return ValidationResult.error("Invalid array count for value"); + } + + posxxxx += VarInt.size(valueArrCount); + + for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { + posxxxx += 6; + } + } } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int statModifiersOffset = buffer.getIntLE(offset + 14); - if (statModifiersOffset < 0) { - return ValidationResult.error("Invalid offset for StatModifiers"); - } - - int posx = offset + 30 + statModifiersOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for StatModifiers"); - } - - int statModifiersCount = VarInt.peek(buffer, posx); - if (statModifiersCount < 0) { - return ValidationResult.error("Invalid dictionary count for StatModifiers"); - } - - if (statModifiersCount > 4096000) { - return ValidationResult.error("StatModifiers exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < statModifiersCount; i++) { - posx += 4; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - int valueArrCount = VarInt.peek(buffer, posx); - if (valueArrCount < 0) { - return ValidationResult.error("Invalid array count for value"); - } - - posx += VarInt.length(buffer, posx); - - for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - posx += 6; - } - } - } - - if ((nullBits & 4) != 0) { - int damageResistanceOffset = buffer.getIntLE(offset + 18); - if (damageResistanceOffset < 0) { - return ValidationResult.error("Invalid offset for DamageResistance"); - } - - int posxx = offset + 30 + damageResistanceOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DamageResistance"); - } - - int damageResistanceCount = VarInt.peek(buffer, posxx); - if (damageResistanceCount < 0) { - return ValidationResult.error("Invalid dictionary count for DamageResistance"); - } - - if (damageResistanceCount > 4096000) { - return ValidationResult.error("DamageResistance exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - - for (int i = 0; i < damageResistanceCount; i++) { - int keyLen = VarInt.peek(buffer, posxx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += keyLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - int valueArrCount = VarInt.peek(buffer, posxx); - if (valueArrCount < 0) { - return ValidationResult.error("Invalid array count for value"); - } - - posxx += VarInt.length(buffer, posxx); - - for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - posxx += 6; - } - } - } - - if ((nullBits & 8) != 0) { - int damageEnhancementOffset = buffer.getIntLE(offset + 22); - if (damageEnhancementOffset < 0) { - return ValidationResult.error("Invalid offset for DamageEnhancement"); - } - - int posxxx = offset + 30 + damageEnhancementOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DamageEnhancement"); - } - - int damageEnhancementCount = VarInt.peek(buffer, posxxx); - if (damageEnhancementCount < 0) { - return ValidationResult.error("Invalid dictionary count for DamageEnhancement"); - } - - if (damageEnhancementCount > 4096000) { - return ValidationResult.error("DamageEnhancement exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - - for (int i = 0; i < damageEnhancementCount; i++) { - int keyLenx = VarInt.peek(buffer, posxxx); - if (keyLenx < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLenx > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += keyLenx; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - int valueArrCount = VarInt.peek(buffer, posxxx); - if (valueArrCount < 0) { - return ValidationResult.error("Invalid array count for value"); - } - - posxxx += VarInt.length(buffer, posxxx); - - for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - posxxx += 6; - } - } - } - - if ((nullBits & 16) != 0) { - int damageClassEnhancementOffset = buffer.getIntLE(offset + 26); - if (damageClassEnhancementOffset < 0) { - return ValidationResult.error("Invalid offset for DamageClassEnhancement"); - } - - int posxxxx = offset + 30 + damageClassEnhancementOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DamageClassEnhancement"); - } - - int damageClassEnhancementCount = VarInt.peek(buffer, posxxxx); - if (damageClassEnhancementCount < 0) { - return ValidationResult.error("Invalid dictionary count for DamageClassEnhancement"); - } - - if (damageClassEnhancementCount > 4096000) { - return ValidationResult.error("DamageClassEnhancement exceeds max length 4096000"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - - for (int i = 0; i < damageClassEnhancementCount; i++) { - int keyLenxx = VarInt.peek(buffer, posxxxx); - if (keyLenxx < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLenxx > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - posxxxx += keyLenxx; - if (posxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - int valueArrCount = VarInt.peek(buffer, posxxxx); - if (valueArrCount < 0) { - return ValidationResult.error("Invalid array count for value"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - - for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - posxxxx += 6; - } - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ItemBase.java b/src/com/hypixel/hytale/protocol/ItemBase.java index c3556007..00237559 100644 --- a/src/com/hypixel/hytale/protocol/ItemBase.java +++ b/src/com/hypixel/hytale/protocol/ItemBase.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.VarInt; +import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolState; import io.netty.buffer.ByteBuf; import java.util.Arrays; import java.util.HashMap; @@ -14,10 +15,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class ItemBase { - public static final int NULLABLE_BIT_FIELD_SIZE = 4; - public static final int FIXED_BLOCK_SIZE = 147; - public static final int VARIABLE_FIELD_COUNT = 26; - public static final int VARIABLE_BLOCK_START = 251; + public static final int NULLABLE_BIT_FIELD_SIZE = 5; + public static final int FIXED_BLOCK_SIZE = 148; + public static final int VARIABLE_FIELD_COUNT = 28; + public static final int VARIABLE_BLOCK_START = 260; public static final int MAX_SIZE = 1677721600; @Nullable public String id; @@ -59,7 +60,7 @@ public class ItemBase { @Nullable public BlockSelectorToolData blockSelectorTool; @Nullable - public ItemBuilderToolData builderToolData; + public BuilderToolState builderToolData; @Nullable public ItemEntityConfig itemEntity; @Nullable @@ -67,6 +68,8 @@ public class ItemBase { @Nullable public String[] categories; @Nullable + public String subCategory; + @Nullable public ModelParticle[] particles; @Nullable public ModelParticle[] firstPersonParticles; @@ -95,6 +98,8 @@ public class ItemBase { public ItemPullbackConfiguration pullbackConfig; public boolean clipsGeometry; public boolean renderDeployablePreview; + @Nullable + public ItemHudUI[] hudUI; public ItemBase() { } @@ -124,10 +129,11 @@ public class ItemBase { @Nullable ItemGlider gliderConfig, @Nullable ItemUtility utility, @Nullable BlockSelectorToolData blockSelectorTool, - @Nullable ItemBuilderToolData builderToolData, + @Nullable BuilderToolState builderToolData, @Nullable ItemEntityConfig itemEntity, @Nullable String set, @Nullable String[] categories, + @Nullable String subCategory, @Nullable ModelParticle[] particles, @Nullable ModelParticle[] firstPersonParticles, @Nullable ModelTrail[] trails, @@ -144,7 +150,8 @@ public class ItemBase { @Nullable int[] displayEntityStatsHUD, @Nullable ItemPullbackConfiguration pullbackConfig, boolean clipsGeometry, - boolean renderDeployablePreview + boolean renderDeployablePreview, + @Nullable ItemHudUI[] hudUI ) { this.id = id; this.model = model; @@ -174,6 +181,7 @@ public class ItemBase { this.itemEntity = itemEntity; this.set = set; this.categories = categories; + this.subCategory = subCategory; this.particles = particles; this.firstPersonParticles = firstPersonParticles; this.trails = trails; @@ -191,6 +199,7 @@ public class ItemBase { this.pullbackConfig = pullbackConfig; this.clipsGeometry = clipsGeometry; this.renderDeployablePreview = renderDeployablePreview; + this.hudUI = hudUI; } public ItemBase(@Nonnull ItemBase other) { @@ -222,6 +231,7 @@ public class ItemBase { this.itemEntity = other.itemEntity; this.set = other.set; this.categories = other.categories; + this.subCategory = other.subCategory; this.particles = other.particles; this.firstPersonParticles = other.firstPersonParticles; this.trails = other.trails; @@ -239,555 +249,822 @@ public class ItemBase { this.pullbackConfig = other.pullbackConfig; this.clipsGeometry = other.clipsGeometry; this.renderDeployablePreview = other.renderDeployablePreview; + this.hudUI = other.hudUI; } @Nonnull public static ItemBase deserialize(@Nonnull ByteBuf buf, int offset) { - ItemBase obj = new ItemBase(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 4); - obj.scale = buf.getFloatLE(offset + 4); - obj.usePlayerAnimations = buf.getByte(offset + 8) != 0; - obj.maxStack = buf.getIntLE(offset + 9); - obj.reticleIndex = buf.getIntLE(offset + 13); - if ((nullBits[0] & 1) != 0) { - obj.iconProperties = AssetIconProperties.deserialize(buf, offset + 17); - } - - obj.itemLevel = buf.getIntLE(offset + 42); - obj.qualityIndex = buf.getIntLE(offset + 46); - obj.consumable = buf.getByte(offset + 50) != 0; - obj.variant = buf.getByte(offset + 51) != 0; - obj.blockId = buf.getIntLE(offset + 52); - if ((nullBits[0] & 2) != 0) { - obj.gliderConfig = ItemGlider.deserialize(buf, offset + 56); - } - - if ((nullBits[0] & 4) != 0) { - obj.blockSelectorTool = BlockSelectorToolData.deserialize(buf, offset + 72); - } - - if ((nullBits[0] & 8) != 0) { - obj.light = ColorLight.deserialize(buf, offset + 76); - } - - obj.durability = buf.getDoubleLE(offset + 80); - obj.soundEventIndex = buf.getIntLE(offset + 88); - obj.itemSoundSetIndex = buf.getIntLE(offset + 92); - if ((nullBits[0] & 16) != 0) { - obj.pullbackConfig = ItemPullbackConfiguration.deserialize(buf, offset + 96); - } - - obj.clipsGeometry = buf.getByte(offset + 145) != 0; - obj.renderDeployablePreview = buf.getByte(offset + 146) != 0; - if ((nullBits[0] & 32) != 0) { - int varPos0 = offset + 251 + buf.getIntLE(offset + 147); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 260) { + throw ProtocolException.bufferTooSmall("ItemBase", 260, buf.readableBytes() - offset); + } else { + ItemBase obj = new ItemBase(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 5); + obj.scale = buf.getFloatLE(offset + 5); + obj.usePlayerAnimations = buf.getByte(offset + 9) != 0; + obj.maxStack = buf.getIntLE(offset + 10); + obj.reticleIndex = buf.getIntLE(offset + 14); + if ((nullBits[0] & 1) != 0) { + obj.iconProperties = AssetIconProperties.deserialize(buf, offset + 18); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + obj.itemLevel = buf.getIntLE(offset + 43); + obj.qualityIndex = buf.getIntLE(offset + 47); + obj.consumable = buf.getByte(offset + 51) != 0; + obj.variant = buf.getByte(offset + 52) != 0; + obj.blockId = buf.getIntLE(offset + 53); + if ((nullBits[0] & 2) != 0) { + obj.gliderConfig = ItemGlider.deserialize(buf, offset + 57); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits[0] & 64) != 0) { - int varPos1 = offset + 251 + buf.getIntLE(offset + 151); - int modelLen = VarInt.peek(buf, varPos1); - if (modelLen < 0) { - throw ProtocolException.negativeLength("Model", modelLen); + if ((nullBits[0] & 4) != 0) { + obj.blockSelectorTool = BlockSelectorToolData.deserialize(buf, offset + 73); } - if (modelLen > 4096000) { - throw ProtocolException.stringTooLong("Model", modelLen, 4096000); + if ((nullBits[0] & 8) != 0) { + obj.light = ColorLight.deserialize(buf, offset + 77); } - obj.model = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits[0] & 128) != 0) { - int varPos2 = offset + 251 + buf.getIntLE(offset + 155); - int textureLen = VarInt.peek(buf, varPos2); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); + obj.durability = buf.getDoubleLE(offset + 81); + obj.soundEventIndex = buf.getIntLE(offset + 89); + obj.itemSoundSetIndex = buf.getIntLE(offset + 93); + if ((nullBits[0] & 16) != 0) { + obj.pullbackConfig = ItemPullbackConfiguration.deserialize(buf, offset + 97); } - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } - - obj.texture = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits[1] & 1) != 0) { - int varPos3 = offset + 251 + buf.getIntLE(offset + 159); - int animationLen = VarInt.peek(buf, varPos3); - if (animationLen < 0) { - throw ProtocolException.negativeLength("Animation", animationLen); - } - - if (animationLen > 4096000) { - throw ProtocolException.stringTooLong("Animation", animationLen, 4096000); - } - - obj.animation = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits[1] & 2) != 0) { - int varPos4 = offset + 251 + buf.getIntLE(offset + 163); - int playerAnimationsIdLen = VarInt.peek(buf, varPos4); - if (playerAnimationsIdLen < 0) { - throw ProtocolException.negativeLength("PlayerAnimationsId", playerAnimationsIdLen); - } - - if (playerAnimationsIdLen > 4096000) { - throw ProtocolException.stringTooLong("PlayerAnimationsId", playerAnimationsIdLen, 4096000); - } - - obj.playerAnimationsId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - if ((nullBits[1] & 4) != 0) { - int varPos5 = offset + 251 + buf.getIntLE(offset + 167); - int iconLen = VarInt.peek(buf, varPos5); - if (iconLen < 0) { - throw ProtocolException.negativeLength("Icon", iconLen); - } - - if (iconLen > 4096000) { - throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); - } - - obj.icon = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - if ((nullBits[1] & 8) != 0) { - int varPos6 = offset + 251 + buf.getIntLE(offset + 171); - obj.translationProperties = ItemTranslationProperties.deserialize(buf, varPos6); - } - - if ((nullBits[1] & 16) != 0) { - int varPos7 = offset + 251 + buf.getIntLE(offset + 175); - int resourceTypesCount = VarInt.peek(buf, varPos7); - if (resourceTypesCount < 0) { - throw ProtocolException.negativeLength("ResourceTypes", resourceTypesCount); - } - - if (resourceTypesCount > 4096000) { - throw ProtocolException.arrayTooLong("ResourceTypes", resourceTypesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos7); - if (varPos7 + varIntLen + resourceTypesCount * 5L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ResourceTypes", varPos7 + varIntLen + resourceTypesCount * 5, buf.readableBytes()); - } - - obj.resourceTypes = new ItemResourceType[resourceTypesCount]; - int elemPos = varPos7 + varIntLen; - - for (int i = 0; i < resourceTypesCount; i++) { - obj.resourceTypes[i] = ItemResourceType.deserialize(buf, elemPos); - elemPos += ItemResourceType.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[1] & 32) != 0) { - int varPos8 = offset + 251 + buf.getIntLE(offset + 179); - obj.tool = ItemTool.deserialize(buf, varPos8); - } - - if ((nullBits[1] & 64) != 0) { - int varPos9 = offset + 251 + buf.getIntLE(offset + 183); - obj.weapon = ItemWeapon.deserialize(buf, varPos9); - } - - if ((nullBits[1] & 128) != 0) { - int varPos10 = offset + 251 + buf.getIntLE(offset + 187); - obj.armor = ItemArmor.deserialize(buf, varPos10); - } - - if ((nullBits[2] & 1) != 0) { - int varPos11 = offset + 251 + buf.getIntLE(offset + 191); - obj.utility = ItemUtility.deserialize(buf, varPos11); - } - - if ((nullBits[2] & 2) != 0) { - int varPos12 = offset + 251 + buf.getIntLE(offset + 195); - obj.builderToolData = ItemBuilderToolData.deserialize(buf, varPos12); - } - - if ((nullBits[2] & 4) != 0) { - int varPos13 = offset + 251 + buf.getIntLE(offset + 199); - obj.itemEntity = ItemEntityConfig.deserialize(buf, varPos13); - } - - if ((nullBits[2] & 8) != 0) { - int varPos14 = offset + 251 + buf.getIntLE(offset + 203); - int setLen = VarInt.peek(buf, varPos14); - if (setLen < 0) { - throw ProtocolException.negativeLength("Set", setLen); - } - - if (setLen > 4096000) { - throw ProtocolException.stringTooLong("Set", setLen, 4096000); - } - - obj.set = PacketIO.readVarString(buf, varPos14, PacketIO.UTF8); - } - - if ((nullBits[2] & 16) != 0) { - int varPos15 = offset + 251 + buf.getIntLE(offset + 207); - int categoriesCount = VarInt.peek(buf, varPos15); - if (categoriesCount < 0) { - throw ProtocolException.negativeLength("Categories", categoriesCount); - } - - if (categoriesCount > 4096000) { - throw ProtocolException.arrayTooLong("Categories", categoriesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos15); - if (varPos15 + varIntLen + categoriesCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Categories", varPos15 + varIntLen + categoriesCount * 1, buf.readableBytes()); - } - - obj.categories = new String[categoriesCount]; - int elemPos = varPos15 + varIntLen; - - for (int i = 0; i < categoriesCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("categories[" + i + "]", strLen); + obj.clipsGeometry = buf.getByte(offset + 146) != 0; + obj.renderDeployablePreview = buf.getByte(offset + 147) != 0; + if ((nullBits[0] & 32) != 0) { + int varPosBase0 = buf.getIntLE(offset + 148); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("categories[" + i + "]", strLen, 4096000); + int varPos0 = offset + 260 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); } - int strVarLen = VarInt.length(buf, elemPos); - obj.categories[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } - - if ((nullBits[2] & 32) != 0) { - int varPos16 = offset + 251 + buf.getIntLE(offset + 211); - int particlesCount = VarInt.peek(buf, varPos16); - if (particlesCount < 0) { - throw ProtocolException.negativeLength("Particles", particlesCount); - } - - if (particlesCount > 4096000) { - throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos16); - if (varPos16 + varIntLen + particlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Particles", varPos16 + varIntLen + particlesCount * 34, buf.readableBytes()); - } - - obj.particles = new ModelParticle[particlesCount]; - int elemPos = varPos16 + varIntLen; - - for (int i = 0; i < particlesCount; i++) { - obj.particles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[2] & 64) != 0) { - int varPos17 = offset + 251 + buf.getIntLE(offset + 215); - int firstPersonParticlesCount = VarInt.peek(buf, varPos17); - if (firstPersonParticlesCount < 0) { - throw ProtocolException.negativeLength("FirstPersonParticles", firstPersonParticlesCount); - } - - if (firstPersonParticlesCount > 4096000) { - throw ProtocolException.arrayTooLong("FirstPersonParticles", firstPersonParticlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos17); - if (varPos17 + varIntLen + firstPersonParticlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FirstPersonParticles", varPos17 + varIntLen + firstPersonParticlesCount * 34, buf.readableBytes()); - } - - obj.firstPersonParticles = new ModelParticle[firstPersonParticlesCount]; - int elemPos = varPos17 + varIntLen; - - for (int i = 0; i < firstPersonParticlesCount; i++) { - obj.firstPersonParticles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[2] & 128) != 0) { - int varPos18 = offset + 251 + buf.getIntLE(offset + 219); - int trailsCount = VarInt.peek(buf, varPos18); - if (trailsCount < 0) { - throw ProtocolException.negativeLength("Trails", trailsCount); - } - - if (trailsCount > 4096000) { - throw ProtocolException.arrayTooLong("Trails", trailsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos18); - if (varPos18 + varIntLen + trailsCount * 27L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Trails", varPos18 + varIntLen + trailsCount * 27, buf.readableBytes()); - } - - obj.trails = new ModelTrail[trailsCount]; - int elemPos = varPos18 + varIntLen; - - for (int i = 0; i < trailsCount; i++) { - obj.trails[i] = ModelTrail.deserialize(buf, elemPos); - elemPos += ModelTrail.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[3] & 1) != 0) { - int varPos19 = offset + 251 + buf.getIntLE(offset + 223); - int interactionsCount = VarInt.peek(buf, varPos19); - if (interactionsCount < 0) { - throw ProtocolException.negativeLength("Interactions", interactionsCount); - } - - if (interactionsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos19); - obj.interactions = new HashMap<>(interactionsCount); - int dictPos = varPos19 + varIntLen; - - for (int i = 0; i < interactionsCount; i++) { - InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.interactions.put(key, val) != null) { - throw ProtocolException.duplicateKey("interactions", key); - } - } - } - - if ((nullBits[3] & 2) != 0) { - int varPos20 = offset + 251 + buf.getIntLE(offset + 227); - int interactionVarsCount = VarInt.peek(buf, varPos20); - if (interactionVarsCount < 0) { - throw ProtocolException.negativeLength("InteractionVars", interactionVarsCount); - } - - if (interactionVarsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("InteractionVars", interactionVarsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos20); - obj.interactionVars = new HashMap<>(interactionVarsCount); - int dictPos = varPos20 + varIntLen; - - for (int ix = 0; ix < interactionVarsCount; ix++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - int val = buf.getIntLE(dictPos); - dictPos += 4; - if (obj.interactionVars.put(key, val) != null) { - throw ProtocolException.duplicateKey("interactionVars", key); + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits[0] & 64) != 0) { + int varPosBase1 = buf.getIntLE(offset + 152); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Model", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 260 + varPosBase1; + int modelLen = VarInt.peek(buf, varPos1); + if (modelLen < 0) { + throw ProtocolException.invalidVarInt("Model"); + } + + int modelVarIntLen = VarInt.size(modelLen); + if (modelLen > 4096000) { + throw ProtocolException.stringTooLong("Model", modelLen, 4096000); + } + + if (varPos1 + modelVarIntLen + modelLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Model", varPos1 + modelVarIntLen + modelLen, buf.readableBytes()); + } + + obj.model = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase2 = buf.getIntLE(offset + 156); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Texture", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 260 + varPosBase2; + int textureLen = VarInt.peek(buf, varPos2); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarIntLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (varPos2 + textureVarIntLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", varPos2 + textureVarIntLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase3 = buf.getIntLE(offset + 160); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Animation", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 260 + varPosBase3; + int animationLen = VarInt.peek(buf, varPos3); + if (animationLen < 0) { + throw ProtocolException.invalidVarInt("Animation"); + } + + int animationVarIntLen = VarInt.size(animationLen); + if (animationLen > 4096000) { + throw ProtocolException.stringTooLong("Animation", animationLen, 4096000); + } + + if (varPos3 + animationVarIntLen + animationLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Animation", varPos3 + animationVarIntLen + animationLen, buf.readableBytes()); + } + + obj.animation = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits[1] & 2) != 0) { + int varPosBase4 = buf.getIntLE(offset + 164); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("PlayerAnimationsId", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 260 + varPosBase4; + int playerAnimationsIdLen = VarInt.peek(buf, varPos4); + if (playerAnimationsIdLen < 0) { + throw ProtocolException.invalidVarInt("PlayerAnimationsId"); + } + + int playerAnimationsIdVarIntLen = VarInt.size(playerAnimationsIdLen); + if (playerAnimationsIdLen > 4096000) { + throw ProtocolException.stringTooLong("PlayerAnimationsId", playerAnimationsIdLen, 4096000); + } + + if (varPos4 + playerAnimationsIdVarIntLen + playerAnimationsIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("PlayerAnimationsId", varPos4 + playerAnimationsIdVarIntLen + playerAnimationsIdLen, buf.readableBytes()); + } + + obj.playerAnimationsId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + if ((nullBits[1] & 4) != 0) { + int varPosBase5 = buf.getIntLE(offset + 168); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Icon", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 260 + varPosBase5; + int iconLen = VarInt.peek(buf, varPos5); + if (iconLen < 0) { + throw ProtocolException.invalidVarInt("Icon"); + } + + int iconVarIntLen = VarInt.size(iconLen); + if (iconLen > 4096000) { + throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + } + + if (varPos5 + iconVarIntLen + iconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Icon", varPos5 + iconVarIntLen + iconLen, buf.readableBytes()); + } + + obj.icon = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + if ((nullBits[1] & 8) != 0) { + int varPosBase6 = buf.getIntLE(offset + 172); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("TranslationProperties", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 260 + varPosBase6; + obj.translationProperties = ItemTranslationProperties.deserialize(buf, varPos6); + } + + if ((nullBits[1] & 16) != 0) { + int varPosBase7 = buf.getIntLE(offset + 176); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("ResourceTypes", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 260 + varPosBase7; + int resourceTypesCount = VarInt.peek(buf, varPos7); + if (resourceTypesCount < 0) { + throw ProtocolException.invalidVarInt("ResourceTypes"); + } + + int varIntLen = VarInt.size(resourceTypesCount); + if (resourceTypesCount > 4096000) { + throw ProtocolException.arrayTooLong("ResourceTypes", resourceTypesCount, 4096000); + } + + if (varPos7 + varIntLen + resourceTypesCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ResourceTypes", varPos7 + varIntLen + resourceTypesCount * 5, buf.readableBytes()); + } + + obj.resourceTypes = new ItemResourceType[resourceTypesCount]; + int elemPos = varPos7 + varIntLen; + + for (int i = 0; i < resourceTypesCount; i++) { + obj.resourceTypes[i] = ItemResourceType.deserialize(buf, elemPos); + elemPos += ItemResourceType.computeBytesConsumed(buf, elemPos); } } + + if ((nullBits[1] & 32) != 0) { + int varPosBase8 = buf.getIntLE(offset + 180); + if (varPosBase8 < 0 || varPosBase8 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Tool", varPosBase8, buf.readableBytes()); + } + + int varPos8 = offset + 260 + varPosBase8; + obj.tool = ItemTool.deserialize(buf, varPos8); + } + + if ((nullBits[1] & 64) != 0) { + int varPosBase9 = buf.getIntLE(offset + 184); + if (varPosBase9 < 0 || varPosBase9 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Weapon", varPosBase9, buf.readableBytes()); + } + + int varPos9 = offset + 260 + varPosBase9; + obj.weapon = ItemWeapon.deserialize(buf, varPos9); + } + + if ((nullBits[1] & 128) != 0) { + int varPosBase10 = buf.getIntLE(offset + 188); + if (varPosBase10 < 0 || varPosBase10 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Armor", varPosBase10, buf.readableBytes()); + } + + int varPos10 = offset + 260 + varPosBase10; + obj.armor = ItemArmor.deserialize(buf, varPos10); + } + + if ((nullBits[2] & 1) != 0) { + int varPosBase11 = buf.getIntLE(offset + 192); + if (varPosBase11 < 0 || varPosBase11 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Utility", varPosBase11, buf.readableBytes()); + } + + int varPos11 = offset + 260 + varPosBase11; + obj.utility = ItemUtility.deserialize(buf, varPos11); + } + + if ((nullBits[2] & 2) != 0) { + int varPosBase12 = buf.getIntLE(offset + 196); + if (varPosBase12 < 0 || varPosBase12 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("BuilderToolData", varPosBase12, buf.readableBytes()); + } + + int varPos12 = offset + 260 + varPosBase12; + obj.builderToolData = BuilderToolState.deserialize(buf, varPos12); + } + + if ((nullBits[2] & 4) != 0) { + int varPosBase13 = buf.getIntLE(offset + 200); + if (varPosBase13 < 0 || varPosBase13 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("ItemEntity", varPosBase13, buf.readableBytes()); + } + + int varPos13 = offset + 260 + varPosBase13; + obj.itemEntity = ItemEntityConfig.deserialize(buf, varPos13); + } + + if ((nullBits[2] & 8) != 0) { + int varPosBase14 = buf.getIntLE(offset + 204); + if (varPosBase14 < 0 || varPosBase14 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Set", varPosBase14, buf.readableBytes()); + } + + int varPos14 = offset + 260 + varPosBase14; + int setLen = VarInt.peek(buf, varPos14); + if (setLen < 0) { + throw ProtocolException.invalidVarInt("Set"); + } + + int setVarIntLen = VarInt.size(setLen); + if (setLen > 4096000) { + throw ProtocolException.stringTooLong("Set", setLen, 4096000); + } + + if (varPos14 + setVarIntLen + setLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Set", varPos14 + setVarIntLen + setLen, buf.readableBytes()); + } + + obj.set = PacketIO.readVarString(buf, varPos14, PacketIO.UTF8); + } + + if ((nullBits[2] & 16) != 0) { + int varPosBase15 = buf.getIntLE(offset + 208); + if (varPosBase15 < 0 || varPosBase15 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Categories", varPosBase15, buf.readableBytes()); + } + + int varPos15 = offset + 260 + varPosBase15; + int categoriesCount = VarInt.peek(buf, varPos15); + if (categoriesCount < 0) { + throw ProtocolException.invalidVarInt("Categories"); + } + + int varIntLenx = VarInt.size(categoriesCount); + if (categoriesCount > 4096000) { + throw ProtocolException.arrayTooLong("Categories", categoriesCount, 4096000); + } + + if (varPos15 + varIntLenx + categoriesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Categories", varPos15 + varIntLenx + categoriesCount * 1, buf.readableBytes()); + } + + obj.categories = new String[categoriesCount]; + int elemPos = varPos15 + varIntLenx; + + for (int i = 0; i < categoriesCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("categories[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("categories[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("categories[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.categories[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + if ((nullBits[2] & 32) != 0) { + int varPosBase16 = buf.getIntLE(offset + 212); + if (varPosBase16 < 0 || varPosBase16 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("SubCategory", varPosBase16, buf.readableBytes()); + } + + int varPos16 = offset + 260 + varPosBase16; + int subCategoryLen = VarInt.peek(buf, varPos16); + if (subCategoryLen < 0) { + throw ProtocolException.invalidVarInt("SubCategory"); + } + + int subCategoryVarIntLen = VarInt.size(subCategoryLen); + if (subCategoryLen > 4096000) { + throw ProtocolException.stringTooLong("SubCategory", subCategoryLen, 4096000); + } + + if (varPos16 + subCategoryVarIntLen + subCategoryLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SubCategory", varPos16 + subCategoryVarIntLen + subCategoryLen, buf.readableBytes()); + } + + obj.subCategory = PacketIO.readVarString(buf, varPos16, PacketIO.UTF8); + } + + if ((nullBits[2] & 64) != 0) { + int varPosBase17 = buf.getIntLE(offset + 216); + if (varPosBase17 < 0 || varPosBase17 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Particles", varPosBase17, buf.readableBytes()); + } + + int varPos17 = offset + 260 + varPosBase17; + int particlesCount = VarInt.peek(buf, varPos17); + if (particlesCount < 0) { + throw ProtocolException.invalidVarInt("Particles"); + } + + int varIntLenxx = VarInt.size(particlesCount); + if (particlesCount > 4096000) { + throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); + } + + if (varPos17 + varIntLenxx + particlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Particles", varPos17 + varIntLenxx + particlesCount * 34, buf.readableBytes()); + } + + obj.particles = new ModelParticle[particlesCount]; + int elemPos = varPos17 + varIntLenxx; + + for (int i = 0; i < particlesCount; i++) { + obj.particles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[2] & 128) != 0) { + int varPosBase18 = buf.getIntLE(offset + 220); + if (varPosBase18 < 0 || varPosBase18 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("FirstPersonParticles", varPosBase18, buf.readableBytes()); + } + + int varPos18 = offset + 260 + varPosBase18; + int firstPersonParticlesCount = VarInt.peek(buf, varPos18); + if (firstPersonParticlesCount < 0) { + throw ProtocolException.invalidVarInt("FirstPersonParticles"); + } + + int varIntLenxxx = VarInt.size(firstPersonParticlesCount); + if (firstPersonParticlesCount > 4096000) { + throw ProtocolException.arrayTooLong("FirstPersonParticles", firstPersonParticlesCount, 4096000); + } + + if (varPos18 + varIntLenxxx + firstPersonParticlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FirstPersonParticles", varPos18 + varIntLenxxx + firstPersonParticlesCount * 34, buf.readableBytes()); + } + + obj.firstPersonParticles = new ModelParticle[firstPersonParticlesCount]; + int elemPos = varPos18 + varIntLenxxx; + + for (int i = 0; i < firstPersonParticlesCount; i++) { + obj.firstPersonParticles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[3] & 1) != 0) { + int varPosBase19 = buf.getIntLE(offset + 224); + if (varPosBase19 < 0 || varPosBase19 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Trails", varPosBase19, buf.readableBytes()); + } + + int varPos19 = offset + 260 + varPosBase19; + int trailsCount = VarInt.peek(buf, varPos19); + if (trailsCount < 0) { + throw ProtocolException.invalidVarInt("Trails"); + } + + int varIntLenxxxx = VarInt.size(trailsCount); + if (trailsCount > 4096000) { + throw ProtocolException.arrayTooLong("Trails", trailsCount, 4096000); + } + + if (varPos19 + varIntLenxxxx + trailsCount * 27L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Trails", varPos19 + varIntLenxxxx + trailsCount * 27, buf.readableBytes()); + } + + obj.trails = new ModelTrail[trailsCount]; + int elemPos = varPos19 + varIntLenxxxx; + + for (int i = 0; i < trailsCount; i++) { + obj.trails[i] = ModelTrail.deserialize(buf, elemPos); + elemPos += ModelTrail.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[3] & 2) != 0) { + int varPosBase20 = buf.getIntLE(offset + 228); + if (varPosBase20 < 0 || varPosBase20 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Interactions", varPosBase20, buf.readableBytes()); + } + + int varPos20 = offset + 260 + varPosBase20; + int interactionsCount = VarInt.peek(buf, varPos20); + if (interactionsCount < 0) { + throw ProtocolException.invalidVarInt("Interactions"); + } + + int varIntLenxxxxx = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + } + + obj.interactions = new HashMap<>(interactionsCount); + int dictPos = varPos20 + varIntLenxxxxx; + + for (int i = 0; i < interactionsCount; i++) { + InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.interactions.put(key, val) != null) { + throw ProtocolException.duplicateKey("interactions", key); + } + } + } + + if ((nullBits[3] & 4) != 0) { + int varPosBase21 = buf.getIntLE(offset + 232); + if (varPosBase21 < 0 || varPosBase21 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("InteractionVars", varPosBase21, buf.readableBytes()); + } + + int varPos21 = offset + 260 + varPosBase21; + int interactionVarsCount = VarInt.peek(buf, varPos21); + if (interactionVarsCount < 0) { + throw ProtocolException.invalidVarInt("InteractionVars"); + } + + int varIntLenxxxxx = VarInt.size(interactionVarsCount); + if (interactionVarsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("InteractionVars", interactionVarsCount, 4096000); + } + + obj.interactionVars = new HashMap<>(interactionVarsCount); + int dictPos = varPos21 + varIntLenxxxxx; + + for (int ix = 0; ix < interactionVarsCount; ix++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + int val = buf.getIntLE(dictPos); + dictPos += 4; + if (obj.interactionVars.put(key, val) != null) { + throw ProtocolException.duplicateKey("interactionVars", key); + } + } + } + + if ((nullBits[3] & 8) != 0) { + int varPosBase22 = buf.getIntLE(offset + 236); + if (varPosBase22 < 0 || varPosBase22 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("InteractionConfig", varPosBase22, buf.readableBytes()); + } + + int varPos22 = offset + 260 + varPosBase22; + obj.interactionConfig = InteractionConfiguration.deserialize(buf, varPos22); + } + + if ((nullBits[3] & 16) != 0) { + int varPosBase23 = buf.getIntLE(offset + 240); + if (varPosBase23 < 0 || varPosBase23 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("DroppedItemAnimation", varPosBase23, buf.readableBytes()); + } + + int varPos23 = offset + 260 + varPosBase23; + int droppedItemAnimationLen = VarInt.peek(buf, varPos23); + if (droppedItemAnimationLen < 0) { + throw ProtocolException.invalidVarInt("DroppedItemAnimation"); + } + + int droppedItemAnimationVarIntLen = VarInt.size(droppedItemAnimationLen); + if (droppedItemAnimationLen > 4096000) { + throw ProtocolException.stringTooLong("DroppedItemAnimation", droppedItemAnimationLen, 4096000); + } + + if (varPos23 + droppedItemAnimationVarIntLen + droppedItemAnimationLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "DroppedItemAnimation", varPos23 + droppedItemAnimationVarIntLen + droppedItemAnimationLen, buf.readableBytes() + ); + } + + obj.droppedItemAnimation = PacketIO.readVarString(buf, varPos23, PacketIO.UTF8); + } + + if ((nullBits[3] & 32) != 0) { + int varPosBase24 = buf.getIntLE(offset + 244); + if (varPosBase24 < 0 || varPosBase24 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("TagIndexes", varPosBase24, buf.readableBytes()); + } + + int varPos24 = offset + 260 + varPosBase24; + int tagIndexesCount = VarInt.peek(buf, varPos24); + if (tagIndexesCount < 0) { + throw ProtocolException.invalidVarInt("TagIndexes"); + } + + int varIntLenxxxxx = VarInt.size(tagIndexesCount); + if (tagIndexesCount > 4096000) { + throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); + } + + if (varPos24 + varIntLenxxxxx + tagIndexesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TagIndexes", varPos24 + varIntLenxxxxx + tagIndexesCount * 4, buf.readableBytes()); + } + + obj.tagIndexes = new int[tagIndexesCount]; + + for (int ix = 0; ix < tagIndexesCount; ix++) { + obj.tagIndexes[ix] = buf.getIntLE(varPos24 + varIntLenxxxxx + ix * 4); + } + } + + if ((nullBits[3] & 64) != 0) { + int varPosBase25 = buf.getIntLE(offset + 248); + if (varPosBase25 < 0 || varPosBase25 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("ItemAppearanceConditions", varPosBase25, buf.readableBytes()); + } + + int varPos25 = offset + 260 + varPosBase25; + int itemAppearanceConditionsCount = VarInt.peek(buf, varPos25); + if (itemAppearanceConditionsCount < 0) { + throw ProtocolException.invalidVarInt("ItemAppearanceConditions"); + } + + int varIntLenxxxxxx = VarInt.size(itemAppearanceConditionsCount); + if (itemAppearanceConditionsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ItemAppearanceConditions", itemAppearanceConditionsCount, 4096000); + } + + obj.itemAppearanceConditions = new HashMap<>(itemAppearanceConditionsCount); + int dictPos = varPos25 + varIntLenxxxxxx; + + for (int ix = 0; ix < itemAppearanceConditionsCount; ix++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + int valLen = VarInt.peek(buf, dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 64) { + throw ProtocolException.arrayTooLong("val", valLen, 64); + } + + if (dictPos + valVarLen + valLen * 18L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 18, buf.readableBytes()); + } + + dictPos += valVarLen; + ItemAppearanceCondition[] val = new ItemAppearanceCondition[valLen]; + + for (int valIdx = 0; valIdx < valLen; valIdx++) { + val[valIdx] = ItemAppearanceCondition.deserialize(buf, dictPos); + dictPos += ItemAppearanceCondition.computeBytesConsumed(buf, dictPos); + } + + if (obj.itemAppearanceConditions.put(key, val) != null) { + throw ProtocolException.duplicateKey("itemAppearanceConditions", key); + } + } + } + + if ((nullBits[3] & 128) != 0) { + int varPosBase26 = buf.getIntLE(offset + 252); + if (varPosBase26 < 0 || varPosBase26 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("DisplayEntityStatsHUD", varPosBase26, buf.readableBytes()); + } + + int varPos26 = offset + 260 + varPosBase26; + int displayEntityStatsHUDCount = VarInt.peek(buf, varPos26); + if (displayEntityStatsHUDCount < 0) { + throw ProtocolException.invalidVarInt("DisplayEntityStatsHUD"); + } + + int varIntLenxxxxxx = VarInt.size(displayEntityStatsHUDCount); + if (displayEntityStatsHUDCount > 4096000) { + throw ProtocolException.arrayTooLong("DisplayEntityStatsHUD", displayEntityStatsHUDCount, 4096000); + } + + if (varPos26 + varIntLenxxxxxx + displayEntityStatsHUDCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("DisplayEntityStatsHUD", varPos26 + varIntLenxxxxxx + displayEntityStatsHUDCount * 4, buf.readableBytes()); + } + + obj.displayEntityStatsHUD = new int[displayEntityStatsHUDCount]; + + for (int ix = 0; ix < displayEntityStatsHUDCount; ix++) { + obj.displayEntityStatsHUD[ix] = buf.getIntLE(varPos26 + varIntLenxxxxxx + ix * 4); + } + } + + if ((nullBits[4] & 1) != 0) { + int varPosBase27 = buf.getIntLE(offset + 256); + if (varPosBase27 < 0 || varPosBase27 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("HudUI", varPosBase27, buf.readableBytes()); + } + + int varPos27 = offset + 260 + varPosBase27; + int hudUICount = VarInt.peek(buf, varPos27); + if (hudUICount < 0) { + throw ProtocolException.invalidVarInt("HudUI"); + } + + int varIntLenxxxxxxx = VarInt.size(hudUICount); + if (hudUICount > 4096000) { + throw ProtocolException.arrayTooLong("HudUI", hudUICount, 4096000); + } + + if (varPos27 + varIntLenxxxxxxx + hudUICount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("HudUI", varPos27 + varIntLenxxxxxxx + hudUICount * 2, buf.readableBytes()); + } + + obj.hudUI = new ItemHudUI[hudUICount]; + int elemPos = varPos27 + varIntLenxxxxxxx; + + for (int ix = 0; ix < hudUICount; ix++) { + obj.hudUI[ix] = ItemHudUI.deserialize(buf, elemPos); + elemPos += ItemHudUI.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits[3] & 4) != 0) { - int varPos21 = offset + 251 + buf.getIntLE(offset + 231); - obj.interactionConfig = InteractionConfiguration.deserialize(buf, varPos21); - } - - if ((nullBits[3] & 8) != 0) { - int varPos22 = offset + 251 + buf.getIntLE(offset + 235); - int droppedItemAnimationLen = VarInt.peek(buf, varPos22); - if (droppedItemAnimationLen < 0) { - throw ProtocolException.negativeLength("DroppedItemAnimation", droppedItemAnimationLen); - } - - if (droppedItemAnimationLen > 4096000) { - throw ProtocolException.stringTooLong("DroppedItemAnimation", droppedItemAnimationLen, 4096000); - } - - obj.droppedItemAnimation = PacketIO.readVarString(buf, varPos22, PacketIO.UTF8); - } - - if ((nullBits[3] & 16) != 0) { - int varPos23 = offset + 251 + buf.getIntLE(offset + 239); - int tagIndexesCount = VarInt.peek(buf, varPos23); - if (tagIndexesCount < 0) { - throw ProtocolException.negativeLength("TagIndexes", tagIndexesCount); - } - - if (tagIndexesCount > 4096000) { - throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos23); - if (varPos23 + varIntLen + tagIndexesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("TagIndexes", varPos23 + varIntLen + tagIndexesCount * 4, buf.readableBytes()); - } - - obj.tagIndexes = new int[tagIndexesCount]; - - for (int ix = 0; ix < tagIndexesCount; ix++) { - obj.tagIndexes[ix] = buf.getIntLE(varPos23 + varIntLen + ix * 4); - } - } - - if ((nullBits[3] & 32) != 0) { - int varPos24 = offset + 251 + buf.getIntLE(offset + 243); - int itemAppearanceConditionsCount = VarInt.peek(buf, varPos24); - if (itemAppearanceConditionsCount < 0) { - throw ProtocolException.negativeLength("ItemAppearanceConditions", itemAppearanceConditionsCount); - } - - if (itemAppearanceConditionsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ItemAppearanceConditions", itemAppearanceConditionsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos24); - obj.itemAppearanceConditions = new HashMap<>(itemAppearanceConditionsCount); - int dictPos = varPos24 + varIntLen; - - for (int ix = 0; ix < itemAppearanceConditionsCount; ix++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - int valLen = VarInt.peek(buf, dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); - } - - if (valLen > 64) { - throw ProtocolException.arrayTooLong("val", valLen, 64); - } - - int valVarLen = VarInt.length(buf, dictPos); - if (dictPos + valVarLen + valLen * 18L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 18, buf.readableBytes()); - } - - dictPos += valVarLen; - ItemAppearanceCondition[] val = new ItemAppearanceCondition[valLen]; - - for (int valIdx = 0; valIdx < valLen; valIdx++) { - val[valIdx] = ItemAppearanceCondition.deserialize(buf, dictPos); - dictPos += ItemAppearanceCondition.computeBytesConsumed(buf, dictPos); - } - - if (obj.itemAppearanceConditions.put(key, val) != null) { - throw ProtocolException.duplicateKey("itemAppearanceConditions", key); - } - } - } - - if ((nullBits[3] & 64) != 0) { - int varPos25 = offset + 251 + buf.getIntLE(offset + 247); - int displayEntityStatsHUDCount = VarInt.peek(buf, varPos25); - if (displayEntityStatsHUDCount < 0) { - throw ProtocolException.negativeLength("DisplayEntityStatsHUD", displayEntityStatsHUDCount); - } - - if (displayEntityStatsHUDCount > 4096000) { - throw ProtocolException.arrayTooLong("DisplayEntityStatsHUD", displayEntityStatsHUDCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos25); - if (varPos25 + varIntLen + displayEntityStatsHUDCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("DisplayEntityStatsHUD", varPos25 + varIntLen + displayEntityStatsHUDCount * 4, buf.readableBytes()); - } - - obj.displayEntityStatsHUD = new int[displayEntityStatsHUDCount]; - - for (int ix = 0; ix < displayEntityStatsHUDCount; ix++) { - obj.displayEntityStatsHUD[ix] = buf.getIntLE(varPos25 + varIntLen + ix * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - byte[] nullBits = PacketIO.readBytes(buf, offset, 4); - int maxEnd = 251; + byte[] nullBits = PacketIO.readBytes(buf, offset, 5); + int maxEnd = 260; if ((nullBits[0] & 32) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 147); - int pos0 = offset + 251 + fieldOffset0; + int fieldOffset0 = buf.getIntLE(offset + 148); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + + int pos0 = offset + 260 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } } if ((nullBits[0] & 64) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 151); - int pos1 = offset + 251 + fieldOffset1; + int fieldOffset1 = buf.getIntLE(offset + 152); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Model", fieldOffset1, maxEnd); + } + + int pos1 = offset + 260 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } } if ((nullBits[0] & 128) != 0) { - int fieldOffset2 = buf.getIntLE(offset + 155); - int pos2 = offset + 251 + fieldOffset2; + int fieldOffset2 = buf.getIntLE(offset + 156); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Texture", fieldOffset2, maxEnd); + } + + int pos2 = offset + 260 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } } if ((nullBits[1] & 1) != 0) { - int fieldOffset3 = buf.getIntLE(offset + 159); - int pos3 = offset + 251 + fieldOffset3; + int fieldOffset3 = buf.getIntLE(offset + 160); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Animation", fieldOffset3, maxEnd); + } + + int pos3 = offset + 260 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } } if ((nullBits[1] & 2) != 0) { - int fieldOffset4 = buf.getIntLE(offset + 163); - int pos4 = offset + 251 + fieldOffset4; + int fieldOffset4 = buf.getIntLE(offset + 164); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("PlayerAnimationsId", fieldOffset4, maxEnd); + } + + int pos4 = offset + 260 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } } if ((nullBits[1] & 4) != 0) { - int fieldOffset5 = buf.getIntLE(offset + 167); - int pos5 = offset + 251 + fieldOffset5; + int fieldOffset5 = buf.getIntLE(offset + 168); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Icon", fieldOffset5, maxEnd); + } + + int pos5 = offset + 260 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } } if ((nullBits[1] & 8) != 0) { - int fieldOffset6 = buf.getIntLE(offset + 171); - int pos6 = offset + 251 + fieldOffset6; + int fieldOffset6 = buf.getIntLE(offset + 172); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("TranslationProperties", fieldOffset6, maxEnd); + } + + int pos6 = offset + 260 + fieldOffset6; pos6 += ItemTranslationProperties.computeBytesConsumed(buf, pos6); if (pos6 - offset > maxEnd) { maxEnd = pos6 - offset; @@ -795,10 +1072,14 @@ public class ItemBase { } if ((nullBits[1] & 16) != 0) { - int fieldOffset7 = buf.getIntLE(offset + 175); - int pos7 = offset + 251 + fieldOffset7; + int fieldOffset7 = buf.getIntLE(offset + 176); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("ResourceTypes", fieldOffset7, maxEnd); + } + + int pos7 = offset + 260 + fieldOffset7; int arrLen = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7); + pos7 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos7 += ItemResourceType.computeBytesConsumed(buf, pos7); @@ -810,8 +1091,12 @@ public class ItemBase { } if ((nullBits[1] & 32) != 0) { - int fieldOffset8 = buf.getIntLE(offset + 179); - int pos8 = offset + 251 + fieldOffset8; + int fieldOffset8 = buf.getIntLE(offset + 180); + if (fieldOffset8 < 0 || fieldOffset8 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Tool", fieldOffset8, maxEnd); + } + + int pos8 = offset + 260 + fieldOffset8; pos8 += ItemTool.computeBytesConsumed(buf, pos8); if (pos8 - offset > maxEnd) { maxEnd = pos8 - offset; @@ -819,8 +1104,12 @@ public class ItemBase { } if ((nullBits[1] & 64) != 0) { - int fieldOffset9 = buf.getIntLE(offset + 183); - int pos9 = offset + 251 + fieldOffset9; + int fieldOffset9 = buf.getIntLE(offset + 184); + if (fieldOffset9 < 0 || fieldOffset9 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Weapon", fieldOffset9, maxEnd); + } + + int pos9 = offset + 260 + fieldOffset9; pos9 += ItemWeapon.computeBytesConsumed(buf, pos9); if (pos9 - offset > maxEnd) { maxEnd = pos9 - offset; @@ -828,8 +1117,12 @@ public class ItemBase { } if ((nullBits[1] & 128) != 0) { - int fieldOffset10 = buf.getIntLE(offset + 187); - int pos10 = offset + 251 + fieldOffset10; + int fieldOffset10 = buf.getIntLE(offset + 188); + if (fieldOffset10 < 0 || fieldOffset10 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Armor", fieldOffset10, maxEnd); + } + + int pos10 = offset + 260 + fieldOffset10; pos10 += ItemArmor.computeBytesConsumed(buf, pos10); if (pos10 - offset > maxEnd) { maxEnd = pos10 - offset; @@ -837,8 +1130,12 @@ public class ItemBase { } if ((nullBits[2] & 1) != 0) { - int fieldOffset11 = buf.getIntLE(offset + 191); - int pos11 = offset + 251 + fieldOffset11; + int fieldOffset11 = buf.getIntLE(offset + 192); + if (fieldOffset11 < 0 || fieldOffset11 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Utility", fieldOffset11, maxEnd); + } + + int pos11 = offset + 260 + fieldOffset11; pos11 += ItemUtility.computeBytesConsumed(buf, pos11); if (pos11 - offset > maxEnd) { maxEnd = pos11 - offset; @@ -846,17 +1143,25 @@ public class ItemBase { } if ((nullBits[2] & 2) != 0) { - int fieldOffset12 = buf.getIntLE(offset + 195); - int pos12 = offset + 251 + fieldOffset12; - pos12 += ItemBuilderToolData.computeBytesConsumed(buf, pos12); + int fieldOffset12 = buf.getIntLE(offset + 196); + if (fieldOffset12 < 0 || fieldOffset12 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("BuilderToolData", fieldOffset12, maxEnd); + } + + int pos12 = offset + 260 + fieldOffset12; + pos12 += BuilderToolState.computeBytesConsumed(buf, pos12); if (pos12 - offset > maxEnd) { maxEnd = pos12 - offset; } } if ((nullBits[2] & 4) != 0) { - int fieldOffset13 = buf.getIntLE(offset + 199); - int pos13 = offset + 251 + fieldOffset13; + int fieldOffset13 = buf.getIntLE(offset + 200); + if (fieldOffset13 < 0 || fieldOffset13 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("ItemEntity", fieldOffset13, maxEnd); + } + + int pos13 = offset + 260 + fieldOffset13; pos13 += ItemEntityConfig.computeBytesConsumed(buf, pos13); if (pos13 - offset > maxEnd) { maxEnd = pos13 - offset; @@ -864,24 +1169,32 @@ public class ItemBase { } if ((nullBits[2] & 8) != 0) { - int fieldOffset14 = buf.getIntLE(offset + 203); - int pos14 = offset + 251 + fieldOffset14; + int fieldOffset14 = buf.getIntLE(offset + 204); + if (fieldOffset14 < 0 || fieldOffset14 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Set", fieldOffset14, maxEnd); + } + + int pos14 = offset + 260 + fieldOffset14; int sl = VarInt.peek(buf, pos14); - pos14 += VarInt.length(buf, pos14) + sl; + pos14 += VarInt.size(sl) + sl; if (pos14 - offset > maxEnd) { maxEnd = pos14 - offset; } } if ((nullBits[2] & 16) != 0) { - int fieldOffset15 = buf.getIntLE(offset + 207); - int pos15 = offset + 251 + fieldOffset15; + int fieldOffset15 = buf.getIntLE(offset + 208); + if (fieldOffset15 < 0 || fieldOffset15 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Categories", fieldOffset15, maxEnd); + } + + int pos15 = offset + 260 + fieldOffset15; int arrLen = VarInt.peek(buf, pos15); - pos15 += VarInt.length(buf, pos15); + pos15 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos15); - pos15 += VarInt.length(buf, pos15) + sl; + pos15 += VarInt.size(sl) + sl; } if (pos15 - offset > maxEnd) { @@ -890,25 +1203,28 @@ public class ItemBase { } if ((nullBits[2] & 32) != 0) { - int fieldOffset16 = buf.getIntLE(offset + 211); - int pos16 = offset + 251 + fieldOffset16; - int arrLen = VarInt.peek(buf, pos16); - pos16 += VarInt.length(buf, pos16); - - for (int i = 0; i < arrLen; i++) { - pos16 += ModelParticle.computeBytesConsumed(buf, pos16); + int fieldOffset16 = buf.getIntLE(offset + 212); + if (fieldOffset16 < 0 || fieldOffset16 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("SubCategory", fieldOffset16, maxEnd); } + int pos16 = offset + 260 + fieldOffset16; + int sl = VarInt.peek(buf, pos16); + pos16 += VarInt.size(sl) + sl; if (pos16 - offset > maxEnd) { maxEnd = pos16 - offset; } } if ((nullBits[2] & 64) != 0) { - int fieldOffset17 = buf.getIntLE(offset + 215); - int pos17 = offset + 251 + fieldOffset17; + int fieldOffset17 = buf.getIntLE(offset + 216); + if (fieldOffset17 < 0 || fieldOffset17 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Particles", fieldOffset17, maxEnd); + } + + int pos17 = offset + 260 + fieldOffset17; int arrLen = VarInt.peek(buf, pos17); - pos17 += VarInt.length(buf, pos17); + pos17 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos17 += ModelParticle.computeBytesConsumed(buf, pos17); @@ -920,13 +1236,17 @@ public class ItemBase { } if ((nullBits[2] & 128) != 0) { - int fieldOffset18 = buf.getIntLE(offset + 219); - int pos18 = offset + 251 + fieldOffset18; + int fieldOffset18 = buf.getIntLE(offset + 220); + if (fieldOffset18 < 0 || fieldOffset18 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("FirstPersonParticles", fieldOffset18, maxEnd); + } + + int pos18 = offset + 260 + fieldOffset18; int arrLen = VarInt.peek(buf, pos18); - pos18 += VarInt.length(buf, pos18); + pos18 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { - pos18 += ModelTrail.computeBytesConsumed(buf, pos18); + pos18 += ModelParticle.computeBytesConsumed(buf, pos18); } if (pos18 - offset > maxEnd) { @@ -935,13 +1255,17 @@ public class ItemBase { } if ((nullBits[3] & 1) != 0) { - int fieldOffset19 = buf.getIntLE(offset + 223); - int pos19 = offset + 251 + fieldOffset19; - int dictLen = VarInt.peek(buf, pos19); - pos19 += VarInt.length(buf, pos19); + int fieldOffset19 = buf.getIntLE(offset + 224); + if (fieldOffset19 < 0 || fieldOffset19 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Trails", fieldOffset19, maxEnd); + } - for (int i = 0; i < dictLen; i++) { - pos19 = ++pos19 + 4; + int pos19 = offset + 260 + fieldOffset19; + int arrLen = VarInt.peek(buf, pos19); + pos19 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos19 += ModelTrail.computeBytesConsumed(buf, pos19); } if (pos19 - offset > maxEnd) { @@ -950,15 +1274,17 @@ public class ItemBase { } if ((nullBits[3] & 2) != 0) { - int fieldOffset20 = buf.getIntLE(offset + 227); - int pos20 = offset + 251 + fieldOffset20; + int fieldOffset20 = buf.getIntLE(offset + 228); + if (fieldOffset20 < 0 || fieldOffset20 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("Interactions", fieldOffset20, maxEnd); + } + + int pos20 = offset + 260 + fieldOffset20; int dictLen = VarInt.peek(buf, pos20); - pos20 += VarInt.length(buf, pos20); + pos20 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { - int sl = VarInt.peek(buf, pos20); - pos20 += VarInt.length(buf, pos20) + sl; - pos20 += 4; + pos20 = ++pos20 + 4; } if (pos20 - offset > maxEnd) { @@ -967,71 +1293,131 @@ public class ItemBase { } if ((nullBits[3] & 4) != 0) { - int fieldOffset21 = buf.getIntLE(offset + 231); - int pos21 = offset + 251 + fieldOffset21; - pos21 += InteractionConfiguration.computeBytesConsumed(buf, pos21); + int fieldOffset21 = buf.getIntLE(offset + 232); + if (fieldOffset21 < 0 || fieldOffset21 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("InteractionVars", fieldOffset21, maxEnd); + } + + int pos21 = offset + 260 + fieldOffset21; + int dictLen = VarInt.peek(buf, pos21); + pos21 += VarInt.size(dictLen); + + for (int i = 0; i < dictLen; i++) { + int sl = VarInt.peek(buf, pos21); + pos21 += VarInt.size(sl) + sl; + pos21 += 4; + } + if (pos21 - offset > maxEnd) { maxEnd = pos21 - offset; } } if ((nullBits[3] & 8) != 0) { - int fieldOffset22 = buf.getIntLE(offset + 235); - int pos22 = offset + 251 + fieldOffset22; - int sl = VarInt.peek(buf, pos22); - pos22 += VarInt.length(buf, pos22) + sl; + int fieldOffset22 = buf.getIntLE(offset + 236); + if (fieldOffset22 < 0 || fieldOffset22 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("InteractionConfig", fieldOffset22, maxEnd); + } + + int pos22 = offset + 260 + fieldOffset22; + pos22 += InteractionConfiguration.computeBytesConsumed(buf, pos22); if (pos22 - offset > maxEnd) { maxEnd = pos22 - offset; } } if ((nullBits[3] & 16) != 0) { - int fieldOffset23 = buf.getIntLE(offset + 239); - int pos23 = offset + 251 + fieldOffset23; - int arrLen = VarInt.peek(buf, pos23); - pos23 += VarInt.length(buf, pos23) + arrLen * 4; + int fieldOffset23 = buf.getIntLE(offset + 240); + if (fieldOffset23 < 0 || fieldOffset23 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("DroppedItemAnimation", fieldOffset23, maxEnd); + } + + int pos23 = offset + 260 + fieldOffset23; + int sl = VarInt.peek(buf, pos23); + pos23 += VarInt.size(sl) + sl; if (pos23 - offset > maxEnd) { maxEnd = pos23 - offset; } } if ((nullBits[3] & 32) != 0) { - int fieldOffset24 = buf.getIntLE(offset + 243); - int pos24 = offset + 251 + fieldOffset24; - int dictLen = VarInt.peek(buf, pos24); - pos24 += VarInt.length(buf, pos24); - - for (int i = 0; i < dictLen; i++) { - pos24 += 4; - int al = VarInt.peek(buf, pos24); - pos24 += VarInt.length(buf, pos24); - - for (int j = 0; j < al; j++) { - pos24 += ItemAppearanceCondition.computeBytesConsumed(buf, pos24); - } + int fieldOffset24 = buf.getIntLE(offset + 244); + if (fieldOffset24 < 0 || fieldOffset24 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("TagIndexes", fieldOffset24, maxEnd); } + int pos24 = offset + 260 + fieldOffset24; + int arrLen = VarInt.peek(buf, pos24); + pos24 += VarInt.size(arrLen) + arrLen * 4; if (pos24 - offset > maxEnd) { maxEnd = pos24 - offset; } } if ((nullBits[3] & 64) != 0) { - int fieldOffset25 = buf.getIntLE(offset + 247); - int pos25 = offset + 251 + fieldOffset25; - int arrLen = VarInt.peek(buf, pos25); - pos25 += VarInt.length(buf, pos25) + arrLen * 4; + int fieldOffset25 = buf.getIntLE(offset + 248); + if (fieldOffset25 < 0 || fieldOffset25 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("ItemAppearanceConditions", fieldOffset25, maxEnd); + } + + int pos25 = offset + 260 + fieldOffset25; + int dictLen = VarInt.peek(buf, pos25); + pos25 += VarInt.size(dictLen); + + for (int i = 0; i < dictLen; i++) { + pos25 += 4; + int al = VarInt.peek(buf, pos25); + pos25 += VarInt.size(al); + + for (int j = 0; j < al; j++) { + pos25 += ItemAppearanceCondition.computeBytesConsumed(buf, pos25); + } + } + if (pos25 - offset > maxEnd) { maxEnd = pos25 - offset; } } + if ((nullBits[3] & 128) != 0) { + int fieldOffset26 = buf.getIntLE(offset + 252); + if (fieldOffset26 < 0 || fieldOffset26 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("DisplayEntityStatsHUD", fieldOffset26, maxEnd); + } + + int pos26 = offset + 260 + fieldOffset26; + int arrLen = VarInt.peek(buf, pos26); + pos26 += VarInt.size(arrLen) + arrLen * 4; + if (pos26 - offset > maxEnd) { + maxEnd = pos26 - offset; + } + } + + if ((nullBits[4] & 1) != 0) { + int fieldOffset27 = buf.getIntLE(offset + 256); + if (fieldOffset27 < 0 || fieldOffset27 > buf.writerIndex() - offset - 260) { + throw ProtocolException.invalidOffset("HudUI", fieldOffset27, maxEnd); + } + + int pos27 = offset + 260 + fieldOffset27; + int arrLen = VarInt.peek(buf, pos27); + pos27 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos27 += ItemHudUI.computeBytesConsumed(buf, pos27); + } + + if (pos27 - offset > maxEnd) { + maxEnd = pos27 - offset; + } + } + return maxEnd; } public void serialize(@Nonnull ByteBuf buf) { int startPos = buf.writerIndex(); - byte[] nullBits = new byte[4]; + byte[] nullBits = new byte[5]; if (this.iconProperties != null) { nullBits[0] = (byte)(nullBits[0] | 1); } @@ -1116,46 +1502,54 @@ public class ItemBase { nullBits[2] = (byte)(nullBits[2] | 16); } - if (this.particles != null) { + if (this.subCategory != null) { nullBits[2] = (byte)(nullBits[2] | 32); } - if (this.firstPersonParticles != null) { + if (this.particles != null) { nullBits[2] = (byte)(nullBits[2] | 64); } - if (this.trails != null) { + if (this.firstPersonParticles != null) { nullBits[2] = (byte)(nullBits[2] | 128); } - if (this.interactions != null) { + if (this.trails != null) { nullBits[3] = (byte)(nullBits[3] | 1); } - if (this.interactionVars != null) { + if (this.interactions != null) { nullBits[3] = (byte)(nullBits[3] | 2); } - if (this.interactionConfig != null) { + if (this.interactionVars != null) { nullBits[3] = (byte)(nullBits[3] | 4); } - if (this.droppedItemAnimation != null) { + if (this.interactionConfig != null) { nullBits[3] = (byte)(nullBits[3] | 8); } - if (this.tagIndexes != null) { + if (this.droppedItemAnimation != null) { nullBits[3] = (byte)(nullBits[3] | 16); } - if (this.itemAppearanceConditions != null) { + if (this.tagIndexes != null) { nullBits[3] = (byte)(nullBits[3] | 32); } - if (this.displayEntityStatsHUD != null) { + if (this.itemAppearanceConditions != null) { nullBits[3] = (byte)(nullBits[3] | 64); } + if (this.displayEntityStatsHUD != null) { + nullBits[3] = (byte)(nullBits[3] | 128); + } + + if (this.hudUI != null) { + nullBits[4] = (byte)(nullBits[4] | 1); + } + buf.writeBytes(nullBits); buf.writeFloatLE(this.scale); buf.writeByte(this.usePlayerAnimations ? 1 : 0); @@ -1233,6 +1627,8 @@ public class ItemBase { buf.writeIntLE(0); int categoriesOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int subCategoryOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int particlesOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int firstPersonParticlesOffsetSlot = buf.writerIndex(); @@ -1253,6 +1649,8 @@ public class ItemBase { buf.writeIntLE(0); int displayEntityStatsHUDOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int hudUIOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); if (this.id != null) { buf.setIntLE(idOffsetSlot, buf.writerIndex() - varBlockStart); @@ -1382,6 +1780,13 @@ public class ItemBase { buf.setIntLE(categoriesOffsetSlot, -1); } + if (this.subCategory != null) { + buf.setIntLE(subCategoryOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.subCategory, 4096000); + } else { + buf.setIntLE(subCategoryOffsetSlot, -1); + } + if (this.particles != null) { buf.setIntLE(particlesOffsetSlot, buf.writerIndex() - varBlockStart); if (this.particles.length > 4096000) { @@ -1522,10 +1927,25 @@ public class ItemBase { } else { buf.setIntLE(displayEntityStatsHUDOffsetSlot, -1); } + + if (this.hudUI != null) { + buf.setIntLE(hudUIOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.hudUI.length > 4096000) { + throw ProtocolException.arrayTooLong("HudUI", this.hudUI.length, 4096000); + } + + VarInt.write(buf, this.hudUI.length); + + for (ItemHudUI item : this.hudUI) { + item.serialize(buf); + } + } else { + buf.setIntLE(hudUIOffsetSlot, -1); + } } public int computeSize() { - int size = 251; + int size = 260; if (this.id != null) { size += PacketIO.stringSize(this.id); } @@ -1602,6 +2022,10 @@ public class ItemBase { size += VarInt.size(this.categories.length) + categoriesSize; } + if (this.subCategory != null) { + size += PacketIO.stringSize(this.subCategory); + } + if (this.particles != null) { int particlesSize = 0; @@ -1672,25 +2096,31 @@ public class ItemBase { size += VarInt.size(this.displayEntityStatsHUD.length) + this.displayEntityStatsHUD.length * 4; } + if (this.hudUI != null) { + int hudUISize = 0; + + for (ItemHudUI elem : this.hudUI) { + hudUISize += elem.computeSize(); + } + + size += VarInt.size(this.hudUI.length) + hudUISize; + } + return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 251) { - return ValidationResult.error("Buffer too small: expected at least 251 bytes"); + if (buffer.readableBytes() - offset < 260) { + return ValidationResult.error("Buffer too small: expected at least 260 bytes"); } else { - byte[] nullBits = PacketIO.readBytes(buffer, offset, 4); + byte[] nullBits = PacketIO.readBytes(buffer, offset, 5); if ((nullBits[0] & 32) != 0) { - int idOffset = buffer.getIntLE(offset + 147); - if (idOffset < 0) { + int idOffset = buffer.getIntLE(offset + 148); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Id"); } - int pos = offset + 251 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - + int pos = offset + 260 + idOffset; int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -1700,7 +2130,7 @@ public class ItemBase { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -1708,16 +2138,12 @@ public class ItemBase { } if ((nullBits[0] & 64) != 0) { - int modelOffset = buffer.getIntLE(offset + 151); - if (modelOffset < 0) { + int modelOffset = buffer.getIntLE(offset + 152); + if (modelOffset < 0 || modelOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Model"); } - int posx = offset + 251 + modelOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } - + int posx = offset + 260 + modelOffset; int modelLen = VarInt.peek(buffer, posx); if (modelLen < 0) { return ValidationResult.error("Invalid string length for Model"); @@ -1727,7 +2153,7 @@ public class ItemBase { return ValidationResult.error("Model exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(modelLen); posx += modelLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Model"); @@ -1735,16 +2161,12 @@ public class ItemBase { } if ((nullBits[0] & 128) != 0) { - int textureOffset = buffer.getIntLE(offset + 155); - if (textureOffset < 0) { + int textureOffset = buffer.getIntLE(offset + 156); + if (textureOffset < 0 || textureOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Texture"); } - int posxx = offset + 251 + textureOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Texture"); - } - + int posxx = offset + 260 + textureOffset; int textureLen = VarInt.peek(buffer, posxx); if (textureLen < 0) { return ValidationResult.error("Invalid string length for Texture"); @@ -1754,7 +2176,7 @@ public class ItemBase { return ValidationResult.error("Texture exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(textureLen); posxx += textureLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Texture"); @@ -1762,16 +2184,12 @@ public class ItemBase { } if ((nullBits[1] & 1) != 0) { - int animationOffset = buffer.getIntLE(offset + 159); - if (animationOffset < 0) { + int animationOffset = buffer.getIntLE(offset + 160); + if (animationOffset < 0 || animationOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Animation"); } - int posxxx = offset + 251 + animationOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Animation"); - } - + int posxxx = offset + 260 + animationOffset; int animationLen = VarInt.peek(buffer, posxxx); if (animationLen < 0) { return ValidationResult.error("Invalid string length for Animation"); @@ -1781,7 +2199,7 @@ public class ItemBase { return ValidationResult.error("Animation exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(animationLen); posxxx += animationLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Animation"); @@ -1789,16 +2207,12 @@ public class ItemBase { } if ((nullBits[1] & 2) != 0) { - int playerAnimationsIdOffset = buffer.getIntLE(offset + 163); - if (playerAnimationsIdOffset < 0) { + int playerAnimationsIdOffset = buffer.getIntLE(offset + 164); + if (playerAnimationsIdOffset < 0 || playerAnimationsIdOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for PlayerAnimationsId"); } - int posxxxx = offset + 251 + playerAnimationsIdOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for PlayerAnimationsId"); - } - + int posxxxx = offset + 260 + playerAnimationsIdOffset; int playerAnimationsIdLen = VarInt.peek(buffer, posxxxx); if (playerAnimationsIdLen < 0) { return ValidationResult.error("Invalid string length for PlayerAnimationsId"); @@ -1808,7 +2222,7 @@ public class ItemBase { return ValidationResult.error("PlayerAnimationsId exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(playerAnimationsIdLen); posxxxx += playerAnimationsIdLen; if (posxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading PlayerAnimationsId"); @@ -1816,16 +2230,12 @@ public class ItemBase { } if ((nullBits[1] & 4) != 0) { - int iconOffset = buffer.getIntLE(offset + 167); - if (iconOffset < 0) { + int iconOffset = buffer.getIntLE(offset + 168); + if (iconOffset < 0 || iconOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Icon"); } - int posxxxxx = offset + 251 + iconOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Icon"); - } - + int posxxxxx = offset + 260 + iconOffset; int iconLen = VarInt.peek(buffer, posxxxxx); if (iconLen < 0) { return ValidationResult.error("Invalid string length for Icon"); @@ -1835,7 +2245,7 @@ public class ItemBase { return ValidationResult.error("Icon exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); + posxxxxx += VarInt.size(iconLen); posxxxxx += iconLen; if (posxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Icon"); @@ -1843,16 +2253,12 @@ public class ItemBase { } if ((nullBits[1] & 8) != 0) { - int translationPropertiesOffset = buffer.getIntLE(offset + 171); - if (translationPropertiesOffset < 0) { + int translationPropertiesOffset = buffer.getIntLE(offset + 172); + if (translationPropertiesOffset < 0 || translationPropertiesOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for TranslationProperties"); } - int posxxxxxx = offset + 251 + translationPropertiesOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TranslationProperties"); - } - + int posxxxxxx = offset + 260 + translationPropertiesOffset; ValidationResult translationPropertiesResult = ItemTranslationProperties.validateStructure(buffer, posxxxxxx); if (!translationPropertiesResult.isValid()) { return ValidationResult.error("Invalid TranslationProperties: " + translationPropertiesResult.error()); @@ -1862,17 +2268,13 @@ public class ItemBase { } if ((nullBits[1] & 16) != 0) { - int resourceTypesOffset = buffer.getIntLE(offset + 175); - if (resourceTypesOffset < 0) { + int resourceTypesOffset = buffer.getIntLE(offset + 176); + if (resourceTypesOffset < 0 || resourceTypesOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for ResourceTypes"); } - int posxxxxxxx = offset + 251 + resourceTypesOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ResourceTypes"); - } - - int resourceTypesCount = VarInt.peek(buffer, posxxxxxxx); + int posxxxxxx = offset + 260 + resourceTypesOffset; + int resourceTypesCount = VarInt.peek(buffer, posxxxxxx); if (resourceTypesCount < 0) { return ValidationResult.error("Invalid array count for ResourceTypes"); } @@ -1881,144 +2283,116 @@ public class ItemBase { return ValidationResult.error("ResourceTypes exceeds max length 4096000"); } - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); + posxxxxxx += VarInt.size(resourceTypesCount); for (int i = 0; i < resourceTypesCount; i++) { - ValidationResult structResult = ItemResourceType.validateStructure(buffer, posxxxxxxx); + ValidationResult structResult = ItemResourceType.validateStructure(buffer, posxxxxxx); if (!structResult.isValid()) { return ValidationResult.error("Invalid ItemResourceType in ResourceTypes[" + i + "]: " + structResult.error()); } - posxxxxxxx += ItemResourceType.computeBytesConsumed(buffer, posxxxxxxx); + posxxxxxx += ItemResourceType.computeBytesConsumed(buffer, posxxxxxx); } } if ((nullBits[1] & 32) != 0) { - int toolOffset = buffer.getIntLE(offset + 179); - if (toolOffset < 0) { + int toolOffset = buffer.getIntLE(offset + 180); + if (toolOffset < 0 || toolOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Tool"); } - int posxxxxxxxx = offset + 251 + toolOffset; - if (posxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tool"); - } - - ValidationResult toolResult = ItemTool.validateStructure(buffer, posxxxxxxxx); + int posxxxxxxx = offset + 260 + toolOffset; + ValidationResult toolResult = ItemTool.validateStructure(buffer, posxxxxxxx); if (!toolResult.isValid()) { return ValidationResult.error("Invalid Tool: " + toolResult.error()); } - posxxxxxxxx += ItemTool.computeBytesConsumed(buffer, posxxxxxxxx); + posxxxxxxx += ItemTool.computeBytesConsumed(buffer, posxxxxxxx); } if ((nullBits[1] & 64) != 0) { - int weaponOffset = buffer.getIntLE(offset + 183); - if (weaponOffset < 0) { + int weaponOffset = buffer.getIntLE(offset + 184); + if (weaponOffset < 0 || weaponOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Weapon"); } - int posxxxxxxxxx = offset + 251 + weaponOffset; - if (posxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Weapon"); - } - - ValidationResult weaponResult = ItemWeapon.validateStructure(buffer, posxxxxxxxxx); + int posxxxxxxx = offset + 260 + weaponOffset; + ValidationResult weaponResult = ItemWeapon.validateStructure(buffer, posxxxxxxx); if (!weaponResult.isValid()) { return ValidationResult.error("Invalid Weapon: " + weaponResult.error()); } - posxxxxxxxxx += ItemWeapon.computeBytesConsumed(buffer, posxxxxxxxxx); + posxxxxxxx += ItemWeapon.computeBytesConsumed(buffer, posxxxxxxx); } if ((nullBits[1] & 128) != 0) { - int armorOffset = buffer.getIntLE(offset + 187); - if (armorOffset < 0) { + int armorOffset = buffer.getIntLE(offset + 188); + if (armorOffset < 0 || armorOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Armor"); } - int posxxxxxxxxxx = offset + 251 + armorOffset; - if (posxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Armor"); - } - - ValidationResult armorResult = ItemArmor.validateStructure(buffer, posxxxxxxxxxx); + int posxxxxxxx = offset + 260 + armorOffset; + ValidationResult armorResult = ItemArmor.validateStructure(buffer, posxxxxxxx); if (!armorResult.isValid()) { return ValidationResult.error("Invalid Armor: " + armorResult.error()); } - posxxxxxxxxxx += ItemArmor.computeBytesConsumed(buffer, posxxxxxxxxxx); + posxxxxxxx += ItemArmor.computeBytesConsumed(buffer, posxxxxxxx); } if ((nullBits[2] & 1) != 0) { - int utilityOffset = buffer.getIntLE(offset + 191); - if (utilityOffset < 0) { + int utilityOffset = buffer.getIntLE(offset + 192); + if (utilityOffset < 0 || utilityOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Utility"); } - int posxxxxxxxxxxx = offset + 251 + utilityOffset; - if (posxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Utility"); - } - - ValidationResult utilityResult = ItemUtility.validateStructure(buffer, posxxxxxxxxxxx); + int posxxxxxxx = offset + 260 + utilityOffset; + ValidationResult utilityResult = ItemUtility.validateStructure(buffer, posxxxxxxx); if (!utilityResult.isValid()) { return ValidationResult.error("Invalid Utility: " + utilityResult.error()); } - posxxxxxxxxxxx += ItemUtility.computeBytesConsumed(buffer, posxxxxxxxxxxx); + posxxxxxxx += ItemUtility.computeBytesConsumed(buffer, posxxxxxxx); } if ((nullBits[2] & 2) != 0) { - int builderToolDataOffset = buffer.getIntLE(offset + 195); - if (builderToolDataOffset < 0) { + int builderToolDataOffset = buffer.getIntLE(offset + 196); + if (builderToolDataOffset < 0 || builderToolDataOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for BuilderToolData"); } - int posxxxxxxxxxxxx = offset + 251 + builderToolDataOffset; - if (posxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BuilderToolData"); - } - - ValidationResult builderToolDataResult = ItemBuilderToolData.validateStructure(buffer, posxxxxxxxxxxxx); + int posxxxxxxx = offset + 260 + builderToolDataOffset; + ValidationResult builderToolDataResult = BuilderToolState.validateStructure(buffer, posxxxxxxx); if (!builderToolDataResult.isValid()) { return ValidationResult.error("Invalid BuilderToolData: " + builderToolDataResult.error()); } - posxxxxxxxxxxxx += ItemBuilderToolData.computeBytesConsumed(buffer, posxxxxxxxxxxxx); + posxxxxxxx += BuilderToolState.computeBytesConsumed(buffer, posxxxxxxx); } if ((nullBits[2] & 4) != 0) { - int itemEntityOffset = buffer.getIntLE(offset + 199); - if (itemEntityOffset < 0) { + int itemEntityOffset = buffer.getIntLE(offset + 200); + if (itemEntityOffset < 0 || itemEntityOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for ItemEntity"); } - int posxxxxxxxxxxxxx = offset + 251 + itemEntityOffset; - if (posxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemEntity"); - } - - ValidationResult itemEntityResult = ItemEntityConfig.validateStructure(buffer, posxxxxxxxxxxxxx); + int posxxxxxxx = offset + 260 + itemEntityOffset; + ValidationResult itemEntityResult = ItemEntityConfig.validateStructure(buffer, posxxxxxxx); if (!itemEntityResult.isValid()) { return ValidationResult.error("Invalid ItemEntity: " + itemEntityResult.error()); } - posxxxxxxxxxxxxx += ItemEntityConfig.computeBytesConsumed(buffer, posxxxxxxxxxxxxx); + posxxxxxxx += ItemEntityConfig.computeBytesConsumed(buffer, posxxxxxxx); } if ((nullBits[2] & 8) != 0) { - int setOffset = buffer.getIntLE(offset + 203); - if (setOffset < 0) { + int setOffset = buffer.getIntLE(offset + 204); + if (setOffset < 0 || setOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Set"); } - int posxxxxxxxxxxxxxx = offset + 251 + setOffset; - if (posxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Set"); - } - - int setLen = VarInt.peek(buffer, posxxxxxxxxxxxxxx); + int posxxxxxxx = offset + 260 + setOffset; + int setLen = VarInt.peek(buffer, posxxxxxxx); if (setLen < 0) { return ValidationResult.error("Invalid string length for Set"); } @@ -2027,25 +2401,21 @@ public class ItemBase { return ValidationResult.error("Set exceeds max length 4096000"); } - posxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxx += setLen; - if (posxxxxxxxxxxxxxx > buffer.writerIndex()) { + posxxxxxxx += VarInt.size(setLen); + posxxxxxxx += setLen; + if (posxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Set"); } } if ((nullBits[2] & 16) != 0) { - int categoriesOffset = buffer.getIntLE(offset + 207); - if (categoriesOffset < 0) { + int categoriesOffset = buffer.getIntLE(offset + 208); + if (categoriesOffset < 0 || categoriesOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Categories"); } - int posxxxxxxxxxxxxxxx = offset + 251 + categoriesOffset; - if (posxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Categories"); - } - - int categoriesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxx); + int posxxxxxxxx = offset + 260 + categoriesOffset; + int categoriesCount = VarInt.peek(buffer, posxxxxxxxx); if (categoriesCount < 0) { return ValidationResult.error("Invalid array count for Categories"); } @@ -2054,34 +2424,53 @@ public class ItemBase { return ValidationResult.error("Categories exceeds max length 4096000"); } - posxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxx); + posxxxxxxxx += VarInt.size(categoriesCount); for (int i = 0; i < categoriesCount; i++) { - int strLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxx); + int strLen = VarInt.peek(buffer, posxxxxxxxx); if (strLen < 0) { return ValidationResult.error("Invalid string length in Categories"); } - posxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxxx += strLen; - if (posxxxxxxxxxxxxxxx > buffer.writerIndex()) { + posxxxxxxxx += VarInt.size(strLen); + posxxxxxxxx += strLen; + if (posxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Categories"); } } } if ((nullBits[2] & 32) != 0) { - int particlesOffset = buffer.getIntLE(offset + 211); - if (particlesOffset < 0) { + int subCategoryOffset = buffer.getIntLE(offset + 212); + if (subCategoryOffset < 0 || subCategoryOffset > buffer.writerIndex() - offset - 260) { + return ValidationResult.error("Invalid offset for SubCategory"); + } + + int posxxxxxxxxx = offset + 260 + subCategoryOffset; + int subCategoryLen = VarInt.peek(buffer, posxxxxxxxxx); + if (subCategoryLen < 0) { + return ValidationResult.error("Invalid string length for SubCategory"); + } + + if (subCategoryLen > 4096000) { + return ValidationResult.error("SubCategory exceeds max length 4096000"); + } + + posxxxxxxxxx += VarInt.size(subCategoryLen); + posxxxxxxxxx += subCategoryLen; + if (posxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading SubCategory"); + } + } + + if ((nullBits[2] & 64) != 0) { + int particlesOffset = buffer.getIntLE(offset + 216); + if (particlesOffset < 0 || particlesOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Particles"); } - int posxxxxxxxxxxxxxxxx = offset + 251 + particlesOffset; - if (posxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particles"); - } - - int particlesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxx); + int posxxxxxxxxxx = offset + 260 + particlesOffset; + int particlesCount = VarInt.peek(buffer, posxxxxxxxxxx); if (particlesCount < 0) { return ValidationResult.error("Invalid array count for Particles"); } @@ -2090,30 +2479,26 @@ public class ItemBase { return ValidationResult.error("Particles exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxx); + posxxxxxxxxxx += VarInt.size(particlesCount); for (int i = 0; i < particlesCount; i++) { - ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxxxxxxxxxxxxxxx); + ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxxxxxxxxx); if (!structResult.isValid()) { return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); } - posxxxxxxxxxxxxxxxx += ModelParticle.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxx); + posxxxxxxxxxx += ModelParticle.computeBytesConsumed(buffer, posxxxxxxxxxx); } } - if ((nullBits[2] & 64) != 0) { - int firstPersonParticlesOffset = buffer.getIntLE(offset + 215); - if (firstPersonParticlesOffset < 0) { + if ((nullBits[2] & 128) != 0) { + int firstPersonParticlesOffset = buffer.getIntLE(offset + 220); + if (firstPersonParticlesOffset < 0 || firstPersonParticlesOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for FirstPersonParticles"); } - int posxxxxxxxxxxxxxxxxx = offset + 251 + firstPersonParticlesOffset; - if (posxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstPersonParticles"); - } - - int firstPersonParticlesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxx = offset + 260 + firstPersonParticlesOffset; + int firstPersonParticlesCount = VarInt.peek(buffer, posxxxxxxxxxxx); if (firstPersonParticlesCount < 0) { return ValidationResult.error("Invalid array count for FirstPersonParticles"); } @@ -2122,30 +2507,26 @@ public class ItemBase { return ValidationResult.error("FirstPersonParticles exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxx += VarInt.size(firstPersonParticlesCount); for (int i = 0; i < firstPersonParticlesCount; i++) { - ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxxxxxxxxxxxxxxxx); + ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxxxxxxxxxx); if (!structResult.isValid()) { return ValidationResult.error("Invalid ModelParticle in FirstPersonParticles[" + i + "]: " + structResult.error()); } - posxxxxxxxxxxxxxxxxx += ModelParticle.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxx += ModelParticle.computeBytesConsumed(buffer, posxxxxxxxxxxx); } } - if ((nullBits[2] & 128) != 0) { - int trailsOffset = buffer.getIntLE(offset + 219); - if (trailsOffset < 0) { + if ((nullBits[3] & 1) != 0) { + int trailsOffset = buffer.getIntLE(offset + 224); + if (trailsOffset < 0 || trailsOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Trails"); } - int posxxxxxxxxxxxxxxxxxx = offset + 251 + trailsOffset; - if (posxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Trails"); - } - - int trailsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxxx = offset + 260 + trailsOffset; + int trailsCount = VarInt.peek(buffer, posxxxxxxxxxxxx); if (trailsCount < 0) { return ValidationResult.error("Invalid array count for Trails"); } @@ -2154,30 +2535,26 @@ public class ItemBase { return ValidationResult.error("Trails exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxx += VarInt.size(trailsCount); for (int i = 0; i < trailsCount; i++) { - ValidationResult structResult = ModelTrail.validateStructure(buffer, posxxxxxxxxxxxxxxxxxx); + ValidationResult structResult = ModelTrail.validateStructure(buffer, posxxxxxxxxxxxx); if (!structResult.isValid()) { return ValidationResult.error("Invalid ModelTrail in Trails[" + i + "]: " + structResult.error()); } - posxxxxxxxxxxxxxxxxxx += ModelTrail.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxx += ModelTrail.computeBytesConsumed(buffer, posxxxxxxxxxxxx); } } - if ((nullBits[3] & 1) != 0) { - int interactionsOffset = buffer.getIntLE(offset + 223); - if (interactionsOffset < 0) { + if ((nullBits[3] & 2) != 0) { + int interactionsOffset = buffer.getIntLE(offset + 228); + if (interactionsOffset < 0 || interactionsOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for Interactions"); } - int posxxxxxxxxxxxxxxxxxxx = offset + 251 + interactionsOffset; - if (posxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Interactions"); - } - - int interactionsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxxxx = offset + 260 + interactionsOffset; + int interactionsCount = VarInt.peek(buffer, posxxxxxxxxxxxxx); if (interactionsCount < 0) { return ValidationResult.error("Invalid dictionary count for Interactions"); } @@ -2186,28 +2563,29 @@ public class ItemBase { return ValidationResult.error("Interactions exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxx += VarInt.size(interactionsCount); for (int i = 0; i < interactionsCount; i++) { - posxxxxxxxxxxxxxxxxxxx = ++posxxxxxxxxxxxxxxxxxxx + 4; - if (posxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + int v = buffer.getByte(posxxxxxxxxxxxxx) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); + } + + posxxxxxxxxxxxxx = ++posxxxxxxxxxxxxx + 4; + if (posxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); } } } - if ((nullBits[3] & 2) != 0) { - int interactionVarsOffset = buffer.getIntLE(offset + 227); - if (interactionVarsOffset < 0) { + if ((nullBits[3] & 4) != 0) { + int interactionVarsOffset = buffer.getIntLE(offset + 232); + if (interactionVarsOffset < 0 || interactionVarsOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for InteractionVars"); } - int posxxxxxxxxxxxxxxxxxxxx = offset + 251 + interactionVarsOffset; - if (posxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for InteractionVars"); - } - - int interactionVarsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxxxxx = offset + 260 + interactionVarsOffset; + int interactionVarsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxx); if (interactionVarsCount < 0) { return ValidationResult.error("Invalid dictionary count for InteractionVars"); } @@ -2216,10 +2594,10 @@ public class ItemBase { return ValidationResult.error("InteractionVars exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxx += VarInt.size(interactionVarsCount); - for (int ix = 0; ix < interactionVarsCount; ix++) { - int keyLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxx); + for (int i = 0; i < interactionVarsCount; i++) { + int keyLen = VarInt.peek(buffer, posxxxxxxxxxxxxxx); if (keyLen < 0) { return ValidationResult.error("Invalid string length for key"); } @@ -2228,50 +2606,42 @@ public class ItemBase { return ValidationResult.error("key exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxxxxxxxx += keyLen; - if (posxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + posxxxxxxxxxxxxxx += VarInt.size(keyLen); + posxxxxxxxxxxxxxx += keyLen; + if (posxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading key"); } - posxxxxxxxxxxxxxxxxxxxx += 4; - if (posxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + posxxxxxxxxxxxxxx += 4; + if (posxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); } } } - if ((nullBits[3] & 4) != 0) { - int interactionConfigOffset = buffer.getIntLE(offset + 231); - if (interactionConfigOffset < 0) { + if ((nullBits[3] & 8) != 0) { + int interactionConfigOffset = buffer.getIntLE(offset + 236); + if (interactionConfigOffset < 0 || interactionConfigOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for InteractionConfig"); } - int posxxxxxxxxxxxxxxxxxxxxx = offset + 251 + interactionConfigOffset; - if (posxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for InteractionConfig"); - } - - ValidationResult interactionConfigResult = InteractionConfiguration.validateStructure(buffer, posxxxxxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxxxxxx = offset + 260 + interactionConfigOffset; + ValidationResult interactionConfigResult = InteractionConfiguration.validateStructure(buffer, posxxxxxxxxxxxxxxx); if (!interactionConfigResult.isValid()) { return ValidationResult.error("Invalid InteractionConfig: " + interactionConfigResult.error()); } - posxxxxxxxxxxxxxxxxxxxxx += InteractionConfiguration.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxx += InteractionConfiguration.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxx); } - if ((nullBits[3] & 8) != 0) { - int droppedItemAnimationOffset = buffer.getIntLE(offset + 235); - if (droppedItemAnimationOffset < 0) { + if ((nullBits[3] & 16) != 0) { + int droppedItemAnimationOffset = buffer.getIntLE(offset + 240); + if (droppedItemAnimationOffset < 0 || droppedItemAnimationOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for DroppedItemAnimation"); } - int posxxxxxxxxxxxxxxxxxxxxxx = offset + 251 + droppedItemAnimationOffset; - if (posxxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DroppedItemAnimation"); - } - - int droppedItemAnimationLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxxxxxx = offset + 260 + droppedItemAnimationOffset; + int droppedItemAnimationLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxx); if (droppedItemAnimationLen < 0) { return ValidationResult.error("Invalid string length for DroppedItemAnimation"); } @@ -2280,25 +2650,21 @@ public class ItemBase { return ValidationResult.error("DroppedItemAnimation exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxxxxxxxxxx += droppedItemAnimationLen; - if (posxxxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + posxxxxxxxxxxxxxxx += VarInt.size(droppedItemAnimationLen); + posxxxxxxxxxxxxxxx += droppedItemAnimationLen; + if (posxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading DroppedItemAnimation"); } } - if ((nullBits[3] & 16) != 0) { - int tagIndexesOffset = buffer.getIntLE(offset + 239); - if (tagIndexesOffset < 0) { + if ((nullBits[3] & 32) != 0) { + int tagIndexesOffset = buffer.getIntLE(offset + 244); + if (tagIndexesOffset < 0 || tagIndexesOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for TagIndexes"); } - int posxxxxxxxxxxxxxxxxxxxxxxx = offset + 251 + tagIndexesOffset; - if (posxxxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TagIndexes"); - } - - int tagIndexesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxxxxxxx = offset + 260 + tagIndexesOffset; + int tagIndexesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxx); if (tagIndexesCount < 0) { return ValidationResult.error("Invalid array count for TagIndexes"); } @@ -2307,25 +2673,21 @@ public class ItemBase { return ValidationResult.error("TagIndexes exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxxxxxxxxxxx += tagIndexesCount * 4; - if (posxxxxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + posxxxxxxxxxxxxxxxx += VarInt.size(tagIndexesCount); + posxxxxxxxxxxxxxxxx += tagIndexesCount * 4; + if (posxxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading TagIndexes"); } } - if ((nullBits[3] & 32) != 0) { - int itemAppearanceConditionsOffset = buffer.getIntLE(offset + 243); - if (itemAppearanceConditionsOffset < 0) { + if ((nullBits[3] & 64) != 0) { + int itemAppearanceConditionsOffset = buffer.getIntLE(offset + 248); + if (itemAppearanceConditionsOffset < 0 || itemAppearanceConditionsOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for ItemAppearanceConditions"); } - int posxxxxxxxxxxxxxxxxxxxxxxxx = offset + 251 + itemAppearanceConditionsOffset; - if (posxxxxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemAppearanceConditions"); - } - - int itemAppearanceConditionsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxxxxxxxx = offset + 260 + itemAppearanceConditionsOffset; + int itemAppearanceConditionsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxx); if (itemAppearanceConditionsCount < 0) { return ValidationResult.error("Invalid dictionary count for ItemAppearanceConditions"); } @@ -2334,39 +2696,35 @@ public class ItemBase { return ValidationResult.error("ItemAppearanceConditions exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxx += VarInt.size(itemAppearanceConditionsCount); - for (int ix = 0; ix < itemAppearanceConditionsCount; ix++) { - posxxxxxxxxxxxxxxxxxxxxxxxx += 4; - if (posxxxxxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + for (int i = 0; i < itemAppearanceConditionsCount; i++) { + posxxxxxxxxxxxxxxxxx += 4; + if (posxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading key"); } - int valueArrCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxxxxxx); + int valueArrCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxx); if (valueArrCount < 0) { return ValidationResult.error("Invalid array count for value"); } - posxxxxxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxx += VarInt.size(valueArrCount); for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - posxxxxxxxxxxxxxxxxxxxxxxxx += ItemAppearanceCondition.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxx += ItemAppearanceCondition.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxx); } } } - if ((nullBits[3] & 64) != 0) { - int displayEntityStatsHUDOffset = buffer.getIntLE(offset + 247); - if (displayEntityStatsHUDOffset < 0) { + if ((nullBits[3] & 128) != 0) { + int displayEntityStatsHUDOffset = buffer.getIntLE(offset + 252); + if (displayEntityStatsHUDOffset < 0 || displayEntityStatsHUDOffset > buffer.writerIndex() - offset - 260) { return ValidationResult.error("Invalid offset for DisplayEntityStatsHUD"); } - int posxxxxxxxxxxxxxxxxxxxxxxxxx = offset + 251 + displayEntityStatsHUDOffset; - if (posxxxxxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DisplayEntityStatsHUD"); - } - - int displayEntityStatsHUDCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxxxxxxx); + int posxxxxxxxxxxxxxxxxxx = offset + 260 + displayEntityStatsHUDOffset; + int displayEntityStatsHUDCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxx); if (displayEntityStatsHUDCount < 0) { return ValidationResult.error("Invalid array count for DisplayEntityStatsHUD"); } @@ -2375,13 +2733,41 @@ public class ItemBase { return ValidationResult.error("DisplayEntityStatsHUD exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxxxxxxx); - posxxxxxxxxxxxxxxxxxxxxxxxxx += displayEntityStatsHUDCount * 4; - if (posxxxxxxxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { + posxxxxxxxxxxxxxxxxxx += VarInt.size(displayEntityStatsHUDCount); + posxxxxxxxxxxxxxxxxxx += displayEntityStatsHUDCount * 4; + if (posxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading DisplayEntityStatsHUD"); } } + if ((nullBits[4] & 1) != 0) { + int hudUIOffset = buffer.getIntLE(offset + 256); + if (hudUIOffset < 0 || hudUIOffset > buffer.writerIndex() - offset - 260) { + return ValidationResult.error("Invalid offset for HudUI"); + } + + int posxxxxxxxxxxxxxxxxxxx = offset + 260 + hudUIOffset; + int hudUICount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxx); + if (hudUICount < 0) { + return ValidationResult.error("Invalid array count for HudUI"); + } + + if (hudUICount > 4096000) { + return ValidationResult.error("HudUI exceeds max length 4096000"); + } + + posxxxxxxxxxxxxxxxxxxx += VarInt.size(hudUICount); + + for (int i = 0; i < hudUICount; i++) { + ValidationResult structResult = ItemHudUI.validateStructure(buffer, posxxxxxxxxxxxxxxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ItemHudUI in HudUI[" + i + "]: " + structResult.error()); + } + + posxxxxxxxxxxxxxxxxxxx += ItemHudUI.computeBytesConsumed(buffer, posxxxxxxxxxxxxxxxxxxx); + } + } + return ValidationResult.OK; } } @@ -2416,6 +2802,7 @@ public class ItemBase { copy.itemEntity = this.itemEntity != null ? this.itemEntity.clone() : null; copy.set = this.set; copy.categories = this.categories != null ? Arrays.copyOf(this.categories, this.categories.length) : null; + copy.subCategory = this.subCategory; copy.particles = this.particles != null ? Arrays.stream(this.particles).map(ex -> ex.clone()).toArray(ModelParticle[]::new) : null; copy.firstPersonParticles = this.firstPersonParticles != null ? Arrays.stream(this.firstPersonParticles).map(ex -> ex.clone()).toArray(ModelParticle[]::new) @@ -2444,6 +2831,7 @@ public class ItemBase { copy.pullbackConfig = this.pullbackConfig != null ? this.pullbackConfig.clone() : null; copy.clipsGeometry = this.clipsGeometry; copy.renderDeployablePreview = this.renderDeployablePreview; + copy.hudUI = this.hudUI != null ? Arrays.stream(this.hudUI).map(ex -> ex.clone()).toArray(ItemHudUI[]::new) : null; return copy; } @@ -2482,6 +2870,7 @@ public class ItemBase { && Objects.equals(this.itemEntity, other.itemEntity) && Objects.equals(this.set, other.set) && Arrays.equals((Object[])this.categories, (Object[])other.categories) + && Objects.equals(this.subCategory, other.subCategory) && Arrays.equals((Object[])this.particles, (Object[])other.particles) && Arrays.equals((Object[])this.firstPersonParticles, (Object[])other.firstPersonParticles) && Arrays.equals((Object[])this.trails, (Object[])other.trails) @@ -2498,7 +2887,8 @@ public class ItemBase { && Arrays.equals(this.displayEntityStatsHUD, other.displayEntityStatsHUD) && Objects.equals(this.pullbackConfig, other.pullbackConfig) && this.clipsGeometry == other.clipsGeometry - && this.renderDeployablePreview == other.renderDeployablePreview; + && this.renderDeployablePreview == other.renderDeployablePreview + && Arrays.equals((Object[])this.hudUI, (Object[])other.hudUI); } } @@ -2533,6 +2923,7 @@ public class ItemBase { result = 31 * result + Objects.hashCode(this.itemEntity); result = 31 * result + Objects.hashCode(this.set); result = 31 * result + Arrays.hashCode((Object[])this.categories); + result = 31 * result + Objects.hashCode(this.subCategory); result = 31 * result + Arrays.hashCode((Object[])this.particles); result = 31 * result + Arrays.hashCode((Object[])this.firstPersonParticles); result = 31 * result + Arrays.hashCode((Object[])this.trails); @@ -2549,6 +2940,7 @@ public class ItemBase { result = 31 * result + Arrays.hashCode(this.displayEntityStatsHUD); result = 31 * result + Objects.hashCode(this.pullbackConfig); result = 31 * result + Boolean.hashCode(this.clipsGeometry); - return 31 * result + Boolean.hashCode(this.renderDeployablePreview); + result = 31 * result + Boolean.hashCode(this.renderDeployablePreview); + return 31 * result + Arrays.hashCode((Object[])this.hudUI); } } diff --git a/src/com/hypixel/hytale/protocol/ItemBuilderToolData.java b/src/com/hypixel/hytale/protocol/ItemBuilderToolData.java deleted file mode 100644 index 3548ccbe..00000000 --- a/src/com/hypixel/hytale/protocol/ItemBuilderToolData.java +++ /dev/null @@ -1,315 +0,0 @@ -package com.hypixel.hytale.protocol; - -import com.hypixel.hytale.protocol.io.PacketIO; -import com.hypixel.hytale.protocol.io.ProtocolException; -import com.hypixel.hytale.protocol.io.ValidationResult; -import com.hypixel.hytale.protocol.io.VarInt; -import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolState; -import io.netty.buffer.ByteBuf; -import java.util.Arrays; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class ItemBuilderToolData { - public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 1; - public static final int VARIABLE_FIELD_COUNT = 2; - public static final int VARIABLE_BLOCK_START = 9; - public static final int MAX_SIZE = 1677721600; - @Nullable - public String[] ui; - @Nullable - public BuilderToolState[] tools; - - public ItemBuilderToolData() { - } - - public ItemBuilderToolData(@Nullable String[] ui, @Nullable BuilderToolState[] tools) { - this.ui = ui; - this.tools = tools; - } - - public ItemBuilderToolData(@Nonnull ItemBuilderToolData other) { - this.ui = other.ui; - this.tools = other.tools; - } - - @Nonnull - public static ItemBuilderToolData deserialize(@Nonnull ByteBuf buf, int offset) { - ItemBuilderToolData obj = new ItemBuilderToolData(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int uiCount = VarInt.peek(buf, varPos0); - if (uiCount < 0) { - throw ProtocolException.negativeLength("Ui", uiCount); - } - - if (uiCount > 4096000) { - throw ProtocolException.arrayTooLong("Ui", uiCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + uiCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Ui", varPos0 + varIntLen + uiCount * 1, buf.readableBytes()); - } - - obj.ui = new String[uiCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < uiCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("ui[" + i + "]", strLen); - } - - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("ui[" + i + "]", strLen, 4096000); - } - - int strVarLen = VarInt.length(buf, elemPos); - obj.ui[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int toolsCount = VarInt.peek(buf, varPos1); - if (toolsCount < 0) { - throw ProtocolException.negativeLength("Tools", toolsCount); - } - - if (toolsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tools", toolsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + toolsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tools", varPos1 + varIntLen + toolsCount * 2, buf.readableBytes()); - } - - obj.tools = new BuilderToolState[toolsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < toolsCount; i++) { - obj.tools[i] = BuilderToolState.deserialize(buf, elemPos); - elemPos += BuilderToolState.computeBytesConsumed(buf, elemPos); - } - } - - return obj; - } - - public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - byte nullBits = buf.getByte(offset); - int maxEnd = 9; - if ((nullBits & 1) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 1); - int pos0 = offset + 9 + fieldOffset0; - int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); - - for (int i = 0; i < arrLen; i++) { - int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - } - - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - } - - if ((nullBits & 2) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 5); - int pos1 = offset + 9 + fieldOffset1; - int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); - - for (int i = 0; i < arrLen; i++) { - pos1 += BuilderToolState.computeBytesConsumed(buf, pos1); - } - - if (pos1 - offset > maxEnd) { - maxEnd = pos1 - offset; - } - } - - return maxEnd; - } - - public void serialize(@Nonnull ByteBuf buf) { - int startPos = buf.writerIndex(); - byte nullBits = 0; - if (this.ui != null) { - nullBits = (byte)(nullBits | 1); - } - - if (this.tools != null) { - nullBits = (byte)(nullBits | 2); - } - - buf.writeByte(nullBits); - int uiOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int toolsOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int varBlockStart = buf.writerIndex(); - if (this.ui != null) { - buf.setIntLE(uiOffsetSlot, buf.writerIndex() - varBlockStart); - if (this.ui.length > 4096000) { - throw ProtocolException.arrayTooLong("Ui", this.ui.length, 4096000); - } - - VarInt.write(buf, this.ui.length); - - for (String item : this.ui) { - PacketIO.writeVarString(buf, item, 4096000); - } - } else { - buf.setIntLE(uiOffsetSlot, -1); - } - - if (this.tools != null) { - buf.setIntLE(toolsOffsetSlot, buf.writerIndex() - varBlockStart); - if (this.tools.length > 4096000) { - throw ProtocolException.arrayTooLong("Tools", this.tools.length, 4096000); - } - - VarInt.write(buf, this.tools.length); - - for (BuilderToolState item : this.tools) { - item.serialize(buf); - } - } else { - buf.setIntLE(toolsOffsetSlot, -1); - } - } - - public int computeSize() { - int size = 9; - if (this.ui != null) { - int uiSize = 0; - - for (String elem : this.ui) { - uiSize += PacketIO.stringSize(elem); - } - - size += VarInt.size(this.ui.length) + uiSize; - } - - if (this.tools != null) { - int toolsSize = 0; - - for (BuilderToolState elem : this.tools) { - toolsSize += elem.computeSize(); - } - - size += VarInt.size(this.tools.length) + toolsSize; - } - - return size; - } - - public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 9) { - return ValidationResult.error("Buffer too small: expected at least 9 bytes"); - } else { - byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int uiOffset = buffer.getIntLE(offset + 1); - if (uiOffset < 0) { - return ValidationResult.error("Invalid offset for Ui"); - } - - int pos = offset + 9 + uiOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Ui"); - } - - int uiCount = VarInt.peek(buffer, pos); - if (uiCount < 0) { - return ValidationResult.error("Invalid array count for Ui"); - } - - if (uiCount > 4096000) { - return ValidationResult.error("Ui exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < uiCount; i++) { - int strLen = VarInt.peek(buffer, pos); - if (strLen < 0) { - return ValidationResult.error("Invalid string length in Ui"); - } - - pos += VarInt.length(buffer, pos); - pos += strLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in Ui"); - } - } - } - - if ((nullBits & 2) != 0) { - int toolsOffset = buffer.getIntLE(offset + 5); - if (toolsOffset < 0) { - return ValidationResult.error("Invalid offset for Tools"); - } - - int posx = offset + 9 + toolsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tools"); - } - - int toolsCount = VarInt.peek(buffer, posx); - if (toolsCount < 0) { - return ValidationResult.error("Invalid array count for Tools"); - } - - if (toolsCount > 4096000) { - return ValidationResult.error("Tools exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < toolsCount; i++) { - ValidationResult structResult = BuilderToolState.validateStructure(buffer, posx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid BuilderToolState in Tools[" + i + "]: " + structResult.error()); - } - - posx += BuilderToolState.computeBytesConsumed(buffer, posx); - } - } - - return ValidationResult.OK; - } - } - - public ItemBuilderToolData clone() { - ItemBuilderToolData copy = new ItemBuilderToolData(); - copy.ui = this.ui != null ? Arrays.copyOf(this.ui, this.ui.length) : null; - copy.tools = this.tools != null ? Arrays.stream(this.tools).map(e -> e.clone()).toArray(BuilderToolState[]::new) : null; - return copy; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else { - return !(obj instanceof ItemBuilderToolData other) - ? false - : Arrays.equals((Object[])this.ui, (Object[])other.ui) && Arrays.equals((Object[])this.tools, (Object[])other.tools); - } - } - - @Override - public int hashCode() { - int result = 1; - result = 31 * result + Arrays.hashCode((Object[])this.ui); - return 31 * result + Arrays.hashCode((Object[])this.tools); - } -} diff --git a/src/com/hypixel/hytale/protocol/ItemCategory.java b/src/com/hypixel/hytale/protocol/ItemCategory.java index da5c11a1..46465a22 100644 --- a/src/com/hypixel/hytale/protocol/ItemCategory.java +++ b/src/com/hypixel/hytale/protocol/ItemCategory.java @@ -13,8 +13,8 @@ import javax.annotation.Nullable; public class ItemCategory { public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 6; - public static final int VARIABLE_FIELD_COUNT = 4; - public static final int VARIABLE_BLOCK_START = 22; + public static final int VARIABLE_FIELD_COUNT = 5; + public static final int VARIABLE_BLOCK_START = 26; public static final int MAX_SIZE = 1677721600; @Nullable public String id; @@ -27,6 +27,8 @@ public class ItemCategory { public ItemGridInfoDisplayMode infoDisplayMode = ItemGridInfoDisplayMode.Tooltip; @Nullable public ItemCategory[] children; + @Nullable + public SubCategoryDefinition[] subCategories; public ItemCategory() { } @@ -37,7 +39,8 @@ public class ItemCategory { @Nullable String icon, int order, @Nonnull ItemGridInfoDisplayMode infoDisplayMode, - @Nullable ItemCategory[] children + @Nullable ItemCategory[] children, + @Nullable SubCategoryDefinition[] subCategories ) { this.id = id; this.name = name; @@ -45,6 +48,7 @@ public class ItemCategory { this.order = order; this.infoDisplayMode = infoDisplayMode; this.children = children; + this.subCategories = subCategories; } public ItemCategory(@Nonnull ItemCategory other) { @@ -54,92 +58,166 @@ public class ItemCategory { this.order = other.order; this.infoDisplayMode = other.infoDisplayMode; this.children = other.children; + this.subCategories = other.subCategories; } @Nonnull public static ItemCategory deserialize(@Nonnull ByteBuf buf, int offset) { - ItemCategory obj = new ItemCategory(); - byte nullBits = buf.getByte(offset); - obj.order = buf.getIntLE(offset + 1); - obj.infoDisplayMode = ItemGridInfoDisplayMode.fromValue(buf.getByte(offset + 5)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 22 + buf.getIntLE(offset + 6); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 26) { + throw ProtocolException.bufferTooSmall("ItemCategory", 26, buf.readableBytes() - offset); + } else { + ItemCategory obj = new ItemCategory(); + byte nullBits = buf.getByte(offset); + obj.order = buf.getIntLE(offset + 1); + obj.infoDisplayMode = ItemGridInfoDisplayMode.fromValue(buf.getByte(offset + 5)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 26 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 10); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Name", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 26 + varPosBase1; + int nameLen = VarInt.peek(buf, varPos1); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos1 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos1 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 14); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Icon", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 26 + varPosBase2; + int iconLen = VarInt.peek(buf, varPos2); + if (iconLen < 0) { + throw ProtocolException.invalidVarInt("Icon"); + } + + int iconVarIntLen = VarInt.size(iconLen); + if (iconLen > 4096000) { + throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + } + + if (varPos2 + iconVarIntLen + iconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Icon", varPos2 + iconVarIntLen + iconLen, buf.readableBytes()); + } + + obj.icon = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 18); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Children", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 26 + varPosBase3; + int childrenCount = VarInt.peek(buf, varPos3); + if (childrenCount < 0) { + throw ProtocolException.invalidVarInt("Children"); + } + + int varIntLen = VarInt.size(childrenCount); + if (childrenCount > 4096000) { + throw ProtocolException.arrayTooLong("Children", childrenCount, 4096000); + } + + if (varPos3 + varIntLen + childrenCount * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Children", varPos3 + varIntLen + childrenCount * 6, buf.readableBytes()); + } + + obj.children = new ItemCategory[childrenCount]; + int elemPos = varPos3 + varIntLen; + + for (int i = 0; i < childrenCount; i++) { + obj.children[i] = deserialize(buf, elemPos); + elemPos += computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 22); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("SubCategories", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 26 + varPosBase4; + int subCategoriesCount = VarInt.peek(buf, varPos4); + if (subCategoriesCount < 0) { + throw ProtocolException.invalidVarInt("SubCategories"); + } + + int varIntLenx = VarInt.size(subCategoriesCount); + if (subCategoriesCount > 4096000) { + throw ProtocolException.arrayTooLong("SubCategories", subCategoriesCount, 4096000); + } + + if (varPos4 + varIntLenx + subCategoriesCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SubCategories", varPos4 + varIntLenx + subCategoriesCount * 5, buf.readableBytes()); + } + + obj.subCategories = new SubCategoryDefinition[subCategoriesCount]; + int elemPos = varPos4 + varIntLenx; + + for (int i = 0; i < subCategoriesCount; i++) { + obj.subCategories[i] = SubCategoryDefinition.deserialize(buf, elemPos); + elemPos += SubCategoryDefinition.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 22 + buf.getIntLE(offset + 10); - int nameLen = VarInt.peek(buf, varPos1); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); - } - - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); - } - - obj.name = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 22 + buf.getIntLE(offset + 14); - int iconLen = VarInt.peek(buf, varPos2); - if (iconLen < 0) { - throw ProtocolException.negativeLength("Icon", iconLen); - } - - if (iconLen > 4096000) { - throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); - } - - obj.icon = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 22 + buf.getIntLE(offset + 18); - int childrenCount = VarInt.peek(buf, varPos3); - if (childrenCount < 0) { - throw ProtocolException.negativeLength("Children", childrenCount); - } - - if (childrenCount > 4096000) { - throw ProtocolException.arrayTooLong("Children", childrenCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + childrenCount * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Children", varPos3 + varIntLen + childrenCount * 6, buf.readableBytes()); - } - - obj.children = new ItemCategory[childrenCount]; - int elemPos = varPos3 + varIntLen; - - for (int i = 0; i < childrenCount; i++) { - obj.children[i] = deserialize(buf, elemPos); - elemPos += computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 22; + int maxEnd = 26; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6); - int pos0 = offset + 22 + fieldOffset0; + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + + int pos0 = offset + 26 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -147,9 +225,13 @@ public class ItemCategory { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 10); - int pos1 = offset + 22 + fieldOffset1; + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Name", fieldOffset1, maxEnd); + } + + int pos1 = offset + 26 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -157,9 +239,13 @@ public class ItemCategory { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 14); - int pos2 = offset + 22 + fieldOffset2; + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Icon", fieldOffset2, maxEnd); + } + + int pos2 = offset + 26 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -167,9 +253,13 @@ public class ItemCategory { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 18); - int pos3 = offset + 22 + fieldOffset3; + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Children", fieldOffset3, maxEnd); + } + + int pos3 = offset + 26 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos3 += computeBytesConsumed(buf, pos3); @@ -180,6 +270,25 @@ public class ItemCategory { } } + if ((nullBits & 16) != 0) { + int fieldOffset4 = buf.getIntLE(offset + 22); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("SubCategories", fieldOffset4, maxEnd); + } + + int pos4 = offset + 26 + fieldOffset4; + int arrLen = VarInt.peek(buf, pos4); + pos4 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos4 += SubCategoryDefinition.computeBytesConsumed(buf, pos4); + } + + if (pos4 - offset > maxEnd) { + maxEnd = pos4 - offset; + } + } + return maxEnd; } @@ -202,6 +311,10 @@ public class ItemCategory { nullBits = (byte)(nullBits | 8); } + if (this.subCategories != null) { + nullBits = (byte)(nullBits | 16); + } + buf.writeByte(nullBits); buf.writeIntLE(this.order); buf.writeByte(this.infoDisplayMode.getValue()); @@ -213,6 +326,8 @@ public class ItemCategory { buf.writeIntLE(0); int childrenOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int subCategoriesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); if (this.id != null) { buf.setIntLE(idOffsetSlot, buf.writerIndex() - varBlockStart); @@ -249,10 +364,25 @@ public class ItemCategory { } else { buf.setIntLE(childrenOffsetSlot, -1); } + + if (this.subCategories != null) { + buf.setIntLE(subCategoriesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.subCategories.length > 4096000) { + throw ProtocolException.arrayTooLong("SubCategories", this.subCategories.length, 4096000); + } + + VarInt.write(buf, this.subCategories.length); + + for (SubCategoryDefinition item : this.subCategories) { + item.serialize(buf); + } + } else { + buf.setIntLE(subCategoriesOffsetSlot, -1); + } } public int computeSize() { - int size = 22; + int size = 26; if (this.id != null) { size += PacketIO.stringSize(this.id); } @@ -275,128 +405,155 @@ public class ItemCategory { size += VarInt.size(this.children.length) + childrenSize; } + if (this.subCategories != null) { + int subCategoriesSize = 0; + + for (SubCategoryDefinition elem : this.subCategories) { + subCategoriesSize += elem.computeSize(); + } + + size += VarInt.size(this.subCategories.length) + subCategoriesSize; + } + return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 22) { - return ValidationResult.error("Buffer too small: expected at least 22 bytes"); + if (buffer.readableBytes() - offset < 26) { + return ValidationResult.error("Buffer too small: expected at least 26 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int idOffset = buffer.getIntLE(offset + 6); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); - } - - int pos = offset + 22 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } - - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); - } - } - - if ((nullBits & 2) != 0) { - int nameOffset = buffer.getIntLE(offset + 10); - if (nameOffset < 0) { - return ValidationResult.error("Invalid offset for Name"); - } - - int posx = offset + 22 + nameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - - int nameLen = VarInt.peek(buffer, posx); - if (nameLen < 0) { - return ValidationResult.error("Invalid string length for Name"); - } - - if (nameLen > 4096000) { - return ValidationResult.error("Name exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += nameLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Name"); - } - } - - if ((nullBits & 4) != 0) { - int iconOffset = buffer.getIntLE(offset + 14); - if (iconOffset < 0) { - return ValidationResult.error("Invalid offset for Icon"); - } - - int posxx = offset + 22 + iconOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Icon"); - } - - int iconLen = VarInt.peek(buffer, posxx); - if (iconLen < 0) { - return ValidationResult.error("Invalid string length for Icon"); - } - - if (iconLen > 4096000) { - return ValidationResult.error("Icon exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += iconLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Icon"); - } - } - - if ((nullBits & 8) != 0) { - int childrenOffset = buffer.getIntLE(offset + 18); - if (childrenOffset < 0) { - return ValidationResult.error("Invalid offset for Children"); - } - - int posxxx = offset + 22 + childrenOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Children"); - } - - int childrenCount = VarInt.peek(buffer, posxxx); - if (childrenCount < 0) { - return ValidationResult.error("Invalid array count for Children"); - } - - if (childrenCount > 4096000) { - return ValidationResult.error("Children exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - - for (int i = 0; i < childrenCount; i++) { - ValidationResult structResult = validateStructure(buffer, posxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ItemCategory in Children[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 5) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid ItemGridInfoDisplayMode value for InfoDisplayMode"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 26) { + return ValidationResult.error("Invalid offset for Id"); } - posxxx += computeBytesConsumed(buffer, posxxx); - } - } + int pos = offset + 26 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } - return ValidationResult.OK; + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 26) { + return ValidationResult.error("Invalid offset for Name"); + } + + int posx = offset + 26 + v; + int nameLen = VarInt.peek(buffer, posx); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } + + if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); + } + + posx += VarInt.size(nameLen); + posx += nameLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); + } + } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 14); + if (v < 0 || v > buffer.writerIndex() - offset - 26) { + return ValidationResult.error("Invalid offset for Icon"); + } + + int posxx = offset + 26 + v; + int iconLen = VarInt.peek(buffer, posxx); + if (iconLen < 0) { + return ValidationResult.error("Invalid string length for Icon"); + } + + if (iconLen > 4096000) { + return ValidationResult.error("Icon exceeds max length 4096000"); + } + + posxx += VarInt.size(iconLen); + posxx += iconLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Icon"); + } + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 18); + if (v < 0 || v > buffer.writerIndex() - offset - 26) { + return ValidationResult.error("Invalid offset for Children"); + } + + int posxxx = offset + 26 + v; + int childrenCount = VarInt.peek(buffer, posxxx); + if (childrenCount < 0) { + return ValidationResult.error("Invalid array count for Children"); + } + + if (childrenCount > 4096000) { + return ValidationResult.error("Children exceeds max length 4096000"); + } + + posxxx += VarInt.size(childrenCount); + + for (int i = 0; i < childrenCount; i++) { + ValidationResult structResult = validateStructure(buffer, posxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ItemCategory in Children[" + i + "]: " + structResult.error()); + } + + posxxx += computeBytesConsumed(buffer, posxxx); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 22); + if (v < 0 || v > buffer.writerIndex() - offset - 26) { + return ValidationResult.error("Invalid offset for SubCategories"); + } + + int posxxxx = offset + 26 + v; + int subCategoriesCount = VarInt.peek(buffer, posxxxx); + if (subCategoriesCount < 0) { + return ValidationResult.error("Invalid array count for SubCategories"); + } + + if (subCategoriesCount > 4096000) { + return ValidationResult.error("SubCategories exceeds max length 4096000"); + } + + posxxxx += VarInt.size(subCategoriesCount); + + for (int i = 0; i < subCategoriesCount; i++) { + ValidationResult structResult = SubCategoryDefinition.validateStructure(buffer, posxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid SubCategoryDefinition in SubCategories[" + i + "]: " + structResult.error()); + } + + posxxxx += SubCategoryDefinition.computeBytesConsumed(buffer, posxxxx); + } + } + + return ValidationResult.OK; + } } } @@ -408,6 +565,7 @@ public class ItemCategory { copy.order = this.order; copy.infoDisplayMode = this.infoDisplayMode; copy.children = this.children != null ? Arrays.stream(this.children).map(e -> e.clone()).toArray(ItemCategory[]::new) : null; + copy.subCategories = this.subCategories != null ? Arrays.stream(this.subCategories).map(e -> e.clone()).toArray(SubCategoryDefinition[]::new) : null; return copy; } @@ -423,7 +581,8 @@ public class ItemCategory { && Objects.equals(this.icon, other.icon) && this.order == other.order && Objects.equals(this.infoDisplayMode, other.infoDisplayMode) - && Arrays.equals((Object[])this.children, (Object[])other.children); + && Arrays.equals((Object[])this.children, (Object[])other.children) + && Arrays.equals((Object[])this.subCategories, (Object[])other.subCategories); } } @@ -435,6 +594,7 @@ public class ItemCategory { result = 31 * result + Objects.hashCode(this.icon); result = 31 * result + Integer.hashCode(this.order); result = 31 * result + Objects.hashCode(this.infoDisplayMode); - return 31 * result + Arrays.hashCode((Object[])this.children); + result = 31 * result + Arrays.hashCode((Object[])this.children); + return 31 * result + Arrays.hashCode((Object[])this.subCategories); } } diff --git a/src/com/hypixel/hytale/protocol/ItemEntityConfig.java b/src/com/hypixel/hytale/protocol/ItemEntityConfig.java index 1f9a94f4..89c8c38b 100644 --- a/src/com/hypixel/hytale/protocol/ItemEntityConfig.java +++ b/src/com/hypixel/hytale/protocol/ItemEntityConfig.java @@ -38,30 +38,38 @@ public class ItemEntityConfig { @Nonnull public static ItemEntityConfig deserialize(@Nonnull ByteBuf buf, int offset) { - ItemEntityConfig obj = new ItemEntityConfig(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.particleColor = Color.deserialize(buf, offset + 1); - } - - obj.showItemParticles = buf.getByte(offset + 4) != 0; - int pos = offset + 5; - if ((nullBits & 2) != 0) { - int particleSystemIdLen = VarInt.peek(buf, pos); - if (particleSystemIdLen < 0) { - throw ProtocolException.negativeLength("ParticleSystemId", particleSystemIdLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("ItemEntityConfig", 5, buf.readableBytes() - offset); + } else { + ItemEntityConfig obj = new ItemEntityConfig(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.particleColor = Color.deserialize(buf, offset + 1); } - if (particleSystemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ParticleSystemId", particleSystemIdLen, 4096000); + obj.showItemParticles = buf.getByte(offset + 4) != 0; + int pos = offset + 5; + if ((nullBits & 2) != 0) { + int particleSystemIdLen = VarInt.peek(buf, pos); + if (particleSystemIdLen < 0) { + throw ProtocolException.invalidVarInt("ParticleSystemId"); + } + + int particleSystemIdVarLen = VarInt.size(particleSystemIdLen); + if (particleSystemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ParticleSystemId", particleSystemIdLen, 4096000); + } + + if (pos + particleSystemIdVarLen + particleSystemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ParticleSystemId", pos + particleSystemIdVarLen + particleSystemIdLen, buf.readableBytes()); + } + + obj.particleSystemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += particleSystemIdVarLen + particleSystemIdLen; } - int particleSystemIdVarLen = VarInt.length(buf, pos); - obj.particleSystemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += particleSystemIdVarLen + particleSystemIdLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -69,7 +77,7 @@ public class ItemEntityConfig { int pos = offset + 5; if ((nullBits & 2) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -123,7 +131,7 @@ public class ItemEntityConfig { return ValidationResult.error("ParticleSystemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(particleSystemIdLen); pos += particleSystemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ParticleSystemId"); diff --git a/src/com/hypixel/hytale/protocol/ItemGlider.java b/src/com/hypixel/hytale/protocol/ItemGlider.java index 46888df7..2bf1390a 100644 --- a/src/com/hypixel/hytale/protocol/ItemGlider.java +++ b/src/com/hypixel/hytale/protocol/ItemGlider.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -35,12 +36,16 @@ public class ItemGlider { @Nonnull public static ItemGlider deserialize(@Nonnull ByteBuf buf, int offset) { - ItemGlider obj = new ItemGlider(); - obj.terminalVelocity = buf.getFloatLE(offset + 0); - obj.fallSpeedMultiplier = buf.getFloatLE(offset + 4); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 8); - obj.speed = buf.getFloatLE(offset + 12); - return obj; + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("ItemGlider", 16, buf.readableBytes() - offset); + } else { + ItemGlider obj = new ItemGlider(); + obj.terminalVelocity = buf.getFloatLE(offset + 0); + obj.fallSpeedMultiplier = buf.getFloatLE(offset + 4); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 8); + obj.speed = buf.getFloatLE(offset + 12); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ItemHudUI.java b/src/com/hypixel/hytale/protocol/ItemHudUI.java new file mode 100644 index 00000000..3807b354 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/ItemHudUI.java @@ -0,0 +1,153 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ItemHudUI { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 2; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 2; + public static final int MAX_SIZE = 16384007; + @Nullable + public String path; + @Nonnull + public ItemHudUIType type = ItemHudUIType.Hud; + + public ItemHudUI() { + } + + public ItemHudUI(@Nullable String path, @Nonnull ItemHudUIType type) { + this.path = path; + this.type = type; + } + + public ItemHudUI(@Nonnull ItemHudUI other) { + this.path = other.path; + this.type = other.type; + } + + @Nonnull + public static ItemHudUI deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("ItemHudUI", 2, buf.readableBytes() - offset); + } else { + ItemHudUI obj = new ItemHudUI(); + byte nullBits = buf.getByte(offset); + obj.type = ItemHudUIType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int pathLen = VarInt.peek(buf, pos); + if (pathLen < 0) { + throw ProtocolException.invalidVarInt("Path"); + } + + int pathVarLen = VarInt.size(pathLen); + if (pathLen > 4096000) { + throw ProtocolException.stringTooLong("Path", pathLen, 4096000); + } + + if (pos + pathVarLen + pathLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Path", pos + pathVarLen + pathLen, buf.readableBytes()); + } + + obj.path = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += pathVarLen + pathLen; + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + } + + return pos - offset; + } + + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.path != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + buf.writeByte(this.type.getValue()); + if (this.path != null) { + PacketIO.writeVarString(buf, this.path, 4096000); + } + } + + public int computeSize() { + int size = 2; + if (this.path != null) { + size += PacketIO.stringSize(this.path); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 2) { + return ValidationResult.error("Buffer too small: expected at least 2 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ItemHudUIType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int pathLen = VarInt.peek(buffer, v); + if (pathLen < 0) { + return ValidationResult.error("Invalid string length for Path"); + } + + if (pathLen > 4096000) { + return ValidationResult.error("Path exceeds max length 4096000"); + } + + v += VarInt.size(pathLen); + v += pathLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Path"); + } + } + + return ValidationResult.OK; + } + } + } + + public ItemHudUI clone() { + ItemHudUI copy = new ItemHudUI(); + copy.path = this.path; + copy.type = this.type; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof ItemHudUI other) ? false : Objects.equals(this.path, other.path) && Objects.equals(this.type, other.type); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.path, this.type); + } +} diff --git a/src/com/hypixel/hytale/protocol/ItemHudUIType.java b/src/com/hypixel/hytale/protocol/ItemHudUIType.java new file mode 100644 index 00000000..109159c1 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/ItemHudUIType.java @@ -0,0 +1,27 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum ItemHudUIType { + Hud(0), + Legend(1); + + public static final ItemHudUIType[] VALUES = values(); + private final int value; + + private ItemHudUIType(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static ItemHudUIType fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("ItemHudUIType", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/ItemLibrary.java b/src/com/hypixel/hytale/protocol/ItemLibrary.java index 5590433e..23ae4e65 100644 --- a/src/com/hypixel/hytale/protocol/ItemLibrary.java +++ b/src/com/hypixel/hytale/protocol/ItemLibrary.java @@ -38,81 +38,103 @@ public class ItemLibrary { @Nonnull public static ItemLibrary deserialize(@Nonnull ByteBuf buf, int offset) { - ItemLibrary obj = new ItemLibrary(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int itemsCount = VarInt.peek(buf, varPos0); - if (itemsCount < 0) { - throw ProtocolException.negativeLength("Items", itemsCount); - } - - if (itemsCount > 4096000) { - throw ProtocolException.arrayTooLong("Items", itemsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + itemsCount * 147L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Items", varPos0 + varIntLen + itemsCount * 147, buf.readableBytes()); - } - - obj.items = new ItemBase[itemsCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < itemsCount; i++) { - obj.items[i] = ItemBase.deserialize(buf, elemPos); - elemPos += ItemBase.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int blockMapCount = VarInt.peek(buf, varPos1); - if (blockMapCount < 0) { - throw ProtocolException.negativeLength("BlockMap", blockMapCount); - } - - if (blockMapCount > 4096000) { - throw ProtocolException.arrayTooLong("BlockMap", blockMapCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - Map[] blockMapArr = new Map[blockMapCount]; - obj.blockMap = blockMapArr; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < blockMapCount; i++) { - int mapLen = VarInt.peek(buf, elemPos); - int mapVarLen = VarInt.length(buf, elemPos); - HashMap map = new HashMap<>(mapLen); - int mapPos = elemPos + mapVarLen; - - for (int j = 0; j < mapLen; j++) { - int key = buf.getIntLE(mapPos); - mapPos += 4; - int valLen = VarInt.peek(buf, mapPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); - } - - if (valLen > 4096000) { - throw ProtocolException.stringTooLong("val", valLen, 4096000); - } - - int valVarLen = VarInt.length(buf, mapPos); - String val = PacketIO.readVarString(buf, mapPos); - mapPos += valVarLen + valLen; - if (map.put(key, val) != null) { - throw ProtocolException.duplicateKey("BlockMap[" + i + "]", key); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ItemLibrary", 9, buf.readableBytes() - offset); + } else { + ItemLibrary obj = new ItemLibrary(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Items", varPosBase0, buf.readableBytes()); } - obj.blockMap[i] = map; - elemPos = mapPos; - } - } + int varPos0 = offset + 9 + varPosBase0; + int itemsCount = VarInt.peek(buf, varPos0); + if (itemsCount < 0) { + throw ProtocolException.invalidVarInt("Items"); + } - return obj; + int varIntLen = VarInt.size(itemsCount); + if (itemsCount > 4096000) { + throw ProtocolException.arrayTooLong("Items", itemsCount, 4096000); + } + + if (varPos0 + varIntLen + itemsCount * 148L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Items", varPos0 + varIntLen + itemsCount * 148, buf.readableBytes()); + } + + obj.items = new ItemBase[itemsCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < itemsCount; i++) { + obj.items[i] = ItemBase.deserialize(buf, elemPos); + elemPos += ItemBase.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("BlockMap", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int blockMapCount = VarInt.peek(buf, varPos1); + if (blockMapCount < 0) { + throw ProtocolException.invalidVarInt("BlockMap"); + } + + int varIntLenx = VarInt.size(blockMapCount); + if (blockMapCount > 4096000) { + throw ProtocolException.arrayTooLong("BlockMap", blockMapCount, 4096000); + } + + Map[] blockMapArr = new Map[blockMapCount]; + obj.blockMap = blockMapArr; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < blockMapCount; i++) { + int mapLen = VarInt.peek(buf, elemPos); + if (mapLen < 0) { + throw ProtocolException.invalidVarInt("BlockMap[" + i + "]"); + } + + int mapVarLen = VarInt.size(mapLen); + HashMap map = new HashMap<>(mapLen); + int mapPos = elemPos + mapVarLen; + + for (int j = 0; j < mapLen; j++) { + int key = buf.getIntLE(mapPos); + mapPos += 4; + int valLen = VarInt.peek(buf, mapPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 4096000) { + throw ProtocolException.stringTooLong("val", valLen, 4096000); + } + + if (mapPos + valVarLen + valLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", mapPos + valVarLen + valLen, buf.readableBytes()); + } + + String val = PacketIO.readVarString(buf, mapPos); + mapPos += valVarLen + valLen; + if (map.put(key, val) != null) { + throw ProtocolException.duplicateKey("BlockMap[" + i + "]", key); + } + } + + obj.blockMap[i] = map; + elemPos = mapPos; + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -120,9 +142,13 @@ public class ItemLibrary { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Items", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += ItemBase.computeBytesConsumed(buf, pos0); @@ -135,18 +161,22 @@ public class ItemLibrary { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("BlockMap", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int j = 0; j < dictLen; j++) { pos1 += 4; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } } @@ -243,15 +273,11 @@ public class ItemLibrary { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int itemsOffset = buffer.getIntLE(offset + 1); - if (itemsOffset < 0) { + if (itemsOffset < 0 || itemsOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Items"); } int pos = offset + 9 + itemsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Items"); - } - int itemsCount = VarInt.peek(buffer, pos); if (itemsCount < 0) { return ValidationResult.error("Invalid array count for Items"); @@ -261,7 +287,7 @@ public class ItemLibrary { return ValidationResult.error("Items exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(itemsCount); for (int i = 0; i < itemsCount; i++) { ValidationResult structResult = ItemBase.validateStructure(buffer, pos); @@ -275,15 +301,11 @@ public class ItemLibrary { if ((nullBits & 2) != 0) { int blockMapOffset = buffer.getIntLE(offset + 5); - if (blockMapOffset < 0) { + if (blockMapOffset < 0 || blockMapOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for BlockMap"); } int posx = offset + 9 + blockMapOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockMap"); - } - int blockMapCount = VarInt.peek(buffer, posx); if (blockMapCount < 0) { return ValidationResult.error("Invalid array count for BlockMap"); @@ -293,7 +315,7 @@ public class ItemLibrary { return ValidationResult.error("BlockMap exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(blockMapCount); for (int i = 0; i < blockMapCount; i++) { int blockMapDictLen = VarInt.peek(buffer, posx); @@ -301,7 +323,7 @@ public class ItemLibrary { return ValidationResult.error("Invalid dictionary count in BlockMap[" + i + "]"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(blockMapDictLen); for (int j = 0; j < blockMapDictLen; j++) { posx += 4; @@ -318,7 +340,7 @@ public class ItemLibrary { return ValidationResult.error("blockMapVal exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(blockMapValLen); posx += blockMapValLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading blockMapVal"); diff --git a/src/com/hypixel/hytale/protocol/ItemPlayerAnimations.java b/src/com/hypixel/hytale/protocol/ItemPlayerAnimations.java index e748e722..05c2477d 100644 --- a/src/com/hypixel/hytale/protocol/ItemPlayerAnimations.java +++ b/src/com/hypixel/hytale/protocol/ItemPlayerAnimations.java @@ -60,73 +60,101 @@ public class ItemPlayerAnimations { @Nonnull public static ItemPlayerAnimations deserialize(@Nonnull ByteBuf buf, int offset) { - ItemPlayerAnimations obj = new ItemPlayerAnimations(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.wiggleWeights = WiggleWeights.deserialize(buf, offset + 1); - } - - if ((nullBits & 2) != 0) { - obj.pullbackConfig = ItemPullbackConfiguration.deserialize(buf, offset + 41); - } - - obj.useFirstPersonOverride = buf.getByte(offset + 90) != 0; - if ((nullBits & 4) != 0) { - int varPos0 = offset + 103 + buf.getIntLE(offset + 91); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 103) { + throw ProtocolException.bufferTooSmall("ItemPlayerAnimations", 103, buf.readableBytes() - offset); + } else { + ItemPlayerAnimations obj = new ItemPlayerAnimations(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.wiggleWeights = WiggleWeights.deserialize(buf, offset + 1); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + obj.pullbackConfig = ItemPullbackConfiguration.deserialize(buf, offset + 41); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos1 = offset + 103 + buf.getIntLE(offset + 95); - int animationsCount = VarInt.peek(buf, varPos1); - if (animationsCount < 0) { - throw ProtocolException.negativeLength("Animations", animationsCount); - } - - if (animationsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Animations", animationsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.animations = new HashMap<>(animationsCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < animationsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + obj.useFirstPersonOverride = buf.getByte(offset + 90) != 0; + if ((nullBits & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 91); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 103) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 103 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - ItemAnimation val = ItemAnimation.deserialize(buf, dictPos); - dictPos += ItemAnimation.computeBytesConsumed(buf, dictPos); - if (obj.animations.put(key, val) != null) { - throw ProtocolException.duplicateKey("animations", key); + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 95); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 103) { + throw ProtocolException.invalidOffset("Animations", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 103 + varPosBase1; + int animationsCount = VarInt.peek(buf, varPos1); + if (animationsCount < 0) { + throw ProtocolException.invalidVarInt("Animations"); + } + + int varIntLen = VarInt.size(animationsCount); + if (animationsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Animations", animationsCount, 4096000); + } + + obj.animations = new HashMap<>(animationsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < animationsCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + ItemAnimation val = ItemAnimation.deserialize(buf, dictPos); + dictPos += ItemAnimation.computeBytesConsumed(buf, dictPos); + if (obj.animations.put(key, val) != null) { + throw ProtocolException.duplicateKey("animations", key); + } } } - } - if ((nullBits & 16) != 0) { - int varPos2 = offset + 103 + buf.getIntLE(offset + 99); - obj.camera = CameraSettings.deserialize(buf, varPos2); - } + if ((nullBits & 16) != 0) { + int varPosBase2 = buf.getIntLE(offset + 99); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 103) { + throw ProtocolException.invalidOffset("Camera", varPosBase2, buf.readableBytes()); + } - return obj; + int varPos2 = offset + 103 + varPosBase2; + obj.camera = CameraSettings.deserialize(buf, varPos2); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -134,9 +162,13 @@ public class ItemPlayerAnimations { int maxEnd = 103; if ((nullBits & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 91); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 103) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 103 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -144,13 +176,17 @@ public class ItemPlayerAnimations { if ((nullBits & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 95); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 103) { + throw ProtocolException.invalidOffset("Animations", fieldOffset1, maxEnd); + } + int pos1 = offset + 103 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; pos1 += ItemAnimation.computeBytesConsumed(buf, pos1); } @@ -161,6 +197,10 @@ public class ItemPlayerAnimations { if ((nullBits & 16) != 0) { int fieldOffset2 = buf.getIntLE(offset + 99); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 103) { + throw ProtocolException.invalidOffset("Camera", fieldOffset2, maxEnd); + } + int pos2 = offset + 103 + fieldOffset2; pos2 += CameraSettings.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -276,15 +316,11 @@ public class ItemPlayerAnimations { byte nullBits = buffer.getByte(offset); if ((nullBits & 4) != 0) { int idOffset = buffer.getIntLE(offset + 91); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 103) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 103 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -294,7 +330,7 @@ public class ItemPlayerAnimations { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -303,15 +339,11 @@ public class ItemPlayerAnimations { if ((nullBits & 8) != 0) { int animationsOffset = buffer.getIntLE(offset + 95); - if (animationsOffset < 0) { + if (animationsOffset < 0 || animationsOffset > buffer.writerIndex() - offset - 103) { return ValidationResult.error("Invalid offset for Animations"); } int posx = offset + 103 + animationsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Animations"); - } - int animationsCount = VarInt.peek(buffer, posx); if (animationsCount < 0) { return ValidationResult.error("Invalid dictionary count for Animations"); @@ -321,7 +353,7 @@ public class ItemPlayerAnimations { return ValidationResult.error("Animations exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(animationsCount); for (int i = 0; i < animationsCount; i++) { int keyLen = VarInt.peek(buffer, posx); @@ -333,7 +365,7 @@ public class ItemPlayerAnimations { return ValidationResult.error("key exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(keyLen); posx += keyLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading key"); @@ -345,15 +377,11 @@ public class ItemPlayerAnimations { if ((nullBits & 16) != 0) { int cameraOffset = buffer.getIntLE(offset + 99); - if (cameraOffset < 0) { + if (cameraOffset < 0 || cameraOffset > buffer.writerIndex() - offset - 103) { return ValidationResult.error("Invalid offset for Camera"); } int posxx = offset + 103 + cameraOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - ValidationResult cameraResult = CameraSettings.validateStructure(buffer, posxx); if (!cameraResult.isValid()) { return ValidationResult.error("Invalid Camera: " + cameraResult.error()); diff --git a/src/com/hypixel/hytale/protocol/ItemPullbackConfiguration.java b/src/com/hypixel/hytale/protocol/ItemPullbackConfiguration.java index 8cc6180d..e9930415 100644 --- a/src/com/hypixel/hytale/protocol/ItemPullbackConfiguration.java +++ b/src/com/hypixel/hytale/protocol/ItemPullbackConfiguration.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class ItemPullbackConfiguration { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -13,22 +16,22 @@ public class ItemPullbackConfiguration { public static final int VARIABLE_BLOCK_START = 49; public static final int MAX_SIZE = 49; @Nullable - public Vector3f leftOffsetOverride; + public Vector3fc leftOffsetOverride; @Nullable - public Vector3f leftRotationOverride; + public Vector3fc leftRotationOverride; @Nullable - public Vector3f rightOffsetOverride; + public Vector3fc rightOffsetOverride; @Nullable - public Vector3f rightRotationOverride; + public Vector3fc rightRotationOverride; public ItemPullbackConfiguration() { } public ItemPullbackConfiguration( - @Nullable Vector3f leftOffsetOverride, - @Nullable Vector3f leftRotationOverride, - @Nullable Vector3f rightOffsetOverride, - @Nullable Vector3f rightRotationOverride + @Nullable Vector3fc leftOffsetOverride, + @Nullable Vector3fc leftRotationOverride, + @Nullable Vector3fc rightOffsetOverride, + @Nullable Vector3fc rightRotationOverride ) { this.leftOffsetOverride = leftOffsetOverride; this.leftRotationOverride = leftRotationOverride; @@ -45,25 +48,29 @@ public class ItemPullbackConfiguration { @Nonnull public static ItemPullbackConfiguration deserialize(@Nonnull ByteBuf buf, int offset) { - ItemPullbackConfiguration obj = new ItemPullbackConfiguration(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.leftOffsetOverride = Vector3f.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 49) { + throw ProtocolException.bufferTooSmall("ItemPullbackConfiguration", 49, buf.readableBytes() - offset); + } else { + ItemPullbackConfiguration obj = new ItemPullbackConfiguration(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.leftOffsetOverride = PacketIO.readVector3f(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.leftRotationOverride = Vector3f.deserialize(buf, offset + 13); - } + if ((nullBits & 2) != 0) { + obj.leftRotationOverride = PacketIO.readVector3f(buf, offset + 13); + } - if ((nullBits & 4) != 0) { - obj.rightOffsetOverride = Vector3f.deserialize(buf, offset + 25); - } + if ((nullBits & 4) != 0) { + obj.rightOffsetOverride = PacketIO.readVector3f(buf, offset + 25); + } - if ((nullBits & 8) != 0) { - obj.rightRotationOverride = Vector3f.deserialize(buf, offset + 37); - } + if ((nullBits & 8) != 0) { + obj.rightRotationOverride = PacketIO.readVector3f(buf, offset + 37); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,25 +97,25 @@ public class ItemPullbackConfiguration { buf.writeByte(nullBits); if (this.leftOffsetOverride != null) { - this.leftOffsetOverride.serialize(buf); + PacketIO.writeVector3f(buf, this.leftOffsetOverride); } else { buf.writeZero(12); } if (this.leftRotationOverride != null) { - this.leftRotationOverride.serialize(buf); + PacketIO.writeVector3f(buf, this.leftRotationOverride); } else { buf.writeZero(12); } if (this.rightOffsetOverride != null) { - this.rightOffsetOverride.serialize(buf); + PacketIO.writeVector3f(buf, this.rightOffsetOverride); } else { buf.writeZero(12); } if (this.rightRotationOverride != null) { - this.rightRotationOverride.serialize(buf); + PacketIO.writeVector3f(buf, this.rightRotationOverride); } else { buf.writeZero(12); } @@ -119,15 +126,20 @@ public class ItemPullbackConfiguration { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 49 ? ValidationResult.error("Buffer too small: expected at least 49 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 49) { + return ValidationResult.error("Buffer too small: expected at least 49 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public ItemPullbackConfiguration clone() { ItemPullbackConfiguration copy = new ItemPullbackConfiguration(); - copy.leftOffsetOverride = this.leftOffsetOverride != null ? this.leftOffsetOverride.clone() : null; - copy.leftRotationOverride = this.leftRotationOverride != null ? this.leftRotationOverride.clone() : null; - copy.rightOffsetOverride = this.rightOffsetOverride != null ? this.rightOffsetOverride.clone() : null; - copy.rightRotationOverride = this.rightRotationOverride != null ? this.rightRotationOverride.clone() : null; + copy.leftOffsetOverride = this.leftOffsetOverride; + copy.leftRotationOverride = this.leftRotationOverride; + copy.rightOffsetOverride = this.rightOffsetOverride; + copy.rightRotationOverride = this.rightRotationOverride; return copy; } diff --git a/src/com/hypixel/hytale/protocol/ItemQuality.java b/src/com/hypixel/hytale/protocol/ItemQuality.java index c6ccc1e5..79541ec0 100644 --- a/src/com/hypixel/hytale/protocol/ItemQuality.java +++ b/src/com/hypixel/hytale/protocol/ItemQuality.java @@ -80,114 +80,190 @@ public class ItemQuality { @Nonnull public static ItemQuality deserialize(@Nonnull ByteBuf buf, int offset) { - ItemQuality obj = new ItemQuality(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.textColor = Color.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 35) { + throw ProtocolException.bufferTooSmall("ItemQuality", 35, buf.readableBytes() - offset); + } else { + ItemQuality obj = new ItemQuality(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.textColor = Color.deserialize(buf, offset + 1); + } + + obj.visibleQualityLabel = buf.getByte(offset + 4) != 0; + obj.renderSpecialSlot = buf.getByte(offset + 5) != 0; + obj.hideFromSearch = buf.getByte(offset + 6) != 0; + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 7); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 35 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 11); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("ItemTooltipTexture", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 35 + varPosBase1; + int itemTooltipTextureLen = VarInt.peek(buf, varPos1); + if (itemTooltipTextureLen < 0) { + throw ProtocolException.invalidVarInt("ItemTooltipTexture"); + } + + int itemTooltipTextureVarIntLen = VarInt.size(itemTooltipTextureLen); + if (itemTooltipTextureLen > 4096000) { + throw ProtocolException.stringTooLong("ItemTooltipTexture", itemTooltipTextureLen, 4096000); + } + + if (varPos1 + itemTooltipTextureVarIntLen + itemTooltipTextureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemTooltipTexture", varPos1 + itemTooltipTextureVarIntLen + itemTooltipTextureLen, buf.readableBytes()); + } + + obj.itemTooltipTexture = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 15); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("ItemTooltipArrowTexture", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 35 + varPosBase2; + int itemTooltipArrowTextureLen = VarInt.peek(buf, varPos2); + if (itemTooltipArrowTextureLen < 0) { + throw ProtocolException.invalidVarInt("ItemTooltipArrowTexture"); + } + + int itemTooltipArrowTextureVarIntLen = VarInt.size(itemTooltipArrowTextureLen); + if (itemTooltipArrowTextureLen > 4096000) { + throw ProtocolException.stringTooLong("ItemTooltipArrowTexture", itemTooltipArrowTextureLen, 4096000); + } + + if (varPos2 + itemTooltipArrowTextureVarIntLen + itemTooltipArrowTextureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "ItemTooltipArrowTexture", varPos2 + itemTooltipArrowTextureVarIntLen + itemTooltipArrowTextureLen, buf.readableBytes() + ); + } + + obj.itemTooltipArrowTexture = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase3 = buf.getIntLE(offset + 19); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("SlotTexture", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 35 + varPosBase3; + int slotTextureLen = VarInt.peek(buf, varPos3); + if (slotTextureLen < 0) { + throw ProtocolException.invalidVarInt("SlotTexture"); + } + + int slotTextureVarIntLen = VarInt.size(slotTextureLen); + if (slotTextureLen > 4096000) { + throw ProtocolException.stringTooLong("SlotTexture", slotTextureLen, 4096000); + } + + if (varPos3 + slotTextureVarIntLen + slotTextureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SlotTexture", varPos3 + slotTextureVarIntLen + slotTextureLen, buf.readableBytes()); + } + + obj.slotTexture = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits & 32) != 0) { + int varPosBase4 = buf.getIntLE(offset + 23); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("BlockSlotTexture", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 35 + varPosBase4; + int blockSlotTextureLen = VarInt.peek(buf, varPos4); + if (blockSlotTextureLen < 0) { + throw ProtocolException.invalidVarInt("BlockSlotTexture"); + } + + int blockSlotTextureVarIntLen = VarInt.size(blockSlotTextureLen); + if (blockSlotTextureLen > 4096000) { + throw ProtocolException.stringTooLong("BlockSlotTexture", blockSlotTextureLen, 4096000); + } + + if (varPos4 + blockSlotTextureVarIntLen + blockSlotTextureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BlockSlotTexture", varPos4 + blockSlotTextureVarIntLen + blockSlotTextureLen, buf.readableBytes()); + } + + obj.blockSlotTexture = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + if ((nullBits & 64) != 0) { + int varPosBase5 = buf.getIntLE(offset + 27); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("SpecialSlotTexture", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 35 + varPosBase5; + int specialSlotTextureLen = VarInt.peek(buf, varPos5); + if (specialSlotTextureLen < 0) { + throw ProtocolException.invalidVarInt("SpecialSlotTexture"); + } + + int specialSlotTextureVarIntLen = VarInt.size(specialSlotTextureLen); + if (specialSlotTextureLen > 4096000) { + throw ProtocolException.stringTooLong("SpecialSlotTexture", specialSlotTextureLen, 4096000); + } + + if (varPos5 + specialSlotTextureVarIntLen + specialSlotTextureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SpecialSlotTexture", varPos5 + specialSlotTextureVarIntLen + specialSlotTextureLen, buf.readableBytes()); + } + + obj.specialSlotTexture = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + if ((nullBits & 128) != 0) { + int varPosBase6 = buf.getIntLE(offset + 31); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("LocalizationKey", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 35 + varPosBase6; + int localizationKeyLen = VarInt.peek(buf, varPos6); + if (localizationKeyLen < 0) { + throw ProtocolException.invalidVarInt("LocalizationKey"); + } + + int localizationKeyVarIntLen = VarInt.size(localizationKeyLen); + if (localizationKeyLen > 4096000) { + throw ProtocolException.stringTooLong("LocalizationKey", localizationKeyLen, 4096000); + } + + if (varPos6 + localizationKeyVarIntLen + localizationKeyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("LocalizationKey", varPos6 + localizationKeyVarIntLen + localizationKeyLen, buf.readableBytes()); + } + + obj.localizationKey = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); + } + + return obj; } - - obj.visibleQualityLabel = buf.getByte(offset + 4) != 0; - obj.renderSpecialSlot = buf.getByte(offset + 5) != 0; - obj.hideFromSearch = buf.getByte(offset + 6) != 0; - if ((nullBits & 2) != 0) { - int varPos0 = offset + 35 + buf.getIntLE(offset + 7); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); - } - - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 35 + buf.getIntLE(offset + 11); - int itemTooltipTextureLen = VarInt.peek(buf, varPos1); - if (itemTooltipTextureLen < 0) { - throw ProtocolException.negativeLength("ItemTooltipTexture", itemTooltipTextureLen); - } - - if (itemTooltipTextureLen > 4096000) { - throw ProtocolException.stringTooLong("ItemTooltipTexture", itemTooltipTextureLen, 4096000); - } - - obj.itemTooltipTexture = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos2 = offset + 35 + buf.getIntLE(offset + 15); - int itemTooltipArrowTextureLen = VarInt.peek(buf, varPos2); - if (itemTooltipArrowTextureLen < 0) { - throw ProtocolException.negativeLength("ItemTooltipArrowTexture", itemTooltipArrowTextureLen); - } - - if (itemTooltipArrowTextureLen > 4096000) { - throw ProtocolException.stringTooLong("ItemTooltipArrowTexture", itemTooltipArrowTextureLen, 4096000); - } - - obj.itemTooltipArrowTexture = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 16) != 0) { - int varPos3 = offset + 35 + buf.getIntLE(offset + 19); - int slotTextureLen = VarInt.peek(buf, varPos3); - if (slotTextureLen < 0) { - throw ProtocolException.negativeLength("SlotTexture", slotTextureLen); - } - - if (slotTextureLen > 4096000) { - throw ProtocolException.stringTooLong("SlotTexture", slotTextureLen, 4096000); - } - - obj.slotTexture = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits & 32) != 0) { - int varPos4 = offset + 35 + buf.getIntLE(offset + 23); - int blockSlotTextureLen = VarInt.peek(buf, varPos4); - if (blockSlotTextureLen < 0) { - throw ProtocolException.negativeLength("BlockSlotTexture", blockSlotTextureLen); - } - - if (blockSlotTextureLen > 4096000) { - throw ProtocolException.stringTooLong("BlockSlotTexture", blockSlotTextureLen, 4096000); - } - - obj.blockSlotTexture = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - if ((nullBits & 64) != 0) { - int varPos5 = offset + 35 + buf.getIntLE(offset + 27); - int specialSlotTextureLen = VarInt.peek(buf, varPos5); - if (specialSlotTextureLen < 0) { - throw ProtocolException.negativeLength("SpecialSlotTexture", specialSlotTextureLen); - } - - if (specialSlotTextureLen > 4096000) { - throw ProtocolException.stringTooLong("SpecialSlotTexture", specialSlotTextureLen, 4096000); - } - - obj.specialSlotTexture = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - if ((nullBits & 128) != 0) { - int varPos6 = offset + 35 + buf.getIntLE(offset + 31); - int localizationKeyLen = VarInt.peek(buf, varPos6); - if (localizationKeyLen < 0) { - throw ProtocolException.negativeLength("LocalizationKey", localizationKeyLen); - } - - if (localizationKeyLen > 4096000) { - throw ProtocolException.stringTooLong("LocalizationKey", localizationKeyLen, 4096000); - } - - obj.localizationKey = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -195,9 +271,13 @@ public class ItemQuality { int maxEnd = 35; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 7); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 35 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -205,9 +285,13 @@ public class ItemQuality { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 11); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("ItemTooltipTexture", fieldOffset1, maxEnd); + } + int pos1 = offset + 35 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -215,9 +299,13 @@ public class ItemQuality { if ((nullBits & 8) != 0) { int fieldOffset2 = buf.getIntLE(offset + 15); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("ItemTooltipArrowTexture", fieldOffset2, maxEnd); + } + int pos2 = offset + 35 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -225,9 +313,13 @@ public class ItemQuality { if ((nullBits & 16) != 0) { int fieldOffset3 = buf.getIntLE(offset + 19); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("SlotTexture", fieldOffset3, maxEnd); + } + int pos3 = offset + 35 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -235,9 +327,13 @@ public class ItemQuality { if ((nullBits & 32) != 0) { int fieldOffset4 = buf.getIntLE(offset + 23); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("BlockSlotTexture", fieldOffset4, maxEnd); + } + int pos4 = offset + 35 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -245,9 +341,13 @@ public class ItemQuality { if ((nullBits & 64) != 0) { int fieldOffset5 = buf.getIntLE(offset + 27); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("SpecialSlotTexture", fieldOffset5, maxEnd); + } + int pos5 = offset + 35 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -255,9 +355,13 @@ public class ItemQuality { if ((nullBits & 128) != 0) { int fieldOffset6 = buf.getIntLE(offset + 31); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("LocalizationKey", fieldOffset6, maxEnd); + } + int pos6 = offset + 35 + fieldOffset6; int sl = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6) + sl; + pos6 += VarInt.size(sl) + sl; if (pos6 - offset > maxEnd) { maxEnd = pos6 - offset; } @@ -416,15 +520,11 @@ public class ItemQuality { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { int idOffset = buffer.getIntLE(offset + 7); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 35) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 35 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -434,7 +534,7 @@ public class ItemQuality { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -443,15 +543,11 @@ public class ItemQuality { if ((nullBits & 4) != 0) { int itemTooltipTextureOffset = buffer.getIntLE(offset + 11); - if (itemTooltipTextureOffset < 0) { + if (itemTooltipTextureOffset < 0 || itemTooltipTextureOffset > buffer.writerIndex() - offset - 35) { return ValidationResult.error("Invalid offset for ItemTooltipTexture"); } int posx = offset + 35 + itemTooltipTextureOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemTooltipTexture"); - } - int itemTooltipTextureLen = VarInt.peek(buffer, posx); if (itemTooltipTextureLen < 0) { return ValidationResult.error("Invalid string length for ItemTooltipTexture"); @@ -461,7 +557,7 @@ public class ItemQuality { return ValidationResult.error("ItemTooltipTexture exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(itemTooltipTextureLen); posx += itemTooltipTextureLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemTooltipTexture"); @@ -470,15 +566,11 @@ public class ItemQuality { if ((nullBits & 8) != 0) { int itemTooltipArrowTextureOffset = buffer.getIntLE(offset + 15); - if (itemTooltipArrowTextureOffset < 0) { + if (itemTooltipArrowTextureOffset < 0 || itemTooltipArrowTextureOffset > buffer.writerIndex() - offset - 35) { return ValidationResult.error("Invalid offset for ItemTooltipArrowTexture"); } int posxx = offset + 35 + itemTooltipArrowTextureOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemTooltipArrowTexture"); - } - int itemTooltipArrowTextureLen = VarInt.peek(buffer, posxx); if (itemTooltipArrowTextureLen < 0) { return ValidationResult.error("Invalid string length for ItemTooltipArrowTexture"); @@ -488,7 +580,7 @@ public class ItemQuality { return ValidationResult.error("ItemTooltipArrowTexture exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(itemTooltipArrowTextureLen); posxx += itemTooltipArrowTextureLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemTooltipArrowTexture"); @@ -497,15 +589,11 @@ public class ItemQuality { if ((nullBits & 16) != 0) { int slotTextureOffset = buffer.getIntLE(offset + 19); - if (slotTextureOffset < 0) { + if (slotTextureOffset < 0 || slotTextureOffset > buffer.writerIndex() - offset - 35) { return ValidationResult.error("Invalid offset for SlotTexture"); } int posxxx = offset + 35 + slotTextureOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SlotTexture"); - } - int slotTextureLen = VarInt.peek(buffer, posxxx); if (slotTextureLen < 0) { return ValidationResult.error("Invalid string length for SlotTexture"); @@ -515,7 +603,7 @@ public class ItemQuality { return ValidationResult.error("SlotTexture exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(slotTextureLen); posxxx += slotTextureLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SlotTexture"); @@ -524,15 +612,11 @@ public class ItemQuality { if ((nullBits & 32) != 0) { int blockSlotTextureOffset = buffer.getIntLE(offset + 23); - if (blockSlotTextureOffset < 0) { + if (blockSlotTextureOffset < 0 || blockSlotTextureOffset > buffer.writerIndex() - offset - 35) { return ValidationResult.error("Invalid offset for BlockSlotTexture"); } int posxxxx = offset + 35 + blockSlotTextureOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockSlotTexture"); - } - int blockSlotTextureLen = VarInt.peek(buffer, posxxxx); if (blockSlotTextureLen < 0) { return ValidationResult.error("Invalid string length for BlockSlotTexture"); @@ -542,7 +626,7 @@ public class ItemQuality { return ValidationResult.error("BlockSlotTexture exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(blockSlotTextureLen); posxxxx += blockSlotTextureLen; if (posxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading BlockSlotTexture"); @@ -551,15 +635,11 @@ public class ItemQuality { if ((nullBits & 64) != 0) { int specialSlotTextureOffset = buffer.getIntLE(offset + 27); - if (specialSlotTextureOffset < 0) { + if (specialSlotTextureOffset < 0 || specialSlotTextureOffset > buffer.writerIndex() - offset - 35) { return ValidationResult.error("Invalid offset for SpecialSlotTexture"); } int posxxxxx = offset + 35 + specialSlotTextureOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SpecialSlotTexture"); - } - int specialSlotTextureLen = VarInt.peek(buffer, posxxxxx); if (specialSlotTextureLen < 0) { return ValidationResult.error("Invalid string length for SpecialSlotTexture"); @@ -569,7 +649,7 @@ public class ItemQuality { return ValidationResult.error("SpecialSlotTexture exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); + posxxxxx += VarInt.size(specialSlotTextureLen); posxxxxx += specialSlotTextureLen; if (posxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SpecialSlotTexture"); @@ -578,15 +658,11 @@ public class ItemQuality { if ((nullBits & 128) != 0) { int localizationKeyOffset = buffer.getIntLE(offset + 31); - if (localizationKeyOffset < 0) { + if (localizationKeyOffset < 0 || localizationKeyOffset > buffer.writerIndex() - offset - 35) { return ValidationResult.error("Invalid offset for LocalizationKey"); } int posxxxxxx = offset + 35 + localizationKeyOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for LocalizationKey"); - } - int localizationKeyLen = VarInt.peek(buffer, posxxxxxx); if (localizationKeyLen < 0) { return ValidationResult.error("Invalid string length for LocalizationKey"); @@ -596,7 +672,7 @@ public class ItemQuality { return ValidationResult.error("LocalizationKey exceeds max length 4096000"); } - posxxxxxx += VarInt.length(buffer, posxxxxxx); + posxxxxxx += VarInt.size(localizationKeyLen); posxxxxxx += localizationKeyLen; if (posxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading LocalizationKey"); diff --git a/src/com/hypixel/hytale/protocol/ItemQuantity.java b/src/com/hypixel/hytale/protocol/ItemQuantity.java index 1ecb51ea..2a9de1f1 100644 --- a/src/com/hypixel/hytale/protocol/ItemQuantity.java +++ b/src/com/hypixel/hytale/protocol/ItemQuantity.java @@ -34,26 +34,34 @@ public class ItemQuantity { @Nonnull public static ItemQuantity deserialize(@Nonnull ByteBuf buf, int offset) { - ItemQuantity obj = new ItemQuantity(); - byte nullBits = buf.getByte(offset); - obj.quantity = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int itemIdLen = VarInt.peek(buf, pos); - if (itemIdLen < 0) { - throw ProtocolException.negativeLength("ItemId", itemIdLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("ItemQuantity", 5, buf.readableBytes() - offset); + } else { + ItemQuantity obj = new ItemQuantity(); + byte nullBits = buf.getByte(offset); + obj.quantity = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int itemIdLen = VarInt.peek(buf, pos); + if (itemIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemId"); + } + + int itemIdVarLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + } + + if (pos + itemIdVarLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemId", pos + itemIdVarLen + itemIdLen, buf.readableBytes()); + } + + obj.itemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += itemIdVarLen + itemIdLen; } - if (itemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); - } - - int itemIdVarLen = VarInt.length(buf, pos); - obj.itemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += itemIdVarLen + itemIdLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,7 +69,7 @@ public class ItemQuantity { int pos = offset + 5; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -105,7 +113,7 @@ public class ItemQuantity { return ValidationResult.error("ItemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(itemIdLen); pos += itemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemId"); diff --git a/src/com/hypixel/hytale/protocol/ItemResourceType.java b/src/com/hypixel/hytale/protocol/ItemResourceType.java index 90a96aa1..5124e5eb 100644 --- a/src/com/hypixel/hytale/protocol/ItemResourceType.java +++ b/src/com/hypixel/hytale/protocol/ItemResourceType.java @@ -34,26 +34,34 @@ public class ItemResourceType { @Nonnull public static ItemResourceType deserialize(@Nonnull ByteBuf buf, int offset) { - ItemResourceType obj = new ItemResourceType(); - byte nullBits = buf.getByte(offset); - obj.quantity = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int idLen = VarInt.peek(buf, pos); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("ItemResourceType", 5, buf.readableBytes() - offset); + } else { + ItemResourceType obj = new ItemResourceType(); + byte nullBits = buf.getByte(offset); + obj.quantity = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int idLen = VarInt.peek(buf, pos); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (pos + idVarLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", pos + idVarLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += idVarLen + idLen; } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - int idVarLen = VarInt.length(buf, pos); - obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += idVarLen + idLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,7 +69,7 @@ public class ItemResourceType { int pos = offset + 5; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -105,7 +113,7 @@ public class ItemResourceType { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); diff --git a/src/com/hypixel/hytale/protocol/ItemReticle.java b/src/com/hypixel/hytale/protocol/ItemReticle.java index 700993f8..cda09649 100644 --- a/src/com/hypixel/hytale/protocol/ItemReticle.java +++ b/src/com/hypixel/hytale/protocol/ItemReticle.java @@ -37,46 +37,54 @@ public class ItemReticle { @Nonnull public static ItemReticle deserialize(@Nonnull ByteBuf buf, int offset) { - ItemReticle obj = new ItemReticle(); - byte nullBits = buf.getByte(offset); - obj.hideBase = buf.getByte(offset + 1) != 0; - obj.duration = buf.getFloatLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int partsCount = VarInt.peek(buf, pos); - if (partsCount < 0) { - throw ProtocolException.negativeLength("Parts", partsCount); - } - - if (partsCount > 4096000) { - throw ProtocolException.arrayTooLong("Parts", partsCount, 4096000); - } - - int partsVarLen = VarInt.size(partsCount); - if (pos + partsVarLen + partsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Parts", pos + partsVarLen + partsCount * 1, buf.readableBytes()); - } - - pos += partsVarLen; - obj.parts = new String[partsCount]; - - for (int i = 0; i < partsCount; i++) { - int strLen = VarInt.peek(buf, pos); - if (strLen < 0) { - throw ProtocolException.negativeLength("parts[" + i + "]", strLen); + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("ItemReticle", 6, buf.readableBytes() - offset); + } else { + ItemReticle obj = new ItemReticle(); + byte nullBits = buf.getByte(offset); + obj.hideBase = buf.getByte(offset + 1) != 0; + obj.duration = buf.getFloatLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int partsCount = VarInt.peek(buf, pos); + if (partsCount < 0) { + throw ProtocolException.invalidVarInt("Parts"); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("parts[" + i + "]", strLen, 4096000); + int partsVarLen = VarInt.size(partsCount); + if (partsCount > 4096000) { + throw ProtocolException.arrayTooLong("Parts", partsCount, 4096000); } - int strVarLen = VarInt.length(buf, pos); - obj.parts[i] = PacketIO.readVarString(buf, pos); - pos += strVarLen + strLen; + if (pos + partsVarLen + partsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Parts", pos + partsVarLen + partsCount * 1, buf.readableBytes()); + } + + pos += partsVarLen; + obj.parts = new String[partsCount]; + + for (int i = 0; i < partsCount; i++) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("parts[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("parts[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("parts[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.parts[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -84,11 +92,11 @@ public class ItemReticle { int pos = offset + 6; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -148,7 +156,7 @@ public class ItemReticle { return ValidationResult.error("Parts exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(partsCount); for (int i = 0; i < partsCount; i++) { int strLen = VarInt.peek(buffer, pos); @@ -156,7 +164,7 @@ public class ItemReticle { return ValidationResult.error("Invalid string length in Parts"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(strLen); pos += strLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Parts"); diff --git a/src/com/hypixel/hytale/protocol/ItemReticleConfig.java b/src/com/hypixel/hytale/protocol/ItemReticleConfig.java index 7e453ae0..522dfc5e 100644 --- a/src/com/hypixel/hytale/protocol/ItemReticleConfig.java +++ b/src/com/hypixel/hytale/protocol/ItemReticleConfig.java @@ -52,109 +52,142 @@ public class ItemReticleConfig { @Nonnull public static ItemReticleConfig deserialize(@Nonnull ByteBuf buf, int offset) { - ItemReticleConfig obj = new ItemReticleConfig(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 17 + buf.getIntLE(offset + 1); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); - } - - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 17 + buf.getIntLE(offset + 5); - int baseCount = VarInt.peek(buf, varPos1); - if (baseCount < 0) { - throw ProtocolException.negativeLength("Base", baseCount); - } - - if (baseCount > 4096000) { - throw ProtocolException.arrayTooLong("Base", baseCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + baseCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Base", varPos1 + varIntLen + baseCount * 1, buf.readableBytes()); - } - - obj.base = new String[baseCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < baseCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("base[" + i + "]", strLen); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("ItemReticleConfig", 17, buf.readableBytes() - offset); + } else { + ItemReticleConfig obj = new ItemReticleConfig(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("base[" + i + "]", strLen, 4096000); + int varPos0 = offset + 17 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); } - int strVarLen = VarInt.length(buf, elemPos); - obj.base[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 17 + buf.getIntLE(offset + 9); - int serverEventsCount = VarInt.peek(buf, varPos2); - if (serverEventsCount < 0) { - throw ProtocolException.negativeLength("ServerEvents", serverEventsCount); + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (serverEventsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ServerEvents", serverEventsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Base", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos2); - obj.serverEvents = new HashMap<>(serverEventsCount); - int dictPos = varPos2 + varIntLen; + int varPos1 = offset + 17 + varPosBase1; + int baseCount = VarInt.peek(buf, varPos1); + if (baseCount < 0) { + throw ProtocolException.invalidVarInt("Base"); + } - for (int i = 0; i < serverEventsCount; i++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - ItemReticle val = ItemReticle.deserialize(buf, dictPos); - dictPos += ItemReticle.computeBytesConsumed(buf, dictPos); - if (obj.serverEvents.put(key, val) != null) { - throw ProtocolException.duplicateKey("serverEvents", key); + int varIntLen = VarInt.size(baseCount); + if (baseCount > 4096000) { + throw ProtocolException.arrayTooLong("Base", baseCount, 4096000); + } + + if (varPos1 + varIntLen + baseCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Base", varPos1 + varIntLen + baseCount * 1, buf.readableBytes()); + } + + obj.base = new String[baseCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < baseCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("base[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("base[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("base[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.base[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; } } - } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 17 + buf.getIntLE(offset + 13); - int clientEventsCount = VarInt.peek(buf, varPos3); - if (clientEventsCount < 0) { - throw ProtocolException.negativeLength("ClientEvents", clientEventsCount); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ServerEvents", varPosBase2, buf.readableBytes()); + } - if (clientEventsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ClientEvents", clientEventsCount, 4096000); - } + int varPos2 = offset + 17 + varPosBase2; + int serverEventsCount = VarInt.peek(buf, varPos2); + if (serverEventsCount < 0) { + throw ProtocolException.invalidVarInt("ServerEvents"); + } - int varIntLen = VarInt.length(buf, varPos3); - obj.clientEvents = new HashMap<>(clientEventsCount); - int dictPos = varPos3 + varIntLen; + int varIntLenx = VarInt.size(serverEventsCount); + if (serverEventsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ServerEvents", serverEventsCount, 4096000); + } - for (int ix = 0; ix < clientEventsCount; ix++) { - ItemReticleClientEvent key = ItemReticleClientEvent.fromValue(buf.getByte(dictPos)); - ItemReticle val = ItemReticle.deserialize(buf, ++dictPos); - dictPos += ItemReticle.computeBytesConsumed(buf, dictPos); - if (obj.clientEvents.put(key, val) != null) { - throw ProtocolException.duplicateKey("clientEvents", key); + obj.serverEvents = new HashMap<>(serverEventsCount); + int dictPos = varPos2 + varIntLenx; + + for (int i = 0; i < serverEventsCount; i++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + ItemReticle val = ItemReticle.deserialize(buf, dictPos); + dictPos += ItemReticle.computeBytesConsumed(buf, dictPos); + if (obj.serverEvents.put(key, val) != null) { + throw ProtocolException.duplicateKey("serverEvents", key); + } } } - } - return obj; + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 13); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ClientEvents", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 17 + varPosBase3; + int clientEventsCount = VarInt.peek(buf, varPos3); + if (clientEventsCount < 0) { + throw ProtocolException.invalidVarInt("ClientEvents"); + } + + int varIntLenx = VarInt.size(clientEventsCount); + if (clientEventsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ClientEvents", clientEventsCount, 4096000); + } + + obj.clientEvents = new HashMap<>(clientEventsCount); + int dictPos = varPos3 + varIntLenx; + + for (int ix = 0; ix < clientEventsCount; ix++) { + ItemReticleClientEvent key = ItemReticleClientEvent.fromValue(buf.getByte(dictPos)); + ItemReticle val = ItemReticle.deserialize(buf, ++dictPos); + dictPos += ItemReticle.computeBytesConsumed(buf, dictPos); + if (obj.clientEvents.put(key, val) != null) { + throw ProtocolException.duplicateKey("clientEvents", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -162,9 +195,13 @@ public class ItemReticleConfig { int maxEnd = 17; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -172,13 +209,17 @@ public class ItemReticleConfig { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Base", fieldOffset1, maxEnd); + } + int pos1 = offset + 17 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -188,9 +229,13 @@ public class ItemReticleConfig { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ServerEvents", fieldOffset2, maxEnd); + } + int pos2 = offset + 17 + fieldOffset2; int dictLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos2 += 4; @@ -204,9 +249,13 @@ public class ItemReticleConfig { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 13); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ClientEvents", fieldOffset3, maxEnd); + } + int pos3 = offset + 17 + fieldOffset3; int dictLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos3 = ++pos3 + ItemReticle.computeBytesConsumed(buf, pos3); @@ -350,15 +399,11 @@ public class ItemReticleConfig { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 1); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 17 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -368,7 +413,7 @@ public class ItemReticleConfig { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -377,15 +422,11 @@ public class ItemReticleConfig { if ((nullBits & 2) != 0) { int baseOffset = buffer.getIntLE(offset + 5); - if (baseOffset < 0) { + if (baseOffset < 0 || baseOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Base"); } int posx = offset + 17 + baseOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Base"); - } - int baseCount = VarInt.peek(buffer, posx); if (baseCount < 0) { return ValidationResult.error("Invalid array count for Base"); @@ -395,7 +436,7 @@ public class ItemReticleConfig { return ValidationResult.error("Base exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(baseCount); for (int i = 0; i < baseCount; i++) { int strLen = VarInt.peek(buffer, posx); @@ -403,7 +444,7 @@ public class ItemReticleConfig { return ValidationResult.error("Invalid string length in Base"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(strLen); posx += strLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Base"); @@ -413,15 +454,11 @@ public class ItemReticleConfig { if ((nullBits & 4) != 0) { int serverEventsOffset = buffer.getIntLE(offset + 9); - if (serverEventsOffset < 0) { + if (serverEventsOffset < 0 || serverEventsOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for ServerEvents"); } int posxx = offset + 17 + serverEventsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ServerEvents"); - } - int serverEventsCount = VarInt.peek(buffer, posxx); if (serverEventsCount < 0) { return ValidationResult.error("Invalid dictionary count for ServerEvents"); @@ -431,7 +468,7 @@ public class ItemReticleConfig { return ValidationResult.error("ServerEvents exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(serverEventsCount); for (int i = 0; i < serverEventsCount; i++) { posxx += 4; @@ -445,15 +482,11 @@ public class ItemReticleConfig { if ((nullBits & 8) != 0) { int clientEventsOffset = buffer.getIntLE(offset + 13); - if (clientEventsOffset < 0) { + if (clientEventsOffset < 0 || clientEventsOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for ClientEvents"); } int posxxx = offset + 17 + clientEventsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ClientEvents"); - } - int clientEventsCount = VarInt.peek(buffer, posxxx); if (clientEventsCount < 0) { return ValidationResult.error("Invalid dictionary count for ClientEvents"); @@ -463,9 +496,14 @@ public class ItemReticleConfig { return ValidationResult.error("ClientEvents exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(clientEventsCount); for (int i = 0; i < clientEventsCount; i++) { + int v = buffer.getByte(posxxx) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid ItemReticleClientEvent value for key"); + } + posxxx = ++posxxx + ItemReticle.computeBytesConsumed(buffer, posxxx); } } diff --git a/src/com/hypixel/hytale/protocol/ItemSoundSet.java b/src/com/hypixel/hytale/protocol/ItemSoundSet.java index 122dbfc4..983277fa 100644 --- a/src/com/hypixel/hytale/protocol/ItemSoundSet.java +++ b/src/com/hypixel/hytale/protocol/ItemSoundSet.java @@ -38,48 +38,67 @@ public class ItemSoundSet { @Nonnull public static ItemSoundSet deserialize(@Nonnull ByteBuf buf, int offset) { - ItemSoundSet obj = new ItemSoundSet(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ItemSoundSet", 9, buf.readableBytes() - offset); + } else { + ItemSoundSet obj = new ItemSoundSet(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("SoundEventIndices", varPosBase1, buf.readableBytes()); + } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + int varPos1 = offset + 9 + varPosBase1; + int soundEventIndicesCount = VarInt.peek(buf, varPos1); + if (soundEventIndicesCount < 0) { + throw ProtocolException.invalidVarInt("SoundEventIndices"); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int soundEventIndicesCount = VarInt.peek(buf, varPos1); - if (soundEventIndicesCount < 0) { - throw ProtocolException.negativeLength("SoundEventIndices", soundEventIndicesCount); - } + int varIntLen = VarInt.size(soundEventIndicesCount); + if (soundEventIndicesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SoundEventIndices", soundEventIndicesCount, 4096000); + } - if (soundEventIndicesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SoundEventIndices", soundEventIndicesCount, 4096000); - } + obj.soundEventIndices = new HashMap<>(soundEventIndicesCount); + int dictPos = varPos1 + varIntLen; - int varIntLen = VarInt.length(buf, varPos1); - obj.soundEventIndices = new HashMap<>(soundEventIndicesCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < soundEventIndicesCount; i++) { - ItemSoundEvent key = ItemSoundEvent.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.soundEventIndices.put(key, val) != null) { - throw ProtocolException.duplicateKey("soundEventIndices", key); + for (int i = 0; i < soundEventIndicesCount; i++) { + ItemSoundEvent key = ItemSoundEvent.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.soundEventIndices.put(key, val) != null) { + throw ProtocolException.duplicateKey("soundEventIndices", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -87,9 +106,13 @@ public class ItemSoundSet { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -97,9 +120,13 @@ public class ItemSoundSet { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("SoundEventIndices", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + 4; @@ -174,15 +201,11 @@ public class ItemSoundSet { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 1); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 9 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -192,7 +215,7 @@ public class ItemSoundSet { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -201,15 +224,11 @@ public class ItemSoundSet { if ((nullBits & 2) != 0) { int soundEventIndicesOffset = buffer.getIntLE(offset + 5); - if (soundEventIndicesOffset < 0) { + if (soundEventIndicesOffset < 0 || soundEventIndicesOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for SoundEventIndices"); } int posx = offset + 9 + soundEventIndicesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SoundEventIndices"); - } - int soundEventIndicesCount = VarInt.peek(buffer, posx); if (soundEventIndicesCount < 0) { return ValidationResult.error("Invalid dictionary count for SoundEventIndices"); @@ -219,9 +238,14 @@ public class ItemSoundSet { return ValidationResult.error("SoundEventIndices exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(soundEventIndicesCount); for (int i = 0; i < soundEventIndicesCount; i++) { + int v = buffer.getByte(posx) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ItemSoundEvent value for key"); + } + posx = ++posx + 4; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); diff --git a/src/com/hypixel/hytale/protocol/ItemTool.java b/src/com/hypixel/hytale/protocol/ItemTool.java index ebf35e64..928eb462 100644 --- a/src/com/hypixel/hytale/protocol/ItemTool.java +++ b/src/com/hypixel/hytale/protocol/ItemTool.java @@ -33,35 +33,39 @@ public class ItemTool { @Nonnull public static ItemTool deserialize(@Nonnull ByteBuf buf, int offset) { - ItemTool obj = new ItemTool(); - byte nullBits = buf.getByte(offset); - obj.speed = buf.getFloatLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int specsCount = VarInt.peek(buf, pos); - if (specsCount < 0) { - throw ProtocolException.negativeLength("Specs", specsCount); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("ItemTool", 5, buf.readableBytes() - offset); + } else { + ItemTool obj = new ItemTool(); + byte nullBits = buf.getByte(offset); + obj.speed = buf.getFloatLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int specsCount = VarInt.peek(buf, pos); + if (specsCount < 0) { + throw ProtocolException.invalidVarInt("Specs"); + } + + int specsVarLen = VarInt.size(specsCount); + if (specsCount > 4096000) { + throw ProtocolException.arrayTooLong("Specs", specsCount, 4096000); + } + + if (pos + specsVarLen + specsCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Specs", pos + specsVarLen + specsCount * 9, buf.readableBytes()); + } + + pos += specsVarLen; + obj.specs = new ItemToolSpec[specsCount]; + + for (int i = 0; i < specsCount; i++) { + obj.specs[i] = ItemToolSpec.deserialize(buf, pos); + pos += ItemToolSpec.computeBytesConsumed(buf, pos); + } } - if (specsCount > 4096000) { - throw ProtocolException.arrayTooLong("Specs", specsCount, 4096000); - } - - int specsVarLen = VarInt.size(specsCount); - if (pos + specsVarLen + specsCount * 9L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Specs", pos + specsVarLen + specsCount * 9, buf.readableBytes()); - } - - pos += specsVarLen; - obj.specs = new ItemToolSpec[specsCount]; - - for (int i = 0; i < specsCount; i++) { - obj.specs[i] = ItemToolSpec.deserialize(buf, pos); - pos += ItemToolSpec.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -69,7 +73,7 @@ public class ItemTool { int pos = offset + 5; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += ItemToolSpec.computeBytesConsumed(buf, pos); @@ -131,7 +135,7 @@ public class ItemTool { return ValidationResult.error("Specs exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(specsCount); for (int i = 0; i < specsCount; i++) { ValidationResult structResult = ItemToolSpec.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/ItemToolSpec.java b/src/com/hypixel/hytale/protocol/ItemToolSpec.java index 4f7a993e..caf24fef 100644 --- a/src/com/hypixel/hytale/protocol/ItemToolSpec.java +++ b/src/com/hypixel/hytale/protocol/ItemToolSpec.java @@ -37,27 +37,35 @@ public class ItemToolSpec { @Nonnull public static ItemToolSpec deserialize(@Nonnull ByteBuf buf, int offset) { - ItemToolSpec obj = new ItemToolSpec(); - byte nullBits = buf.getByte(offset); - obj.power = buf.getFloatLE(offset + 1); - obj.quality = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - int gatherTypeLen = VarInt.peek(buf, pos); - if (gatherTypeLen < 0) { - throw ProtocolException.negativeLength("GatherType", gatherTypeLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ItemToolSpec", 9, buf.readableBytes() - offset); + } else { + ItemToolSpec obj = new ItemToolSpec(); + byte nullBits = buf.getByte(offset); + obj.power = buf.getFloatLE(offset + 1); + obj.quality = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int gatherTypeLen = VarInt.peek(buf, pos); + if (gatherTypeLen < 0) { + throw ProtocolException.invalidVarInt("GatherType"); + } + + int gatherTypeVarLen = VarInt.size(gatherTypeLen); + if (gatherTypeLen > 4096000) { + throw ProtocolException.stringTooLong("GatherType", gatherTypeLen, 4096000); + } + + if (pos + gatherTypeVarLen + gatherTypeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("GatherType", pos + gatherTypeVarLen + gatherTypeLen, buf.readableBytes()); + } + + obj.gatherType = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += gatherTypeVarLen + gatherTypeLen; } - if (gatherTypeLen > 4096000) { - throw ProtocolException.stringTooLong("GatherType", gatherTypeLen, 4096000); - } - - int gatherTypeVarLen = VarInt.length(buf, pos); - obj.gatherType = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += gatherTypeVarLen + gatherTypeLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -65,7 +73,7 @@ public class ItemToolSpec { int pos = offset + 9; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -110,7 +118,7 @@ public class ItemToolSpec { return ValidationResult.error("GatherType exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(gatherTypeLen); pos += gatherTypeLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading GatherType"); diff --git a/src/com/hypixel/hytale/protocol/ItemTranslationProperties.java b/src/com/hypixel/hytale/protocol/ItemTranslationProperties.java index ba536d74..3c5a4e52 100644 --- a/src/com/hypixel/hytale/protocol/ItemTranslationProperties.java +++ b/src/com/hypixel/hytale/protocol/ItemTranslationProperties.java @@ -35,37 +35,61 @@ public class ItemTranslationProperties { @Nonnull public static ItemTranslationProperties deserialize(@Nonnull ByteBuf buf, int offset) { - ItemTranslationProperties obj = new ItemTranslationProperties(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ItemTranslationProperties", 9, buf.readableBytes() - offset); + } else { + ItemTranslationProperties obj = new ItemTranslationProperties(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Description", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int descriptionLen = VarInt.peek(buf, varPos1); + if (descriptionLen < 0) { + throw ProtocolException.invalidVarInt("Description"); + } + + int descriptionVarIntLen = VarInt.size(descriptionLen); + if (descriptionLen > 4096000) { + throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); + } + + if (varPos1 + descriptionVarIntLen + descriptionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Description", varPos1 + descriptionVarIntLen + descriptionLen, buf.readableBytes()); + } + + obj.description = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int descriptionLen = VarInt.peek(buf, varPos1); - if (descriptionLen < 0) { - throw ProtocolException.negativeLength("Description", descriptionLen); - } - - if (descriptionLen > 4096000) { - throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); - } - - obj.description = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,9 +97,13 @@ public class ItemTranslationProperties { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -83,9 +111,13 @@ public class ItemTranslationProperties { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Description", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -146,15 +178,11 @@ public class ItemTranslationProperties { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int nameOffset = buffer.getIntLE(offset + 1); - if (nameOffset < 0) { + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Name"); } int pos = offset + 9 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - int nameLen = VarInt.peek(buffer, pos); if (nameLen < 0) { return ValidationResult.error("Invalid string length for Name"); @@ -164,7 +192,7 @@ public class ItemTranslationProperties { return ValidationResult.error("Name exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Name"); @@ -173,15 +201,11 @@ public class ItemTranslationProperties { if ((nullBits & 2) != 0) { int descriptionOffset = buffer.getIntLE(offset + 5); - if (descriptionOffset < 0) { + if (descriptionOffset < 0 || descriptionOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Description"); } int posx = offset + 9 + descriptionOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Description"); - } - int descriptionLen = VarInt.peek(buffer, posx); if (descriptionLen < 0) { return ValidationResult.error("Invalid string length for Description"); @@ -191,7 +215,7 @@ public class ItemTranslationProperties { return ValidationResult.error("Description exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(descriptionLen); posx += descriptionLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Description"); diff --git a/src/com/hypixel/hytale/protocol/ItemUpdate.java b/src/com/hypixel/hytale/protocol/ItemUpdate.java index 4367e454..7a8bab4a 100644 --- a/src/com/hypixel/hytale/protocol/ItemUpdate.java +++ b/src/com/hypixel/hytale/protocol/ItemUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -30,12 +31,16 @@ public class ItemUpdate extends ComponentUpdate { @Nonnull public static ItemUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - ItemUpdate obj = new ItemUpdate(); - obj.entityScale = buf.getFloatLE(offset + 0); - int pos = offset + 4; - obj.item = ItemWithAllMetadata.deserialize(buf, pos); - pos += ItemWithAllMetadata.computeBytesConsumed(buf, pos); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("ItemUpdate", 4, buf.readableBytes() - offset); + } else { + ItemUpdate obj = new ItemUpdate(); + obj.entityScale = buf.getFloatLE(offset + 0); + int pos = offset + 4; + obj.item = ItemWithAllMetadata.deserialize(buf, pos); + pos += ItemWithAllMetadata.computeBytesConsumed(buf, pos); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ItemUtility.java b/src/com/hypixel/hytale/protocol/ItemUtility.java index 56af5c86..b41d5580 100644 --- a/src/com/hypixel/hytale/protocol/ItemUtility.java +++ b/src/com/hypixel/hytale/protocol/ItemUtility.java @@ -44,80 +44,94 @@ public class ItemUtility { @Nonnull public static ItemUtility deserialize(@Nonnull ByteBuf buf, int offset) { - ItemUtility obj = new ItemUtility(); - byte nullBits = buf.getByte(offset); - obj.usable = buf.getByte(offset + 1) != 0; - obj.compatible = buf.getByte(offset + 2) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 11 + buf.getIntLE(offset + 3); - int entityStatsToClearCount = VarInt.peek(buf, varPos0); - if (entityStatsToClearCount < 0) { - throw ProtocolException.negativeLength("EntityStatsToClear", entityStatsToClearCount); + if (buf.readableBytes() - offset < 11) { + throw ProtocolException.bufferTooSmall("ItemUtility", 11, buf.readableBytes() - offset); + } else { + ItemUtility obj = new ItemUtility(); + byte nullBits = buf.getByte(offset); + obj.usable = buf.getByte(offset + 1) != 0; + obj.compatible = buf.getByte(offset + 2) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 3); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 11) { + throw ProtocolException.invalidOffset("EntityStatsToClear", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 11 + varPosBase0; + int entityStatsToClearCount = VarInt.peek(buf, varPos0); + if (entityStatsToClearCount < 0) { + throw ProtocolException.invalidVarInt("EntityStatsToClear"); + } + + int varIntLen = VarInt.size(entityStatsToClearCount); + if (entityStatsToClearCount > 4096000) { + throw ProtocolException.arrayTooLong("EntityStatsToClear", entityStatsToClearCount, 4096000); + } + + if (varPos0 + varIntLen + entityStatsToClearCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EntityStatsToClear", varPos0 + varIntLen + entityStatsToClearCount * 4, buf.readableBytes()); + } + + obj.entityStatsToClear = new int[entityStatsToClearCount]; + + for (int i = 0; i < entityStatsToClearCount; i++) { + obj.entityStatsToClear[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); + } } - if (entityStatsToClearCount > 4096000) { - throw ProtocolException.arrayTooLong("EntityStatsToClear", entityStatsToClearCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 7); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 11) { + throw ProtocolException.invalidOffset("StatModifiers", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 11 + varPosBase1; + int statModifiersCount = VarInt.peek(buf, varPos1); + if (statModifiersCount < 0) { + throw ProtocolException.invalidVarInt("StatModifiers"); + } + + int varIntLenx = VarInt.size(statModifiersCount); + if (statModifiersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); + } + + obj.statModifiers = new HashMap<>(statModifiersCount); + int dictPos = varPos1 + varIntLenx; + + for (int i = 0; i < statModifiersCount; i++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + int valLen = VarInt.peek(buf, dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 64) { + throw ProtocolException.arrayTooLong("val", valLen, 64); + } + + if (dictPos + valVarLen + valLen * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 6, buf.readableBytes()); + } + + dictPos += valVarLen; + Modifier[] val = new Modifier[valLen]; + + for (int valIdx = 0; valIdx < valLen; valIdx++) { + val[valIdx] = Modifier.deserialize(buf, dictPos); + dictPos += Modifier.computeBytesConsumed(buf, dictPos); + } + + if (obj.statModifiers.put(key, val) != null) { + throw ProtocolException.duplicateKey("statModifiers", key); + } + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + entityStatsToClearCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("EntityStatsToClear", varPos0 + varIntLen + entityStatsToClearCount * 4, buf.readableBytes()); - } - - obj.entityStatsToClear = new int[entityStatsToClearCount]; - - for (int i = 0; i < entityStatsToClearCount; i++) { - obj.entityStatsToClear[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 11 + buf.getIntLE(offset + 7); - int statModifiersCount = VarInt.peek(buf, varPos1); - if (statModifiersCount < 0) { - throw ProtocolException.negativeLength("StatModifiers", statModifiersCount); - } - - if (statModifiersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.statModifiers = new HashMap<>(statModifiersCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < statModifiersCount; i++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - int valLen = VarInt.peek(buf, dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); - } - - if (valLen > 64) { - throw ProtocolException.arrayTooLong("val", valLen, 64); - } - - int valVarLen = VarInt.length(buf, dictPos); - if (dictPos + valVarLen + valLen * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 6, buf.readableBytes()); - } - - dictPos += valVarLen; - Modifier[] val = new Modifier[valLen]; - - for (int valIdx = 0; valIdx < valLen; valIdx++) { - val[valIdx] = Modifier.deserialize(buf, dictPos); - dictPos += Modifier.computeBytesConsumed(buf, dictPos); - } - - if (obj.statModifiers.put(key, val) != null) { - throw ProtocolException.duplicateKey("statModifiers", key); - } - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -125,9 +139,13 @@ public class ItemUtility { int maxEnd = 11; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 3); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 11) { + throw ProtocolException.invalidOffset("EntityStatsToClear", fieldOffset0, maxEnd); + } + int pos0 = offset + 11 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 4; + pos0 += VarInt.size(arrLen) + arrLen * 4; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -135,14 +153,18 @@ public class ItemUtility { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 7); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 11) { + throw ProtocolException.invalidOffset("StatModifiers", fieldOffset1, maxEnd); + } + int pos1 = offset + 11 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 += 4; int al = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(al); for (int j = 0; j < al; j++) { pos1 += Modifier.computeBytesConsumed(buf, pos1); @@ -238,15 +260,11 @@ public class ItemUtility { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int entityStatsToClearOffset = buffer.getIntLE(offset + 3); - if (entityStatsToClearOffset < 0) { + if (entityStatsToClearOffset < 0 || entityStatsToClearOffset > buffer.writerIndex() - offset - 11) { return ValidationResult.error("Invalid offset for EntityStatsToClear"); } int pos = offset + 11 + entityStatsToClearOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EntityStatsToClear"); - } - int entityStatsToClearCount = VarInt.peek(buffer, pos); if (entityStatsToClearCount < 0) { return ValidationResult.error("Invalid array count for EntityStatsToClear"); @@ -256,7 +274,7 @@ public class ItemUtility { return ValidationResult.error("EntityStatsToClear exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(entityStatsToClearCount); pos += entityStatsToClearCount * 4; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading EntityStatsToClear"); @@ -265,15 +283,11 @@ public class ItemUtility { if ((nullBits & 2) != 0) { int statModifiersOffset = buffer.getIntLE(offset + 7); - if (statModifiersOffset < 0) { + if (statModifiersOffset < 0 || statModifiersOffset > buffer.writerIndex() - offset - 11) { return ValidationResult.error("Invalid offset for StatModifiers"); } int posx = offset + 11 + statModifiersOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for StatModifiers"); - } - int statModifiersCount = VarInt.peek(buffer, posx); if (statModifiersCount < 0) { return ValidationResult.error("Invalid dictionary count for StatModifiers"); @@ -283,7 +297,7 @@ public class ItemUtility { return ValidationResult.error("StatModifiers exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(statModifiersCount); for (int i = 0; i < statModifiersCount; i++) { posx += 4; @@ -296,7 +310,7 @@ public class ItemUtility { return ValidationResult.error("Invalid array count for value"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(valueArrCount); for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { posx += 6; diff --git a/src/com/hypixel/hytale/protocol/ItemWeapon.java b/src/com/hypixel/hytale/protocol/ItemWeapon.java index 5c01a1a6..9b29d44f 100644 --- a/src/com/hypixel/hytale/protocol/ItemWeapon.java +++ b/src/com/hypixel/hytale/protocol/ItemWeapon.java @@ -41,79 +41,93 @@ public class ItemWeapon { @Nonnull public static ItemWeapon deserialize(@Nonnull ByteBuf buf, int offset) { - ItemWeapon obj = new ItemWeapon(); - byte nullBits = buf.getByte(offset); - obj.renderDualWielded = buf.getByte(offset + 1) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - int entityStatsToClearCount = VarInt.peek(buf, varPos0); - if (entityStatsToClearCount < 0) { - throw ProtocolException.negativeLength("EntityStatsToClear", entityStatsToClearCount); + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("ItemWeapon", 10, buf.readableBytes() - offset); + } else { + ItemWeapon obj = new ItemWeapon(); + byte nullBits = buf.getByte(offset); + obj.renderDualWielded = buf.getByte(offset + 1) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("EntityStatsToClear", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 10 + varPosBase0; + int entityStatsToClearCount = VarInt.peek(buf, varPos0); + if (entityStatsToClearCount < 0) { + throw ProtocolException.invalidVarInt("EntityStatsToClear"); + } + + int varIntLen = VarInt.size(entityStatsToClearCount); + if (entityStatsToClearCount > 4096000) { + throw ProtocolException.arrayTooLong("EntityStatsToClear", entityStatsToClearCount, 4096000); + } + + if (varPos0 + varIntLen + entityStatsToClearCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EntityStatsToClear", varPos0 + varIntLen + entityStatsToClearCount * 4, buf.readableBytes()); + } + + obj.entityStatsToClear = new int[entityStatsToClearCount]; + + for (int i = 0; i < entityStatsToClearCount; i++) { + obj.entityStatsToClear[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); + } } - if (entityStatsToClearCount > 4096000) { - throw ProtocolException.arrayTooLong("EntityStatsToClear", entityStatsToClearCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("StatModifiers", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + int statModifiersCount = VarInt.peek(buf, varPos1); + if (statModifiersCount < 0) { + throw ProtocolException.invalidVarInt("StatModifiers"); + } + + int varIntLenx = VarInt.size(statModifiersCount); + if (statModifiersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); + } + + obj.statModifiers = new HashMap<>(statModifiersCount); + int dictPos = varPos1 + varIntLenx; + + for (int i = 0; i < statModifiersCount; i++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + int valLen = VarInt.peek(buf, dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 64) { + throw ProtocolException.arrayTooLong("val", valLen, 64); + } + + if (dictPos + valVarLen + valLen * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 6, buf.readableBytes()); + } + + dictPos += valVarLen; + Modifier[] val = new Modifier[valLen]; + + for (int valIdx = 0; valIdx < valLen; valIdx++) { + val[valIdx] = Modifier.deserialize(buf, dictPos); + dictPos += Modifier.computeBytesConsumed(buf, dictPos); + } + + if (obj.statModifiers.put(key, val) != null) { + throw ProtocolException.duplicateKey("statModifiers", key); + } + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + entityStatsToClearCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("EntityStatsToClear", varPos0 + varIntLen + entityStatsToClearCount * 4, buf.readableBytes()); - } - - obj.entityStatsToClear = new int[entityStatsToClearCount]; - - for (int i = 0; i < entityStatsToClearCount; i++) { - obj.entityStatsToClear[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - int statModifiersCount = VarInt.peek(buf, varPos1); - if (statModifiersCount < 0) { - throw ProtocolException.negativeLength("StatModifiers", statModifiersCount); - } - - if (statModifiersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("StatModifiers", statModifiersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.statModifiers = new HashMap<>(statModifiersCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < statModifiersCount; i++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - int valLen = VarInt.peek(buf, dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); - } - - if (valLen > 64) { - throw ProtocolException.arrayTooLong("val", valLen, 64); - } - - int valVarLen = VarInt.length(buf, dictPos); - if (dictPos + valVarLen + valLen * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 6, buf.readableBytes()); - } - - dictPos += valVarLen; - Modifier[] val = new Modifier[valLen]; - - for (int valIdx = 0; valIdx < valLen; valIdx++) { - val[valIdx] = Modifier.deserialize(buf, dictPos); - dictPos += Modifier.computeBytesConsumed(buf, dictPos); - } - - if (obj.statModifiers.put(key, val) != null) { - throw ProtocolException.duplicateKey("statModifiers", key); - } - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -121,9 +135,13 @@ public class ItemWeapon { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("EntityStatsToClear", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 4; + pos0 += VarInt.size(arrLen) + arrLen * 4; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -131,14 +149,18 @@ public class ItemWeapon { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("StatModifiers", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 += 4; int al = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(al); for (int j = 0; j < al; j++) { pos1 += Modifier.computeBytesConsumed(buf, pos1); @@ -233,15 +255,11 @@ public class ItemWeapon { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int entityStatsToClearOffset = buffer.getIntLE(offset + 2); - if (entityStatsToClearOffset < 0) { + if (entityStatsToClearOffset < 0 || entityStatsToClearOffset > buffer.writerIndex() - offset - 10) { return ValidationResult.error("Invalid offset for EntityStatsToClear"); } int pos = offset + 10 + entityStatsToClearOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EntityStatsToClear"); - } - int entityStatsToClearCount = VarInt.peek(buffer, pos); if (entityStatsToClearCount < 0) { return ValidationResult.error("Invalid array count for EntityStatsToClear"); @@ -251,7 +269,7 @@ public class ItemWeapon { return ValidationResult.error("EntityStatsToClear exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(entityStatsToClearCount); pos += entityStatsToClearCount * 4; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading EntityStatsToClear"); @@ -260,15 +278,11 @@ public class ItemWeapon { if ((nullBits & 2) != 0) { int statModifiersOffset = buffer.getIntLE(offset + 6); - if (statModifiersOffset < 0) { + if (statModifiersOffset < 0 || statModifiersOffset > buffer.writerIndex() - offset - 10) { return ValidationResult.error("Invalid offset for StatModifiers"); } int posx = offset + 10 + statModifiersOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for StatModifiers"); - } - int statModifiersCount = VarInt.peek(buffer, posx); if (statModifiersCount < 0) { return ValidationResult.error("Invalid dictionary count for StatModifiers"); @@ -278,7 +292,7 @@ public class ItemWeapon { return ValidationResult.error("StatModifiers exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(statModifiersCount); for (int i = 0; i < statModifiersCount; i++) { posx += 4; @@ -291,7 +305,7 @@ public class ItemWeapon { return ValidationResult.error("Invalid array count for value"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(valueArrCount); for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { posx += 6; diff --git a/src/com/hypixel/hytale/protocol/ItemWithAllMetadata.java b/src/com/hypixel/hytale/protocol/ItemWithAllMetadata.java index 189d80e5..8a867b91 100644 --- a/src/com/hypixel/hytale/protocol/ItemWithAllMetadata.java +++ b/src/com/hypixel/hytale/protocol/ItemWithAllMetadata.java @@ -49,35 +49,59 @@ public class ItemWithAllMetadata { @Nonnull public static ItemWithAllMetadata deserialize(@Nonnull ByteBuf buf, int offset) { - ItemWithAllMetadata obj = new ItemWithAllMetadata(); - byte nullBits = buf.getByte(offset); - obj.quantity = buf.getIntLE(offset + 1); - obj.durability = buf.getDoubleLE(offset + 5); - obj.maxDurability = buf.getDoubleLE(offset + 13); - obj.overrideDroppedItemAnimation = buf.getByte(offset + 21) != 0; - int varPos0 = offset + 30 + buf.getIntLE(offset + 22); - int itemIdLen = VarInt.peek(buf, varPos0); - if (itemIdLen < 0) { - throw ProtocolException.negativeLength("ItemId", itemIdLen); - } else if (itemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + if (buf.readableBytes() - offset < 30) { + throw ProtocolException.bufferTooSmall("ItemWithAllMetadata", 30, buf.readableBytes() - offset); } else { - obj.itemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - if ((nullBits & 1) != 0) { - varPos0 = offset + 30 + buf.getIntLE(offset + 26); - itemIdLen = VarInt.peek(buf, varPos0); + ItemWithAllMetadata obj = new ItemWithAllMetadata(); + byte nullBits = buf.getByte(offset); + obj.quantity = buf.getIntLE(offset + 1); + obj.durability = buf.getDoubleLE(offset + 5); + obj.maxDurability = buf.getDoubleLE(offset + 13); + obj.overrideDroppedItemAnimation = buf.getByte(offset + 21) != 0; + int varPosBase0 = buf.getIntLE(offset + 22); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 30) { + int varPos0 = offset + 30 + varPosBase0; + int itemIdLen = VarInt.peek(buf, varPos0); if (itemIdLen < 0) { - throw ProtocolException.negativeLength("Metadata", itemIdLen); - } + throw ProtocolException.invalidVarInt("ItemId"); + } else { + int itemIdVarIntLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + } else if (varPos0 + itemIdVarIntLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemId", varPos0 + itemIdVarIntLen + itemIdLen, buf.readableBytes()); + } else { + obj.itemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 1) != 0) { + varPosBase0 = buf.getIntLE(offset + 26); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Metadata", varPosBase0, buf.readableBytes()); + } - if (itemIdLen > 4096000) { - throw ProtocolException.stringTooLong("Metadata", itemIdLen, 4096000); - } + varPos0 = offset + 30 + varPosBase0; + itemIdLen = VarInt.peek(buf, varPos0); + if (itemIdLen < 0) { + throw ProtocolException.invalidVarInt("Metadata"); + } - obj.metadata = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + itemIdVarIntLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("Metadata", itemIdLen, 4096000); + } + + if (varPos0 + itemIdVarIntLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Metadata", varPos0 + itemIdVarIntLen + itemIdLen, buf.readableBytes()); + } + + obj.metadata = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("ItemId", varPosBase0, buf.readableBytes()); } - - return obj; } } @@ -85,24 +109,32 @@ public class ItemWithAllMetadata { byte nullBits = buf.getByte(offset); int maxEnd = 30; int fieldOffset0 = buf.getIntLE(offset + 22); - int pos0 = offset + 30 + fieldOffset0; - int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - - if ((nullBits & 1) != 0) { - fieldOffset0 = buf.getIntLE(offset + 26); - pos0 = offset + 30 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 30) { + int pos0 = offset + 30 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } - } - return maxEnd; + if ((nullBits & 1) != 0) { + fieldOffset0 = buf.getIntLE(offset + 26); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Metadata", fieldOffset0, maxEnd); + } + + pos0 = offset + 30 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("ItemId", fieldOffset0, maxEnd); + } } public void serialize(@Nonnull ByteBuf buf) { @@ -148,55 +180,47 @@ public class ItemWithAllMetadata { } else { byte nullBits = buffer.getByte(offset); int itemIdOffset = buffer.getIntLE(offset + 22); - if (itemIdOffset < 0) { - return ValidationResult.error("Invalid offset for ItemId"); - } else { + if (itemIdOffset >= 0 && itemIdOffset <= buffer.writerIndex() - offset - 30) { int pos = offset + 30 + itemIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemId"); + int itemIdLen = VarInt.peek(buffer, pos); + if (itemIdLen < 0) { + return ValidationResult.error("Invalid string length for ItemId"); + } else if (itemIdLen > 4096000) { + return ValidationResult.error("ItemId exceeds max length 4096000"); } else { - int itemIdLen = VarInt.peek(buffer, pos); - if (itemIdLen < 0) { - return ValidationResult.error("Invalid string length for ItemId"); - } else if (itemIdLen > 4096000) { - return ValidationResult.error("ItemId exceeds max length 4096000"); + pos += VarInt.size(itemIdLen); + pos += itemIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ItemId"); } else { - pos += VarInt.length(buffer, pos); - pos += itemIdLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ItemId"); - } else { - if ((nullBits & 1) != 0) { - itemIdOffset = buffer.getIntLE(offset + 26); - if (itemIdOffset < 0) { - return ValidationResult.error("Invalid offset for Metadata"); - } - - pos = offset + 30 + itemIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Metadata"); - } - - itemIdLen = VarInt.peek(buffer, pos); - if (itemIdLen < 0) { - return ValidationResult.error("Invalid string length for Metadata"); - } - - if (itemIdLen > 4096000) { - return ValidationResult.error("Metadata exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += itemIdLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Metadata"); - } + if ((nullBits & 1) != 0) { + itemIdOffset = buffer.getIntLE(offset + 26); + if (itemIdOffset < 0 || itemIdOffset > buffer.writerIndex() - offset - 30) { + return ValidationResult.error("Invalid offset for Metadata"); } - return ValidationResult.OK; + pos = offset + 30 + itemIdOffset; + itemIdLen = VarInt.peek(buffer, pos); + if (itemIdLen < 0) { + return ValidationResult.error("Invalid string length for Metadata"); + } + + if (itemIdLen > 4096000) { + return ValidationResult.error("Metadata exceeds max length 4096000"); + } + + pos += VarInt.size(itemIdLen); + pos += itemIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Metadata"); + } } + + return ValidationResult.OK; } } + } else { + return ValidationResult.error("Invalid offset for ItemId"); } } } diff --git a/src/com/hypixel/hytale/protocol/LongParamValue.java b/src/com/hypixel/hytale/protocol/LongParamValue.java index f9829218..e3eab497 100644 --- a/src/com/hypixel/hytale/protocol/LongParamValue.java +++ b/src/com/hypixel/hytale/protocol/LongParamValue.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class LongParamValue extends ParamValue { @Nonnull public static LongParamValue deserialize(@Nonnull ByteBuf buf, int offset) { - LongParamValue obj = new LongParamValue(); - obj.value = buf.getLongLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("LongParamValue", 8, buf.readableBytes() - offset); + } else { + LongParamValue obj = new LongParamValue(); + obj.value = buf.getLongLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/MaterialQuantity.java b/src/com/hypixel/hytale/protocol/MaterialQuantity.java index c44bc2ef..742d52d4 100644 --- a/src/com/hypixel/hytale/protocol/MaterialQuantity.java +++ b/src/com/hypixel/hytale/protocol/MaterialQuantity.java @@ -41,39 +41,63 @@ public class MaterialQuantity { @Nonnull public static MaterialQuantity deserialize(@Nonnull ByteBuf buf, int offset) { - MaterialQuantity obj = new MaterialQuantity(); - byte nullBits = buf.getByte(offset); - obj.itemTag = buf.getIntLE(offset + 1); - obj.quantity = buf.getIntLE(offset + 5); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 17 + buf.getIntLE(offset + 9); - int itemIdLen = VarInt.peek(buf, varPos0); - if (itemIdLen < 0) { - throw ProtocolException.negativeLength("ItemId", itemIdLen); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("MaterialQuantity", 17, buf.readableBytes() - offset); + } else { + MaterialQuantity obj = new MaterialQuantity(); + byte nullBits = buf.getByte(offset); + obj.itemTag = buf.getIntLE(offset + 1); + obj.quantity = buf.getIntLE(offset + 5); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ItemId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int itemIdLen = VarInt.peek(buf, varPos0); + if (itemIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemId"); + } + + int itemIdVarIntLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + } + + if (varPos0 + itemIdVarIntLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemId", varPos0 + itemIdVarIntLen + itemIdLen, buf.readableBytes()); + } + + obj.itemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (itemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 13); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ResourceTypeId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + int resourceTypeIdLen = VarInt.peek(buf, varPos1); + if (resourceTypeIdLen < 0) { + throw ProtocolException.invalidVarInt("ResourceTypeId"); + } + + int resourceTypeIdVarIntLen = VarInt.size(resourceTypeIdLen); + if (resourceTypeIdLen > 4096000) { + throw ProtocolException.stringTooLong("ResourceTypeId", resourceTypeIdLen, 4096000); + } + + if (varPos1 + resourceTypeIdVarIntLen + resourceTypeIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ResourceTypeId", varPos1 + resourceTypeIdVarIntLen + resourceTypeIdLen, buf.readableBytes()); + } + + obj.resourceTypeId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.itemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 17 + buf.getIntLE(offset + 13); - int resourceTypeIdLen = VarInt.peek(buf, varPos1); - if (resourceTypeIdLen < 0) { - throw ProtocolException.negativeLength("ResourceTypeId", resourceTypeIdLen); - } - - if (resourceTypeIdLen > 4096000) { - throw ProtocolException.stringTooLong("ResourceTypeId", resourceTypeIdLen, 4096000); - } - - obj.resourceTypeId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,9 +105,13 @@ public class MaterialQuantity { int maxEnd = 17; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ItemId", fieldOffset0, maxEnd); + } + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -91,9 +119,13 @@ public class MaterialQuantity { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 13); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ResourceTypeId", fieldOffset1, maxEnd); + } + int pos1 = offset + 17 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -156,15 +188,11 @@ public class MaterialQuantity { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int itemIdOffset = buffer.getIntLE(offset + 9); - if (itemIdOffset < 0) { + if (itemIdOffset < 0 || itemIdOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for ItemId"); } int pos = offset + 17 + itemIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemId"); - } - int itemIdLen = VarInt.peek(buffer, pos); if (itemIdLen < 0) { return ValidationResult.error("Invalid string length for ItemId"); @@ -174,7 +202,7 @@ public class MaterialQuantity { return ValidationResult.error("ItemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(itemIdLen); pos += itemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemId"); @@ -183,15 +211,11 @@ public class MaterialQuantity { if ((nullBits & 2) != 0) { int resourceTypeIdOffset = buffer.getIntLE(offset + 13); - if (resourceTypeIdOffset < 0) { + if (resourceTypeIdOffset < 0 || resourceTypeIdOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for ResourceTypeId"); } int posx = offset + 17 + resourceTypeIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ResourceTypeId"); - } - int resourceTypeIdLen = VarInt.peek(buffer, posx); if (resourceTypeIdLen < 0) { return ValidationResult.error("Invalid string length for ResourceTypeId"); @@ -201,7 +225,7 @@ public class MaterialQuantity { return ValidationResult.error("ResourceTypeId exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(resourceTypeIdLen); posx += resourceTypeIdLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ResourceTypeId"); diff --git a/src/com/hypixel/hytale/protocol/MemoriesConditionInteraction.java b/src/com/hypixel/hytale/protocol/MemoriesConditionInteraction.java index 7d3cfe88..502431d2 100644 --- a/src/com/hypixel/hytale/protocol/MemoriesConditionInteraction.java +++ b/src/com/hypixel/hytale/protocol/MemoriesConditionInteraction.java @@ -67,103 +67,137 @@ public class MemoriesConditionInteraction extends Interaction { @Nonnull public static MemoriesConditionInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - MemoriesConditionInteraction obj = new MemoriesConditionInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.failed = buf.getIntLE(offset + 11); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 39 + buf.getIntLE(offset + 15); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 39) { + throw ProtocolException.bufferTooSmall("MemoriesConditionInteraction", 39, buf.readableBytes() - offset); + } else { + MemoriesConditionInteraction obj = new MemoriesConditionInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.failed = buf.getIntLE(offset + 11); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 15); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 39 + buf.getIntLE(offset + 19); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 39 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 19); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 39 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 39 + buf.getIntLE(offset + 23); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 23); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 39 + buf.getIntLE(offset + 27); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 39 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 27); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } + int varPos3 = offset + 39 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } - obj.tags = new int[tagsCount]; + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 39 + buf.getIntLE(offset + 31); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } + obj.tags = new int[tagsCount]; - if ((nullBits & 32) != 0) { - int varPos5 = offset + 39 + buf.getIntLE(offset + 35); - int memoriesNextCount = VarInt.peek(buf, varPos5); - if (memoriesNextCount < 0) { - throw ProtocolException.negativeLength("MemoriesNext", memoriesNextCount); - } - - if (memoriesNextCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("MemoriesNext", memoriesNextCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - obj.memoriesNext = new HashMap<>(memoriesNextCount); - int dictPos = varPos5 + varIntLen; - - for (int ix = 0; ix < memoriesNextCount; ix++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - int val = buf.getIntLE(dictPos); - dictPos += 4; - if (obj.memoriesNext.put(key, val) != null) { - throw ProtocolException.duplicateKey("memoriesNext", key); + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); } } - } - return obj; + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 31); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 39 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 35); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("MemoriesNext", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 39 + varPosBase5; + int memoriesNextCount = VarInt.peek(buf, varPos5); + if (memoriesNextCount < 0) { + throw ProtocolException.invalidVarInt("MemoriesNext"); + } + + int varIntLenx = VarInt.size(memoriesNextCount); + if (memoriesNextCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("MemoriesNext", memoriesNextCount, 4096000); + } + + obj.memoriesNext = new HashMap<>(memoriesNextCount); + int dictPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < memoriesNextCount; ix++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + int val = buf.getIntLE(dictPos); + dictPos += 4; + if (obj.memoriesNext.put(key, val) != null) { + throw ProtocolException.duplicateKey("memoriesNext", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -171,6 +205,10 @@ public class MemoriesConditionInteraction extends Interaction { int maxEnd = 39; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 15); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 39 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -180,9 +218,13 @@ public class MemoriesConditionInteraction extends Interaction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 19); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 39 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -195,6 +237,10 @@ public class MemoriesConditionInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 23); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 39 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -204,9 +250,13 @@ public class MemoriesConditionInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 27); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 39 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -214,6 +264,10 @@ public class MemoriesConditionInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 31); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 39 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -223,9 +277,13 @@ public class MemoriesConditionInteraction extends Interaction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 35); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("MemoriesNext", fieldOffset5, maxEnd); + } + int pos5 = offset + 39 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos5 += 4; @@ -393,154 +451,140 @@ public class MemoriesConditionInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 39 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 15); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 39 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 19); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 39 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 23); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 39 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 27); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 39 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 31); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 39 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int memoriesNextOffset = buffer.getIntLE(offset + 35); - if (memoriesNextOffset < 0) { - return ValidationResult.error("Invalid offset for MemoriesNext"); - } - - int posxxxxx = offset + 39 + memoriesNextOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MemoriesNext"); - } - - int memoriesNextCount = VarInt.peek(buffer, posxxxxx); - if (memoriesNextCount < 0) { - return ValidationResult.error("Invalid dictionary count for MemoriesNext"); - } - - if (memoriesNextCount > 4096000) { - return ValidationResult.error("MemoriesNext exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < memoriesNextCount; i++) { - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Effects"); } - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + int pos = offset + 39 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 39 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; } } - } - return ValidationResult.OK; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 39 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 39 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 39 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for MemoriesNext"); + } + + int posxx = offset + 39 + v; + int memoriesNextCount = VarInt.peek(buffer, posxx); + if (memoriesNextCount < 0) { + return ValidationResult.error("Invalid dictionary count for MemoriesNext"); + } + + if (memoriesNextCount > 4096000) { + return ValidationResult.error("MemoriesNext exceeds max length 4096000"); + } + + posxx += VarInt.size(memoriesNextCount); + + for (int i = 0; i < memoriesNextCount; i++) { + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/Model.java b/src/com/hypixel/hytale/protocol/Model.java index 09270fd6..b6961496 100644 --- a/src/com/hypixel/hytale/protocol/Model.java +++ b/src/com/hypixel/hytale/protocol/Model.java @@ -127,270 +127,367 @@ public class Model { @Nonnull public static Model deserialize(@Nonnull ByteBuf buf, int offset) { - Model obj = new Model(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.scale = buf.getFloatLE(offset + 2); - obj.eyeHeight = buf.getFloatLE(offset + 6); - obj.crouchOffset = buf.getFloatLE(offset + 10); - obj.sittingOffset = buf.getFloatLE(offset + 14); - obj.sleepingOffset = buf.getFloatLE(offset + 18); - if ((nullBits[0] & 1) != 0) { - obj.hitbox = Hitbox.deserialize(buf, offset + 22); - } - - if ((nullBits[0] & 2) != 0) { - obj.light = ColorLight.deserialize(buf, offset + 46); - } - - obj.phobia = Phobia.fromValue(buf.getByte(offset + 50)); - if ((nullBits[0] & 4) != 0) { - int varPos0 = offset + 99 + buf.getIntLE(offset + 51); - int assetIdLen = VarInt.peek(buf, varPos0); - if (assetIdLen < 0) { - throw ProtocolException.negativeLength("AssetId", assetIdLen); + if (buf.readableBytes() - offset < 99) { + throw ProtocolException.bufferTooSmall("Model", 99, buf.readableBytes() - offset); + } else { + Model obj = new Model(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + obj.scale = buf.getFloatLE(offset + 2); + obj.eyeHeight = buf.getFloatLE(offset + 6); + obj.crouchOffset = buf.getFloatLE(offset + 10); + obj.sittingOffset = buf.getFloatLE(offset + 14); + obj.sleepingOffset = buf.getFloatLE(offset + 18); + if ((nullBits[0] & 1) != 0) { + obj.hitbox = Hitbox.deserialize(buf, offset + 22); } - if (assetIdLen > 4096000) { - throw ProtocolException.stringTooLong("AssetId", assetIdLen, 4096000); + if ((nullBits[0] & 2) != 0) { + obj.light = ColorLight.deserialize(buf, offset + 46); } - obj.assetId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits[0] & 8) != 0) { - int varPos1 = offset + 99 + buf.getIntLE(offset + 55); - int pathLen = VarInt.peek(buf, varPos1); - if (pathLen < 0) { - throw ProtocolException.negativeLength("Path", pathLen); - } - - if (pathLen > 4096000) { - throw ProtocolException.stringTooLong("Path", pathLen, 4096000); - } - - obj.path = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits[0] & 16) != 0) { - int varPos2 = offset + 99 + buf.getIntLE(offset + 59); - int textureLen = VarInt.peek(buf, varPos2); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); - } - - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } - - obj.texture = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits[0] & 32) != 0) { - int varPos3 = offset + 99 + buf.getIntLE(offset + 63); - int gradientSetLen = VarInt.peek(buf, varPos3); - if (gradientSetLen < 0) { - throw ProtocolException.negativeLength("GradientSet", gradientSetLen); - } - - if (gradientSetLen > 4096000) { - throw ProtocolException.stringTooLong("GradientSet", gradientSetLen, 4096000); - } - - obj.gradientSet = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits[0] & 64) != 0) { - int varPos4 = offset + 99 + buf.getIntLE(offset + 67); - int gradientIdLen = VarInt.peek(buf, varPos4); - if (gradientIdLen < 0) { - throw ProtocolException.negativeLength("GradientId", gradientIdLen); - } - - if (gradientIdLen > 4096000) { - throw ProtocolException.stringTooLong("GradientId", gradientIdLen, 4096000); - } - - obj.gradientId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - if ((nullBits[0] & 128) != 0) { - int varPos5 = offset + 99 + buf.getIntLE(offset + 71); - obj.camera = CameraSettings.deserialize(buf, varPos5); - } - - if ((nullBits[1] & 1) != 0) { - int varPos6 = offset + 99 + buf.getIntLE(offset + 75); - int animationSetsCount = VarInt.peek(buf, varPos6); - if (animationSetsCount < 0) { - throw ProtocolException.negativeLength("AnimationSets", animationSetsCount); - } - - if (animationSetsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("AnimationSets", animationSetsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos6); - obj.animationSets = new HashMap<>(animationSetsCount); - int dictPos = varPos6 + varIntLen; - - for (int i = 0; i < animationSetsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + obj.phobia = Phobia.fromValue(buf.getByte(offset + 50)); + if ((nullBits[0] & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 51); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("AssetId", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 99 + varPosBase0; + int assetIdLen = VarInt.peek(buf, varPos0); + if (assetIdLen < 0) { + throw ProtocolException.invalidVarInt("AssetId"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - AnimationSet val = AnimationSet.deserialize(buf, dictPos); - dictPos += AnimationSet.computeBytesConsumed(buf, dictPos); - if (obj.animationSets.put(key, val) != null) { - throw ProtocolException.duplicateKey("animationSets", key); + int assetIdVarIntLen = VarInt.size(assetIdLen); + if (assetIdLen > 4096000) { + throw ProtocolException.stringTooLong("AssetId", assetIdLen, 4096000); + } + + if (varPos0 + assetIdVarIntLen + assetIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AssetId", varPos0 + assetIdVarIntLen + assetIdLen, buf.readableBytes()); + } + + obj.assetId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits[0] & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 55); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Path", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 99 + varPosBase1; + int pathLen = VarInt.peek(buf, varPos1); + if (pathLen < 0) { + throw ProtocolException.invalidVarInt("Path"); + } + + int pathVarIntLen = VarInt.size(pathLen); + if (pathLen > 4096000) { + throw ProtocolException.stringTooLong("Path", pathLen, 4096000); + } + + if (varPos1 + pathVarIntLen + pathLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Path", varPos1 + pathVarIntLen + pathLen, buf.readableBytes()); + } + + obj.path = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits[0] & 16) != 0) { + int varPosBase2 = buf.getIntLE(offset + 59); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Texture", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 99 + varPosBase2; + int textureLen = VarInt.peek(buf, varPos2); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarIntLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (varPos2 + textureVarIntLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", varPos2 + textureVarIntLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits[0] & 32) != 0) { + int varPosBase3 = buf.getIntLE(offset + 63); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("GradientSet", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 99 + varPosBase3; + int gradientSetLen = VarInt.peek(buf, varPos3); + if (gradientSetLen < 0) { + throw ProtocolException.invalidVarInt("GradientSet"); + } + + int gradientSetVarIntLen = VarInt.size(gradientSetLen); + if (gradientSetLen > 4096000) { + throw ProtocolException.stringTooLong("GradientSet", gradientSetLen, 4096000); + } + + if (varPos3 + gradientSetVarIntLen + gradientSetLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("GradientSet", varPos3 + gradientSetVarIntLen + gradientSetLen, buf.readableBytes()); + } + + obj.gradientSet = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits[0] & 64) != 0) { + int varPosBase4 = buf.getIntLE(offset + 67); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("GradientId", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 99 + varPosBase4; + int gradientIdLen = VarInt.peek(buf, varPos4); + if (gradientIdLen < 0) { + throw ProtocolException.invalidVarInt("GradientId"); + } + + int gradientIdVarIntLen = VarInt.size(gradientIdLen); + if (gradientIdLen > 4096000) { + throw ProtocolException.stringTooLong("GradientId", gradientIdLen, 4096000); + } + + if (varPos4 + gradientIdVarIntLen + gradientIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("GradientId", varPos4 + gradientIdVarIntLen + gradientIdLen, buf.readableBytes()); + } + + obj.gradientId = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase5 = buf.getIntLE(offset + 71); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Camera", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 99 + varPosBase5; + obj.camera = CameraSettings.deserialize(buf, varPos5); + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase6 = buf.getIntLE(offset + 75); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("AnimationSets", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 99 + varPosBase6; + int animationSetsCount = VarInt.peek(buf, varPos6); + if (animationSetsCount < 0) { + throw ProtocolException.invalidVarInt("AnimationSets"); + } + + int varIntLen = VarInt.size(animationSetsCount); + if (animationSetsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("AnimationSets", animationSetsCount, 4096000); + } + + obj.animationSets = new HashMap<>(animationSetsCount); + int dictPos = varPos6 + varIntLen; + + for (int i = 0; i < animationSetsCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + AnimationSet val = AnimationSet.deserialize(buf, dictPos); + dictPos += AnimationSet.computeBytesConsumed(buf, dictPos); + if (obj.animationSets.put(key, val) != null) { + throw ProtocolException.duplicateKey("animationSets", key); + } } } + + if ((nullBits[1] & 2) != 0) { + int varPosBase7 = buf.getIntLE(offset + 79); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Attachments", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 99 + varPosBase7; + int attachmentsCount = VarInt.peek(buf, varPos7); + if (attachmentsCount < 0) { + throw ProtocolException.invalidVarInt("Attachments"); + } + + int varIntLen = VarInt.size(attachmentsCount); + if (attachmentsCount > 4096000) { + throw ProtocolException.arrayTooLong("Attachments", attachmentsCount, 4096000); + } + + if (varPos7 + varIntLen + attachmentsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Attachments", varPos7 + varIntLen + attachmentsCount * 1, buf.readableBytes()); + } + + obj.attachments = new ModelAttachment[attachmentsCount]; + int elemPos = varPos7 + varIntLen; + + for (int i = 0; i < attachmentsCount; i++) { + obj.attachments[i] = ModelAttachment.deserialize(buf, elemPos); + elemPos += ModelAttachment.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[1] & 4) != 0) { + int varPosBase8 = buf.getIntLE(offset + 83); + if (varPosBase8 < 0 || varPosBase8 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Particles", varPosBase8, buf.readableBytes()); + } + + int varPos8 = offset + 99 + varPosBase8; + int particlesCount = VarInt.peek(buf, varPos8); + if (particlesCount < 0) { + throw ProtocolException.invalidVarInt("Particles"); + } + + int varIntLenx = VarInt.size(particlesCount); + if (particlesCount > 4096000) { + throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); + } + + if (varPos8 + varIntLenx + particlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Particles", varPos8 + varIntLenx + particlesCount * 34, buf.readableBytes()); + } + + obj.particles = new ModelParticle[particlesCount]; + int elemPos = varPos8 + varIntLenx; + + for (int i = 0; i < particlesCount; i++) { + obj.particles[i] = ModelParticle.deserialize(buf, elemPos); + elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[1] & 8) != 0) { + int varPosBase9 = buf.getIntLE(offset + 87); + if (varPosBase9 < 0 || varPosBase9 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Trails", varPosBase9, buf.readableBytes()); + } + + int varPos9 = offset + 99 + varPosBase9; + int trailsCount = VarInt.peek(buf, varPos9); + if (trailsCount < 0) { + throw ProtocolException.invalidVarInt("Trails"); + } + + int varIntLenxx = VarInt.size(trailsCount); + if (trailsCount > 4096000) { + throw ProtocolException.arrayTooLong("Trails", trailsCount, 4096000); + } + + if (varPos9 + varIntLenxx + trailsCount * 27L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Trails", varPos9 + varIntLenxx + trailsCount * 27, buf.readableBytes()); + } + + obj.trails = new ModelTrail[trailsCount]; + int elemPos = varPos9 + varIntLenxx; + + for (int i = 0; i < trailsCount; i++) { + obj.trails[i] = ModelTrail.deserialize(buf, elemPos); + elemPos += ModelTrail.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[1] & 16) != 0) { + int varPosBase10 = buf.getIntLE(offset + 91); + if (varPosBase10 < 0 || varPosBase10 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("DetailBoxes", varPosBase10, buf.readableBytes()); + } + + int varPos10 = offset + 99 + varPosBase10; + int detailBoxesCount = VarInt.peek(buf, varPos10); + if (detailBoxesCount < 0) { + throw ProtocolException.invalidVarInt("DetailBoxes"); + } + + int varIntLenxxx = VarInt.size(detailBoxesCount); + if (detailBoxesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("DetailBoxes", detailBoxesCount, 4096000); + } + + obj.detailBoxes = new HashMap<>(detailBoxesCount); + int dictPos = varPos10 + varIntLenxxx; + + for (int i = 0; i < detailBoxesCount; i++) { + int keyLenx = VarInt.peek(buf, dictPos); + if (keyLenx < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLenx = VarInt.size(keyLenx); + if (keyLenx > 4096000) { + throw ProtocolException.stringTooLong("key", keyLenx, 4096000); + } + + if (dictPos + keyVarLenx + keyLenx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLenx + keyLenx, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLenx + keyLenx; + int valLen = VarInt.peek(buf, dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 64) { + throw ProtocolException.arrayTooLong("val", valLen, 64); + } + + if (dictPos + valVarLen + valLen * 37L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 37, buf.readableBytes()); + } + + dictPos += valVarLen; + DetailBox[] val = new DetailBox[valLen]; + + for (int valIdx = 0; valIdx < valLen; valIdx++) { + val[valIdx] = DetailBox.deserialize(buf, dictPos); + dictPos += DetailBox.computeBytesConsumed(buf, dictPos); + } + + if (obj.detailBoxes.put(key, val) != null) { + throw ProtocolException.duplicateKey("detailBoxes", key); + } + } + } + + if ((nullBits[1] & 32) != 0) { + int varPosBase11 = buf.getIntLE(offset + 95); + if (varPosBase11 < 0 || varPosBase11 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("PhobiaModel", varPosBase11, buf.readableBytes()); + } + + int varPos11 = offset + 99 + varPosBase11; + obj.phobiaModel = deserialize(buf, varPos11); + } + + return obj; } - - if ((nullBits[1] & 2) != 0) { - int varPos7 = offset + 99 + buf.getIntLE(offset + 79); - int attachmentsCount = VarInt.peek(buf, varPos7); - if (attachmentsCount < 0) { - throw ProtocolException.negativeLength("Attachments", attachmentsCount); - } - - if (attachmentsCount > 4096000) { - throw ProtocolException.arrayTooLong("Attachments", attachmentsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos7); - if (varPos7 + varIntLen + attachmentsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Attachments", varPos7 + varIntLen + attachmentsCount * 1, buf.readableBytes()); - } - - obj.attachments = new ModelAttachment[attachmentsCount]; - int elemPos = varPos7 + varIntLen; - - for (int i = 0; i < attachmentsCount; i++) { - obj.attachments[i] = ModelAttachment.deserialize(buf, elemPos); - elemPos += ModelAttachment.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[1] & 4) != 0) { - int varPos8 = offset + 99 + buf.getIntLE(offset + 83); - int particlesCount = VarInt.peek(buf, varPos8); - if (particlesCount < 0) { - throw ProtocolException.negativeLength("Particles", particlesCount); - } - - if (particlesCount > 4096000) { - throw ProtocolException.arrayTooLong("Particles", particlesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos8); - if (varPos8 + varIntLen + particlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Particles", varPos8 + varIntLen + particlesCount * 34, buf.readableBytes()); - } - - obj.particles = new ModelParticle[particlesCount]; - int elemPos = varPos8 + varIntLen; - - for (int i = 0; i < particlesCount; i++) { - obj.particles[i] = ModelParticle.deserialize(buf, elemPos); - elemPos += ModelParticle.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[1] & 8) != 0) { - int varPos9 = offset + 99 + buf.getIntLE(offset + 87); - int trailsCount = VarInt.peek(buf, varPos9); - if (trailsCount < 0) { - throw ProtocolException.negativeLength("Trails", trailsCount); - } - - if (trailsCount > 4096000) { - throw ProtocolException.arrayTooLong("Trails", trailsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos9); - if (varPos9 + varIntLen + trailsCount * 27L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Trails", varPos9 + varIntLen + trailsCount * 27, buf.readableBytes()); - } - - obj.trails = new ModelTrail[trailsCount]; - int elemPos = varPos9 + varIntLen; - - for (int i = 0; i < trailsCount; i++) { - obj.trails[i] = ModelTrail.deserialize(buf, elemPos); - elemPos += ModelTrail.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[1] & 16) != 0) { - int varPos10 = offset + 99 + buf.getIntLE(offset + 91); - int detailBoxesCount = VarInt.peek(buf, varPos10); - if (detailBoxesCount < 0) { - throw ProtocolException.negativeLength("DetailBoxes", detailBoxesCount); - } - - if (detailBoxesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("DetailBoxes", detailBoxesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos10); - obj.detailBoxes = new HashMap<>(detailBoxesCount); - int dictPos = varPos10 + varIntLen; - - for (int i = 0; i < detailBoxesCount; i++) { - int keyLenx = VarInt.peek(buf, dictPos); - if (keyLenx < 0) { - throw ProtocolException.negativeLength("key", keyLenx); - } - - if (keyLenx > 4096000) { - throw ProtocolException.stringTooLong("key", keyLenx, 4096000); - } - - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLenx; - int valLen = VarInt.peek(buf, dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); - } - - if (valLen > 64) { - throw ProtocolException.arrayTooLong("val", valLen, 64); - } - - int valVarLen = VarInt.length(buf, dictPos); - if (dictPos + valVarLen + valLen * 37L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen * 37, buf.readableBytes()); - } - - dictPos += valVarLen; - DetailBox[] val = new DetailBox[valLen]; - - for (int valIdx = 0; valIdx < valLen; valIdx++) { - val[valIdx] = DetailBox.deserialize(buf, dictPos); - dictPos += DetailBox.computeBytesConsumed(buf, dictPos); - } - - if (obj.detailBoxes.put(key, val) != null) { - throw ProtocolException.duplicateKey("detailBoxes", key); - } - } - } - - if ((nullBits[1] & 32) != 0) { - int varPos11 = offset + 99 + buf.getIntLE(offset + 95); - obj.phobiaModel = deserialize(buf, varPos11); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -398,9 +495,13 @@ public class Model { int maxEnd = 99; if ((nullBits[0] & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 51); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("AssetId", fieldOffset0, maxEnd); + } + int pos0 = offset + 99 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -408,9 +509,13 @@ public class Model { if ((nullBits[0] & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 55); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Path", fieldOffset1, maxEnd); + } + int pos1 = offset + 99 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -418,9 +523,13 @@ public class Model { if ((nullBits[0] & 16) != 0) { int fieldOffset2 = buf.getIntLE(offset + 59); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Texture", fieldOffset2, maxEnd); + } + int pos2 = offset + 99 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -428,9 +537,13 @@ public class Model { if ((nullBits[0] & 32) != 0) { int fieldOffset3 = buf.getIntLE(offset + 63); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("GradientSet", fieldOffset3, maxEnd); + } + int pos3 = offset + 99 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -438,9 +551,13 @@ public class Model { if ((nullBits[0] & 64) != 0) { int fieldOffset4 = buf.getIntLE(offset + 67); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("GradientId", fieldOffset4, maxEnd); + } + int pos4 = offset + 99 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -448,6 +565,10 @@ public class Model { if ((nullBits[0] & 128) != 0) { int fieldOffset5 = buf.getIntLE(offset + 71); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Camera", fieldOffset5, maxEnd); + } + int pos5 = offset + 99 + fieldOffset5; pos5 += CameraSettings.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { @@ -457,13 +578,17 @@ public class Model { if ((nullBits[1] & 1) != 0) { int fieldOffset6 = buf.getIntLE(offset + 75); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("AnimationSets", fieldOffset6, maxEnd); + } + int pos6 = offset + 99 + fieldOffset6; int dictLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6) + sl; + pos6 += VarInt.size(sl) + sl; pos6 += AnimationSet.computeBytesConsumed(buf, pos6); } @@ -474,9 +599,13 @@ public class Model { if ((nullBits[1] & 2) != 0) { int fieldOffset7 = buf.getIntLE(offset + 79); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Attachments", fieldOffset7, maxEnd); + } + int pos7 = offset + 99 + fieldOffset7; int arrLen = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7); + pos7 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos7 += ModelAttachment.computeBytesConsumed(buf, pos7); @@ -489,9 +618,13 @@ public class Model { if ((nullBits[1] & 4) != 0) { int fieldOffset8 = buf.getIntLE(offset + 83); + if (fieldOffset8 < 0 || fieldOffset8 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Particles", fieldOffset8, maxEnd); + } + int pos8 = offset + 99 + fieldOffset8; int arrLen = VarInt.peek(buf, pos8); - pos8 += VarInt.length(buf, pos8); + pos8 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos8 += ModelParticle.computeBytesConsumed(buf, pos8); @@ -504,9 +637,13 @@ public class Model { if ((nullBits[1] & 8) != 0) { int fieldOffset9 = buf.getIntLE(offset + 87); + if (fieldOffset9 < 0 || fieldOffset9 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("Trails", fieldOffset9, maxEnd); + } + int pos9 = offset + 99 + fieldOffset9; int arrLen = VarInt.peek(buf, pos9); - pos9 += VarInt.length(buf, pos9); + pos9 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos9 += ModelTrail.computeBytesConsumed(buf, pos9); @@ -519,15 +656,19 @@ public class Model { if ((nullBits[1] & 16) != 0) { int fieldOffset10 = buf.getIntLE(offset + 91); + if (fieldOffset10 < 0 || fieldOffset10 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("DetailBoxes", fieldOffset10, maxEnd); + } + int pos10 = offset + 99 + fieldOffset10; int dictLen = VarInt.peek(buf, pos10); - pos10 += VarInt.length(buf, pos10); + pos10 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos10); - pos10 += VarInt.length(buf, pos10) + sl; + pos10 += VarInt.size(sl) + sl; sl = VarInt.peek(buf, pos10); - pos10 += VarInt.length(buf, pos10); + pos10 += VarInt.size(sl); for (int j = 0; j < sl; j++) { pos10 += DetailBox.computeBytesConsumed(buf, pos10); @@ -541,6 +682,10 @@ public class Model { if ((nullBits[1] & 32) != 0) { int fieldOffset11 = buf.getIntLE(offset + 95); + if (fieldOffset11 < 0 || fieldOffset11 > buf.writerIndex() - offset - 99) { + throw ProtocolException.invalidOffset("PhobiaModel", fieldOffset11, maxEnd); + } + int pos11 = offset + 99 + fieldOffset11; pos11 += computeBytesConsumed(buf, pos11); if (pos11 - offset > maxEnd) { @@ -873,369 +1018,326 @@ public class Model { return ValidationResult.error("Buffer too small: expected at least 99 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[0] & 4) != 0) { - int assetIdOffset = buffer.getIntLE(offset + 51); - if (assetIdOffset < 0) { - return ValidationResult.error("Invalid offset for AssetId"); - } - - int pos = offset + 99 + assetIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AssetId"); - } - - int assetIdLen = VarInt.peek(buffer, pos); - if (assetIdLen < 0) { - return ValidationResult.error("Invalid string length for AssetId"); - } - - if (assetIdLen > 4096000) { - return ValidationResult.error("AssetId exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += assetIdLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading AssetId"); - } - } - - if ((nullBits[0] & 8) != 0) { - int pathOffset = buffer.getIntLE(offset + 55); - if (pathOffset < 0) { - return ValidationResult.error("Invalid offset for Path"); - } - - int posx = offset + 99 + pathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - - int pathLen = VarInt.peek(buffer, posx); - if (pathLen < 0) { - return ValidationResult.error("Invalid string length for Path"); - } - - if (pathLen > 4096000) { - return ValidationResult.error("Path exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += pathLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Path"); - } - } - - if ((nullBits[0] & 16) != 0) { - int textureOffset = buffer.getIntLE(offset + 59); - if (textureOffset < 0) { - return ValidationResult.error("Invalid offset for Texture"); - } - - int posxx = offset + 99 + textureOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Texture"); - } - - int textureLen = VarInt.peek(buffer, posxx); - if (textureLen < 0) { - return ValidationResult.error("Invalid string length for Texture"); - } - - if (textureLen > 4096000) { - return ValidationResult.error("Texture exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += textureLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Texture"); - } - } - - if ((nullBits[0] & 32) != 0) { - int gradientSetOffset = buffer.getIntLE(offset + 63); - if (gradientSetOffset < 0) { - return ValidationResult.error("Invalid offset for GradientSet"); - } - - int posxxx = offset + 99 + gradientSetOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for GradientSet"); - } - - int gradientSetLen = VarInt.peek(buffer, posxxx); - if (gradientSetLen < 0) { - return ValidationResult.error("Invalid string length for GradientSet"); - } - - if (gradientSetLen > 4096000) { - return ValidationResult.error("GradientSet exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += gradientSetLen; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading GradientSet"); - } - } - - if ((nullBits[0] & 64) != 0) { - int gradientIdOffset = buffer.getIntLE(offset + 67); - if (gradientIdOffset < 0) { - return ValidationResult.error("Invalid offset for GradientId"); - } - - int posxxxx = offset + 99 + gradientIdOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for GradientId"); - } - - int gradientIdLen = VarInt.peek(buffer, posxxxx); - if (gradientIdLen < 0) { - return ValidationResult.error("Invalid string length for GradientId"); - } - - if (gradientIdLen > 4096000) { - return ValidationResult.error("GradientId exceeds max length 4096000"); - } - - posxxxx += VarInt.length(buffer, posxxxx); - posxxxx += gradientIdLen; - if (posxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading GradientId"); - } - } - - if ((nullBits[0] & 128) != 0) { - int cameraOffset = buffer.getIntLE(offset + 71); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxxx = offset + 99 + cameraOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = CameraSettings.validateStructure(buffer, posxxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxxx += CameraSettings.computeBytesConsumed(buffer, posxxxxx); - } - - if ((nullBits[1] & 1) != 0) { - int animationSetsOffset = buffer.getIntLE(offset + 75); - if (animationSetsOffset < 0) { - return ValidationResult.error("Invalid offset for AnimationSets"); - } - - int posxxxxxx = offset + 99 + animationSetsOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AnimationSets"); - } - - int animationSetsCount = VarInt.peek(buffer, posxxxxxx); - if (animationSetsCount < 0) { - return ValidationResult.error("Invalid dictionary count for AnimationSets"); - } - - if (animationSetsCount > 4096000) { - return ValidationResult.error("AnimationSets exceeds max length 4096000"); - } - - posxxxxxx += VarInt.length(buffer, posxxxxxx); - - for (int i = 0; i < animationSetsCount; i++) { - int keyLen = VarInt.peek(buffer, posxxxxxx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 50) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid Phobia value for Phobia"); + } else { + if ((nullBits[0] & 4) != 0) { + v = buffer.getIntLE(offset + 51); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for AssetId"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 99 + v; + int assetIdLen = VarInt.peek(buffer, pos); + if (assetIdLen < 0) { + return ValidationResult.error("Invalid string length for AssetId"); } - posxxxxxx += VarInt.length(buffer, posxxxxxx); - posxxxxxx += keyLen; - if (posxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + if (assetIdLen > 4096000) { + return ValidationResult.error("AssetId exceeds max length 4096000"); } - posxxxxxx += AnimationSet.computeBytesConsumed(buffer, posxxxxxx); + pos += VarInt.size(assetIdLen); + pos += assetIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading AssetId"); + } } + + if ((nullBits[0] & 8) != 0) { + v = buffer.getIntLE(offset + 55); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for Path"); + } + + int posx = offset + 99 + v; + int pathLen = VarInt.peek(buffer, posx); + if (pathLen < 0) { + return ValidationResult.error("Invalid string length for Path"); + } + + if (pathLen > 4096000) { + return ValidationResult.error("Path exceeds max length 4096000"); + } + + posx += VarInt.size(pathLen); + posx += pathLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Path"); + } + } + + if ((nullBits[0] & 16) != 0) { + v = buffer.getIntLE(offset + 59); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for Texture"); + } + + int posxx = offset + 99 + v; + int textureLen = VarInt.peek(buffer, posxx); + if (textureLen < 0) { + return ValidationResult.error("Invalid string length for Texture"); + } + + if (textureLen > 4096000) { + return ValidationResult.error("Texture exceeds max length 4096000"); + } + + posxx += VarInt.size(textureLen); + posxx += textureLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Texture"); + } + } + + if ((nullBits[0] & 32) != 0) { + v = buffer.getIntLE(offset + 63); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for GradientSet"); + } + + int posxxx = offset + 99 + v; + int gradientSetLen = VarInt.peek(buffer, posxxx); + if (gradientSetLen < 0) { + return ValidationResult.error("Invalid string length for GradientSet"); + } + + if (gradientSetLen > 4096000) { + return ValidationResult.error("GradientSet exceeds max length 4096000"); + } + + posxxx += VarInt.size(gradientSetLen); + posxxx += gradientSetLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading GradientSet"); + } + } + + if ((nullBits[0] & 64) != 0) { + v = buffer.getIntLE(offset + 67); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for GradientId"); + } + + int posxxxx = offset + 99 + v; + int gradientIdLen = VarInt.peek(buffer, posxxxx); + if (gradientIdLen < 0) { + return ValidationResult.error("Invalid string length for GradientId"); + } + + if (gradientIdLen > 4096000) { + return ValidationResult.error("GradientId exceeds max length 4096000"); + } + + posxxxx += VarInt.size(gradientIdLen); + posxxxx += gradientIdLen; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading GradientId"); + } + } + + if ((nullBits[0] & 128) != 0) { + v = buffer.getIntLE(offset + 71); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxxxxx = offset + 99 + v; + ValidationResult cameraResult = CameraSettings.validateStructure(buffer, posxxxxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxxxxx += CameraSettings.computeBytesConsumed(buffer, posxxxxx); + } + + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 75); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for AnimationSets"); + } + + int posxxxxx = offset + 99 + v; + int animationSetsCount = VarInt.peek(buffer, posxxxxx); + if (animationSetsCount < 0) { + return ValidationResult.error("Invalid dictionary count for AnimationSets"); + } + + if (animationSetsCount > 4096000) { + return ValidationResult.error("AnimationSets exceeds max length 4096000"); + } + + posxxxxx += VarInt.size(animationSetsCount); + + for (int i = 0; i < animationSetsCount; i++) { + int keyLen = VarInt.peek(buffer, posxxxxx); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxxxx += VarInt.size(keyLen); + posxxxxx += keyLen; + if (posxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxxxxx += AnimationSet.computeBytesConsumed(buffer, posxxxxx); + } + } + + if ((nullBits[1] & 2) != 0) { + v = buffer.getIntLE(offset + 79); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for Attachments"); + } + + int posxxxxxx = offset + 99 + v; + int attachmentsCount = VarInt.peek(buffer, posxxxxxx); + if (attachmentsCount < 0) { + return ValidationResult.error("Invalid array count for Attachments"); + } + + if (attachmentsCount > 4096000) { + return ValidationResult.error("Attachments exceeds max length 4096000"); + } + + posxxxxxx += VarInt.size(attachmentsCount); + + for (int i = 0; i < attachmentsCount; i++) { + ValidationResult structResult = ModelAttachment.validateStructure(buffer, posxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ModelAttachment in Attachments[" + i + "]: " + structResult.error()); + } + + posxxxxxx += ModelAttachment.computeBytesConsumed(buffer, posxxxxxx); + } + } + + if ((nullBits[1] & 4) != 0) { + v = buffer.getIntLE(offset + 83); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for Particles"); + } + + int posxxxxxxx = offset + 99 + v; + int particlesCount = VarInt.peek(buffer, posxxxxxxx); + if (particlesCount < 0) { + return ValidationResult.error("Invalid array count for Particles"); + } + + if (particlesCount > 4096000) { + return ValidationResult.error("Particles exceeds max length 4096000"); + } + + posxxxxxxx += VarInt.size(particlesCount); + + for (int i = 0; i < particlesCount; i++) { + ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); + } + + posxxxxxxx += ModelParticle.computeBytesConsumed(buffer, posxxxxxxx); + } + } + + if ((nullBits[1] & 8) != 0) { + v = buffer.getIntLE(offset + 87); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for Trails"); + } + + int posxxxxxxxx = offset + 99 + v; + int trailsCount = VarInt.peek(buffer, posxxxxxxxx); + if (trailsCount < 0) { + return ValidationResult.error("Invalid array count for Trails"); + } + + if (trailsCount > 4096000) { + return ValidationResult.error("Trails exceeds max length 4096000"); + } + + posxxxxxxxx += VarInt.size(trailsCount); + + for (int i = 0; i < trailsCount; i++) { + ValidationResult structResult = ModelTrail.validateStructure(buffer, posxxxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ModelTrail in Trails[" + i + "]: " + structResult.error()); + } + + posxxxxxxxx += ModelTrail.computeBytesConsumed(buffer, posxxxxxxxx); + } + } + + if ((nullBits[1] & 16) != 0) { + v = buffer.getIntLE(offset + 91); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for DetailBoxes"); + } + + int posxxxxxxxxx = offset + 99 + v; + int detailBoxesCount = VarInt.peek(buffer, posxxxxxxxxx); + if (detailBoxesCount < 0) { + return ValidationResult.error("Invalid dictionary count for DetailBoxes"); + } + + if (detailBoxesCount > 4096000) { + return ValidationResult.error("DetailBoxes exceeds max length 4096000"); + } + + posxxxxxxxxx += VarInt.size(detailBoxesCount); + + for (int i = 0; i < detailBoxesCount; i++) { + int keyLenx = VarInt.peek(buffer, posxxxxxxxxx); + if (keyLenx < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLenx > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posxxxxxxxxx += VarInt.size(keyLenx); + posxxxxxxxxx += keyLenx; + if (posxxxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + int valueArrCount = VarInt.peek(buffer, posxxxxxxxxx); + if (valueArrCount < 0) { + return ValidationResult.error("Invalid array count for value"); + } + + posxxxxxxxxx += VarInt.size(valueArrCount); + + for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { + posxxxxxxxxx += 37; + } + } + } + + if ((nullBits[1] & 32) != 0) { + v = buffer.getIntLE(offset + 95); + if (v < 0 || v > buffer.writerIndex() - offset - 99) { + return ValidationResult.error("Invalid offset for PhobiaModel"); + } + + int posxxxxxxxxxx = offset + 99 + v; + ValidationResult phobiaModelResult = validateStructure(buffer, posxxxxxxxxxx); + if (!phobiaModelResult.isValid()) { + return ValidationResult.error("Invalid PhobiaModel: " + phobiaModelResult.error()); + } + + posxxxxxxxxxx += computeBytesConsumed(buffer, posxxxxxxxxxx); + } + + return ValidationResult.OK; } - - if ((nullBits[1] & 2) != 0) { - int attachmentsOffset = buffer.getIntLE(offset + 79); - if (attachmentsOffset < 0) { - return ValidationResult.error("Invalid offset for Attachments"); - } - - int posxxxxxxx = offset + 99 + attachmentsOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Attachments"); - } - - int attachmentsCount = VarInt.peek(buffer, posxxxxxxx); - if (attachmentsCount < 0) { - return ValidationResult.error("Invalid array count for Attachments"); - } - - if (attachmentsCount > 4096000) { - return ValidationResult.error("Attachments exceeds max length 4096000"); - } - - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); - - for (int i = 0; i < attachmentsCount; i++) { - ValidationResult structResult = ModelAttachment.validateStructure(buffer, posxxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ModelAttachment in Attachments[" + i + "]: " + structResult.error()); - } - - posxxxxxxx += ModelAttachment.computeBytesConsumed(buffer, posxxxxxxx); - } - } - - if ((nullBits[1] & 4) != 0) { - int particlesOffset = buffer.getIntLE(offset + 83); - if (particlesOffset < 0) { - return ValidationResult.error("Invalid offset for Particles"); - } - - int posxxxxxxxx = offset + 99 + particlesOffset; - if (posxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particles"); - } - - int particlesCount = VarInt.peek(buffer, posxxxxxxxx); - if (particlesCount < 0) { - return ValidationResult.error("Invalid array count for Particles"); - } - - if (particlesCount > 4096000) { - return ValidationResult.error("Particles exceeds max length 4096000"); - } - - posxxxxxxxx += VarInt.length(buffer, posxxxxxxxx); - - for (int i = 0; i < particlesCount; i++) { - ValidationResult structResult = ModelParticle.validateStructure(buffer, posxxxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ModelParticle in Particles[" + i + "]: " + structResult.error()); - } - - posxxxxxxxx += ModelParticle.computeBytesConsumed(buffer, posxxxxxxxx); - } - } - - if ((nullBits[1] & 8) != 0) { - int trailsOffset = buffer.getIntLE(offset + 87); - if (trailsOffset < 0) { - return ValidationResult.error("Invalid offset for Trails"); - } - - int posxxxxxxxxx = offset + 99 + trailsOffset; - if (posxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Trails"); - } - - int trailsCount = VarInt.peek(buffer, posxxxxxxxxx); - if (trailsCount < 0) { - return ValidationResult.error("Invalid array count for Trails"); - } - - if (trailsCount > 4096000) { - return ValidationResult.error("Trails exceeds max length 4096000"); - } - - posxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxx); - - for (int i = 0; i < trailsCount; i++) { - ValidationResult structResult = ModelTrail.validateStructure(buffer, posxxxxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ModelTrail in Trails[" + i + "]: " + structResult.error()); - } - - posxxxxxxxxx += ModelTrail.computeBytesConsumed(buffer, posxxxxxxxxx); - } - } - - if ((nullBits[1] & 16) != 0) { - int detailBoxesOffset = buffer.getIntLE(offset + 91); - if (detailBoxesOffset < 0) { - return ValidationResult.error("Invalid offset for DetailBoxes"); - } - - int posxxxxxxxxxx = offset + 99 + detailBoxesOffset; - if (posxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DetailBoxes"); - } - - int detailBoxesCount = VarInt.peek(buffer, posxxxxxxxxxx); - if (detailBoxesCount < 0) { - return ValidationResult.error("Invalid dictionary count for DetailBoxes"); - } - - if (detailBoxesCount > 4096000) { - return ValidationResult.error("DetailBoxes exceeds max length 4096000"); - } - - posxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxx); - - for (int i = 0; i < detailBoxesCount; i++) { - int keyLenx = VarInt.peek(buffer, posxxxxxxxxxx); - if (keyLenx < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLenx > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxx); - posxxxxxxxxxx += keyLenx; - if (posxxxxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - int valueArrCount = VarInt.peek(buffer, posxxxxxxxxxx); - if (valueArrCount < 0) { - return ValidationResult.error("Invalid array count for value"); - } - - posxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxx); - - for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - posxxxxxxxxxx += 37; - } - } - } - - if ((nullBits[1] & 32) != 0) { - int phobiaModelOffset = buffer.getIntLE(offset + 95); - if (phobiaModelOffset < 0) { - return ValidationResult.error("Invalid offset for PhobiaModel"); - } - - int posxxxxxxxxxxx = offset + 99 + phobiaModelOffset; - if (posxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for PhobiaModel"); - } - - ValidationResult phobiaModelResult = validateStructure(buffer, posxxxxxxxxxxx); - if (!phobiaModelResult.isValid()) { - return ValidationResult.error("Invalid PhobiaModel: " + phobiaModelResult.error()); - } - - posxxxxxxxxxxx += computeBytesConsumed(buffer, posxxxxxxxxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ModelAttachment.java b/src/com/hypixel/hytale/protocol/ModelAttachment.java index cf2b6cee..79132805 100644 --- a/src/com/hypixel/hytale/protocol/ModelAttachment.java +++ b/src/com/hypixel/hytale/protocol/ModelAttachment.java @@ -43,65 +43,109 @@ public class ModelAttachment { @Nonnull public static ModelAttachment deserialize(@Nonnull ByteBuf buf, int offset) { - ModelAttachment obj = new ModelAttachment(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 17 + buf.getIntLE(offset + 1); - int modelLen = VarInt.peek(buf, varPos0); - if (modelLen < 0) { - throw ProtocolException.negativeLength("Model", modelLen); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("ModelAttachment", 17, buf.readableBytes() - offset); + } else { + ModelAttachment obj = new ModelAttachment(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Model", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int modelLen = VarInt.peek(buf, varPos0); + if (modelLen < 0) { + throw ProtocolException.invalidVarInt("Model"); + } + + int modelVarIntLen = VarInt.size(modelLen); + if (modelLen > 4096000) { + throw ProtocolException.stringTooLong("Model", modelLen, 4096000); + } + + if (varPos0 + modelVarIntLen + modelLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Model", varPos0 + modelVarIntLen + modelLen, buf.readableBytes()); + } + + obj.model = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (modelLen > 4096000) { - throw ProtocolException.stringTooLong("Model", modelLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Texture", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + int textureLen = VarInt.peek(buf, varPos1); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarIntLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (varPos1 + textureVarIntLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", varPos1 + textureVarIntLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.model = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("GradientSet", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 17 + varPosBase2; + int gradientSetLen = VarInt.peek(buf, varPos2); + if (gradientSetLen < 0) { + throw ProtocolException.invalidVarInt("GradientSet"); + } + + int gradientSetVarIntLen = VarInt.size(gradientSetLen); + if (gradientSetLen > 4096000) { + throw ProtocolException.stringTooLong("GradientSet", gradientSetLen, 4096000); + } + + if (varPos2 + gradientSetVarIntLen + gradientSetLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("GradientSet", varPos2 + gradientSetVarIntLen + gradientSetLen, buf.readableBytes()); + } + + obj.gradientSet = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 13); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("GradientId", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 17 + varPosBase3; + int gradientIdLen = VarInt.peek(buf, varPos3); + if (gradientIdLen < 0) { + throw ProtocolException.invalidVarInt("GradientId"); + } + + int gradientIdVarIntLen = VarInt.size(gradientIdLen); + if (gradientIdLen > 4096000) { + throw ProtocolException.stringTooLong("GradientId", gradientIdLen, 4096000); + } + + if (varPos3 + gradientIdVarIntLen + gradientIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("GradientId", varPos3 + gradientIdVarIntLen + gradientIdLen, buf.readableBytes()); + } + + obj.gradientId = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 17 + buf.getIntLE(offset + 5); - int textureLen = VarInt.peek(buf, varPos1); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); - } - - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } - - obj.texture = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 17 + buf.getIntLE(offset + 9); - int gradientSetLen = VarInt.peek(buf, varPos2); - if (gradientSetLen < 0) { - throw ProtocolException.negativeLength("GradientSet", gradientSetLen); - } - - if (gradientSetLen > 4096000) { - throw ProtocolException.stringTooLong("GradientSet", gradientSetLen, 4096000); - } - - obj.gradientSet = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 17 + buf.getIntLE(offset + 13); - int gradientIdLen = VarInt.peek(buf, varPos3); - if (gradientIdLen < 0) { - throw ProtocolException.negativeLength("GradientId", gradientIdLen); - } - - if (gradientIdLen > 4096000) { - throw ProtocolException.stringTooLong("GradientId", gradientIdLen, 4096000); - } - - obj.gradientId = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -109,9 +153,13 @@ public class ModelAttachment { int maxEnd = 17; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Model", fieldOffset0, maxEnd); + } + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -119,9 +167,13 @@ public class ModelAttachment { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Texture", fieldOffset1, maxEnd); + } + int pos1 = offset + 17 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -129,9 +181,13 @@ public class ModelAttachment { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("GradientSet", fieldOffset2, maxEnd); + } + int pos2 = offset + 17 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -139,9 +195,13 @@ public class ModelAttachment { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 13); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("GradientId", fieldOffset3, maxEnd); + } + int pos3 = offset + 17 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -236,15 +296,11 @@ public class ModelAttachment { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int modelOffset = buffer.getIntLE(offset + 1); - if (modelOffset < 0) { + if (modelOffset < 0 || modelOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Model"); } int pos = offset + 17 + modelOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } - int modelLen = VarInt.peek(buffer, pos); if (modelLen < 0) { return ValidationResult.error("Invalid string length for Model"); @@ -254,7 +310,7 @@ public class ModelAttachment { return ValidationResult.error("Model exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(modelLen); pos += modelLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Model"); @@ -263,15 +319,11 @@ public class ModelAttachment { if ((nullBits & 2) != 0) { int textureOffset = buffer.getIntLE(offset + 5); - if (textureOffset < 0) { + if (textureOffset < 0 || textureOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Texture"); } int posx = offset + 17 + textureOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Texture"); - } - int textureLen = VarInt.peek(buffer, posx); if (textureLen < 0) { return ValidationResult.error("Invalid string length for Texture"); @@ -281,7 +333,7 @@ public class ModelAttachment { return ValidationResult.error("Texture exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(textureLen); posx += textureLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Texture"); @@ -290,15 +342,11 @@ public class ModelAttachment { if ((nullBits & 4) != 0) { int gradientSetOffset = buffer.getIntLE(offset + 9); - if (gradientSetOffset < 0) { + if (gradientSetOffset < 0 || gradientSetOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for GradientSet"); } int posxx = offset + 17 + gradientSetOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for GradientSet"); - } - int gradientSetLen = VarInt.peek(buffer, posxx); if (gradientSetLen < 0) { return ValidationResult.error("Invalid string length for GradientSet"); @@ -308,7 +356,7 @@ public class ModelAttachment { return ValidationResult.error("GradientSet exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(gradientSetLen); posxx += gradientSetLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading GradientSet"); @@ -317,15 +365,11 @@ public class ModelAttachment { if ((nullBits & 8) != 0) { int gradientIdOffset = buffer.getIntLE(offset + 13); - if (gradientIdOffset < 0) { + if (gradientIdOffset < 0 || gradientIdOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for GradientId"); } int posxxx = offset + 17 + gradientIdOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for GradientId"); - } - int gradientIdLen = VarInt.peek(buffer, posxxx); if (gradientIdLen < 0) { return ValidationResult.error("Invalid string length for GradientId"); @@ -335,7 +379,7 @@ public class ModelAttachment { return ValidationResult.error("GradientId exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(gradientIdLen); posxxx += gradientIdLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading GradientId"); diff --git a/src/com/hypixel/hytale/protocol/ModelDisplay.java b/src/com/hypixel/hytale/protocol/ModelDisplay.java index 0cfa81b8..9a7617b1 100644 --- a/src/com/hypixel/hytale/protocol/ModelDisplay.java +++ b/src/com/hypixel/hytale/protocol/ModelDisplay.java @@ -8,6 +8,7 @@ import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class ModelDisplay { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -20,16 +21,18 @@ public class ModelDisplay { @Nullable public String attachTo; @Nullable - public Vector3f translation; + public Vector3fc translation; @Nullable - public Vector3f rotation; + public Vector3fc rotation; @Nullable - public Vector3f scale; + public Vector3fc scale; public ModelDisplay() { } - public ModelDisplay(@Nullable String node, @Nullable String attachTo, @Nullable Vector3f translation, @Nullable Vector3f rotation, @Nullable Vector3f scale) { + public ModelDisplay( + @Nullable String node, @Nullable String attachTo, @Nullable Vector3fc translation, @Nullable Vector3fc rotation, @Nullable Vector3fc scale + ) { this.node = node; this.attachTo = attachTo; this.translation = translation; @@ -47,49 +50,73 @@ public class ModelDisplay { @Nonnull public static ModelDisplay deserialize(@Nonnull ByteBuf buf, int offset) { - ModelDisplay obj = new ModelDisplay(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.translation = Vector3f.deserialize(buf, offset + 1); - } - - if ((nullBits & 2) != 0) { - obj.rotation = Vector3f.deserialize(buf, offset + 13); - } - - if ((nullBits & 4) != 0) { - obj.scale = Vector3f.deserialize(buf, offset + 25); - } - - if ((nullBits & 8) != 0) { - int varPos0 = offset + 45 + buf.getIntLE(offset + 37); - int nodeLen = VarInt.peek(buf, varPos0); - if (nodeLen < 0) { - throw ProtocolException.negativeLength("Node", nodeLen); + if (buf.readableBytes() - offset < 45) { + throw ProtocolException.bufferTooSmall("ModelDisplay", 45, buf.readableBytes() - offset); + } else { + ModelDisplay obj = new ModelDisplay(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.translation = PacketIO.readVector3f(buf, offset + 1); } - if (nodeLen > 4096000) { - throw ProtocolException.stringTooLong("Node", nodeLen, 4096000); + if ((nullBits & 2) != 0) { + obj.rotation = PacketIO.readVector3f(buf, offset + 13); } - obj.node = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + obj.scale = PacketIO.readVector3f(buf, offset + 25); + } + + if ((nullBits & 8) != 0) { + int varPosBase0 = buf.getIntLE(offset + 37); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Node", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 45 + varPosBase0; + int nodeLen = VarInt.peek(buf, varPos0); + if (nodeLen < 0) { + throw ProtocolException.invalidVarInt("Node"); + } + + int nodeVarIntLen = VarInt.size(nodeLen); + if (nodeLen > 4096000) { + throw ProtocolException.stringTooLong("Node", nodeLen, 4096000); + } + + if (varPos0 + nodeVarIntLen + nodeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Node", varPos0 + nodeVarIntLen + nodeLen, buf.readableBytes()); + } + + obj.node = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase1 = buf.getIntLE(offset + 41); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("AttachTo", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 45 + varPosBase1; + int attachToLen = VarInt.peek(buf, varPos1); + if (attachToLen < 0) { + throw ProtocolException.invalidVarInt("AttachTo"); + } + + int attachToVarIntLen = VarInt.size(attachToLen); + if (attachToLen > 4096000) { + throw ProtocolException.stringTooLong("AttachTo", attachToLen, 4096000); + } + + if (varPos1 + attachToVarIntLen + attachToLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AttachTo", varPos1 + attachToVarIntLen + attachToLen, buf.readableBytes()); + } + + obj.attachTo = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 16) != 0) { - int varPos1 = offset + 45 + buf.getIntLE(offset + 41); - int attachToLen = VarInt.peek(buf, varPos1); - if (attachToLen < 0) { - throw ProtocolException.negativeLength("AttachTo", attachToLen); - } - - if (attachToLen > 4096000) { - throw ProtocolException.stringTooLong("AttachTo", attachToLen, 4096000); - } - - obj.attachTo = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -97,9 +124,13 @@ public class ModelDisplay { int maxEnd = 45; if ((nullBits & 8) != 0) { int fieldOffset0 = buf.getIntLE(offset + 37); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Node", fieldOffset0, maxEnd); + } + int pos0 = offset + 45 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -107,9 +138,13 @@ public class ModelDisplay { if ((nullBits & 16) != 0) { int fieldOffset1 = buf.getIntLE(offset + 41); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("AttachTo", fieldOffset1, maxEnd); + } + int pos1 = offset + 45 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -143,19 +178,19 @@ public class ModelDisplay { buf.writeByte(nullBits); if (this.translation != null) { - this.translation.serialize(buf); + PacketIO.writeVector3f(buf, this.translation); } else { buf.writeZero(12); } if (this.rotation != null) { - this.rotation.serialize(buf); + PacketIO.writeVector3f(buf, this.rotation); } else { buf.writeZero(12); } if (this.scale != null) { - this.scale.serialize(buf); + PacketIO.writeVector3f(buf, this.scale); } else { buf.writeZero(12); } @@ -200,15 +235,11 @@ public class ModelDisplay { byte nullBits = buffer.getByte(offset); if ((nullBits & 8) != 0) { int nodeOffset = buffer.getIntLE(offset + 37); - if (nodeOffset < 0) { + if (nodeOffset < 0 || nodeOffset > buffer.writerIndex() - offset - 45) { return ValidationResult.error("Invalid offset for Node"); } int pos = offset + 45 + nodeOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Node"); - } - int nodeLen = VarInt.peek(buffer, pos); if (nodeLen < 0) { return ValidationResult.error("Invalid string length for Node"); @@ -218,7 +249,7 @@ public class ModelDisplay { return ValidationResult.error("Node exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nodeLen); pos += nodeLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Node"); @@ -227,15 +258,11 @@ public class ModelDisplay { if ((nullBits & 16) != 0) { int attachToOffset = buffer.getIntLE(offset + 41); - if (attachToOffset < 0) { + if (attachToOffset < 0 || attachToOffset > buffer.writerIndex() - offset - 45) { return ValidationResult.error("Invalid offset for AttachTo"); } int posx = offset + 45 + attachToOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AttachTo"); - } - int attachToLen = VarInt.peek(buffer, posx); if (attachToLen < 0) { return ValidationResult.error("Invalid string length for AttachTo"); @@ -245,7 +272,7 @@ public class ModelDisplay { return ValidationResult.error("AttachTo exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(attachToLen); posx += attachToLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading AttachTo"); @@ -260,9 +287,9 @@ public class ModelDisplay { ModelDisplay copy = new ModelDisplay(); copy.node = this.node; copy.attachTo = this.attachTo; - copy.translation = this.translation != null ? this.translation.clone() : null; - copy.rotation = this.rotation != null ? this.rotation.clone() : null; - copy.scale = this.scale != null ? this.scale.clone() : null; + copy.translation = this.translation; + copy.rotation = this.rotation; + copy.scale = this.scale; return copy; } diff --git a/src/com/hypixel/hytale/protocol/ModelOverride.java b/src/com/hypixel/hytale/protocol/ModelOverride.java index 63588071..dbb33225 100644 --- a/src/com/hypixel/hytale/protocol/ModelOverride.java +++ b/src/com/hypixel/hytale/protocol/ModelOverride.java @@ -42,73 +42,106 @@ public class ModelOverride { @Nonnull public static ModelOverride deserialize(@Nonnull ByteBuf buf, int offset) { - ModelOverride obj = new ModelOverride(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int modelLen = VarInt.peek(buf, varPos0); - if (modelLen < 0) { - throw ProtocolException.negativeLength("Model", modelLen); - } - - if (modelLen > 4096000) { - throw ProtocolException.stringTooLong("Model", modelLen, 4096000); - } - - obj.model = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int textureLen = VarInt.peek(buf, varPos1); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); - } - - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } - - obj.texture = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int animationSetsCount = VarInt.peek(buf, varPos2); - if (animationSetsCount < 0) { - throw ProtocolException.negativeLength("AnimationSets", animationSetsCount); - } - - if (animationSetsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("AnimationSets", animationSetsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - obj.animationSets = new HashMap<>(animationSetsCount); - int dictPos = varPos2 + varIntLen; - - for (int i = 0; i < animationSetsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("ModelOverride", 13, buf.readableBytes() - offset); + } else { + ModelOverride obj = new ModelOverride(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Model", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 13 + varPosBase0; + int modelLen = VarInt.peek(buf, varPos0); + if (modelLen < 0) { + throw ProtocolException.invalidVarInt("Model"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - AnimationSet val = AnimationSet.deserialize(buf, dictPos); - dictPos += AnimationSet.computeBytesConsumed(buf, dictPos); - if (obj.animationSets.put(key, val) != null) { - throw ProtocolException.duplicateKey("animationSets", key); + int modelVarIntLen = VarInt.size(modelLen); + if (modelLen > 4096000) { + throw ProtocolException.stringTooLong("Model", modelLen, 4096000); + } + + if (varPos0 + modelVarIntLen + modelLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Model", varPos0 + modelVarIntLen + modelLen, buf.readableBytes()); + } + + obj.model = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Texture", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int textureLen = VarInt.peek(buf, varPos1); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarIntLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (varPos1 + textureVarIntLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", varPos1 + textureVarIntLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("AnimationSets", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int animationSetsCount = VarInt.peek(buf, varPos2); + if (animationSetsCount < 0) { + throw ProtocolException.invalidVarInt("AnimationSets"); + } + + int varIntLen = VarInt.size(animationSetsCount); + if (animationSetsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("AnimationSets", animationSetsCount, 4096000); + } + + obj.animationSets = new HashMap<>(animationSetsCount); + int dictPos = varPos2 + varIntLen; + + for (int i = 0; i < animationSetsCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + AnimationSet val = AnimationSet.deserialize(buf, dictPos); + dictPos += AnimationSet.computeBytesConsumed(buf, dictPos); + if (obj.animationSets.put(key, val) != null) { + throw ProtocolException.duplicateKey("animationSets", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -116,9 +149,13 @@ public class ModelOverride { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Model", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -126,9 +163,13 @@ public class ModelOverride { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Texture", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -136,13 +177,17 @@ public class ModelOverride { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("AnimationSets", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int dictLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; pos2 += AnimationSet.computeBytesConsumed(buf, pos2); } @@ -238,15 +283,11 @@ public class ModelOverride { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int modelOffset = buffer.getIntLE(offset + 1); - if (modelOffset < 0) { + if (modelOffset < 0 || modelOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Model"); } int pos = offset + 13 + modelOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } - int modelLen = VarInt.peek(buffer, pos); if (modelLen < 0) { return ValidationResult.error("Invalid string length for Model"); @@ -256,7 +297,7 @@ public class ModelOverride { return ValidationResult.error("Model exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(modelLen); pos += modelLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Model"); @@ -265,15 +306,11 @@ public class ModelOverride { if ((nullBits & 2) != 0) { int textureOffset = buffer.getIntLE(offset + 5); - if (textureOffset < 0) { + if (textureOffset < 0 || textureOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Texture"); } int posx = offset + 13 + textureOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Texture"); - } - int textureLen = VarInt.peek(buffer, posx); if (textureLen < 0) { return ValidationResult.error("Invalid string length for Texture"); @@ -283,7 +320,7 @@ public class ModelOverride { return ValidationResult.error("Texture exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(textureLen); posx += textureLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Texture"); @@ -292,15 +329,11 @@ public class ModelOverride { if ((nullBits & 4) != 0) { int animationSetsOffset = buffer.getIntLE(offset + 9); - if (animationSetsOffset < 0) { + if (animationSetsOffset < 0 || animationSetsOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for AnimationSets"); } int posxx = offset + 13 + animationSetsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AnimationSets"); - } - int animationSetsCount = VarInt.peek(buffer, posxx); if (animationSetsCount < 0) { return ValidationResult.error("Invalid dictionary count for AnimationSets"); @@ -310,7 +343,7 @@ public class ModelOverride { return ValidationResult.error("AnimationSets exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(animationSetsCount); for (int i = 0; i < animationSetsCount; i++) { int keyLen = VarInt.peek(buffer, posxx); @@ -322,7 +355,7 @@ public class ModelOverride { return ValidationResult.error("key exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(keyLen); posxx += keyLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading key"); diff --git a/src/com/hypixel/hytale/protocol/ModelParticle.java b/src/com/hypixel/hytale/protocol/ModelParticle.java index 150c0f78..246830c8 100644 --- a/src/com/hypixel/hytale/protocol/ModelParticle.java +++ b/src/com/hypixel/hytale/protocol/ModelParticle.java @@ -8,6 +8,7 @@ import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class ModelParticle { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -25,7 +26,7 @@ public class ModelParticle { @Nullable public String targetNodeName; @Nullable - public Vector3f positionOffset; + public Vector3fc positionOffset; @Nullable public Direction rotationOffset; public boolean detachedFromModel; @@ -39,7 +40,7 @@ public class ModelParticle { @Nullable Color color, @Nonnull EntityPart targetEntityPart, @Nullable String targetNodeName, - @Nullable Vector3f positionOffset, + @Nullable Vector3fc positionOffset, @Nullable Direction rotationOffset, boolean detachedFromModel ) { @@ -66,52 +67,76 @@ public class ModelParticle { @Nonnull public static ModelParticle deserialize(@Nonnull ByteBuf buf, int offset) { - ModelParticle obj = new ModelParticle(); - byte nullBits = buf.getByte(offset); - obj.scale = buf.getFloatLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.color = Color.deserialize(buf, offset + 5); - } - - obj.targetEntityPart = EntityPart.fromValue(buf.getByte(offset + 8)); - if ((nullBits & 2) != 0) { - obj.positionOffset = Vector3f.deserialize(buf, offset + 9); - } - - if ((nullBits & 4) != 0) { - obj.rotationOffset = Direction.deserialize(buf, offset + 21); - } - - obj.detachedFromModel = buf.getByte(offset + 33) != 0; - if ((nullBits & 8) != 0) { - int varPos0 = offset + 42 + buf.getIntLE(offset + 34); - int systemIdLen = VarInt.peek(buf, varPos0); - if (systemIdLen < 0) { - throw ProtocolException.negativeLength("SystemId", systemIdLen); + if (buf.readableBytes() - offset < 42) { + throw ProtocolException.bufferTooSmall("ModelParticle", 42, buf.readableBytes() - offset); + } else { + ModelParticle obj = new ModelParticle(); + byte nullBits = buf.getByte(offset); + obj.scale = buf.getFloatLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.color = Color.deserialize(buf, offset + 5); } - if (systemIdLen > 4096000) { - throw ProtocolException.stringTooLong("SystemId", systemIdLen, 4096000); + obj.targetEntityPart = EntityPart.fromValue(buf.getByte(offset + 8)); + if ((nullBits & 2) != 0) { + obj.positionOffset = PacketIO.readVector3f(buf, offset + 9); } - obj.systemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + obj.rotationOffset = Direction.deserialize(buf, offset + 21); + } + + obj.detachedFromModel = buf.getByte(offset + 33) != 0; + if ((nullBits & 8) != 0) { + int varPosBase0 = buf.getIntLE(offset + 34); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("SystemId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 42 + varPosBase0; + int systemIdLen = VarInt.peek(buf, varPos0); + if (systemIdLen < 0) { + throw ProtocolException.invalidVarInt("SystemId"); + } + + int systemIdVarIntLen = VarInt.size(systemIdLen); + if (systemIdLen > 4096000) { + throw ProtocolException.stringTooLong("SystemId", systemIdLen, 4096000); + } + + if (varPos0 + systemIdVarIntLen + systemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SystemId", varPos0 + systemIdVarIntLen + systemIdLen, buf.readableBytes()); + } + + obj.systemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase1 = buf.getIntLE(offset + 38); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("TargetNodeName", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 42 + varPosBase1; + int targetNodeNameLen = VarInt.peek(buf, varPos1); + if (targetNodeNameLen < 0) { + throw ProtocolException.invalidVarInt("TargetNodeName"); + } + + int targetNodeNameVarIntLen = VarInt.size(targetNodeNameLen); + if (targetNodeNameLen > 4096000) { + throw ProtocolException.stringTooLong("TargetNodeName", targetNodeNameLen, 4096000); + } + + if (varPos1 + targetNodeNameVarIntLen + targetNodeNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TargetNodeName", varPos1 + targetNodeNameVarIntLen + targetNodeNameLen, buf.readableBytes()); + } + + obj.targetNodeName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 16) != 0) { - int varPos1 = offset + 42 + buf.getIntLE(offset + 38); - int targetNodeNameLen = VarInt.peek(buf, varPos1); - if (targetNodeNameLen < 0) { - throw ProtocolException.negativeLength("TargetNodeName", targetNodeNameLen); - } - - if (targetNodeNameLen > 4096000) { - throw ProtocolException.stringTooLong("TargetNodeName", targetNodeNameLen, 4096000); - } - - obj.targetNodeName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -119,9 +144,13 @@ public class ModelParticle { int maxEnd = 42; if ((nullBits & 8) != 0) { int fieldOffset0 = buf.getIntLE(offset + 34); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("SystemId", fieldOffset0, maxEnd); + } + int pos0 = offset + 42 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -129,9 +158,13 @@ public class ModelParticle { if ((nullBits & 16) != 0) { int fieldOffset1 = buf.getIntLE(offset + 38); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("TargetNodeName", fieldOffset1, maxEnd); + } + int pos1 = offset + 42 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -173,7 +206,7 @@ public class ModelParticle { buf.writeByte(this.targetEntityPart.getValue()); if (this.positionOffset != null) { - this.positionOffset.serialize(buf); + PacketIO.writeVector3f(buf, this.positionOffset); } else { buf.writeZero(12); } @@ -223,61 +256,58 @@ public class ModelParticle { return ValidationResult.error("Buffer too small: expected at least 42 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 8) != 0) { - int systemIdOffset = buffer.getIntLE(offset + 34); - if (systemIdOffset < 0) { - return ValidationResult.error("Invalid offset for SystemId"); + int v = buffer.getByte(offset + 8) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid EntityPart value for TargetEntityPart"); + } else { + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 34); + if (v < 0 || v > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for SystemId"); + } + + int pos = offset + 42 + v; + int systemIdLen = VarInt.peek(buffer, pos); + if (systemIdLen < 0) { + return ValidationResult.error("Invalid string length for SystemId"); + } + + if (systemIdLen > 4096000) { + return ValidationResult.error("SystemId exceeds max length 4096000"); + } + + pos += VarInt.size(systemIdLen); + pos += systemIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading SystemId"); + } } - int pos = offset + 42 + systemIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SystemId"); + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 38); + if (v < 0 || v > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for TargetNodeName"); + } + + int posx = offset + 42 + v; + int targetNodeNameLen = VarInt.peek(buffer, posx); + if (targetNodeNameLen < 0) { + return ValidationResult.error("Invalid string length for TargetNodeName"); + } + + if (targetNodeNameLen > 4096000) { + return ValidationResult.error("TargetNodeName exceeds max length 4096000"); + } + + posx += VarInt.size(targetNodeNameLen); + posx += targetNodeNameLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TargetNodeName"); + } } - int systemIdLen = VarInt.peek(buffer, pos); - if (systemIdLen < 0) { - return ValidationResult.error("Invalid string length for SystemId"); - } - - if (systemIdLen > 4096000) { - return ValidationResult.error("SystemId exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += systemIdLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading SystemId"); - } + return ValidationResult.OK; } - - if ((nullBits & 16) != 0) { - int targetNodeNameOffset = buffer.getIntLE(offset + 38); - if (targetNodeNameOffset < 0) { - return ValidationResult.error("Invalid offset for TargetNodeName"); - } - - int posx = offset + 42 + targetNodeNameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TargetNodeName"); - } - - int targetNodeNameLen = VarInt.peek(buffer, posx); - if (targetNodeNameLen < 0) { - return ValidationResult.error("Invalid string length for TargetNodeName"); - } - - if (targetNodeNameLen > 4096000) { - return ValidationResult.error("TargetNodeName exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += targetNodeNameLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading TargetNodeName"); - } - } - - return ValidationResult.OK; } } @@ -288,7 +318,7 @@ public class ModelParticle { copy.color = this.color != null ? this.color.clone() : null; copy.targetEntityPart = this.targetEntityPart; copy.targetNodeName = this.targetNodeName; - copy.positionOffset = this.positionOffset != null ? this.positionOffset.clone() : null; + copy.positionOffset = this.positionOffset; copy.rotationOffset = this.rotationOffset != null ? this.rotationOffset.clone() : null; copy.detachedFromModel = this.detachedFromModel; return copy; diff --git a/src/com/hypixel/hytale/protocol/ModelTexture.java b/src/com/hypixel/hytale/protocol/ModelTexture.java index 58578b3d..b64e3b75 100644 --- a/src/com/hypixel/hytale/protocol/ModelTexture.java +++ b/src/com/hypixel/hytale/protocol/ModelTexture.java @@ -34,26 +34,34 @@ public class ModelTexture { @Nonnull public static ModelTexture deserialize(@Nonnull ByteBuf buf, int offset) { - ModelTexture obj = new ModelTexture(); - byte nullBits = buf.getByte(offset); - obj.weight = buf.getFloatLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int textureLen = VarInt.peek(buf, pos); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("ModelTexture", 5, buf.readableBytes() - offset); + } else { + ModelTexture obj = new ModelTexture(); + byte nullBits = buf.getByte(offset); + obj.weight = buf.getFloatLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int textureLen = VarInt.peek(buf, pos); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (pos + textureVarLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", pos + textureVarLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += textureVarLen + textureLen; } - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } - - int textureVarLen = VarInt.length(buf, pos); - obj.texture = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += textureVarLen + textureLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,7 +69,7 @@ public class ModelTexture { int pos = offset + 5; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -105,7 +113,7 @@ public class ModelTexture { return ValidationResult.error("Texture exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(textureLen); pos += textureLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Texture"); diff --git a/src/com/hypixel/hytale/protocol/ModelTrail.java b/src/com/hypixel/hytale/protocol/ModelTrail.java index 5cc86eed..a7dd8911 100644 --- a/src/com/hypixel/hytale/protocol/ModelTrail.java +++ b/src/com/hypixel/hytale/protocol/ModelTrail.java @@ -8,6 +8,7 @@ import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class ModelTrail { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -22,7 +23,7 @@ public class ModelTrail { @Nullable public String targetNodeName; @Nullable - public Vector3f positionOffset; + public Vector3fc positionOffset; @Nullable public Direction rotationOffset; public boolean fixedRotation; @@ -34,7 +35,7 @@ public class ModelTrail { @Nullable String trailId, @Nonnull EntityPart targetEntityPart, @Nullable String targetNodeName, - @Nullable Vector3f positionOffset, + @Nullable Vector3fc positionOffset, @Nullable Direction rotationOffset, boolean fixedRotation ) { @@ -57,47 +58,71 @@ public class ModelTrail { @Nonnull public static ModelTrail deserialize(@Nonnull ByteBuf buf, int offset) { - ModelTrail obj = new ModelTrail(); - byte nullBits = buf.getByte(offset); - obj.targetEntityPart = EntityPart.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - obj.positionOffset = Vector3f.deserialize(buf, offset + 2); - } - - if ((nullBits & 2) != 0) { - obj.rotationOffset = Direction.deserialize(buf, offset + 14); - } - - obj.fixedRotation = buf.getByte(offset + 26) != 0; - if ((nullBits & 4) != 0) { - int varPos0 = offset + 35 + buf.getIntLE(offset + 27); - int trailIdLen = VarInt.peek(buf, varPos0); - if (trailIdLen < 0) { - throw ProtocolException.negativeLength("TrailId", trailIdLen); + if (buf.readableBytes() - offset < 35) { + throw ProtocolException.bufferTooSmall("ModelTrail", 35, buf.readableBytes() - offset); + } else { + ModelTrail obj = new ModelTrail(); + byte nullBits = buf.getByte(offset); + obj.targetEntityPart = EntityPart.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + obj.positionOffset = PacketIO.readVector3f(buf, offset + 2); } - if (trailIdLen > 4096000) { - throw ProtocolException.stringTooLong("TrailId", trailIdLen, 4096000); + if ((nullBits & 2) != 0) { + obj.rotationOffset = Direction.deserialize(buf, offset + 14); } - obj.trailId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + obj.fixedRotation = buf.getByte(offset + 26) != 0; + if ((nullBits & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 27); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("TrailId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 35 + varPosBase0; + int trailIdLen = VarInt.peek(buf, varPos0); + if (trailIdLen < 0) { + throw ProtocolException.invalidVarInt("TrailId"); + } + + int trailIdVarIntLen = VarInt.size(trailIdLen); + if (trailIdLen > 4096000) { + throw ProtocolException.stringTooLong("TrailId", trailIdLen, 4096000); + } + + if (varPos0 + trailIdVarIntLen + trailIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TrailId", varPos0 + trailIdVarIntLen + trailIdLen, buf.readableBytes()); + } + + obj.trailId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 31); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("TargetNodeName", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 35 + varPosBase1; + int targetNodeNameLen = VarInt.peek(buf, varPos1); + if (targetNodeNameLen < 0) { + throw ProtocolException.invalidVarInt("TargetNodeName"); + } + + int targetNodeNameVarIntLen = VarInt.size(targetNodeNameLen); + if (targetNodeNameLen > 4096000) { + throw ProtocolException.stringTooLong("TargetNodeName", targetNodeNameLen, 4096000); + } + + if (varPos1 + targetNodeNameVarIntLen + targetNodeNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TargetNodeName", varPos1 + targetNodeNameVarIntLen + targetNodeNameLen, buf.readableBytes()); + } + + obj.targetNodeName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 8) != 0) { - int varPos1 = offset + 35 + buf.getIntLE(offset + 31); - int targetNodeNameLen = VarInt.peek(buf, varPos1); - if (targetNodeNameLen < 0) { - throw ProtocolException.negativeLength("TargetNodeName", targetNodeNameLen); - } - - if (targetNodeNameLen > 4096000) { - throw ProtocolException.stringTooLong("TargetNodeName", targetNodeNameLen, 4096000); - } - - obj.targetNodeName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -105,9 +130,13 @@ public class ModelTrail { int maxEnd = 35; if ((nullBits & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 27); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("TrailId", fieldOffset0, maxEnd); + } + int pos0 = offset + 35 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -115,9 +144,13 @@ public class ModelTrail { if ((nullBits & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 31); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("TargetNodeName", fieldOffset1, maxEnd); + } + int pos1 = offset + 35 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -148,7 +181,7 @@ public class ModelTrail { buf.writeByte(nullBits); buf.writeByte(this.targetEntityPart.getValue()); if (this.positionOffset != null) { - this.positionOffset.serialize(buf); + PacketIO.writeVector3f(buf, this.positionOffset); } else { buf.writeZero(12); } @@ -198,61 +231,58 @@ public class ModelTrail { return ValidationResult.error("Buffer too small: expected at least 35 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 4) != 0) { - int trailIdOffset = buffer.getIntLE(offset + 27); - if (trailIdOffset < 0) { - return ValidationResult.error("Invalid offset for TrailId"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid EntityPart value for TargetEntityPart"); + } else { + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for TrailId"); + } + + int pos = offset + 35 + v; + int trailIdLen = VarInt.peek(buffer, pos); + if (trailIdLen < 0) { + return ValidationResult.error("Invalid string length for TrailId"); + } + + if (trailIdLen > 4096000) { + return ValidationResult.error("TrailId exceeds max length 4096000"); + } + + pos += VarInt.size(trailIdLen); + pos += trailIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TrailId"); + } } - int pos = offset + 35 + trailIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TrailId"); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for TargetNodeName"); + } + + int posx = offset + 35 + v; + int targetNodeNameLen = VarInt.peek(buffer, posx); + if (targetNodeNameLen < 0) { + return ValidationResult.error("Invalid string length for TargetNodeName"); + } + + if (targetNodeNameLen > 4096000) { + return ValidationResult.error("TargetNodeName exceeds max length 4096000"); + } + + posx += VarInt.size(targetNodeNameLen); + posx += targetNodeNameLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TargetNodeName"); + } } - int trailIdLen = VarInt.peek(buffer, pos); - if (trailIdLen < 0) { - return ValidationResult.error("Invalid string length for TrailId"); - } - - if (trailIdLen > 4096000) { - return ValidationResult.error("TrailId exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += trailIdLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading TrailId"); - } + return ValidationResult.OK; } - - if ((nullBits & 8) != 0) { - int targetNodeNameOffset = buffer.getIntLE(offset + 31); - if (targetNodeNameOffset < 0) { - return ValidationResult.error("Invalid offset for TargetNodeName"); - } - - int posx = offset + 35 + targetNodeNameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TargetNodeName"); - } - - int targetNodeNameLen = VarInt.peek(buffer, posx); - if (targetNodeNameLen < 0) { - return ValidationResult.error("Invalid string length for TargetNodeName"); - } - - if (targetNodeNameLen > 4096000) { - return ValidationResult.error("TargetNodeName exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += targetNodeNameLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading TargetNodeName"); - } - } - - return ValidationResult.OK; } } @@ -261,7 +291,7 @@ public class ModelTrail { copy.trailId = this.trailId; copy.targetEntityPart = this.targetEntityPart; copy.targetNodeName = this.targetNodeName; - copy.positionOffset = this.positionOffset != null ? this.positionOffset.clone() : null; + copy.positionOffset = this.positionOffset; copy.rotationOffset = this.rotationOffset != null ? this.rotationOffset.clone() : null; copy.fixedRotation = this.fixedRotation; return copy; diff --git a/src/com/hypixel/hytale/protocol/ModelTransform.java b/src/com/hypixel/hytale/protocol/ModelTransform.java index 191dbc62..1ada72ce 100644 --- a/src/com/hypixel/hytale/protocol/ModelTransform.java +++ b/src/com/hypixel/hytale/protocol/ModelTransform.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -36,21 +37,25 @@ public class ModelTransform { @Nonnull public static ModelTransform deserialize(@Nonnull ByteBuf buf, int offset) { - ModelTransform obj = new ModelTransform(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.position = Position.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 49) { + throw ProtocolException.bufferTooSmall("ModelTransform", 49, buf.readableBytes() - offset); + } else { + ModelTransform obj = new ModelTransform(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.position = Position.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.bodyOrientation = Direction.deserialize(buf, offset + 25); - } + if ((nullBits & 2) != 0) { + obj.bodyOrientation = Direction.deserialize(buf, offset + 25); + } - if ((nullBits & 4) != 0) { - obj.lookOrientation = Direction.deserialize(buf, offset + 37); - } + if ((nullBits & 4) != 0) { + obj.lookOrientation = Direction.deserialize(buf, offset + 37); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -96,7 +101,12 @@ public class ModelTransform { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 49 ? ValidationResult.error("Buffer too small: expected at least 49 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 49) { + return ValidationResult.error("Buffer too small: expected at least 49 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public ModelTransform clone() { diff --git a/src/com/hypixel/hytale/protocol/ModelUpdate.java b/src/com/hypixel/hytale/protocol/ModelUpdate.java index c8b67a39..27f0953a 100644 --- a/src/com/hypixel/hytale/protocol/ModelUpdate.java +++ b/src/com/hypixel/hytale/protocol/ModelUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -31,16 +32,20 @@ public class ModelUpdate extends ComponentUpdate { @Nonnull public static ModelUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - ModelUpdate obj = new ModelUpdate(); - byte nullBits = buf.getByte(offset); - obj.entityScale = buf.getFloatLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.model = Model.deserialize(buf, pos); - pos += Model.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("ModelUpdate", 5, buf.readableBytes() - offset); + } else { + ModelUpdate obj = new ModelUpdate(); + byte nullBits = buf.getByte(offset); + obj.entityScale = buf.getFloatLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.model = Model.deserialize(buf, pos); + pos += Model.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ModelVFX.java b/src/com/hypixel/hytale/protocol/ModelVFX.java index a734cda8..1cec8fd8 100644 --- a/src/com/hypixel/hytale/protocol/ModelVFX.java +++ b/src/com/hypixel/hytale/protocol/ModelVFX.java @@ -8,6 +8,7 @@ import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2fc; public class ModelVFX { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -23,7 +24,7 @@ public class ModelVFX { public EffectDirection effectDirection = EffectDirection.None; public float animationDuration; @Nullable - public Vector2f animationRange; + public Vector2fc animationRange; @Nonnull public LoopOption loopOption = LoopOption.PlayOnce; @Nonnull @@ -34,9 +35,9 @@ public class ModelVFX { public boolean useBloomOnHighlight; public boolean useProgessiveHighlight; @Nullable - public Vector2f noiseScale; + public Vector2fc noiseScale; @Nullable - public Vector2f noiseScrollSpeed; + public Vector2fc noiseScrollSpeed; @Nullable public Color postColor; public float postColorOpacity; @@ -49,15 +50,15 @@ public class ModelVFX { @Nonnull SwitchTo switchTo, @Nonnull EffectDirection effectDirection, float animationDuration, - @Nullable Vector2f animationRange, + @Nullable Vector2fc animationRange, @Nonnull LoopOption loopOption, @Nonnull CurveType curveType, @Nullable Color highlightColor, float highlightThickness, boolean useBloomOnHighlight, boolean useProgessiveHighlight, - @Nullable Vector2f noiseScale, - @Nullable Vector2f noiseScrollSpeed, + @Nullable Vector2fc noiseScale, + @Nullable Vector2fc noiseScrollSpeed, @Nullable Color postColor, float postColorOpacity ) { @@ -98,54 +99,62 @@ public class ModelVFX { @Nonnull public static ModelVFX deserialize(@Nonnull ByteBuf buf, int offset) { - ModelVFX obj = new ModelVFX(); - byte nullBits = buf.getByte(offset); - obj.switchTo = SwitchTo.fromValue(buf.getByte(offset + 1)); - obj.effectDirection = EffectDirection.fromValue(buf.getByte(offset + 2)); - obj.animationDuration = buf.getFloatLE(offset + 3); - if ((nullBits & 1) != 0) { - obj.animationRange = Vector2f.deserialize(buf, offset + 7); - } - - obj.loopOption = LoopOption.fromValue(buf.getByte(offset + 15)); - obj.curveType = CurveType.fromValue(buf.getByte(offset + 16)); - if ((nullBits & 2) != 0) { - obj.highlightColor = Color.deserialize(buf, offset + 17); - } - - obj.highlightThickness = buf.getFloatLE(offset + 20); - obj.useBloomOnHighlight = buf.getByte(offset + 24) != 0; - obj.useProgessiveHighlight = buf.getByte(offset + 25) != 0; - if ((nullBits & 4) != 0) { - obj.noiseScale = Vector2f.deserialize(buf, offset + 26); - } - - if ((nullBits & 8) != 0) { - obj.noiseScrollSpeed = Vector2f.deserialize(buf, offset + 34); - } - - if ((nullBits & 16) != 0) { - obj.postColor = Color.deserialize(buf, offset + 42); - } - - obj.postColorOpacity = buf.getFloatLE(offset + 45); - int pos = offset + 49; - if ((nullBits & 32) != 0) { - int idLen = VarInt.peek(buf, pos); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 49) { + throw ProtocolException.bufferTooSmall("ModelVFX", 49, buf.readableBytes() - offset); + } else { + ModelVFX obj = new ModelVFX(); + byte nullBits = buf.getByte(offset); + obj.switchTo = SwitchTo.fromValue(buf.getByte(offset + 1)); + obj.effectDirection = EffectDirection.fromValue(buf.getByte(offset + 2)); + obj.animationDuration = buf.getFloatLE(offset + 3); + if ((nullBits & 1) != 0) { + obj.animationRange = PacketIO.readVector2f(buf, offset + 7); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + obj.loopOption = LoopOption.fromValue(buf.getByte(offset + 15)); + obj.curveType = CurveType.fromValue(buf.getByte(offset + 16)); + if ((nullBits & 2) != 0) { + obj.highlightColor = Color.deserialize(buf, offset + 17); } - int idVarLen = VarInt.length(buf, pos); - obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += idVarLen + idLen; - } + obj.highlightThickness = buf.getFloatLE(offset + 20); + obj.useBloomOnHighlight = buf.getByte(offset + 24) != 0; + obj.useProgessiveHighlight = buf.getByte(offset + 25) != 0; + if ((nullBits & 4) != 0) { + obj.noiseScale = PacketIO.readVector2f(buf, offset + 26); + } - return obj; + if ((nullBits & 8) != 0) { + obj.noiseScrollSpeed = PacketIO.readVector2f(buf, offset + 34); + } + + if ((nullBits & 16) != 0) { + obj.postColor = Color.deserialize(buf, offset + 42); + } + + obj.postColorOpacity = buf.getFloatLE(offset + 45); + int pos = offset + 49; + if ((nullBits & 32) != 0) { + int idLen = VarInt.peek(buf, pos); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (pos + idVarLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", pos + idVarLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += idVarLen + idLen; + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -153,7 +162,7 @@ public class ModelVFX { int pos = offset + 49; if ((nullBits & 32) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -190,7 +199,7 @@ public class ModelVFX { buf.writeByte(this.effectDirection.getValue()); buf.writeFloatLE(this.animationDuration); if (this.animationRange != null) { - this.animationRange.serialize(buf); + PacketIO.writeVector2f(buf, this.animationRange); } else { buf.writeZero(8); } @@ -207,13 +216,13 @@ public class ModelVFX { buf.writeByte(this.useBloomOnHighlight ? 1 : 0); buf.writeByte(this.useProgessiveHighlight ? 1 : 0); if (this.noiseScale != null) { - this.noiseScale.serialize(buf); + PacketIO.writeVector2f(buf, this.noiseScale); } else { buf.writeZero(8); } if (this.noiseScrollSpeed != null) { - this.noiseScrollSpeed.serialize(buf); + PacketIO.writeVector2f(buf, this.noiseScrollSpeed); } else { buf.writeZero(8); } @@ -244,25 +253,45 @@ public class ModelVFX { return ValidationResult.error("Buffer too small: expected at least 49 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 49; - if ((nullBits & 32) != 0) { - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid SwitchTo value for SwitchTo"); + } else { + v = buffer.getByte(offset + 2) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid EffectDirection value for EffectDirection"); + } else { + v = buffer.getByte(offset + 15) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid LoopOption value for LoopOption"); + } else { + v = buffer.getByte(offset + 16) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid CurveType value for CurveType"); + } else { + v = offset + 49; + if ((nullBits & 32) != 0) { + int idLen = VarInt.peek(buffer, v); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); + v += VarInt.size(idLen); + v += idLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } + } + + return ValidationResult.OK; + } + } } } - - return ValidationResult.OK; } } @@ -272,15 +301,15 @@ public class ModelVFX { copy.switchTo = this.switchTo; copy.effectDirection = this.effectDirection; copy.animationDuration = this.animationDuration; - copy.animationRange = this.animationRange != null ? this.animationRange.clone() : null; + copy.animationRange = this.animationRange; copy.loopOption = this.loopOption; copy.curveType = this.curveType; copy.highlightColor = this.highlightColor != null ? this.highlightColor.clone() : null; copy.highlightThickness = this.highlightThickness; copy.useBloomOnHighlight = this.useBloomOnHighlight; copy.useProgessiveHighlight = this.useProgessiveHighlight; - copy.noiseScale = this.noiseScale != null ? this.noiseScale.clone() : null; - copy.noiseScrollSpeed = this.noiseScrollSpeed != null ? this.noiseScrollSpeed.clone() : null; + copy.noiseScale = this.noiseScale; + copy.noiseScrollSpeed = this.noiseScrollSpeed; copy.postColor = this.postColor != null ? this.postColor.clone() : null; copy.postColorOpacity = this.postColorOpacity; return copy; diff --git a/src/com/hypixel/hytale/protocol/Modifier.java b/src/com/hypixel/hytale/protocol/Modifier.java index 81a9bd4d..d4b19ae3 100644 --- a/src/com/hypixel/hytale/protocol/Modifier.java +++ b/src/com/hypixel/hytale/protocol/Modifier.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -34,11 +35,15 @@ public class Modifier { @Nonnull public static Modifier deserialize(@Nonnull ByteBuf buf, int offset) { - Modifier obj = new Modifier(); - obj.target = ModifierTarget.fromValue(buf.getByte(offset + 0)); - obj.calculationType = CalculationType.fromValue(buf.getByte(offset + 1)); - obj.amount = buf.getFloatLE(offset + 2); - return obj; + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("Modifier", 6, buf.readableBytes() - offset); + } else { + Modifier obj = new Modifier(); + obj.target = ModifierTarget.fromValue(buf.getByte(offset + 0)); + obj.calculationType = CalculationType.fromValue(buf.getByte(offset + 1)); + obj.amount = buf.getFloatLE(offset + 2); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -56,7 +61,17 @@ public class Modifier { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 6 ? ValidationResult.error("Buffer too small: expected at least 6 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 6) { + return ValidationResult.error("Buffer too small: expected at least 6 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ModifierTarget value for Target"); + } else { + v = buffer.getByte(offset + 1) & 255; + return v >= 2 ? ValidationResult.error("Invalid CalculationType value for CalculationType") : ValidationResult.OK; + } + } } public Modifier clone() { diff --git a/src/com/hypixel/hytale/protocol/ModifyInventoryInteraction.java b/src/com/hypixel/hytale/protocol/ModifyInventoryInteraction.java index ebc122a9..94bc20e3 100644 --- a/src/com/hypixel/hytale/protocol/ModifyInventoryInteraction.java +++ b/src/com/hypixel/hytale/protocol/ModifyInventoryInteraction.java @@ -93,108 +93,157 @@ public class ModifyInventoryInteraction extends SimpleInteraction { @Nonnull public static ModifyInventoryInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ModifyInventoryInteraction obj = new ModifyInventoryInteraction(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 2)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 3); - obj.runTime = buf.getFloatLE(offset + 7); - obj.cancelOnItemChange = buf.getByte(offset + 11) != 0; - obj.next = buf.getIntLE(offset + 12); - obj.failed = buf.getIntLE(offset + 16); - if ((nullBits[0] & 1) != 0) { - obj.requiredGameMode = GameMode.fromValue(buf.getByte(offset + 20)); - } - - obj.adjustHeldItemQuantity = buf.getIntLE(offset + 21); - obj.adjustHeldItemDurability = buf.getDoubleLE(offset + 25); - if ((nullBits[0] & 2) != 0) { - int varPos0 = offset + 65 + buf.getIntLE(offset + 33); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } - - if ((nullBits[0] & 4) != 0) { - int varPos1 = offset + 65 + buf.getIntLE(offset + 37); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + if (buf.readableBytes() - offset < 65) { + throw ProtocolException.bufferTooSmall("ModifyInventoryInteraction", 65, buf.readableBytes() - offset); + } else { + ModifyInventoryInteraction obj = new ModifyInventoryInteraction(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 2)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 3); + obj.runTime = buf.getFloatLE(offset + 7); + obj.cancelOnItemChange = buf.getByte(offset + 11) != 0; + obj.next = buf.getIntLE(offset + 12); + obj.failed = buf.getIntLE(offset + 16); + if ((nullBits[0] & 1) != 0) { + obj.requiredGameMode = GameMode.fromValue(buf.getByte(offset + 20)); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + obj.adjustHeldItemQuantity = buf.getIntLE(offset + 21); + obj.adjustHeldItemDurability = buf.getDoubleLE(offset + 25); + if ((nullBits[0] & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 33); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 65 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + if ((nullBits[0] & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 37); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varPos1 = offset + 65 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } + + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits[0] & 8) != 0) { - int varPos2 = offset + 65 + buf.getIntLE(offset + 41); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits[0] & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 41); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits[0] & 16) != 0) { - int varPos3 = offset + 65 + buf.getIntLE(offset + 45); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 65 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits[0] & 16) != 0) { + int varPosBase3 = buf.getIntLE(offset + 45); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 65 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits[0] & 32) != 0) { + int varPosBase4 = buf.getIntLE(offset + 49); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 65 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; + if ((nullBits[0] & 64) != 0) { + int varPosBase5 = buf.getIntLE(offset + 53); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("ItemToRemove", varPosBase5, buf.readableBytes()); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits[0] & 32) != 0) { - int varPos4 = offset + 65 + buf.getIntLE(offset + 49); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits[0] & 64) != 0) { - int varPos5 = offset + 65 + buf.getIntLE(offset + 53); - obj.itemToRemove = ItemWithAllMetadata.deserialize(buf, varPos5); - } - - if ((nullBits[0] & 128) != 0) { - int varPos6 = offset + 65 + buf.getIntLE(offset + 57); - obj.itemToAdd = ItemWithAllMetadata.deserialize(buf, varPos6); - } - - if ((nullBits[1] & 1) != 0) { - int varPos7 = offset + 65 + buf.getIntLE(offset + 61); - int brokenItemLen = VarInt.peek(buf, varPos7); - if (brokenItemLen < 0) { - throw ProtocolException.negativeLength("BrokenItem", brokenItemLen); + int varPos5 = offset + 65 + varPosBase5; + obj.itemToRemove = ItemWithAllMetadata.deserialize(buf, varPos5); } - if (brokenItemLen > 4096000) { - throw ProtocolException.stringTooLong("BrokenItem", brokenItemLen, 4096000); + if ((nullBits[0] & 128) != 0) { + int varPosBase6 = buf.getIntLE(offset + 57); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("ItemToAdd", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 65 + varPosBase6; + obj.itemToAdd = ItemWithAllMetadata.deserialize(buf, varPos6); } - obj.brokenItem = PacketIO.readVarString(buf, varPos7, PacketIO.UTF8); - } + if ((nullBits[1] & 1) != 0) { + int varPosBase7 = buf.getIntLE(offset + 61); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("BrokenItem", varPosBase7, buf.readableBytes()); + } - return obj; + int varPos7 = offset + 65 + varPosBase7; + int brokenItemLen = VarInt.peek(buf, varPos7); + if (brokenItemLen < 0) { + throw ProtocolException.invalidVarInt("BrokenItem"); + } + + int brokenItemVarIntLen = VarInt.size(brokenItemLen); + if (brokenItemLen > 4096000) { + throw ProtocolException.stringTooLong("BrokenItem", brokenItemLen, 4096000); + } + + if (varPos7 + brokenItemVarIntLen + brokenItemLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BrokenItem", varPos7 + brokenItemVarIntLen + brokenItemLen, buf.readableBytes()); + } + + obj.brokenItem = PacketIO.readVarString(buf, varPos7, PacketIO.UTF8); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -202,6 +251,10 @@ public class ModifyInventoryInteraction extends SimpleInteraction { int maxEnd = 65; if ((nullBits[0] & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 33); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 65 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -211,9 +264,13 @@ public class ModifyInventoryInteraction extends SimpleInteraction { if ((nullBits[0] & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 37); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 65 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -226,6 +283,10 @@ public class ModifyInventoryInteraction extends SimpleInteraction { if ((nullBits[0] & 8) != 0) { int fieldOffset2 = buf.getIntLE(offset + 41); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 65 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -235,9 +296,13 @@ public class ModifyInventoryInteraction extends SimpleInteraction { if ((nullBits[0] & 16) != 0) { int fieldOffset3 = buf.getIntLE(offset + 45); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 65 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -245,6 +310,10 @@ public class ModifyInventoryInteraction extends SimpleInteraction { if ((nullBits[0] & 32) != 0) { int fieldOffset4 = buf.getIntLE(offset + 49); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 65 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -254,6 +323,10 @@ public class ModifyInventoryInteraction extends SimpleInteraction { if ((nullBits[0] & 64) != 0) { int fieldOffset5 = buf.getIntLE(offset + 53); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("ItemToRemove", fieldOffset5, maxEnd); + } + int pos5 = offset + 65 + fieldOffset5; pos5 += ItemWithAllMetadata.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { @@ -263,6 +336,10 @@ public class ModifyInventoryInteraction extends SimpleInteraction { if ((nullBits[0] & 128) != 0) { int fieldOffset6 = buf.getIntLE(offset + 57); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("ItemToAdd", fieldOffset6, maxEnd); + } + int pos6 = offset + 65 + fieldOffset6; pos6 += ItemWithAllMetadata.computeBytesConsumed(buf, pos6); if (pos6 - offset > maxEnd) { @@ -272,9 +349,13 @@ public class ModifyInventoryInteraction extends SimpleInteraction { if ((nullBits[1] & 1) != 0) { int fieldOffset7 = buf.getIntLE(offset + 61); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 65) { + throw ProtocolException.invalidOffset("BrokenItem", fieldOffset7, maxEnd); + } + int pos7 = offset + 65 + fieldOffset7; int sl = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7) + sl; + pos7 += VarInt.size(sl) + sl; if (pos7 - offset > maxEnd) { maxEnd = pos7 - offset; } @@ -474,184 +555,169 @@ public class ModifyInventoryInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 65 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[0] & 2) != 0) { - int effectsOffset = buffer.getIntLE(offset + 33); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 2) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits[0] & 1) != 0) { + v = buffer.getByte(offset + 20) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid GameMode value for RequiredGameMode"); + } } - int pos = offset + 65 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits[0] & 2) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 65) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 65 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits[0] & 4) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 65) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 65 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits[0] & 8) != 0) { + v = buffer.getIntLE(offset + 41); + if (v < 0 || v > buffer.writerIndex() - offset - 65) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 65 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits[0] & 16) != 0) { + v = buffer.getIntLE(offset + 45); + if (v < 0 || v > buffer.writerIndex() - offset - 65) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 65 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits[0] & 32) != 0) { + v = buffer.getIntLE(offset + 49); + if (v < 0 || v > buffer.writerIndex() - offset - 65) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 65 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits[0] & 64) != 0) { + v = buffer.getIntLE(offset + 53); + if (v < 0 || v > buffer.writerIndex() - offset - 65) { + return ValidationResult.error("Invalid offset for ItemToRemove"); + } + + int posxx = offset + 65 + v; + ValidationResult itemToRemoveResult = ItemWithAllMetadata.validateStructure(buffer, posxx); + if (!itemToRemoveResult.isValid()) { + return ValidationResult.error("Invalid ItemToRemove: " + itemToRemoveResult.error()); + } + + posxx += ItemWithAllMetadata.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits[0] & 128) != 0) { + v = buffer.getIntLE(offset + 57); + if (v < 0 || v > buffer.writerIndex() - offset - 65) { + return ValidationResult.error("Invalid offset for ItemToAdd"); + } + + int posxx = offset + 65 + v; + ValidationResult itemToAddResult = ItemWithAllMetadata.validateStructure(buffer, posxx); + if (!itemToAddResult.isValid()) { + return ValidationResult.error("Invalid ItemToAdd: " + itemToAddResult.error()); + } + + posxx += ItemWithAllMetadata.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 61); + if (v < 0 || v > buffer.writerIndex() - offset - 65) { + return ValidationResult.error("Invalid offset for BrokenItem"); + } + + int posxx = offset + 65 + v; + int brokenItemLen = VarInt.peek(buffer, posxx); + if (brokenItemLen < 0) { + return ValidationResult.error("Invalid string length for BrokenItem"); + } + + if (brokenItemLen > 4096000) { + return ValidationResult.error("BrokenItem exceeds max length 4096000"); + } + + posxx += VarInt.size(brokenItemLen); + posxx += brokenItemLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading BrokenItem"); + } + } + + return ValidationResult.OK; } - - if ((nullBits[0] & 4) != 0) { - int settingsOffset = buffer.getIntLE(offset + 37); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 65 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits[0] & 8) != 0) { - int rulesOffset = buffer.getIntLE(offset + 41); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 65 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits[0] & 16) != 0) { - int tagsOffset = buffer.getIntLE(offset + 45); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 65 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits[0] & 32) != 0) { - int cameraOffset = buffer.getIntLE(offset + 49); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 65 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits[0] & 64) != 0) { - int itemToRemoveOffset = buffer.getIntLE(offset + 53); - if (itemToRemoveOffset < 0) { - return ValidationResult.error("Invalid offset for ItemToRemove"); - } - - int posxxxxx = offset + 65 + itemToRemoveOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemToRemove"); - } - - ValidationResult itemToRemoveResult = ItemWithAllMetadata.validateStructure(buffer, posxxxxx); - if (!itemToRemoveResult.isValid()) { - return ValidationResult.error("Invalid ItemToRemove: " + itemToRemoveResult.error()); - } - - posxxxxx += ItemWithAllMetadata.computeBytesConsumed(buffer, posxxxxx); - } - - if ((nullBits[0] & 128) != 0) { - int itemToAddOffset = buffer.getIntLE(offset + 57); - if (itemToAddOffset < 0) { - return ValidationResult.error("Invalid offset for ItemToAdd"); - } - - int posxxxxxx = offset + 65 + itemToAddOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemToAdd"); - } - - ValidationResult itemToAddResult = ItemWithAllMetadata.validateStructure(buffer, posxxxxxx); - if (!itemToAddResult.isValid()) { - return ValidationResult.error("Invalid ItemToAdd: " + itemToAddResult.error()); - } - - posxxxxxx += ItemWithAllMetadata.computeBytesConsumed(buffer, posxxxxxx); - } - - if ((nullBits[1] & 1) != 0) { - int brokenItemOffset = buffer.getIntLE(offset + 61); - if (brokenItemOffset < 0) { - return ValidationResult.error("Invalid offset for BrokenItem"); - } - - int posxxxxxxx = offset + 65 + brokenItemOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BrokenItem"); - } - - int brokenItemLen = VarInt.peek(buffer, posxxxxxxx); - if (brokenItemLen < 0) { - return ValidationResult.error("Invalid string length for BrokenItem"); - } - - if (brokenItemLen > 4096000) { - return ValidationResult.error("BrokenItem exceeds max length 4096000"); - } - - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); - posxxxxxxx += brokenItemLen; - if (posxxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading BrokenItem"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/MountedUpdate.java b/src/com/hypixel/hytale/protocol/MountedUpdate.java index 22332a03..b456823c 100644 --- a/src/com/hypixel/hytale/protocol/MountedUpdate.java +++ b/src/com/hypixel/hytale/protocol/MountedUpdate.java @@ -1,20 +1,23 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class MountedUpdate extends ComponentUpdate { public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 48; + public static final int FIXED_BLOCK_SIZE = 47; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 48; - public static final int MAX_SIZE = 48; + public static final int VARIABLE_BLOCK_START = 47; + public static final int MAX_SIZE = 47; public int mountedToEntity; - @Nullable - public Vector3f attachmentOffset; + @Nonnull + public Vector3fc attachmentOffset = PacketIO.ZERO_VECTOR3; @Nonnull public MountController controller = MountController.Minecart; @Nullable @@ -23,7 +26,7 @@ public class MountedUpdate extends ComponentUpdate { public MountedUpdate() { } - public MountedUpdate(int mountedToEntity, @Nullable Vector3f attachmentOffset, @Nonnull MountController controller, @Nullable BlockMount block) { + public MountedUpdate(int mountedToEntity, @Nonnull Vector3fc attachmentOffset, @Nonnull MountController controller, @Nullable BlockMount block) { this.mountedToEntity = mountedToEntity; this.attachmentOffset = attachmentOffset; this.controller = controller; @@ -39,50 +42,42 @@ public class MountedUpdate extends ComponentUpdate { @Nonnull public static MountedUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - MountedUpdate obj = new MountedUpdate(); - byte nullBits = buf.getByte(offset); - obj.mountedToEntity = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.attachmentOffset = Vector3f.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 47) { + throw ProtocolException.bufferTooSmall("MountedUpdate", 47, buf.readableBytes() - offset); + } else { + MountedUpdate obj = new MountedUpdate(); + byte nullBits = buf.getByte(offset); + obj.mountedToEntity = buf.getIntLE(offset + 1); + obj.attachmentOffset = PacketIO.readVector3f(buf, offset + 5); + obj.controller = MountController.fromValue(buf.getByte(offset + 17)); + if ((nullBits & 1) != 0) { + obj.block = BlockMount.deserialize(buf, offset + 18); + } - obj.controller = MountController.fromValue(buf.getByte(offset + 17)); - if ((nullBits & 2) != 0) { - obj.block = BlockMount.deserialize(buf, offset + 18); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 48; + return 47; } @Override public int serialize(@Nonnull ByteBuf buf) { int startPos = buf.writerIndex(); byte nullBits = 0; - if (this.attachmentOffset != null) { - nullBits = (byte)(nullBits | 1); - } - if (this.block != null) { - nullBits = (byte)(nullBits | 2); + nullBits = (byte)(nullBits | 1); } buf.writeByte(nullBits); buf.writeIntLE(this.mountedToEntity); - if (this.attachmentOffset != null) { - this.attachmentOffset.serialize(buf); - } else { - buf.writeZero(12); - } - + PacketIO.writeVector3f(buf, this.attachmentOffset); buf.writeByte(this.controller.getValue()); if (this.block != null) { this.block.serialize(buf); } else { - buf.writeZero(30); + buf.writeZero(29); } return buf.writerIndex() - startPos; @@ -90,17 +85,23 @@ public class MountedUpdate extends ComponentUpdate { @Override public int computeSize() { - return 48; + return 47; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 48 ? ValidationResult.error("Buffer too small: expected at least 48 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 47) { + return ValidationResult.error("Buffer too small: expected at least 47 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 17) & 255; + return v >= 2 ? ValidationResult.error("Invalid MountController value for Controller") : ValidationResult.OK; + } } public MountedUpdate clone() { MountedUpdate copy = new MountedUpdate(); copy.mountedToEntity = this.mountedToEntity; - copy.attachmentOffset = this.attachmentOffset != null ? this.attachmentOffset.clone() : null; + copy.attachmentOffset = this.attachmentOffset; copy.controller = this.controller; copy.block = this.block != null ? this.block.clone() : null; return copy; diff --git a/src/com/hypixel/hytale/protocol/MouseButtonEvent.java b/src/com/hypixel/hytale/protocol/MouseButtonEvent.java index 55e85fc1..f29e5228 100644 --- a/src/com/hypixel/hytale/protocol/MouseButtonEvent.java +++ b/src/com/hypixel/hytale/protocol/MouseButtonEvent.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -34,11 +35,15 @@ public class MouseButtonEvent { @Nonnull public static MouseButtonEvent deserialize(@Nonnull ByteBuf buf, int offset) { - MouseButtonEvent obj = new MouseButtonEvent(); - obj.mouseButtonType = MouseButtonType.fromValue(buf.getByte(offset + 0)); - obj.state = MouseButtonState.fromValue(buf.getByte(offset + 1)); - obj.clicks = buf.getByte(offset + 2); - return obj; + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("MouseButtonEvent", 3, buf.readableBytes() - offset); + } else { + MouseButtonEvent obj = new MouseButtonEvent(); + obj.mouseButtonType = MouseButtonType.fromValue(buf.getByte(offset + 0)); + obj.state = MouseButtonState.fromValue(buf.getByte(offset + 1)); + obj.clicks = buf.getByte(offset + 2); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -56,7 +61,17 @@ public class MouseButtonEvent { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 3 ? ValidationResult.error("Buffer too small: expected at least 3 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 3) { + return ValidationResult.error("Buffer too small: expected at least 3 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid MouseButtonType value for MouseButtonType"); + } else { + v = buffer.getByte(offset + 1) & 255; + return v >= 2 ? ValidationResult.error("Invalid MouseButtonState value for State") : ValidationResult.OK; + } + } } public MouseButtonEvent clone() { diff --git a/src/com/hypixel/hytale/protocol/MouseMotionEvent.java b/src/com/hypixel/hytale/protocol/MouseMotionEvent.java index b1ab3379..8cea3f1f 100644 --- a/src/com/hypixel/hytale/protocol/MouseMotionEvent.java +++ b/src/com/hypixel/hytale/protocol/MouseMotionEvent.java @@ -35,38 +35,42 @@ public class MouseMotionEvent { @Nonnull public static MouseMotionEvent deserialize(@Nonnull ByteBuf buf, int offset) { - MouseMotionEvent obj = new MouseMotionEvent(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.relativeMotion = Vector2i.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("MouseMotionEvent", 9, buf.readableBytes() - offset); + } else { + MouseMotionEvent obj = new MouseMotionEvent(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.relativeMotion = Vector2i.deserialize(buf, offset + 1); + } + + int pos = offset + 9; + if ((nullBits & 2) != 0) { + int mouseButtonTypeCount = VarInt.peek(buf, pos); + if (mouseButtonTypeCount < 0) { + throw ProtocolException.invalidVarInt("MouseButtonType"); + } + + int mouseButtonTypeVarLen = VarInt.size(mouseButtonTypeCount); + if (mouseButtonTypeCount > 4096000) { + throw ProtocolException.arrayTooLong("MouseButtonType", mouseButtonTypeCount, 4096000); + } + + if (pos + mouseButtonTypeVarLen + mouseButtonTypeCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("MouseButtonType", pos + mouseButtonTypeVarLen + mouseButtonTypeCount * 1, buf.readableBytes()); + } + + pos += mouseButtonTypeVarLen; + obj.mouseButtonType = new MouseButtonType[mouseButtonTypeCount]; + + for (int i = 0; i < mouseButtonTypeCount; i++) { + obj.mouseButtonType[i] = MouseButtonType.fromValue(buf.getByte(pos)); + pos++; + } + } + + return obj; } - - int pos = offset + 9; - if ((nullBits & 2) != 0) { - int mouseButtonTypeCount = VarInt.peek(buf, pos); - if (mouseButtonTypeCount < 0) { - throw ProtocolException.negativeLength("MouseButtonType", mouseButtonTypeCount); - } - - if (mouseButtonTypeCount > 4096000) { - throw ProtocolException.arrayTooLong("MouseButtonType", mouseButtonTypeCount, 4096000); - } - - int mouseButtonTypeVarLen = VarInt.size(mouseButtonTypeCount); - if (pos + mouseButtonTypeVarLen + mouseButtonTypeCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("MouseButtonType", pos + mouseButtonTypeVarLen + mouseButtonTypeCount * 1, buf.readableBytes()); - } - - pos += mouseButtonTypeVarLen; - obj.mouseButtonType = new MouseButtonType[mouseButtonTypeCount]; - - for (int i = 0; i < mouseButtonTypeCount; i++) { - obj.mouseButtonType[i] = MouseButtonType.fromValue(buf.getByte(pos)); - pos++; - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -74,7 +78,7 @@ public class MouseMotionEvent { int pos = offset + 9; if ((nullBits & 2) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -135,11 +139,19 @@ public class MouseMotionEvent { return ValidationResult.error("MouseButtonType exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += mouseButtonTypeCount * 1; - if (pos > buffer.writerIndex()) { + pos += VarInt.size(mouseButtonTypeCount); + if (pos + mouseButtonTypeCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading MouseButtonType"); } + + for (int i = 0; i < mouseButtonTypeCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid MouseButtonType value for MouseButtonType[i]"); + } + + pos++; + } } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/MovementConditionInteraction.java b/src/com/hypixel/hytale/protocol/MovementConditionInteraction.java index c4a2d2ea..b4fd8444 100644 --- a/src/com/hypixel/hytale/protocol/MovementConditionInteraction.java +++ b/src/com/hypixel/hytale/protocol/MovementConditionInteraction.java @@ -96,86 +96,115 @@ public class MovementConditionInteraction extends SimpleInteraction { @Nonnull public static MovementConditionInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - MovementConditionInteraction obj = new MovementConditionInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.forward = buf.getIntLE(offset + 19); - obj.back = buf.getIntLE(offset + 23); - obj.left = buf.getIntLE(offset + 27); - obj.right = buf.getIntLE(offset + 31); - obj.forwardLeft = buf.getIntLE(offset + 35); - obj.forwardRight = buf.getIntLE(offset + 39); - obj.backLeft = buf.getIntLE(offset + 43); - obj.backRight = buf.getIntLE(offset + 47); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 71 + buf.getIntLE(offset + 51); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 71) { + throw ProtocolException.bufferTooSmall("MovementConditionInteraction", 71, buf.readableBytes() - offset); + } else { + MovementConditionInteraction obj = new MovementConditionInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.forward = buf.getIntLE(offset + 19); + obj.back = buf.getIntLE(offset + 23); + obj.left = buf.getIntLE(offset + 27); + obj.right = buf.getIntLE(offset + 31); + obj.forwardLeft = buf.getIntLE(offset + 35); + obj.forwardRight = buf.getIntLE(offset + 39); + obj.backLeft = buf.getIntLE(offset + 43); + obj.backRight = buf.getIntLE(offset + 47); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 51); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 71 + buf.getIntLE(offset + 55); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 71 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 55); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 71 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 71 + buf.getIntLE(offset + 59); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 59); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 71 + buf.getIntLE(offset + 63); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 71 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 63); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 71 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 67); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 71 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 71 + buf.getIntLE(offset + 67); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -183,6 +212,10 @@ public class MovementConditionInteraction extends SimpleInteraction { int maxEnd = 71; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 51); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 71 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -192,9 +225,13 @@ public class MovementConditionInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 55); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 71 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -207,6 +244,10 @@ public class MovementConditionInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 59); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 71 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -216,9 +257,13 @@ public class MovementConditionInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 63); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 71 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -226,6 +271,10 @@ public class MovementConditionInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 67); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 71) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 71 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -372,119 +421,109 @@ public class MovementConditionInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 71 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 51); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 51); + if (v < 0 || v > buffer.writerIndex() - offset - 71) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 71 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 71 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 55); + if (v < 0 || v > buffer.writerIndex() - offset - 71) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 71 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 59); + if (v < 0 || v > buffer.writerIndex() - offset - 71) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 71 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 63); + if (v < 0 || v > buffer.writerIndex() - offset - 71) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 71 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 67); + if (v < 0 || v > buffer.writerIndex() - offset - 71) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 71 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 55); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 71 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 59); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 71 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 63); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 71 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 67); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 71 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/MovementEffects.java b/src/com/hypixel/hytale/protocol/MovementEffects.java index 7c64efd8..f1b939c0 100644 --- a/src/com/hypixel/hytale/protocol/MovementEffects.java +++ b/src/com/hypixel/hytale/protocol/MovementEffects.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -52,15 +53,19 @@ public class MovementEffects { @Nonnull public static MovementEffects deserialize(@Nonnull ByteBuf buf, int offset) { - MovementEffects obj = new MovementEffects(); - obj.disableForward = buf.getByte(offset + 0) != 0; - obj.disableBackward = buf.getByte(offset + 1) != 0; - obj.disableLeft = buf.getByte(offset + 2) != 0; - obj.disableRight = buf.getByte(offset + 3) != 0; - obj.disableSprint = buf.getByte(offset + 4) != 0; - obj.disableJump = buf.getByte(offset + 5) != 0; - obj.disableCrouch = buf.getByte(offset + 6) != 0; - return obj; + if (buf.readableBytes() - offset < 7) { + throw ProtocolException.bufferTooSmall("MovementEffects", 7, buf.readableBytes() - offset); + } else { + MovementEffects obj = new MovementEffects(); + obj.disableForward = buf.getByte(offset + 0) != 0; + obj.disableBackward = buf.getByte(offset + 1) != 0; + obj.disableLeft = buf.getByte(offset + 2) != 0; + obj.disableRight = buf.getByte(offset + 3) != 0; + obj.disableSprint = buf.getByte(offset + 4) != 0; + obj.disableJump = buf.getByte(offset + 5) != 0; + obj.disableCrouch = buf.getByte(offset + 6) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/MovementSettings.java b/src/com/hypixel/hytale/protocol/MovementSettings.java index 5230d85b..077b56e8 100644 --- a/src/com/hypixel/hytale/protocol/MovementSettings.java +++ b/src/com/hypixel/hytale/protocol/MovementSettings.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -284,73 +285,77 @@ public class MovementSettings { @Nonnull public static MovementSettings deserialize(@Nonnull ByteBuf buf, int offset) { - MovementSettings obj = new MovementSettings(); - obj.mass = buf.getFloatLE(offset + 0); - obj.dragCoefficient = buf.getFloatLE(offset + 4); - obj.invertedGravity = buf.getByte(offset + 8) != 0; - obj.velocityResistance = buf.getFloatLE(offset + 9); - obj.jumpForce = buf.getFloatLE(offset + 13); - obj.swimJumpForce = buf.getFloatLE(offset + 17); - obj.jumpBufferDuration = buf.getFloatLE(offset + 21); - obj.jumpBufferMaxYVelocity = buf.getFloatLE(offset + 25); - obj.acceleration = buf.getFloatLE(offset + 29); - obj.airDragMin = buf.getFloatLE(offset + 33); - obj.airDragMax = buf.getFloatLE(offset + 37); - obj.airDragMinSpeed = buf.getFloatLE(offset + 41); - obj.airDragMaxSpeed = buf.getFloatLE(offset + 45); - obj.airFrictionMin = buf.getFloatLE(offset + 49); - obj.airFrictionMax = buf.getFloatLE(offset + 53); - obj.airFrictionMinSpeed = buf.getFloatLE(offset + 57); - obj.airFrictionMaxSpeed = buf.getFloatLE(offset + 61); - obj.airSpeedMultiplier = buf.getFloatLE(offset + 65); - obj.airControlMinSpeed = buf.getFloatLE(offset + 69); - obj.airControlMaxSpeed = buf.getFloatLE(offset + 73); - obj.airControlMinMultiplier = buf.getFloatLE(offset + 77); - obj.airControlMaxMultiplier = buf.getFloatLE(offset + 81); - obj.comboAirSpeedMultiplier = buf.getFloatLE(offset + 85); - obj.baseSpeed = buf.getFloatLE(offset + 89); - obj.climbSpeed = buf.getFloatLE(offset + 93); - obj.climbSpeedLateral = buf.getFloatLE(offset + 97); - obj.climbUpSprintSpeed = buf.getFloatLE(offset + 101); - obj.climbDownSprintSpeed = buf.getFloatLE(offset + 105); - obj.horizontalFlySpeed = buf.getFloatLE(offset + 109); - obj.verticalFlySpeed = buf.getFloatLE(offset + 113); - obj.maxSpeedMultiplier = buf.getFloatLE(offset + 117); - obj.minSpeedMultiplier = buf.getFloatLE(offset + 121); - obj.wishDirectionGravityX = buf.getFloatLE(offset + 125); - obj.wishDirectionGravityY = buf.getFloatLE(offset + 129); - obj.wishDirectionWeightX = buf.getFloatLE(offset + 133); - obj.wishDirectionWeightY = buf.getFloatLE(offset + 137); - obj.canFly = buf.getByte(offset + 141) != 0; - obj.collisionExpulsionForce = buf.getFloatLE(offset + 142); - obj.forwardWalkSpeedMultiplier = buf.getFloatLE(offset + 146); - obj.backwardWalkSpeedMultiplier = buf.getFloatLE(offset + 150); - obj.strafeWalkSpeedMultiplier = buf.getFloatLE(offset + 154); - obj.forwardRunSpeedMultiplier = buf.getFloatLE(offset + 158); - obj.backwardRunSpeedMultiplier = buf.getFloatLE(offset + 162); - obj.strafeRunSpeedMultiplier = buf.getFloatLE(offset + 166); - obj.forwardCrouchSpeedMultiplier = buf.getFloatLE(offset + 170); - obj.backwardCrouchSpeedMultiplier = buf.getFloatLE(offset + 174); - obj.strafeCrouchSpeedMultiplier = buf.getFloatLE(offset + 178); - obj.forwardSprintSpeedMultiplier = buf.getFloatLE(offset + 182); - obj.variableJumpFallForce = buf.getFloatLE(offset + 186); - obj.fallEffectDuration = buf.getFloatLE(offset + 190); - obj.fallJumpForce = buf.getFloatLE(offset + 194); - obj.fallMomentumLoss = buf.getFloatLE(offset + 198); - obj.autoJumpObstacleSpeedLoss = buf.getFloatLE(offset + 202); - obj.autoJumpObstacleSprintSpeedLoss = buf.getFloatLE(offset + 206); - obj.autoJumpObstacleEffectDuration = buf.getFloatLE(offset + 210); - obj.autoJumpObstacleSprintEffectDuration = buf.getFloatLE(offset + 214); - obj.autoJumpObstacleMaxAngle = buf.getFloatLE(offset + 218); - obj.autoJumpDisableJumping = buf.getByte(offset + 222) != 0; - obj.minSlideEntrySpeed = buf.getFloatLE(offset + 223); - obj.slideExitSpeed = buf.getFloatLE(offset + 227); - obj.minFallSpeedToEngageRoll = buf.getFloatLE(offset + 231); - obj.maxFallSpeedToEngageRoll = buf.getFloatLE(offset + 235); - obj.rollStartSpeedModifier = buf.getFloatLE(offset + 239); - obj.rollExitSpeedModifier = buf.getFloatLE(offset + 243); - obj.rollTimeToComplete = buf.getFloatLE(offset + 247); - return obj; + if (buf.readableBytes() - offset < 251) { + throw ProtocolException.bufferTooSmall("MovementSettings", 251, buf.readableBytes() - offset); + } else { + MovementSettings obj = new MovementSettings(); + obj.mass = buf.getFloatLE(offset + 0); + obj.dragCoefficient = buf.getFloatLE(offset + 4); + obj.invertedGravity = buf.getByte(offset + 8) != 0; + obj.velocityResistance = buf.getFloatLE(offset + 9); + obj.jumpForce = buf.getFloatLE(offset + 13); + obj.swimJumpForce = buf.getFloatLE(offset + 17); + obj.jumpBufferDuration = buf.getFloatLE(offset + 21); + obj.jumpBufferMaxYVelocity = buf.getFloatLE(offset + 25); + obj.acceleration = buf.getFloatLE(offset + 29); + obj.airDragMin = buf.getFloatLE(offset + 33); + obj.airDragMax = buf.getFloatLE(offset + 37); + obj.airDragMinSpeed = buf.getFloatLE(offset + 41); + obj.airDragMaxSpeed = buf.getFloatLE(offset + 45); + obj.airFrictionMin = buf.getFloatLE(offset + 49); + obj.airFrictionMax = buf.getFloatLE(offset + 53); + obj.airFrictionMinSpeed = buf.getFloatLE(offset + 57); + obj.airFrictionMaxSpeed = buf.getFloatLE(offset + 61); + obj.airSpeedMultiplier = buf.getFloatLE(offset + 65); + obj.airControlMinSpeed = buf.getFloatLE(offset + 69); + obj.airControlMaxSpeed = buf.getFloatLE(offset + 73); + obj.airControlMinMultiplier = buf.getFloatLE(offset + 77); + obj.airControlMaxMultiplier = buf.getFloatLE(offset + 81); + obj.comboAirSpeedMultiplier = buf.getFloatLE(offset + 85); + obj.baseSpeed = buf.getFloatLE(offset + 89); + obj.climbSpeed = buf.getFloatLE(offset + 93); + obj.climbSpeedLateral = buf.getFloatLE(offset + 97); + obj.climbUpSprintSpeed = buf.getFloatLE(offset + 101); + obj.climbDownSprintSpeed = buf.getFloatLE(offset + 105); + obj.horizontalFlySpeed = buf.getFloatLE(offset + 109); + obj.verticalFlySpeed = buf.getFloatLE(offset + 113); + obj.maxSpeedMultiplier = buf.getFloatLE(offset + 117); + obj.minSpeedMultiplier = buf.getFloatLE(offset + 121); + obj.wishDirectionGravityX = buf.getFloatLE(offset + 125); + obj.wishDirectionGravityY = buf.getFloatLE(offset + 129); + obj.wishDirectionWeightX = buf.getFloatLE(offset + 133); + obj.wishDirectionWeightY = buf.getFloatLE(offset + 137); + obj.canFly = buf.getByte(offset + 141) != 0; + obj.collisionExpulsionForce = buf.getFloatLE(offset + 142); + obj.forwardWalkSpeedMultiplier = buf.getFloatLE(offset + 146); + obj.backwardWalkSpeedMultiplier = buf.getFloatLE(offset + 150); + obj.strafeWalkSpeedMultiplier = buf.getFloatLE(offset + 154); + obj.forwardRunSpeedMultiplier = buf.getFloatLE(offset + 158); + obj.backwardRunSpeedMultiplier = buf.getFloatLE(offset + 162); + obj.strafeRunSpeedMultiplier = buf.getFloatLE(offset + 166); + obj.forwardCrouchSpeedMultiplier = buf.getFloatLE(offset + 170); + obj.backwardCrouchSpeedMultiplier = buf.getFloatLE(offset + 174); + obj.strafeCrouchSpeedMultiplier = buf.getFloatLE(offset + 178); + obj.forwardSprintSpeedMultiplier = buf.getFloatLE(offset + 182); + obj.variableJumpFallForce = buf.getFloatLE(offset + 186); + obj.fallEffectDuration = buf.getFloatLE(offset + 190); + obj.fallJumpForce = buf.getFloatLE(offset + 194); + obj.fallMomentumLoss = buf.getFloatLE(offset + 198); + obj.autoJumpObstacleSpeedLoss = buf.getFloatLE(offset + 202); + obj.autoJumpObstacleSprintSpeedLoss = buf.getFloatLE(offset + 206); + obj.autoJumpObstacleEffectDuration = buf.getFloatLE(offset + 210); + obj.autoJumpObstacleSprintEffectDuration = buf.getFloatLE(offset + 214); + obj.autoJumpObstacleMaxAngle = buf.getFloatLE(offset + 218); + obj.autoJumpDisableJumping = buf.getByte(offset + 222) != 0; + obj.minSlideEntrySpeed = buf.getFloatLE(offset + 223); + obj.slideExitSpeed = buf.getFloatLE(offset + 227); + obj.minFallSpeedToEngageRoll = buf.getFloatLE(offset + 231); + obj.maxFallSpeedToEngageRoll = buf.getFloatLE(offset + 235); + obj.rollStartSpeedModifier = buf.getFloatLE(offset + 239); + obj.rollExitSpeedModifier = buf.getFloatLE(offset + 243); + obj.rollTimeToComplete = buf.getFloatLE(offset + 247); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/MovementStates.java b/src/com/hypixel/hytale/protocol/MovementStates.java index f38bf623..9a6e0a81 100644 --- a/src/com/hypixel/hytale/protocol/MovementStates.java +++ b/src/com/hypixel/hytale/protocol/MovementStates.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -7,10 +8,10 @@ import javax.annotation.Nonnull; public class MovementStates { public static final int NULLABLE_BIT_FIELD_SIZE = 0; - public static final int FIXED_BLOCK_SIZE = 22; + public static final int FIXED_BLOCK_SIZE = 23; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 22; - public static final int MAX_SIZE = 22; + public static final int VARIABLE_BLOCK_START = 23; + public static final int MAX_SIZE = 23; public boolean idle; public boolean horizontalIdle; public boolean jumping; @@ -21,6 +22,7 @@ public class MovementStates { public boolean crouching; public boolean forcedCrouching; public boolean falling; + public boolean fallingFar; public boolean climbing; public boolean inFluid; public boolean swimming; @@ -48,6 +50,7 @@ public class MovementStates { boolean crouching, boolean forcedCrouching, boolean falling, + boolean fallingFar, boolean climbing, boolean inFluid, boolean swimming, @@ -71,6 +74,7 @@ public class MovementStates { this.crouching = crouching; this.forcedCrouching = forcedCrouching; this.falling = falling; + this.fallingFar = fallingFar; this.climbing = climbing; this.inFluid = inFluid; this.swimming = swimming; @@ -96,6 +100,7 @@ public class MovementStates { this.crouching = other.crouching; this.forcedCrouching = other.forcedCrouching; this.falling = other.falling; + this.fallingFar = other.fallingFar; this.climbing = other.climbing; this.inFluid = other.inFluid; this.swimming = other.swimming; @@ -112,34 +117,39 @@ public class MovementStates { @Nonnull public static MovementStates deserialize(@Nonnull ByteBuf buf, int offset) { - MovementStates obj = new MovementStates(); - obj.idle = buf.getByte(offset + 0) != 0; - obj.horizontalIdle = buf.getByte(offset + 1) != 0; - obj.jumping = buf.getByte(offset + 2) != 0; - obj.flying = buf.getByte(offset + 3) != 0; - obj.walking = buf.getByte(offset + 4) != 0; - obj.running = buf.getByte(offset + 5) != 0; - obj.sprinting = buf.getByte(offset + 6) != 0; - obj.crouching = buf.getByte(offset + 7) != 0; - obj.forcedCrouching = buf.getByte(offset + 8) != 0; - obj.falling = buf.getByte(offset + 9) != 0; - obj.climbing = buf.getByte(offset + 10) != 0; - obj.inFluid = buf.getByte(offset + 11) != 0; - obj.swimming = buf.getByte(offset + 12) != 0; - obj.swimJumping = buf.getByte(offset + 13) != 0; - obj.onGround = buf.getByte(offset + 14) != 0; - obj.mantling = buf.getByte(offset + 15) != 0; - obj.sliding = buf.getByte(offset + 16) != 0; - obj.mounting = buf.getByte(offset + 17) != 0; - obj.rolling = buf.getByte(offset + 18) != 0; - obj.sitting = buf.getByte(offset + 19) != 0; - obj.gliding = buf.getByte(offset + 20) != 0; - obj.sleeping = buf.getByte(offset + 21) != 0; - return obj; + if (buf.readableBytes() - offset < 23) { + throw ProtocolException.bufferTooSmall("MovementStates", 23, buf.readableBytes() - offset); + } else { + MovementStates obj = new MovementStates(); + obj.idle = buf.getByte(offset + 0) != 0; + obj.horizontalIdle = buf.getByte(offset + 1) != 0; + obj.jumping = buf.getByte(offset + 2) != 0; + obj.flying = buf.getByte(offset + 3) != 0; + obj.walking = buf.getByte(offset + 4) != 0; + obj.running = buf.getByte(offset + 5) != 0; + obj.sprinting = buf.getByte(offset + 6) != 0; + obj.crouching = buf.getByte(offset + 7) != 0; + obj.forcedCrouching = buf.getByte(offset + 8) != 0; + obj.falling = buf.getByte(offset + 9) != 0; + obj.fallingFar = buf.getByte(offset + 10) != 0; + obj.climbing = buf.getByte(offset + 11) != 0; + obj.inFluid = buf.getByte(offset + 12) != 0; + obj.swimming = buf.getByte(offset + 13) != 0; + obj.swimJumping = buf.getByte(offset + 14) != 0; + obj.onGround = buf.getByte(offset + 15) != 0; + obj.mantling = buf.getByte(offset + 16) != 0; + obj.sliding = buf.getByte(offset + 17) != 0; + obj.mounting = buf.getByte(offset + 18) != 0; + obj.rolling = buf.getByte(offset + 19) != 0; + obj.sitting = buf.getByte(offset + 20) != 0; + obj.gliding = buf.getByte(offset + 21) != 0; + obj.sleeping = buf.getByte(offset + 22) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 22; + return 23; } public void serialize(@Nonnull ByteBuf buf) { @@ -153,6 +163,7 @@ public class MovementStates { buf.writeByte(this.crouching ? 1 : 0); buf.writeByte(this.forcedCrouching ? 1 : 0); buf.writeByte(this.falling ? 1 : 0); + buf.writeByte(this.fallingFar ? 1 : 0); buf.writeByte(this.climbing ? 1 : 0); buf.writeByte(this.inFluid ? 1 : 0); buf.writeByte(this.swimming ? 1 : 0); @@ -168,11 +179,11 @@ public class MovementStates { } public int computeSize() { - return 22; + return 23; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 22 ? ValidationResult.error("Buffer too small: expected at least 22 bytes") : ValidationResult.OK; + return buffer.readableBytes() - offset < 23 ? ValidationResult.error("Buffer too small: expected at least 23 bytes") : ValidationResult.OK; } public MovementStates clone() { @@ -187,6 +198,7 @@ public class MovementStates { copy.crouching = this.crouching; copy.forcedCrouching = this.forcedCrouching; copy.falling = this.falling; + copy.fallingFar = this.fallingFar; copy.climbing = this.climbing; copy.inFluid = this.inFluid; copy.swimming = this.swimming; @@ -219,6 +231,7 @@ public class MovementStates { && this.crouching == other.crouching && this.forcedCrouching == other.forcedCrouching && this.falling == other.falling + && this.fallingFar == other.fallingFar && this.climbing == other.climbing && this.inFluid == other.inFluid && this.swimming == other.swimming @@ -247,6 +260,7 @@ public class MovementStates { this.crouching, this.forcedCrouching, this.falling, + this.fallingFar, this.climbing, this.inFluid, this.swimming, diff --git a/src/com/hypixel/hytale/protocol/MovementStatesUpdate.java b/src/com/hypixel/hytale/protocol/MovementStatesUpdate.java index 0ef57666..a6a0fc16 100644 --- a/src/com/hypixel/hytale/protocol/MovementStatesUpdate.java +++ b/src/com/hypixel/hytale/protocol/MovementStatesUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -7,10 +8,10 @@ import javax.annotation.Nonnull; public class MovementStatesUpdate extends ComponentUpdate { public static final int NULLABLE_BIT_FIELD_SIZE = 0; - public static final int FIXED_BLOCK_SIZE = 22; + public static final int FIXED_BLOCK_SIZE = 23; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 22; - public static final int MAX_SIZE = 22; + public static final int VARIABLE_BLOCK_START = 23; + public static final int MAX_SIZE = 23; @Nonnull public MovementStates movementStates = new MovementStates(); @@ -27,13 +28,17 @@ public class MovementStatesUpdate extends ComponentUpdate { @Nonnull public static MovementStatesUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - MovementStatesUpdate obj = new MovementStatesUpdate(); - obj.movementStates = MovementStates.deserialize(buf, offset + 0); - return obj; + if (buf.readableBytes() - offset < 23) { + throw ProtocolException.bufferTooSmall("MovementStatesUpdate", 23, buf.readableBytes() - offset); + } else { + MovementStatesUpdate obj = new MovementStatesUpdate(); + obj.movementStates = MovementStates.deserialize(buf, offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 22; + return 23; } @Override @@ -45,11 +50,11 @@ public class MovementStatesUpdate extends ComponentUpdate { @Override public int computeSize() { - return 22; + return 23; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 22 ? ValidationResult.error("Buffer too small: expected at least 22 bytes") : ValidationResult.OK; + return buffer.readableBytes() - offset < 23 ? ValidationResult.error("Buffer too small: expected at least 23 bytes") : ValidationResult.OK; } public MovementStatesUpdate clone() { diff --git a/src/com/hypixel/hytale/protocol/NameplateUpdate.java b/src/com/hypixel/hytale/protocol/NameplateUpdate.java index 69de5d1f..e29a3712 100644 --- a/src/com/hypixel/hytale/protocol/NameplateUpdate.java +++ b/src/com/hypixel/hytale/protocol/NameplateUpdate.java @@ -34,21 +34,25 @@ public class NameplateUpdate extends ComponentUpdate { int pos = offset + 0; int textLen = VarInt.peek(buf, pos); if (textLen < 0) { - throw ProtocolException.negativeLength("Text", textLen); - } else if (textLen > 4096000) { - throw ProtocolException.stringTooLong("Text", textLen, 4096000); + throw ProtocolException.invalidVarInt("Text"); } else { - int textVarLen = VarInt.length(buf, pos); - obj.text = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += textVarLen + textLen; - return obj; + int textVarLen = VarInt.size(textLen); + if (textLen > 4096000) { + throw ProtocolException.stringTooLong("Text", textLen, 4096000); + } else if (pos + textVarLen + textLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Text", pos + textVarLen + textLen, buf.readableBytes()); + } else { + obj.text = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += textVarLen + textLen; + return obj; + } } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 0; int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; return pos - offset; } @@ -76,7 +80,7 @@ public class NameplateUpdate extends ComponentUpdate { } else if (textLen > 4096000) { return ValidationResult.error("Text exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(textLen); pos += textLen; return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Text") : ValidationResult.OK; } diff --git a/src/com/hypixel/hytale/protocol/NearFar.java b/src/com/hypixel/hytale/protocol/NearFar.java index 6ef93bb6..59f82a13 100644 --- a/src/com/hypixel/hytale/protocol/NearFar.java +++ b/src/com/hypixel/hytale/protocol/NearFar.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class NearFar { @Nonnull public static NearFar deserialize(@Nonnull ByteBuf buf, int offset) { - NearFar obj = new NearFar(); - obj.near = buf.getFloatLE(offset + 0); - obj.far = buf.getFloatLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("NearFar", 8, buf.readableBytes() - offset); + } else { + NearFar obj = new NearFar(); + obj.near = buf.getFloatLE(offset + 0); + obj.far = buf.getFloatLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/NetworkChannel.java b/src/com/hypixel/hytale/protocol/NetworkChannel.java index c605e321..5b3c6dec 100644 --- a/src/com/hypixel/hytale/protocol/NetworkChannel.java +++ b/src/com/hypixel/hytale/protocol/NetworkChannel.java @@ -3,7 +3,8 @@ package com.hypixel.hytale.protocol; public enum NetworkChannel { Default(0), Chunks(1), - WorldMap(2); + WorldMap(2), + Voice(3); public static final NetworkChannel[] VALUES = values(); public static final int COUNT = VALUES.length; diff --git a/src/com/hypixel/hytale/protocol/NoiseConfig.java b/src/com/hypixel/hytale/protocol/NoiseConfig.java index aaeb8f14..a0635cef 100644 --- a/src/com/hypixel/hytale/protocol/NoiseConfig.java +++ b/src/com/hypixel/hytale/protocol/NoiseConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,17 +42,21 @@ public class NoiseConfig { @Nonnull public static NoiseConfig deserialize(@Nonnull ByteBuf buf, int offset) { - NoiseConfig obj = new NoiseConfig(); - byte nullBits = buf.getByte(offset); - obj.seed = buf.getIntLE(offset + 1); - obj.type = NoiseType.fromValue(buf.getByte(offset + 5)); - obj.frequency = buf.getFloatLE(offset + 6); - obj.amplitude = buf.getFloatLE(offset + 10); - if ((nullBits & 1) != 0) { - obj.clamp = ClampConfig.deserialize(buf, offset + 14); - } + if (buf.readableBytes() - offset < 23) { + throw ProtocolException.bufferTooSmall("NoiseConfig", 23, buf.readableBytes() - offset); + } else { + NoiseConfig obj = new NoiseConfig(); + byte nullBits = buf.getByte(offset); + obj.seed = buf.getIntLE(offset + 1); + obj.type = NoiseType.fromValue(buf.getByte(offset + 5)); + obj.frequency = buf.getFloatLE(offset + 6); + obj.amplitude = buf.getFloatLE(offset + 10); + if ((nullBits & 1) != 0) { + obj.clamp = ClampConfig.deserialize(buf, offset + 14); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,7 +86,13 @@ public class NoiseConfig { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 23 ? ValidationResult.error("Buffer too small: expected at least 23 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 23) { + return ValidationResult.error("Buffer too small: expected at least 23 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 5) & 255; + return v >= 6 ? ValidationResult.error("Invalid NoiseType value for Type") : ValidationResult.OK; + } } public NoiseConfig clone() { diff --git a/src/com/hypixel/hytale/protocol/Objective.java b/src/com/hypixel/hytale/protocol/Objective.java index 2811bf18..a941da02 100644 --- a/src/com/hypixel/hytale/protocol/Objective.java +++ b/src/com/hypixel/hytale/protocol/Objective.java @@ -55,59 +55,88 @@ public class Objective { @Nonnull public static Objective deserialize(@Nonnull ByteBuf buf, int offset) { - Objective obj = new Objective(); - byte nullBits = buf.getByte(offset); - obj.objectiveUuid = PacketIO.readUUID(buf, offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 33 + buf.getIntLE(offset + 17); - obj.objectiveTitleKey = FormattedMessage.deserialize(buf, varPos0); + if (buf.readableBytes() - offset < 33) { + throw ProtocolException.bufferTooSmall("Objective", 33, buf.readableBytes() - offset); + } else { + Objective obj = new Objective(); + byte nullBits = buf.getByte(offset); + obj.objectiveUuid = PacketIO.readUUID(buf, offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 17); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("ObjectiveTitleKey", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 33 + varPosBase0; + obj.objectiveTitleKey = FormattedMessage.deserialize(buf, varPos0); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 21); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("ObjectiveDescriptionKey", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 33 + varPosBase1; + obj.objectiveDescriptionKey = FormattedMessage.deserialize(buf, varPos1); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 25); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("ObjectiveLineId", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 33 + varPosBase2; + int objectiveLineIdLen = VarInt.peek(buf, varPos2); + if (objectiveLineIdLen < 0) { + throw ProtocolException.invalidVarInt("ObjectiveLineId"); + } + + int objectiveLineIdVarIntLen = VarInt.size(objectiveLineIdLen); + if (objectiveLineIdLen > 4096000) { + throw ProtocolException.stringTooLong("ObjectiveLineId", objectiveLineIdLen, 4096000); + } + + if (varPos2 + objectiveLineIdVarIntLen + objectiveLineIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ObjectiveLineId", varPos2 + objectiveLineIdVarIntLen + objectiveLineIdLen, buf.readableBytes()); + } + + obj.objectiveLineId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 29); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("Tasks", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 33 + varPosBase3; + int tasksCount = VarInt.peek(buf, varPos3); + if (tasksCount < 0) { + throw ProtocolException.invalidVarInt("Tasks"); + } + + int varIntLen = VarInt.size(tasksCount); + if (tasksCount > 4096000) { + throw ProtocolException.arrayTooLong("Tasks", tasksCount, 4096000); + } + + if (varPos3 + varIntLen + tasksCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tasks", varPos3 + varIntLen + tasksCount * 9, buf.readableBytes()); + } + + obj.tasks = new ObjectiveTask[tasksCount]; + int elemPos = varPos3 + varIntLen; + + for (int i = 0; i < tasksCount; i++) { + obj.tasks[i] = ObjectiveTask.deserialize(buf, elemPos); + elemPos += ObjectiveTask.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 33 + buf.getIntLE(offset + 21); - obj.objectiveDescriptionKey = FormattedMessage.deserialize(buf, varPos1); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 33 + buf.getIntLE(offset + 25); - int objectiveLineIdLen = VarInt.peek(buf, varPos2); - if (objectiveLineIdLen < 0) { - throw ProtocolException.negativeLength("ObjectiveLineId", objectiveLineIdLen); - } - - if (objectiveLineIdLen > 4096000) { - throw ProtocolException.stringTooLong("ObjectiveLineId", objectiveLineIdLen, 4096000); - } - - obj.objectiveLineId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 33 + buf.getIntLE(offset + 29); - int tasksCount = VarInt.peek(buf, varPos3); - if (tasksCount < 0) { - throw ProtocolException.negativeLength("Tasks", tasksCount); - } - - if (tasksCount > 4096000) { - throw ProtocolException.arrayTooLong("Tasks", tasksCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tasksCount * 9L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tasks", varPos3 + varIntLen + tasksCount * 9, buf.readableBytes()); - } - - obj.tasks = new ObjectiveTask[tasksCount]; - int elemPos = varPos3 + varIntLen; - - for (int i = 0; i < tasksCount; i++) { - obj.tasks[i] = ObjectiveTask.deserialize(buf, elemPos); - elemPos += ObjectiveTask.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -115,6 +144,10 @@ public class Objective { int maxEnd = 33; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 17); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("ObjectiveTitleKey", fieldOffset0, maxEnd); + } + int pos0 = offset + 33 + fieldOffset0; pos0 += FormattedMessage.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -124,6 +157,10 @@ public class Objective { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 21); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("ObjectiveDescriptionKey", fieldOffset1, maxEnd); + } + int pos1 = offset + 33 + fieldOffset1; pos1 += FormattedMessage.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -133,9 +170,13 @@ public class Objective { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 25); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("ObjectiveLineId", fieldOffset2, maxEnd); + } + int pos2 = offset + 33 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -143,9 +184,13 @@ public class Objective { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 29); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("Tasks", fieldOffset3, maxEnd); + } + int pos3 = offset + 33 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos3 += ObjectiveTask.computeBytesConsumed(buf, pos3); @@ -260,15 +305,11 @@ public class Objective { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int objectiveTitleKeyOffset = buffer.getIntLE(offset + 17); - if (objectiveTitleKeyOffset < 0) { + if (objectiveTitleKeyOffset < 0 || objectiveTitleKeyOffset > buffer.writerIndex() - offset - 33) { return ValidationResult.error("Invalid offset for ObjectiveTitleKey"); } int pos = offset + 33 + objectiveTitleKeyOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ObjectiveTitleKey"); - } - ValidationResult objectiveTitleKeyResult = FormattedMessage.validateStructure(buffer, pos); if (!objectiveTitleKeyResult.isValid()) { return ValidationResult.error("Invalid ObjectiveTitleKey: " + objectiveTitleKeyResult.error()); @@ -279,35 +320,27 @@ public class Objective { if ((nullBits & 2) != 0) { int objectiveDescriptionKeyOffset = buffer.getIntLE(offset + 21); - if (objectiveDescriptionKeyOffset < 0) { + if (objectiveDescriptionKeyOffset < 0 || objectiveDescriptionKeyOffset > buffer.writerIndex() - offset - 33) { return ValidationResult.error("Invalid offset for ObjectiveDescriptionKey"); } - int posx = offset + 33 + objectiveDescriptionKeyOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ObjectiveDescriptionKey"); - } - - ValidationResult objectiveDescriptionKeyResult = FormattedMessage.validateStructure(buffer, posx); + int pos = offset + 33 + objectiveDescriptionKeyOffset; + ValidationResult objectiveDescriptionKeyResult = FormattedMessage.validateStructure(buffer, pos); if (!objectiveDescriptionKeyResult.isValid()) { return ValidationResult.error("Invalid ObjectiveDescriptionKey: " + objectiveDescriptionKeyResult.error()); } - posx += FormattedMessage.computeBytesConsumed(buffer, posx); + pos += FormattedMessage.computeBytesConsumed(buffer, pos); } if ((nullBits & 4) != 0) { int objectiveLineIdOffset = buffer.getIntLE(offset + 25); - if (objectiveLineIdOffset < 0) { + if (objectiveLineIdOffset < 0 || objectiveLineIdOffset > buffer.writerIndex() - offset - 33) { return ValidationResult.error("Invalid offset for ObjectiveLineId"); } - int posxx = offset + 33 + objectiveLineIdOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ObjectiveLineId"); - } - - int objectiveLineIdLen = VarInt.peek(buffer, posxx); + int pos = offset + 33 + objectiveLineIdOffset; + int objectiveLineIdLen = VarInt.peek(buffer, pos); if (objectiveLineIdLen < 0) { return ValidationResult.error("Invalid string length for ObjectiveLineId"); } @@ -316,25 +349,21 @@ public class Objective { return ValidationResult.error("ObjectiveLineId exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += objectiveLineIdLen; - if (posxx > buffer.writerIndex()) { + pos += VarInt.size(objectiveLineIdLen); + pos += objectiveLineIdLen; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ObjectiveLineId"); } } if ((nullBits & 8) != 0) { int tasksOffset = buffer.getIntLE(offset + 29); - if (tasksOffset < 0) { + if (tasksOffset < 0 || tasksOffset > buffer.writerIndex() - offset - 33) { return ValidationResult.error("Invalid offset for Tasks"); } - int posxxx = offset + 33 + tasksOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tasks"); - } - - int tasksCount = VarInt.peek(buffer, posxxx); + int posx = offset + 33 + tasksOffset; + int tasksCount = VarInt.peek(buffer, posx); if (tasksCount < 0) { return ValidationResult.error("Invalid array count for Tasks"); } @@ -343,15 +372,15 @@ public class Objective { return ValidationResult.error("Tasks exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posx += VarInt.size(tasksCount); for (int i = 0; i < tasksCount; i++) { - ValidationResult structResult = ObjectiveTask.validateStructure(buffer, posxxx); + ValidationResult structResult = ObjectiveTask.validateStructure(buffer, posx); if (!structResult.isValid()) { return ValidationResult.error("Invalid ObjectiveTask in Tasks[" + i + "]: " + structResult.error()); } - posxxx += ObjectiveTask.computeBytesConsumed(buffer, posxxx); + posx += ObjectiveTask.computeBytesConsumed(buffer, posx); } } diff --git a/src/com/hypixel/hytale/protocol/ObjectiveTask.java b/src/com/hypixel/hytale/protocol/ObjectiveTask.java index 3aaf2545..a43553b3 100644 --- a/src/com/hypixel/hytale/protocol/ObjectiveTask.java +++ b/src/com/hypixel/hytale/protocol/ObjectiveTask.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -34,17 +35,21 @@ public class ObjectiveTask { @Nonnull public static ObjectiveTask deserialize(@Nonnull ByteBuf buf, int offset) { - ObjectiveTask obj = new ObjectiveTask(); - byte nullBits = buf.getByte(offset); - obj.currentCompletion = buf.getIntLE(offset + 1); - obj.completionNeeded = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - obj.taskDescriptionKey = FormattedMessage.deserialize(buf, pos); - pos += FormattedMessage.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ObjectiveTask", 9, buf.readableBytes() - offset); + } else { + ObjectiveTask obj = new ObjectiveTask(); + byte nullBits = buf.getByte(offset); + obj.currentCompletion = buf.getIntLE(offset + 1); + obj.completionNeeded = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + obj.taskDescriptionKey = FormattedMessage.deserialize(buf, pos); + pos += FormattedMessage.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/OffsetNoise.java b/src/com/hypixel/hytale/protocol/OffsetNoise.java index efbee016..40d11f94 100644 --- a/src/com/hypixel/hytale/protocol/OffsetNoise.java +++ b/src/com/hypixel/hytale/protocol/OffsetNoise.java @@ -38,84 +38,103 @@ public class OffsetNoise { @Nonnull public static OffsetNoise deserialize(@Nonnull ByteBuf buf, int offset) { - OffsetNoise obj = new OffsetNoise(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int xCount = VarInt.peek(buf, varPos0); - if (xCount < 0) { - throw ProtocolException.negativeLength("X", xCount); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("OffsetNoise", 13, buf.readableBytes() - offset); + } else { + OffsetNoise obj = new OffsetNoise(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("X", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int xCount = VarInt.peek(buf, varPos0); + if (xCount < 0) { + throw ProtocolException.invalidVarInt("X"); + } + + int varIntLen = VarInt.size(xCount); + if (xCount > 4096000) { + throw ProtocolException.arrayTooLong("X", xCount, 4096000); + } + + if (varPos0 + varIntLen + xCount * 23L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("X", varPos0 + varIntLen + xCount * 23, buf.readableBytes()); + } + + obj.x = new NoiseConfig[xCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < xCount; i++) { + obj.x[i] = NoiseConfig.deserialize(buf, elemPos); + elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); + } } - if (xCount > 4096000) { - throw ProtocolException.arrayTooLong("X", xCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Y", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int yCount = VarInt.peek(buf, varPos1); + if (yCount < 0) { + throw ProtocolException.invalidVarInt("Y"); + } + + int varIntLenx = VarInt.size(yCount); + if (yCount > 4096000) { + throw ProtocolException.arrayTooLong("Y", yCount, 4096000); + } + + if (varPos1 + varIntLenx + yCount * 23L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Y", varPos1 + varIntLenx + yCount * 23, buf.readableBytes()); + } + + obj.y = new NoiseConfig[yCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < yCount; i++) { + obj.y[i] = NoiseConfig.deserialize(buf, elemPos); + elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + xCount * 23L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("X", varPos0 + varIntLen + xCount * 23, buf.readableBytes()); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Z", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int zCount = VarInt.peek(buf, varPos2); + if (zCount < 0) { + throw ProtocolException.invalidVarInt("Z"); + } + + int varIntLenxx = VarInt.size(zCount); + if (zCount > 4096000) { + throw ProtocolException.arrayTooLong("Z", zCount, 4096000); + } + + if (varPos2 + varIntLenxx + zCount * 23L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Z", varPos2 + varIntLenxx + zCount * 23, buf.readableBytes()); + } + + obj.z = new NoiseConfig[zCount]; + int elemPos = varPos2 + varIntLenxx; + + for (int i = 0; i < zCount; i++) { + obj.z[i] = NoiseConfig.deserialize(buf, elemPos); + elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); + } } - obj.x = new NoiseConfig[xCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < xCount; i++) { - obj.x[i] = NoiseConfig.deserialize(buf, elemPos); - elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int yCount = VarInt.peek(buf, varPos1); - if (yCount < 0) { - throw ProtocolException.negativeLength("Y", yCount); - } - - if (yCount > 4096000) { - throw ProtocolException.arrayTooLong("Y", yCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + yCount * 23L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Y", varPos1 + varIntLen + yCount * 23, buf.readableBytes()); - } - - obj.y = new NoiseConfig[yCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < yCount; i++) { - obj.y[i] = NoiseConfig.deserialize(buf, elemPos); - elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int zCount = VarInt.peek(buf, varPos2); - if (zCount < 0) { - throw ProtocolException.negativeLength("Z", zCount); - } - - if (zCount > 4096000) { - throw ProtocolException.arrayTooLong("Z", zCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + zCount * 23L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Z", varPos2 + varIntLen + zCount * 23, buf.readableBytes()); - } - - obj.z = new NoiseConfig[zCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < zCount; i++) { - obj.z[i] = NoiseConfig.deserialize(buf, elemPos); - elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -123,9 +142,13 @@ public class OffsetNoise { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("X", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += NoiseConfig.computeBytesConsumed(buf, pos0); @@ -138,9 +161,13 @@ public class OffsetNoise { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Y", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += NoiseConfig.computeBytesConsumed(buf, pos1); @@ -153,9 +180,13 @@ public class OffsetNoise { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Z", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += NoiseConfig.computeBytesConsumed(buf, pos2); @@ -262,15 +293,11 @@ public class OffsetNoise { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int xOffset = buffer.getIntLE(offset + 1); - if (xOffset < 0) { + if (xOffset < 0 || xOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for X"); } int pos = offset + 13 + xOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for X"); - } - int xCount = VarInt.peek(buffer, pos); if (xCount < 0) { return ValidationResult.error("Invalid array count for X"); @@ -280,7 +307,7 @@ public class OffsetNoise { return ValidationResult.error("X exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(xCount); pos += xCount * 23; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading X"); @@ -289,15 +316,11 @@ public class OffsetNoise { if ((nullBits & 2) != 0) { int yOffset = buffer.getIntLE(offset + 5); - if (yOffset < 0) { + if (yOffset < 0 || yOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Y"); } int posx = offset + 13 + yOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Y"); - } - int yCount = VarInt.peek(buffer, posx); if (yCount < 0) { return ValidationResult.error("Invalid array count for Y"); @@ -307,7 +330,7 @@ public class OffsetNoise { return ValidationResult.error("Y exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(yCount); posx += yCount * 23; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Y"); @@ -316,15 +339,11 @@ public class OffsetNoise { if ((nullBits & 4) != 0) { int zOffset = buffer.getIntLE(offset + 9); - if (zOffset < 0) { + if (zOffset < 0 || zOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Z"); } int posxx = offset + 13 + zOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Z"); - } - int zCount = VarInt.peek(buffer, posxx); if (zCount < 0) { return ValidationResult.error("Invalid array count for Z"); @@ -334,7 +353,7 @@ public class OffsetNoise { return ValidationResult.error("Z exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(zCount); posxx += zCount * 23; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Z"); diff --git a/src/com/hypixel/hytale/protocol/PacketRegistry.java b/src/com/hypixel/hytale/protocol/PacketRegistry.java index 2e06be79..227fd4c8 100644 --- a/src/com/hypixel/hytale/protocol/PacketRegistry.java +++ b/src/com/hypixel/hytale/protocol/PacketRegistry.java @@ -32,6 +32,7 @@ import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorInitialize; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorJsonAssetUpdated; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorLastModifiedAssets; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorModifiedAssetsCount; +import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorModsDirectories; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorPopupNotification; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorRedoChanges; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorRenameAsset; @@ -67,6 +68,7 @@ import com.hypixel.hytale.protocol.packets.assets.UpdateBlockSets; import com.hypixel.hytale.protocol.packets.assets.UpdateBlockSoundSets; import com.hypixel.hytale.protocol.packets.assets.UpdateBlockTypes; import com.hypixel.hytale.protocol.packets.assets.UpdateCameraShake; +import com.hypixel.hytale.protocol.packets.assets.UpdateEmotes; import com.hypixel.hytale.protocol.packets.assets.UpdateEntityEffects; import com.hypixel.hytale.protocol.packets.assets.UpdateEntityStatTypes; import com.hypixel.hytale.protocol.packets.assets.UpdateEntityUIComponents; @@ -87,6 +89,7 @@ import com.hypixel.hytale.protocol.packets.assets.UpdateModelvfxs; import com.hypixel.hytale.protocol.packets.assets.UpdateObjectiveTask; import com.hypixel.hytale.protocol.packets.assets.UpdateParticleSpawners; import com.hypixel.hytale.protocol.packets.assets.UpdateParticleSystems; +import com.hypixel.hytale.protocol.packets.assets.UpdatePhysicalMaterials; import com.hypixel.hytale.protocol.packets.assets.UpdateProjectileConfigs; import com.hypixel.hytale.protocol.packets.assets.UpdateRecipes; import com.hypixel.hytale.protocol.packets.assets.UpdateRepulsionConfig; @@ -118,6 +121,7 @@ import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolLaserPointer; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolLineAction; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOnUseInteraction; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolPasteClipboard; +import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolResetClipboardRotation; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolRotateClipboard; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolSelectionToolAskForClipboard; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolSelectionToolReplyWithClipboard; @@ -133,20 +137,23 @@ import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolSetTransforma import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolShowAnchor; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolStackArea; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolsSetSoundSet; +import com.hypixel.hytale.protocol.packets.buildertools.PrefabSetAnchor; import com.hypixel.hytale.protocol.packets.buildertools.PrefabUnselectPrefab; import com.hypixel.hytale.protocol.packets.camera.CameraShakeEffect; import com.hypixel.hytale.protocol.packets.camera.RequestFlyCameraMode; import com.hypixel.hytale.protocol.packets.camera.SetFlyCameraMode; import com.hypixel.hytale.protocol.packets.camera.SetServerCamera; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnect; import com.hypixel.hytale.protocol.packets.connection.Connect; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; import com.hypixel.hytale.protocol.packets.connection.Ping; import com.hypixel.hytale.protocol.packets.connection.Pong; +import com.hypixel.hytale.protocol.packets.connection.ServerDisconnect; import com.hypixel.hytale.protocol.packets.entities.ApplyKnockback; import com.hypixel.hytale.protocol.packets.entities.ChangeVelocity; import com.hypixel.hytale.protocol.packets.entities.EntityUpdates; import com.hypixel.hytale.protocol.packets.entities.MountMovement; import com.hypixel.hytale.protocol.packets.entities.PlayAnimation; +import com.hypixel.hytale.protocol.packets.entities.PlayEmote; import com.hypixel.hytale.protocol.packets.entities.SetEntitySeed; import com.hypixel.hytale.protocol.packets.entities.SpawnModelParticles; import com.hypixel.hytale.protocol.packets.interaction.CancelInteractionChain; @@ -155,11 +162,18 @@ import com.hypixel.hytale.protocol.packets.interaction.MountNPC; import com.hypixel.hytale.protocol.packets.interaction.PlayInteractionFor; import com.hypixel.hytale.protocol.packets.interaction.SyncInteractionChains; import com.hypixel.hytale.protocol.packets.interface_.AddToServerPlayerList; +import com.hypixel.hytale.protocol.packets.interface_.ArgCacheInvalidation; +import com.hypixel.hytale.protocol.packets.interface_.ArgValuesRequest; +import com.hypixel.hytale.protocol.packets.interface_.ArgValuesResponse; import com.hypixel.hytale.protocol.packets.interface_.ChatMessage; +import com.hypixel.hytale.protocol.packets.interface_.CommandSuggestionsRequest; +import com.hypixel.hytale.protocol.packets.interface_.CommandSuggestionsResponse; +import com.hypixel.hytale.protocol.packets.interface_.CommandTreeSync; import com.hypixel.hytale.protocol.packets.interface_.CustomHud; import com.hypixel.hytale.protocol.packets.interface_.CustomPage; import com.hypixel.hytale.protocol.packets.interface_.CustomPageEvent; import com.hypixel.hytale.protocol.packets.interface_.EditorBlocksChange; +import com.hypixel.hytale.protocol.packets.interface_.ExecuteServersidePageCommand; import com.hypixel.hytale.protocol.packets.interface_.HideEventTitle; import com.hypixel.hytale.protocol.packets.interface_.KillFeedMessage; import com.hypixel.hytale.protocol.packets.interface_.Notification; @@ -176,6 +190,7 @@ import com.hypixel.hytale.protocol.packets.interface_.UpdateLanguage; import com.hypixel.hytale.protocol.packets.interface_.UpdatePortal; import com.hypixel.hytale.protocol.packets.interface_.UpdateServerPlayerList; import com.hypixel.hytale.protocol.packets.interface_.UpdateServerPlayerListPing; +import com.hypixel.hytale.protocol.packets.interface_.UpdateServersideUIPage; import com.hypixel.hytale.protocol.packets.interface_.UpdateVisibleHudComponents; import com.hypixel.hytale.protocol.packets.interface_.WorldSavingStatus; import com.hypixel.hytale.protocol.packets.inventory.DropCreativeItem; @@ -229,6 +244,11 @@ import com.hypixel.hytale.protocol.packets.setup.ViewRadius; import com.hypixel.hytale.protocol.packets.setup.WorldLoadFinished; import com.hypixel.hytale.protocol.packets.setup.WorldLoadProgress; import com.hypixel.hytale.protocol.packets.setup.WorldSettings; +import com.hypixel.hytale.protocol.packets.stream.StreamOpen; +import com.hypixel.hytale.protocol.packets.stream.StreamOpenResponse; +import com.hypixel.hytale.protocol.packets.voice.RelayedVoiceData; +import com.hypixel.hytale.protocol.packets.voice.VoiceConfig; +import com.hypixel.hytale.protocol.packets.voice.VoiceData; import com.hypixel.hytale.protocol.packets.window.ClientOpenWindow; import com.hypixel.hytale.protocol.packets.window.CloseWindow; import com.hypixel.hytale.protocol.packets.window.OpenWindow; @@ -238,6 +258,7 @@ import com.hypixel.hytale.protocol.packets.world.ClearEditorTimeOverride; import com.hypixel.hytale.protocol.packets.world.PlaySoundEvent2D; import com.hypixel.hytale.protocol.packets.world.PlaySoundEvent3D; import com.hypixel.hytale.protocol.packets.world.PlaySoundEventEntity; +import com.hypixel.hytale.protocol.packets.world.PlaySoundEventLocalPlayer; import com.hypixel.hytale.protocol.packets.world.ServerSetBlock; import com.hypixel.hytale.protocol.packets.world.ServerSetBlocks; import com.hypixel.hytale.protocol.packets.world.ServerSetFluid; @@ -355,22 +376,34 @@ public final class PacketRegistry { Connect::deserialize ); register( - PacketRegistry.PacketDirection.Both, + PacketRegistry.PacketDirection.ToServer, NetworkChannel.Default, 1, - "Disconnect", - Disconnect.class, + "ClientDisconnect", + ClientDisconnect.class, + 2, 2, - 16384007, false, - Disconnect::validateStructure, - Disconnect::deserialize + ClientDisconnect::validateStructure, + ClientDisconnect::deserialize ); register( - PacketRegistry.PacketDirection.ToClient, NetworkChannel.Default, 2, "Ping", Ping.class, 29, 29, false, Ping::validateStructure, Ping::deserialize + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 2, + "ServerDisconnect", + ServerDisconnect.class, + 2, + 1677721600, + false, + ServerDisconnect::validateStructure, + ServerDisconnect::deserialize ); register( - PacketRegistry.PacketDirection.ToServer, NetworkChannel.Default, 3, "Pong", Pong.class, 20, 20, false, Pong::validateStructure, Pong::deserialize + PacketRegistry.PacketDirection.ToClient, NetworkChannel.Default, 3, "Ping", Ping.class, 29, 29, false, Ping::validateStructure, Ping::deserialize + ); + register( + PacketRegistry.PacketDirection.ToServer, NetworkChannel.Default, 4, "Pong", Pong.class, 20, 20, false, Pong::validateStructure, Pong::deserialize ); register( PacketRegistry.PacketDirection.ToClient, @@ -1200,6 +1233,30 @@ public final class PacketRegistry { UpdateProjectileConfigs::validateStructure, UpdateProjectileConfigs::deserialize ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 86, + "UpdateEmotes", + UpdateEmotes.class, + 6, + 1677721600, + true, + UpdateEmotes::validateStructure, + UpdateEmotes::deserialize + ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 87, + "UpdatePhysicalMaterials", + UpdatePhysicalMaterials.class, + 6, + 1677721600, + true, + UpdatePhysicalMaterials::validateStructure, + UpdatePhysicalMaterials::deserialize + ); register( PacketRegistry.PacketDirection.ToClient, NetworkChannel.Default, @@ -1302,8 +1359,8 @@ public final class PacketRegistry { 108, "ClientMovement", ClientMovement.class, - 153, - 153, + 155, + 155, false, ClientMovement::validateStructure, ClientMovement::deserialize @@ -1410,8 +1467,8 @@ public final class PacketRegistry { 117, "ClientPlaceBlock", ClientPlaceBlock.class, - 20, - 20, + 21, + 21, false, ClientPlaceBlock::validateStructure, ClientPlaceBlock::deserialize @@ -1830,19 +1887,31 @@ public final class PacketRegistry { 166, "MountMovement", MountMovement.class, - 59, - 59, + 60, + 60, false, MountMovement::validateStructure, MountMovement::deserialize ); + register( + PacketRegistry.PacketDirection.ToServer, + NetworkChannel.Default, + 167, + "PlayEmote", + PlayEmote.class, + 1, + 16384006, + false, + PlayEmote::validateStructure, + PlayEmote::deserialize + ); register( PacketRegistry.PacketDirection.ToClient, NetworkChannel.Default, 170, "UpdatePlayerInventory", UpdatePlayerInventory.class, - 2, + 1, 1677721600, true, UpdatePlayerInventory::validateStructure, @@ -2035,7 +2104,7 @@ public final class PacketRegistry { "ChatMessage", ChatMessage.class, 1, - 16384006, + 1026, false, ChatMessage::validateStructure, ChatMessage::deserialize @@ -2106,7 +2175,7 @@ public final class PacketRegistry { 217, "CustomHud", CustomHud.class, - 2, + 6, 1677721600, true, CustomHud::validateStructure, @@ -2142,8 +2211,8 @@ public final class PacketRegistry { 222, "EditorBlocksChange", EditorBlocksChange.class, - 30, - 139264048, + 31, + 1677721600, true, EditorBlocksChange::validateStructure, EditorBlocksChange::deserialize @@ -2155,7 +2224,7 @@ public final class PacketRegistry { "ServerInfo", ServerInfo.class, 5, - 32768023, + 32769058, false, ServerInfo::validateStructure, ServerInfo::deserialize @@ -2304,6 +2373,54 @@ public final class PacketRegistry { UpdateAnchorUI::validateStructure, UpdateAnchorUI::deserialize ); + register( + PacketRegistry.PacketDirection.ToServer, + NetworkChannel.Default, + 236, + "CommandSuggestionsRequest", + CommandSuggestionsRequest.class, + 9, + 16384014, + false, + CommandSuggestionsRequest::validateStructure, + CommandSuggestionsRequest::deserialize + ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 237, + "CommandSuggestionsResponse", + CommandSuggestionsResponse.class, + 17, + 1677721600, + false, + CommandSuggestionsResponse::validateStructure, + CommandSuggestionsResponse::deserialize + ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 238, + "CommandTreeSync", + CommandTreeSync.class, + 1, + 1677721600, + true, + CommandTreeSync::validateStructure, + CommandTreeSync::deserialize + ); + register( + PacketRegistry.PacketDirection.ToServer, + NetworkChannel.Default, + 239, + "ArgValuesRequest", + ArgValuesRequest.class, + 1, + 32768019, + false, + ArgValuesRequest::validateStructure, + ArgValuesRequest::deserialize + ); register( PacketRegistry.PacketDirection.ToClient, NetworkChannel.Default, @@ -2388,6 +2505,30 @@ public final class PacketRegistry { CreateUserMarker::validateStructure, CreateUserMarker::deserialize ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 247, + "ArgValuesResponse", + ArgValuesResponse.class, + 2, + 1677721600, + false, + ArgValuesResponse::validateStructure, + ArgValuesResponse::deserialize + ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 248, + "ArgCacheInvalidation", + ArgCacheInvalidation.class, + 1, + 1677721600, + false, + ArgCacheInvalidation::validateStructure, + ArgCacheInvalidation::deserialize + ); register( PacketRegistry.PacketDirection.ToClient, NetworkChannel.Default, @@ -2766,7 +2907,7 @@ public final class PacketRegistry { 316, "AssetEditorCreateAssetPack", AssetEditorCreateAssetPack.class, - 5, + 9, 1677721600, false, AssetEditorCreateAssetPack::validateStructure, @@ -3210,12 +3351,24 @@ public final class PacketRegistry { 355, "AssetEditorUpdateModelPreview", AssetEditorUpdateModelPreview.class, - 30, + 29, 1677721600, false, AssetEditorUpdateModelPreview::validateStructure, AssetEditorUpdateModelPreview::deserialize ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 356, + "AssetEditorModsDirectories", + AssetEditorModsDirectories.class, + 1, + 1677721600, + false, + AssetEditorModsDirectories::validateStructure, + AssetEditorModsDirectories::deserialize + ); register( PacketRegistry.PacketDirection.ToClient, NetworkChannel.Default, @@ -3240,14 +3393,26 @@ public final class PacketRegistry { UpdatePostFxSettings::validateStructure, UpdatePostFxSettings::deserialize ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 362, + "PlaySoundEventLocalPlayer", + PlaySoundEventLocalPlayer.class, + 17, + 17, + false, + PlaySoundEventLocalPlayer::validateStructure, + PlaySoundEventLocalPlayer::deserialize + ); register( PacketRegistry.PacketDirection.ToServer, NetworkChannel.Default, 400, "BuilderToolArgUpdate", BuilderToolArgUpdate.class, - 14, - 32768032, + 13, + 32768031, false, BuilderToolArgUpdate::validateStructure, BuilderToolArgUpdate::deserialize @@ -3379,7 +3544,7 @@ public final class PacketRegistry { "BuilderToolSelectionToolReplyWithClipboard", BuilderToolSelectionToolReplyWithClipboard.class, 1, - 139264019, + 1677721600, true, BuilderToolSelectionToolReplyWithClipboard::validateStructure, BuilderToolSelectionToolReplyWithClipboard::deserialize @@ -3402,8 +3567,8 @@ public final class PacketRegistry { 413, "BuilderToolOnUseInteraction", BuilderToolOnUseInteraction.class, - 57, - 57, + 61, + 61, false, BuilderToolOnUseInteraction::validateStructure, BuilderToolOnUseInteraction::deserialize @@ -3540,6 +3705,114 @@ public final class PacketRegistry { BuilderToolSetEntityCollision::validateStructure, BuilderToolSetEntityCollision::deserialize ); + register( + PacketRegistry.PacketDirection.ToServer, + NetworkChannel.Default, + 426, + "PrefabSetAnchor", + PrefabSetAnchor.class, + 12, + 12, + false, + PrefabSetAnchor::validateStructure, + PrefabSetAnchor::deserialize + ); + register( + PacketRegistry.PacketDirection.ToServer, + NetworkChannel.Default, + 427, + "BuilderToolResetClipboardRotation", + BuilderToolResetClipboardRotation.class, + 0, + 0, + false, + BuilderToolResetClipboardRotation::validateStructure, + BuilderToolResetClipboardRotation::deserialize + ); + register( + PacketRegistry.PacketDirection.ToServer, + NetworkChannel.Voice, + 450, + "VoiceData", + VoiceData.class, + 6, + 523, + false, + VoiceData::validateStructure, + VoiceData::deserialize + ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Voice, + 451, + "RelayedVoiceData", + RelayedVoiceData.class, + 52, + 569, + false, + RelayedVoiceData::validateStructure, + RelayedVoiceData::deserialize + ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 452, + "VoiceConfig", + VoiceConfig.class, + 17, + 17, + false, + VoiceConfig::validateStructure, + VoiceConfig::deserialize + ); + register( + PacketRegistry.PacketDirection.ToServer, + NetworkChannel.Default, + 460, + "StreamOpen", + StreamOpen.class, + 1, + 1, + false, + StreamOpen::validateStructure, + StreamOpen::deserialize + ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 461, + "StreamOpenResponse", + StreamOpenResponse.class, + 3, + 16384008, + false, + StreamOpenResponse::validateStructure, + StreamOpenResponse::deserialize + ); + register( + PacketRegistry.PacketDirection.ToClient, + NetworkChannel.Default, + 1200, + "UpdateServersideUIPage", + UpdateServersideUIPage.class, + 0, + 1677721600, + false, + UpdateServersideUIPage::validateStructure, + UpdateServersideUIPage::deserialize + ); + register( + PacketRegistry.PacketDirection.ToServer, + NetworkChannel.Default, + 1202, + "ExecuteServersidePageCommand", + ExecuteServersidePageCommand.class, + 1, + 1677721600, + false, + ExecuteServersidePageCommand::validateStructure, + ExecuteServersidePageCommand::deserialize + ); } public static enum PacketDirection { diff --git a/src/com/hypixel/hytale/protocol/ParallelInteraction.java b/src/com/hypixel/hytale/protocol/ParallelInteraction.java index 0c33d83c..9b596858 100644 --- a/src/com/hypixel/hytale/protocol/ParallelInteraction.java +++ b/src/com/hypixel/hytale/protocol/ParallelInteraction.java @@ -63,99 +63,133 @@ public class ParallelInteraction extends Interaction { @Nonnull public static ParallelInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ParallelInteraction obj = new ParallelInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 35 + buf.getIntLE(offset + 11); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 35) { + throw ProtocolException.bufferTooSmall("ParallelInteraction", 35, buf.readableBytes() - offset); + } else { + ParallelInteraction obj = new ParallelInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 11); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 35 + buf.getIntLE(offset + 15); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 35 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 15); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 35 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 19); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 35 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 23); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 35 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 27); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 35 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 31); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Next", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 35 + varPosBase5; + int nextCount = VarInt.peek(buf, varPos5); + if (nextCount < 0) { + throw ProtocolException.invalidVarInt("Next"); + } + + int varIntLenx = VarInt.size(nextCount); + if (nextCount > 4096000) { + throw ProtocolException.arrayTooLong("Next", nextCount, 4096000); + } + + if (varPos5 + varIntLenx + nextCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Next", varPos5 + varIntLenx + nextCount * 4, buf.readableBytes()); + } + + obj.next = new int[nextCount]; + + for (int ix = 0; ix < nextCount; ix++) { + obj.next[ix] = buf.getIntLE(varPos5 + varIntLenx + ix * 4); + } + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 35 + buf.getIntLE(offset + 19); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 35 + buf.getIntLE(offset + 23); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 35 + buf.getIntLE(offset + 27); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 35 + buf.getIntLE(offset + 31); - int nextCount = VarInt.peek(buf, varPos5); - if (nextCount < 0) { - throw ProtocolException.negativeLength("Next", nextCount); - } - - if (nextCount > 4096000) { - throw ProtocolException.arrayTooLong("Next", nextCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + nextCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Next", varPos5 + varIntLen + nextCount * 4, buf.readableBytes()); - } - - obj.next = new int[nextCount]; - - for (int ix = 0; ix < nextCount; ix++) { - obj.next[ix] = buf.getIntLE(varPos5 + varIntLen + ix * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -163,6 +197,10 @@ public class ParallelInteraction extends Interaction { int maxEnd = 35; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 11); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 35 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -172,9 +210,13 @@ public class ParallelInteraction extends Interaction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 15); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 35 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -187,6 +229,10 @@ public class ParallelInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 19); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 35 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -196,9 +242,13 @@ public class ParallelInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 23); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 35 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -206,6 +256,10 @@ public class ParallelInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 27); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 35 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -215,9 +269,13 @@ public class ParallelInteraction extends Interaction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 31); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Next", fieldOffset5, maxEnd); + } + int pos5 = offset + 35 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + arrLen * 4; + pos5 += VarInt.size(arrLen) + arrLen * 4; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -377,146 +435,132 @@ public class ParallelInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 35 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 11); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 11); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 35 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 35 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 35 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 35 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 35 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 35 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Next"); + } + + int posxx = offset + 35 + v; + int nextCount = VarInt.peek(buffer, posxx); + if (nextCount < 0) { + return ValidationResult.error("Invalid array count for Next"); + } + + if (nextCount > 4096000) { + return ValidationResult.error("Next exceeds max length 4096000"); + } + + posxx += VarInt.size(nextCount); + posxx += nextCount * 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Next"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 15); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 35 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 19); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 35 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 23); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 35 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 27); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 35 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int nextOffset = buffer.getIntLE(offset + 31); - if (nextOffset < 0) { - return ValidationResult.error("Invalid offset for Next"); - } - - int posxxxxx = offset + 35 + nextOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Next"); - } - - int nextCount = VarInt.peek(buffer, posxxxxx); - if (nextCount < 0) { - return ValidationResult.error("Invalid array count for Next"); - } - - if (nextCount > 4096000) { - return ValidationResult.error("Next exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += nextCount * 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Next"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ParamValue.java b/src/com/hypixel/hytale/protocol/ParamValue.java index f2d9fb1a..203181d4 100644 --- a/src/com/hypixel/hytale/protocol/ParamValue.java +++ b/src/com/hypixel/hytale/protocol/ParamValue.java @@ -12,7 +12,7 @@ public abstract class ParamValue { @Nonnull public static ParamValue deserialize(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return (ParamValue)(switch (typeId) { case 0 -> StringParamValue.deserialize(buf, offset + typeIdLen); @@ -26,7 +26,7 @@ public abstract class ParamValue { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return typeIdLen + switch (typeId) { case 0 -> StringParamValue.computeBytesConsumed(buf, offset + typeIdLen); @@ -71,7 +71,7 @@ public abstract class ParamValue { public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { int typeId = VarInt.peek(buffer, offset); - int typeIdLen = VarInt.length(buffer, offset); + int typeIdLen = VarInt.size(typeId); return switch (typeId) { case 0 -> StringParamValue.validateStructure(buffer, offset + typeIdLen); diff --git a/src/com/hypixel/hytale/protocol/Particle.java b/src/com/hypixel/hytale/protocol/Particle.java index c3e19462..65fee4b9 100644 --- a/src/com/hypixel/hytale/protocol/Particle.java +++ b/src/com/hypixel/hytale/protocol/Particle.java @@ -79,66 +79,85 @@ public class Particle { @Nonnull public static Particle deserialize(@Nonnull ByteBuf buf, int offset) { - Particle obj = new Particle(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.frameSize = Size.deserialize(buf, offset + 1); - } - - obj.uvOption = ParticleUVOption.fromValue(buf.getByte(offset + 9)); - obj.scaleRatioConstraint = ParticleScaleRatioConstraint.fromValue(buf.getByte(offset + 10)); - obj.softParticles = SoftParticle.fromValue(buf.getByte(offset + 11)); - obj.softParticlesFadeFactor = buf.getFloatLE(offset + 12); - obj.useSpriteBlending = buf.getByte(offset + 16) != 0; - if ((nullBits & 2) != 0) { - obj.initialAnimationFrame = ParticleAnimationFrame.deserialize(buf, offset + 17); - } - - if ((nullBits & 4) != 0) { - obj.collisionAnimationFrame = ParticleAnimationFrame.deserialize(buf, offset + 75); - } - - if ((nullBits & 8) != 0) { - int varPos0 = offset + 141 + buf.getIntLE(offset + 133); - int texturePathLen = VarInt.peek(buf, varPos0); - if (texturePathLen < 0) { - throw ProtocolException.negativeLength("TexturePath", texturePathLen); + if (buf.readableBytes() - offset < 141) { + throw ProtocolException.bufferTooSmall("Particle", 141, buf.readableBytes() - offset); + } else { + Particle obj = new Particle(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.frameSize = Size.deserialize(buf, offset + 1); } - if (texturePathLen > 4096000) { - throw ProtocolException.stringTooLong("TexturePath", texturePathLen, 4096000); + obj.uvOption = ParticleUVOption.fromValue(buf.getByte(offset + 9)); + obj.scaleRatioConstraint = ParticleScaleRatioConstraint.fromValue(buf.getByte(offset + 10)); + obj.softParticles = SoftParticle.fromValue(buf.getByte(offset + 11)); + obj.softParticlesFadeFactor = buf.getFloatLE(offset + 12); + obj.useSpriteBlending = buf.getByte(offset + 16) != 0; + if ((nullBits & 2) != 0) { + obj.initialAnimationFrame = ParticleAnimationFrame.deserialize(buf, offset + 17); } - obj.texturePath = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 16) != 0) { - int varPos1 = offset + 141 + buf.getIntLE(offset + 137); - int animationFramesCount = VarInt.peek(buf, varPos1); - if (animationFramesCount < 0) { - throw ProtocolException.negativeLength("AnimationFrames", animationFramesCount); + if ((nullBits & 4) != 0) { + obj.collisionAnimationFrame = ParticleAnimationFrame.deserialize(buf, offset + 75); } - if (animationFramesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("AnimationFrames", animationFramesCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase0 = buf.getIntLE(offset + 133); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 141) { + throw ProtocolException.invalidOffset("TexturePath", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 141 + varPosBase0; + int texturePathLen = VarInt.peek(buf, varPos0); + if (texturePathLen < 0) { + throw ProtocolException.invalidVarInt("TexturePath"); + } + + int texturePathVarIntLen = VarInt.size(texturePathLen); + if (texturePathLen > 4096000) { + throw ProtocolException.stringTooLong("TexturePath", texturePathLen, 4096000); + } + + if (varPos0 + texturePathVarIntLen + texturePathLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TexturePath", varPos0 + texturePathVarIntLen + texturePathLen, buf.readableBytes()); + } + + obj.texturePath = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - int varIntLen = VarInt.length(buf, varPos1); - obj.animationFrames = new HashMap<>(animationFramesCount); - int dictPos = varPos1 + varIntLen; + if ((nullBits & 16) != 0) { + int varPosBase1 = buf.getIntLE(offset + 137); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 141) { + throw ProtocolException.invalidOffset("AnimationFrames", varPosBase1, buf.readableBytes()); + } - for (int i = 0; i < animationFramesCount; i++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - ParticleAnimationFrame val = ParticleAnimationFrame.deserialize(buf, dictPos); - dictPos += ParticleAnimationFrame.computeBytesConsumed(buf, dictPos); - if (obj.animationFrames.put(key, val) != null) { - throw ProtocolException.duplicateKey("animationFrames", key); + int varPos1 = offset + 141 + varPosBase1; + int animationFramesCount = VarInt.peek(buf, varPos1); + if (animationFramesCount < 0) { + throw ProtocolException.invalidVarInt("AnimationFrames"); + } + + int varIntLen = VarInt.size(animationFramesCount); + if (animationFramesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("AnimationFrames", animationFramesCount, 4096000); + } + + obj.animationFrames = new HashMap<>(animationFramesCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < animationFramesCount; i++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + ParticleAnimationFrame val = ParticleAnimationFrame.deserialize(buf, dictPos); + dictPos += ParticleAnimationFrame.computeBytesConsumed(buf, dictPos); + if (obj.animationFrames.put(key, val) != null) { + throw ProtocolException.duplicateKey("animationFrames", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -146,9 +165,13 @@ public class Particle { int maxEnd = 141; if ((nullBits & 8) != 0) { int fieldOffset0 = buf.getIntLE(offset + 133); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 141) { + throw ProtocolException.invalidOffset("TexturePath", fieldOffset0, maxEnd); + } + int pos0 = offset + 141 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -156,9 +179,13 @@ public class Particle { if ((nullBits & 16) != 0) { int fieldOffset1 = buf.getIntLE(offset + 137); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 141) { + throw ProtocolException.invalidOffset("AnimationFrames", fieldOffset1, maxEnd); + } + int pos1 = offset + 141 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 += 4; @@ -267,66 +294,73 @@ public class Particle { return ValidationResult.error("Buffer too small: expected at least 141 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 8) != 0) { - int texturePathOffset = buffer.getIntLE(offset + 133); - if (texturePathOffset < 0) { - return ValidationResult.error("Invalid offset for TexturePath"); - } + int v = buffer.getByte(offset + 9) & 255; + if (v >= 7) { + return ValidationResult.error("Invalid ParticleUVOption value for UvOption"); + } else { + v = buffer.getByte(offset + 10) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid ParticleScaleRatioConstraint value for ScaleRatioConstraint"); + } else { + v = buffer.getByte(offset + 11) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid SoftParticle value for SoftParticles"); + } else { + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 133); + if (v < 0 || v > buffer.writerIndex() - offset - 141) { + return ValidationResult.error("Invalid offset for TexturePath"); + } - int pos = offset + 141 + texturePathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TexturePath"); - } + int pos = offset + 141 + v; + int texturePathLen = VarInt.peek(buffer, pos); + if (texturePathLen < 0) { + return ValidationResult.error("Invalid string length for TexturePath"); + } - int texturePathLen = VarInt.peek(buffer, pos); - if (texturePathLen < 0) { - return ValidationResult.error("Invalid string length for TexturePath"); - } + if (texturePathLen > 4096000) { + return ValidationResult.error("TexturePath exceeds max length 4096000"); + } - if (texturePathLen > 4096000) { - return ValidationResult.error("TexturePath exceeds max length 4096000"); - } + pos += VarInt.size(texturePathLen); + pos += texturePathLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TexturePath"); + } + } - pos += VarInt.length(buffer, pos); - pos += texturePathLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading TexturePath"); - } - } + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 137); + if (v < 0 || v > buffer.writerIndex() - offset - 141) { + return ValidationResult.error("Invalid offset for AnimationFrames"); + } - if ((nullBits & 16) != 0) { - int animationFramesOffset = buffer.getIntLE(offset + 137); - if (animationFramesOffset < 0) { - return ValidationResult.error("Invalid offset for AnimationFrames"); - } + int posx = offset + 141 + v; + int animationFramesCount = VarInt.peek(buffer, posx); + if (animationFramesCount < 0) { + return ValidationResult.error("Invalid dictionary count for AnimationFrames"); + } - int posx = offset + 141 + animationFramesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AnimationFrames"); - } + if (animationFramesCount > 4096000) { + return ValidationResult.error("AnimationFrames exceeds max length 4096000"); + } - int animationFramesCount = VarInt.peek(buffer, posx); - if (animationFramesCount < 0) { - return ValidationResult.error("Invalid dictionary count for AnimationFrames"); - } + posx += VarInt.size(animationFramesCount); - if (animationFramesCount > 4096000) { - return ValidationResult.error("AnimationFrames exceeds max length 4096000"); - } + for (int i = 0; i < animationFramesCount; i++) { + posx += 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } - posx += VarInt.length(buffer, posx); + posx += 58; + } + } - for (int i = 0; i < animationFramesCount; i++) { - posx += 4; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + return ValidationResult.OK; } - - posx += 58; } } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ParticleAnimationFrame.java b/src/com/hypixel/hytale/protocol/ParticleAnimationFrame.java index 7baf615e..0298a16c 100644 --- a/src/com/hypixel/hytale/protocol/ParticleAnimationFrame.java +++ b/src/com/hypixel/hytale/protocol/ParticleAnimationFrame.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -45,26 +46,30 @@ public class ParticleAnimationFrame { @Nonnull public static ParticleAnimationFrame deserialize(@Nonnull ByteBuf buf, int offset) { - ParticleAnimationFrame obj = new ParticleAnimationFrame(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.frameIndex = Range.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 58) { + throw ProtocolException.bufferTooSmall("ParticleAnimationFrame", 58, buf.readableBytes() - offset); + } else { + ParticleAnimationFrame obj = new ParticleAnimationFrame(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.frameIndex = Range.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.scale = RangeVector2f.deserialize(buf, offset + 9); - } + if ((nullBits & 2) != 0) { + obj.scale = RangeVector2f.deserialize(buf, offset + 9); + } - if ((nullBits & 4) != 0) { - obj.rotation = RangeVector3f.deserialize(buf, offset + 26); - } + if ((nullBits & 4) != 0) { + obj.rotation = RangeVector3f.deserialize(buf, offset + 26); + } - if ((nullBits & 8) != 0) { - obj.color = Color.deserialize(buf, offset + 51); - } + if ((nullBits & 8) != 0) { + obj.color = Color.deserialize(buf, offset + 51); + } - obj.opacity = buf.getFloatLE(offset + 54); - return obj; + obj.opacity = buf.getFloatLE(offset + 54); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -122,7 +127,12 @@ public class ParticleAnimationFrame { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 58 ? ValidationResult.error("Buffer too small: expected at least 58 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 58) { + return ValidationResult.error("Buffer too small: expected at least 58 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public ParticleAnimationFrame clone() { diff --git a/src/com/hypixel/hytale/protocol/ParticleAttractor.java b/src/com/hypixel/hytale/protocol/ParticleAttractor.java index da861a11..126cede7 100644 --- a/src/com/hypixel/hytale/protocol/ParticleAttractor.java +++ b/src/com/hypixel/hytale/protocol/ParticleAttractor.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class ParticleAttractor { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -13,37 +16,37 @@ public class ParticleAttractor { public static final int VARIABLE_BLOCK_START = 85; public static final int MAX_SIZE = 85; @Nullable - public Vector3f position; + public Vector3fc position; @Nullable - public Vector3f radialAxis; + public Vector3fc radialAxis; public float trailPositionMultiplier; public float radius; public float radialAcceleration; public float radialTangentAcceleration; @Nullable - public Vector3f linearAcceleration; + public Vector3fc linearAcceleration; public float radialImpulse; public float radialTangentImpulse; @Nullable - public Vector3f linearImpulse; + public Vector3fc linearImpulse; @Nullable - public Vector3f dampingMultiplier; + public Vector3fc dampingMultiplier; public ParticleAttractor() { } public ParticleAttractor( - @Nullable Vector3f position, - @Nullable Vector3f radialAxis, + @Nullable Vector3fc position, + @Nullable Vector3fc radialAxis, float trailPositionMultiplier, float radius, float radialAcceleration, float radialTangentAcceleration, - @Nullable Vector3f linearAcceleration, + @Nullable Vector3fc linearAcceleration, float radialImpulse, float radialTangentImpulse, - @Nullable Vector3f linearImpulse, - @Nullable Vector3f dampingMultiplier + @Nullable Vector3fc linearImpulse, + @Nullable Vector3fc dampingMultiplier ) { this.position = position; this.radialAxis = radialAxis; @@ -74,35 +77,39 @@ public class ParticleAttractor { @Nonnull public static ParticleAttractor deserialize(@Nonnull ByteBuf buf, int offset) { - ParticleAttractor obj = new ParticleAttractor(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.position = Vector3f.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 85) { + throw ProtocolException.bufferTooSmall("ParticleAttractor", 85, buf.readableBytes() - offset); + } else { + ParticleAttractor obj = new ParticleAttractor(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.position = PacketIO.readVector3f(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.radialAxis = Vector3f.deserialize(buf, offset + 13); - } + if ((nullBits & 2) != 0) { + obj.radialAxis = PacketIO.readVector3f(buf, offset + 13); + } - obj.trailPositionMultiplier = buf.getFloatLE(offset + 25); - obj.radius = buf.getFloatLE(offset + 29); - obj.radialAcceleration = buf.getFloatLE(offset + 33); - obj.radialTangentAcceleration = buf.getFloatLE(offset + 37); - if ((nullBits & 4) != 0) { - obj.linearAcceleration = Vector3f.deserialize(buf, offset + 41); - } + obj.trailPositionMultiplier = buf.getFloatLE(offset + 25); + obj.radius = buf.getFloatLE(offset + 29); + obj.radialAcceleration = buf.getFloatLE(offset + 33); + obj.radialTangentAcceleration = buf.getFloatLE(offset + 37); + if ((nullBits & 4) != 0) { + obj.linearAcceleration = PacketIO.readVector3f(buf, offset + 41); + } - obj.radialImpulse = buf.getFloatLE(offset + 53); - obj.radialTangentImpulse = buf.getFloatLE(offset + 57); - if ((nullBits & 8) != 0) { - obj.linearImpulse = Vector3f.deserialize(buf, offset + 61); - } + obj.radialImpulse = buf.getFloatLE(offset + 53); + obj.radialTangentImpulse = buf.getFloatLE(offset + 57); + if ((nullBits & 8) != 0) { + obj.linearImpulse = PacketIO.readVector3f(buf, offset + 61); + } - if ((nullBits & 16) != 0) { - obj.dampingMultiplier = Vector3f.deserialize(buf, offset + 73); - } + if ((nullBits & 16) != 0) { + obj.dampingMultiplier = PacketIO.readVector3f(buf, offset + 73); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -133,13 +140,13 @@ public class ParticleAttractor { buf.writeByte(nullBits); if (this.position != null) { - this.position.serialize(buf); + PacketIO.writeVector3f(buf, this.position); } else { buf.writeZero(12); } if (this.radialAxis != null) { - this.radialAxis.serialize(buf); + PacketIO.writeVector3f(buf, this.radialAxis); } else { buf.writeZero(12); } @@ -149,7 +156,7 @@ public class ParticleAttractor { buf.writeFloatLE(this.radialAcceleration); buf.writeFloatLE(this.radialTangentAcceleration); if (this.linearAcceleration != null) { - this.linearAcceleration.serialize(buf); + PacketIO.writeVector3f(buf, this.linearAcceleration); } else { buf.writeZero(12); } @@ -157,13 +164,13 @@ public class ParticleAttractor { buf.writeFloatLE(this.radialImpulse); buf.writeFloatLE(this.radialTangentImpulse); if (this.linearImpulse != null) { - this.linearImpulse.serialize(buf); + PacketIO.writeVector3f(buf, this.linearImpulse); } else { buf.writeZero(12); } if (this.dampingMultiplier != null) { - this.dampingMultiplier.serialize(buf); + PacketIO.writeVector3f(buf, this.dampingMultiplier); } else { buf.writeZero(12); } @@ -174,22 +181,27 @@ public class ParticleAttractor { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 85 ? ValidationResult.error("Buffer too small: expected at least 85 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 85) { + return ValidationResult.error("Buffer too small: expected at least 85 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public ParticleAttractor clone() { ParticleAttractor copy = new ParticleAttractor(); - copy.position = this.position != null ? this.position.clone() : null; - copy.radialAxis = this.radialAxis != null ? this.radialAxis.clone() : null; + copy.position = this.position; + copy.radialAxis = this.radialAxis; copy.trailPositionMultiplier = this.trailPositionMultiplier; copy.radius = this.radius; copy.radialAcceleration = this.radialAcceleration; copy.radialTangentAcceleration = this.radialTangentAcceleration; - copy.linearAcceleration = this.linearAcceleration != null ? this.linearAcceleration.clone() : null; + copy.linearAcceleration = this.linearAcceleration; copy.radialImpulse = this.radialImpulse; copy.radialTangentImpulse = this.radialTangentImpulse; - copy.linearImpulse = this.linearImpulse != null ? this.linearImpulse.clone() : null; - copy.dampingMultiplier = this.dampingMultiplier != null ? this.dampingMultiplier.clone() : null; + copy.linearImpulse = this.linearImpulse; + copy.dampingMultiplier = this.dampingMultiplier; return copy; } diff --git a/src/com/hypixel/hytale/protocol/ParticleCollision.java b/src/com/hypixel/hytale/protocol/ParticleCollision.java index 57328293..e5bd5c9f 100644 --- a/src/com/hypixel/hytale/protocol/ParticleCollision.java +++ b/src/com/hypixel/hytale/protocol/ParticleCollision.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -37,11 +38,15 @@ public class ParticleCollision { @Nonnull public static ParticleCollision deserialize(@Nonnull ByteBuf buf, int offset) { - ParticleCollision obj = new ParticleCollision(); - obj.blockType = ParticleCollisionBlockType.fromValue(buf.getByte(offset + 0)); - obj.action = ParticleCollisionAction.fromValue(buf.getByte(offset + 1)); - obj.particleRotationInfluence = ParticleRotationInfluence.fromValue(buf.getByte(offset + 2)); - return obj; + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("ParticleCollision", 3, buf.readableBytes() - offset); + } else { + ParticleCollision obj = new ParticleCollision(); + obj.blockType = ParticleCollisionBlockType.fromValue(buf.getByte(offset + 0)); + obj.action = ParticleCollisionAction.fromValue(buf.getByte(offset + 1)); + obj.particleRotationInfluence = ParticleRotationInfluence.fromValue(buf.getByte(offset + 2)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -59,7 +64,22 @@ public class ParticleCollision { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 3 ? ValidationResult.error("Buffer too small: expected at least 3 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 3) { + return ValidationResult.error("Buffer too small: expected at least 3 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid ParticleCollisionBlockType value for BlockType"); + } else { + v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid ParticleCollisionAction value for Action"); + } else { + v = buffer.getByte(offset + 2) & 255; + return v >= 5 ? ValidationResult.error("Invalid ParticleRotationInfluence value for ParticleRotationInfluence") : ValidationResult.OK; + } + } + } } public ParticleCollision clone() { diff --git a/src/com/hypixel/hytale/protocol/ParticleSpawner.java b/src/com/hypixel/hytale/protocol/ParticleSpawner.java index c7897e12..34eb6a57 100644 --- a/src/com/hypixel/hytale/protocol/ParticleSpawner.java +++ b/src/com/hypixel/hytale/protocol/ParticleSpawner.java @@ -152,105 +152,134 @@ public class ParticleSpawner { @Nonnull public static ParticleSpawner deserialize(@Nonnull ByteBuf buf, int offset) { - ParticleSpawner obj = new ParticleSpawner(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.shape = EmitShape.fromValue(buf.getByte(offset + 2)); - if ((nullBits[0] & 1) != 0) { - obj.emitOffset = RangeVector3f.deserialize(buf, offset + 3); - } - - obj.cameraOffset = buf.getFloatLE(offset + 28); - obj.useEmitDirection = buf.getByte(offset + 32) != 0; - obj.lifeSpan = buf.getFloatLE(offset + 33); - if ((nullBits[0] & 2) != 0) { - obj.spawnRate = Rangef.deserialize(buf, offset + 37); - } - - obj.spawnBurst = buf.getByte(offset + 45) != 0; - if ((nullBits[0] & 4) != 0) { - obj.waveDelay = Rangef.deserialize(buf, offset + 46); - } - - if ((nullBits[0] & 8) != 0) { - obj.totalParticles = Range.deserialize(buf, offset + 54); - } - - obj.maxConcurrentParticles = buf.getIntLE(offset + 62); - if ((nullBits[0] & 16) != 0) { - obj.initialVelocity = InitialVelocity.deserialize(buf, offset + 66); - } - - obj.velocityStretchMultiplier = buf.getFloatLE(offset + 91); - obj.particleRotationInfluence = ParticleRotationInfluence.fromValue(buf.getByte(offset + 95)); - obj.particleRotateWithSpawner = buf.getByte(offset + 96) != 0; - obj.isLowRes = buf.getByte(offset + 97) != 0; - obj.trailSpawnerPositionMultiplier = buf.getFloatLE(offset + 98); - obj.trailSpawnerRotationMultiplier = buf.getFloatLE(offset + 102); - if ((nullBits[0] & 32) != 0) { - obj.particleCollision = ParticleCollision.deserialize(buf, offset + 106); - } - - obj.renderMode = FXRenderMode.fromValue(buf.getByte(offset + 109)); - obj.lightInfluence = buf.getFloatLE(offset + 110); - obj.linearFiltering = buf.getByte(offset + 114) != 0; - if ((nullBits[0] & 64) != 0) { - obj.particleLifeSpan = Rangef.deserialize(buf, offset + 115); - } - - if ((nullBits[0] & 128) != 0) { - obj.intersectionHighlight = IntersectionHighlight.deserialize(buf, offset + 123); - } - - if ((nullBits[1] & 1) != 0) { - int varPos0 = offset + 147 + buf.getIntLE(offset + 131); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 147) { + throw ProtocolException.bufferTooSmall("ParticleSpawner", 147, buf.readableBytes() - offset); + } else { + ParticleSpawner obj = new ParticleSpawner(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + obj.shape = EmitShape.fromValue(buf.getByte(offset + 2)); + if ((nullBits[0] & 1) != 0) { + obj.emitOffset = RangeVector3f.deserialize(buf, offset + 3); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + obj.cameraOffset = buf.getFloatLE(offset + 28); + obj.useEmitDirection = buf.getByte(offset + 32) != 0; + obj.lifeSpan = buf.getFloatLE(offset + 33); + if ((nullBits[0] & 2) != 0) { + obj.spawnRate = Rangef.deserialize(buf, offset + 37); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + obj.spawnBurst = buf.getByte(offset + 45) != 0; + if ((nullBits[0] & 4) != 0) { + obj.waveDelay = Rangef.deserialize(buf, offset + 46); + } + + if ((nullBits[0] & 8) != 0) { + obj.totalParticles = Range.deserialize(buf, offset + 54); + } + + obj.maxConcurrentParticles = buf.getIntLE(offset + 62); + if ((nullBits[0] & 16) != 0) { + obj.initialVelocity = InitialVelocity.deserialize(buf, offset + 66); + } + + obj.velocityStretchMultiplier = buf.getFloatLE(offset + 91); + obj.particleRotationInfluence = ParticleRotationInfluence.fromValue(buf.getByte(offset + 95)); + obj.particleRotateWithSpawner = buf.getByte(offset + 96) != 0; + obj.isLowRes = buf.getByte(offset + 97) != 0; + obj.trailSpawnerPositionMultiplier = buf.getFloatLE(offset + 98); + obj.trailSpawnerRotationMultiplier = buf.getFloatLE(offset + 102); + if ((nullBits[0] & 32) != 0) { + obj.particleCollision = ParticleCollision.deserialize(buf, offset + 106); + } + + obj.renderMode = FXRenderMode.fromValue(buf.getByte(offset + 109)); + obj.lightInfluence = buf.getFloatLE(offset + 110); + obj.linearFiltering = buf.getByte(offset + 114) != 0; + if ((nullBits[0] & 64) != 0) { + obj.particleLifeSpan = Rangef.deserialize(buf, offset + 115); + } + + if ((nullBits[0] & 128) != 0) { + obj.intersectionHighlight = IntersectionHighlight.deserialize(buf, offset + 123); + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 131); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 147) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 147 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits[1] & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 135); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 147) { + throw ProtocolException.invalidOffset("Particle", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 147 + varPosBase1; + obj.particle = Particle.deserialize(buf, varPos1); + } + + if ((nullBits[1] & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 139); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 147) { + throw ProtocolException.invalidOffset("UvMotion", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 147 + varPosBase2; + obj.uvMotion = UVMotion.deserialize(buf, varPos2); + } + + if ((nullBits[1] & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 143); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 147) { + throw ProtocolException.invalidOffset("Attractors", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 147 + varPosBase3; + int attractorsCount = VarInt.peek(buf, varPos3); + if (attractorsCount < 0) { + throw ProtocolException.invalidVarInt("Attractors"); + } + + int varIntLen = VarInt.size(attractorsCount); + if (attractorsCount > 4096000) { + throw ProtocolException.arrayTooLong("Attractors", attractorsCount, 4096000); + } + + if (varPos3 + varIntLen + attractorsCount * 85L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Attractors", varPos3 + varIntLen + attractorsCount * 85, buf.readableBytes()); + } + + obj.attractors = new ParticleAttractor[attractorsCount]; + int elemPos = varPos3 + varIntLen; + + for (int i = 0; i < attractorsCount; i++) { + obj.attractors[i] = ParticleAttractor.deserialize(buf, elemPos); + elemPos += ParticleAttractor.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits[1] & 2) != 0) { - int varPos1 = offset + 147 + buf.getIntLE(offset + 135); - obj.particle = Particle.deserialize(buf, varPos1); - } - - if ((nullBits[1] & 4) != 0) { - int varPos2 = offset + 147 + buf.getIntLE(offset + 139); - obj.uvMotion = UVMotion.deserialize(buf, varPos2); - } - - if ((nullBits[1] & 8) != 0) { - int varPos3 = offset + 147 + buf.getIntLE(offset + 143); - int attractorsCount = VarInt.peek(buf, varPos3); - if (attractorsCount < 0) { - throw ProtocolException.negativeLength("Attractors", attractorsCount); - } - - if (attractorsCount > 4096000) { - throw ProtocolException.arrayTooLong("Attractors", attractorsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + attractorsCount * 85L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Attractors", varPos3 + varIntLen + attractorsCount * 85, buf.readableBytes()); - } - - obj.attractors = new ParticleAttractor[attractorsCount]; - int elemPos = varPos3 + varIntLen; - - for (int i = 0; i < attractorsCount; i++) { - obj.attractors[i] = ParticleAttractor.deserialize(buf, elemPos); - elemPos += ParticleAttractor.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -258,9 +287,13 @@ public class ParticleSpawner { int maxEnd = 147; if ((nullBits[1] & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 131); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 147) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 147 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -268,6 +301,10 @@ public class ParticleSpawner { if ((nullBits[1] & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 135); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 147) { + throw ProtocolException.invalidOffset("Particle", fieldOffset1, maxEnd); + } + int pos1 = offset + 147 + fieldOffset1; pos1 += Particle.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -277,6 +314,10 @@ public class ParticleSpawner { if ((nullBits[1] & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 139); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 147) { + throw ProtocolException.invalidOffset("UvMotion", fieldOffset2, maxEnd); + } + int pos2 = offset + 147 + fieldOffset2; pos2 += UVMotion.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -286,9 +327,13 @@ public class ParticleSpawner { if ((nullBits[1] & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 143); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 147) { + throw ProtocolException.invalidOffset("Attractors", fieldOffset3, maxEnd); + } + int pos3 = offset + 147 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos3 += ParticleAttractor.computeBytesConsumed(buf, pos3); @@ -489,99 +534,98 @@ public class ParticleSpawner { return ValidationResult.error("Buffer too small: expected at least 147 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[1] & 1) != 0) { - int idOffset = buffer.getIntLE(offset + 131); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); - } + int v = buffer.getByte(offset + 2) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid EmitShape value for Shape"); + } else { + v = buffer.getByte(offset + 95) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid ParticleRotationInfluence value for ParticleRotationInfluence"); + } else { + v = buffer.getByte(offset + 109) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid FXRenderMode value for RenderMode"); + } else { + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 131); + if (v < 0 || v > buffer.writerIndex() - offset - 147) { + return ValidationResult.error("Invalid offset for Id"); + } - int pos = offset + 147 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } + int pos = offset + 147 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } + } - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); + if ((nullBits[1] & 2) != 0) { + v = buffer.getIntLE(offset + 135); + if (v < 0 || v > buffer.writerIndex() - offset - 147) { + return ValidationResult.error("Invalid offset for Particle"); + } + + int posx = offset + 147 + v; + ValidationResult particleResult = Particle.validateStructure(buffer, posx); + if (!particleResult.isValid()) { + return ValidationResult.error("Invalid Particle: " + particleResult.error()); + } + + posx += Particle.computeBytesConsumed(buffer, posx); + } + + if ((nullBits[1] & 4) != 0) { + v = buffer.getIntLE(offset + 139); + if (v < 0 || v > buffer.writerIndex() - offset - 147) { + return ValidationResult.error("Invalid offset for UvMotion"); + } + + int posx = offset + 147 + v; + ValidationResult uvMotionResult = UVMotion.validateStructure(buffer, posx); + if (!uvMotionResult.isValid()) { + return ValidationResult.error("Invalid UvMotion: " + uvMotionResult.error()); + } + + posx += UVMotion.computeBytesConsumed(buffer, posx); + } + + if ((nullBits[1] & 8) != 0) { + v = buffer.getIntLE(offset + 143); + if (v < 0 || v > buffer.writerIndex() - offset - 147) { + return ValidationResult.error("Invalid offset for Attractors"); + } + + int posx = offset + 147 + v; + int attractorsCount = VarInt.peek(buffer, posx); + if (attractorsCount < 0) { + return ValidationResult.error("Invalid array count for Attractors"); + } + + if (attractorsCount > 4096000) { + return ValidationResult.error("Attractors exceeds max length 4096000"); + } + + posx += VarInt.size(attractorsCount); + posx += attractorsCount * 85; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Attractors"); + } + } + + return ValidationResult.OK; + } } } - - if ((nullBits[1] & 2) != 0) { - int particleOffset = buffer.getIntLE(offset + 135); - if (particleOffset < 0) { - return ValidationResult.error("Invalid offset for Particle"); - } - - int posx = offset + 147 + particleOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particle"); - } - - ValidationResult particleResult = Particle.validateStructure(buffer, posx); - if (!particleResult.isValid()) { - return ValidationResult.error("Invalid Particle: " + particleResult.error()); - } - - posx += Particle.computeBytesConsumed(buffer, posx); - } - - if ((nullBits[1] & 4) != 0) { - int uvMotionOffset = buffer.getIntLE(offset + 139); - if (uvMotionOffset < 0) { - return ValidationResult.error("Invalid offset for UvMotion"); - } - - int posxx = offset + 147 + uvMotionOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for UvMotion"); - } - - ValidationResult uvMotionResult = UVMotion.validateStructure(buffer, posxx); - if (!uvMotionResult.isValid()) { - return ValidationResult.error("Invalid UvMotion: " + uvMotionResult.error()); - } - - posxx += UVMotion.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits[1] & 8) != 0) { - int attractorsOffset = buffer.getIntLE(offset + 143); - if (attractorsOffset < 0) { - return ValidationResult.error("Invalid offset for Attractors"); - } - - int posxxx = offset + 147 + attractorsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Attractors"); - } - - int attractorsCount = VarInt.peek(buffer, posxxx); - if (attractorsCount < 0) { - return ValidationResult.error("Invalid array count for Attractors"); - } - - if (attractorsCount > 4096000) { - return ValidationResult.error("Attractors exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += attractorsCount * 85; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Attractors"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ParticleSpawnerGroup.java b/src/com/hypixel/hytale/protocol/ParticleSpawnerGroup.java index 0eb8cc25..d50de562 100644 --- a/src/com/hypixel/hytale/protocol/ParticleSpawnerGroup.java +++ b/src/com/hypixel/hytale/protocol/ParticleSpawnerGroup.java @@ -9,6 +9,7 @@ import java.util.Arrays; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class ParticleSpawnerGroup { public static final int NULLABLE_BIT_FIELD_SIZE = 2; @@ -19,7 +20,7 @@ public class ParticleSpawnerGroup { @Nullable public String spawnerId; @Nullable - public Vector3f positionOffset; + public Vector3fc positionOffset; @Nullable public Direction rotationOffset; public boolean fixedRotation; @@ -44,7 +45,7 @@ public class ParticleSpawnerGroup { public ParticleSpawnerGroup( @Nullable String spawnerId, - @Nullable Vector3f positionOffset, + @Nullable Vector3fc positionOffset, @Nullable Direction rotationOffset, boolean fixedRotation, float startDelay, @@ -90,80 +91,99 @@ public class ParticleSpawnerGroup { @Nonnull public static ParticleSpawnerGroup deserialize(@Nonnull ByteBuf buf, int offset) { - ParticleSpawnerGroup obj = new ParticleSpawnerGroup(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - if ((nullBits[0] & 1) != 0) { - obj.positionOffset = Vector3f.deserialize(buf, offset + 2); - } - - if ((nullBits[0] & 2) != 0) { - obj.rotationOffset = Direction.deserialize(buf, offset + 14); - } - - obj.fixedRotation = buf.getByte(offset + 26) != 0; - obj.startDelay = buf.getFloatLE(offset + 27); - if ((nullBits[0] & 4) != 0) { - obj.spawnRate = Rangef.deserialize(buf, offset + 31); - } - - if ((nullBits[0] & 8) != 0) { - obj.waveDelay = Rangef.deserialize(buf, offset + 39); - } - - obj.totalSpawners = buf.getIntLE(offset + 47); - obj.maxConcurrent = buf.getIntLE(offset + 51); - if ((nullBits[0] & 16) != 0) { - obj.initialVelocity = InitialVelocity.deserialize(buf, offset + 55); - } - - if ((nullBits[0] & 32) != 0) { - obj.emitOffset = RangeVector3f.deserialize(buf, offset + 80); - } - - if ((nullBits[0] & 64) != 0) { - obj.lifeSpan = Rangef.deserialize(buf, offset + 105); - } - - if ((nullBits[0] & 128) != 0) { - int varPos0 = offset + 121 + buf.getIntLE(offset + 113); - int spawnerIdLen = VarInt.peek(buf, varPos0); - if (spawnerIdLen < 0) { - throw ProtocolException.negativeLength("SpawnerId", spawnerIdLen); + if (buf.readableBytes() - offset < 121) { + throw ProtocolException.bufferTooSmall("ParticleSpawnerGroup", 121, buf.readableBytes() - offset); + } else { + ParticleSpawnerGroup obj = new ParticleSpawnerGroup(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + if ((nullBits[0] & 1) != 0) { + obj.positionOffset = PacketIO.readVector3f(buf, offset + 2); } - if (spawnerIdLen > 4096000) { - throw ProtocolException.stringTooLong("SpawnerId", spawnerIdLen, 4096000); + if ((nullBits[0] & 2) != 0) { + obj.rotationOffset = Direction.deserialize(buf, offset + 14); } - obj.spawnerId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + obj.fixedRotation = buf.getByte(offset + 26) != 0; + obj.startDelay = buf.getFloatLE(offset + 27); + if ((nullBits[0] & 4) != 0) { + obj.spawnRate = Rangef.deserialize(buf, offset + 31); + } + + if ((nullBits[0] & 8) != 0) { + obj.waveDelay = Rangef.deserialize(buf, offset + 39); + } + + obj.totalSpawners = buf.getIntLE(offset + 47); + obj.maxConcurrent = buf.getIntLE(offset + 51); + if ((nullBits[0] & 16) != 0) { + obj.initialVelocity = InitialVelocity.deserialize(buf, offset + 55); + } + + if ((nullBits[0] & 32) != 0) { + obj.emitOffset = RangeVector3f.deserialize(buf, offset + 80); + } + + if ((nullBits[0] & 64) != 0) { + obj.lifeSpan = Rangef.deserialize(buf, offset + 105); + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase0 = buf.getIntLE(offset + 113); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 121) { + throw ProtocolException.invalidOffset("SpawnerId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 121 + varPosBase0; + int spawnerIdLen = VarInt.peek(buf, varPos0); + if (spawnerIdLen < 0) { + throw ProtocolException.invalidVarInt("SpawnerId"); + } + + int spawnerIdVarIntLen = VarInt.size(spawnerIdLen); + if (spawnerIdLen > 4096000) { + throw ProtocolException.stringTooLong("SpawnerId", spawnerIdLen, 4096000); + } + + if (varPos0 + spawnerIdVarIntLen + spawnerIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SpawnerId", varPos0 + spawnerIdVarIntLen + spawnerIdLen, buf.readableBytes()); + } + + obj.spawnerId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase1 = buf.getIntLE(offset + 117); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 121) { + throw ProtocolException.invalidOffset("Attractors", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 121 + varPosBase1; + int attractorsCount = VarInt.peek(buf, varPos1); + if (attractorsCount < 0) { + throw ProtocolException.invalidVarInt("Attractors"); + } + + int varIntLen = VarInt.size(attractorsCount); + if (attractorsCount > 4096000) { + throw ProtocolException.arrayTooLong("Attractors", attractorsCount, 4096000); + } + + if (varPos1 + varIntLen + attractorsCount * 85L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Attractors", varPos1 + varIntLen + attractorsCount * 85, buf.readableBytes()); + } + + obj.attractors = new ParticleAttractor[attractorsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < attractorsCount; i++) { + obj.attractors[i] = ParticleAttractor.deserialize(buf, elemPos); + elemPos += ParticleAttractor.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits[1] & 1) != 0) { - int varPos1 = offset + 121 + buf.getIntLE(offset + 117); - int attractorsCount = VarInt.peek(buf, varPos1); - if (attractorsCount < 0) { - throw ProtocolException.negativeLength("Attractors", attractorsCount); - } - - if (attractorsCount > 4096000) { - throw ProtocolException.arrayTooLong("Attractors", attractorsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + attractorsCount * 85L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Attractors", varPos1 + varIntLen + attractorsCount * 85, buf.readableBytes()); - } - - obj.attractors = new ParticleAttractor[attractorsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < attractorsCount; i++) { - obj.attractors[i] = ParticleAttractor.deserialize(buf, elemPos); - elemPos += ParticleAttractor.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -171,9 +191,13 @@ public class ParticleSpawnerGroup { int maxEnd = 121; if ((nullBits[0] & 128) != 0) { int fieldOffset0 = buf.getIntLE(offset + 113); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 121) { + throw ProtocolException.invalidOffset("SpawnerId", fieldOffset0, maxEnd); + } + int pos0 = offset + 121 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -181,9 +205,13 @@ public class ParticleSpawnerGroup { if ((nullBits[1] & 1) != 0) { int fieldOffset1 = buf.getIntLE(offset + 117); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 121) { + throw ProtocolException.invalidOffset("Attractors", fieldOffset1, maxEnd); + } + int pos1 = offset + 121 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += ParticleAttractor.computeBytesConsumed(buf, pos1); @@ -238,7 +266,7 @@ public class ParticleSpawnerGroup { buf.writeBytes(nullBits); if (this.positionOffset != null) { - this.positionOffset.serialize(buf); + PacketIO.writeVector3f(buf, this.positionOffset); } else { buf.writeZero(12); } @@ -331,15 +359,11 @@ public class ParticleSpawnerGroup { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); if ((nullBits[0] & 128) != 0) { int spawnerIdOffset = buffer.getIntLE(offset + 113); - if (spawnerIdOffset < 0) { + if (spawnerIdOffset < 0 || spawnerIdOffset > buffer.writerIndex() - offset - 121) { return ValidationResult.error("Invalid offset for SpawnerId"); } int pos = offset + 121 + spawnerIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SpawnerId"); - } - int spawnerIdLen = VarInt.peek(buffer, pos); if (spawnerIdLen < 0) { return ValidationResult.error("Invalid string length for SpawnerId"); @@ -349,7 +373,7 @@ public class ParticleSpawnerGroup { return ValidationResult.error("SpawnerId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(spawnerIdLen); pos += spawnerIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SpawnerId"); @@ -358,15 +382,11 @@ public class ParticleSpawnerGroup { if ((nullBits[1] & 1) != 0) { int attractorsOffset = buffer.getIntLE(offset + 117); - if (attractorsOffset < 0) { + if (attractorsOffset < 0 || attractorsOffset > buffer.writerIndex() - offset - 121) { return ValidationResult.error("Invalid offset for Attractors"); } int posx = offset + 121 + attractorsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Attractors"); - } - int attractorsCount = VarInt.peek(buffer, posx); if (attractorsCount < 0) { return ValidationResult.error("Invalid array count for Attractors"); @@ -376,7 +396,7 @@ public class ParticleSpawnerGroup { return ValidationResult.error("Attractors exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(attractorsCount); posx += attractorsCount * 85; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Attractors"); @@ -390,7 +410,7 @@ public class ParticleSpawnerGroup { public ParticleSpawnerGroup clone() { ParticleSpawnerGroup copy = new ParticleSpawnerGroup(); copy.spawnerId = this.spawnerId; - copy.positionOffset = this.positionOffset != null ? this.positionOffset.clone() : null; + copy.positionOffset = this.positionOffset; copy.rotationOffset = this.rotationOffset != null ? this.rotationOffset.clone() : null; copy.fixedRotation = this.fixedRotation; copy.startDelay = this.startDelay; diff --git a/src/com/hypixel/hytale/protocol/ParticleSystem.java b/src/com/hypixel/hytale/protocol/ParticleSystem.java index 6462f9d0..24fb0d82 100644 --- a/src/com/hypixel/hytale/protocol/ParticleSystem.java +++ b/src/com/hypixel/hytale/protocol/ParticleSystem.java @@ -50,52 +50,71 @@ public class ParticleSystem { @Nonnull public static ParticleSystem deserialize(@Nonnull ByteBuf buf, int offset) { - ParticleSystem obj = new ParticleSystem(); - byte nullBits = buf.getByte(offset); - obj.lifeSpan = buf.getFloatLE(offset + 1); - obj.cullDistance = buf.getFloatLE(offset + 5); - obj.boundingRadius = buf.getFloatLE(offset + 9); - obj.isImportant = buf.getByte(offset + 13) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 22 + buf.getIntLE(offset + 14); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 22) { + throw ProtocolException.bufferTooSmall("ParticleSystem", 22, buf.readableBytes() - offset); + } else { + ParticleSystem obj = new ParticleSystem(); + byte nullBits = buf.getByte(offset); + obj.lifeSpan = buf.getFloatLE(offset + 1); + obj.cullDistance = buf.getFloatLE(offset + 5); + obj.boundingRadius = buf.getFloatLE(offset + 9); + obj.isImportant = buf.getByte(offset + 13) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 14); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 22 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 18); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("Spawners", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 22 + varPosBase1; + int spawnersCount = VarInt.peek(buf, varPos1); + if (spawnersCount < 0) { + throw ProtocolException.invalidVarInt("Spawners"); + } + + int varIntLen = VarInt.size(spawnersCount); + if (spawnersCount > 4096000) { + throw ProtocolException.arrayTooLong("Spawners", spawnersCount, 4096000); + } + + if (varPos1 + varIntLen + spawnersCount * 113L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Spawners", varPos1 + varIntLen + spawnersCount * 113, buf.readableBytes()); + } + + obj.spawners = new ParticleSpawnerGroup[spawnersCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < spawnersCount; i++) { + obj.spawners[i] = ParticleSpawnerGroup.deserialize(buf, elemPos); + elemPos += ParticleSpawnerGroup.computeBytesConsumed(buf, elemPos); + } } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 22 + buf.getIntLE(offset + 18); - int spawnersCount = VarInt.peek(buf, varPos1); - if (spawnersCount < 0) { - throw ProtocolException.negativeLength("Spawners", spawnersCount); - } - - if (spawnersCount > 4096000) { - throw ProtocolException.arrayTooLong("Spawners", spawnersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + spawnersCount * 113L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Spawners", varPos1 + varIntLen + spawnersCount * 113, buf.readableBytes()); - } - - obj.spawners = new ParticleSpawnerGroup[spawnersCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < spawnersCount; i++) { - obj.spawners[i] = ParticleSpawnerGroup.deserialize(buf, elemPos); - elemPos += ParticleSpawnerGroup.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -103,9 +122,13 @@ public class ParticleSystem { int maxEnd = 22; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 14); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 22 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -113,9 +136,13 @@ public class ParticleSystem { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 18); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("Spawners", fieldOffset1, maxEnd); + } + int pos1 = offset + 22 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += ParticleSpawnerGroup.computeBytesConsumed(buf, pos1); @@ -199,15 +226,11 @@ public class ParticleSystem { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 14); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 22) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 22 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -217,7 +240,7 @@ public class ParticleSystem { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -226,15 +249,11 @@ public class ParticleSystem { if ((nullBits & 2) != 0) { int spawnersOffset = buffer.getIntLE(offset + 18); - if (spawnersOffset < 0) { + if (spawnersOffset < 0 || spawnersOffset > buffer.writerIndex() - offset - 22) { return ValidationResult.error("Invalid offset for Spawners"); } int posx = offset + 22 + spawnersOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Spawners"); - } - int spawnersCount = VarInt.peek(buffer, posx); if (spawnersCount < 0) { return ValidationResult.error("Invalid array count for Spawners"); @@ -244,7 +263,7 @@ public class ParticleSystem { return ValidationResult.error("Spawners exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(spawnersCount); for (int i = 0; i < spawnersCount; i++) { ValidationResult structResult = ParticleSpawnerGroup.validateStructure(buffer, posx); diff --git a/src/com/hypixel/hytale/protocol/PhysicalMaterial.java b/src/com/hypixel/hytale/protocol/PhysicalMaterial.java new file mode 100644 index 00000000..c556985a --- /dev/null +++ b/src/com/hypixel/hytale/protocol/PhysicalMaterial.java @@ -0,0 +1,171 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class PhysicalMaterial { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 17; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 17; + public static final int MAX_SIZE = 16384022; + @Nullable + public String id; + public float reflectionCoeff; + public float gainPerBlock; + public float hFGainPerBlock; + public float shelterOpacity; + + public PhysicalMaterial() { + } + + public PhysicalMaterial(@Nullable String id, float reflectionCoeff, float gainPerBlock, float hFGainPerBlock, float shelterOpacity) { + this.id = id; + this.reflectionCoeff = reflectionCoeff; + this.gainPerBlock = gainPerBlock; + this.hFGainPerBlock = hFGainPerBlock; + this.shelterOpacity = shelterOpacity; + } + + public PhysicalMaterial(@Nonnull PhysicalMaterial other) { + this.id = other.id; + this.reflectionCoeff = other.reflectionCoeff; + this.gainPerBlock = other.gainPerBlock; + this.hFGainPerBlock = other.hFGainPerBlock; + this.shelterOpacity = other.shelterOpacity; + } + + @Nonnull + public static PhysicalMaterial deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("PhysicalMaterial", 17, buf.readableBytes() - offset); + } else { + PhysicalMaterial obj = new PhysicalMaterial(); + byte nullBits = buf.getByte(offset); + obj.reflectionCoeff = buf.getFloatLE(offset + 1); + obj.gainPerBlock = buf.getFloatLE(offset + 5); + obj.hFGainPerBlock = buf.getFloatLE(offset + 9); + obj.shelterOpacity = buf.getFloatLE(offset + 13); + int pos = offset + 17; + if ((nullBits & 1) != 0) { + int idLen = VarInt.peek(buf, pos); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (pos + idVarLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", pos + idVarLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += idVarLen + idLen; + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 17; + if ((nullBits & 1) != 0) { + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + } + + return pos - offset; + } + + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.id != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + buf.writeFloatLE(this.reflectionCoeff); + buf.writeFloatLE(this.gainPerBlock); + buf.writeFloatLE(this.hFGainPerBlock); + buf.writeFloatLE(this.shelterOpacity); + if (this.id != null) { + PacketIO.writeVarString(buf, this.id, 4096000); + } + } + + public int computeSize() { + int size = 17; + if (this.id != null) { + size += PacketIO.stringSize(this.id); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pos = offset + 17; + if ((nullBits & 1) != 0) { + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } + } + + return ValidationResult.OK; + } + } + + public PhysicalMaterial clone() { + PhysicalMaterial copy = new PhysicalMaterial(); + copy.id = this.id; + copy.reflectionCoeff = this.reflectionCoeff; + copy.gainPerBlock = this.gainPerBlock; + copy.hFGainPerBlock = this.hFGainPerBlock; + copy.shelterOpacity = this.shelterOpacity; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof PhysicalMaterial other) + ? false + : Objects.equals(this.id, other.id) + && this.reflectionCoeff == other.reflectionCoeff + && this.gainPerBlock == other.gainPerBlock + && this.hFGainPerBlock == other.hFGainPerBlock + && this.shelterOpacity == other.shelterOpacity; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.id, this.reflectionCoeff, this.gainPerBlock, this.hFGainPerBlock, this.shelterOpacity); + } +} diff --git a/src/com/hypixel/hytale/protocol/PhysicsConfig.java b/src/com/hypixel/hytale/protocol/PhysicsConfig.java index 7e00eb34..af144e7d 100644 --- a/src/com/hypixel/hytale/protocol/PhysicsConfig.java +++ b/src/com/hypixel/hytale/protocol/PhysicsConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -114,30 +115,34 @@ public class PhysicsConfig { @Nonnull public static PhysicsConfig deserialize(@Nonnull ByteBuf buf, int offset) { - PhysicsConfig obj = new PhysicsConfig(); - obj.type = PhysicsType.fromValue(buf.getByte(offset + 0)); - obj.density = buf.getDoubleLE(offset + 1); - obj.gravity = buf.getDoubleLE(offset + 9); - obj.bounciness = buf.getDoubleLE(offset + 17); - obj.bounceCount = buf.getIntLE(offset + 25); - obj.bounceLimit = buf.getDoubleLE(offset + 29); - obj.sticksVertically = buf.getByte(offset + 37) != 0; - obj.computeYaw = buf.getByte(offset + 38) != 0; - obj.computePitch = buf.getByte(offset + 39) != 0; - obj.rotationMode = RotationMode.fromValue(buf.getByte(offset + 40)); - obj.moveOutOfSolidSpeed = buf.getDoubleLE(offset + 41); - obj.terminalVelocityAir = buf.getDoubleLE(offset + 49); - obj.densityAir = buf.getDoubleLE(offset + 57); - obj.terminalVelocityWater = buf.getDoubleLE(offset + 65); - obj.densityWater = buf.getDoubleLE(offset + 73); - obj.hitWaterImpulseLoss = buf.getDoubleLE(offset + 81); - obj.rotationForce = buf.getDoubleLE(offset + 89); - obj.speedRotationFactor = buf.getFloatLE(offset + 97); - obj.swimmingDampingFactor = buf.getDoubleLE(offset + 101); - obj.allowRolling = buf.getByte(offset + 109) != 0; - obj.rollingFrictionFactor = buf.getDoubleLE(offset + 110); - obj.rollingSpeed = buf.getFloatLE(offset + 118); - return obj; + if (buf.readableBytes() - offset < 122) { + throw ProtocolException.bufferTooSmall("PhysicsConfig", 122, buf.readableBytes() - offset); + } else { + PhysicsConfig obj = new PhysicsConfig(); + obj.type = PhysicsType.fromValue(buf.getByte(offset + 0)); + obj.density = buf.getDoubleLE(offset + 1); + obj.gravity = buf.getDoubleLE(offset + 9); + obj.bounciness = buf.getDoubleLE(offset + 17); + obj.bounceCount = buf.getIntLE(offset + 25); + obj.bounceLimit = buf.getDoubleLE(offset + 29); + obj.sticksVertically = buf.getByte(offset + 37) != 0; + obj.computeYaw = buf.getByte(offset + 38) != 0; + obj.computePitch = buf.getByte(offset + 39) != 0; + obj.rotationMode = RotationMode.fromValue(buf.getByte(offset + 40)); + obj.moveOutOfSolidSpeed = buf.getDoubleLE(offset + 41); + obj.terminalVelocityAir = buf.getDoubleLE(offset + 49); + obj.densityAir = buf.getDoubleLE(offset + 57); + obj.terminalVelocityWater = buf.getDoubleLE(offset + 65); + obj.densityWater = buf.getDoubleLE(offset + 73); + obj.hitWaterImpulseLoss = buf.getDoubleLE(offset + 81); + obj.rotationForce = buf.getDoubleLE(offset + 89); + obj.speedRotationFactor = buf.getFloatLE(offset + 97); + obj.swimmingDampingFactor = buf.getDoubleLE(offset + 101); + obj.allowRolling = buf.getByte(offset + 109) != 0; + obj.rollingFrictionFactor = buf.getDoubleLE(offset + 110); + obj.rollingSpeed = buf.getFloatLE(offset + 118); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -174,7 +179,17 @@ public class PhysicsConfig { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 122 ? ValidationResult.error("Buffer too small: expected at least 122 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 122) { + return ValidationResult.error("Buffer too small: expected at least 122 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + if (v >= 1) { + return ValidationResult.error("Invalid PhysicsType value for Type"); + } else { + v = buffer.getByte(offset + 40) & 255; + return v >= 4 ? ValidationResult.error("Invalid RotationMode value for RotationMode") : ValidationResult.OK; + } + } } public PhysicsConfig clone() { diff --git a/src/com/hypixel/hytale/protocol/PickBlockInteraction.java b/src/com/hypixel/hytale/protocol/PickBlockInteraction.java index 5571a1ff..78e5393f 100644 --- a/src/com/hypixel/hytale/protocol/PickBlockInteraction.java +++ b/src/com/hypixel/hytale/protocol/PickBlockInteraction.java @@ -67,79 +67,108 @@ public class PickBlockInteraction extends SimpleBlockInteraction { @Nonnull public static PickBlockInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - PickBlockInteraction obj = new PickBlockInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.useLatestTarget = buf.getByte(offset + 19) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 40 + buf.getIntLE(offset + 20); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 40) { + throw ProtocolException.bufferTooSmall("PickBlockInteraction", 40, buf.readableBytes() - offset); + } else { + PickBlockInteraction obj = new PickBlockInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.useLatestTarget = buf.getByte(offset + 19) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 20); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 40 + buf.getIntLE(offset + 24); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 40 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 24); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 40 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 40 + buf.getIntLE(offset + 28); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 28); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 40 + buf.getIntLE(offset + 32); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 40 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 32); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 40 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 36); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 40 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 40 + buf.getIntLE(offset + 36); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -147,6 +176,10 @@ public class PickBlockInteraction extends SimpleBlockInteraction { int maxEnd = 40; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 20); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 40 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -156,9 +189,13 @@ public class PickBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 24); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 40 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -171,6 +208,10 @@ public class PickBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 28); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 40 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -180,9 +221,13 @@ public class PickBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 32); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 40 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -190,6 +235,10 @@ public class PickBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 36); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 40 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -329,119 +378,109 @@ public class PickBlockInteraction extends SimpleBlockInteraction { return ValidationResult.error("Buffer too small: expected at least 40 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 20); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 20); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 40 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 40 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 40 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 40 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 40 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 40 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 24); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 40 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 28); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 40 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 32); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 40 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 36); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 40 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/PlaceBlockInteraction.java b/src/com/hypixel/hytale/protocol/PlaceBlockInteraction.java index 01e02a8b..0d3e0828 100644 --- a/src/com/hypixel/hytale/protocol/PlaceBlockInteraction.java +++ b/src/com/hypixel/hytale/protocol/PlaceBlockInteraction.java @@ -76,81 +76,110 @@ public class PlaceBlockInteraction extends SimpleInteraction { @Nonnull public static PlaceBlockInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - PlaceBlockInteraction obj = new PlaceBlockInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.blockId = buf.getIntLE(offset + 19); - obj.removeItemInHand = buf.getByte(offset + 23) != 0; - obj.allowDragPlacement = buf.getByte(offset + 24) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 45 + buf.getIntLE(offset + 25); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 45) { + throw ProtocolException.bufferTooSmall("PlaceBlockInteraction", 45, buf.readableBytes() - offset); + } else { + PlaceBlockInteraction obj = new PlaceBlockInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.blockId = buf.getIntLE(offset + 19); + obj.removeItemInHand = buf.getByte(offset + 23) != 0; + obj.allowDragPlacement = buf.getByte(offset + 24) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 25); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 45 + buf.getIntLE(offset + 29); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 45 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 29); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 45 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 45 + buf.getIntLE(offset + 33); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 33); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 45 + buf.getIntLE(offset + 37); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 45 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 37); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 45 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 41); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 45 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 45 + buf.getIntLE(offset + 41); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -158,6 +187,10 @@ public class PlaceBlockInteraction extends SimpleInteraction { int maxEnd = 45; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 25); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 45 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -167,9 +200,13 @@ public class PlaceBlockInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 29); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 45 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -182,6 +219,10 @@ public class PlaceBlockInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 33); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 45 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -191,9 +232,13 @@ public class PlaceBlockInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 37); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 45 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -201,6 +246,10 @@ public class PlaceBlockInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 41); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 45) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 45 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -342,119 +391,109 @@ public class PlaceBlockInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 45 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 25); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 25); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 45 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 45 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 29); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 45 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 45 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 45 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 41); + if (v < 0 || v > buffer.writerIndex() - offset - 45) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 45 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 29); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 45 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 33); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 45 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 37); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 45 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 41); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 45 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/PlayerSkin.java b/src/com/hypixel/hytale/protocol/PlayerSkin.java index f13657e0..6651c453 100644 --- a/src/com/hypixel/hytale/protocol/PlayerSkin.java +++ b/src/com/hypixel/hytale/protocol/PlayerSkin.java @@ -128,289 +128,493 @@ public class PlayerSkin { @Nonnull public static PlayerSkin deserialize(@Nonnull ByteBuf buf, int offset) { - PlayerSkin obj = new PlayerSkin(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 3); - if ((nullBits[0] & 1) != 0) { - int varPos0 = offset + 83 + buf.getIntLE(offset + 3); - int bodyCharacteristicLen = VarInt.peek(buf, varPos0); - if (bodyCharacteristicLen < 0) { - throw ProtocolException.negativeLength("BodyCharacteristic", bodyCharacteristicLen); + if (buf.readableBytes() - offset < 83) { + throw ProtocolException.bufferTooSmall("PlayerSkin", 83, buf.readableBytes() - offset); + } else { + PlayerSkin obj = new PlayerSkin(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 3); + if ((nullBits[0] & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 3); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("BodyCharacteristic", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 83 + varPosBase0; + int bodyCharacteristicLen = VarInt.peek(buf, varPos0); + if (bodyCharacteristicLen < 0) { + throw ProtocolException.invalidVarInt("BodyCharacteristic"); + } + + int bodyCharacteristicVarIntLen = VarInt.size(bodyCharacteristicLen); + if (bodyCharacteristicLen > 4096000) { + throw ProtocolException.stringTooLong("BodyCharacteristic", bodyCharacteristicLen, 4096000); + } + + if (varPos0 + bodyCharacteristicVarIntLen + bodyCharacteristicLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BodyCharacteristic", varPos0 + bodyCharacteristicVarIntLen + bodyCharacteristicLen, buf.readableBytes()); + } + + obj.bodyCharacteristic = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (bodyCharacteristicLen > 4096000) { - throw ProtocolException.stringTooLong("BodyCharacteristic", bodyCharacteristicLen, 4096000); + if ((nullBits[0] & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 7); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Underwear", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 83 + varPosBase1; + int underwearLen = VarInt.peek(buf, varPos1); + if (underwearLen < 0) { + throw ProtocolException.invalidVarInt("Underwear"); + } + + int underwearVarIntLen = VarInt.size(underwearLen); + if (underwearLen > 4096000) { + throw ProtocolException.stringTooLong("Underwear", underwearLen, 4096000); + } + + if (varPos1 + underwearVarIntLen + underwearLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Underwear", varPos1 + underwearVarIntLen + underwearLen, buf.readableBytes()); + } + + obj.underwear = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.bodyCharacteristic = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits[0] & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 11); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Face", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 83 + varPosBase2; + int faceLen = VarInt.peek(buf, varPos2); + if (faceLen < 0) { + throw ProtocolException.invalidVarInt("Face"); + } + + int faceVarIntLen = VarInt.size(faceLen); + if (faceLen > 4096000) { + throw ProtocolException.stringTooLong("Face", faceLen, 4096000); + } + + if (varPos2 + faceVarIntLen + faceLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Face", varPos2 + faceVarIntLen + faceLen, buf.readableBytes()); + } + + obj.face = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits[0] & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 15); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Eyes", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 83 + varPosBase3; + int eyesLen = VarInt.peek(buf, varPos3); + if (eyesLen < 0) { + throw ProtocolException.invalidVarInt("Eyes"); + } + + int eyesVarIntLen = VarInt.size(eyesLen); + if (eyesLen > 4096000) { + throw ProtocolException.stringTooLong("Eyes", eyesLen, 4096000); + } + + if (varPos3 + eyesVarIntLen + eyesLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Eyes", varPos3 + eyesVarIntLen + eyesLen, buf.readableBytes()); + } + + obj.eyes = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits[0] & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 19); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Ears", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 83 + varPosBase4; + int earsLen = VarInt.peek(buf, varPos4); + if (earsLen < 0) { + throw ProtocolException.invalidVarInt("Ears"); + } + + int earsVarIntLen = VarInt.size(earsLen); + if (earsLen > 4096000) { + throw ProtocolException.stringTooLong("Ears", earsLen, 4096000); + } + + if (varPos4 + earsVarIntLen + earsLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Ears", varPos4 + earsVarIntLen + earsLen, buf.readableBytes()); + } + + obj.ears = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + if ((nullBits[0] & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 23); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Mouth", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 83 + varPosBase5; + int mouthLen = VarInt.peek(buf, varPos5); + if (mouthLen < 0) { + throw ProtocolException.invalidVarInt("Mouth"); + } + + int mouthVarIntLen = VarInt.size(mouthLen); + if (mouthLen > 4096000) { + throw ProtocolException.stringTooLong("Mouth", mouthLen, 4096000); + } + + if (varPos5 + mouthVarIntLen + mouthLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Mouth", varPos5 + mouthVarIntLen + mouthLen, buf.readableBytes()); + } + + obj.mouth = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + if ((nullBits[0] & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 27); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("FacialHair", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 83 + varPosBase6; + int facialHairLen = VarInt.peek(buf, varPos6); + if (facialHairLen < 0) { + throw ProtocolException.invalidVarInt("FacialHair"); + } + + int facialHairVarIntLen = VarInt.size(facialHairLen); + if (facialHairLen > 4096000) { + throw ProtocolException.stringTooLong("FacialHair", facialHairLen, 4096000); + } + + if (varPos6 + facialHairVarIntLen + facialHairLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FacialHair", varPos6 + facialHairVarIntLen + facialHairLen, buf.readableBytes()); + } + + obj.facialHair = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase7 = buf.getIntLE(offset + 31); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Haircut", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 83 + varPosBase7; + int haircutLen = VarInt.peek(buf, varPos7); + if (haircutLen < 0) { + throw ProtocolException.invalidVarInt("Haircut"); + } + + int haircutVarIntLen = VarInt.size(haircutLen); + if (haircutLen > 4096000) { + throw ProtocolException.stringTooLong("Haircut", haircutLen, 4096000); + } + + if (varPos7 + haircutVarIntLen + haircutLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Haircut", varPos7 + haircutVarIntLen + haircutLen, buf.readableBytes()); + } + + obj.haircut = PacketIO.readVarString(buf, varPos7, PacketIO.UTF8); + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase8 = buf.getIntLE(offset + 35); + if (varPosBase8 < 0 || varPosBase8 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Eyebrows", varPosBase8, buf.readableBytes()); + } + + int varPos8 = offset + 83 + varPosBase8; + int eyebrowsLen = VarInt.peek(buf, varPos8); + if (eyebrowsLen < 0) { + throw ProtocolException.invalidVarInt("Eyebrows"); + } + + int eyebrowsVarIntLen = VarInt.size(eyebrowsLen); + if (eyebrowsLen > 4096000) { + throw ProtocolException.stringTooLong("Eyebrows", eyebrowsLen, 4096000); + } + + if (varPos8 + eyebrowsVarIntLen + eyebrowsLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Eyebrows", varPos8 + eyebrowsVarIntLen + eyebrowsLen, buf.readableBytes()); + } + + obj.eyebrows = PacketIO.readVarString(buf, varPos8, PacketIO.UTF8); + } + + if ((nullBits[1] & 2) != 0) { + int varPosBase9 = buf.getIntLE(offset + 39); + if (varPosBase9 < 0 || varPosBase9 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Pants", varPosBase9, buf.readableBytes()); + } + + int varPos9 = offset + 83 + varPosBase9; + int pantsLen = VarInt.peek(buf, varPos9); + if (pantsLen < 0) { + throw ProtocolException.invalidVarInt("Pants"); + } + + int pantsVarIntLen = VarInt.size(pantsLen); + if (pantsLen > 4096000) { + throw ProtocolException.stringTooLong("Pants", pantsLen, 4096000); + } + + if (varPos9 + pantsVarIntLen + pantsLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Pants", varPos9 + pantsVarIntLen + pantsLen, buf.readableBytes()); + } + + obj.pants = PacketIO.readVarString(buf, varPos9, PacketIO.UTF8); + } + + if ((nullBits[1] & 4) != 0) { + int varPosBase10 = buf.getIntLE(offset + 43); + if (varPosBase10 < 0 || varPosBase10 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Overpants", varPosBase10, buf.readableBytes()); + } + + int varPos10 = offset + 83 + varPosBase10; + int overpantsLen = VarInt.peek(buf, varPos10); + if (overpantsLen < 0) { + throw ProtocolException.invalidVarInt("Overpants"); + } + + int overpantsVarIntLen = VarInt.size(overpantsLen); + if (overpantsLen > 4096000) { + throw ProtocolException.stringTooLong("Overpants", overpantsLen, 4096000); + } + + if (varPos10 + overpantsVarIntLen + overpantsLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Overpants", varPos10 + overpantsVarIntLen + overpantsLen, buf.readableBytes()); + } + + obj.overpants = PacketIO.readVarString(buf, varPos10, PacketIO.UTF8); + } + + if ((nullBits[1] & 8) != 0) { + int varPosBase11 = buf.getIntLE(offset + 47); + if (varPosBase11 < 0 || varPosBase11 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Undertop", varPosBase11, buf.readableBytes()); + } + + int varPos11 = offset + 83 + varPosBase11; + int undertopLen = VarInt.peek(buf, varPos11); + if (undertopLen < 0) { + throw ProtocolException.invalidVarInt("Undertop"); + } + + int undertopVarIntLen = VarInt.size(undertopLen); + if (undertopLen > 4096000) { + throw ProtocolException.stringTooLong("Undertop", undertopLen, 4096000); + } + + if (varPos11 + undertopVarIntLen + undertopLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Undertop", varPos11 + undertopVarIntLen + undertopLen, buf.readableBytes()); + } + + obj.undertop = PacketIO.readVarString(buf, varPos11, PacketIO.UTF8); + } + + if ((nullBits[1] & 16) != 0) { + int varPosBase12 = buf.getIntLE(offset + 51); + if (varPosBase12 < 0 || varPosBase12 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Overtop", varPosBase12, buf.readableBytes()); + } + + int varPos12 = offset + 83 + varPosBase12; + int overtopLen = VarInt.peek(buf, varPos12); + if (overtopLen < 0) { + throw ProtocolException.invalidVarInt("Overtop"); + } + + int overtopVarIntLen = VarInt.size(overtopLen); + if (overtopLen > 4096000) { + throw ProtocolException.stringTooLong("Overtop", overtopLen, 4096000); + } + + if (varPos12 + overtopVarIntLen + overtopLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Overtop", varPos12 + overtopVarIntLen + overtopLen, buf.readableBytes()); + } + + obj.overtop = PacketIO.readVarString(buf, varPos12, PacketIO.UTF8); + } + + if ((nullBits[1] & 32) != 0) { + int varPosBase13 = buf.getIntLE(offset + 55); + if (varPosBase13 < 0 || varPosBase13 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Shoes", varPosBase13, buf.readableBytes()); + } + + int varPos13 = offset + 83 + varPosBase13; + int shoesLen = VarInt.peek(buf, varPos13); + if (shoesLen < 0) { + throw ProtocolException.invalidVarInt("Shoes"); + } + + int shoesVarIntLen = VarInt.size(shoesLen); + if (shoesLen > 4096000) { + throw ProtocolException.stringTooLong("Shoes", shoesLen, 4096000); + } + + if (varPos13 + shoesVarIntLen + shoesLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Shoes", varPos13 + shoesVarIntLen + shoesLen, buf.readableBytes()); + } + + obj.shoes = PacketIO.readVarString(buf, varPos13, PacketIO.UTF8); + } + + if ((nullBits[1] & 64) != 0) { + int varPosBase14 = buf.getIntLE(offset + 59); + if (varPosBase14 < 0 || varPosBase14 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("HeadAccessory", varPosBase14, buf.readableBytes()); + } + + int varPos14 = offset + 83 + varPosBase14; + int headAccessoryLen = VarInt.peek(buf, varPos14); + if (headAccessoryLen < 0) { + throw ProtocolException.invalidVarInt("HeadAccessory"); + } + + int headAccessoryVarIntLen = VarInt.size(headAccessoryLen); + if (headAccessoryLen > 4096000) { + throw ProtocolException.stringTooLong("HeadAccessory", headAccessoryLen, 4096000); + } + + if (varPos14 + headAccessoryVarIntLen + headAccessoryLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("HeadAccessory", varPos14 + headAccessoryVarIntLen + headAccessoryLen, buf.readableBytes()); + } + + obj.headAccessory = PacketIO.readVarString(buf, varPos14, PacketIO.UTF8); + } + + if ((nullBits[1] & 128) != 0) { + int varPosBase15 = buf.getIntLE(offset + 63); + if (varPosBase15 < 0 || varPosBase15 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("FaceAccessory", varPosBase15, buf.readableBytes()); + } + + int varPos15 = offset + 83 + varPosBase15; + int faceAccessoryLen = VarInt.peek(buf, varPos15); + if (faceAccessoryLen < 0) { + throw ProtocolException.invalidVarInt("FaceAccessory"); + } + + int faceAccessoryVarIntLen = VarInt.size(faceAccessoryLen); + if (faceAccessoryLen > 4096000) { + throw ProtocolException.stringTooLong("FaceAccessory", faceAccessoryLen, 4096000); + } + + if (varPos15 + faceAccessoryVarIntLen + faceAccessoryLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FaceAccessory", varPos15 + faceAccessoryVarIntLen + faceAccessoryLen, buf.readableBytes()); + } + + obj.faceAccessory = PacketIO.readVarString(buf, varPos15, PacketIO.UTF8); + } + + if ((nullBits[2] & 1) != 0) { + int varPosBase16 = buf.getIntLE(offset + 67); + if (varPosBase16 < 0 || varPosBase16 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("EarAccessory", varPosBase16, buf.readableBytes()); + } + + int varPos16 = offset + 83 + varPosBase16; + int earAccessoryLen = VarInt.peek(buf, varPos16); + if (earAccessoryLen < 0) { + throw ProtocolException.invalidVarInt("EarAccessory"); + } + + int earAccessoryVarIntLen = VarInt.size(earAccessoryLen); + if (earAccessoryLen > 4096000) { + throw ProtocolException.stringTooLong("EarAccessory", earAccessoryLen, 4096000); + } + + if (varPos16 + earAccessoryVarIntLen + earAccessoryLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EarAccessory", varPos16 + earAccessoryVarIntLen + earAccessoryLen, buf.readableBytes()); + } + + obj.earAccessory = PacketIO.readVarString(buf, varPos16, PacketIO.UTF8); + } + + if ((nullBits[2] & 2) != 0) { + int varPosBase17 = buf.getIntLE(offset + 71); + if (varPosBase17 < 0 || varPosBase17 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("SkinFeature", varPosBase17, buf.readableBytes()); + } + + int varPos17 = offset + 83 + varPosBase17; + int skinFeatureLen = VarInt.peek(buf, varPos17); + if (skinFeatureLen < 0) { + throw ProtocolException.invalidVarInt("SkinFeature"); + } + + int skinFeatureVarIntLen = VarInt.size(skinFeatureLen); + if (skinFeatureLen > 4096000) { + throw ProtocolException.stringTooLong("SkinFeature", skinFeatureLen, 4096000); + } + + if (varPos17 + skinFeatureVarIntLen + skinFeatureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SkinFeature", varPos17 + skinFeatureVarIntLen + skinFeatureLen, buf.readableBytes()); + } + + obj.skinFeature = PacketIO.readVarString(buf, varPos17, PacketIO.UTF8); + } + + if ((nullBits[2] & 4) != 0) { + int varPosBase18 = buf.getIntLE(offset + 75); + if (varPosBase18 < 0 || varPosBase18 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Gloves", varPosBase18, buf.readableBytes()); + } + + int varPos18 = offset + 83 + varPosBase18; + int glovesLen = VarInt.peek(buf, varPos18); + if (glovesLen < 0) { + throw ProtocolException.invalidVarInt("Gloves"); + } + + int glovesVarIntLen = VarInt.size(glovesLen); + if (glovesLen > 4096000) { + throw ProtocolException.stringTooLong("Gloves", glovesLen, 4096000); + } + + if (varPos18 + glovesVarIntLen + glovesLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Gloves", varPos18 + glovesVarIntLen + glovesLen, buf.readableBytes()); + } + + obj.gloves = PacketIO.readVarString(buf, varPos18, PacketIO.UTF8); + } + + if ((nullBits[2] & 8) != 0) { + int varPosBase19 = buf.getIntLE(offset + 79); + if (varPosBase19 < 0 || varPosBase19 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Cape", varPosBase19, buf.readableBytes()); + } + + int varPos19 = offset + 83 + varPosBase19; + int capeLen = VarInt.peek(buf, varPos19); + if (capeLen < 0) { + throw ProtocolException.invalidVarInt("Cape"); + } + + int capeVarIntLen = VarInt.size(capeLen); + if (capeLen > 4096000) { + throw ProtocolException.stringTooLong("Cape", capeLen, 4096000); + } + + if (varPos19 + capeVarIntLen + capeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Cape", varPos19 + capeVarIntLen + capeLen, buf.readableBytes()); + } + + obj.cape = PacketIO.readVarString(buf, varPos19, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits[0] & 2) != 0) { - int varPos1 = offset + 83 + buf.getIntLE(offset + 7); - int underwearLen = VarInt.peek(buf, varPos1); - if (underwearLen < 0) { - throw ProtocolException.negativeLength("Underwear", underwearLen); - } - - if (underwearLen > 4096000) { - throw ProtocolException.stringTooLong("Underwear", underwearLen, 4096000); - } - - obj.underwear = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits[0] & 4) != 0) { - int varPos2 = offset + 83 + buf.getIntLE(offset + 11); - int faceLen = VarInt.peek(buf, varPos2); - if (faceLen < 0) { - throw ProtocolException.negativeLength("Face", faceLen); - } - - if (faceLen > 4096000) { - throw ProtocolException.stringTooLong("Face", faceLen, 4096000); - } - - obj.face = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits[0] & 8) != 0) { - int varPos3 = offset + 83 + buf.getIntLE(offset + 15); - int eyesLen = VarInt.peek(buf, varPos3); - if (eyesLen < 0) { - throw ProtocolException.negativeLength("Eyes", eyesLen); - } - - if (eyesLen > 4096000) { - throw ProtocolException.stringTooLong("Eyes", eyesLen, 4096000); - } - - obj.eyes = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits[0] & 16) != 0) { - int varPos4 = offset + 83 + buf.getIntLE(offset + 19); - int earsLen = VarInt.peek(buf, varPos4); - if (earsLen < 0) { - throw ProtocolException.negativeLength("Ears", earsLen); - } - - if (earsLen > 4096000) { - throw ProtocolException.stringTooLong("Ears", earsLen, 4096000); - } - - obj.ears = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - if ((nullBits[0] & 32) != 0) { - int varPos5 = offset + 83 + buf.getIntLE(offset + 23); - int mouthLen = VarInt.peek(buf, varPos5); - if (mouthLen < 0) { - throw ProtocolException.negativeLength("Mouth", mouthLen); - } - - if (mouthLen > 4096000) { - throw ProtocolException.stringTooLong("Mouth", mouthLen, 4096000); - } - - obj.mouth = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - if ((nullBits[0] & 64) != 0) { - int varPos6 = offset + 83 + buf.getIntLE(offset + 27); - int facialHairLen = VarInt.peek(buf, varPos6); - if (facialHairLen < 0) { - throw ProtocolException.negativeLength("FacialHair", facialHairLen); - } - - if (facialHairLen > 4096000) { - throw ProtocolException.stringTooLong("FacialHair", facialHairLen, 4096000); - } - - obj.facialHair = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); - } - - if ((nullBits[0] & 128) != 0) { - int varPos7 = offset + 83 + buf.getIntLE(offset + 31); - int haircutLen = VarInt.peek(buf, varPos7); - if (haircutLen < 0) { - throw ProtocolException.negativeLength("Haircut", haircutLen); - } - - if (haircutLen > 4096000) { - throw ProtocolException.stringTooLong("Haircut", haircutLen, 4096000); - } - - obj.haircut = PacketIO.readVarString(buf, varPos7, PacketIO.UTF8); - } - - if ((nullBits[1] & 1) != 0) { - int varPos8 = offset + 83 + buf.getIntLE(offset + 35); - int eyebrowsLen = VarInt.peek(buf, varPos8); - if (eyebrowsLen < 0) { - throw ProtocolException.negativeLength("Eyebrows", eyebrowsLen); - } - - if (eyebrowsLen > 4096000) { - throw ProtocolException.stringTooLong("Eyebrows", eyebrowsLen, 4096000); - } - - obj.eyebrows = PacketIO.readVarString(buf, varPos8, PacketIO.UTF8); - } - - if ((nullBits[1] & 2) != 0) { - int varPos9 = offset + 83 + buf.getIntLE(offset + 39); - int pantsLen = VarInt.peek(buf, varPos9); - if (pantsLen < 0) { - throw ProtocolException.negativeLength("Pants", pantsLen); - } - - if (pantsLen > 4096000) { - throw ProtocolException.stringTooLong("Pants", pantsLen, 4096000); - } - - obj.pants = PacketIO.readVarString(buf, varPos9, PacketIO.UTF8); - } - - if ((nullBits[1] & 4) != 0) { - int varPos10 = offset + 83 + buf.getIntLE(offset + 43); - int overpantsLen = VarInt.peek(buf, varPos10); - if (overpantsLen < 0) { - throw ProtocolException.negativeLength("Overpants", overpantsLen); - } - - if (overpantsLen > 4096000) { - throw ProtocolException.stringTooLong("Overpants", overpantsLen, 4096000); - } - - obj.overpants = PacketIO.readVarString(buf, varPos10, PacketIO.UTF8); - } - - if ((nullBits[1] & 8) != 0) { - int varPos11 = offset + 83 + buf.getIntLE(offset + 47); - int undertopLen = VarInt.peek(buf, varPos11); - if (undertopLen < 0) { - throw ProtocolException.negativeLength("Undertop", undertopLen); - } - - if (undertopLen > 4096000) { - throw ProtocolException.stringTooLong("Undertop", undertopLen, 4096000); - } - - obj.undertop = PacketIO.readVarString(buf, varPos11, PacketIO.UTF8); - } - - if ((nullBits[1] & 16) != 0) { - int varPos12 = offset + 83 + buf.getIntLE(offset + 51); - int overtopLen = VarInt.peek(buf, varPos12); - if (overtopLen < 0) { - throw ProtocolException.negativeLength("Overtop", overtopLen); - } - - if (overtopLen > 4096000) { - throw ProtocolException.stringTooLong("Overtop", overtopLen, 4096000); - } - - obj.overtop = PacketIO.readVarString(buf, varPos12, PacketIO.UTF8); - } - - if ((nullBits[1] & 32) != 0) { - int varPos13 = offset + 83 + buf.getIntLE(offset + 55); - int shoesLen = VarInt.peek(buf, varPos13); - if (shoesLen < 0) { - throw ProtocolException.negativeLength("Shoes", shoesLen); - } - - if (shoesLen > 4096000) { - throw ProtocolException.stringTooLong("Shoes", shoesLen, 4096000); - } - - obj.shoes = PacketIO.readVarString(buf, varPos13, PacketIO.UTF8); - } - - if ((nullBits[1] & 64) != 0) { - int varPos14 = offset + 83 + buf.getIntLE(offset + 59); - int headAccessoryLen = VarInt.peek(buf, varPos14); - if (headAccessoryLen < 0) { - throw ProtocolException.negativeLength("HeadAccessory", headAccessoryLen); - } - - if (headAccessoryLen > 4096000) { - throw ProtocolException.stringTooLong("HeadAccessory", headAccessoryLen, 4096000); - } - - obj.headAccessory = PacketIO.readVarString(buf, varPos14, PacketIO.UTF8); - } - - if ((nullBits[1] & 128) != 0) { - int varPos15 = offset + 83 + buf.getIntLE(offset + 63); - int faceAccessoryLen = VarInt.peek(buf, varPos15); - if (faceAccessoryLen < 0) { - throw ProtocolException.negativeLength("FaceAccessory", faceAccessoryLen); - } - - if (faceAccessoryLen > 4096000) { - throw ProtocolException.stringTooLong("FaceAccessory", faceAccessoryLen, 4096000); - } - - obj.faceAccessory = PacketIO.readVarString(buf, varPos15, PacketIO.UTF8); - } - - if ((nullBits[2] & 1) != 0) { - int varPos16 = offset + 83 + buf.getIntLE(offset + 67); - int earAccessoryLen = VarInt.peek(buf, varPos16); - if (earAccessoryLen < 0) { - throw ProtocolException.negativeLength("EarAccessory", earAccessoryLen); - } - - if (earAccessoryLen > 4096000) { - throw ProtocolException.stringTooLong("EarAccessory", earAccessoryLen, 4096000); - } - - obj.earAccessory = PacketIO.readVarString(buf, varPos16, PacketIO.UTF8); - } - - if ((nullBits[2] & 2) != 0) { - int varPos17 = offset + 83 + buf.getIntLE(offset + 71); - int skinFeatureLen = VarInt.peek(buf, varPos17); - if (skinFeatureLen < 0) { - throw ProtocolException.negativeLength("SkinFeature", skinFeatureLen); - } - - if (skinFeatureLen > 4096000) { - throw ProtocolException.stringTooLong("SkinFeature", skinFeatureLen, 4096000); - } - - obj.skinFeature = PacketIO.readVarString(buf, varPos17, PacketIO.UTF8); - } - - if ((nullBits[2] & 4) != 0) { - int varPos18 = offset + 83 + buf.getIntLE(offset + 75); - int glovesLen = VarInt.peek(buf, varPos18); - if (glovesLen < 0) { - throw ProtocolException.negativeLength("Gloves", glovesLen); - } - - if (glovesLen > 4096000) { - throw ProtocolException.stringTooLong("Gloves", glovesLen, 4096000); - } - - obj.gloves = PacketIO.readVarString(buf, varPos18, PacketIO.UTF8); - } - - if ((nullBits[2] & 8) != 0) { - int varPos19 = offset + 83 + buf.getIntLE(offset + 79); - int capeLen = VarInt.peek(buf, varPos19); - if (capeLen < 0) { - throw ProtocolException.negativeLength("Cape", capeLen); - } - - if (capeLen > 4096000) { - throw ProtocolException.stringTooLong("Cape", capeLen, 4096000); - } - - obj.cape = PacketIO.readVarString(buf, varPos19, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -418,9 +622,13 @@ public class PlayerSkin { int maxEnd = 83; if ((nullBits[0] & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 3); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("BodyCharacteristic", fieldOffset0, maxEnd); + } + int pos0 = offset + 83 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -428,9 +636,13 @@ public class PlayerSkin { if ((nullBits[0] & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 7); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Underwear", fieldOffset1, maxEnd); + } + int pos1 = offset + 83 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -438,9 +650,13 @@ public class PlayerSkin { if ((nullBits[0] & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 11); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Face", fieldOffset2, maxEnd); + } + int pos2 = offset + 83 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -448,9 +664,13 @@ public class PlayerSkin { if ((nullBits[0] & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 15); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Eyes", fieldOffset3, maxEnd); + } + int pos3 = offset + 83 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -458,9 +678,13 @@ public class PlayerSkin { if ((nullBits[0] & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 19); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Ears", fieldOffset4, maxEnd); + } + int pos4 = offset + 83 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -468,9 +692,13 @@ public class PlayerSkin { if ((nullBits[0] & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 23); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Mouth", fieldOffset5, maxEnd); + } + int pos5 = offset + 83 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -478,9 +706,13 @@ public class PlayerSkin { if ((nullBits[0] & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 27); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("FacialHair", fieldOffset6, maxEnd); + } + int pos6 = offset + 83 + fieldOffset6; int sl = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6) + sl; + pos6 += VarInt.size(sl) + sl; if (pos6 - offset > maxEnd) { maxEnd = pos6 - offset; } @@ -488,9 +720,13 @@ public class PlayerSkin { if ((nullBits[0] & 128) != 0) { int fieldOffset7 = buf.getIntLE(offset + 31); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Haircut", fieldOffset7, maxEnd); + } + int pos7 = offset + 83 + fieldOffset7; int sl = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7) + sl; + pos7 += VarInt.size(sl) + sl; if (pos7 - offset > maxEnd) { maxEnd = pos7 - offset; } @@ -498,9 +734,13 @@ public class PlayerSkin { if ((nullBits[1] & 1) != 0) { int fieldOffset8 = buf.getIntLE(offset + 35); + if (fieldOffset8 < 0 || fieldOffset8 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Eyebrows", fieldOffset8, maxEnd); + } + int pos8 = offset + 83 + fieldOffset8; int sl = VarInt.peek(buf, pos8); - pos8 += VarInt.length(buf, pos8) + sl; + pos8 += VarInt.size(sl) + sl; if (pos8 - offset > maxEnd) { maxEnd = pos8 - offset; } @@ -508,9 +748,13 @@ public class PlayerSkin { if ((nullBits[1] & 2) != 0) { int fieldOffset9 = buf.getIntLE(offset + 39); + if (fieldOffset9 < 0 || fieldOffset9 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Pants", fieldOffset9, maxEnd); + } + int pos9 = offset + 83 + fieldOffset9; int sl = VarInt.peek(buf, pos9); - pos9 += VarInt.length(buf, pos9) + sl; + pos9 += VarInt.size(sl) + sl; if (pos9 - offset > maxEnd) { maxEnd = pos9 - offset; } @@ -518,9 +762,13 @@ public class PlayerSkin { if ((nullBits[1] & 4) != 0) { int fieldOffset10 = buf.getIntLE(offset + 43); + if (fieldOffset10 < 0 || fieldOffset10 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Overpants", fieldOffset10, maxEnd); + } + int pos10 = offset + 83 + fieldOffset10; int sl = VarInt.peek(buf, pos10); - pos10 += VarInt.length(buf, pos10) + sl; + pos10 += VarInt.size(sl) + sl; if (pos10 - offset > maxEnd) { maxEnd = pos10 - offset; } @@ -528,9 +776,13 @@ public class PlayerSkin { if ((nullBits[1] & 8) != 0) { int fieldOffset11 = buf.getIntLE(offset + 47); + if (fieldOffset11 < 0 || fieldOffset11 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Undertop", fieldOffset11, maxEnd); + } + int pos11 = offset + 83 + fieldOffset11; int sl = VarInt.peek(buf, pos11); - pos11 += VarInt.length(buf, pos11) + sl; + pos11 += VarInt.size(sl) + sl; if (pos11 - offset > maxEnd) { maxEnd = pos11 - offset; } @@ -538,9 +790,13 @@ public class PlayerSkin { if ((nullBits[1] & 16) != 0) { int fieldOffset12 = buf.getIntLE(offset + 51); + if (fieldOffset12 < 0 || fieldOffset12 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Overtop", fieldOffset12, maxEnd); + } + int pos12 = offset + 83 + fieldOffset12; int sl = VarInt.peek(buf, pos12); - pos12 += VarInt.length(buf, pos12) + sl; + pos12 += VarInt.size(sl) + sl; if (pos12 - offset > maxEnd) { maxEnd = pos12 - offset; } @@ -548,9 +804,13 @@ public class PlayerSkin { if ((nullBits[1] & 32) != 0) { int fieldOffset13 = buf.getIntLE(offset + 55); + if (fieldOffset13 < 0 || fieldOffset13 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Shoes", fieldOffset13, maxEnd); + } + int pos13 = offset + 83 + fieldOffset13; int sl = VarInt.peek(buf, pos13); - pos13 += VarInt.length(buf, pos13) + sl; + pos13 += VarInt.size(sl) + sl; if (pos13 - offset > maxEnd) { maxEnd = pos13 - offset; } @@ -558,9 +818,13 @@ public class PlayerSkin { if ((nullBits[1] & 64) != 0) { int fieldOffset14 = buf.getIntLE(offset + 59); + if (fieldOffset14 < 0 || fieldOffset14 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("HeadAccessory", fieldOffset14, maxEnd); + } + int pos14 = offset + 83 + fieldOffset14; int sl = VarInt.peek(buf, pos14); - pos14 += VarInt.length(buf, pos14) + sl; + pos14 += VarInt.size(sl) + sl; if (pos14 - offset > maxEnd) { maxEnd = pos14 - offset; } @@ -568,9 +832,13 @@ public class PlayerSkin { if ((nullBits[1] & 128) != 0) { int fieldOffset15 = buf.getIntLE(offset + 63); + if (fieldOffset15 < 0 || fieldOffset15 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("FaceAccessory", fieldOffset15, maxEnd); + } + int pos15 = offset + 83 + fieldOffset15; int sl = VarInt.peek(buf, pos15); - pos15 += VarInt.length(buf, pos15) + sl; + pos15 += VarInt.size(sl) + sl; if (pos15 - offset > maxEnd) { maxEnd = pos15 - offset; } @@ -578,9 +846,13 @@ public class PlayerSkin { if ((nullBits[2] & 1) != 0) { int fieldOffset16 = buf.getIntLE(offset + 67); + if (fieldOffset16 < 0 || fieldOffset16 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("EarAccessory", fieldOffset16, maxEnd); + } + int pos16 = offset + 83 + fieldOffset16; int sl = VarInt.peek(buf, pos16); - pos16 += VarInt.length(buf, pos16) + sl; + pos16 += VarInt.size(sl) + sl; if (pos16 - offset > maxEnd) { maxEnd = pos16 - offset; } @@ -588,9 +860,13 @@ public class PlayerSkin { if ((nullBits[2] & 2) != 0) { int fieldOffset17 = buf.getIntLE(offset + 71); + if (fieldOffset17 < 0 || fieldOffset17 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("SkinFeature", fieldOffset17, maxEnd); + } + int pos17 = offset + 83 + fieldOffset17; int sl = VarInt.peek(buf, pos17); - pos17 += VarInt.length(buf, pos17) + sl; + pos17 += VarInt.size(sl) + sl; if (pos17 - offset > maxEnd) { maxEnd = pos17 - offset; } @@ -598,9 +874,13 @@ public class PlayerSkin { if ((nullBits[2] & 4) != 0) { int fieldOffset18 = buf.getIntLE(offset + 75); + if (fieldOffset18 < 0 || fieldOffset18 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Gloves", fieldOffset18, maxEnd); + } + int pos18 = offset + 83 + fieldOffset18; int sl = VarInt.peek(buf, pos18); - pos18 += VarInt.length(buf, pos18) + sl; + pos18 += VarInt.size(sl) + sl; if (pos18 - offset > maxEnd) { maxEnd = pos18 - offset; } @@ -608,9 +888,13 @@ public class PlayerSkin { if ((nullBits[2] & 8) != 0) { int fieldOffset19 = buf.getIntLE(offset + 79); + if (fieldOffset19 < 0 || fieldOffset19 > buf.writerIndex() - offset - 83) { + throw ProtocolException.invalidOffset("Cape", fieldOffset19, maxEnd); + } + int pos19 = offset + 83 + fieldOffset19; int sl = VarInt.peek(buf, pos19); - pos19 += VarInt.length(buf, pos19) + sl; + pos19 += VarInt.size(sl) + sl; if (pos19 - offset > maxEnd) { maxEnd = pos19 - offset; } @@ -977,15 +1261,11 @@ public class PlayerSkin { byte[] nullBits = PacketIO.readBytes(buffer, offset, 3); if ((nullBits[0] & 1) != 0) { int bodyCharacteristicOffset = buffer.getIntLE(offset + 3); - if (bodyCharacteristicOffset < 0) { + if (bodyCharacteristicOffset < 0 || bodyCharacteristicOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for BodyCharacteristic"); } int pos = offset + 83 + bodyCharacteristicOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BodyCharacteristic"); - } - int bodyCharacteristicLen = VarInt.peek(buffer, pos); if (bodyCharacteristicLen < 0) { return ValidationResult.error("Invalid string length for BodyCharacteristic"); @@ -995,7 +1275,7 @@ public class PlayerSkin { return ValidationResult.error("BodyCharacteristic exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(bodyCharacteristicLen); pos += bodyCharacteristicLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading BodyCharacteristic"); @@ -1004,15 +1284,11 @@ public class PlayerSkin { if ((nullBits[0] & 2) != 0) { int underwearOffset = buffer.getIntLE(offset + 7); - if (underwearOffset < 0) { + if (underwearOffset < 0 || underwearOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Underwear"); } int posx = offset + 83 + underwearOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Underwear"); - } - int underwearLen = VarInt.peek(buffer, posx); if (underwearLen < 0) { return ValidationResult.error("Invalid string length for Underwear"); @@ -1022,7 +1298,7 @@ public class PlayerSkin { return ValidationResult.error("Underwear exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(underwearLen); posx += underwearLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Underwear"); @@ -1031,15 +1307,11 @@ public class PlayerSkin { if ((nullBits[0] & 4) != 0) { int faceOffset = buffer.getIntLE(offset + 11); - if (faceOffset < 0) { + if (faceOffset < 0 || faceOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Face"); } int posxx = offset + 83 + faceOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Face"); - } - int faceLen = VarInt.peek(buffer, posxx); if (faceLen < 0) { return ValidationResult.error("Invalid string length for Face"); @@ -1049,7 +1321,7 @@ public class PlayerSkin { return ValidationResult.error("Face exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(faceLen); posxx += faceLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Face"); @@ -1058,15 +1330,11 @@ public class PlayerSkin { if ((nullBits[0] & 8) != 0) { int eyesOffset = buffer.getIntLE(offset + 15); - if (eyesOffset < 0) { + if (eyesOffset < 0 || eyesOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Eyes"); } int posxxx = offset + 83 + eyesOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Eyes"); - } - int eyesLen = VarInt.peek(buffer, posxxx); if (eyesLen < 0) { return ValidationResult.error("Invalid string length for Eyes"); @@ -1076,7 +1344,7 @@ public class PlayerSkin { return ValidationResult.error("Eyes exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(eyesLen); posxxx += eyesLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Eyes"); @@ -1085,15 +1353,11 @@ public class PlayerSkin { if ((nullBits[0] & 16) != 0) { int earsOffset = buffer.getIntLE(offset + 19); - if (earsOffset < 0) { + if (earsOffset < 0 || earsOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Ears"); } int posxxxx = offset + 83 + earsOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Ears"); - } - int earsLen = VarInt.peek(buffer, posxxxx); if (earsLen < 0) { return ValidationResult.error("Invalid string length for Ears"); @@ -1103,7 +1367,7 @@ public class PlayerSkin { return ValidationResult.error("Ears exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(earsLen); posxxxx += earsLen; if (posxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Ears"); @@ -1112,15 +1376,11 @@ public class PlayerSkin { if ((nullBits[0] & 32) != 0) { int mouthOffset = buffer.getIntLE(offset + 23); - if (mouthOffset < 0) { + if (mouthOffset < 0 || mouthOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Mouth"); } int posxxxxx = offset + 83 + mouthOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Mouth"); - } - int mouthLen = VarInt.peek(buffer, posxxxxx); if (mouthLen < 0) { return ValidationResult.error("Invalid string length for Mouth"); @@ -1130,7 +1390,7 @@ public class PlayerSkin { return ValidationResult.error("Mouth exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); + posxxxxx += VarInt.size(mouthLen); posxxxxx += mouthLen; if (posxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Mouth"); @@ -1139,15 +1399,11 @@ public class PlayerSkin { if ((nullBits[0] & 64) != 0) { int facialHairOffset = buffer.getIntLE(offset + 27); - if (facialHairOffset < 0) { + if (facialHairOffset < 0 || facialHairOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for FacialHair"); } int posxxxxxx = offset + 83 + facialHairOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FacialHair"); - } - int facialHairLen = VarInt.peek(buffer, posxxxxxx); if (facialHairLen < 0) { return ValidationResult.error("Invalid string length for FacialHair"); @@ -1157,7 +1413,7 @@ public class PlayerSkin { return ValidationResult.error("FacialHair exceeds max length 4096000"); } - posxxxxxx += VarInt.length(buffer, posxxxxxx); + posxxxxxx += VarInt.size(facialHairLen); posxxxxxx += facialHairLen; if (posxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FacialHair"); @@ -1166,15 +1422,11 @@ public class PlayerSkin { if ((nullBits[0] & 128) != 0) { int haircutOffset = buffer.getIntLE(offset + 31); - if (haircutOffset < 0) { + if (haircutOffset < 0 || haircutOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Haircut"); } int posxxxxxxx = offset + 83 + haircutOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Haircut"); - } - int haircutLen = VarInt.peek(buffer, posxxxxxxx); if (haircutLen < 0) { return ValidationResult.error("Invalid string length for Haircut"); @@ -1184,7 +1436,7 @@ public class PlayerSkin { return ValidationResult.error("Haircut exceeds max length 4096000"); } - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); + posxxxxxxx += VarInt.size(haircutLen); posxxxxxxx += haircutLen; if (posxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Haircut"); @@ -1193,15 +1445,11 @@ public class PlayerSkin { if ((nullBits[1] & 1) != 0) { int eyebrowsOffset = buffer.getIntLE(offset + 35); - if (eyebrowsOffset < 0) { + if (eyebrowsOffset < 0 || eyebrowsOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Eyebrows"); } int posxxxxxxxx = offset + 83 + eyebrowsOffset; - if (posxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Eyebrows"); - } - int eyebrowsLen = VarInt.peek(buffer, posxxxxxxxx); if (eyebrowsLen < 0) { return ValidationResult.error("Invalid string length for Eyebrows"); @@ -1211,7 +1459,7 @@ public class PlayerSkin { return ValidationResult.error("Eyebrows exceeds max length 4096000"); } - posxxxxxxxx += VarInt.length(buffer, posxxxxxxxx); + posxxxxxxxx += VarInt.size(eyebrowsLen); posxxxxxxxx += eyebrowsLen; if (posxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Eyebrows"); @@ -1220,15 +1468,11 @@ public class PlayerSkin { if ((nullBits[1] & 2) != 0) { int pantsOffset = buffer.getIntLE(offset + 39); - if (pantsOffset < 0) { + if (pantsOffset < 0 || pantsOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Pants"); } int posxxxxxxxxx = offset + 83 + pantsOffset; - if (posxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Pants"); - } - int pantsLen = VarInt.peek(buffer, posxxxxxxxxx); if (pantsLen < 0) { return ValidationResult.error("Invalid string length for Pants"); @@ -1238,7 +1482,7 @@ public class PlayerSkin { return ValidationResult.error("Pants exceeds max length 4096000"); } - posxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxx); + posxxxxxxxxx += VarInt.size(pantsLen); posxxxxxxxxx += pantsLen; if (posxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Pants"); @@ -1247,15 +1491,11 @@ public class PlayerSkin { if ((nullBits[1] & 4) != 0) { int overpantsOffset = buffer.getIntLE(offset + 43); - if (overpantsOffset < 0) { + if (overpantsOffset < 0 || overpantsOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Overpants"); } int posxxxxxxxxxx = offset + 83 + overpantsOffset; - if (posxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Overpants"); - } - int overpantsLen = VarInt.peek(buffer, posxxxxxxxxxx); if (overpantsLen < 0) { return ValidationResult.error("Invalid string length for Overpants"); @@ -1265,7 +1505,7 @@ public class PlayerSkin { return ValidationResult.error("Overpants exceeds max length 4096000"); } - posxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxx); + posxxxxxxxxxx += VarInt.size(overpantsLen); posxxxxxxxxxx += overpantsLen; if (posxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Overpants"); @@ -1274,15 +1514,11 @@ public class PlayerSkin { if ((nullBits[1] & 8) != 0) { int undertopOffset = buffer.getIntLE(offset + 47); - if (undertopOffset < 0) { + if (undertopOffset < 0 || undertopOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Undertop"); } int posxxxxxxxxxxx = offset + 83 + undertopOffset; - if (posxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Undertop"); - } - int undertopLen = VarInt.peek(buffer, posxxxxxxxxxxx); if (undertopLen < 0) { return ValidationResult.error("Invalid string length for Undertop"); @@ -1292,7 +1528,7 @@ public class PlayerSkin { return ValidationResult.error("Undertop exceeds max length 4096000"); } - posxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxx); + posxxxxxxxxxxx += VarInt.size(undertopLen); posxxxxxxxxxxx += undertopLen; if (posxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Undertop"); @@ -1301,15 +1537,11 @@ public class PlayerSkin { if ((nullBits[1] & 16) != 0) { int overtopOffset = buffer.getIntLE(offset + 51); - if (overtopOffset < 0) { + if (overtopOffset < 0 || overtopOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Overtop"); } int posxxxxxxxxxxxx = offset + 83 + overtopOffset; - if (posxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Overtop"); - } - int overtopLen = VarInt.peek(buffer, posxxxxxxxxxxxx); if (overtopLen < 0) { return ValidationResult.error("Invalid string length for Overtop"); @@ -1319,7 +1551,7 @@ public class PlayerSkin { return ValidationResult.error("Overtop exceeds max length 4096000"); } - posxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxx); + posxxxxxxxxxxxx += VarInt.size(overtopLen); posxxxxxxxxxxxx += overtopLen; if (posxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Overtop"); @@ -1328,15 +1560,11 @@ public class PlayerSkin { if ((nullBits[1] & 32) != 0) { int shoesOffset = buffer.getIntLE(offset + 55); - if (shoesOffset < 0) { + if (shoesOffset < 0 || shoesOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Shoes"); } int posxxxxxxxxxxxxx = offset + 83 + shoesOffset; - if (posxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Shoes"); - } - int shoesLen = VarInt.peek(buffer, posxxxxxxxxxxxxx); if (shoesLen < 0) { return ValidationResult.error("Invalid string length for Shoes"); @@ -1346,7 +1574,7 @@ public class PlayerSkin { return ValidationResult.error("Shoes exceeds max length 4096000"); } - posxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxx); + posxxxxxxxxxxxxx += VarInt.size(shoesLen); posxxxxxxxxxxxxx += shoesLen; if (posxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Shoes"); @@ -1355,15 +1583,11 @@ public class PlayerSkin { if ((nullBits[1] & 64) != 0) { int headAccessoryOffset = buffer.getIntLE(offset + 59); - if (headAccessoryOffset < 0) { + if (headAccessoryOffset < 0 || headAccessoryOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for HeadAccessory"); } int posxxxxxxxxxxxxxx = offset + 83 + headAccessoryOffset; - if (posxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for HeadAccessory"); - } - int headAccessoryLen = VarInt.peek(buffer, posxxxxxxxxxxxxxx); if (headAccessoryLen < 0) { return ValidationResult.error("Invalid string length for HeadAccessory"); @@ -1373,7 +1597,7 @@ public class PlayerSkin { return ValidationResult.error("HeadAccessory exceeds max length 4096000"); } - posxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxx += VarInt.size(headAccessoryLen); posxxxxxxxxxxxxxx += headAccessoryLen; if (posxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading HeadAccessory"); @@ -1382,15 +1606,11 @@ public class PlayerSkin { if ((nullBits[1] & 128) != 0) { int faceAccessoryOffset = buffer.getIntLE(offset + 63); - if (faceAccessoryOffset < 0) { + if (faceAccessoryOffset < 0 || faceAccessoryOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for FaceAccessory"); } int posxxxxxxxxxxxxxxx = offset + 83 + faceAccessoryOffset; - if (posxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FaceAccessory"); - } - int faceAccessoryLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxx); if (faceAccessoryLen < 0) { return ValidationResult.error("Invalid string length for FaceAccessory"); @@ -1400,7 +1620,7 @@ public class PlayerSkin { return ValidationResult.error("FaceAccessory exceeds max length 4096000"); } - posxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxx += VarInt.size(faceAccessoryLen); posxxxxxxxxxxxxxxx += faceAccessoryLen; if (posxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FaceAccessory"); @@ -1409,15 +1629,11 @@ public class PlayerSkin { if ((nullBits[2] & 1) != 0) { int earAccessoryOffset = buffer.getIntLE(offset + 67); - if (earAccessoryOffset < 0) { + if (earAccessoryOffset < 0 || earAccessoryOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for EarAccessory"); } int posxxxxxxxxxxxxxxxx = offset + 83 + earAccessoryOffset; - if (posxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EarAccessory"); - } - int earAccessoryLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxx); if (earAccessoryLen < 0) { return ValidationResult.error("Invalid string length for EarAccessory"); @@ -1427,7 +1643,7 @@ public class PlayerSkin { return ValidationResult.error("EarAccessory exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxx += VarInt.size(earAccessoryLen); posxxxxxxxxxxxxxxxx += earAccessoryLen; if (posxxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading EarAccessory"); @@ -1436,15 +1652,11 @@ public class PlayerSkin { if ((nullBits[2] & 2) != 0) { int skinFeatureOffset = buffer.getIntLE(offset + 71); - if (skinFeatureOffset < 0) { + if (skinFeatureOffset < 0 || skinFeatureOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for SkinFeature"); } int posxxxxxxxxxxxxxxxxx = offset + 83 + skinFeatureOffset; - if (posxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SkinFeature"); - } - int skinFeatureLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxx); if (skinFeatureLen < 0) { return ValidationResult.error("Invalid string length for SkinFeature"); @@ -1454,7 +1666,7 @@ public class PlayerSkin { return ValidationResult.error("SkinFeature exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxx += VarInt.size(skinFeatureLen); posxxxxxxxxxxxxxxxxx += skinFeatureLen; if (posxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SkinFeature"); @@ -1463,15 +1675,11 @@ public class PlayerSkin { if ((nullBits[2] & 4) != 0) { int glovesOffset = buffer.getIntLE(offset + 75); - if (glovesOffset < 0) { + if (glovesOffset < 0 || glovesOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Gloves"); } int posxxxxxxxxxxxxxxxxxx = offset + 83 + glovesOffset; - if (posxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Gloves"); - } - int glovesLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxx); if (glovesLen < 0) { return ValidationResult.error("Invalid string length for Gloves"); @@ -1481,7 +1689,7 @@ public class PlayerSkin { return ValidationResult.error("Gloves exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxxx += VarInt.size(glovesLen); posxxxxxxxxxxxxxxxxxx += glovesLen; if (posxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Gloves"); @@ -1490,15 +1698,11 @@ public class PlayerSkin { if ((nullBits[2] & 8) != 0) { int capeOffset = buffer.getIntLE(offset + 79); - if (capeOffset < 0) { + if (capeOffset < 0 || capeOffset > buffer.writerIndex() - offset - 83) { return ValidationResult.error("Invalid offset for Cape"); } int posxxxxxxxxxxxxxxxxxxx = offset + 83 + capeOffset; - if (posxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Cape"); - } - int capeLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxx); if (capeLen < 0) { return ValidationResult.error("Invalid string length for Cape"); @@ -1508,7 +1712,7 @@ public class PlayerSkin { return ValidationResult.error("Cape exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxxxx += VarInt.size(capeLen); posxxxxxxxxxxxxxxxxxxx += capeLen; if (posxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Cape"); diff --git a/src/com/hypixel/hytale/protocol/PlayerSkinUpdate.java b/src/com/hypixel/hytale/protocol/PlayerSkinUpdate.java index ff5d9183..1d5e75c9 100644 --- a/src/com/hypixel/hytale/protocol/PlayerSkinUpdate.java +++ b/src/com/hypixel/hytale/protocol/PlayerSkinUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -28,15 +29,19 @@ public class PlayerSkinUpdate extends ComponentUpdate { @Nonnull public static PlayerSkinUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - PlayerSkinUpdate obj = new PlayerSkinUpdate(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - obj.skin = PlayerSkin.deserialize(buf, pos); - pos += PlayerSkin.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("PlayerSkinUpdate", 1, buf.readableBytes() - offset); + } else { + PlayerSkinUpdate obj = new PlayerSkinUpdate(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + obj.skin = PlayerSkin.deserialize(buf, pos); + pos += PlayerSkin.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Position.java b/src/com/hypixel/hytale/protocol/Position.java index c5e9c3e2..53420973 100644 --- a/src/com/hypixel/hytale/protocol/Position.java +++ b/src/com/hypixel/hytale/protocol/Position.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class Position { @Nonnull public static Position deserialize(@Nonnull ByteBuf buf, int offset) { - Position obj = new Position(); - obj.x = buf.getDoubleLE(offset + 0); - obj.y = buf.getDoubleLE(offset + 8); - obj.z = buf.getDoubleLE(offset + 16); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("Position", 24, buf.readableBytes() - offset); + } else { + Position obj = new Position(); + obj.x = buf.getDoubleLE(offset + 0); + obj.y = buf.getDoubleLE(offset + 8); + obj.z = buf.getDoubleLE(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/PredictionUpdate.java b/src/com/hypixel/hytale/protocol/PredictionUpdate.java index 9687a9d0..8694f8be 100644 --- a/src/com/hypixel/hytale/protocol/PredictionUpdate.java +++ b/src/com/hypixel/hytale/protocol/PredictionUpdate.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.protocol; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,9 +30,13 @@ public class PredictionUpdate extends ComponentUpdate { @Nonnull public static PredictionUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - PredictionUpdate obj = new PredictionUpdate(); - obj.predictionId = PacketIO.readUUID(buf, offset + 0); - return obj; + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("PredictionUpdate", 16, buf.readableBytes() - offset); + } else { + PredictionUpdate obj = new PredictionUpdate(); + obj.predictionId = PacketIO.readUUID(buf, offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ProjectileConfig.java b/src/com/hypixel/hytale/protocol/ProjectileConfig.java index e65e9bad..b9e3acf8 100644 --- a/src/com/hypixel/hytale/protocol/ProjectileConfig.java +++ b/src/com/hypixel/hytale/protocol/ProjectileConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.VarInt; @@ -10,12 +11,13 @@ import java.util.Objects; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class ProjectileConfig { public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 163; + public static final int FIXED_BLOCK_SIZE = 167; public static final int VARIABLE_FIELD_COUNT = 2; - public static final int VARIABLE_BLOCK_START = 171; + public static final int VARIABLE_BLOCK_START = 175; public static final int MAX_SIZE = 1677721600; @Nullable public PhysicsConfig physicsConfig; @@ -23,12 +25,13 @@ public class ProjectileConfig { public Model model; public double launchForce; @Nullable - public Vector3f spawnOffset; + public Vector3fc spawnOffset; @Nullable public Direction rotationOffset; @Nullable public Map interactions; public int launchLocalSoundEventIndex; + public int launchWorldSoundEventIndex; public int projectileSoundEventIndex; public ProjectileConfig() { @@ -38,10 +41,11 @@ public class ProjectileConfig { @Nullable PhysicsConfig physicsConfig, @Nullable Model model, double launchForce, - @Nullable Vector3f spawnOffset, + @Nullable Vector3fc spawnOffset, @Nullable Direction rotationOffset, @Nullable Map interactions, int launchLocalSoundEventIndex, + int launchWorldSoundEventIndex, int projectileSoundEventIndex ) { this.physicsConfig = physicsConfig; @@ -51,6 +55,7 @@ public class ProjectileConfig { this.rotationOffset = rotationOffset; this.interactions = interactions; this.launchLocalSoundEventIndex = launchLocalSoundEventIndex; + this.launchWorldSoundEventIndex = launchWorldSoundEventIndex; this.projectileSoundEventIndex = projectileSoundEventIndex; } @@ -62,67 +67,87 @@ public class ProjectileConfig { this.rotationOffset = other.rotationOffset; this.interactions = other.interactions; this.launchLocalSoundEventIndex = other.launchLocalSoundEventIndex; + this.launchWorldSoundEventIndex = other.launchWorldSoundEventIndex; this.projectileSoundEventIndex = other.projectileSoundEventIndex; } @Nonnull public static ProjectileConfig deserialize(@Nonnull ByteBuf buf, int offset) { - ProjectileConfig obj = new ProjectileConfig(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.physicsConfig = PhysicsConfig.deserialize(buf, offset + 1); - } - - obj.launchForce = buf.getDoubleLE(offset + 123); - if ((nullBits & 2) != 0) { - obj.spawnOffset = Vector3f.deserialize(buf, offset + 131); - } - - if ((nullBits & 4) != 0) { - obj.rotationOffset = Direction.deserialize(buf, offset + 143); - } - - obj.launchLocalSoundEventIndex = buf.getIntLE(offset + 155); - obj.projectileSoundEventIndex = buf.getIntLE(offset + 159); - if ((nullBits & 8) != 0) { - int varPos0 = offset + 171 + buf.getIntLE(offset + 163); - obj.model = Model.deserialize(buf, varPos0); - } - - if ((nullBits & 16) != 0) { - int varPos1 = offset + 171 + buf.getIntLE(offset + 167); - int interactionsCount = VarInt.peek(buf, varPos1); - if (interactionsCount < 0) { - throw ProtocolException.negativeLength("Interactions", interactionsCount); + if (buf.readableBytes() - offset < 175) { + throw ProtocolException.bufferTooSmall("ProjectileConfig", 175, buf.readableBytes() - offset); + } else { + ProjectileConfig obj = new ProjectileConfig(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.physicsConfig = PhysicsConfig.deserialize(buf, offset + 1); } - if (interactionsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + obj.launchForce = buf.getDoubleLE(offset + 123); + if ((nullBits & 2) != 0) { + obj.spawnOffset = PacketIO.readVector3f(buf, offset + 131); } - int varIntLen = VarInt.length(buf, varPos1); - obj.interactions = new HashMap<>(interactionsCount); - int dictPos = varPos1 + varIntLen; + if ((nullBits & 4) != 0) { + obj.rotationOffset = Direction.deserialize(buf, offset + 143); + } - for (int i = 0; i < interactionsCount; i++) { - InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.interactions.put(key, val) != null) { - throw ProtocolException.duplicateKey("interactions", key); + obj.launchLocalSoundEventIndex = buf.getIntLE(offset + 155); + obj.launchWorldSoundEventIndex = buf.getIntLE(offset + 159); + obj.projectileSoundEventIndex = buf.getIntLE(offset + 163); + if ((nullBits & 8) != 0) { + int varPosBase0 = buf.getIntLE(offset + 167); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 175) { + throw ProtocolException.invalidOffset("Model", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 175 + varPosBase0; + obj.model = Model.deserialize(buf, varPos0); + } + + if ((nullBits & 16) != 0) { + int varPosBase1 = buf.getIntLE(offset + 171); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 175) { + throw ProtocolException.invalidOffset("Interactions", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 175 + varPosBase1; + int interactionsCount = VarInt.peek(buf, varPos1); + if (interactionsCount < 0) { + throw ProtocolException.invalidVarInt("Interactions"); + } + + int varIntLen = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + } + + obj.interactions = new HashMap<>(interactionsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < interactionsCount; i++) { + InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.interactions.put(key, val) != null) { + throw ProtocolException.duplicateKey("interactions", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 171; + int maxEnd = 175; if ((nullBits & 8) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 163); - int pos0 = offset + 171 + fieldOffset0; + int fieldOffset0 = buf.getIntLE(offset + 167); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 175) { + throw ProtocolException.invalidOffset("Model", fieldOffset0, maxEnd); + } + + int pos0 = offset + 175 + fieldOffset0; pos0 += Model.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; @@ -130,10 +155,14 @@ public class ProjectileConfig { } if ((nullBits & 16) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 167); - int pos1 = offset + 171 + fieldOffset1; + int fieldOffset1 = buf.getIntLE(offset + 171); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 175) { + throw ProtocolException.invalidOffset("Interactions", fieldOffset1, maxEnd); + } + + int pos1 = offset + 175 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + 4; @@ -179,7 +208,7 @@ public class ProjectileConfig { buf.writeDoubleLE(this.launchForce); if (this.spawnOffset != null) { - this.spawnOffset.serialize(buf); + PacketIO.writeVector3f(buf, this.spawnOffset); } else { buf.writeZero(12); } @@ -191,6 +220,7 @@ public class ProjectileConfig { } buf.writeIntLE(this.launchLocalSoundEventIndex); + buf.writeIntLE(this.launchWorldSoundEventIndex); buf.writeIntLE(this.projectileSoundEventIndex); int modelOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); @@ -222,7 +252,7 @@ public class ProjectileConfig { } public int computeSize() { - int size = 171; + int size = 175; if (this.model != null) { size += this.model.computeSize(); } @@ -235,21 +265,17 @@ public class ProjectileConfig { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 171) { - return ValidationResult.error("Buffer too small: expected at least 171 bytes"); + if (buffer.readableBytes() - offset < 175) { + return ValidationResult.error("Buffer too small: expected at least 175 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 8) != 0) { - int modelOffset = buffer.getIntLE(offset + 163); - if (modelOffset < 0) { + int modelOffset = buffer.getIntLE(offset + 167); + if (modelOffset < 0 || modelOffset > buffer.writerIndex() - offset - 175) { return ValidationResult.error("Invalid offset for Model"); } - int pos = offset + 171 + modelOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } - + int pos = offset + 175 + modelOffset; ValidationResult modelResult = Model.validateStructure(buffer, pos); if (!modelResult.isValid()) { return ValidationResult.error("Invalid Model: " + modelResult.error()); @@ -259,17 +285,13 @@ public class ProjectileConfig { } if ((nullBits & 16) != 0) { - int interactionsOffset = buffer.getIntLE(offset + 167); - if (interactionsOffset < 0) { + int interactionsOffset = buffer.getIntLE(offset + 171); + if (interactionsOffset < 0 || interactionsOffset > buffer.writerIndex() - offset - 175) { return ValidationResult.error("Invalid offset for Interactions"); } - int posx = offset + 171 + interactionsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Interactions"); - } - - int interactionsCount = VarInt.peek(buffer, posx); + int pos = offset + 175 + interactionsOffset; + int interactionsCount = VarInt.peek(buffer, pos); if (interactionsCount < 0) { return ValidationResult.error("Invalid dictionary count for Interactions"); } @@ -278,11 +300,16 @@ public class ProjectileConfig { return ValidationResult.error("Interactions exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + pos += VarInt.size(interactionsCount); for (int i = 0; i < interactionsCount; i++) { - posx = ++posx + 4; - if (posx > buffer.writerIndex()) { + int v = buffer.getByte(pos) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); + } + + pos = ++pos + 4; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); } } @@ -297,10 +324,11 @@ public class ProjectileConfig { copy.physicsConfig = this.physicsConfig != null ? this.physicsConfig.clone() : null; copy.model = this.model != null ? this.model.clone() : null; copy.launchForce = this.launchForce; - copy.spawnOffset = this.spawnOffset != null ? this.spawnOffset.clone() : null; + copy.spawnOffset = this.spawnOffset; copy.rotationOffset = this.rotationOffset != null ? this.rotationOffset.clone() : null; copy.interactions = this.interactions != null ? new HashMap<>(this.interactions) : null; copy.launchLocalSoundEventIndex = this.launchLocalSoundEventIndex; + copy.launchWorldSoundEventIndex = this.launchWorldSoundEventIndex; copy.projectileSoundEventIndex = this.projectileSoundEventIndex; return copy; } @@ -319,6 +347,7 @@ public class ProjectileConfig { && Objects.equals(this.rotationOffset, other.rotationOffset) && Objects.equals(this.interactions, other.interactions) && this.launchLocalSoundEventIndex == other.launchLocalSoundEventIndex + && this.launchWorldSoundEventIndex == other.launchWorldSoundEventIndex && this.projectileSoundEventIndex == other.projectileSoundEventIndex; } } @@ -333,6 +362,7 @@ public class ProjectileConfig { this.rotationOffset, this.interactions, this.launchLocalSoundEventIndex, + this.launchWorldSoundEventIndex, this.projectileSoundEventIndex ); } diff --git a/src/com/hypixel/hytale/protocol/ProjectileInteraction.java b/src/com/hypixel/hytale/protocol/ProjectileInteraction.java index 5accf55d..79a89b60 100644 --- a/src/com/hypixel/hytale/protocol/ProjectileInteraction.java +++ b/src/com/hypixel/hytale/protocol/ProjectileInteraction.java @@ -70,92 +70,131 @@ public class ProjectileInteraction extends SimpleInteraction { @Nonnull public static ProjectileInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ProjectileInteraction obj = new ProjectileInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 43 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 43) { + throw ProtocolException.bufferTooSmall("ProjectileInteraction", 43, buf.readableBytes() - offset); + } else { + ProjectileInteraction obj = new ProjectileInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 43 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 43 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 43 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 43 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 43 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 43 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 39); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("ConfigId", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 43 + varPosBase5; + int configIdLen = VarInt.peek(buf, varPos5); + if (configIdLen < 0) { + throw ProtocolException.invalidVarInt("ConfigId"); + } + + int configIdVarIntLen = VarInt.size(configIdLen); + if (configIdLen > 4096000) { + throw ProtocolException.stringTooLong("ConfigId", configIdLen, 4096000); + } + + if (varPos5 + configIdVarIntLen + configIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ConfigId", varPos5 + configIdVarIntLen + configIdLen, buf.readableBytes()); + } + + obj.configId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 43 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 43 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 43 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 43 + buf.getIntLE(offset + 39); - int configIdLen = VarInt.peek(buf, varPos5); - if (configIdLen < 0) { - throw ProtocolException.negativeLength("ConfigId", configIdLen); - } - - if (configIdLen > 4096000) { - throw ProtocolException.stringTooLong("ConfigId", configIdLen, 4096000); - } - - obj.configId = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -163,6 +202,10 @@ public class ProjectileInteraction extends SimpleInteraction { int maxEnd = 43; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 43 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -172,9 +215,13 @@ public class ProjectileInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 43 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -187,6 +234,10 @@ public class ProjectileInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 43 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -196,9 +247,13 @@ public class ProjectileInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 43 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -206,6 +261,10 @@ public class ProjectileInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 43 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -215,9 +274,13 @@ public class ProjectileInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 39); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("ConfigId", fieldOffset5, maxEnd); + } + int pos5 = offset + 43 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -371,146 +434,132 @@ public class ProjectileInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 43 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 43 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 43 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 43 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 43 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 43 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 43 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for ConfigId"); + } + + int posxx = offset + 43 + v; + int configIdLen = VarInt.peek(buffer, posxx); + if (configIdLen < 0) { + return ValidationResult.error("Invalid string length for ConfigId"); + } + + if (configIdLen > 4096000) { + return ValidationResult.error("ConfigId exceeds max length 4096000"); + } + + posxx += VarInt.size(configIdLen); + posxx += configIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ConfigId"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 43 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 43 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 43 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 43 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int configIdOffset = buffer.getIntLE(offset + 39); - if (configIdOffset < 0) { - return ValidationResult.error("Invalid offset for ConfigId"); - } - - int posxxxxx = offset + 43 + configIdOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ConfigId"); - } - - int configIdLen = VarInt.peek(buffer, posxxxxx); - if (configIdLen < 0) { - return ValidationResult.error("Invalid string length for ConfigId"); - } - - if (configIdLen > 4096000) { - return ValidationResult.error("ConfigId exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += configIdLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ConfigId"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ProtocolEmote.java b/src/com/hypixel/hytale/protocol/ProtocolEmote.java new file mode 100644 index 00000000..7341a6cd --- /dev/null +++ b/src/com/hypixel/hytale/protocol/ProtocolEmote.java @@ -0,0 +1,427 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ProtocolEmote { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 2; + public static final int VARIABLE_FIELD_COUNT = 4; + public static final int VARIABLE_BLOCK_START = 18; + public static final int MAX_SIZE = 65536038; + @Nullable + public String id; + @Nullable + public String name; + @Nullable + public String animation; + @Nullable + public String icon; + public boolean isLooping; + + public ProtocolEmote() { + } + + public ProtocolEmote(@Nullable String id, @Nullable String name, @Nullable String animation, @Nullable String icon, boolean isLooping) { + this.id = id; + this.name = name; + this.animation = animation; + this.icon = icon; + this.isLooping = isLooping; + } + + public ProtocolEmote(@Nonnull ProtocolEmote other) { + this.id = other.id; + this.name = other.name; + this.animation = other.animation; + this.icon = other.icon; + this.isLooping = other.isLooping; + } + + @Nonnull + public static ProtocolEmote deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 18) { + throw ProtocolException.bufferTooSmall("ProtocolEmote", 18, buf.readableBytes() - offset); + } else { + ProtocolEmote obj = new ProtocolEmote(); + byte nullBits = buf.getByte(offset); + obj.isLooping = buf.getByte(offset + 1) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 18 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Name", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 18 + varPosBase1; + int nameLen = VarInt.peek(buf, varPos1); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos1 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos1 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 10); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Animation", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 18 + varPosBase2; + int animationLen = VarInt.peek(buf, varPos2); + if (animationLen < 0) { + throw ProtocolException.invalidVarInt("Animation"); + } + + int animationVarIntLen = VarInt.size(animationLen); + if (animationLen > 4096000) { + throw ProtocolException.stringTooLong("Animation", animationLen, 4096000); + } + + if (varPos2 + animationVarIntLen + animationLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Animation", varPos2 + animationVarIntLen + animationLen, buf.readableBytes()); + } + + obj.animation = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 14); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Icon", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 18 + varPosBase3; + int iconLen = VarInt.peek(buf, varPos3); + if (iconLen < 0) { + throw ProtocolException.invalidVarInt("Icon"); + } + + int iconVarIntLen = VarInt.size(iconLen); + if (iconLen > 4096000) { + throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + } + + if (varPos3 + iconVarIntLen + iconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Icon", varPos3 + iconVarIntLen + iconLen, buf.readableBytes()); + } + + obj.icon = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 18; + if ((nullBits & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + + int pos0 = offset + 18 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Name", fieldOffset1, maxEnd); + } + + int pos1 = offset + 18 + fieldOffset1; + int sl = VarInt.peek(buf, pos1); + pos1 += VarInt.size(sl) + sl; + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 10); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Animation", fieldOffset2, maxEnd); + } + + int pos2 = offset + 18 + fieldOffset2; + int sl = VarInt.peek(buf, pos2); + pos2 += VarInt.size(sl) + sl; + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + + if ((nullBits & 8) != 0) { + int fieldOffset3 = buf.getIntLE(offset + 14); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Icon", fieldOffset3, maxEnd); + } + + int pos3 = offset + 18 + fieldOffset3; + int sl = VarInt.peek(buf, pos3); + pos3 += VarInt.size(sl) + sl; + if (pos3 - offset > maxEnd) { + maxEnd = pos3 - offset; + } + } + + return maxEnd; + } + + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.id != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.name != null) { + nullBits = (byte)(nullBits | 2); + } + + if (this.animation != null) { + nullBits = (byte)(nullBits | 4); + } + + if (this.icon != null) { + nullBits = (byte)(nullBits | 8); + } + + buf.writeByte(nullBits); + buf.writeByte(this.isLooping ? 1 : 0); + int idOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int nameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int animationOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int iconOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.id != null) { + buf.setIntLE(idOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.id, 4096000); + } else { + buf.setIntLE(idOffsetSlot, -1); + } + + if (this.name != null) { + buf.setIntLE(nameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.name, 4096000); + } else { + buf.setIntLE(nameOffsetSlot, -1); + } + + if (this.animation != null) { + buf.setIntLE(animationOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.animation, 4096000); + } else { + buf.setIntLE(animationOffsetSlot, -1); + } + + if (this.icon != null) { + buf.setIntLE(iconOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.icon, 4096000); + } else { + buf.setIntLE(iconOffsetSlot, -1); + } + } + + public int computeSize() { + int size = 18; + if (this.id != null) { + size += PacketIO.stringSize(this.id); + } + + if (this.name != null) { + size += PacketIO.stringSize(this.name); + } + + if (this.animation != null) { + size += PacketIO.stringSize(this.animation); + } + + if (this.icon != null) { + size += PacketIO.stringSize(this.icon); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 18) { + return ValidationResult.error("Buffer too small: expected at least 18 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 1) != 0) { + int idOffset = buffer.getIntLE(offset + 2); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Id"); + } + + int pos = offset + 18 + idOffset; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } + } + + if ((nullBits & 2) != 0) { + int nameOffset = buffer.getIntLE(offset + 6); + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Name"); + } + + int posx = offset + 18 + nameOffset; + int nameLen = VarInt.peek(buffer, posx); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } + + if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); + } + + posx += VarInt.size(nameLen); + posx += nameLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); + } + } + + if ((nullBits & 4) != 0) { + int animationOffset = buffer.getIntLE(offset + 10); + if (animationOffset < 0 || animationOffset > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Animation"); + } + + int posxx = offset + 18 + animationOffset; + int animationLen = VarInt.peek(buffer, posxx); + if (animationLen < 0) { + return ValidationResult.error("Invalid string length for Animation"); + } + + if (animationLen > 4096000) { + return ValidationResult.error("Animation exceeds max length 4096000"); + } + + posxx += VarInt.size(animationLen); + posxx += animationLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Animation"); + } + } + + if ((nullBits & 8) != 0) { + int iconOffset = buffer.getIntLE(offset + 14); + if (iconOffset < 0 || iconOffset > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Icon"); + } + + int posxxx = offset + 18 + iconOffset; + int iconLen = VarInt.peek(buffer, posxxx); + if (iconLen < 0) { + return ValidationResult.error("Invalid string length for Icon"); + } + + if (iconLen > 4096000) { + return ValidationResult.error("Icon exceeds max length 4096000"); + } + + posxxx += VarInt.size(iconLen); + posxxx += iconLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Icon"); + } + } + + return ValidationResult.OK; + } + } + + public ProtocolEmote clone() { + ProtocolEmote copy = new ProtocolEmote(); + copy.id = this.id; + copy.name = this.name; + copy.animation = this.animation; + copy.icon = this.icon; + copy.isLooping = this.isLooping; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof ProtocolEmote other) + ? false + : Objects.equals(this.id, other.id) + && Objects.equals(this.name, other.name) + && Objects.equals(this.animation, other.animation) + && Objects.equals(this.icon, other.icon) + && this.isLooping == other.isLooping; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.id, this.name, this.animation, this.icon, this.isLooping); + } +} diff --git a/src/com/hypixel/hytale/protocol/ProtocolSettings.java b/src/com/hypixel/hytale/protocol/ProtocolSettings.java index d535f3b1..ca464b81 100644 --- a/src/com/hypixel/hytale/protocol/ProtocolSettings.java +++ b/src/com/hypixel/hytale/protocol/ProtocolSettings.java @@ -1,18 +1,18 @@ package com.hypixel.hytale.protocol; public final class ProtocolSettings { - public static final int PROTOCOL_CRC = -1356075132; + public static final int PROTOCOL_CRC = 1608127164; public static final int PROTOCOL_VERSION = 2; - public static final int PROTOCOL_BUILD_NUMBER = 20; - public static final int PACKET_COUNT = 268; - public static final int STRUCT_COUNT = 339; - public static final int ENUM_COUNT = 137; + public static final int PROTOCOL_BUILD_NUMBER = 63; + public static final int PACKET_COUNT = 289; + public static final int STRUCT_COUNT = 369; + public static final int ENUM_COUNT = 146; public static final int MAX_PACKET_SIZE = 1677721600; private ProtocolSettings() { } public static boolean validateCrc(int crc) { - return -1356075132 == crc; + return 1608127164 == crc; } } diff --git a/src/com/hypixel/hytale/protocol/RailConfig.java b/src/com/hypixel/hytale/protocol/RailConfig.java index bcdabb26..d6a64cda 100644 --- a/src/com/hypixel/hytale/protocol/RailConfig.java +++ b/src/com/hypixel/hytale/protocol/RailConfig.java @@ -13,7 +13,7 @@ public class RailConfig { public static final int FIXED_BLOCK_SIZE = 1; public static final int VARIABLE_FIELD_COUNT = 1; public static final int VARIABLE_BLOCK_START = 1; - public static final int MAX_SIZE = 102400006; + public static final int MAX_SIZE = 98304006; @Nullable public RailPoint[] points; @@ -30,34 +30,38 @@ public class RailConfig { @Nonnull public static RailConfig deserialize(@Nonnull ByteBuf buf, int offset) { - RailConfig obj = new RailConfig(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int pointsCount = VarInt.peek(buf, pos); - if (pointsCount < 0) { - throw ProtocolException.negativeLength("Points", pointsCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("RailConfig", 1, buf.readableBytes() - offset); + } else { + RailConfig obj = new RailConfig(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int pointsCount = VarInt.peek(buf, pos); + if (pointsCount < 0) { + throw ProtocolException.invalidVarInt("Points"); + } + + int pointsVarLen = VarInt.size(pointsCount); + if (pointsCount > 4096000) { + throw ProtocolException.arrayTooLong("Points", pointsCount, 4096000); + } + + if (pos + pointsVarLen + pointsCount * 24L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Points", pos + pointsVarLen + pointsCount * 24, buf.readableBytes()); + } + + pos += pointsVarLen; + obj.points = new RailPoint[pointsCount]; + + for (int i = 0; i < pointsCount; i++) { + obj.points[i] = RailPoint.deserialize(buf, pos); + pos += RailPoint.computeBytesConsumed(buf, pos); + } } - if (pointsCount > 4096000) { - throw ProtocolException.arrayTooLong("Points", pointsCount, 4096000); - } - - int pointsVarLen = VarInt.size(pointsCount); - if (pos + pointsVarLen + pointsCount * 25L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Points", pos + pointsVarLen + pointsCount * 25, buf.readableBytes()); - } - - pos += pointsVarLen; - obj.points = new RailPoint[pointsCount]; - - for (int i = 0; i < pointsCount; i++) { - obj.points[i] = RailPoint.deserialize(buf, pos); - pos += RailPoint.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -65,7 +69,7 @@ public class RailConfig { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += RailPoint.computeBytesConsumed(buf, pos); @@ -98,7 +102,7 @@ public class RailConfig { public int computeSize() { int size = 1; if (this.points != null) { - size += VarInt.size(this.points.length) + this.points.length * 25; + size += VarInt.size(this.points.length) + this.points.length * 24; } return size; @@ -120,8 +124,8 @@ public class RailConfig { return ValidationResult.error("Points exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += pointsCount * 25; + pos += VarInt.size(pointsCount); + pos += pointsCount * 24; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Points"); } diff --git a/src/com/hypixel/hytale/protocol/RailPoint.java b/src/com/hypixel/hytale/protocol/RailPoint.java index a0a356ad..33ab1815 100644 --- a/src/com/hypixel/hytale/protocol/RailPoint.java +++ b/src/com/hypixel/hytale/protocol/RailPoint.java @@ -1,26 +1,28 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import org.joml.Vector3fc; public class RailPoint { - public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 25; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 24; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 25; - public static final int MAX_SIZE = 25; - @Nullable - public Vector3f point; - @Nullable - public Vector3f normal; + public static final int VARIABLE_BLOCK_START = 24; + public static final int MAX_SIZE = 24; + @Nonnull + public Vector3fc point = PacketIO.ZERO_VECTOR3; + @Nonnull + public Vector3fc normal = PacketIO.ZERO_VECTOR3; public RailPoint() { } - public RailPoint(@Nullable Vector3f point, @Nullable Vector3f normal) { + public RailPoint(@Nonnull Vector3fc point, @Nonnull Vector3fc normal) { this.point = point; this.normal = normal; } @@ -32,59 +34,37 @@ public class RailPoint { @Nonnull public static RailPoint deserialize(@Nonnull ByteBuf buf, int offset) { - RailPoint obj = new RailPoint(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.point = Vector3f.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("RailPoint", 24, buf.readableBytes() - offset); + } else { + RailPoint obj = new RailPoint(); + obj.point = PacketIO.readVector3f(buf, offset + 0); + obj.normal = PacketIO.readVector3f(buf, offset + 12); + return obj; } - - if ((nullBits & 2) != 0) { - obj.normal = Vector3f.deserialize(buf, offset + 13); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 25; + return 24; } public void serialize(@Nonnull ByteBuf buf) { - byte nullBits = 0; - if (this.point != null) { - nullBits = (byte)(nullBits | 1); - } - - if (this.normal != null) { - nullBits = (byte)(nullBits | 2); - } - - buf.writeByte(nullBits); - if (this.point != null) { - this.point.serialize(buf); - } else { - buf.writeZero(12); - } - - if (this.normal != null) { - this.normal.serialize(buf); - } else { - buf.writeZero(12); - } + PacketIO.writeVector3f(buf, this.point); + PacketIO.writeVector3f(buf, this.normal); } public int computeSize() { - return 25; + return 24; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 25 ? ValidationResult.error("Buffer too small: expected at least 25 bytes") : ValidationResult.OK; + return buffer.readableBytes() - offset < 24 ? ValidationResult.error("Buffer too small: expected at least 24 bytes") : ValidationResult.OK; } public RailPoint clone() { RailPoint copy = new RailPoint(); - copy.point = this.point != null ? this.point.clone() : null; - copy.normal = this.normal != null ? this.normal.clone() : null; + copy.point = this.point; + copy.normal = this.normal; return copy; } diff --git a/src/com/hypixel/hytale/protocol/Range.java b/src/com/hypixel/hytale/protocol/Range.java index 8b58babb..e4ebe88b 100644 --- a/src/com/hypixel/hytale/protocol/Range.java +++ b/src/com/hypixel/hytale/protocol/Range.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class Range { @Nonnull public static Range deserialize(@Nonnull ByteBuf buf, int offset) { - Range obj = new Range(); - obj.min = buf.getIntLE(offset + 0); - obj.max = buf.getIntLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("Range", 8, buf.readableBytes() - offset); + } else { + Range obj = new Range(); + obj.min = buf.getIntLE(offset + 0); + obj.max = buf.getIntLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/RangeVector2f.java b/src/com/hypixel/hytale/protocol/RangeVector2f.java index 55d82642..a7a5d74a 100644 --- a/src/com/hypixel/hytale/protocol/RangeVector2f.java +++ b/src/com/hypixel/hytale/protocol/RangeVector2f.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,17 +33,21 @@ public class RangeVector2f { @Nonnull public static RangeVector2f deserialize(@Nonnull ByteBuf buf, int offset) { - RangeVector2f obj = new RangeVector2f(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.x = Rangef.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("RangeVector2f", 17, buf.readableBytes() - offset); + } else { + RangeVector2f obj = new RangeVector2f(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.x = Rangef.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.y = Rangef.deserialize(buf, offset + 9); - } + if ((nullBits & 2) != 0) { + obj.y = Rangef.deserialize(buf, offset + 9); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,7 +83,12 @@ public class RangeVector2f { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 17 ? ValidationResult.error("Buffer too small: expected at least 17 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public RangeVector2f clone() { diff --git a/src/com/hypixel/hytale/protocol/RangeVector3f.java b/src/com/hypixel/hytale/protocol/RangeVector3f.java index b378611f..42f4532c 100644 --- a/src/com/hypixel/hytale/protocol/RangeVector3f.java +++ b/src/com/hypixel/hytale/protocol/RangeVector3f.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -36,21 +37,25 @@ public class RangeVector3f { @Nonnull public static RangeVector3f deserialize(@Nonnull ByteBuf buf, int offset) { - RangeVector3f obj = new RangeVector3f(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.x = Rangef.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 25) { + throw ProtocolException.bufferTooSmall("RangeVector3f", 25, buf.readableBytes() - offset); + } else { + RangeVector3f obj = new RangeVector3f(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.x = Rangef.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.y = Rangef.deserialize(buf, offset + 9); - } + if ((nullBits & 2) != 0) { + obj.y = Rangef.deserialize(buf, offset + 9); + } - if ((nullBits & 4) != 0) { - obj.z = Rangef.deserialize(buf, offset + 17); - } + if ((nullBits & 4) != 0) { + obj.z = Rangef.deserialize(buf, offset + 17); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -96,7 +101,12 @@ public class RangeVector3f { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 25 ? ValidationResult.error("Buffer too small: expected at least 25 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 25) { + return ValidationResult.error("Buffer too small: expected at least 25 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public RangeVector3f clone() { diff --git a/src/com/hypixel/hytale/protocol/Rangeb.java b/src/com/hypixel/hytale/protocol/Rangeb.java index e80f7c57..1481eb7c 100644 --- a/src/com/hypixel/hytale/protocol/Rangeb.java +++ b/src/com/hypixel/hytale/protocol/Rangeb.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class Rangeb { @Nonnull public static Rangeb deserialize(@Nonnull ByteBuf buf, int offset) { - Rangeb obj = new Rangeb(); - obj.min = buf.getByte(offset + 0); - obj.max = buf.getByte(offset + 1); - return obj; + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("Rangeb", 2, buf.readableBytes() - offset); + } else { + Rangeb obj = new Rangeb(); + obj.min = buf.getByte(offset + 0); + obj.max = buf.getByte(offset + 1); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Rangef.java b/src/com/hypixel/hytale/protocol/Rangef.java index 5fb08190..7df8ca89 100644 --- a/src/com/hypixel/hytale/protocol/Rangef.java +++ b/src/com/hypixel/hytale/protocol/Rangef.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class Rangef { @Nonnull public static Rangef deserialize(@Nonnull ByteBuf buf, int offset) { - Rangef obj = new Rangef(); - obj.min = buf.getFloatLE(offset + 0); - obj.max = buf.getFloatLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("Rangef", 8, buf.readableBytes() - offset); + } else { + Rangef obj = new Rangef(); + obj.min = buf.getFloatLE(offset + 0); + obj.max = buf.getFloatLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/RaycastSelector.java b/src/com/hypixel/hytale/protocol/RaycastSelector.java index 86941cb9..cbe8781f 100644 --- a/src/com/hypixel/hytale/protocol/RaycastSelector.java +++ b/src/com/hypixel/hytale/protocol/RaycastSelector.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class RaycastSelector extends Selector { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -13,7 +16,7 @@ public class RaycastSelector extends Selector { public static final int VARIABLE_BLOCK_START = 23; public static final int MAX_SIZE = 23; @Nullable - public Vector3f offset; + public Vector3fc offset; public int distance; public int blockTagIndex = Integer.MIN_VALUE; public boolean ignoreFluids; @@ -22,7 +25,7 @@ public class RaycastSelector extends Selector { public RaycastSelector() { } - public RaycastSelector(@Nullable Vector3f offset, int distance, int blockTagIndex, boolean ignoreFluids, boolean ignoreEmptyCollisionMaterial) { + public RaycastSelector(@Nullable Vector3fc offset, int distance, int blockTagIndex, boolean ignoreFluids, boolean ignoreEmptyCollisionMaterial) { this.offset = offset; this.distance = distance; this.blockTagIndex = blockTagIndex; @@ -40,17 +43,21 @@ public class RaycastSelector extends Selector { @Nonnull public static RaycastSelector deserialize(@Nonnull ByteBuf buf, int offset) { - RaycastSelector obj = new RaycastSelector(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.offset = Vector3f.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 23) { + throw ProtocolException.bufferTooSmall("RaycastSelector", 23, buf.readableBytes() - offset); + } else { + RaycastSelector obj = new RaycastSelector(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.offset = PacketIO.readVector3f(buf, offset + 1); + } - obj.distance = buf.getIntLE(offset + 13); - obj.blockTagIndex = buf.getIntLE(offset + 17); - obj.ignoreFluids = buf.getByte(offset + 21) != 0; - obj.ignoreEmptyCollisionMaterial = buf.getByte(offset + 22) != 0; - return obj; + obj.distance = buf.getIntLE(offset + 13); + obj.blockTagIndex = buf.getIntLE(offset + 17); + obj.ignoreFluids = buf.getByte(offset + 21) != 0; + obj.ignoreEmptyCollisionMaterial = buf.getByte(offset + 22) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -67,7 +74,7 @@ public class RaycastSelector extends Selector { buf.writeByte(nullBits); if (this.offset != null) { - this.offset.serialize(buf); + PacketIO.writeVector3f(buf, this.offset); } else { buf.writeZero(12); } @@ -85,12 +92,17 @@ public class RaycastSelector extends Selector { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 23 ? ValidationResult.error("Buffer too small: expected at least 23 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 23) { + return ValidationResult.error("Buffer too small: expected at least 23 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public RaycastSelector clone() { RaycastSelector copy = new RaycastSelector(); - copy.offset = this.offset != null ? this.offset.clone() : null; + copy.offset = this.offset; copy.distance = this.distance; copy.blockTagIndex = this.blockTagIndex; copy.ignoreFluids = this.ignoreFluids; diff --git a/src/com/hypixel/hytale/protocol/RemoveEntityInteraction.java b/src/com/hypixel/hytale/protocol/RemoveEntityInteraction.java index 45aa3b9a..8e35bf40 100644 --- a/src/com/hypixel/hytale/protocol/RemoveEntityInteraction.java +++ b/src/com/hypixel/hytale/protocol/RemoveEntityInteraction.java @@ -69,79 +69,108 @@ public class RemoveEntityInteraction extends SimpleInteraction { @Nonnull public static RemoveEntityInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - RemoveEntityInteraction obj = new RemoveEntityInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 19)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 40 + buf.getIntLE(offset + 20); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 40) { + throw ProtocolException.bufferTooSmall("RemoveEntityInteraction", 40, buf.readableBytes() - offset); + } else { + RemoveEntityInteraction obj = new RemoveEntityInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.entityTarget = InteractionTarget.fromValue(buf.getByte(offset + 19)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 20); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 40 + buf.getIntLE(offset + 24); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 40 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 24); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 40 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 40 + buf.getIntLE(offset + 28); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 28); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 40 + buf.getIntLE(offset + 32); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 40 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 32); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 40 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 36); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 40 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 40 + buf.getIntLE(offset + 36); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -149,6 +178,10 @@ public class RemoveEntityInteraction extends SimpleInteraction { int maxEnd = 40; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 20); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 40 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -158,9 +191,13 @@ public class RemoveEntityInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 24); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 40 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -173,6 +210,10 @@ public class RemoveEntityInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 28); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 40 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -182,9 +223,13 @@ public class RemoveEntityInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 32); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 40 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -192,6 +237,10 @@ public class RemoveEntityInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 36); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 40 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -331,119 +380,114 @@ public class RemoveEntityInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 40 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 20); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 19) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid InteractionTarget value for EntityTarget"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 20); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 40 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 40 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Settings"); + } - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 24); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } + int pos = offset + 40 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } - int posx = offset + 40 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } + pos += VarInt.size(settingsCount); - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } - posx += VarInt.length(buffer, posx); + pos++; + pos++; + } + } - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 40 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 40 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 40 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 28); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 40 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 32); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 40 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 36); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 40 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/RepeatInteraction.java b/src/com/hypixel/hytale/protocol/RepeatInteraction.java index 54090129..46c77bdb 100644 --- a/src/com/hypixel/hytale/protocol/RepeatInteraction.java +++ b/src/com/hypixel/hytale/protocol/RepeatInteraction.java @@ -72,80 +72,109 @@ public class RepeatInteraction extends SimpleInteraction { @Nonnull public static RepeatInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - RepeatInteraction obj = new RepeatInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.forkInteractions = buf.getIntLE(offset + 19); - obj.repeat = buf.getIntLE(offset + 23); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 47 + buf.getIntLE(offset + 27); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 47) { + throw ProtocolException.bufferTooSmall("RepeatInteraction", 47, buf.readableBytes() - offset); + } else { + RepeatInteraction obj = new RepeatInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.forkInteractions = buf.getIntLE(offset + 19); + obj.repeat = buf.getIntLE(offset + 23); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 27); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 47 + buf.getIntLE(offset + 31); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 47 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 31); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 47 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 47 + buf.getIntLE(offset + 35); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 35); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 47 + buf.getIntLE(offset + 39); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 47 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 39); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 47 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 43); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 47 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 47 + buf.getIntLE(offset + 43); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -153,6 +182,10 @@ public class RepeatInteraction extends SimpleInteraction { int maxEnd = 47; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 27); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 47 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -162,9 +195,13 @@ public class RepeatInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 31); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 47 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -177,6 +214,10 @@ public class RepeatInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 35); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 47 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -186,9 +227,13 @@ public class RepeatInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 39); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 47 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -196,6 +241,10 @@ public class RepeatInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 43); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 47) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 47 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -336,119 +385,109 @@ public class RepeatInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 47 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 27); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 47 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 47 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 47 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 47 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 47 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 43); + if (v < 0 || v > buffer.writerIndex() - offset - 47) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 47 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 31); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 47 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 35); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 47 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 39); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 47 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 43); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 47 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ReplaceInteraction.java b/src/com/hypixel/hytale/protocol/ReplaceInteraction.java index 9919d655..b409ef4e 100644 --- a/src/com/hypixel/hytale/protocol/ReplaceInteraction.java +++ b/src/com/hypixel/hytale/protocol/ReplaceInteraction.java @@ -68,91 +68,130 @@ public class ReplaceInteraction extends Interaction { @Nonnull public static ReplaceInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ReplaceInteraction obj = new ReplaceInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.defaultValue = buf.getIntLE(offset + 11); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 39 + buf.getIntLE(offset + 15); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 39) { + throw ProtocolException.bufferTooSmall("ReplaceInteraction", 39, buf.readableBytes() - offset); + } else { + ReplaceInteraction obj = new ReplaceInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.defaultValue = buf.getIntLE(offset + 11); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 15); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 39 + buf.getIntLE(offset + 19); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 39 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 19); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 39 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 23); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 39 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 27); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 39 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 31); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 39 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 35); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Variable", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 39 + varPosBase5; + int variableLen = VarInt.peek(buf, varPos5); + if (variableLen < 0) { + throw ProtocolException.invalidVarInt("Variable"); + } + + int variableVarIntLen = VarInt.size(variableLen); + if (variableLen > 4096000) { + throw ProtocolException.stringTooLong("Variable", variableLen, 4096000); + } + + if (varPos5 + variableVarIntLen + variableLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Variable", varPos5 + variableVarIntLen + variableLen, buf.readableBytes()); + } + + obj.variable = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 39 + buf.getIntLE(offset + 23); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 39 + buf.getIntLE(offset + 27); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 39 + buf.getIntLE(offset + 31); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 39 + buf.getIntLE(offset + 35); - int variableLen = VarInt.peek(buf, varPos5); - if (variableLen < 0) { - throw ProtocolException.negativeLength("Variable", variableLen); - } - - if (variableLen > 4096000) { - throw ProtocolException.stringTooLong("Variable", variableLen, 4096000); - } - - obj.variable = PacketIO.readVarString(buf, varPos5, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -160,6 +199,10 @@ public class ReplaceInteraction extends Interaction { int maxEnd = 39; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 15); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 39 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -169,9 +212,13 @@ public class ReplaceInteraction extends Interaction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 19); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 39 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -184,6 +231,10 @@ public class ReplaceInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 23); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 39 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -193,9 +244,13 @@ public class ReplaceInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 27); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 39 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -203,6 +258,10 @@ public class ReplaceInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 31); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 39 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -212,9 +271,13 @@ public class ReplaceInteraction extends Interaction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 35); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Variable", fieldOffset5, maxEnd); + } + int pos5 = offset + 39 + fieldOffset5; int sl = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + sl; + pos5 += VarInt.size(sl) + sl; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -367,146 +430,132 @@ public class ReplaceInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 39 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 15); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 39 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 39 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 39 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 39 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 39 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 39 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Variable"); + } + + int posxx = offset + 39 + v; + int variableLen = VarInt.peek(buffer, posxx); + if (variableLen < 0) { + return ValidationResult.error("Invalid string length for Variable"); + } + + if (variableLen > 4096000) { + return ValidationResult.error("Variable exceeds max length 4096000"); + } + + posxx += VarInt.size(variableLen); + posxx += variableLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Variable"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 19); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 39 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 23); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 39 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 27); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 39 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 31); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 39 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int variableOffset = buffer.getIntLE(offset + 35); - if (variableOffset < 0) { - return ValidationResult.error("Invalid offset for Variable"); - } - - int posxxxxx = offset + 39 + variableOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Variable"); - } - - int variableLen = VarInt.peek(buffer, posxxxxx); - if (variableLen < 0) { - return ValidationResult.error("Invalid string length for Variable"); - } - - if (variableLen > 4096000) { - return ValidationResult.error("Variable exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += variableLen; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Variable"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/RepulsionConfig.java b/src/com/hypixel/hytale/protocol/RepulsionConfig.java index aa51d344..e60fdc54 100644 --- a/src/com/hypixel/hytale/protocol/RepulsionConfig.java +++ b/src/com/hypixel/hytale/protocol/RepulsionConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class RepulsionConfig { @Nonnull public static RepulsionConfig deserialize(@Nonnull ByteBuf buf, int offset) { - RepulsionConfig obj = new RepulsionConfig(); - obj.radius = buf.getFloatLE(offset + 0); - obj.minForce = buf.getFloatLE(offset + 4); - obj.maxForce = buf.getFloatLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("RepulsionConfig", 12, buf.readableBytes() - offset); + } else { + RepulsionConfig obj = new RepulsionConfig(); + obj.radius = buf.getFloatLE(offset + 0); + obj.minForce = buf.getFloatLE(offset + 4); + obj.maxForce = buf.getFloatLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/RepulsionUpdate.java b/src/com/hypixel/hytale/protocol/RepulsionUpdate.java index fb01b04f..2fe9f602 100644 --- a/src/com/hypixel/hytale/protocol/RepulsionUpdate.java +++ b/src/com/hypixel/hytale/protocol/RepulsionUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class RepulsionUpdate extends ComponentUpdate { @Nonnull public static RepulsionUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - RepulsionUpdate obj = new RepulsionUpdate(); - obj.repulsionConfigIndex = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("RepulsionUpdate", 4, buf.readableBytes() - offset); + } else { + RepulsionUpdate obj = new RepulsionUpdate(); + obj.repulsionConfigIndex = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/RequiredBlockFaceSupport.java b/src/com/hypixel/hytale/protocol/RequiredBlockFaceSupport.java index b4889cd2..05ceb12e 100644 --- a/src/com/hypixel/hytale/protocol/RequiredBlockFaceSupport.java +++ b/src/com/hypixel/hytale/protocol/RequiredBlockFaceSupport.java @@ -79,83 +79,122 @@ public class RequiredBlockFaceSupport { @Nonnull public static RequiredBlockFaceSupport deserialize(@Nonnull ByteBuf buf, int offset) { - RequiredBlockFaceSupport obj = new RequiredBlockFaceSupport(); - byte nullBits = buf.getByte(offset); - obj.blockTypeId = buf.getIntLE(offset + 1); - obj.tagIndex = buf.getIntLE(offset + 5); - obj.fluidId = buf.getIntLE(offset + 9); - obj.support = SupportMatch.fromValue(buf.getByte(offset + 13)); - obj.matchSelf = SupportMatch.fromValue(buf.getByte(offset + 14)); - obj.allowSupportPropagation = buf.getByte(offset + 15) != 0; - obj.rotate = buf.getByte(offset + 16) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 33 + buf.getIntLE(offset + 17); - int faceTypeLen = VarInt.peek(buf, varPos0); - if (faceTypeLen < 0) { - throw ProtocolException.negativeLength("FaceType", faceTypeLen); + if (buf.readableBytes() - offset < 33) { + throw ProtocolException.bufferTooSmall("RequiredBlockFaceSupport", 33, buf.readableBytes() - offset); + } else { + RequiredBlockFaceSupport obj = new RequiredBlockFaceSupport(); + byte nullBits = buf.getByte(offset); + obj.blockTypeId = buf.getIntLE(offset + 1); + obj.tagIndex = buf.getIntLE(offset + 5); + obj.fluidId = buf.getIntLE(offset + 9); + obj.support = SupportMatch.fromValue(buf.getByte(offset + 13)); + obj.matchSelf = SupportMatch.fromValue(buf.getByte(offset + 14)); + obj.allowSupportPropagation = buf.getByte(offset + 15) != 0; + obj.rotate = buf.getByte(offset + 16) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 17); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("FaceType", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 33 + varPosBase0; + int faceTypeLen = VarInt.peek(buf, varPos0); + if (faceTypeLen < 0) { + throw ProtocolException.invalidVarInt("FaceType"); + } + + int faceTypeVarIntLen = VarInt.size(faceTypeLen); + if (faceTypeLen > 4096000) { + throw ProtocolException.stringTooLong("FaceType", faceTypeLen, 4096000); + } + + if (varPos0 + faceTypeVarIntLen + faceTypeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FaceType", varPos0 + faceTypeVarIntLen + faceTypeLen, buf.readableBytes()); + } + + obj.faceType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (faceTypeLen > 4096000) { - throw ProtocolException.stringTooLong("FaceType", faceTypeLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 21); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("SelfFaceType", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 33 + varPosBase1; + int selfFaceTypeLen = VarInt.peek(buf, varPos1); + if (selfFaceTypeLen < 0) { + throw ProtocolException.invalidVarInt("SelfFaceType"); + } + + int selfFaceTypeVarIntLen = VarInt.size(selfFaceTypeLen); + if (selfFaceTypeLen > 4096000) { + throw ProtocolException.stringTooLong("SelfFaceType", selfFaceTypeLen, 4096000); + } + + if (varPos1 + selfFaceTypeVarIntLen + selfFaceTypeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SelfFaceType", varPos1 + selfFaceTypeVarIntLen + selfFaceTypeLen, buf.readableBytes()); + } + + obj.selfFaceType = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.faceType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 25); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("BlockSetId", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 33 + varPosBase2; + int blockSetIdLen = VarInt.peek(buf, varPos2); + if (blockSetIdLen < 0) { + throw ProtocolException.invalidVarInt("BlockSetId"); + } + + int blockSetIdVarIntLen = VarInt.size(blockSetIdLen); + if (blockSetIdLen > 4096000) { + throw ProtocolException.stringTooLong("BlockSetId", blockSetIdLen, 4096000); + } + + if (varPos2 + blockSetIdVarIntLen + blockSetIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BlockSetId", varPos2 + blockSetIdVarIntLen + blockSetIdLen, buf.readableBytes()); + } + + obj.blockSetId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 29); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("Filler", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 33 + varPosBase3; + int fillerCount = VarInt.peek(buf, varPos3); + if (fillerCount < 0) { + throw ProtocolException.invalidVarInt("Filler"); + } + + int varIntLen = VarInt.size(fillerCount); + if (fillerCount > 4096000) { + throw ProtocolException.arrayTooLong("Filler", fillerCount, 4096000); + } + + if (varPos3 + varIntLen + fillerCount * 12L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Filler", varPos3 + varIntLen + fillerCount * 12, buf.readableBytes()); + } + + obj.filler = new Vector3i[fillerCount]; + int elemPos = varPos3 + varIntLen; + + for (int i = 0; i < fillerCount; i++) { + obj.filler[i] = Vector3i.deserialize(buf, elemPos); + elemPos += Vector3i.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 33 + buf.getIntLE(offset + 21); - int selfFaceTypeLen = VarInt.peek(buf, varPos1); - if (selfFaceTypeLen < 0) { - throw ProtocolException.negativeLength("SelfFaceType", selfFaceTypeLen); - } - - if (selfFaceTypeLen > 4096000) { - throw ProtocolException.stringTooLong("SelfFaceType", selfFaceTypeLen, 4096000); - } - - obj.selfFaceType = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 33 + buf.getIntLE(offset + 25); - int blockSetIdLen = VarInt.peek(buf, varPos2); - if (blockSetIdLen < 0) { - throw ProtocolException.negativeLength("BlockSetId", blockSetIdLen); - } - - if (blockSetIdLen > 4096000) { - throw ProtocolException.stringTooLong("BlockSetId", blockSetIdLen, 4096000); - } - - obj.blockSetId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 33 + buf.getIntLE(offset + 29); - int fillerCount = VarInt.peek(buf, varPos3); - if (fillerCount < 0) { - throw ProtocolException.negativeLength("Filler", fillerCount); - } - - if (fillerCount > 4096000) { - throw ProtocolException.arrayTooLong("Filler", fillerCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + fillerCount * 12L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Filler", varPos3 + varIntLen + fillerCount * 12, buf.readableBytes()); - } - - obj.filler = new Vector3i[fillerCount]; - int elemPos = varPos3 + varIntLen; - - for (int i = 0; i < fillerCount; i++) { - obj.filler[i] = Vector3i.deserialize(buf, elemPos); - elemPos += Vector3i.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -163,9 +202,13 @@ public class RequiredBlockFaceSupport { int maxEnd = 33; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 17); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("FaceType", fieldOffset0, maxEnd); + } + int pos0 = offset + 33 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -173,9 +216,13 @@ public class RequiredBlockFaceSupport { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 21); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("SelfFaceType", fieldOffset1, maxEnd); + } + int pos1 = offset + 33 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -183,9 +230,13 @@ public class RequiredBlockFaceSupport { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 25); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("BlockSetId", fieldOffset2, maxEnd); + } + int pos2 = offset + 33 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -193,9 +244,13 @@ public class RequiredBlockFaceSupport { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 29); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 33) { + throw ProtocolException.invalidOffset("Filler", fieldOffset3, maxEnd); + } + int pos3 = offset + 33 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos3 += Vector3i.computeBytesConsumed(buf, pos3); @@ -308,115 +363,109 @@ public class RequiredBlockFaceSupport { return ValidationResult.error("Buffer too small: expected at least 33 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int faceTypeOffset = buffer.getIntLE(offset + 17); - if (faceTypeOffset < 0) { - return ValidationResult.error("Invalid offset for FaceType"); - } + int v = buffer.getByte(offset + 13) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid SupportMatch value for Support"); + } else { + v = buffer.getByte(offset + 14) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid SupportMatch value for MatchSelf"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 17); + if (v < 0 || v > buffer.writerIndex() - offset - 33) { + return ValidationResult.error("Invalid offset for FaceType"); + } - int pos = offset + 33 + faceTypeOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FaceType"); - } + int pos = offset + 33 + v; + int faceTypeLen = VarInt.peek(buffer, pos); + if (faceTypeLen < 0) { + return ValidationResult.error("Invalid string length for FaceType"); + } - int faceTypeLen = VarInt.peek(buffer, pos); - if (faceTypeLen < 0) { - return ValidationResult.error("Invalid string length for FaceType"); - } + if (faceTypeLen > 4096000) { + return ValidationResult.error("FaceType exceeds max length 4096000"); + } - if (faceTypeLen > 4096000) { - return ValidationResult.error("FaceType exceeds max length 4096000"); - } + pos += VarInt.size(faceTypeLen); + pos += faceTypeLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading FaceType"); + } + } - pos += VarInt.length(buffer, pos); - pos += faceTypeLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading FaceType"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 21); + if (v < 0 || v > buffer.writerIndex() - offset - 33) { + return ValidationResult.error("Invalid offset for SelfFaceType"); + } + + int posx = offset + 33 + v; + int selfFaceTypeLen = VarInt.peek(buffer, posx); + if (selfFaceTypeLen < 0) { + return ValidationResult.error("Invalid string length for SelfFaceType"); + } + + if (selfFaceTypeLen > 4096000) { + return ValidationResult.error("SelfFaceType exceeds max length 4096000"); + } + + posx += VarInt.size(selfFaceTypeLen); + posx += selfFaceTypeLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading SelfFaceType"); + } + } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 25); + if (v < 0 || v > buffer.writerIndex() - offset - 33) { + return ValidationResult.error("Invalid offset for BlockSetId"); + } + + int posxx = offset + 33 + v; + int blockSetIdLen = VarInt.peek(buffer, posxx); + if (blockSetIdLen < 0) { + return ValidationResult.error("Invalid string length for BlockSetId"); + } + + if (blockSetIdLen > 4096000) { + return ValidationResult.error("BlockSetId exceeds max length 4096000"); + } + + posxx += VarInt.size(blockSetIdLen); + posxx += blockSetIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading BlockSetId"); + } + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 29); + if (v < 0 || v > buffer.writerIndex() - offset - 33) { + return ValidationResult.error("Invalid offset for Filler"); + } + + int posxxx = offset + 33 + v; + int fillerCount = VarInt.peek(buffer, posxxx); + if (fillerCount < 0) { + return ValidationResult.error("Invalid array count for Filler"); + } + + if (fillerCount > 4096000) { + return ValidationResult.error("Filler exceeds max length 4096000"); + } + + posxxx += VarInt.size(fillerCount); + posxxx += fillerCount * 12; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Filler"); + } + } + + return ValidationResult.OK; } } - - if ((nullBits & 2) != 0) { - int selfFaceTypeOffset = buffer.getIntLE(offset + 21); - if (selfFaceTypeOffset < 0) { - return ValidationResult.error("Invalid offset for SelfFaceType"); - } - - int posx = offset + 33 + selfFaceTypeOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SelfFaceType"); - } - - int selfFaceTypeLen = VarInt.peek(buffer, posx); - if (selfFaceTypeLen < 0) { - return ValidationResult.error("Invalid string length for SelfFaceType"); - } - - if (selfFaceTypeLen > 4096000) { - return ValidationResult.error("SelfFaceType exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += selfFaceTypeLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading SelfFaceType"); - } - } - - if ((nullBits & 4) != 0) { - int blockSetIdOffset = buffer.getIntLE(offset + 25); - if (blockSetIdOffset < 0) { - return ValidationResult.error("Invalid offset for BlockSetId"); - } - - int posxx = offset + 33 + blockSetIdOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockSetId"); - } - - int blockSetIdLen = VarInt.peek(buffer, posxx); - if (blockSetIdLen < 0) { - return ValidationResult.error("Invalid string length for BlockSetId"); - } - - if (blockSetIdLen > 4096000) { - return ValidationResult.error("BlockSetId exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += blockSetIdLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading BlockSetId"); - } - } - - if ((nullBits & 8) != 0) { - int fillerOffset = buffer.getIntLE(offset + 29); - if (fillerOffset < 0) { - return ValidationResult.error("Invalid offset for Filler"); - } - - int posxxx = offset + 33 + fillerOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Filler"); - } - - int fillerCount = VarInt.peek(buffer, posxxx); - if (fillerCount < 0) { - return ValidationResult.error("Invalid array count for Filler"); - } - - if (fillerCount > 4096000) { - return ValidationResult.error("Filler exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += fillerCount * 12; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Filler"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ResetCooldownInteraction.java b/src/com/hypixel/hytale/protocol/ResetCooldownInteraction.java index 4c727959..7619d386 100644 --- a/src/com/hypixel/hytale/protocol/ResetCooldownInteraction.java +++ b/src/com/hypixel/hytale/protocol/ResetCooldownInteraction.java @@ -69,83 +69,117 @@ public class ResetCooldownInteraction extends SimpleInteraction { @Nonnull public static ResetCooldownInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ResetCooldownInteraction obj = new ResetCooldownInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 43 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 43) { + throw ProtocolException.bufferTooSmall("ResetCooldownInteraction", 43, buf.readableBytes() - offset); + } else { + ResetCooldownInteraction obj = new ResetCooldownInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 43 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 43 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 43 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 43 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 43 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 43 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 43 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 43 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 39); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Cooldown", varPosBase5, buf.readableBytes()); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + int varPos5 = offset + 43 + varPosBase5; + obj.cooldown = InteractionCooldown.deserialize(buf, varPos5); } - } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 43 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + return obj; } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 43 + buf.getIntLE(offset + 39); - obj.cooldown = InteractionCooldown.deserialize(buf, varPos5); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -153,6 +187,10 @@ public class ResetCooldownInteraction extends SimpleInteraction { int maxEnd = 43; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 43 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -162,9 +200,13 @@ public class ResetCooldownInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 43 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -177,6 +219,10 @@ public class ResetCooldownInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 43 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -186,9 +232,13 @@ public class ResetCooldownInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 43 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -196,6 +246,10 @@ public class ResetCooldownInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 43 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -205,6 +259,10 @@ public class ResetCooldownInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 39); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Cooldown", fieldOffset5, maxEnd); + } + int pos5 = offset + 43 + fieldOffset5; pos5 += InteractionCooldown.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { @@ -360,138 +418,124 @@ public class ResetCooldownInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 43 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 43 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 43 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 43 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 43 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 43 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 43 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Cooldown"); + } + + int posxx = offset + 43 + v; + ValidationResult cooldownResult = InteractionCooldown.validateStructure(buffer, posxx); + if (!cooldownResult.isValid()) { + return ValidationResult.error("Invalid Cooldown: " + cooldownResult.error()); + } + + posxx += InteractionCooldown.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 43 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 43 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 43 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 43 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int cooldownOffset = buffer.getIntLE(offset + 39); - if (cooldownOffset < 0) { - return ValidationResult.error("Invalid offset for Cooldown"); - } - - int posxxxxx = offset + 43 + cooldownOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Cooldown"); - } - - ValidationResult cooldownResult = InteractionCooldown.validateStructure(buffer, posxxxxx); - if (!cooldownResult.isValid()) { - return ValidationResult.error("Invalid Cooldown: " + cooldownResult.error()); - } - - posxxxxx += InteractionCooldown.computeBytesConsumed(buffer, posxxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ResourceType.java b/src/com/hypixel/hytale/protocol/ResourceType.java index efa01359..3c475b68 100644 --- a/src/com/hypixel/hytale/protocol/ResourceType.java +++ b/src/com/hypixel/hytale/protocol/ResourceType.java @@ -35,37 +35,61 @@ public class ResourceType { @Nonnull public static ResourceType deserialize(@Nonnull ByteBuf buf, int offset) { - ResourceType obj = new ResourceType(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ResourceType", 9, buf.readableBytes() - offset); + } else { + ResourceType obj = new ResourceType(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Icon", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int iconLen = VarInt.peek(buf, varPos1); + if (iconLen < 0) { + throw ProtocolException.invalidVarInt("Icon"); + } + + int iconVarIntLen = VarInt.size(iconLen); + if (iconLen > 4096000) { + throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + } + + if (varPos1 + iconVarIntLen + iconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Icon", varPos1 + iconVarIntLen + iconLen, buf.readableBytes()); + } + + obj.icon = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int iconLen = VarInt.peek(buf, varPos1); - if (iconLen < 0) { - throw ProtocolException.negativeLength("Icon", iconLen); - } - - if (iconLen > 4096000) { - throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); - } - - obj.icon = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,9 +97,13 @@ public class ResourceType { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -83,9 +111,13 @@ public class ResourceType { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Icon", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -146,15 +178,11 @@ public class ResourceType { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 1); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 9 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -164,7 +192,7 @@ public class ResourceType { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -173,15 +201,11 @@ public class ResourceType { if ((nullBits & 2) != 0) { int iconOffset = buffer.getIntLE(offset + 5); - if (iconOffset < 0) { + if (iconOffset < 0 || iconOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Icon"); } int posx = offset + 9 + iconOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Icon"); - } - int iconLen = VarInt.peek(buffer, posx); if (iconLen < 0) { return ValidationResult.error("Invalid string length for Icon"); @@ -191,7 +215,7 @@ public class ResourceType { return ValidationResult.error("Icon exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(iconLen); posx += iconLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Icon"); diff --git a/src/com/hypixel/hytale/protocol/ReverbEffect.java b/src/com/hypixel/hytale/protocol/ReverbEffect.java index 66b31a8b..e0856f53 100644 --- a/src/com/hypixel/hytale/protocol/ReverbEffect.java +++ b/src/com/hypixel/hytale/protocol/ReverbEffect.java @@ -89,39 +89,47 @@ public class ReverbEffect { @Nonnull public static ReverbEffect deserialize(@Nonnull ByteBuf buf, int offset) { - ReverbEffect obj = new ReverbEffect(); - byte nullBits = buf.getByte(offset); - obj.dryGain = buf.getFloatLE(offset + 1); - obj.modalDensity = buf.getFloatLE(offset + 5); - obj.diffusion = buf.getFloatLE(offset + 9); - obj.gain = buf.getFloatLE(offset + 13); - obj.highFrequencyGain = buf.getFloatLE(offset + 17); - obj.decayTime = buf.getFloatLE(offset + 21); - obj.highFrequencyDecayRatio = buf.getFloatLE(offset + 25); - obj.reflectionGain = buf.getFloatLE(offset + 29); - obj.reflectionDelay = buf.getFloatLE(offset + 33); - obj.lateReverbGain = buf.getFloatLE(offset + 37); - obj.lateReverbDelay = buf.getFloatLE(offset + 41); - obj.roomRolloffFactor = buf.getFloatLE(offset + 45); - obj.airAbsorptionHighFrequencyGain = buf.getFloatLE(offset + 49); - obj.limitDecayHighFrequency = buf.getByte(offset + 53) != 0; - int pos = offset + 54; - if ((nullBits & 1) != 0) { - int idLen = VarInt.peek(buf, pos); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 54) { + throw ProtocolException.bufferTooSmall("ReverbEffect", 54, buf.readableBytes() - offset); + } else { + ReverbEffect obj = new ReverbEffect(); + byte nullBits = buf.getByte(offset); + obj.dryGain = buf.getFloatLE(offset + 1); + obj.modalDensity = buf.getFloatLE(offset + 5); + obj.diffusion = buf.getFloatLE(offset + 9); + obj.gain = buf.getFloatLE(offset + 13); + obj.highFrequencyGain = buf.getFloatLE(offset + 17); + obj.decayTime = buf.getFloatLE(offset + 21); + obj.highFrequencyDecayRatio = buf.getFloatLE(offset + 25); + obj.reflectionGain = buf.getFloatLE(offset + 29); + obj.reflectionDelay = buf.getFloatLE(offset + 33); + obj.lateReverbGain = buf.getFloatLE(offset + 37); + obj.lateReverbDelay = buf.getFloatLE(offset + 41); + obj.roomRolloffFactor = buf.getFloatLE(offset + 45); + obj.airAbsorptionHighFrequencyGain = buf.getFloatLE(offset + 49); + obj.limitDecayHighFrequency = buf.getByte(offset + 53) != 0; + int pos = offset + 54; + if ((nullBits & 1) != 0) { + int idLen = VarInt.peek(buf, pos); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (pos + idVarLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", pos + idVarLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += idVarLen + idLen; } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - int idVarLen = VarInt.length(buf, pos); - obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += idVarLen + idLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -129,7 +137,7 @@ public class ReverbEffect { int pos = offset + 54; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -186,7 +194,7 @@ public class ReverbEffect { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); diff --git a/src/com/hypixel/hytale/protocol/RoofConnectedBlockRuleSet.java b/src/com/hypixel/hytale/protocol/RoofConnectedBlockRuleSet.java index b00b5fe9..1df51404 100644 --- a/src/com/hypixel/hytale/protocol/RoofConnectedBlockRuleSet.java +++ b/src/com/hypixel/hytale/protocol/RoofConnectedBlockRuleSet.java @@ -47,35 +47,59 @@ public class RoofConnectedBlockRuleSet { @Nonnull public static RoofConnectedBlockRuleSet deserialize(@Nonnull ByteBuf buf, int offset) { - RoofConnectedBlockRuleSet obj = new RoofConnectedBlockRuleSet(); - byte nullBits = buf.getByte(offset); - obj.topperBlockId = buf.getIntLE(offset + 1); - obj.width = buf.getIntLE(offset + 5); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 21 + buf.getIntLE(offset + 9); - obj.regular = StairConnectedBlockRuleSet.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("RoofConnectedBlockRuleSet", 21, buf.readableBytes() - offset); + } else { + RoofConnectedBlockRuleSet obj = new RoofConnectedBlockRuleSet(); + byte nullBits = buf.getByte(offset); + obj.topperBlockId = buf.getIntLE(offset + 1); + obj.width = buf.getIntLE(offset + 5); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Regular", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 21 + buf.getIntLE(offset + 13); - obj.hollow = StairConnectedBlockRuleSet.deserialize(buf, varPos1); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 21 + buf.getIntLE(offset + 17); - int materialNameLen = VarInt.peek(buf, varPos2); - if (materialNameLen < 0) { - throw ProtocolException.negativeLength("MaterialName", materialNameLen); + int varPos0 = offset + 21 + varPosBase0; + obj.regular = StairConnectedBlockRuleSet.deserialize(buf, varPos0); } - if (materialNameLen > 4096000) { - throw ProtocolException.stringTooLong("MaterialName", materialNameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 13); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Hollow", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + obj.hollow = StairConnectedBlockRuleSet.deserialize(buf, varPos1); } - obj.materialName = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 17); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("MaterialName", varPosBase2, buf.readableBytes()); + } - return obj; + int varPos2 = offset + 21 + varPosBase2; + int materialNameLen = VarInt.peek(buf, varPos2); + if (materialNameLen < 0) { + throw ProtocolException.invalidVarInt("MaterialName"); + } + + int materialNameVarIntLen = VarInt.size(materialNameLen); + if (materialNameLen > 4096000) { + throw ProtocolException.stringTooLong("MaterialName", materialNameLen, 4096000); + } + + if (varPos2 + materialNameVarIntLen + materialNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("MaterialName", varPos2 + materialNameVarIntLen + materialNameLen, buf.readableBytes()); + } + + obj.materialName = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -83,6 +107,10 @@ public class RoofConnectedBlockRuleSet { int maxEnd = 21; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Regular", fieldOffset0, maxEnd); + } + int pos0 = offset + 21 + fieldOffset0; pos0 += StairConnectedBlockRuleSet.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -92,6 +120,10 @@ public class RoofConnectedBlockRuleSet { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 13); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Hollow", fieldOffset1, maxEnd); + } + int pos1 = offset + 21 + fieldOffset1; pos1 += StairConnectedBlockRuleSet.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -101,9 +133,13 @@ public class RoofConnectedBlockRuleSet { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 17); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("MaterialName", fieldOffset2, maxEnd); + } + int pos2 = offset + 21 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -183,15 +219,11 @@ public class RoofConnectedBlockRuleSet { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int regularOffset = buffer.getIntLE(offset + 9); - if (regularOffset < 0) { + if (regularOffset < 0 || regularOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Regular"); } int pos = offset + 21 + regularOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Regular"); - } - ValidationResult regularResult = StairConnectedBlockRuleSet.validateStructure(buffer, pos); if (!regularResult.isValid()) { return ValidationResult.error("Invalid Regular: " + regularResult.error()); @@ -202,35 +234,27 @@ public class RoofConnectedBlockRuleSet { if ((nullBits & 2) != 0) { int hollowOffset = buffer.getIntLE(offset + 13); - if (hollowOffset < 0) { + if (hollowOffset < 0 || hollowOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Hollow"); } - int posx = offset + 21 + hollowOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Hollow"); - } - - ValidationResult hollowResult = StairConnectedBlockRuleSet.validateStructure(buffer, posx); + int pos = offset + 21 + hollowOffset; + ValidationResult hollowResult = StairConnectedBlockRuleSet.validateStructure(buffer, pos); if (!hollowResult.isValid()) { return ValidationResult.error("Invalid Hollow: " + hollowResult.error()); } - posx += StairConnectedBlockRuleSet.computeBytesConsumed(buffer, posx); + pos += StairConnectedBlockRuleSet.computeBytesConsumed(buffer, pos); } if ((nullBits & 4) != 0) { int materialNameOffset = buffer.getIntLE(offset + 17); - if (materialNameOffset < 0) { + if (materialNameOffset < 0 || materialNameOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for MaterialName"); } - int posxx = offset + 21 + materialNameOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaterialName"); - } - - int materialNameLen = VarInt.peek(buffer, posxx); + int pos = offset + 21 + materialNameOffset; + int materialNameLen = VarInt.peek(buffer, pos); if (materialNameLen < 0) { return ValidationResult.error("Invalid string length for MaterialName"); } @@ -239,9 +263,9 @@ public class RoofConnectedBlockRuleSet { return ValidationResult.error("MaterialName exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += materialNameLen; - if (posxx > buffer.writerIndex()) { + pos += VarInt.size(materialNameLen); + pos += materialNameLen; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading MaterialName"); } } diff --git a/src/com/hypixel/hytale/protocol/SortType.java b/src/com/hypixel/hytale/protocol/RoofState.java similarity index 54% rename from src/com/hypixel/hytale/protocol/SortType.java rename to src/com/hypixel/hytale/protocol/RoofState.java index 6cdd3e01..a80ec75f 100644 --- a/src/com/hypixel/hytale/protocol/SortType.java +++ b/src/com/hypixel/hytale/protocol/RoofState.java @@ -2,15 +2,15 @@ package com.hypixel.hytale.protocol; import com.hypixel.hytale.protocol.io.ProtocolException; -public enum SortType { - Name(0), - Type(1), - Rarity(2); +public enum RoofState { + Any(0), + Roofed(1), + Unroofed(2); - public static final SortType[] VALUES = values(); + public static final RoofState[] VALUES = values(); private final int value; - private SortType(int value) { + private RoofState(int value) { this.value = value; } @@ -18,11 +18,11 @@ public enum SortType { return this.value; } - public static SortType fromValue(int value) { + public static RoofState fromValue(int value) { if (value >= 0 && value < VALUES.length) { return VALUES[value]; } else { - throw ProtocolException.invalidEnumValue("SortType", value); + throw ProtocolException.invalidEnumValue("RoofState", value); } } } diff --git a/src/com/hypixel/hytale/protocol/RootInteraction.java b/src/com/hypixel/hytale/protocol/RootInteraction.java index 042bd176..3e203eb6 100644 --- a/src/com/hypixel/hytale/protocol/RootInteraction.java +++ b/src/com/hypixel/hytale/protocol/RootInteraction.java @@ -70,106 +70,145 @@ public class RootInteraction { @Nonnull public static RootInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - RootInteraction obj = new RootInteraction(); - byte nullBits = buf.getByte(offset); - obj.clickQueuingTimeout = buf.getFloatLE(offset + 1); - obj.requireNewClick = buf.getByte(offset + 5) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 30 + buf.getIntLE(offset + 6); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 30) { + throw ProtocolException.bufferTooSmall("RootInteraction", 30, buf.readableBytes() - offset); + } else { + RootInteraction obj = new RootInteraction(); + byte nullBits = buf.getByte(offset); + obj.clickQueuingTimeout = buf.getFloatLE(offset + 1); + obj.requireNewClick = buf.getByte(offset + 5) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 30 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 10); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Interactions", varPosBase1, buf.readableBytes()); + } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + int varPos1 = offset + 30 + varPosBase1; + int interactionsCount = VarInt.peek(buf, varPos1); + if (interactionsCount < 0) { + throw ProtocolException.invalidVarInt("Interactions"); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 30 + buf.getIntLE(offset + 10); - int interactionsCount = VarInt.peek(buf, varPos1); - if (interactionsCount < 0) { - throw ProtocolException.negativeLength("Interactions", interactionsCount); - } + int varIntLen = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.arrayTooLong("Interactions", interactionsCount, 4096000); + } - if (interactionsCount > 4096000) { - throw ProtocolException.arrayTooLong("Interactions", interactionsCount, 4096000); - } + if (varPos1 + varIntLen + interactionsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Interactions", varPos1 + varIntLen + interactionsCount * 4, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + interactionsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Interactions", varPos1 + varIntLen + interactionsCount * 4, buf.readableBytes()); - } + obj.interactions = new int[interactionsCount]; - obj.interactions = new int[interactionsCount]; - - for (int i = 0; i < interactionsCount; i++) { - obj.interactions[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 30 + buf.getIntLE(offset + 14); - obj.cooldown = InteractionCooldown.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 30 + buf.getIntLE(offset + 18); - int settingsCount = VarInt.peek(buf, varPos3); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); - } - - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos3 + varIntLen; - - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - RootInteractionSettings val = RootInteractionSettings.deserialize(buf, ++dictPos); - dictPos += RootInteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + for (int i = 0; i < interactionsCount; i++) { + obj.interactions[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 14); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Cooldown", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 30 + varPosBase2; + obj.cooldown = InteractionCooldown.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 18); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Settings", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 30 + varPosBase3; + int settingsCount = VarInt.peek(buf, varPos3); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } + + int varIntLenx = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos3 + varIntLenx; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + RootInteractionSettings val = RootInteractionSettings.deserialize(buf, ++dictPos); + dictPos += RootInteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 22); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Rules", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 30 + varPosBase4; + obj.rules = InteractionRules.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 26); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Tags", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 30 + varPosBase5; + int tagsCount = VarInt.peek(buf, varPos5); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLenx = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos5 + varIntLenx + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos5 + varIntLenx + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos5 + varIntLenx + ix * 4); + } + } + + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 30 + buf.getIntLE(offset + 22); - obj.rules = InteractionRules.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 30 + buf.getIntLE(offset + 26); - int tagsCount = VarInt.peek(buf, varPos5); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos5 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos5 + varIntLen + ix * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -177,9 +216,13 @@ public class RootInteraction { int maxEnd = 30; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 30 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -187,9 +230,13 @@ public class RootInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 10); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Interactions", fieldOffset1, maxEnd); + } + int pos1 = offset + 30 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 4; + pos1 += VarInt.size(arrLen) + arrLen * 4; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -197,6 +244,10 @@ public class RootInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 14); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Cooldown", fieldOffset2, maxEnd); + } + int pos2 = offset + 30 + fieldOffset2; pos2 += InteractionCooldown.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -206,9 +257,13 @@ public class RootInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 18); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Settings", fieldOffset3, maxEnd); + } + int pos3 = offset + 30 + fieldOffset3; int dictLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos3 = ++pos3 + RootInteractionSettings.computeBytesConsumed(buf, pos3); @@ -221,6 +276,10 @@ public class RootInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 22); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Rules", fieldOffset4, maxEnd); + } + int pos4 = offset + 30 + fieldOffset4; pos4 += InteractionRules.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -230,9 +289,13 @@ public class RootInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 26); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 30) { + throw ProtocolException.invalidOffset("Tags", fieldOffset5, maxEnd); + } + int pos5 = offset + 30 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + arrLen * 4; + pos5 += VarInt.size(arrLen) + arrLen * 4; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -394,15 +457,11 @@ public class RootInteraction { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 6); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 30 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -412,7 +471,7 @@ public class RootInteraction { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -421,15 +480,11 @@ public class RootInteraction { if ((nullBits & 2) != 0) { int interactionsOffset = buffer.getIntLE(offset + 10); - if (interactionsOffset < 0) { + if (interactionsOffset < 0 || interactionsOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Interactions"); } int posx = offset + 30 + interactionsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Interactions"); - } - int interactionsCount = VarInt.peek(buffer, posx); if (interactionsCount < 0) { return ValidationResult.error("Invalid array count for Interactions"); @@ -439,7 +494,7 @@ public class RootInteraction { return ValidationResult.error("Interactions exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(interactionsCount); posx += interactionsCount * 4; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Interactions"); @@ -448,15 +503,11 @@ public class RootInteraction { if ((nullBits & 4) != 0) { int cooldownOffset = buffer.getIntLE(offset + 14); - if (cooldownOffset < 0) { + if (cooldownOffset < 0 || cooldownOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Cooldown"); } int posxx = offset + 30 + cooldownOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Cooldown"); - } - ValidationResult cooldownResult = InteractionCooldown.validateStructure(buffer, posxx); if (!cooldownResult.isValid()) { return ValidationResult.error("Invalid Cooldown: " + cooldownResult.error()); @@ -467,16 +518,12 @@ public class RootInteraction { if ((nullBits & 8) != 0) { int settingsOffset = buffer.getIntLE(offset + 18); - if (settingsOffset < 0) { + if (settingsOffset < 0 || settingsOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Settings"); } - int posxxx = offset + 30 + settingsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posxxx); + int posxx = offset + 30 + settingsOffset; + int settingsCount = VarInt.peek(buffer, posxx); if (settingsCount < 0) { return ValidationResult.error("Invalid dictionary count for Settings"); } @@ -485,44 +532,41 @@ public class RootInteraction { return ValidationResult.error("Settings exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxx += VarInt.size(settingsCount); for (int i = 0; i < settingsCount; i++) { - posxxx = ++posxxx + RootInteractionSettings.computeBytesConsumed(buffer, posxxx); + int v = buffer.getByte(posxx) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + posxx = ++posxx + RootInteractionSettings.computeBytesConsumed(buffer, posxx); } } if ((nullBits & 16) != 0) { int rulesOffset = buffer.getIntLE(offset + 22); - if (rulesOffset < 0) { + if (rulesOffset < 0 || rulesOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Rules"); } - int posxxxx = offset + 30 + rulesOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxxxx); + int posxxx = offset + 30 + rulesOffset; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxxx); if (!rulesResult.isValid()) { return ValidationResult.error("Invalid Rules: " + rulesResult.error()); } - posxxxx += InteractionRules.computeBytesConsumed(buffer, posxxxx); + posxxx += InteractionRules.computeBytesConsumed(buffer, posxxx); } if ((nullBits & 32) != 0) { int tagsOffset = buffer.getIntLE(offset + 26); - if (tagsOffset < 0) { + if (tagsOffset < 0 || tagsOffset > buffer.writerIndex() - offset - 30) { return ValidationResult.error("Invalid offset for Tags"); } - int posxxxxx = offset + 30 + tagsOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxxxx); + int posxxx = offset + 30 + tagsOffset; + int tagsCount = VarInt.peek(buffer, posxxx); if (tagsCount < 0) { return ValidationResult.error("Invalid array count for Tags"); } @@ -531,9 +575,9 @@ public class RootInteraction { return ValidationResult.error("Tags exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += tagsCount * 4; - if (posxxxxx > buffer.writerIndex()) { + posxxx += VarInt.size(tagsCount); + posxxx += tagsCount * 4; + if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Tags"); } } diff --git a/src/com/hypixel/hytale/protocol/RootInteractionSettings.java b/src/com/hypixel/hytale/protocol/RootInteractionSettings.java index fe78dfe4..3c11c153 100644 --- a/src/com/hypixel/hytale/protocol/RootInteractionSettings.java +++ b/src/com/hypixel/hytale/protocol/RootInteractionSettings.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -31,16 +32,20 @@ public class RootInteractionSettings { @Nonnull public static RootInteractionSettings deserialize(@Nonnull ByteBuf buf, int offset) { - RootInteractionSettings obj = new RootInteractionSettings(); - byte nullBits = buf.getByte(offset); - obj.allowSkipChainOnClick = buf.getByte(offset + 1) != 0; - int pos = offset + 2; - if ((nullBits & 1) != 0) { - obj.cooldown = InteractionCooldown.deserialize(buf, pos); - pos += InteractionCooldown.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("RootInteractionSettings", 2, buf.readableBytes() - offset); + } else { + RootInteractionSettings obj = new RootInteractionSettings(); + byte nullBits = buf.getByte(offset); + obj.allowSkipChainOnClick = buf.getByte(offset + 1) != 0; + int pos = offset + 2; + if ((nullBits & 1) != 0) { + obj.cooldown = InteractionCooldown.deserialize(buf, pos); + pos += InteractionCooldown.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/RotationNoise.java b/src/com/hypixel/hytale/protocol/RotationNoise.java index f90b4cbf..f6063949 100644 --- a/src/com/hypixel/hytale/protocol/RotationNoise.java +++ b/src/com/hypixel/hytale/protocol/RotationNoise.java @@ -38,84 +38,103 @@ public class RotationNoise { @Nonnull public static RotationNoise deserialize(@Nonnull ByteBuf buf, int offset) { - RotationNoise obj = new RotationNoise(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int pitchCount = VarInt.peek(buf, varPos0); - if (pitchCount < 0) { - throw ProtocolException.negativeLength("Pitch", pitchCount); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("RotationNoise", 13, buf.readableBytes() - offset); + } else { + RotationNoise obj = new RotationNoise(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Pitch", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int pitchCount = VarInt.peek(buf, varPos0); + if (pitchCount < 0) { + throw ProtocolException.invalidVarInt("Pitch"); + } + + int varIntLen = VarInt.size(pitchCount); + if (pitchCount > 4096000) { + throw ProtocolException.arrayTooLong("Pitch", pitchCount, 4096000); + } + + if (varPos0 + varIntLen + pitchCount * 23L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Pitch", varPos0 + varIntLen + pitchCount * 23, buf.readableBytes()); + } + + obj.pitch = new NoiseConfig[pitchCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < pitchCount; i++) { + obj.pitch[i] = NoiseConfig.deserialize(buf, elemPos); + elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); + } } - if (pitchCount > 4096000) { - throw ProtocolException.arrayTooLong("Pitch", pitchCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Yaw", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int yawCount = VarInt.peek(buf, varPos1); + if (yawCount < 0) { + throw ProtocolException.invalidVarInt("Yaw"); + } + + int varIntLenx = VarInt.size(yawCount); + if (yawCount > 4096000) { + throw ProtocolException.arrayTooLong("Yaw", yawCount, 4096000); + } + + if (varPos1 + varIntLenx + yawCount * 23L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Yaw", varPos1 + varIntLenx + yawCount * 23, buf.readableBytes()); + } + + obj.yaw = new NoiseConfig[yawCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < yawCount; i++) { + obj.yaw[i] = NoiseConfig.deserialize(buf, elemPos); + elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + pitchCount * 23L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Pitch", varPos0 + varIntLen + pitchCount * 23, buf.readableBytes()); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Roll", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int rollCount = VarInt.peek(buf, varPos2); + if (rollCount < 0) { + throw ProtocolException.invalidVarInt("Roll"); + } + + int varIntLenxx = VarInt.size(rollCount); + if (rollCount > 4096000) { + throw ProtocolException.arrayTooLong("Roll", rollCount, 4096000); + } + + if (varPos2 + varIntLenxx + rollCount * 23L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Roll", varPos2 + varIntLenxx + rollCount * 23, buf.readableBytes()); + } + + obj.roll = new NoiseConfig[rollCount]; + int elemPos = varPos2 + varIntLenxx; + + for (int i = 0; i < rollCount; i++) { + obj.roll[i] = NoiseConfig.deserialize(buf, elemPos); + elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); + } } - obj.pitch = new NoiseConfig[pitchCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < pitchCount; i++) { - obj.pitch[i] = NoiseConfig.deserialize(buf, elemPos); - elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int yawCount = VarInt.peek(buf, varPos1); - if (yawCount < 0) { - throw ProtocolException.negativeLength("Yaw", yawCount); - } - - if (yawCount > 4096000) { - throw ProtocolException.arrayTooLong("Yaw", yawCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + yawCount * 23L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Yaw", varPos1 + varIntLen + yawCount * 23, buf.readableBytes()); - } - - obj.yaw = new NoiseConfig[yawCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < yawCount; i++) { - obj.yaw[i] = NoiseConfig.deserialize(buf, elemPos); - elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int rollCount = VarInt.peek(buf, varPos2); - if (rollCount < 0) { - throw ProtocolException.negativeLength("Roll", rollCount); - } - - if (rollCount > 4096000) { - throw ProtocolException.arrayTooLong("Roll", rollCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + rollCount * 23L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Roll", varPos2 + varIntLen + rollCount * 23, buf.readableBytes()); - } - - obj.roll = new NoiseConfig[rollCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < rollCount; i++) { - obj.roll[i] = NoiseConfig.deserialize(buf, elemPos); - elemPos += NoiseConfig.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -123,9 +142,13 @@ public class RotationNoise { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Pitch", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += NoiseConfig.computeBytesConsumed(buf, pos0); @@ -138,9 +161,13 @@ public class RotationNoise { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Yaw", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += NoiseConfig.computeBytesConsumed(buf, pos1); @@ -153,9 +180,13 @@ public class RotationNoise { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Roll", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += NoiseConfig.computeBytesConsumed(buf, pos2); @@ -262,15 +293,11 @@ public class RotationNoise { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int pitchOffset = buffer.getIntLE(offset + 1); - if (pitchOffset < 0) { + if (pitchOffset < 0 || pitchOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Pitch"); } int pos = offset + 13 + pitchOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Pitch"); - } - int pitchCount = VarInt.peek(buffer, pos); if (pitchCount < 0) { return ValidationResult.error("Invalid array count for Pitch"); @@ -280,7 +307,7 @@ public class RotationNoise { return ValidationResult.error("Pitch exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(pitchCount); pos += pitchCount * 23; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Pitch"); @@ -289,15 +316,11 @@ public class RotationNoise { if ((nullBits & 2) != 0) { int yawOffset = buffer.getIntLE(offset + 5); - if (yawOffset < 0) { + if (yawOffset < 0 || yawOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Yaw"); } int posx = offset + 13 + yawOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Yaw"); - } - int yawCount = VarInt.peek(buffer, posx); if (yawCount < 0) { return ValidationResult.error("Invalid array count for Yaw"); @@ -307,7 +330,7 @@ public class RotationNoise { return ValidationResult.error("Yaw exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(yawCount); posx += yawCount * 23; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Yaw"); @@ -316,15 +339,11 @@ public class RotationNoise { if ((nullBits & 4) != 0) { int rollOffset = buffer.getIntLE(offset + 9); - if (rollOffset < 0) { + if (rollOffset < 0 || rollOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Roll"); } int posxx = offset + 13 + rollOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Roll"); - } - int rollCount = VarInt.peek(buffer, posxx); if (rollCount < 0) { return ValidationResult.error("Invalid array count for Roll"); @@ -334,7 +353,7 @@ public class RotationNoise { return ValidationResult.error("Roll exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(rollCount); posxx += rollCount * 23; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Roll"); diff --git a/src/com/hypixel/hytale/protocol/RunRootInteraction.java b/src/com/hypixel/hytale/protocol/RunRootInteraction.java index 1ea4c849..2d4f451e 100644 --- a/src/com/hypixel/hytale/protocol/RunRootInteraction.java +++ b/src/com/hypixel/hytale/protocol/RunRootInteraction.java @@ -68,79 +68,108 @@ public class RunRootInteraction extends SimpleInteraction { @Nonnull public static RunRootInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - RunRootInteraction obj = new RunRootInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.rootInteraction = buf.getIntLE(offset + 19); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 43 + buf.getIntLE(offset + 23); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 43) { + throw ProtocolException.bufferTooSmall("RunRootInteraction", 43, buf.readableBytes() - offset); + } else { + RunRootInteraction obj = new RunRootInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.rootInteraction = buf.getIntLE(offset + 19); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 23); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 43 + buf.getIntLE(offset + 27); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 43 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 27); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 43 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 43 + buf.getIntLE(offset + 31); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 31); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 43 + buf.getIntLE(offset + 35); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 43 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 35); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 43 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 39); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 43 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 43 + buf.getIntLE(offset + 39); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -148,6 +177,10 @@ public class RunRootInteraction extends SimpleInteraction { int maxEnd = 43; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 23); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 43 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -157,9 +190,13 @@ public class RunRootInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 27); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 43 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -172,6 +209,10 @@ public class RunRootInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 31); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 43 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -181,9 +222,13 @@ public class RunRootInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 35); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 43 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -191,6 +236,10 @@ public class RunRootInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 39); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 43 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -330,119 +379,109 @@ public class RunRootInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 43 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 23); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 43 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 43 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 43 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 43 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 43 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 43 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 27); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 43 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 31); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 43 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 35); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 43 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 39); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 43 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/SavedMovementStates.java b/src/com/hypixel/hytale/protocol/SavedMovementStates.java index 816daac6..8a3497bf 100644 --- a/src/com/hypixel/hytale/protocol/SavedMovementStates.java +++ b/src/com/hypixel/hytale/protocol/SavedMovementStates.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class SavedMovementStates { @Nonnull public static SavedMovementStates deserialize(@Nonnull ByteBuf buf, int offset) { - SavedMovementStates obj = new SavedMovementStates(); - obj.flying = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SavedMovementStates", 1, buf.readableBytes() - offset); + } else { + SavedMovementStates obj = new SavedMovementStates(); + obj.flying = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/SelectInteraction.java b/src/com/hypixel/hytale/protocol/SelectInteraction.java index 7eb4aaaf..d899150e 100644 --- a/src/com/hypixel/hytale/protocol/SelectInteraction.java +++ b/src/com/hypixel/hytale/protocol/SelectInteraction.java @@ -87,111 +87,150 @@ public class SelectInteraction extends SimpleInteraction { @Nonnull public static SelectInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - SelectInteraction obj = new SelectInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.ignoreOwner = buf.getByte(offset + 19) != 0; - obj.hitEntity = buf.getIntLE(offset + 20); - obj.failOn = FailOnType.fromValue(buf.getByte(offset + 24)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 53 + buf.getIntLE(offset + 25); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 53) { + throw ProtocolException.bufferTooSmall("SelectInteraction", 53, buf.readableBytes() - offset); + } else { + SelectInteraction obj = new SelectInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.ignoreOwner = buf.getByte(offset + 19) != 0; + obj.hitEntity = buf.getIntLE(offset + 20); + obj.failOn = FailOnType.fromValue(buf.getByte(offset + 24)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 25); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 53 + buf.getIntLE(offset + 29); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 53 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 29); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 53 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 33); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 53 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 37); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 53 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 41); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 53 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 45); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Selector", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 53 + varPosBase5; + obj.selector = Selector.deserialize(buf, varPos5); + } + + if ((nullBits & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 49); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("HitEntityRules", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 53 + varPosBase6; + int hitEntityRulesCount = VarInt.peek(buf, varPos6); + if (hitEntityRulesCount < 0) { + throw ProtocolException.invalidVarInt("HitEntityRules"); + } + + int varIntLenx = VarInt.size(hitEntityRulesCount); + if (hitEntityRulesCount > 4096000) { + throw ProtocolException.arrayTooLong("HitEntityRules", hitEntityRulesCount, 4096000); + } + + if (varPos6 + varIntLenx + hitEntityRulesCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("HitEntityRules", varPos6 + varIntLenx + hitEntityRulesCount * 5, buf.readableBytes()); + } + + obj.hitEntityRules = new HitEntity[hitEntityRulesCount]; + int elemPos = varPos6 + varIntLenx; + + for (int ix = 0; ix < hitEntityRulesCount; ix++) { + obj.hitEntityRules[ix] = HitEntity.deserialize(buf, elemPos); + elemPos += HitEntity.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 53 + buf.getIntLE(offset + 33); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 53 + buf.getIntLE(offset + 37); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 53 + buf.getIntLE(offset + 41); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 53 + buf.getIntLE(offset + 45); - obj.selector = Selector.deserialize(buf, varPos5); - } - - if ((nullBits & 64) != 0) { - int varPos6 = offset + 53 + buf.getIntLE(offset + 49); - int hitEntityRulesCount = VarInt.peek(buf, varPos6); - if (hitEntityRulesCount < 0) { - throw ProtocolException.negativeLength("HitEntityRules", hitEntityRulesCount); - } - - if (hitEntityRulesCount > 4096000) { - throw ProtocolException.arrayTooLong("HitEntityRules", hitEntityRulesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos6); - if (varPos6 + varIntLen + hitEntityRulesCount * 5L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("HitEntityRules", varPos6 + varIntLen + hitEntityRulesCount * 5, buf.readableBytes()); - } - - obj.hitEntityRules = new HitEntity[hitEntityRulesCount]; - int elemPos = varPos6 + varIntLen; - - for (int ix = 0; ix < hitEntityRulesCount; ix++) { - obj.hitEntityRules[ix] = HitEntity.deserialize(buf, elemPos); - elemPos += HitEntity.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -199,6 +238,10 @@ public class SelectInteraction extends SimpleInteraction { int maxEnd = 53; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 25); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 53 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -208,9 +251,13 @@ public class SelectInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 29); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 53 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -223,6 +270,10 @@ public class SelectInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 33); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 53 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -232,9 +283,13 @@ public class SelectInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 37); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 53 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -242,6 +297,10 @@ public class SelectInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 41); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 53 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -251,6 +310,10 @@ public class SelectInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 45); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Selector", fieldOffset5, maxEnd); + } + int pos5 = offset + 53 + fieldOffset5; pos5 += Selector.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { @@ -260,9 +323,13 @@ public class SelectInteraction extends SimpleInteraction { if ((nullBits & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 49); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("HitEntityRules", fieldOffset6, maxEnd); + } + int pos6 = offset + 53 + fieldOffset6; int arrLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos6 += HitEntity.computeBytesConsumed(buf, pos6); @@ -455,170 +522,157 @@ public class SelectInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 53 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 25); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 24) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid FailOnType value for FailOn"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 25); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 53 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 53 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 29); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 53 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 33); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 53 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 37); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 53 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 41); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 53 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int selectorOffset = buffer.getIntLE(offset + 45); - if (selectorOffset < 0) { - return ValidationResult.error("Invalid offset for Selector"); - } - - int posxxxxx = offset + 53 + selectorOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Selector"); - } - - ValidationResult selectorResult = Selector.validateStructure(buffer, posxxxxx); - if (!selectorResult.isValid()) { - return ValidationResult.error("Invalid Selector: " + selectorResult.error()); - } - - posxxxxx += Selector.computeBytesConsumed(buffer, posxxxxx); - } - - if ((nullBits & 64) != 0) { - int hitEntityRulesOffset = buffer.getIntLE(offset + 49); - if (hitEntityRulesOffset < 0) { - return ValidationResult.error("Invalid offset for HitEntityRules"); - } - - int posxxxxxx = offset + 53 + hitEntityRulesOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for HitEntityRules"); - } - - int hitEntityRulesCount = VarInt.peek(buffer, posxxxxxx); - if (hitEntityRulesCount < 0) { - return ValidationResult.error("Invalid array count for HitEntityRules"); - } - - if (hitEntityRulesCount > 4096000) { - return ValidationResult.error("HitEntityRules exceeds max length 4096000"); - } - - posxxxxxx += VarInt.length(buffer, posxxxxxx); - - for (int i = 0; i < hitEntityRulesCount; i++) { - ValidationResult structResult = HitEntity.validateStructure(buffer, posxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid HitEntity in HitEntityRules[" + i + "]: " + structResult.error()); + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - posxxxxxx += HitEntity.computeBytesConsumed(buffer, posxxxxxx); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 29); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 53 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } + } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 53 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 53 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 41); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 53 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 45); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for Selector"); + } + + int posxx = offset + 53 + v; + ValidationResult selectorResult = Selector.validateStructure(buffer, posxx); + if (!selectorResult.isValid()) { + return ValidationResult.error("Invalid Selector: " + selectorResult.error()); + } + + posxx += Selector.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 49); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for HitEntityRules"); + } + + int posxx = offset + 53 + v; + int hitEntityRulesCount = VarInt.peek(buffer, posxx); + if (hitEntityRulesCount < 0) { + return ValidationResult.error("Invalid array count for HitEntityRules"); + } + + if (hitEntityRulesCount > 4096000) { + return ValidationResult.error("HitEntityRules exceeds max length 4096000"); + } + + posxx += VarInt.size(hitEntityRulesCount); + + for (int i = 0; i < hitEntityRulesCount; i++) { + ValidationResult structResult = HitEntity.validateStructure(buffer, posxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid HitEntity in HitEntityRules[" + i + "]: " + structResult.error()); + } + + posxx += HitEntity.computeBytesConsumed(buffer, posxx); + } + } + + return ValidationResult.OK; } } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/SelectedHitEntity.java b/src/com/hypixel/hytale/protocol/SelectedHitEntity.java index fefc6f8c..b6f0f563 100644 --- a/src/com/hypixel/hytale/protocol/SelectedHitEntity.java +++ b/src/com/hypixel/hytale/protocol/SelectedHitEntity.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class SelectedHitEntity { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -14,7 +17,7 @@ public class SelectedHitEntity { public static final int MAX_SIZE = 53; public int networkId; @Nullable - public Vector3f hitLocation; + public Vector3fc hitLocation; @Nullable public Position position; @Nullable @@ -23,7 +26,7 @@ public class SelectedHitEntity { public SelectedHitEntity() { } - public SelectedHitEntity(int networkId, @Nullable Vector3f hitLocation, @Nullable Position position, @Nullable Direction bodyRotation) { + public SelectedHitEntity(int networkId, @Nullable Vector3fc hitLocation, @Nullable Position position, @Nullable Direction bodyRotation) { this.networkId = networkId; this.hitLocation = hitLocation; this.position = position; @@ -39,22 +42,26 @@ public class SelectedHitEntity { @Nonnull public static SelectedHitEntity deserialize(@Nonnull ByteBuf buf, int offset) { - SelectedHitEntity obj = new SelectedHitEntity(); - byte nullBits = buf.getByte(offset); - obj.networkId = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.hitLocation = Vector3f.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 53) { + throw ProtocolException.bufferTooSmall("SelectedHitEntity", 53, buf.readableBytes() - offset); + } else { + SelectedHitEntity obj = new SelectedHitEntity(); + byte nullBits = buf.getByte(offset); + obj.networkId = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.hitLocation = PacketIO.readVector3f(buf, offset + 5); + } - if ((nullBits & 2) != 0) { - obj.position = Position.deserialize(buf, offset + 17); - } + if ((nullBits & 2) != 0) { + obj.position = Position.deserialize(buf, offset + 17); + } - if ((nullBits & 4) != 0) { - obj.bodyRotation = Direction.deserialize(buf, offset + 41); - } + if ((nullBits & 4) != 0) { + obj.bodyRotation = Direction.deserialize(buf, offset + 41); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,7 +85,7 @@ public class SelectedHitEntity { buf.writeByte(nullBits); buf.writeIntLE(this.networkId); if (this.hitLocation != null) { - this.hitLocation.serialize(buf); + PacketIO.writeVector3f(buf, this.hitLocation); } else { buf.writeZero(12); } @@ -101,13 +108,18 @@ public class SelectedHitEntity { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 53 ? ValidationResult.error("Buffer too small: expected at least 53 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 53) { + return ValidationResult.error("Buffer too small: expected at least 53 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public SelectedHitEntity clone() { SelectedHitEntity copy = new SelectedHitEntity(); copy.networkId = this.networkId; - copy.hitLocation = this.hitLocation != null ? this.hitLocation.clone() : null; + copy.hitLocation = this.hitLocation; copy.position = this.position != null ? this.position.clone() : null; copy.bodyRotation = this.bodyRotation != null ? this.bodyRotation.clone() : null; return copy; diff --git a/src/com/hypixel/hytale/protocol/Selector.java b/src/com/hypixel/hytale/protocol/Selector.java index bf90586b..907b24f5 100644 --- a/src/com/hypixel/hytale/protocol/Selector.java +++ b/src/com/hypixel/hytale/protocol/Selector.java @@ -12,7 +12,7 @@ public abstract class Selector { @Nonnull public static Selector deserialize(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return (Selector)(switch (typeId) { case 0 -> AOECircleSelector.deserialize(buf, offset + typeIdLen); @@ -26,7 +26,7 @@ public abstract class Selector { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return typeIdLen + switch (typeId) { case 0 -> AOECircleSelector.computeBytesConsumed(buf, offset + typeIdLen); @@ -71,7 +71,7 @@ public abstract class Selector { public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { int typeId = VarInt.peek(buffer, offset); - int typeIdLen = VarInt.length(buffer, offset); + int typeIdLen = VarInt.size(typeId); return switch (typeId) { case 0 -> AOECircleSelector.validateStructure(buffer, offset + typeIdLen); diff --git a/src/com/hypixel/hytale/protocol/SerialInteraction.java b/src/com/hypixel/hytale/protocol/SerialInteraction.java index 775d5ee7..738218c3 100644 --- a/src/com/hypixel/hytale/protocol/SerialInteraction.java +++ b/src/com/hypixel/hytale/protocol/SerialInteraction.java @@ -63,99 +63,133 @@ public class SerialInteraction extends Interaction { @Nonnull public static SerialInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - SerialInteraction obj = new SerialInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 35 + buf.getIntLE(offset + 11); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 35) { + throw ProtocolException.bufferTooSmall("SerialInteraction", 35, buf.readableBytes() - offset); + } else { + SerialInteraction obj = new SerialInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 11); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 35 + buf.getIntLE(offset + 15); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 35 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 15); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 35 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 19); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 35 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 23); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 35 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 27); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 35 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 31); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("SerialInteractions", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 35 + varPosBase5; + int serialInteractionsCount = VarInt.peek(buf, varPos5); + if (serialInteractionsCount < 0) { + throw ProtocolException.invalidVarInt("SerialInteractions"); + } + + int varIntLenx = VarInt.size(serialInteractionsCount); + if (serialInteractionsCount > 4096000) { + throw ProtocolException.arrayTooLong("SerialInteractions", serialInteractionsCount, 4096000); + } + + if (varPos5 + varIntLenx + serialInteractionsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SerialInteractions", varPos5 + varIntLenx + serialInteractionsCount * 4, buf.readableBytes()); + } + + obj.serialInteractions = new int[serialInteractionsCount]; + + for (int ix = 0; ix < serialInteractionsCount; ix++) { + obj.serialInteractions[ix] = buf.getIntLE(varPos5 + varIntLenx + ix * 4); + } + } + + return obj; } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 35 + buf.getIntLE(offset + 19); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 35 + buf.getIntLE(offset + 23); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } - - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 35 + buf.getIntLE(offset + 27); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 35 + buf.getIntLE(offset + 31); - int serialInteractionsCount = VarInt.peek(buf, varPos5); - if (serialInteractionsCount < 0) { - throw ProtocolException.negativeLength("SerialInteractions", serialInteractionsCount); - } - - if (serialInteractionsCount > 4096000) { - throw ProtocolException.arrayTooLong("SerialInteractions", serialInteractionsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + serialInteractionsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("SerialInteractions", varPos5 + varIntLen + serialInteractionsCount * 4, buf.readableBytes()); - } - - obj.serialInteractions = new int[serialInteractionsCount]; - - for (int ix = 0; ix < serialInteractionsCount; ix++) { - obj.serialInteractions[ix] = buf.getIntLE(varPos5 + varIntLen + ix * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -163,6 +197,10 @@ public class SerialInteraction extends Interaction { int maxEnd = 35; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 11); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 35 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -172,9 +210,13 @@ public class SerialInteraction extends Interaction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 15); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 35 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -187,6 +229,10 @@ public class SerialInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 19); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 35 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -196,9 +242,13 @@ public class SerialInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 23); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 35 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -206,6 +256,10 @@ public class SerialInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 27); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 35 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -215,9 +269,13 @@ public class SerialInteraction extends Interaction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 31); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 35) { + throw ProtocolException.invalidOffset("SerialInteractions", fieldOffset5, maxEnd); + } + int pos5 = offset + 35 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5) + arrLen * 4; + pos5 += VarInt.size(arrLen) + arrLen * 4; if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } @@ -377,146 +435,132 @@ public class SerialInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 35 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 11); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 11); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 35 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 35 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 35 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 35 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 35 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 35 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 35) { + return ValidationResult.error("Invalid offset for SerialInteractions"); + } + + int posxx = offset + 35 + v; + int serialInteractionsCount = VarInt.peek(buffer, posxx); + if (serialInteractionsCount < 0) { + return ValidationResult.error("Invalid array count for SerialInteractions"); + } + + if (serialInteractionsCount > 4096000) { + return ValidationResult.error("SerialInteractions exceeds max length 4096000"); + } + + posxx += VarInt.size(serialInteractionsCount); + posxx += serialInteractionsCount * 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading SerialInteractions"); + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 15); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 35 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 19); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 35 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 23); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 35 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 27); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 35 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int serialInteractionsOffset = buffer.getIntLE(offset + 31); - if (serialInteractionsOffset < 0) { - return ValidationResult.error("Invalid offset for SerialInteractions"); - } - - int posxxxxx = offset + 35 + serialInteractionsOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SerialInteractions"); - } - - int serialInteractionsCount = VarInt.peek(buffer, posxxxxx); - if (serialInteractionsCount < 0) { - return ValidationResult.error("Invalid array count for SerialInteractions"); - } - - if (serialInteractionsCount > 4096000) { - return ValidationResult.error("SerialInteractions exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - posxxxxx += serialInteractionsCount * 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading SerialInteractions"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/ServerCameraSettings.java b/src/com/hypixel/hytale/protocol/ServerCameraSettings.java index d06428db..3c7a535c 100644 --- a/src/com/hypixel/hytale/protocol/ServerCameraSettings.java +++ b/src/com/hypixel/hytale/protocol/ServerCameraSettings.java @@ -1,10 +1,14 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2fc; +import org.joml.Vector3fc; public class ServerCameraSettings { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -51,15 +55,15 @@ public class ServerCameraSettings { @Nonnull public ApplyMovementType applyMovementType = ApplyMovementType.CharacterController; @Nullable - public Vector3f movementMultiplier; + public Vector3fc movementMultiplier; @Nonnull public ApplyLookType applyLookType = ApplyLookType.LocalPlayerLookOrientation; @Nullable - public Vector2f lookMultiplier; + public Vector2fc lookMultiplier; @Nonnull public MouseInputType mouseInputType = MouseInputType.LookAtTarget; @Nullable - public Vector3f planeNormal; + public Vector3fc planeNormal; public ServerCameraSettings() { } @@ -90,11 +94,11 @@ public class ServerCameraSettings { @Nullable Direction rotation, @Nonnull CanMoveType canMoveType, @Nonnull ApplyMovementType applyMovementType, - @Nullable Vector3f movementMultiplier, + @Nullable Vector3fc movementMultiplier, @Nonnull ApplyLookType applyLookType, - @Nullable Vector2f lookMultiplier, + @Nullable Vector2fc lookMultiplier, @Nonnull MouseInputType mouseInputType, - @Nullable Vector3f planeNormal + @Nullable Vector3fc planeNormal ) { this.positionLerpSpeed = positionLerpSpeed; this.rotationLerpSpeed = rotationLerpSpeed; @@ -163,63 +167,67 @@ public class ServerCameraSettings { @Nonnull public static ServerCameraSettings deserialize(@Nonnull ByteBuf buf, int offset) { - ServerCameraSettings obj = new ServerCameraSettings(); - byte nullBits = buf.getByte(offset); - obj.positionLerpSpeed = buf.getFloatLE(offset + 1); - obj.rotationLerpSpeed = buf.getFloatLE(offset + 5); - obj.distance = buf.getFloatLE(offset + 9); - obj.speedModifier = buf.getFloatLE(offset + 13); - obj.allowPitchControls = buf.getByte(offset + 17) != 0; - obj.displayCursor = buf.getByte(offset + 18) != 0; - obj.displayReticle = buf.getByte(offset + 19) != 0; - obj.mouseInputTargetType = MouseInputTargetType.fromValue(buf.getByte(offset + 20)); - obj.sendMouseMotion = buf.getByte(offset + 21) != 0; - obj.skipCharacterPhysics = buf.getByte(offset + 22) != 0; - obj.isFirstPerson = buf.getByte(offset + 23) != 0; - obj.movementForceRotationType = MovementForceRotationType.fromValue(buf.getByte(offset + 24)); - if ((nullBits & 1) != 0) { - obj.movementForceRotation = Direction.deserialize(buf, offset + 25); - } + if (buf.readableBytes() - offset < 154) { + throw ProtocolException.bufferTooSmall("ServerCameraSettings", 154, buf.readableBytes() - offset); + } else { + ServerCameraSettings obj = new ServerCameraSettings(); + byte nullBits = buf.getByte(offset); + obj.positionLerpSpeed = buf.getFloatLE(offset + 1); + obj.rotationLerpSpeed = buf.getFloatLE(offset + 5); + obj.distance = buf.getFloatLE(offset + 9); + obj.speedModifier = buf.getFloatLE(offset + 13); + obj.allowPitchControls = buf.getByte(offset + 17) != 0; + obj.displayCursor = buf.getByte(offset + 18) != 0; + obj.displayReticle = buf.getByte(offset + 19) != 0; + obj.mouseInputTargetType = MouseInputTargetType.fromValue(buf.getByte(offset + 20)); + obj.sendMouseMotion = buf.getByte(offset + 21) != 0; + obj.skipCharacterPhysics = buf.getByte(offset + 22) != 0; + obj.isFirstPerson = buf.getByte(offset + 23) != 0; + obj.movementForceRotationType = MovementForceRotationType.fromValue(buf.getByte(offset + 24)); + if ((nullBits & 1) != 0) { + obj.movementForceRotation = Direction.deserialize(buf, offset + 25); + } - obj.attachedToType = AttachedToType.fromValue(buf.getByte(offset + 37)); - obj.attachedToEntityId = buf.getIntLE(offset + 38); - obj.eyeOffset = buf.getByte(offset + 42) != 0; - obj.positionDistanceOffsetType = PositionDistanceOffsetType.fromValue(buf.getByte(offset + 43)); - if ((nullBits & 2) != 0) { - obj.positionOffset = Position.deserialize(buf, offset + 44); - } + obj.attachedToType = AttachedToType.fromValue(buf.getByte(offset + 37)); + obj.attachedToEntityId = buf.getIntLE(offset + 38); + obj.eyeOffset = buf.getByte(offset + 42) != 0; + obj.positionDistanceOffsetType = PositionDistanceOffsetType.fromValue(buf.getByte(offset + 43)); + if ((nullBits & 2) != 0) { + obj.positionOffset = Position.deserialize(buf, offset + 44); + } - if ((nullBits & 4) != 0) { - obj.rotationOffset = Direction.deserialize(buf, offset + 68); - } + if ((nullBits & 4) != 0) { + obj.rotationOffset = Direction.deserialize(buf, offset + 68); + } - obj.positionType = PositionType.fromValue(buf.getByte(offset + 80)); - if ((nullBits & 8) != 0) { - obj.position = Position.deserialize(buf, offset + 81); - } + obj.positionType = PositionType.fromValue(buf.getByte(offset + 80)); + if ((nullBits & 8) != 0) { + obj.position = Position.deserialize(buf, offset + 81); + } - obj.rotationType = RotationType.fromValue(buf.getByte(offset + 105)); - if ((nullBits & 16) != 0) { - obj.rotation = Direction.deserialize(buf, offset + 106); - } + obj.rotationType = RotationType.fromValue(buf.getByte(offset + 105)); + if ((nullBits & 16) != 0) { + obj.rotation = Direction.deserialize(buf, offset + 106); + } - obj.canMoveType = CanMoveType.fromValue(buf.getByte(offset + 118)); - obj.applyMovementType = ApplyMovementType.fromValue(buf.getByte(offset + 119)); - if ((nullBits & 32) != 0) { - obj.movementMultiplier = Vector3f.deserialize(buf, offset + 120); - } + obj.canMoveType = CanMoveType.fromValue(buf.getByte(offset + 118)); + obj.applyMovementType = ApplyMovementType.fromValue(buf.getByte(offset + 119)); + if ((nullBits & 32) != 0) { + obj.movementMultiplier = PacketIO.readVector3f(buf, offset + 120); + } - obj.applyLookType = ApplyLookType.fromValue(buf.getByte(offset + 132)); - if ((nullBits & 64) != 0) { - obj.lookMultiplier = Vector2f.deserialize(buf, offset + 133); - } + obj.applyLookType = ApplyLookType.fromValue(buf.getByte(offset + 132)); + if ((nullBits & 64) != 0) { + obj.lookMultiplier = PacketIO.readVector2f(buf, offset + 133); + } - obj.mouseInputType = MouseInputType.fromValue(buf.getByte(offset + 141)); - if ((nullBits & 128) != 0) { - obj.planeNormal = Vector3f.deserialize(buf, offset + 142); - } + obj.mouseInputType = MouseInputType.fromValue(buf.getByte(offset + 141)); + if ((nullBits & 128) != 0) { + obj.planeNormal = PacketIO.readVector3f(buf, offset + 142); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -312,21 +320,21 @@ public class ServerCameraSettings { buf.writeByte(this.canMoveType.getValue()); buf.writeByte(this.applyMovementType.getValue()); if (this.movementMultiplier != null) { - this.movementMultiplier.serialize(buf); + PacketIO.writeVector3f(buf, this.movementMultiplier); } else { buf.writeZero(12); } buf.writeByte(this.applyLookType.getValue()); if (this.lookMultiplier != null) { - this.lookMultiplier.serialize(buf); + PacketIO.writeVector2f(buf, this.lookMultiplier); } else { buf.writeZero(8); } buf.writeByte(this.mouseInputType.getValue()); if (this.planeNormal != null) { - this.planeNormal.serialize(buf); + PacketIO.writeVector3f(buf, this.planeNormal); } else { buf.writeZero(12); } @@ -337,7 +345,58 @@ public class ServerCameraSettings { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 154 ? ValidationResult.error("Buffer too small: expected at least 154 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 154) { + return ValidationResult.error("Buffer too small: expected at least 154 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 20) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid MouseInputTargetType value for MouseInputTargetType"); + } else { + v = buffer.getByte(offset + 24) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid MovementForceRotationType value for MovementForceRotationType"); + } else { + v = buffer.getByte(offset + 37) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid AttachedToType value for AttachedToType"); + } else { + v = buffer.getByte(offset + 43) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid PositionDistanceOffsetType value for PositionDistanceOffsetType"); + } else { + v = buffer.getByte(offset + 80) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid PositionType value for PositionType"); + } else { + v = buffer.getByte(offset + 105) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid RotationType value for RotationType"); + } else { + v = buffer.getByte(offset + 118) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid CanMoveType value for CanMoveType"); + } else { + v = buffer.getByte(offset + 119) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ApplyMovementType value for ApplyMovementType"); + } else { + v = buffer.getByte(offset + 132) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ApplyLookType value for ApplyLookType"); + } else { + v = buffer.getByte(offset + 141) & 255; + return v >= 4 ? ValidationResult.error("Invalid MouseInputType value for MouseInputType") : ValidationResult.OK; + } + } + } + } + } + } + } + } + } + } } public ServerCameraSettings clone() { @@ -367,11 +426,11 @@ public class ServerCameraSettings { copy.rotation = this.rotation != null ? this.rotation.clone() : null; copy.canMoveType = this.canMoveType; copy.applyMovementType = this.applyMovementType; - copy.movementMultiplier = this.movementMultiplier != null ? this.movementMultiplier.clone() : null; + copy.movementMultiplier = this.movementMultiplier; copy.applyLookType = this.applyLookType; - copy.lookMultiplier = this.lookMultiplier != null ? this.lookMultiplier.clone() : null; + copy.lookMultiplier = this.lookMultiplier; copy.mouseInputType = this.mouseInputType; - copy.planeNormal = this.planeNormal != null ? this.planeNormal.clone() : null; + copy.planeNormal = this.planeNormal; return copy; } diff --git a/src/com/hypixel/hytale/protocol/ShelterType.java b/src/com/hypixel/hytale/protocol/ShelterType.java new file mode 100644 index 00000000..dd40efb9 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/ShelterType.java @@ -0,0 +1,30 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum ShelterType { + Any(0), + Open(1), + Partial(2), + Sheltered(3), + Enclosed(4); + + public static final ShelterType[] VALUES = values(); + private final int value; + + private ShelterType(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static ShelterType fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("ShelterType", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/SimpleBlockInteraction.java b/src/com/hypixel/hytale/protocol/SimpleBlockInteraction.java index 2514e7bd..cb98b503 100644 --- a/src/com/hypixel/hytale/protocol/SimpleBlockInteraction.java +++ b/src/com/hypixel/hytale/protocol/SimpleBlockInteraction.java @@ -68,79 +68,108 @@ public class SimpleBlockInteraction extends SimpleInteraction { @Nonnull public static SimpleBlockInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - SimpleBlockInteraction obj = new SimpleBlockInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.useLatestTarget = buf.getByte(offset + 19) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 40 + buf.getIntLE(offset + 20); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 40) { + throw ProtocolException.bufferTooSmall("SimpleBlockInteraction", 40, buf.readableBytes() - offset); + } else { + SimpleBlockInteraction obj = new SimpleBlockInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.useLatestTarget = buf.getByte(offset + 19) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 20); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 40 + buf.getIntLE(offset + 24); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 40 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 24); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 40 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 40 + buf.getIntLE(offset + 28); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 28); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 40 + buf.getIntLE(offset + 32); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 40 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 32); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 40 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 36); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 40 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 40 + buf.getIntLE(offset + 36); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -148,6 +177,10 @@ public class SimpleBlockInteraction extends SimpleInteraction { int maxEnd = 40; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 20); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 40 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -157,9 +190,13 @@ public class SimpleBlockInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 24); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 40 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -172,6 +209,10 @@ public class SimpleBlockInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 28); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 40 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -181,9 +222,13 @@ public class SimpleBlockInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 32); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 40 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -191,6 +236,10 @@ public class SimpleBlockInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 36); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 40 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -330,119 +379,109 @@ public class SimpleBlockInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 40 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 20); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 20); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 40 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 40 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 40 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 40 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 40 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 40 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 24); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 40 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 28); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 40 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 32); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 40 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 36); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 40 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/SimpleInteraction.java b/src/com/hypixel/hytale/protocol/SimpleInteraction.java index c1d11b95..4d75563e 100644 --- a/src/com/hypixel/hytale/protocol/SimpleInteraction.java +++ b/src/com/hypixel/hytale/protocol/SimpleInteraction.java @@ -66,78 +66,107 @@ public class SimpleInteraction extends Interaction { @Nonnull public static SimpleInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - SimpleInteraction obj = new SimpleInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 39 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 39) { + throw ProtocolException.bufferTooSmall("SimpleInteraction", 39, buf.readableBytes() - offset); + } else { + SimpleInteraction obj = new SimpleInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 39 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 39 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 39 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 39 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 39 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 39 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 39 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 39 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 39 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -145,6 +174,10 @@ public class SimpleInteraction extends Interaction { int maxEnd = 39; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 39 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -154,9 +187,13 @@ public class SimpleInteraction extends Interaction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 39 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -169,6 +206,10 @@ public class SimpleInteraction extends Interaction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 39 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -178,9 +219,13 @@ public class SimpleInteraction extends Interaction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 39 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -188,6 +233,10 @@ public class SimpleInteraction extends Interaction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 39 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -326,119 +375,109 @@ public class SimpleInteraction extends Interaction { return ValidationResult.error("Buffer too small: expected at least 39 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 39 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 39 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 39 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 39 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 39 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 39 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 39 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 39 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 39 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 39 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/Size.java b/src/com/hypixel/hytale/protocol/Size.java index 46836de1..451984fe 100644 --- a/src/com/hypixel/hytale/protocol/Size.java +++ b/src/com/hypixel/hytale/protocol/Size.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class Size { @Nonnull public static Size deserialize(@Nonnull ByteBuf buf, int offset) { - Size obj = new Size(); - obj.width = buf.getIntLE(offset + 0); - obj.height = buf.getIntLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("Size", 8, buf.readableBytes() - offset); + } else { + Size obj = new Size(); + obj.width = buf.getIntLE(offset + 0); + obj.height = buf.getIntLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/SoftBlock.java b/src/com/hypixel/hytale/protocol/SoftBlock.java index 7bdaddf4..7038af7b 100644 --- a/src/com/hypixel/hytale/protocol/SoftBlock.java +++ b/src/com/hypixel/hytale/protocol/SoftBlock.java @@ -38,38 +38,62 @@ public class SoftBlock { @Nonnull public static SoftBlock deserialize(@Nonnull ByteBuf buf, int offset) { - SoftBlock obj = new SoftBlock(); - byte nullBits = buf.getByte(offset); - obj.isWeaponBreakable = buf.getByte(offset + 1) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - int itemIdLen = VarInt.peek(buf, varPos0); - if (itemIdLen < 0) { - throw ProtocolException.negativeLength("ItemId", itemIdLen); + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("SoftBlock", 10, buf.readableBytes() - offset); + } else { + SoftBlock obj = new SoftBlock(); + byte nullBits = buf.getByte(offset); + obj.isWeaponBreakable = buf.getByte(offset + 1) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("ItemId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 10 + varPosBase0; + int itemIdLen = VarInt.peek(buf, varPos0); + if (itemIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemId"); + } + + int itemIdVarIntLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + } + + if (varPos0 + itemIdVarIntLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemId", varPos0 + itemIdVarIntLen + itemIdLen, buf.readableBytes()); + } + + obj.itemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (itemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("DropListId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + int dropListIdLen = VarInt.peek(buf, varPos1); + if (dropListIdLen < 0) { + throw ProtocolException.invalidVarInt("DropListId"); + } + + int dropListIdVarIntLen = VarInt.size(dropListIdLen); + if (dropListIdLen > 4096000) { + throw ProtocolException.stringTooLong("DropListId", dropListIdLen, 4096000); + } + + if (varPos1 + dropListIdVarIntLen + dropListIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("DropListId", varPos1 + dropListIdVarIntLen + dropListIdLen, buf.readableBytes()); + } + + obj.dropListId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.itemId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - int dropListIdLen = VarInt.peek(buf, varPos1); - if (dropListIdLen < 0) { - throw ProtocolException.negativeLength("DropListId", dropListIdLen); - } - - if (dropListIdLen > 4096000) { - throw ProtocolException.stringTooLong("DropListId", dropListIdLen, 4096000); - } - - obj.dropListId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -77,9 +101,13 @@ public class SoftBlock { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("ItemId", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -87,9 +115,13 @@ public class SoftBlock { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("DropListId", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -151,15 +183,11 @@ public class SoftBlock { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int itemIdOffset = buffer.getIntLE(offset + 2); - if (itemIdOffset < 0) { + if (itemIdOffset < 0 || itemIdOffset > buffer.writerIndex() - offset - 10) { return ValidationResult.error("Invalid offset for ItemId"); } int pos = offset + 10 + itemIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemId"); - } - int itemIdLen = VarInt.peek(buffer, pos); if (itemIdLen < 0) { return ValidationResult.error("Invalid string length for ItemId"); @@ -169,7 +197,7 @@ public class SoftBlock { return ValidationResult.error("ItemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(itemIdLen); pos += itemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemId"); @@ -178,15 +206,11 @@ public class SoftBlock { if ((nullBits & 2) != 0) { int dropListIdOffset = buffer.getIntLE(offset + 6); - if (dropListIdOffset < 0) { + if (dropListIdOffset < 0 || dropListIdOffset > buffer.writerIndex() - offset - 10) { return ValidationResult.error("Invalid offset for DropListId"); } int posx = offset + 10 + dropListIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DropListId"); - } - int dropListIdLen = VarInt.peek(buffer, posx); if (dropListIdLen < 0) { return ValidationResult.error("Invalid string length for DropListId"); @@ -196,7 +220,7 @@ public class SoftBlock { return ValidationResult.error("DropListId exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(dropListIdLen); posx += dropListIdLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading DropListId"); diff --git a/src/com/hypixel/hytale/protocol/SoundCategory.java b/src/com/hypixel/hytale/protocol/SoundCategory.java index b28845ce..b05eb483 100644 --- a/src/com/hypixel/hytale/protocol/SoundCategory.java +++ b/src/com/hypixel/hytale/protocol/SoundCategory.java @@ -6,7 +6,8 @@ public enum SoundCategory { Music(0), Ambient(1), SFX(2), - UI(3); + UI(3), + Voice(4); public static final SoundCategory[] VALUES = values(); private final int value; diff --git a/src/com/hypixel/hytale/protocol/SoundEvent.java b/src/com/hypixel/hytale/protocol/SoundEvent.java index bb73342a..847b5647 100644 --- a/src/com/hypixel/hytale/protocol/SoundEvent.java +++ b/src/com/hypixel/hytale/protocol/SoundEvent.java @@ -12,9 +12,9 @@ import javax.annotation.Nullable; public class SoundEvent { public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 34; + public static final int FIXED_BLOCK_SIZE = 38; public static final int VARIABLE_FIELD_COUNT = 2; - public static final int VARIABLE_BLOCK_START = 42; + public static final int VARIABLE_BLOCK_START = 46; public static final int MAX_SIZE = 1677721600; @Nullable public String id; @@ -26,6 +26,7 @@ public class SoundEvent { public boolean preventSoundInterruption; public float startAttenuationDistance; public float maxDistance; + public float spatialBlend; @Nullable public SoundEventLayer[] layers; public int audioCategory; @@ -43,6 +44,7 @@ public class SoundEvent { boolean preventSoundInterruption, float startAttenuationDistance, float maxDistance, + float spatialBlend, @Nullable SoundEventLayer[] layers, int audioCategory ) { @@ -55,6 +57,7 @@ public class SoundEvent { this.preventSoundInterruption = preventSoundInterruption; this.startAttenuationDistance = startAttenuationDistance; this.maxDistance = maxDistance; + this.spatialBlend = spatialBlend; this.layers = layers; this.audioCategory = audioCategory; } @@ -69,83 +72,112 @@ public class SoundEvent { this.preventSoundInterruption = other.preventSoundInterruption; this.startAttenuationDistance = other.startAttenuationDistance; this.maxDistance = other.maxDistance; + this.spatialBlend = other.spatialBlend; this.layers = other.layers; this.audioCategory = other.audioCategory; } @Nonnull public static SoundEvent deserialize(@Nonnull ByteBuf buf, int offset) { - SoundEvent obj = new SoundEvent(); - byte nullBits = buf.getByte(offset); - obj.volume = buf.getFloatLE(offset + 1); - obj.pitch = buf.getFloatLE(offset + 5); - obj.musicDuckingVolume = buf.getFloatLE(offset + 9); - obj.ambientDuckingVolume = buf.getFloatLE(offset + 13); - obj.maxInstance = buf.getIntLE(offset + 17); - obj.preventSoundInterruption = buf.getByte(offset + 21) != 0; - obj.startAttenuationDistance = buf.getFloatLE(offset + 22); - obj.maxDistance = buf.getFloatLE(offset + 26); - obj.audioCategory = buf.getIntLE(offset + 30); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 42 + buf.getIntLE(offset + 34); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 46) { + throw ProtocolException.bufferTooSmall("SoundEvent", 46, buf.readableBytes() - offset); + } else { + SoundEvent obj = new SoundEvent(); + byte nullBits = buf.getByte(offset); + obj.volume = buf.getFloatLE(offset + 1); + obj.pitch = buf.getFloatLE(offset + 5); + obj.musicDuckingVolume = buf.getFloatLE(offset + 9); + obj.ambientDuckingVolume = buf.getFloatLE(offset + 13); + obj.maxInstance = buf.getIntLE(offset + 17); + obj.preventSoundInterruption = buf.getByte(offset + 21) != 0; + obj.startAttenuationDistance = buf.getFloatLE(offset + 22); + obj.maxDistance = buf.getFloatLE(offset + 26); + obj.spatialBlend = buf.getFloatLE(offset + 30); + obj.audioCategory = buf.getIntLE(offset + 34); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 38); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 46 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 42); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Layers", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 46 + varPosBase1; + int layersCount = VarInt.peek(buf, varPos1); + if (layersCount < 0) { + throw ProtocolException.invalidVarInt("Layers"); + } + + int varIntLen = VarInt.size(layersCount); + if (layersCount > 4096000) { + throw ProtocolException.arrayTooLong("Layers", layersCount, 4096000); + } + + if (varPos1 + varIntLen + layersCount * 42L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Layers", varPos1 + varIntLen + layersCount * 42, buf.readableBytes()); + } + + obj.layers = new SoundEventLayer[layersCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < layersCount; i++) { + obj.layers[i] = SoundEventLayer.deserialize(buf, elemPos); + elemPos += SoundEventLayer.computeBytesConsumed(buf, elemPos); + } } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 42 + buf.getIntLE(offset + 38); - int layersCount = VarInt.peek(buf, varPos1); - if (layersCount < 0) { - throw ProtocolException.negativeLength("Layers", layersCount); - } - - if (layersCount > 4096000) { - throw ProtocolException.arrayTooLong("Layers", layersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + layersCount * 42L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Layers", varPos1 + varIntLen + layersCount * 42, buf.readableBytes()); - } - - obj.layers = new SoundEventLayer[layersCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < layersCount; i++) { - obj.layers[i] = SoundEventLayer.deserialize(buf, elemPos); - elemPos += SoundEventLayer.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 42; + int maxEnd = 46; if ((nullBits & 1) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 34); - int pos0 = offset + 42 + fieldOffset0; + int fieldOffset0 = buf.getIntLE(offset + 38); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + + int pos0 = offset + 46 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } } if ((nullBits & 2) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 38); - int pos1 = offset + 42 + fieldOffset1; + int fieldOffset1 = buf.getIntLE(offset + 42); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Layers", fieldOffset1, maxEnd); + } + + int pos1 = offset + 46 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += SoundEventLayer.computeBytesConsumed(buf, pos1); @@ -179,6 +211,7 @@ public class SoundEvent { buf.writeByte(this.preventSoundInterruption ? 1 : 0); buf.writeFloatLE(this.startAttenuationDistance); buf.writeFloatLE(this.maxDistance); + buf.writeFloatLE(this.spatialBlend); buf.writeIntLE(this.audioCategory); int idOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); @@ -209,7 +242,7 @@ public class SoundEvent { } public int computeSize() { - int size = 42; + int size = 46; if (this.id != null) { size += PacketIO.stringSize(this.id); } @@ -228,21 +261,17 @@ public class SoundEvent { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 42) { - return ValidationResult.error("Buffer too small: expected at least 42 bytes"); + if (buffer.readableBytes() - offset < 46) { + return ValidationResult.error("Buffer too small: expected at least 46 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { - int idOffset = buffer.getIntLE(offset + 34); - if (idOffset < 0) { + int idOffset = buffer.getIntLE(offset + 38); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 46) { return ValidationResult.error("Invalid offset for Id"); } - int pos = offset + 42 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - + int pos = offset + 46 + idOffset; int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -252,7 +281,7 @@ public class SoundEvent { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -260,16 +289,12 @@ public class SoundEvent { } if ((nullBits & 2) != 0) { - int layersOffset = buffer.getIntLE(offset + 38); - if (layersOffset < 0) { + int layersOffset = buffer.getIntLE(offset + 42); + if (layersOffset < 0 || layersOffset > buffer.writerIndex() - offset - 46) { return ValidationResult.error("Invalid offset for Layers"); } - int posx = offset + 42 + layersOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Layers"); - } - + int posx = offset + 46 + layersOffset; int layersCount = VarInt.peek(buffer, posx); if (layersCount < 0) { return ValidationResult.error("Invalid array count for Layers"); @@ -279,7 +304,7 @@ public class SoundEvent { return ValidationResult.error("Layers exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(layersCount); for (int i = 0; i < layersCount; i++) { ValidationResult structResult = SoundEventLayer.validateStructure(buffer, posx); @@ -306,6 +331,7 @@ public class SoundEvent { copy.preventSoundInterruption = this.preventSoundInterruption; copy.startAttenuationDistance = this.startAttenuationDistance; copy.maxDistance = this.maxDistance; + copy.spatialBlend = this.spatialBlend; copy.layers = this.layers != null ? Arrays.stream(this.layers).map(e -> e.clone()).toArray(SoundEventLayer[]::new) : null; copy.audioCategory = this.audioCategory; return copy; @@ -327,6 +353,7 @@ public class SoundEvent { && this.preventSoundInterruption == other.preventSoundInterruption && this.startAttenuationDistance == other.startAttenuationDistance && this.maxDistance == other.maxDistance + && this.spatialBlend == other.spatialBlend && Arrays.equals((Object[])this.layers, (Object[])other.layers) && this.audioCategory == other.audioCategory; } @@ -344,6 +371,7 @@ public class SoundEvent { result = 31 * result + Boolean.hashCode(this.preventSoundInterruption); result = 31 * result + Float.hashCode(this.startAttenuationDistance); result = 31 * result + Float.hashCode(this.maxDistance); + result = 31 * result + Float.hashCode(this.spatialBlend); result = 31 * result + Arrays.hashCode((Object[])this.layers); return 31 * result + Integer.hashCode(this.audioCategory); } diff --git a/src/com/hypixel/hytale/protocol/SoundEventLayer.java b/src/com/hypixel/hytale/protocol/SoundEventLayer.java index 98c36fc5..28432541 100644 --- a/src/com/hypixel/hytale/protocol/SoundEventLayer.java +++ b/src/com/hypixel/hytale/protocol/SoundEventLayer.java @@ -63,54 +63,62 @@ public class SoundEventLayer { @Nonnull public static SoundEventLayer deserialize(@Nonnull ByteBuf buf, int offset) { - SoundEventLayer obj = new SoundEventLayer(); - byte nullBits = buf.getByte(offset); - obj.volume = buf.getFloatLE(offset + 1); - obj.startDelay = buf.getFloatLE(offset + 5); - obj.looping = buf.getByte(offset + 9) != 0; - obj.probability = buf.getIntLE(offset + 10); - obj.probabilityRerollDelay = buf.getFloatLE(offset + 14); - obj.roundRobinHistorySize = buf.getIntLE(offset + 18); - if ((nullBits & 1) != 0) { - obj.randomSettings = SoundEventLayerRandomSettings.deserialize(buf, offset + 22); - } - - int pos = offset + 42; - if ((nullBits & 2) != 0) { - int filesCount = VarInt.peek(buf, pos); - if (filesCount < 0) { - throw ProtocolException.negativeLength("Files", filesCount); + if (buf.readableBytes() - offset < 42) { + throw ProtocolException.bufferTooSmall("SoundEventLayer", 42, buf.readableBytes() - offset); + } else { + SoundEventLayer obj = new SoundEventLayer(); + byte nullBits = buf.getByte(offset); + obj.volume = buf.getFloatLE(offset + 1); + obj.startDelay = buf.getFloatLE(offset + 5); + obj.looping = buf.getByte(offset + 9) != 0; + obj.probability = buf.getIntLE(offset + 10); + obj.probabilityRerollDelay = buf.getFloatLE(offset + 14); + obj.roundRobinHistorySize = buf.getIntLE(offset + 18); + if ((nullBits & 1) != 0) { + obj.randomSettings = SoundEventLayerRandomSettings.deserialize(buf, offset + 22); } - if (filesCount > 4096000) { - throw ProtocolException.arrayTooLong("Files", filesCount, 4096000); - } - - int filesVarLen = VarInt.size(filesCount); - if (pos + filesVarLen + filesCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Files", pos + filesVarLen + filesCount * 1, buf.readableBytes()); - } - - pos += filesVarLen; - obj.files = new String[filesCount]; - - for (int i = 0; i < filesCount; i++) { - int strLen = VarInt.peek(buf, pos); - if (strLen < 0) { - throw ProtocolException.negativeLength("files[" + i + "]", strLen); + int pos = offset + 42; + if ((nullBits & 2) != 0) { + int filesCount = VarInt.peek(buf, pos); + if (filesCount < 0) { + throw ProtocolException.invalidVarInt("Files"); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("files[" + i + "]", strLen, 4096000); + int filesVarLen = VarInt.size(filesCount); + if (filesCount > 4096000) { + throw ProtocolException.arrayTooLong("Files", filesCount, 4096000); } - int strVarLen = VarInt.length(buf, pos); - obj.files[i] = PacketIO.readVarString(buf, pos); - pos += strVarLen + strLen; - } - } + if (pos + filesVarLen + filesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Files", pos + filesVarLen + filesCount * 1, buf.readableBytes()); + } - return obj; + pos += filesVarLen; + obj.files = new String[filesCount]; + + for (int i = 0; i < filesCount; i++) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("files[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("files[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("files[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.files[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -118,11 +126,11 @@ public class SoundEventLayer { int pos = offset + 42; if ((nullBits & 2) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -196,7 +204,7 @@ public class SoundEventLayer { return ValidationResult.error("Files exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(filesCount); for (int i = 0; i < filesCount; i++) { int strLen = VarInt.peek(buffer, pos); @@ -204,7 +212,7 @@ public class SoundEventLayer { return ValidationResult.error("Invalid string length in Files"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(strLen); pos += strLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Files"); diff --git a/src/com/hypixel/hytale/protocol/SoundEventLayerRandomSettings.java b/src/com/hypixel/hytale/protocol/SoundEventLayerRandomSettings.java index 65fd95de..5036015e 100644 --- a/src/com/hypixel/hytale/protocol/SoundEventLayerRandomSettings.java +++ b/src/com/hypixel/hytale/protocol/SoundEventLayerRandomSettings.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -38,13 +39,17 @@ public class SoundEventLayerRandomSettings { @Nonnull public static SoundEventLayerRandomSettings deserialize(@Nonnull ByteBuf buf, int offset) { - SoundEventLayerRandomSettings obj = new SoundEventLayerRandomSettings(); - obj.minVolume = buf.getFloatLE(offset + 0); - obj.maxVolume = buf.getFloatLE(offset + 4); - obj.minPitch = buf.getFloatLE(offset + 8); - obj.maxPitch = buf.getFloatLE(offset + 12); - obj.maxStartOffset = buf.getFloatLE(offset + 16); - return obj; + if (buf.readableBytes() - offset < 20) { + throw ProtocolException.bufferTooSmall("SoundEventLayerRandomSettings", 20, buf.readableBytes() - offset); + } else { + SoundEventLayerRandomSettings obj = new SoundEventLayerRandomSettings(); + obj.minVolume = buf.getFloatLE(offset + 0); + obj.maxVolume = buf.getFloatLE(offset + 4); + obj.minPitch = buf.getFloatLE(offset + 8); + obj.maxPitch = buf.getFloatLE(offset + 12); + obj.maxStartOffset = buf.getFloatLE(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/SoundSet.java b/src/com/hypixel/hytale/protocol/SoundSet.java index 6846c6fe..6e6df4b8 100644 --- a/src/com/hypixel/hytale/protocol/SoundSet.java +++ b/src/com/hypixel/hytale/protocol/SoundSet.java @@ -42,60 +42,83 @@ public class SoundSet { @Nonnull public static SoundSet deserialize(@Nonnull ByteBuf buf, int offset) { - SoundSet obj = new SoundSet(); - byte nullBits = buf.getByte(offset); - obj.category = SoundCategory.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("SoundSet", 10, buf.readableBytes() - offset); + } else { + SoundSet obj = new SoundSet(); + byte nullBits = buf.getByte(offset); + obj.category = SoundCategory.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 10 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Sounds", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + int soundsCount = VarInt.peek(buf, varPos1); + if (soundsCount < 0) { + throw ProtocolException.invalidVarInt("Sounds"); + } + + int varIntLen = VarInt.size(soundsCount); + if (soundsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Sounds", soundsCount, 4096000); + } + + obj.sounds = new HashMap<>(soundsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < soundsCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + int val = buf.getIntLE(dictPos); + dictPos += 4; + if (obj.sounds.put(key, val) != null) { + throw ProtocolException.duplicateKey("sounds", key); + } + } } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - int soundsCount = VarInt.peek(buf, varPos1); - if (soundsCount < 0) { - throw ProtocolException.negativeLength("Sounds", soundsCount); - } - - if (soundsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Sounds", soundsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - obj.sounds = new HashMap<>(soundsCount); - int dictPos = varPos1 + varIntLen; - - for (int i = 0; i < soundsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); - } - - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); - } - - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - int val = buf.getIntLE(dictPos); - dictPos += 4; - if (obj.sounds.put(key, val) != null) { - throw ProtocolException.duplicateKey("sounds", key); - } - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -103,9 +126,13 @@ public class SoundSet { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -113,13 +140,17 @@ public class SoundSet { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Sounds", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; pos1 += 4; } @@ -197,79 +228,76 @@ public class SoundSet { return ValidationResult.error("Buffer too small: expected at least 10 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int idOffset = buffer.getIntLE(offset + 2); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid SoundCategory value for Category"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 2); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for Id"); + } + + int pos = offset + 10 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } } - int pos = offset + 10 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for Sounds"); + } + + int posx = offset + 10 + v; + int soundsCount = VarInt.peek(buffer, posx); + if (soundsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Sounds"); + } + + if (soundsCount > 4096000) { + return ValidationResult.error("Sounds exceeds max length 4096000"); + } + + posx += VarInt.size(soundsCount); + + for (int i = 0; i < soundsCount; i++) { + int keyLen = VarInt.peek(buffer, posx); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + posx += VarInt.size(keyLen); + posx += keyLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posx += 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } } - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } - - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); - } + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int soundsOffset = buffer.getIntLE(offset + 6); - if (soundsOffset < 0) { - return ValidationResult.error("Invalid offset for Sounds"); - } - - int posx = offset + 10 + soundsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Sounds"); - } - - int soundsCount = VarInt.peek(buffer, posx); - if (soundsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Sounds"); - } - - if (soundsCount > 4096000) { - return ValidationResult.error("Sounds exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < soundsCount; i++) { - int keyLen = VarInt.peek(buffer, posx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); - } - - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += keyLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - posx += 4; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); - } - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/SpaceSize.java b/src/com/hypixel/hytale/protocol/SpaceSize.java new file mode 100644 index 00000000..36695542 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/SpaceSize.java @@ -0,0 +1,31 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum SpaceSize { + Any(0), + Tiny(1), + Small(2), + Medium(3), + Large(4), + Unbounded(5); + + public static final SpaceSize[] VALUES = values(); + private final int value; + + private SpaceSize(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static SpaceSize fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("SpaceSize", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/SpawnDeployableFromRaycastInteraction.java b/src/com/hypixel/hytale/protocol/SpawnDeployableFromRaycastInteraction.java index 6ab6fe0f..c79cbd40 100644 --- a/src/com/hypixel/hytale/protocol/SpawnDeployableFromRaycastInteraction.java +++ b/src/com/hypixel/hytale/protocol/SpawnDeployableFromRaycastInteraction.java @@ -78,110 +78,149 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { @Nonnull public static SpawnDeployableFromRaycastInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - SpawnDeployableFromRaycastInteraction obj = new SpawnDeployableFromRaycastInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.maxDistance = buf.getFloatLE(offset + 19); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 51 + buf.getIntLE(offset + 23); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 51) { + throw ProtocolException.bufferTooSmall("SpawnDeployableFromRaycastInteraction", 51, buf.readableBytes() - offset); + } else { + SpawnDeployableFromRaycastInteraction obj = new SpawnDeployableFromRaycastInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.maxDistance = buf.getFloatLE(offset + 19); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 23); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 51 + buf.getIntLE(offset + 27); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 51 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 27); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 51 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 51 + buf.getIntLE(offset + 31); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 31); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 51 + buf.getIntLE(offset + 35); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 51 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 35); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } + int varPos3 = offset + 51 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } - obj.tags = new int[tagsCount]; + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 51 + buf.getIntLE(offset + 39); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } + obj.tags = new int[tagsCount]; - if ((nullBits & 32) != 0) { - int varPos5 = offset + 51 + buf.getIntLE(offset + 43); - obj.deployableConfig = DeployableConfig.deserialize(buf, varPos5); - } - - if ((nullBits & 64) != 0) { - int varPos6 = offset + 51 + buf.getIntLE(offset + 47); - int costsCount = VarInt.peek(buf, varPos6); - if (costsCount < 0) { - throw ProtocolException.negativeLength("Costs", costsCount); - } - - if (costsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Costs", costsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos6); - obj.costs = new HashMap<>(costsCount); - int dictPos = varPos6 + varIntLen; - - for (int ix = 0; ix < costsCount; ix++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.costs.put(key, val) != null) { - throw ProtocolException.duplicateKey("costs", key); + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); } } - } - return obj; + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 39); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 51 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 43); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("DeployableConfig", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 51 + varPosBase5; + obj.deployableConfig = DeployableConfig.deserialize(buf, varPos5); + } + + if ((nullBits & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 47); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Costs", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 51 + varPosBase6; + int costsCount = VarInt.peek(buf, varPos6); + if (costsCount < 0) { + throw ProtocolException.invalidVarInt("Costs"); + } + + int varIntLenx = VarInt.size(costsCount); + if (costsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Costs", costsCount, 4096000); + } + + obj.costs = new HashMap<>(costsCount); + int dictPos = varPos6 + varIntLenx; + + for (int ix = 0; ix < costsCount; ix++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.costs.put(key, val) != null) { + throw ProtocolException.duplicateKey("costs", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -189,6 +228,10 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { int maxEnd = 51; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 23); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 51 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -198,9 +241,13 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 27); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 51 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -213,6 +260,10 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 31); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 51 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -222,9 +273,13 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 35); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 51 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -232,6 +287,10 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 39); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 51 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -241,6 +300,10 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 43); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("DeployableConfig", fieldOffset5, maxEnd); + } + int pos5 = offset + 51 + fieldOffset5; pos5 += DeployableConfig.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { @@ -250,9 +313,13 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { if ((nullBits & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 47); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 51) { + throw ProtocolException.invalidOffset("Costs", fieldOffset6, maxEnd); + } + int pos6 = offset + 51 + fieldOffset6; int dictLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos6 += 4; @@ -439,173 +506,155 @@ public class SpawnDeployableFromRaycastInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 51 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 23); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 51 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 27); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 51 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 31); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 51 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 35); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 51 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 39); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 51 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int deployableConfigOffset = buffer.getIntLE(offset + 43); - if (deployableConfigOffset < 0) { - return ValidationResult.error("Invalid offset for DeployableConfig"); - } - - int posxxxxx = offset + 51 + deployableConfigOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DeployableConfig"); - } - - ValidationResult deployableConfigResult = DeployableConfig.validateStructure(buffer, posxxxxx); - if (!deployableConfigResult.isValid()) { - return ValidationResult.error("Invalid DeployableConfig: " + deployableConfigResult.error()); - } - - posxxxxx += DeployableConfig.computeBytesConsumed(buffer, posxxxxx); - } - - if ((nullBits & 64) != 0) { - int costsOffset = buffer.getIntLE(offset + 47); - if (costsOffset < 0) { - return ValidationResult.error("Invalid offset for Costs"); - } - - int posxxxxxx = offset + 51 + costsOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Costs"); - } - - int costsCount = VarInt.peek(buffer, posxxxxxx); - if (costsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Costs"); - } - - if (costsCount > 4096000) { - return ValidationResult.error("Costs exceeds max length 4096000"); - } - - posxxxxxx += VarInt.length(buffer, posxxxxxx); - - for (int i = 0; i < costsCount; i++) { - posxxxxxx += 4; - if (posxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 51) { + return ValidationResult.error("Invalid offset for Effects"); } - posxxxxxx += 4; - if (posxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + int pos = offset + 51 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 51) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 51 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; } } - } - return ValidationResult.OK; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 51) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 51 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 51) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 51 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 51) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 51 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 43); + if (v < 0 || v > buffer.writerIndex() - offset - 51) { + return ValidationResult.error("Invalid offset for DeployableConfig"); + } + + int posxx = offset + 51 + v; + ValidationResult deployableConfigResult = DeployableConfig.validateStructure(buffer, posxx); + if (!deployableConfigResult.isValid()) { + return ValidationResult.error("Invalid DeployableConfig: " + deployableConfigResult.error()); + } + + posxx += DeployableConfig.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 47); + if (v < 0 || v > buffer.writerIndex() - offset - 51) { + return ValidationResult.error("Invalid offset for Costs"); + } + + int posxx = offset + 51 + v; + int costsCount = VarInt.peek(buffer, posxx); + if (costsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Costs"); + } + + if (costsCount > 4096000) { + return ValidationResult.error("Costs exceeds max length 4096000"); + } + + posxx += VarInt.size(costsCount); + + for (int i = 0; i < costsCount; i++) { + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/StabSelector.java b/src/com/hypixel/hytale/protocol/StabSelector.java index 090b2c23..05245d92 100644 --- a/src/com/hypixel/hytale/protocol/StabSelector.java +++ b/src/com/hypixel/hytale/protocol/StabSelector.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -64,18 +65,22 @@ public class StabSelector extends Selector { @Nonnull public static StabSelector deserialize(@Nonnull ByteBuf buf, int offset) { - StabSelector obj = new StabSelector(); - obj.extendTop = buf.getFloatLE(offset + 0); - obj.extendBottom = buf.getFloatLE(offset + 4); - obj.extendLeft = buf.getFloatLE(offset + 8); - obj.extendRight = buf.getFloatLE(offset + 12); - obj.yawOffset = buf.getFloatLE(offset + 16); - obj.pitchOffset = buf.getFloatLE(offset + 20); - obj.rollOffset = buf.getFloatLE(offset + 24); - obj.startDistance = buf.getFloatLE(offset + 28); - obj.endDistance = buf.getFloatLE(offset + 32); - obj.testLineOfSight = buf.getByte(offset + 36) != 0; - return obj; + if (buf.readableBytes() - offset < 37) { + throw ProtocolException.bufferTooSmall("StabSelector", 37, buf.readableBytes() - offset); + } else { + StabSelector obj = new StabSelector(); + obj.extendTop = buf.getFloatLE(offset + 0); + obj.extendBottom = buf.getFloatLE(offset + 4); + obj.extendLeft = buf.getFloatLE(offset + 8); + obj.extendRight = buf.getFloatLE(offset + 12); + obj.yawOffset = buf.getFloatLE(offset + 16); + obj.pitchOffset = buf.getFloatLE(offset + 20); + obj.rollOffset = buf.getFloatLE(offset + 24); + obj.startDistance = buf.getFloatLE(offset + 28); + obj.endDistance = buf.getFloatLE(offset + 32); + obj.testLineOfSight = buf.getByte(offset + 36) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/StairConnectedBlockRuleSet.java b/src/com/hypixel/hytale/protocol/StairConnectedBlockRuleSet.java index 20ff4d6c..d11b072f 100644 --- a/src/com/hypixel/hytale/protocol/StairConnectedBlockRuleSet.java +++ b/src/com/hypixel/hytale/protocol/StairConnectedBlockRuleSet.java @@ -53,30 +53,38 @@ public class StairConnectedBlockRuleSet { @Nonnull public static StairConnectedBlockRuleSet deserialize(@Nonnull ByteBuf buf, int offset) { - StairConnectedBlockRuleSet obj = new StairConnectedBlockRuleSet(); - byte nullBits = buf.getByte(offset); - obj.straightBlockId = buf.getIntLE(offset + 1); - obj.cornerLeftBlockId = buf.getIntLE(offset + 5); - obj.cornerRightBlockId = buf.getIntLE(offset + 9); - obj.invertedCornerLeftBlockId = buf.getIntLE(offset + 13); - obj.invertedCornerRightBlockId = buf.getIntLE(offset + 17); - int pos = offset + 21; - if ((nullBits & 1) != 0) { - int materialNameLen = VarInt.peek(buf, pos); - if (materialNameLen < 0) { - throw ProtocolException.negativeLength("MaterialName", materialNameLen); + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("StairConnectedBlockRuleSet", 21, buf.readableBytes() - offset); + } else { + StairConnectedBlockRuleSet obj = new StairConnectedBlockRuleSet(); + byte nullBits = buf.getByte(offset); + obj.straightBlockId = buf.getIntLE(offset + 1); + obj.cornerLeftBlockId = buf.getIntLE(offset + 5); + obj.cornerRightBlockId = buf.getIntLE(offset + 9); + obj.invertedCornerLeftBlockId = buf.getIntLE(offset + 13); + obj.invertedCornerRightBlockId = buf.getIntLE(offset + 17); + int pos = offset + 21; + if ((nullBits & 1) != 0) { + int materialNameLen = VarInt.peek(buf, pos); + if (materialNameLen < 0) { + throw ProtocolException.invalidVarInt("MaterialName"); + } + + int materialNameVarLen = VarInt.size(materialNameLen); + if (materialNameLen > 4096000) { + throw ProtocolException.stringTooLong("MaterialName", materialNameLen, 4096000); + } + + if (pos + materialNameVarLen + materialNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("MaterialName", pos + materialNameVarLen + materialNameLen, buf.readableBytes()); + } + + obj.materialName = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += materialNameVarLen + materialNameLen; } - if (materialNameLen > 4096000) { - throw ProtocolException.stringTooLong("MaterialName", materialNameLen, 4096000); - } - - int materialNameVarLen = VarInt.length(buf, pos); - obj.materialName = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += materialNameVarLen + materialNameLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -84,7 +92,7 @@ public class StairConnectedBlockRuleSet { int pos = offset + 21; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -132,7 +140,7 @@ public class StairConnectedBlockRuleSet { return ValidationResult.error("MaterialName exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(materialNameLen); pos += materialNameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading MaterialName"); diff --git a/src/com/hypixel/hytale/protocol/StatsConditionInteraction.java b/src/com/hypixel/hytale/protocol/StatsConditionInteraction.java index a69cbc8d..681dbbb4 100644 --- a/src/com/hypixel/hytale/protocol/StatsConditionInteraction.java +++ b/src/com/hypixel/hytale/protocol/StatsConditionInteraction.java @@ -82,107 +82,141 @@ public class StatsConditionInteraction extends SimpleInteraction { @Nonnull public static StatsConditionInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - StatsConditionInteraction obj = new StatsConditionInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.lessThan = buf.getByte(offset + 19) != 0; - obj.lenient = buf.getByte(offset + 20) != 0; - obj.valueType = ValueType.fromValue(buf.getByte(offset + 21)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 46 + buf.getIntLE(offset + 22); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 46) { + throw ProtocolException.bufferTooSmall("StatsConditionInteraction", 46, buf.readableBytes() - offset); + } else { + StatsConditionInteraction obj = new StatsConditionInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.lessThan = buf.getByte(offset + 19) != 0; + obj.lenient = buf.getByte(offset + 20) != 0; + obj.valueType = ValueType.fromValue(buf.getByte(offset + 21)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 22); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 46 + buf.getIntLE(offset + 26); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 46 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 26); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 46 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 46 + buf.getIntLE(offset + 30); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 30); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 46 + buf.getIntLE(offset + 34); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 46 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 34); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } + int varPos3 = offset + 46 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } - obj.tags = new int[tagsCount]; + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 46 + buf.getIntLE(offset + 38); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } + obj.tags = new int[tagsCount]; - if ((nullBits & 32) != 0) { - int varPos5 = offset + 46 + buf.getIntLE(offset + 42); - int costsCount = VarInt.peek(buf, varPos5); - if (costsCount < 0) { - throw ProtocolException.negativeLength("Costs", costsCount); - } - - if (costsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Costs", costsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - obj.costs = new HashMap<>(costsCount); - int dictPos = varPos5 + varIntLen; - - for (int ix = 0; ix < costsCount; ix++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.costs.put(key, val) != null) { - throw ProtocolException.duplicateKey("costs", key); + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); } } - } - return obj; + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 38); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 46 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 42); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Costs", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 46 + varPosBase5; + int costsCount = VarInt.peek(buf, varPos5); + if (costsCount < 0) { + throw ProtocolException.invalidVarInt("Costs"); + } + + int varIntLenx = VarInt.size(costsCount); + if (costsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Costs", costsCount, 4096000); + } + + obj.costs = new HashMap<>(costsCount); + int dictPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < costsCount; ix++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.costs.put(key, val) != null) { + throw ProtocolException.duplicateKey("costs", key); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -190,6 +224,10 @@ public class StatsConditionInteraction extends SimpleInteraction { int maxEnd = 46; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 22); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 46 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -199,9 +237,13 @@ public class StatsConditionInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 26); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 46 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -214,6 +256,10 @@ public class StatsConditionInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 30); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 46 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -223,9 +269,13 @@ public class StatsConditionInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 34); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 46 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -233,6 +283,10 @@ public class StatsConditionInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 38); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 46 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -242,9 +296,13 @@ public class StatsConditionInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 42); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 46) { + throw ProtocolException.invalidOffset("Costs", fieldOffset5, maxEnd); + } + int pos5 = offset + 46 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos5 += 4; @@ -416,154 +474,145 @@ public class StatsConditionInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 46 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 22); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + v = buffer.getByte(offset + 21) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ValueType value for ValueType"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 22); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Effects"); + } - int pos = offset + 46 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } + int pos = offset + 46 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 26); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 46 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 30); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 46 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 34); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 46 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 38); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 46 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int costsOffset = buffer.getIntLE(offset + 42); - if (costsOffset < 0) { - return ValidationResult.error("Invalid offset for Costs"); - } - - int posxxxxx = offset + 46 + costsOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Costs"); - } - - int costsCount = VarInt.peek(buffer, posxxxxx); - if (costsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Costs"); - } - - if (costsCount > 4096000) { - return ValidationResult.error("Costs exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < costsCount; i++) { - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 26); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 46 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 30); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 46 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 34); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 46 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 38); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 46 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 42); + if (v < 0 || v > buffer.writerIndex() - offset - 46) { + return ValidationResult.error("Invalid offset for Costs"); + } + + int posxx = offset + 46 + v; + int costsCount = VarInt.peek(buffer, posxx); + if (costsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Costs"); + } + + if (costsCount > 4096000) { + return ValidationResult.error("Costs exceeds max length 4096000"); + } + + posxx += VarInt.size(costsCount); + + for (int i = 0; i < costsCount; i++) { + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + return ValidationResult.OK; } } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/StringParamValue.java b/src/com/hypixel/hytale/protocol/StringParamValue.java index dc99da0d..b61dcb5c 100644 --- a/src/com/hypixel/hytale/protocol/StringParamValue.java +++ b/src/com/hypixel/hytale/protocol/StringParamValue.java @@ -31,25 +31,33 @@ public class StringParamValue extends ParamValue { @Nonnull public static StringParamValue deserialize(@Nonnull ByteBuf buf, int offset) { - StringParamValue obj = new StringParamValue(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int valueLen = VarInt.peek(buf, pos); - if (valueLen < 0) { - throw ProtocolException.negativeLength("Value", valueLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("StringParamValue", 1, buf.readableBytes() - offset); + } else { + StringParamValue obj = new StringParamValue(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int valueLen = VarInt.peek(buf, pos); + if (valueLen < 0) { + throw ProtocolException.invalidVarInt("Value"); + } + + int valueVarLen = VarInt.size(valueLen); + if (valueLen > 4096000) { + throw ProtocolException.stringTooLong("Value", valueLen, 4096000); + } + + if (pos + valueVarLen + valueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Value", pos + valueVarLen + valueLen, buf.readableBytes()); + } + + obj.value = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += valueVarLen + valueLen; } - if (valueLen > 4096000) { - throw ProtocolException.stringTooLong("Value", valueLen, 4096000); - } - - int valueVarLen = VarInt.length(buf, pos); - obj.value = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += valueVarLen + valueLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -57,7 +65,7 @@ public class StringParamValue extends ParamValue { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -105,7 +113,7 @@ public class StringParamValue extends ParamValue { return ValidationResult.error("Value exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(valueLen); pos += valueLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Value"); diff --git a/src/com/hypixel/hytale/protocol/SubCategoryDefinition.java b/src/com/hypixel/hytale/protocol/SubCategoryDefinition.java new file mode 100644 index 00000000..f0d82220 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/SubCategoryDefinition.java @@ -0,0 +1,343 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class SubCategoryDefinition { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 5; + public static final int VARIABLE_FIELD_COUNT = 3; + public static final int VARIABLE_BLOCK_START = 17; + public static final int MAX_SIZE = 49152032; + @Nullable + public String id; + @Nullable + public String name; + @Nullable + public String description; + public int order; + + public SubCategoryDefinition() { + } + + public SubCategoryDefinition(@Nullable String id, @Nullable String name, @Nullable String description, int order) { + this.id = id; + this.name = name; + this.description = description; + this.order = order; + } + + public SubCategoryDefinition(@Nonnull SubCategoryDefinition other) { + this.id = other.id; + this.name = other.name; + this.description = other.description; + this.order = other.order; + } + + @Nonnull + public static SubCategoryDefinition deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("SubCategoryDefinition", 17, buf.readableBytes() - offset); + } else { + SubCategoryDefinition obj = new SubCategoryDefinition(); + byte nullBits = buf.getByte(offset); + obj.order = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Name", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + int nameLen = VarInt.peek(buf, varPos1); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos1 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos1 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 13); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Description", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 17 + varPosBase2; + int descriptionLen = VarInt.peek(buf, varPos2); + if (descriptionLen < 0) { + throw ProtocolException.invalidVarInt("Description"); + } + + int descriptionVarIntLen = VarInt.size(descriptionLen); + if (descriptionLen > 4096000) { + throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); + } + + if (varPos2 + descriptionVarIntLen + descriptionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Description", varPos2 + descriptionVarIntLen + descriptionLen, buf.readableBytes()); + } + + obj.description = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 17; + if ((nullBits & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + + int pos0 = offset + 17 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Name", fieldOffset1, maxEnd); + } + + int pos1 = offset + 17 + fieldOffset1; + int sl = VarInt.peek(buf, pos1); + pos1 += VarInt.size(sl) + sl; + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 13); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Description", fieldOffset2, maxEnd); + } + + int pos2 = offset + 17 + fieldOffset2; + int sl = VarInt.peek(buf, pos2); + pos2 += VarInt.size(sl) + sl; + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + + return maxEnd; + } + + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.id != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.name != null) { + nullBits = (byte)(nullBits | 2); + } + + if (this.description != null) { + nullBits = (byte)(nullBits | 4); + } + + buf.writeByte(nullBits); + buf.writeIntLE(this.order); + int idOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int nameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int descriptionOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.id != null) { + buf.setIntLE(idOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.id, 4096000); + } else { + buf.setIntLE(idOffsetSlot, -1); + } + + if (this.name != null) { + buf.setIntLE(nameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.name, 4096000); + } else { + buf.setIntLE(nameOffsetSlot, -1); + } + + if (this.description != null) { + buf.setIntLE(descriptionOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.description, 4096000); + } else { + buf.setIntLE(descriptionOffsetSlot, -1); + } + } + + public int computeSize() { + int size = 17; + if (this.id != null) { + size += PacketIO.stringSize(this.id); + } + + if (this.name != null) { + size += PacketIO.stringSize(this.name); + } + + if (this.description != null) { + size += PacketIO.stringSize(this.description); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 1) != 0) { + int idOffset = buffer.getIntLE(offset + 5); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 17) { + return ValidationResult.error("Invalid offset for Id"); + } + + int pos = offset + 17 + idOffset; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } + } + + if ((nullBits & 2) != 0) { + int nameOffset = buffer.getIntLE(offset + 9); + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 17) { + return ValidationResult.error("Invalid offset for Name"); + } + + int posx = offset + 17 + nameOffset; + int nameLen = VarInt.peek(buffer, posx); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } + + if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); + } + + posx += VarInt.size(nameLen); + posx += nameLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); + } + } + + if ((nullBits & 4) != 0) { + int descriptionOffset = buffer.getIntLE(offset + 13); + if (descriptionOffset < 0 || descriptionOffset > buffer.writerIndex() - offset - 17) { + return ValidationResult.error("Invalid offset for Description"); + } + + int posxx = offset + 17 + descriptionOffset; + int descriptionLen = VarInt.peek(buffer, posxx); + if (descriptionLen < 0) { + return ValidationResult.error("Invalid string length for Description"); + } + + if (descriptionLen > 4096000) { + return ValidationResult.error("Description exceeds max length 4096000"); + } + + posxx += VarInt.size(descriptionLen); + posxx += descriptionLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Description"); + } + } + + return ValidationResult.OK; + } + } + + public SubCategoryDefinition clone() { + SubCategoryDefinition copy = new SubCategoryDefinition(); + copy.id = this.id; + copy.name = this.name; + copy.description = this.description; + copy.order = this.order; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof SubCategoryDefinition other) + ? false + : Objects.equals(this.id, other.id) + && Objects.equals(this.name, other.name) + && Objects.equals(this.description, other.description) + && this.order == other.order; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.id, this.name, this.description, this.order); + } +} diff --git a/src/com/hypixel/hytale/protocol/SurfaceType.java b/src/com/hypixel/hytale/protocol/SurfaceType.java new file mode 100644 index 00000000..14be0033 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/SurfaceType.java @@ -0,0 +1,29 @@ +package com.hypixel.hytale.protocol; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum SurfaceType { + Any(0), + Reflective(1), + Mixed(2), + Absorbent(3); + + public static final SurfaceType[] VALUES = values(); + private final int value; + + private SurfaceType(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static SurfaceType fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("SurfaceType", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/TagPattern.java b/src/com/hypixel/hytale/protocol/TagPattern.java index 9827988d..72aa7210 100644 --- a/src/com/hypixel/hytale/protocol/TagPattern.java +++ b/src/com/hypixel/hytale/protocol/TagPattern.java @@ -42,41 +42,55 @@ public class TagPattern { @Nonnull public static TagPattern deserialize(@Nonnull ByteBuf buf, int offset) { - TagPattern obj = new TagPattern(); - byte nullBits = buf.getByte(offset); - obj.type = TagPatternType.fromValue(buf.getByte(offset + 1)); - obj.tagIndex = buf.getIntLE(offset + 2); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 14 + buf.getIntLE(offset + 6); - int operandsCount = VarInt.peek(buf, varPos0); - if (operandsCount < 0) { - throw ProtocolException.negativeLength("Operands", operandsCount); + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("TagPattern", 14, buf.readableBytes() - offset); + } else { + TagPattern obj = new TagPattern(); + byte nullBits = buf.getByte(offset); + obj.type = TagPatternType.fromValue(buf.getByte(offset + 1)); + obj.tagIndex = buf.getIntLE(offset + 2); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Operands", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 14 + varPosBase0; + int operandsCount = VarInt.peek(buf, varPos0); + if (operandsCount < 0) { + throw ProtocolException.invalidVarInt("Operands"); + } + + int varIntLen = VarInt.size(operandsCount); + if (operandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Operands", operandsCount, 4096000); + } + + if (varPos0 + varIntLen + operandsCount * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Operands", varPos0 + varIntLen + operandsCount * 6, buf.readableBytes()); + } + + obj.operands = new TagPattern[operandsCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < operandsCount; i++) { + obj.operands[i] = deserialize(buf, elemPos); + elemPos += computeBytesConsumed(buf, elemPos); + } } - if (operandsCount > 4096000) { - throw ProtocolException.arrayTooLong("Operands", operandsCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 10); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Not", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 14 + varPosBase1; + obj.not = deserialize(buf, varPos1); } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + operandsCount * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Operands", varPos0 + varIntLen + operandsCount * 6, buf.readableBytes()); - } - - obj.operands = new TagPattern[operandsCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < operandsCount; i++) { - obj.operands[i] = deserialize(buf, elemPos); - elemPos += computeBytesConsumed(buf, elemPos); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 14 + buf.getIntLE(offset + 10); - obj.not = deserialize(buf, varPos1); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -84,9 +98,13 @@ public class TagPattern { int maxEnd = 14; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Operands", fieldOffset0, maxEnd); + } + int pos0 = offset + 14 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += computeBytesConsumed(buf, pos0); @@ -99,6 +117,10 @@ public class TagPattern { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 10); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Not", fieldOffset1, maxEnd); + } + int pos1 = offset + 14 + fieldOffset1; pos1 += computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -175,58 +197,55 @@ public class TagPattern { return ValidationResult.error("Buffer too small: expected at least 14 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int operandsOffset = buffer.getIntLE(offset + 6); - if (operandsOffset < 0) { - return ValidationResult.error("Invalid offset for Operands"); - } - - int pos = offset + 14 + operandsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Operands"); - } - - int operandsCount = VarInt.peek(buffer, pos); - if (operandsCount < 0) { - return ValidationResult.error("Invalid array count for Operands"); - } - - if (operandsCount > 4096000) { - return ValidationResult.error("Operands exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < operandsCount; i++) { - ValidationResult structResult = validateStructure(buffer, pos); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid TagPattern in Operands[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid TagPatternType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for Operands"); } - pos += computeBytesConsumed(buffer, pos); + int pos = offset + 14 + v; + int operandsCount = VarInt.peek(buffer, pos); + if (operandsCount < 0) { + return ValidationResult.error("Invalid array count for Operands"); + } + + if (operandsCount > 4096000) { + return ValidationResult.error("Operands exceeds max length 4096000"); + } + + pos += VarInt.size(operandsCount); + + for (int i = 0; i < operandsCount; i++) { + ValidationResult structResult = validateStructure(buffer, pos); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid TagPattern in Operands[" + i + "]: " + structResult.error()); + } + + pos += computeBytesConsumed(buffer, pos); + } } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for Not"); + } + + int posx = offset + 14 + v; + ValidationResult notResult = validateStructure(buffer, posx); + if (!notResult.isValid()) { + return ValidationResult.error("Invalid Not: " + notResult.error()); + } + + posx += computeBytesConsumed(buffer, posx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int notOffset = buffer.getIntLE(offset + 10); - if (notOffset < 0) { - return ValidationResult.error("Invalid offset for Not"); - } - - int posx = offset + 14 + notOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Not"); - } - - ValidationResult notResult = validateStructure(buffer, posx); - if (!notResult.isValid()) { - return ValidationResult.error("Invalid Not: " + notResult.error()); - } - - posx += computeBytesConsumed(buffer, posx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/TargetedDamage.java b/src/com/hypixel/hytale/protocol/TargetedDamage.java index cda3da70..624950d1 100644 --- a/src/com/hypixel/hytale/protocol/TargetedDamage.java +++ b/src/com/hypixel/hytale/protocol/TargetedDamage.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -34,17 +35,21 @@ public class TargetedDamage { @Nonnull public static TargetedDamage deserialize(@Nonnull ByteBuf buf, int offset) { - TargetedDamage obj = new TargetedDamage(); - byte nullBits = buf.getByte(offset); - obj.index = buf.getIntLE(offset + 1); - obj.next = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - obj.damageEffects = DamageEffects.deserialize(buf, pos); - pos += DamageEffects.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("TargetedDamage", 9, buf.readableBytes() - offset); + } else { + TargetedDamage obj = new TargetedDamage(); + byte nullBits = buf.getByte(offset); + obj.index = buf.getIntLE(offset + 1); + obj.next = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + obj.damageEffects = DamageEffects.deserialize(buf, pos); + pos += DamageEffects.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/TeleportAck.java b/src/com/hypixel/hytale/protocol/TeleportAck.java index 9593ea42..39bf0989 100644 --- a/src/com/hypixel/hytale/protocol/TeleportAck.java +++ b/src/com/hypixel/hytale/protocol/TeleportAck.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class TeleportAck { @Nonnull public static TeleportAck deserialize(@Nonnull ByteBuf buf, int offset) { - TeleportAck obj = new TeleportAck(); - obj.teleportId = buf.getByte(offset + 0); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("TeleportAck", 1, buf.readableBytes() - offset); + } else { + TeleportAck obj = new TeleportAck(); + obj.teleportId = buf.getByte(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Tint.java b/src/com/hypixel/hytale/protocol/Tint.java index 6bfc52d9..1c601905 100644 --- a/src/com/hypixel/hytale/protocol/Tint.java +++ b/src/com/hypixel/hytale/protocol/Tint.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,14 +42,18 @@ public class Tint { @Nonnull public static Tint deserialize(@Nonnull ByteBuf buf, int offset) { - Tint obj = new Tint(); - obj.top = buf.getIntLE(offset + 0); - obj.bottom = buf.getIntLE(offset + 4); - obj.front = buf.getIntLE(offset + 8); - obj.back = buf.getIntLE(offset + 12); - obj.left = buf.getIntLE(offset + 16); - obj.right = buf.getIntLE(offset + 20); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("Tint", 24, buf.readableBytes() - offset); + } else { + Tint obj = new Tint(); + obj.top = buf.getIntLE(offset + 0); + obj.bottom = buf.getIntLE(offset + 4); + obj.front = buf.getIntLE(offset + 8); + obj.back = buf.getIntLE(offset + 12); + obj.left = buf.getIntLE(offset + 16); + obj.right = buf.getIntLE(offset + 20); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/ToggleGliderInteraction.java b/src/com/hypixel/hytale/protocol/ToggleGliderInteraction.java index 5b8946de..b2f4fa28 100644 --- a/src/com/hypixel/hytale/protocol/ToggleGliderInteraction.java +++ b/src/com/hypixel/hytale/protocol/ToggleGliderInteraction.java @@ -64,78 +64,107 @@ public class ToggleGliderInteraction extends SimpleInteraction { @Nonnull public static ToggleGliderInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - ToggleGliderInteraction obj = new ToggleGliderInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 39 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 39) { + throw ProtocolException.bufferTooSmall("ToggleGliderInteraction", 39, buf.readableBytes() - offset); + } else { + ToggleGliderInteraction obj = new ToggleGliderInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 39 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 39 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 39 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 39 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 39 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 39 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 39 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 39 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 39 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -143,6 +172,10 @@ public class ToggleGliderInteraction extends SimpleInteraction { int maxEnd = 39; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 39 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -152,9 +185,13 @@ public class ToggleGliderInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 39 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -167,6 +204,10 @@ public class ToggleGliderInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 39 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -176,9 +217,13 @@ public class ToggleGliderInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 39 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -186,6 +231,10 @@ public class ToggleGliderInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 39 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -324,119 +373,109 @@ public class ToggleGliderInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 39 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 39 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 39 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 39 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 39 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 39 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 39 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 39 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 39 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 39 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 39 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/Trail.java b/src/com/hypixel/hytale/protocol/Trail.java index 5e4afda2..fcc079bc 100644 --- a/src/com/hypixel/hytale/protocol/Trail.java +++ b/src/com/hypixel/hytale/protocol/Trail.java @@ -88,63 +88,87 @@ public class Trail { @Nonnull public static Trail deserialize(@Nonnull ByteBuf buf, int offset) { - Trail obj = new Trail(); - byte nullBits = buf.getByte(offset); - obj.lifeSpan = buf.getIntLE(offset + 1); - obj.roll = buf.getFloatLE(offset + 5); - if ((nullBits & 1) != 0) { - obj.start = Edge.deserialize(buf, offset + 9); - } - - if ((nullBits & 2) != 0) { - obj.end = Edge.deserialize(buf, offset + 18); - } - - obj.lightInfluence = buf.getFloatLE(offset + 27); - obj.renderMode = FXRenderMode.fromValue(buf.getByte(offset + 31)); - if ((nullBits & 4) != 0) { - obj.intersectionHighlight = IntersectionHighlight.deserialize(buf, offset + 32); - } - - obj.smooth = buf.getByte(offset + 40) != 0; - if ((nullBits & 8) != 0) { - obj.frameSize = Vector2i.deserialize(buf, offset + 41); - } - - if ((nullBits & 16) != 0) { - obj.frameRange = Range.deserialize(buf, offset + 49); - } - - obj.frameLifeSpan = buf.getIntLE(offset + 57); - if ((nullBits & 32) != 0) { - int varPos0 = offset + 69 + buf.getIntLE(offset + 61); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 69) { + throw ProtocolException.bufferTooSmall("Trail", 69, buf.readableBytes() - offset); + } else { + Trail obj = new Trail(); + byte nullBits = buf.getByte(offset); + obj.lifeSpan = buf.getIntLE(offset + 1); + obj.roll = buf.getFloatLE(offset + 5); + if ((nullBits & 1) != 0) { + obj.start = Edge.deserialize(buf, offset + 9); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + obj.end = Edge.deserialize(buf, offset + 18); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + obj.lightInfluence = buf.getFloatLE(offset + 27); + obj.renderMode = FXRenderMode.fromValue(buf.getByte(offset + 31)); + if ((nullBits & 4) != 0) { + obj.intersectionHighlight = IntersectionHighlight.deserialize(buf, offset + 32); + } + + obj.smooth = buf.getByte(offset + 40) != 0; + if ((nullBits & 8) != 0) { + obj.frameSize = Vector2i.deserialize(buf, offset + 41); + } + + if ((nullBits & 16) != 0) { + obj.frameRange = Range.deserialize(buf, offset + 49); + } + + obj.frameLifeSpan = buf.getIntLE(offset + 57); + if ((nullBits & 32) != 0) { + int varPosBase0 = buf.getIntLE(offset + 61); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 69) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 69 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 64) != 0) { + int varPosBase1 = buf.getIntLE(offset + 65); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 69) { + throw ProtocolException.invalidOffset("Texture", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 69 + varPosBase1; + int textureLen = VarInt.peek(buf, varPos1); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarIntLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (varPos1 + textureVarIntLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", varPos1 + textureVarIntLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 64) != 0) { - int varPos1 = offset + 69 + buf.getIntLE(offset + 65); - int textureLen = VarInt.peek(buf, varPos1); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); - } - - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } - - obj.texture = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -152,9 +176,13 @@ public class Trail { int maxEnd = 69; if ((nullBits & 32) != 0) { int fieldOffset0 = buf.getIntLE(offset + 61); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 69) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 69 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -162,9 +190,13 @@ public class Trail { if ((nullBits & 64) != 0) { int fieldOffset1 = buf.getIntLE(offset + 65); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 69) { + throw ProtocolException.invalidOffset("Texture", fieldOffset1, maxEnd); + } + int pos1 = offset + 69 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -279,61 +311,58 @@ public class Trail { return ValidationResult.error("Buffer too small: expected at least 69 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 32) != 0) { - int idOffset = buffer.getIntLE(offset + 61); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); + int v = buffer.getByte(offset + 31) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid FXRenderMode value for RenderMode"); + } else { + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 61); + if (v < 0 || v > buffer.writerIndex() - offset - 69) { + return ValidationResult.error("Invalid offset for Id"); + } + + int pos = offset + 69 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } } - int pos = offset + 69 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 65); + if (v < 0 || v > buffer.writerIndex() - offset - 69) { + return ValidationResult.error("Invalid offset for Texture"); + } + + int posx = offset + 69 + v; + int textureLen = VarInt.peek(buffer, posx); + if (textureLen < 0) { + return ValidationResult.error("Invalid string length for Texture"); + } + + if (textureLen > 4096000) { + return ValidationResult.error("Texture exceeds max length 4096000"); + } + + posx += VarInt.size(textureLen); + posx += textureLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Texture"); + } } - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } - - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); - } + return ValidationResult.OK; } - - if ((nullBits & 64) != 0) { - int textureOffset = buffer.getIntLE(offset + 65); - if (textureOffset < 0) { - return ValidationResult.error("Invalid offset for Texture"); - } - - int posx = offset + 69 + textureOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Texture"); - } - - int textureLen = VarInt.peek(buffer, posx); - if (textureLen < 0) { - return ValidationResult.error("Invalid string length for Texture"); - } - - if (textureLen > 4096000) { - return ValidationResult.error("Texture exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += textureLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Texture"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/Transform.java b/src/com/hypixel/hytale/protocol/Transform.java index e5937862..c15b4064 100644 --- a/src/com/hypixel/hytale/protocol/Transform.java +++ b/src/com/hypixel/hytale/protocol/Transform.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,17 +33,21 @@ public class Transform { @Nonnull public static Transform deserialize(@Nonnull ByteBuf buf, int offset) { - Transform obj = new Transform(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.position = Position.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 37) { + throw ProtocolException.bufferTooSmall("Transform", 37, buf.readableBytes() - offset); + } else { + Transform obj = new Transform(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.position = Position.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.orientation = Direction.deserialize(buf, offset + 25); - } + if ((nullBits & 2) != 0) { + obj.orientation = Direction.deserialize(buf, offset + 25); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,7 +83,12 @@ public class Transform { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 37 ? ValidationResult.error("Buffer too small: expected at least 37 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 37) { + return ValidationResult.error("Buffer too small: expected at least 37 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public Transform clone() { diff --git a/src/com/hypixel/hytale/protocol/TransformUpdate.java b/src/com/hypixel/hytale/protocol/TransformUpdate.java index 111907ed..a78f7f20 100644 --- a/src/com/hypixel/hytale/protocol/TransformUpdate.java +++ b/src/com/hypixel/hytale/protocol/TransformUpdate.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -27,9 +28,13 @@ public class TransformUpdate extends ComponentUpdate { @Nonnull public static TransformUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - TransformUpdate obj = new TransformUpdate(); - obj.transform = ModelTransform.deserialize(buf, offset + 0); - return obj; + if (buf.readableBytes() - offset < 49) { + throw ProtocolException.bufferTooSmall("TransformUpdate", 49, buf.readableBytes() - offset); + } else { + TransformUpdate obj = new TransformUpdate(); + obj.transform = ModelTransform.deserialize(buf, offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/TriggerCooldownInteraction.java b/src/com/hypixel/hytale/protocol/TriggerCooldownInteraction.java index 2fe3287d..f14515c1 100644 --- a/src/com/hypixel/hytale/protocol/TriggerCooldownInteraction.java +++ b/src/com/hypixel/hytale/protocol/TriggerCooldownInteraction.java @@ -69,83 +69,117 @@ public class TriggerCooldownInteraction extends SimpleInteraction { @Nonnull public static TriggerCooldownInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - TriggerCooldownInteraction obj = new TriggerCooldownInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 43 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 43) { + throw ProtocolException.bufferTooSmall("TriggerCooldownInteraction", 43, buf.readableBytes() - offset); + } else { + TriggerCooldownInteraction obj = new TriggerCooldownInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 43 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 43 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 43 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 43 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 43 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 43 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 43 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 43 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 39); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Cooldown", varPosBase5, buf.readableBytes()); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + int varPos5 = offset + 43 + varPosBase5; + obj.cooldown = InteractionCooldown.deserialize(buf, varPos5); } - } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 43 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); + return obj; } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 43 + buf.getIntLE(offset + 39); - obj.cooldown = InteractionCooldown.deserialize(buf, varPos5); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -153,6 +187,10 @@ public class TriggerCooldownInteraction extends SimpleInteraction { int maxEnd = 43; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 43 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -162,9 +200,13 @@ public class TriggerCooldownInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 43 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -177,6 +219,10 @@ public class TriggerCooldownInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 43 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -186,9 +232,13 @@ public class TriggerCooldownInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 43 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -196,6 +246,10 @@ public class TriggerCooldownInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 43 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -205,6 +259,10 @@ public class TriggerCooldownInteraction extends SimpleInteraction { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 39); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("Cooldown", fieldOffset5, maxEnd); + } + int pos5 = offset + 43 + fieldOffset5; pos5 += InteractionCooldown.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { @@ -360,138 +418,124 @@ public class TriggerCooldownInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 43 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 43 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 43 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 43 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 43 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 43 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 43 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 39); + if (v < 0 || v > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for Cooldown"); + } + + int posxx = offset + 43 + v; + ValidationResult cooldownResult = InteractionCooldown.validateStructure(buffer, posxx); + if (!cooldownResult.isValid()) { + return ValidationResult.error("Invalid Cooldown: " + cooldownResult.error()); + } + + posxx += InteractionCooldown.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 43 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 43 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 43 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 43 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int cooldownOffset = buffer.getIntLE(offset + 39); - if (cooldownOffset < 0) { - return ValidationResult.error("Invalid offset for Cooldown"); - } - - int posxxxxx = offset + 43 + cooldownOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Cooldown"); - } - - ValidationResult cooldownResult = InteractionCooldown.validateStructure(buffer, posxxxxx); - if (!cooldownResult.isValid()) { - return ValidationResult.error("Invalid Cooldown: " + cooldownResult.error()); - } - - posxxxxx += InteractionCooldown.computeBytesConsumed(buffer, posxxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/UIComponentsUpdate.java b/src/com/hypixel/hytale/protocol/UIComponentsUpdate.java index aa3a6077..cf63c525 100644 --- a/src/com/hypixel/hytale/protocol/UIComponentsUpdate.java +++ b/src/com/hypixel/hytale/protocol/UIComponentsUpdate.java @@ -33,12 +33,12 @@ public class UIComponentsUpdate extends ComponentUpdate { int pos = offset + 0; int componentsCount = VarInt.peek(buf, pos); if (componentsCount < 0) { - throw ProtocolException.negativeLength("Components", componentsCount); - } else if (componentsCount > 4096000) { - throw ProtocolException.arrayTooLong("Components", componentsCount, 4096000); + throw ProtocolException.invalidVarInt("Components"); } else { int componentsVarLen = VarInt.size(componentsCount); - if (pos + componentsVarLen + componentsCount * 4L > buf.readableBytes()) { + if (componentsCount > 4096000) { + throw ProtocolException.arrayTooLong("Components", componentsCount, 4096000); + } else if (pos + componentsVarLen + componentsCount * 4L > buf.readableBytes()) { throw ProtocolException.bufferTooSmall("Components", pos + componentsVarLen + componentsCount * 4, buf.readableBytes()); } else { pos += componentsVarLen; @@ -57,7 +57,7 @@ public class UIComponentsUpdate extends ComponentUpdate { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 0; int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 4; + pos += VarInt.size(arrLen) + arrLen * 4; return pos - offset; } @@ -94,7 +94,7 @@ public class UIComponentsUpdate extends ComponentUpdate { } else if (componentsCount > 4096000) { return ValidationResult.error("Components exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(componentsCount); pos += componentsCount * 4; return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Components") : ValidationResult.OK; } diff --git a/src/com/hypixel/hytale/protocol/UVMotion.java b/src/com/hypixel/hytale/protocol/UVMotion.java index d066d0bc..1c4d1619 100644 --- a/src/com/hypixel/hytale/protocol/UVMotion.java +++ b/src/com/hypixel/hytale/protocol/UVMotion.java @@ -58,31 +58,39 @@ public class UVMotion { @Nonnull public static UVMotion deserialize(@Nonnull ByteBuf buf, int offset) { - UVMotion obj = new UVMotion(); - byte nullBits = buf.getByte(offset); - obj.addRandomUVOffset = buf.getByte(offset + 1) != 0; - obj.speedX = buf.getFloatLE(offset + 2); - obj.speedY = buf.getFloatLE(offset + 6); - obj.scale = buf.getFloatLE(offset + 10); - obj.strength = buf.getFloatLE(offset + 14); - obj.strengthCurveType = UVMotionCurveType.fromValue(buf.getByte(offset + 18)); - int pos = offset + 19; - if ((nullBits & 1) != 0) { - int textureLen = VarInt.peek(buf, pos); - if (textureLen < 0) { - throw ProtocolException.negativeLength("Texture", textureLen); + if (buf.readableBytes() - offset < 19) { + throw ProtocolException.bufferTooSmall("UVMotion", 19, buf.readableBytes() - offset); + } else { + UVMotion obj = new UVMotion(); + byte nullBits = buf.getByte(offset); + obj.addRandomUVOffset = buf.getByte(offset + 1) != 0; + obj.speedX = buf.getFloatLE(offset + 2); + obj.speedY = buf.getFloatLE(offset + 6); + obj.scale = buf.getFloatLE(offset + 10); + obj.strength = buf.getFloatLE(offset + 14); + obj.strengthCurveType = UVMotionCurveType.fromValue(buf.getByte(offset + 18)); + int pos = offset + 19; + if ((nullBits & 1) != 0) { + int textureLen = VarInt.peek(buf, pos); + if (textureLen < 0) { + throw ProtocolException.invalidVarInt("Texture"); + } + + int textureVarLen = VarInt.size(textureLen); + if (textureLen > 4096000) { + throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); + } + + if (pos + textureVarLen + textureLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Texture", pos + textureVarLen + textureLen, buf.readableBytes()); + } + + obj.texture = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += textureVarLen + textureLen; } - if (textureLen > 4096000) { - throw ProtocolException.stringTooLong("Texture", textureLen, 4096000); - } - - int textureVarLen = VarInt.length(buf, pos); - obj.texture = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += textureVarLen + textureLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,7 +98,7 @@ public class UVMotion { int pos = offset + 19; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -128,25 +136,30 @@ public class UVMotion { return ValidationResult.error("Buffer too small: expected at least 19 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 19; - if ((nullBits & 1) != 0) { - int textureLen = VarInt.peek(buffer, pos); - if (textureLen < 0) { - return ValidationResult.error("Invalid string length for Texture"); + int v = buffer.getByte(offset + 18) & 255; + if (v >= 9) { + return ValidationResult.error("Invalid UVMotionCurveType value for StrengthCurveType"); + } else { + v = offset + 19; + if ((nullBits & 1) != 0) { + int textureLen = VarInt.peek(buffer, v); + if (textureLen < 0) { + return ValidationResult.error("Invalid string length for Texture"); + } + + if (textureLen > 4096000) { + return ValidationResult.error("Texture exceeds max length 4096000"); + } + + v += VarInt.size(textureLen); + v += textureLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Texture"); + } } - if (textureLen > 4096000) { - return ValidationResult.error("Texture exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += textureLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Texture"); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/UseBlockInteraction.java b/src/com/hypixel/hytale/protocol/UseBlockInteraction.java index 932aa51c..c05bc2d1 100644 --- a/src/com/hypixel/hytale/protocol/UseBlockInteraction.java +++ b/src/com/hypixel/hytale/protocol/UseBlockInteraction.java @@ -67,79 +67,108 @@ public class UseBlockInteraction extends SimpleBlockInteraction { @Nonnull public static UseBlockInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - UseBlockInteraction obj = new UseBlockInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - obj.useLatestTarget = buf.getByte(offset + 19) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 40 + buf.getIntLE(offset + 20); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 40) { + throw ProtocolException.bufferTooSmall("UseBlockInteraction", 40, buf.readableBytes() - offset); + } else { + UseBlockInteraction obj = new UseBlockInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + obj.useLatestTarget = buf.getByte(offset + 19) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 20); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 40 + buf.getIntLE(offset + 24); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 40 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 24); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 40 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 40 + buf.getIntLE(offset + 28); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 28); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 40 + buf.getIntLE(offset + 32); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 40 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 32); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 40 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 36); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 40 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 40 + buf.getIntLE(offset + 36); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -147,6 +176,10 @@ public class UseBlockInteraction extends SimpleBlockInteraction { int maxEnd = 40; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 20); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 40 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -156,9 +189,13 @@ public class UseBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 24); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 40 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -171,6 +208,10 @@ public class UseBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 28); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 40 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -180,9 +221,13 @@ public class UseBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 32); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 40 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -190,6 +235,10 @@ public class UseBlockInteraction extends SimpleBlockInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 36); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 40) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 40 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -329,119 +378,109 @@ public class UseBlockInteraction extends SimpleBlockInteraction { return ValidationResult.error("Buffer too small: expected at least 40 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 20); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 20); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 40 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 40 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 24); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 40 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 28); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 40 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 32); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 40 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 36); + if (v < 0 || v > buffer.writerIndex() - offset - 40) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 40 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 24); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 40 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 28); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 40 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 32); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 40 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 36); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 40 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/UseEntityInteraction.java b/src/com/hypixel/hytale/protocol/UseEntityInteraction.java index 05e83b18..a101b0fd 100644 --- a/src/com/hypixel/hytale/protocol/UseEntityInteraction.java +++ b/src/com/hypixel/hytale/protocol/UseEntityInteraction.java @@ -64,78 +64,107 @@ public class UseEntityInteraction extends SimpleInteraction { @Nonnull public static UseEntityInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - UseEntityInteraction obj = new UseEntityInteraction(); - byte nullBits = buf.getByte(offset); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); - obj.runTime = buf.getFloatLE(offset + 6); - obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; - obj.next = buf.getIntLE(offset + 11); - obj.failed = buf.getIntLE(offset + 15); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 39 + buf.getIntLE(offset + 19); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 39) { + throw ProtocolException.bufferTooSmall("UseEntityInteraction", 39, buf.readableBytes() - offset); + } else { + UseEntityInteraction obj = new UseEntityInteraction(); + byte nullBits = buf.getByte(offset); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2); + obj.runTime = buf.getFloatLE(offset + 6); + obj.cancelOnItemChange = buf.getByte(offset + 10) != 0; + obj.next = buf.getIntLE(offset + 11); + obj.failed = buf.getIntLE(offset + 15); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 39 + buf.getIntLE(offset + 23); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + int varPos0 = offset + 39 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + int varPos1 = offset + 39 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 39 + buf.getIntLE(offset + 27); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 27); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 39 + buf.getIntLE(offset + 31); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 39 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 31); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 39 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } + + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } + + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } + + obj.tags = new int[tagsCount]; + + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); + } } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 35); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 39 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - obj.tags = new int[tagsCount]; - - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } + return obj; } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 39 + buf.getIntLE(offset + 35); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -143,6 +172,10 @@ public class UseEntityInteraction extends SimpleInteraction { int maxEnd = 39; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 39 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -152,9 +185,13 @@ public class UseEntityInteraction extends SimpleInteraction { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 39 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -167,6 +204,10 @@ public class UseEntityInteraction extends SimpleInteraction { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 27); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 39 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -176,9 +217,13 @@ public class UseEntityInteraction extends SimpleInteraction { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 31); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 39 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -186,6 +231,10 @@ public class UseEntityInteraction extends SimpleInteraction { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 35); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 39) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 39 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -324,119 +373,109 @@ public class UseEntityInteraction extends SimpleInteraction { return ValidationResult.error("Buffer too small: expected at least 39 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int effectsOffset = buffer.getIntLE(offset + 19); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Effects"); + } + + int pos = offset + 39 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); } - int pos = offset + 39 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 39 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; + } } - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 39 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - pos += InteractionEffects.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 31); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Tags"); + } + + int posx = offset + 39 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } + + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } + + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 35); + if (v < 0 || v > buffer.writerIndex() - offset - 39) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 39 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int settingsOffset = buffer.getIntLE(offset + 23); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 39 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits & 4) != 0) { - int rulesOffset = buffer.getIntLE(offset + 27); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 39 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits & 8) != 0) { - int tagsOffset = buffer.getIntLE(offset + 31); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 39 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits & 16) != 0) { - int cameraOffset = buffer.getIntLE(offset + 35); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 39 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/Vector2f.java b/src/com/hypixel/hytale/protocol/Vector2f.java deleted file mode 100644 index 66a342db..00000000 --- a/src/com/hypixel/hytale/protocol/Vector2f.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.hypixel.hytale.protocol; - -import com.hypixel.hytale.protocol.io.ValidationResult; -import io.netty.buffer.ByteBuf; -import java.util.Objects; -import javax.annotation.Nonnull; - -public class Vector2f { - public static final int NULLABLE_BIT_FIELD_SIZE = 0; - public static final int FIXED_BLOCK_SIZE = 8; - public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 8; - public static final int MAX_SIZE = 8; - public float x; - public float y; - - public Vector2f() { - } - - public Vector2f(float x, float y) { - this.x = x; - this.y = y; - } - - public Vector2f(@Nonnull Vector2f other) { - this.x = other.x; - this.y = other.y; - } - - @Nonnull - public static Vector2f deserialize(@Nonnull ByteBuf buf, int offset) { - Vector2f obj = new Vector2f(); - obj.x = buf.getFloatLE(offset + 0); - obj.y = buf.getFloatLE(offset + 4); - return obj; - } - - public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 8; - } - - public void serialize(@Nonnull ByteBuf buf) { - buf.writeFloatLE(this.x); - buf.writeFloatLE(this.y); - } - - public int computeSize() { - return 8; - } - - public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 8 ? ValidationResult.error("Buffer too small: expected at least 8 bytes") : ValidationResult.OK; - } - - public Vector2f clone() { - Vector2f copy = new Vector2f(); - copy.x = this.x; - copy.y = this.y; - return copy; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else { - return !(obj instanceof Vector2f other) ? false : this.x == other.x && this.y == other.y; - } - } - - @Override - public int hashCode() { - return Objects.hash(this.x, this.y); - } -} diff --git a/src/com/hypixel/hytale/protocol/Vector2i.java b/src/com/hypixel/hytale/protocol/Vector2i.java index 9084b746..526aa7b8 100644 --- a/src/com/hypixel/hytale/protocol/Vector2i.java +++ b/src/com/hypixel/hytale/protocol/Vector2i.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class Vector2i { @Nonnull public static Vector2i deserialize(@Nonnull ByteBuf buf, int offset) { - Vector2i obj = new Vector2i(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("Vector2i", 8, buf.readableBytes() - offset); + } else { + Vector2i obj = new Vector2i(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Vector3d.java b/src/com/hypixel/hytale/protocol/Vector3d.java index 2cdecc4b..37d462db 100644 --- a/src/com/hypixel/hytale/protocol/Vector3d.java +++ b/src/com/hypixel/hytale/protocol/Vector3d.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class Vector3d { @Nonnull public static Vector3d deserialize(@Nonnull ByteBuf buf, int offset) { - Vector3d obj = new Vector3d(); - obj.x = buf.getDoubleLE(offset + 0); - obj.y = buf.getDoubleLE(offset + 8); - obj.z = buf.getDoubleLE(offset + 16); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("Vector3d", 24, buf.readableBytes() - offset); + } else { + Vector3d obj = new Vector3d(); + obj.x = buf.getDoubleLE(offset + 0); + obj.y = buf.getDoubleLE(offset + 8); + obj.z = buf.getDoubleLE(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Vector3f.java b/src/com/hypixel/hytale/protocol/Vector3f.java deleted file mode 100644 index dbf23385..00000000 --- a/src/com/hypixel/hytale/protocol/Vector3f.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.hypixel.hytale.protocol; - -import com.hypixel.hytale.protocol.io.ValidationResult; -import io.netty.buffer.ByteBuf; -import java.util.Objects; -import javax.annotation.Nonnull; - -public class Vector3f { - public static final int NULLABLE_BIT_FIELD_SIZE = 0; - public static final int FIXED_BLOCK_SIZE = 12; - public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 12; - public static final int MAX_SIZE = 12; - public float x; - public float y; - public float z; - - public Vector3f() { - } - - public Vector3f(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - } - - public Vector3f(@Nonnull Vector3f other) { - this.x = other.x; - this.y = other.y; - this.z = other.z; - } - - @Nonnull - public static Vector3f deserialize(@Nonnull ByteBuf buf, int offset) { - Vector3f obj = new Vector3f(); - obj.x = buf.getFloatLE(offset + 0); - obj.y = buf.getFloatLE(offset + 4); - obj.z = buf.getFloatLE(offset + 8); - return obj; - } - - public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 12; - } - - public void serialize(@Nonnull ByteBuf buf) { - buf.writeFloatLE(this.x); - buf.writeFloatLE(this.y); - buf.writeFloatLE(this.z); - } - - public int computeSize() { - return 12; - } - - public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 12 ? ValidationResult.error("Buffer too small: expected at least 12 bytes") : ValidationResult.OK; - } - - public Vector3f clone() { - Vector3f copy = new Vector3f(); - copy.x = this.x; - copy.y = this.y; - copy.z = this.z; - return copy; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else { - return !(obj instanceof Vector3f other) ? false : this.x == other.x && this.y == other.y && this.z == other.z; - } - } - - @Override - public int hashCode() { - return Objects.hash(this.x, this.y, this.z); - } -} diff --git a/src/com/hypixel/hytale/protocol/Vector3i.java b/src/com/hypixel/hytale/protocol/Vector3i.java index 276abaec..4940bf32 100644 --- a/src/com/hypixel/hytale/protocol/Vector3i.java +++ b/src/com/hypixel/hytale/protocol/Vector3i.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class Vector3i { @Nonnull public static Vector3i deserialize(@Nonnull ByteBuf buf, int offset) { - Vector3i obj = new Vector3i(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("Vector3i", 12, buf.readableBytes() - offset); + } else { + Vector3i obj = new Vector3i(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/VelocityConfig.java b/src/com/hypixel/hytale/protocol/VelocityConfig.java index 31bd8c7a..54666ada 100644 --- a/src/com/hypixel/hytale/protocol/VelocityConfig.java +++ b/src/com/hypixel/hytale/protocol/VelocityConfig.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,14 +45,18 @@ public class VelocityConfig { @Nonnull public static VelocityConfig deserialize(@Nonnull ByteBuf buf, int offset) { - VelocityConfig obj = new VelocityConfig(); - obj.groundResistance = buf.getFloatLE(offset + 0); - obj.groundResistanceMax = buf.getFloatLE(offset + 4); - obj.airResistance = buf.getFloatLE(offset + 8); - obj.airResistanceMax = buf.getFloatLE(offset + 12); - obj.threshold = buf.getFloatLE(offset + 16); - obj.style = VelocityThresholdStyle.fromValue(buf.getByte(offset + 20)); - return obj; + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("VelocityConfig", 21, buf.readableBytes() - offset); + } else { + VelocityConfig obj = new VelocityConfig(); + obj.groundResistance = buf.getFloatLE(offset + 0); + obj.groundResistanceMax = buf.getFloatLE(offset + 4); + obj.airResistance = buf.getFloatLE(offset + 8); + obj.airResistanceMax = buf.getFloatLE(offset + 12); + obj.threshold = buf.getFloatLE(offset + 16); + obj.style = VelocityThresholdStyle.fromValue(buf.getByte(offset + 20)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +77,12 @@ public class VelocityConfig { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 21 ? ValidationResult.error("Buffer too small: expected at least 21 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 21) { + return ValidationResult.error("Buffer too small: expected at least 21 bytes"); + } else { + int v = buffer.getByte(offset + 20) & 255; + return v >= 2 ? ValidationResult.error("Invalid VelocityThresholdStyle value for Style") : ValidationResult.OK; + } } public VelocityConfig clone() { diff --git a/src/com/hypixel/hytale/protocol/ViewBobbing.java b/src/com/hypixel/hytale/protocol/ViewBobbing.java index 2f1244ab..fb4d9142 100644 --- a/src/com/hypixel/hytale/protocol/ViewBobbing.java +++ b/src/com/hypixel/hytale/protocol/ViewBobbing.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -28,15 +29,19 @@ public class ViewBobbing { @Nonnull public static ViewBobbing deserialize(@Nonnull ByteBuf buf, int offset) { - ViewBobbing obj = new ViewBobbing(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - obj.firstPerson = CameraShakeConfig.deserialize(buf, pos); - pos += CameraShakeConfig.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ViewBobbing", 1, buf.readableBytes() - offset); + } else { + ViewBobbing obj = new ViewBobbing(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + obj.firstPerson = CameraShakeConfig.deserialize(buf, pos); + pos += CameraShakeConfig.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/Weather.java b/src/com/hypixel/hytale/protocol/Weather.java index 790a565f..d90c2590 100644 --- a/src/com/hypixel/hytale/protocol/Weather.java +++ b/src/com/hypixel/hytale/protocol/Weather.java @@ -162,590 +162,733 @@ public class Weather { @Nonnull public static Weather deserialize(@Nonnull ByteBuf buf, int offset) { - Weather obj = new Weather(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 4); - if ((nullBits[0] & 1) != 0) { - obj.fog = NearFar.deserialize(buf, offset + 4); - } - - if ((nullBits[0] & 2) != 0) { - obj.fogOptions = FogOptions.deserialize(buf, offset + 12); - } - - if ((nullBits[0] & 4) != 0) { - int varPos0 = offset + 126 + buf.getIntLE(offset + 30); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 126) { + throw ProtocolException.bufferTooSmall("Weather", 126, buf.readableBytes() - offset); + } else { + Weather obj = new Weather(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 4); + if ((nullBits[0] & 1) != 0) { + obj.fog = NearFar.deserialize(buf, offset + 4); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits[0] & 2) != 0) { + obj.fogOptions = FogOptions.deserialize(buf, offset + 12); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits[0] & 8) != 0) { - int varPos1 = offset + 126 + buf.getIntLE(offset + 34); - int tagIndexesCount = VarInt.peek(buf, varPos1); - if (tagIndexesCount < 0) { - throw ProtocolException.negativeLength("TagIndexes", tagIndexesCount); - } - - if (tagIndexesCount > 4096000) { - throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + tagIndexesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("TagIndexes", varPos1 + varIntLen + tagIndexesCount * 4, buf.readableBytes()); - } - - obj.tagIndexes = new int[tagIndexesCount]; - - for (int i = 0; i < tagIndexesCount; i++) { - obj.tagIndexes[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); - } - } - - if ((nullBits[0] & 16) != 0) { - int varPos2 = offset + 126 + buf.getIntLE(offset + 38); - int starsLen = VarInt.peek(buf, varPos2); - if (starsLen < 0) { - throw ProtocolException.negativeLength("Stars", starsLen); - } - - if (starsLen > 4096000) { - throw ProtocolException.stringTooLong("Stars", starsLen, 4096000); - } - - obj.stars = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits[0] & 32) != 0) { - int varPos3 = offset + 126 + buf.getIntLE(offset + 42); - int moonsCount = VarInt.peek(buf, varPos3); - if (moonsCount < 0) { - throw ProtocolException.negativeLength("Moons", moonsCount); - } - - if (moonsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Moons", moonsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - obj.moons = new HashMap<>(moonsCount); - int dictPos = varPos3 + varIntLen; - - for (int i = 0; i < moonsCount; i++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - int valLen = VarInt.peek(buf, dictPos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); + if ((nullBits[0] & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 30); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); } - if (valLen > 4096000) { - throw ProtocolException.stringTooLong("val", valLen, 4096000); + int varPos0 = offset + 126 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); } - int valVarLen = VarInt.length(buf, dictPos); - String val = PacketIO.readVarString(buf, dictPos); - dictPos += valVarLen + valLen; - if (obj.moons.put(key, val) != null) { - throw ProtocolException.duplicateKey("moons", key); + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits[0] & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 34); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("TagIndexes", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 126 + varPosBase1; + int tagIndexesCount = VarInt.peek(buf, varPos1); + if (tagIndexesCount < 0) { + throw ProtocolException.invalidVarInt("TagIndexes"); + } + + int varIntLen = VarInt.size(tagIndexesCount); + if (tagIndexesCount > 4096000) { + throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); + } + + if (varPos1 + varIntLen + tagIndexesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TagIndexes", varPos1 + varIntLen + tagIndexesCount * 4, buf.readableBytes()); + } + + obj.tagIndexes = new int[tagIndexesCount]; + + for (int i = 0; i < tagIndexesCount; i++) { + obj.tagIndexes[i] = buf.getIntLE(varPos1 + varIntLen + i * 4); } } - } - if ((nullBits[0] & 64) != 0) { - int varPos4 = offset + 126 + buf.getIntLE(offset + 46); - int cloudsCount = VarInt.peek(buf, varPos4); - if (cloudsCount < 0) { - throw ProtocolException.negativeLength("Clouds", cloudsCount); + if ((nullBits[0] & 16) != 0) { + int varPosBase2 = buf.getIntLE(offset + 38); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Stars", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 126 + varPosBase2; + int starsLen = VarInt.peek(buf, varPos2); + if (starsLen < 0) { + throw ProtocolException.invalidVarInt("Stars"); + } + + int starsVarIntLen = VarInt.size(starsLen); + if (starsLen > 4096000) { + throw ProtocolException.stringTooLong("Stars", starsLen, 4096000); + } + + if (varPos2 + starsVarIntLen + starsLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Stars", varPos2 + starsVarIntLen + starsLen, buf.readableBytes()); + } + + obj.stars = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); } - if (cloudsCount > 4096000) { - throw ProtocolException.arrayTooLong("Clouds", cloudsCount, 4096000); - } + if ((nullBits[0] & 32) != 0) { + int varPosBase3 = buf.getIntLE(offset + 42); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Moons", varPosBase3, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos4); - if (varPos4 + varIntLen + cloudsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Clouds", varPos4 + varIntLen + cloudsCount * 1, buf.readableBytes()); - } + int varPos3 = offset + 126 + varPosBase3; + int moonsCount = VarInt.peek(buf, varPos3); + if (moonsCount < 0) { + throw ProtocolException.invalidVarInt("Moons"); + } - obj.clouds = new Cloud[cloudsCount]; - int elemPos = varPos4 + varIntLen; + int varIntLenx = VarInt.size(moonsCount); + if (moonsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Moons", moonsCount, 4096000); + } - for (int i = 0; i < cloudsCount; i++) { - obj.clouds[i] = Cloud.deserialize(buf, elemPos); - elemPos += Cloud.computeBytesConsumed(buf, elemPos); - } - } + obj.moons = new HashMap<>(moonsCount); + int dictPos = varPos3 + varIntLenx; - if ((nullBits[0] & 128) != 0) { - int varPos5 = offset + 126 + buf.getIntLE(offset + 50); - int sunlightDampingMultiplierCount = VarInt.peek(buf, varPos5); - if (sunlightDampingMultiplierCount < 0) { - throw ProtocolException.negativeLength("SunlightDampingMultiplier", sunlightDampingMultiplierCount); - } + for (int i = 0; i < moonsCount; i++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + int valLen = VarInt.peek(buf, dictPos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } - if (sunlightDampingMultiplierCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SunlightDampingMultiplier", sunlightDampingMultiplierCount, 4096000); - } + int valVarLen = VarInt.size(valLen); + if (valLen > 4096000) { + throw ProtocolException.stringTooLong("val", valLen, 4096000); + } - int varIntLen = VarInt.length(buf, varPos5); - obj.sunlightDampingMultiplier = new HashMap<>(sunlightDampingMultiplierCount); - int dictPos = varPos5 + varIntLen; + if (dictPos + valVarLen + valLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", dictPos + valVarLen + valLen, buf.readableBytes()); + } - for (int i = 0; i < sunlightDampingMultiplierCount; i++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.sunlightDampingMultiplier.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("sunlightDampingMultiplier", keyx); + String val = PacketIO.readVarString(buf, dictPos); + dictPos += valVarLen + valLen; + if (obj.moons.put(key, val) != null) { + throw ProtocolException.duplicateKey("moons", key); + } } } - } - if ((nullBits[1] & 1) != 0) { - int varPos6 = offset + 126 + buf.getIntLE(offset + 54); - int sunlightColorsCount = VarInt.peek(buf, varPos6); - if (sunlightColorsCount < 0) { - throw ProtocolException.negativeLength("SunlightColors", sunlightColorsCount); - } + if ((nullBits[0] & 64) != 0) { + int varPosBase4 = buf.getIntLE(offset + 46); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Clouds", varPosBase4, buf.readableBytes()); + } - if (sunlightColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SunlightColors", sunlightColorsCount, 4096000); - } + int varPos4 = offset + 126 + varPosBase4; + int cloudsCount = VarInt.peek(buf, varPos4); + if (cloudsCount < 0) { + throw ProtocolException.invalidVarInt("Clouds"); + } - int varIntLen = VarInt.length(buf, varPos6); - obj.sunlightColors = new HashMap<>(sunlightColorsCount); - int dictPos = varPos6 + varIntLen; + int varIntLenx = VarInt.size(cloudsCount); + if (cloudsCount > 4096000) { + throw ProtocolException.arrayTooLong("Clouds", cloudsCount, 4096000); + } - for (int ix = 0; ix < sunlightColorsCount; ix++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - Color val = Color.deserialize(buf, dictPos); - dictPos += Color.computeBytesConsumed(buf, dictPos); - if (obj.sunlightColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("sunlightColors", keyx); + if (varPos4 + varIntLenx + cloudsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Clouds", varPos4 + varIntLenx + cloudsCount * 1, buf.readableBytes()); + } + + obj.clouds = new Cloud[cloudsCount]; + int elemPos = varPos4 + varIntLenx; + + for (int i = 0; i < cloudsCount; i++) { + obj.clouds[i] = Cloud.deserialize(buf, elemPos); + elemPos += Cloud.computeBytesConsumed(buf, elemPos); } } - } - if ((nullBits[1] & 2) != 0) { - int varPos7 = offset + 126 + buf.getIntLE(offset + 58); - int skyTopColorsCount = VarInt.peek(buf, varPos7); - if (skyTopColorsCount < 0) { - throw ProtocolException.negativeLength("SkyTopColors", skyTopColorsCount); - } + if ((nullBits[0] & 128) != 0) { + int varPosBase5 = buf.getIntLE(offset + 50); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunlightDampingMultiplier", varPosBase5, buf.readableBytes()); + } - if (skyTopColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SkyTopColors", skyTopColorsCount, 4096000); - } + int varPos5 = offset + 126 + varPosBase5; + int sunlightDampingMultiplierCount = VarInt.peek(buf, varPos5); + if (sunlightDampingMultiplierCount < 0) { + throw ProtocolException.invalidVarInt("SunlightDampingMultiplier"); + } - int varIntLen = VarInt.length(buf, varPos7); - obj.skyTopColors = new HashMap<>(skyTopColorsCount); - int dictPos = varPos7 + varIntLen; + int varIntLenxx = VarInt.size(sunlightDampingMultiplierCount); + if (sunlightDampingMultiplierCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SunlightDampingMultiplier", sunlightDampingMultiplierCount, 4096000); + } - for (int ixx = 0; ixx < skyTopColorsCount; ixx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); - dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); - if (obj.skyTopColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("skyTopColors", keyx); + obj.sunlightDampingMultiplier = new HashMap<>(sunlightDampingMultiplierCount); + int dictPos = varPos5 + varIntLenxx; + + for (int i = 0; i < sunlightDampingMultiplierCount; i++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.sunlightDampingMultiplier.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("sunlightDampingMultiplier", keyx); + } } } - } - if ((nullBits[1] & 4) != 0) { - int varPos8 = offset + 126 + buf.getIntLE(offset + 62); - int skyBottomColorsCount = VarInt.peek(buf, varPos8); - if (skyBottomColorsCount < 0) { - throw ProtocolException.negativeLength("SkyBottomColors", skyBottomColorsCount); - } + if ((nullBits[1] & 1) != 0) { + int varPosBase6 = buf.getIntLE(offset + 54); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunlightColors", varPosBase6, buf.readableBytes()); + } - if (skyBottomColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SkyBottomColors", skyBottomColorsCount, 4096000); - } + int varPos6 = offset + 126 + varPosBase6; + int sunlightColorsCount = VarInt.peek(buf, varPos6); + if (sunlightColorsCount < 0) { + throw ProtocolException.invalidVarInt("SunlightColors"); + } - int varIntLen = VarInt.length(buf, varPos8); - obj.skyBottomColors = new HashMap<>(skyBottomColorsCount); - int dictPos = varPos8 + varIntLen; + int varIntLenxx = VarInt.size(sunlightColorsCount); + if (sunlightColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SunlightColors", sunlightColorsCount, 4096000); + } - for (int ixxx = 0; ixxx < skyBottomColorsCount; ixxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); - dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); - if (obj.skyBottomColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("skyBottomColors", keyx); + obj.sunlightColors = new HashMap<>(sunlightColorsCount); + int dictPos = varPos6 + varIntLenxx; + + for (int ix = 0; ix < sunlightColorsCount; ix++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + Color val = Color.deserialize(buf, dictPos); + dictPos += Color.computeBytesConsumed(buf, dictPos); + if (obj.sunlightColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("sunlightColors", keyx); + } } } - } - if ((nullBits[1] & 8) != 0) { - int varPos9 = offset + 126 + buf.getIntLE(offset + 66); - int skySunsetColorsCount = VarInt.peek(buf, varPos9); - if (skySunsetColorsCount < 0) { - throw ProtocolException.negativeLength("SkySunsetColors", skySunsetColorsCount); - } + if ((nullBits[1] & 2) != 0) { + int varPosBase7 = buf.getIntLE(offset + 58); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SkyTopColors", varPosBase7, buf.readableBytes()); + } - if (skySunsetColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SkySunsetColors", skySunsetColorsCount, 4096000); - } + int varPos7 = offset + 126 + varPosBase7; + int skyTopColorsCount = VarInt.peek(buf, varPos7); + if (skyTopColorsCount < 0) { + throw ProtocolException.invalidVarInt("SkyTopColors"); + } - int varIntLen = VarInt.length(buf, varPos9); - obj.skySunsetColors = new HashMap<>(skySunsetColorsCount); - int dictPos = varPos9 + varIntLen; + int varIntLenxx = VarInt.size(skyTopColorsCount); + if (skyTopColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SkyTopColors", skyTopColorsCount, 4096000); + } - for (int ixxxx = 0; ixxxx < skySunsetColorsCount; ixxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); - dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); - if (obj.skySunsetColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("skySunsetColors", keyx); + obj.skyTopColors = new HashMap<>(skyTopColorsCount); + int dictPos = varPos7 + varIntLenxx; + + for (int ixx = 0; ixx < skyTopColorsCount; ixx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); + dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); + if (obj.skyTopColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("skyTopColors", keyx); + } } } - } - if ((nullBits[1] & 16) != 0) { - int varPos10 = offset + 126 + buf.getIntLE(offset + 70); - int sunColorsCount = VarInt.peek(buf, varPos10); - if (sunColorsCount < 0) { - throw ProtocolException.negativeLength("SunColors", sunColorsCount); - } + if ((nullBits[1] & 4) != 0) { + int varPosBase8 = buf.getIntLE(offset + 62); + if (varPosBase8 < 0 || varPosBase8 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SkyBottomColors", varPosBase8, buf.readableBytes()); + } - if (sunColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SunColors", sunColorsCount, 4096000); - } + int varPos8 = offset + 126 + varPosBase8; + int skyBottomColorsCount = VarInt.peek(buf, varPos8); + if (skyBottomColorsCount < 0) { + throw ProtocolException.invalidVarInt("SkyBottomColors"); + } - int varIntLen = VarInt.length(buf, varPos10); - obj.sunColors = new HashMap<>(sunColorsCount); - int dictPos = varPos10 + varIntLen; + int varIntLenxx = VarInt.size(skyBottomColorsCount); + if (skyBottomColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SkyBottomColors", skyBottomColorsCount, 4096000); + } - for (int ixxxxx = 0; ixxxxx < sunColorsCount; ixxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - Color val = Color.deserialize(buf, dictPos); - dictPos += Color.computeBytesConsumed(buf, dictPos); - if (obj.sunColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("sunColors", keyx); + obj.skyBottomColors = new HashMap<>(skyBottomColorsCount); + int dictPos = varPos8 + varIntLenxx; + + for (int ixxx = 0; ixxx < skyBottomColorsCount; ixxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); + dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); + if (obj.skyBottomColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("skyBottomColors", keyx); + } } } - } - if ((nullBits[1] & 32) != 0) { - int varPos11 = offset + 126 + buf.getIntLE(offset + 74); - int sunScalesCount = VarInt.peek(buf, varPos11); - if (sunScalesCount < 0) { - throw ProtocolException.negativeLength("SunScales", sunScalesCount); - } + if ((nullBits[1] & 8) != 0) { + int varPosBase9 = buf.getIntLE(offset + 66); + if (varPosBase9 < 0 || varPosBase9 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SkySunsetColors", varPosBase9, buf.readableBytes()); + } - if (sunScalesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SunScales", sunScalesCount, 4096000); - } + int varPos9 = offset + 126 + varPosBase9; + int skySunsetColorsCount = VarInt.peek(buf, varPos9); + if (skySunsetColorsCount < 0) { + throw ProtocolException.invalidVarInt("SkySunsetColors"); + } - int varIntLen = VarInt.length(buf, varPos11); - obj.sunScales = new HashMap<>(sunScalesCount); - int dictPos = varPos11 + varIntLen; + int varIntLenxx = VarInt.size(skySunsetColorsCount); + if (skySunsetColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SkySunsetColors", skySunsetColorsCount, 4096000); + } - for (int ixxxxxx = 0; ixxxxxx < sunScalesCount; ixxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.sunScales.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("sunScales", keyx); + obj.skySunsetColors = new HashMap<>(skySunsetColorsCount); + int dictPos = varPos9 + varIntLenxx; + + for (int ixxxx = 0; ixxxx < skySunsetColorsCount; ixxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); + dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); + if (obj.skySunsetColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("skySunsetColors", keyx); + } } } - } - if ((nullBits[1] & 64) != 0) { - int varPos12 = offset + 126 + buf.getIntLE(offset + 78); - int sunGlowColorsCount = VarInt.peek(buf, varPos12); - if (sunGlowColorsCount < 0) { - throw ProtocolException.negativeLength("SunGlowColors", sunGlowColorsCount); - } + if ((nullBits[1] & 16) != 0) { + int varPosBase10 = buf.getIntLE(offset + 70); + if (varPosBase10 < 0 || varPosBase10 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunColors", varPosBase10, buf.readableBytes()); + } - if (sunGlowColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SunGlowColors", sunGlowColorsCount, 4096000); - } + int varPos10 = offset + 126 + varPosBase10; + int sunColorsCount = VarInt.peek(buf, varPos10); + if (sunColorsCount < 0) { + throw ProtocolException.invalidVarInt("SunColors"); + } - int varIntLen = VarInt.length(buf, varPos12); - obj.sunGlowColors = new HashMap<>(sunGlowColorsCount); - int dictPos = varPos12 + varIntLen; + int varIntLenxx = VarInt.size(sunColorsCount); + if (sunColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SunColors", sunColorsCount, 4096000); + } - for (int ixxxxxxx = 0; ixxxxxxx < sunGlowColorsCount; ixxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); - dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); - if (obj.sunGlowColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("sunGlowColors", keyx); + obj.sunColors = new HashMap<>(sunColorsCount); + int dictPos = varPos10 + varIntLenxx; + + for (int ixxxxx = 0; ixxxxx < sunColorsCount; ixxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + Color val = Color.deserialize(buf, dictPos); + dictPos += Color.computeBytesConsumed(buf, dictPos); + if (obj.sunColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("sunColors", keyx); + } } } - } - if ((nullBits[1] & 128) != 0) { - int varPos13 = offset + 126 + buf.getIntLE(offset + 82); - int moonColorsCount = VarInt.peek(buf, varPos13); - if (moonColorsCount < 0) { - throw ProtocolException.negativeLength("MoonColors", moonColorsCount); - } + if ((nullBits[1] & 32) != 0) { + int varPosBase11 = buf.getIntLE(offset + 74); + if (varPosBase11 < 0 || varPosBase11 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunScales", varPosBase11, buf.readableBytes()); + } - if (moonColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("MoonColors", moonColorsCount, 4096000); - } + int varPos11 = offset + 126 + varPosBase11; + int sunScalesCount = VarInt.peek(buf, varPos11); + if (sunScalesCount < 0) { + throw ProtocolException.invalidVarInt("SunScales"); + } - int varIntLen = VarInt.length(buf, varPos13); - obj.moonColors = new HashMap<>(moonColorsCount); - int dictPos = varPos13 + varIntLen; + int varIntLenxx = VarInt.size(sunScalesCount); + if (sunScalesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SunScales", sunScalesCount, 4096000); + } - for (int ixxxxxxxx = 0; ixxxxxxxx < moonColorsCount; ixxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); - dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); - if (obj.moonColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("moonColors", keyx); + obj.sunScales = new HashMap<>(sunScalesCount); + int dictPos = varPos11 + varIntLenxx; + + for (int ixxxxxx = 0; ixxxxxx < sunScalesCount; ixxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.sunScales.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("sunScales", keyx); + } } } - } - if ((nullBits[2] & 1) != 0) { - int varPos14 = offset + 126 + buf.getIntLE(offset + 86); - int moonScalesCount = VarInt.peek(buf, varPos14); - if (moonScalesCount < 0) { - throw ProtocolException.negativeLength("MoonScales", moonScalesCount); - } + if ((nullBits[1] & 64) != 0) { + int varPosBase12 = buf.getIntLE(offset + 78); + if (varPosBase12 < 0 || varPosBase12 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunGlowColors", varPosBase12, buf.readableBytes()); + } - if (moonScalesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("MoonScales", moonScalesCount, 4096000); - } + int varPos12 = offset + 126 + varPosBase12; + int sunGlowColorsCount = VarInt.peek(buf, varPos12); + if (sunGlowColorsCount < 0) { + throw ProtocolException.invalidVarInt("SunGlowColors"); + } - int varIntLen = VarInt.length(buf, varPos14); - obj.moonScales = new HashMap<>(moonScalesCount); - int dictPos = varPos14 + varIntLen; + int varIntLenxx = VarInt.size(sunGlowColorsCount); + if (sunGlowColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SunGlowColors", sunGlowColorsCount, 4096000); + } - for (int ixxxxxxxxx = 0; ixxxxxxxxx < moonScalesCount; ixxxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.moonScales.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("moonScales", keyx); + obj.sunGlowColors = new HashMap<>(sunGlowColorsCount); + int dictPos = varPos12 + varIntLenxx; + + for (int ixxxxxxx = 0; ixxxxxxx < sunGlowColorsCount; ixxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); + dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); + if (obj.sunGlowColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("sunGlowColors", keyx); + } } } - } - if ((nullBits[2] & 2) != 0) { - int varPos15 = offset + 126 + buf.getIntLE(offset + 90); - int moonGlowColorsCount = VarInt.peek(buf, varPos15); - if (moonGlowColorsCount < 0) { - throw ProtocolException.negativeLength("MoonGlowColors", moonGlowColorsCount); - } + if ((nullBits[1] & 128) != 0) { + int varPosBase13 = buf.getIntLE(offset + 82); + if (varPosBase13 < 0 || varPosBase13 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("MoonColors", varPosBase13, buf.readableBytes()); + } - if (moonGlowColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("MoonGlowColors", moonGlowColorsCount, 4096000); - } + int varPos13 = offset + 126 + varPosBase13; + int moonColorsCount = VarInt.peek(buf, varPos13); + if (moonColorsCount < 0) { + throw ProtocolException.invalidVarInt("MoonColors"); + } - int varIntLen = VarInt.length(buf, varPos15); - obj.moonGlowColors = new HashMap<>(moonGlowColorsCount); - int dictPos = varPos15 + varIntLen; + int varIntLenxx = VarInt.size(moonColorsCount); + if (moonColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("MoonColors", moonColorsCount, 4096000); + } - for (int ixxxxxxxxxx = 0; ixxxxxxxxxx < moonGlowColorsCount; ixxxxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); - dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); - if (obj.moonGlowColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("moonGlowColors", keyx); + obj.moonColors = new HashMap<>(moonColorsCount); + int dictPos = varPos13 + varIntLenxx; + + for (int ixxxxxxxx = 0; ixxxxxxxx < moonColorsCount; ixxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); + dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); + if (obj.moonColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("moonColors", keyx); + } } } - } - if ((nullBits[2] & 4) != 0) { - int varPos16 = offset + 126 + buf.getIntLE(offset + 94); - int fogColorsCount = VarInt.peek(buf, varPos16); - if (fogColorsCount < 0) { - throw ProtocolException.negativeLength("FogColors", fogColorsCount); - } + if ((nullBits[2] & 1) != 0) { + int varPosBase14 = buf.getIntLE(offset + 86); + if (varPosBase14 < 0 || varPosBase14 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("MoonScales", varPosBase14, buf.readableBytes()); + } - if (fogColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("FogColors", fogColorsCount, 4096000); - } + int varPos14 = offset + 126 + varPosBase14; + int moonScalesCount = VarInt.peek(buf, varPos14); + if (moonScalesCount < 0) { + throw ProtocolException.invalidVarInt("MoonScales"); + } - int varIntLen = VarInt.length(buf, varPos16); - obj.fogColors = new HashMap<>(fogColorsCount); - int dictPos = varPos16 + varIntLen; + int varIntLenxx = VarInt.size(moonScalesCount); + if (moonScalesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("MoonScales", moonScalesCount, 4096000); + } - for (int ixxxxxxxxxxx = 0; ixxxxxxxxxxx < fogColorsCount; ixxxxxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - Color val = Color.deserialize(buf, dictPos); - dictPos += Color.computeBytesConsumed(buf, dictPos); - if (obj.fogColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("fogColors", keyx); + obj.moonScales = new HashMap<>(moonScalesCount); + int dictPos = varPos14 + varIntLenxx; + + for (int ixxxxxxxxx = 0; ixxxxxxxxx < moonScalesCount; ixxxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.moonScales.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("moonScales", keyx); + } } } - } - if ((nullBits[2] & 8) != 0) { - int varPos17 = offset + 126 + buf.getIntLE(offset + 98); - int fogHeightFalloffsCount = VarInt.peek(buf, varPos17); - if (fogHeightFalloffsCount < 0) { - throw ProtocolException.negativeLength("FogHeightFalloffs", fogHeightFalloffsCount); - } + if ((nullBits[2] & 2) != 0) { + int varPosBase15 = buf.getIntLE(offset + 90); + if (varPosBase15 < 0 || varPosBase15 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("MoonGlowColors", varPosBase15, buf.readableBytes()); + } - if (fogHeightFalloffsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("FogHeightFalloffs", fogHeightFalloffsCount, 4096000); - } + int varPos15 = offset + 126 + varPosBase15; + int moonGlowColorsCount = VarInt.peek(buf, varPos15); + if (moonGlowColorsCount < 0) { + throw ProtocolException.invalidVarInt("MoonGlowColors"); + } - int varIntLen = VarInt.length(buf, varPos17); - obj.fogHeightFalloffs = new HashMap<>(fogHeightFalloffsCount); - int dictPos = varPos17 + varIntLen; + int varIntLenxx = VarInt.size(moonGlowColorsCount); + if (moonGlowColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("MoonGlowColors", moonGlowColorsCount, 4096000); + } - for (int ixxxxxxxxxxxx = 0; ixxxxxxxxxxxx < fogHeightFalloffsCount; ixxxxxxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.fogHeightFalloffs.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("fogHeightFalloffs", keyx); + obj.moonGlowColors = new HashMap<>(moonGlowColorsCount); + int dictPos = varPos15 + varIntLenxx; + + for (int ixxxxxxxxxx = 0; ixxxxxxxxxx < moonGlowColorsCount; ixxxxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); + dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); + if (obj.moonGlowColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("moonGlowColors", keyx); + } } } - } - if ((nullBits[2] & 16) != 0) { - int varPos18 = offset + 126 + buf.getIntLE(offset + 102); - int fogDensitiesCount = VarInt.peek(buf, varPos18); - if (fogDensitiesCount < 0) { - throw ProtocolException.negativeLength("FogDensities", fogDensitiesCount); - } + if ((nullBits[2] & 4) != 0) { + int varPosBase16 = buf.getIntLE(offset + 94); + if (varPosBase16 < 0 || varPosBase16 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("FogColors", varPosBase16, buf.readableBytes()); + } - if (fogDensitiesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("FogDensities", fogDensitiesCount, 4096000); - } + int varPos16 = offset + 126 + varPosBase16; + int fogColorsCount = VarInt.peek(buf, varPos16); + if (fogColorsCount < 0) { + throw ProtocolException.invalidVarInt("FogColors"); + } - int varIntLen = VarInt.length(buf, varPos18); - obj.fogDensities = new HashMap<>(fogDensitiesCount); - int dictPos = varPos18 + varIntLen; + int varIntLenxx = VarInt.size(fogColorsCount); + if (fogColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("FogColors", fogColorsCount, 4096000); + } - for (int ixxxxxxxxxxxxx = 0; ixxxxxxxxxxxxx < fogDensitiesCount; ixxxxxxxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - float val = buf.getFloatLE(dictPos); - dictPos += 4; - if (obj.fogDensities.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("fogDensities", keyx); + obj.fogColors = new HashMap<>(fogColorsCount); + int dictPos = varPos16 + varIntLenxx; + + for (int ixxxxxxxxxxx = 0; ixxxxxxxxxxx < fogColorsCount; ixxxxxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + Color val = Color.deserialize(buf, dictPos); + dictPos += Color.computeBytesConsumed(buf, dictPos); + if (obj.fogColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("fogColors", keyx); + } } } - } - if ((nullBits[2] & 32) != 0) { - int varPos19 = offset + 126 + buf.getIntLE(offset + 106); - int screenEffectLen = VarInt.peek(buf, varPos19); - if (screenEffectLen < 0) { - throw ProtocolException.negativeLength("ScreenEffect", screenEffectLen); - } + if ((nullBits[2] & 8) != 0) { + int varPosBase17 = buf.getIntLE(offset + 98); + if (varPosBase17 < 0 || varPosBase17 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("FogHeightFalloffs", varPosBase17, buf.readableBytes()); + } - if (screenEffectLen > 4096000) { - throw ProtocolException.stringTooLong("ScreenEffect", screenEffectLen, 4096000); - } + int varPos17 = offset + 126 + varPosBase17; + int fogHeightFalloffsCount = VarInt.peek(buf, varPos17); + if (fogHeightFalloffsCount < 0) { + throw ProtocolException.invalidVarInt("FogHeightFalloffs"); + } - obj.screenEffect = PacketIO.readVarString(buf, varPos19, PacketIO.UTF8); - } + int varIntLenxx = VarInt.size(fogHeightFalloffsCount); + if (fogHeightFalloffsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("FogHeightFalloffs", fogHeightFalloffsCount, 4096000); + } - if ((nullBits[2] & 64) != 0) { - int varPos20 = offset + 126 + buf.getIntLE(offset + 110); - int screenEffectColorsCount = VarInt.peek(buf, varPos20); - if (screenEffectColorsCount < 0) { - throw ProtocolException.negativeLength("ScreenEffectColors", screenEffectColorsCount); - } + obj.fogHeightFalloffs = new HashMap<>(fogHeightFalloffsCount); + int dictPos = varPos17 + varIntLenxx; - if (screenEffectColorsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ScreenEffectColors", screenEffectColorsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos20); - obj.screenEffectColors = new HashMap<>(screenEffectColorsCount); - int dictPos = varPos20 + varIntLen; - - for (int ixxxxxxxxxxxxxx = 0; ixxxxxxxxxxxxxx < screenEffectColorsCount; ixxxxxxxxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); - dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); - if (obj.screenEffectColors.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("screenEffectColors", keyx); + for (int ixxxxxxxxxxxx = 0; ixxxxxxxxxxxx < fogHeightFalloffsCount; ixxxxxxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.fogHeightFalloffs.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("fogHeightFalloffs", keyx); + } } } - } - if ((nullBits[2] & 128) != 0) { - int varPos21 = offset + 126 + buf.getIntLE(offset + 114); - int colorFiltersCount = VarInt.peek(buf, varPos21); - if (colorFiltersCount < 0) { - throw ProtocolException.negativeLength("ColorFilters", colorFiltersCount); - } + if ((nullBits[2] & 16) != 0) { + int varPosBase18 = buf.getIntLE(offset + 102); + if (varPosBase18 < 0 || varPosBase18 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("FogDensities", varPosBase18, buf.readableBytes()); + } - if (colorFiltersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ColorFilters", colorFiltersCount, 4096000); - } + int varPos18 = offset + 126 + varPosBase18; + int fogDensitiesCount = VarInt.peek(buf, varPos18); + if (fogDensitiesCount < 0) { + throw ProtocolException.invalidVarInt("FogDensities"); + } - int varIntLen = VarInt.length(buf, varPos21); - obj.colorFilters = new HashMap<>(colorFiltersCount); - int dictPos = varPos21 + varIntLen; + int varIntLenxx = VarInt.size(fogDensitiesCount); + if (fogDensitiesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("FogDensities", fogDensitiesCount, 4096000); + } - for (int ixxxxxxxxxxxxxxx = 0; ixxxxxxxxxxxxxxx < colorFiltersCount; ixxxxxxxxxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - Color val = Color.deserialize(buf, dictPos); - dictPos += Color.computeBytesConsumed(buf, dictPos); - if (obj.colorFilters.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("colorFilters", keyx); + obj.fogDensities = new HashMap<>(fogDensitiesCount); + int dictPos = varPos18 + varIntLenxx; + + for (int ixxxxxxxxxxxxx = 0; ixxxxxxxxxxxxx < fogDensitiesCount; ixxxxxxxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + float val = buf.getFloatLE(dictPos); + dictPos += 4; + if (obj.fogDensities.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("fogDensities", keyx); + } } } - } - if ((nullBits[3] & 1) != 0) { - int varPos22 = offset + 126 + buf.getIntLE(offset + 118); - int waterTintsCount = VarInt.peek(buf, varPos22); - if (waterTintsCount < 0) { - throw ProtocolException.negativeLength("WaterTints", waterTintsCount); + if ((nullBits[2] & 32) != 0) { + int varPosBase19 = buf.getIntLE(offset + 106); + if (varPosBase19 < 0 || varPosBase19 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("ScreenEffect", varPosBase19, buf.readableBytes()); + } + + int varPos19 = offset + 126 + varPosBase19; + int screenEffectLen = VarInt.peek(buf, varPos19); + if (screenEffectLen < 0) { + throw ProtocolException.invalidVarInt("ScreenEffect"); + } + + int screenEffectVarIntLen = VarInt.size(screenEffectLen); + if (screenEffectLen > 4096000) { + throw ProtocolException.stringTooLong("ScreenEffect", screenEffectLen, 4096000); + } + + if (varPos19 + screenEffectVarIntLen + screenEffectLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ScreenEffect", varPos19 + screenEffectVarIntLen + screenEffectLen, buf.readableBytes()); + } + + obj.screenEffect = PacketIO.readVarString(buf, varPos19, PacketIO.UTF8); } - if (waterTintsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("WaterTints", waterTintsCount, 4096000); - } + if ((nullBits[2] & 64) != 0) { + int varPosBase20 = buf.getIntLE(offset + 110); + if (varPosBase20 < 0 || varPosBase20 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("ScreenEffectColors", varPosBase20, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos22); - obj.waterTints = new HashMap<>(waterTintsCount); - int dictPos = varPos22 + varIntLen; + int varPos20 = offset + 126 + varPosBase20; + int screenEffectColorsCount = VarInt.peek(buf, varPos20); + if (screenEffectColorsCount < 0) { + throw ProtocolException.invalidVarInt("ScreenEffectColors"); + } - for (int ixxxxxxxxxxxxxxxx = 0; ixxxxxxxxxxxxxxxx < waterTintsCount; ixxxxxxxxxxxxxxxx++) { - float keyx = buf.getFloatLE(dictPos); - dictPos += 4; - Color val = Color.deserialize(buf, dictPos); - dictPos += Color.computeBytesConsumed(buf, dictPos); - if (obj.waterTints.put(keyx, val) != null) { - throw ProtocolException.duplicateKey("waterTints", keyx); + int varIntLenxx = VarInt.size(screenEffectColorsCount); + if (screenEffectColorsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ScreenEffectColors", screenEffectColorsCount, 4096000); + } + + obj.screenEffectColors = new HashMap<>(screenEffectColorsCount); + int dictPos = varPos20 + varIntLenxx; + + for (int ixxxxxxxxxxxxxx = 0; ixxxxxxxxxxxxxx < screenEffectColorsCount; ixxxxxxxxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + ColorAlpha val = ColorAlpha.deserialize(buf, dictPos); + dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos); + if (obj.screenEffectColors.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("screenEffectColors", keyx); + } } } - } - if ((nullBits[3] & 2) != 0) { - int varPos23 = offset + 126 + buf.getIntLE(offset + 122); - obj.particle = WeatherParticle.deserialize(buf, varPos23); - } + if ((nullBits[2] & 128) != 0) { + int varPosBase21 = buf.getIntLE(offset + 114); + if (varPosBase21 < 0 || varPosBase21 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("ColorFilters", varPosBase21, buf.readableBytes()); + } - return obj; + int varPos21 = offset + 126 + varPosBase21; + int colorFiltersCount = VarInt.peek(buf, varPos21); + if (colorFiltersCount < 0) { + throw ProtocolException.invalidVarInt("ColorFilters"); + } + + int varIntLenxx = VarInt.size(colorFiltersCount); + if (colorFiltersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ColorFilters", colorFiltersCount, 4096000); + } + + obj.colorFilters = new HashMap<>(colorFiltersCount); + int dictPos = varPos21 + varIntLenxx; + + for (int ixxxxxxxxxxxxxxx = 0; ixxxxxxxxxxxxxxx < colorFiltersCount; ixxxxxxxxxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + Color val = Color.deserialize(buf, dictPos); + dictPos += Color.computeBytesConsumed(buf, dictPos); + if (obj.colorFilters.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("colorFilters", keyx); + } + } + } + + if ((nullBits[3] & 1) != 0) { + int varPosBase22 = buf.getIntLE(offset + 118); + if (varPosBase22 < 0 || varPosBase22 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("WaterTints", varPosBase22, buf.readableBytes()); + } + + int varPos22 = offset + 126 + varPosBase22; + int waterTintsCount = VarInt.peek(buf, varPos22); + if (waterTintsCount < 0) { + throw ProtocolException.invalidVarInt("WaterTints"); + } + + int varIntLenxx = VarInt.size(waterTintsCount); + if (waterTintsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("WaterTints", waterTintsCount, 4096000); + } + + obj.waterTints = new HashMap<>(waterTintsCount); + int dictPos = varPos22 + varIntLenxx; + + for (int ixxxxxxxxxxxxxxxx = 0; ixxxxxxxxxxxxxxxx < waterTintsCount; ixxxxxxxxxxxxxxxx++) { + float keyx = buf.getFloatLE(dictPos); + dictPos += 4; + Color val = Color.deserialize(buf, dictPos); + dictPos += Color.computeBytesConsumed(buf, dictPos); + if (obj.waterTints.put(keyx, val) != null) { + throw ProtocolException.duplicateKey("waterTints", keyx); + } + } + } + + if ((nullBits[3] & 2) != 0) { + int varPosBase23 = buf.getIntLE(offset + 122); + if (varPosBase23 < 0 || varPosBase23 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Particle", varPosBase23, buf.readableBytes()); + } + + int varPos23 = offset + 126 + varPosBase23; + obj.particle = WeatherParticle.deserialize(buf, varPos23); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -753,9 +896,13 @@ public class Weather { int maxEnd = 126; if ((nullBits[0] & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 30); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 126 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -763,9 +910,13 @@ public class Weather { if ((nullBits[0] & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 34); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("TagIndexes", fieldOffset1, maxEnd); + } + int pos1 = offset + 126 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 4; + pos1 += VarInt.size(arrLen) + arrLen * 4; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -773,9 +924,13 @@ public class Weather { if ((nullBits[0] & 16) != 0) { int fieldOffset2 = buf.getIntLE(offset + 38); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Stars", fieldOffset2, maxEnd); + } + int pos2 = offset + 126 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -783,14 +938,18 @@ public class Weather { if ((nullBits[0] & 32) != 0) { int fieldOffset3 = buf.getIntLE(offset + 42); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Moons", fieldOffset3, maxEnd); + } + int pos3 = offset + 126 + fieldOffset3; int dictLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos3 += 4; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; } if (pos3 - offset > maxEnd) { @@ -800,9 +959,13 @@ public class Weather { if ((nullBits[0] & 64) != 0) { int fieldOffset4 = buf.getIntLE(offset + 46); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Clouds", fieldOffset4, maxEnd); + } + int pos4 = offset + 126 + fieldOffset4; int arrLen = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4); + pos4 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos4 += Cloud.computeBytesConsumed(buf, pos4); @@ -815,9 +978,13 @@ public class Weather { if ((nullBits[0] & 128) != 0) { int fieldOffset5 = buf.getIntLE(offset + 50); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunlightDampingMultiplier", fieldOffset5, maxEnd); + } + int pos5 = offset + 126 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos5 += 4; @@ -831,9 +998,13 @@ public class Weather { if ((nullBits[1] & 1) != 0) { int fieldOffset6 = buf.getIntLE(offset + 54); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunlightColors", fieldOffset6, maxEnd); + } + int pos6 = offset + 126 + fieldOffset6; int dictLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos6 += 4; @@ -847,9 +1018,13 @@ public class Weather { if ((nullBits[1] & 2) != 0) { int fieldOffset7 = buf.getIntLE(offset + 58); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SkyTopColors", fieldOffset7, maxEnd); + } + int pos7 = offset + 126 + fieldOffset7; int dictLen = VarInt.peek(buf, pos7); - pos7 += VarInt.length(buf, pos7); + pos7 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos7 += 4; @@ -863,9 +1038,13 @@ public class Weather { if ((nullBits[1] & 4) != 0) { int fieldOffset8 = buf.getIntLE(offset + 62); + if (fieldOffset8 < 0 || fieldOffset8 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SkyBottomColors", fieldOffset8, maxEnd); + } + int pos8 = offset + 126 + fieldOffset8; int dictLen = VarInt.peek(buf, pos8); - pos8 += VarInt.length(buf, pos8); + pos8 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos8 += 4; @@ -879,9 +1058,13 @@ public class Weather { if ((nullBits[1] & 8) != 0) { int fieldOffset9 = buf.getIntLE(offset + 66); + if (fieldOffset9 < 0 || fieldOffset9 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SkySunsetColors", fieldOffset9, maxEnd); + } + int pos9 = offset + 126 + fieldOffset9; int dictLen = VarInt.peek(buf, pos9); - pos9 += VarInt.length(buf, pos9); + pos9 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos9 += 4; @@ -895,9 +1078,13 @@ public class Weather { if ((nullBits[1] & 16) != 0) { int fieldOffset10 = buf.getIntLE(offset + 70); + if (fieldOffset10 < 0 || fieldOffset10 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunColors", fieldOffset10, maxEnd); + } + int pos10 = offset + 126 + fieldOffset10; int dictLen = VarInt.peek(buf, pos10); - pos10 += VarInt.length(buf, pos10); + pos10 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos10 += 4; @@ -911,9 +1098,13 @@ public class Weather { if ((nullBits[1] & 32) != 0) { int fieldOffset11 = buf.getIntLE(offset + 74); + if (fieldOffset11 < 0 || fieldOffset11 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunScales", fieldOffset11, maxEnd); + } + int pos11 = offset + 126 + fieldOffset11; int dictLen = VarInt.peek(buf, pos11); - pos11 += VarInt.length(buf, pos11); + pos11 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos11 += 4; @@ -927,9 +1118,13 @@ public class Weather { if ((nullBits[1] & 64) != 0) { int fieldOffset12 = buf.getIntLE(offset + 78); + if (fieldOffset12 < 0 || fieldOffset12 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("SunGlowColors", fieldOffset12, maxEnd); + } + int pos12 = offset + 126 + fieldOffset12; int dictLen = VarInt.peek(buf, pos12); - pos12 += VarInt.length(buf, pos12); + pos12 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos12 += 4; @@ -943,9 +1138,13 @@ public class Weather { if ((nullBits[1] & 128) != 0) { int fieldOffset13 = buf.getIntLE(offset + 82); + if (fieldOffset13 < 0 || fieldOffset13 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("MoonColors", fieldOffset13, maxEnd); + } + int pos13 = offset + 126 + fieldOffset13; int dictLen = VarInt.peek(buf, pos13); - pos13 += VarInt.length(buf, pos13); + pos13 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos13 += 4; @@ -959,9 +1158,13 @@ public class Weather { if ((nullBits[2] & 1) != 0) { int fieldOffset14 = buf.getIntLE(offset + 86); + if (fieldOffset14 < 0 || fieldOffset14 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("MoonScales", fieldOffset14, maxEnd); + } + int pos14 = offset + 126 + fieldOffset14; int dictLen = VarInt.peek(buf, pos14); - pos14 += VarInt.length(buf, pos14); + pos14 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos14 += 4; @@ -975,9 +1178,13 @@ public class Weather { if ((nullBits[2] & 2) != 0) { int fieldOffset15 = buf.getIntLE(offset + 90); + if (fieldOffset15 < 0 || fieldOffset15 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("MoonGlowColors", fieldOffset15, maxEnd); + } + int pos15 = offset + 126 + fieldOffset15; int dictLen = VarInt.peek(buf, pos15); - pos15 += VarInt.length(buf, pos15); + pos15 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos15 += 4; @@ -991,9 +1198,13 @@ public class Weather { if ((nullBits[2] & 4) != 0) { int fieldOffset16 = buf.getIntLE(offset + 94); + if (fieldOffset16 < 0 || fieldOffset16 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("FogColors", fieldOffset16, maxEnd); + } + int pos16 = offset + 126 + fieldOffset16; int dictLen = VarInt.peek(buf, pos16); - pos16 += VarInt.length(buf, pos16); + pos16 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos16 += 4; @@ -1007,9 +1218,13 @@ public class Weather { if ((nullBits[2] & 8) != 0) { int fieldOffset17 = buf.getIntLE(offset + 98); + if (fieldOffset17 < 0 || fieldOffset17 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("FogHeightFalloffs", fieldOffset17, maxEnd); + } + int pos17 = offset + 126 + fieldOffset17; int dictLen = VarInt.peek(buf, pos17); - pos17 += VarInt.length(buf, pos17); + pos17 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos17 += 4; @@ -1023,9 +1238,13 @@ public class Weather { if ((nullBits[2] & 16) != 0) { int fieldOffset18 = buf.getIntLE(offset + 102); + if (fieldOffset18 < 0 || fieldOffset18 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("FogDensities", fieldOffset18, maxEnd); + } + int pos18 = offset + 126 + fieldOffset18; int dictLen = VarInt.peek(buf, pos18); - pos18 += VarInt.length(buf, pos18); + pos18 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos18 += 4; @@ -1039,9 +1258,13 @@ public class Weather { if ((nullBits[2] & 32) != 0) { int fieldOffset19 = buf.getIntLE(offset + 106); + if (fieldOffset19 < 0 || fieldOffset19 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("ScreenEffect", fieldOffset19, maxEnd); + } + int pos19 = offset + 126 + fieldOffset19; int sl = VarInt.peek(buf, pos19); - pos19 += VarInt.length(buf, pos19) + sl; + pos19 += VarInt.size(sl) + sl; if (pos19 - offset > maxEnd) { maxEnd = pos19 - offset; } @@ -1049,9 +1272,13 @@ public class Weather { if ((nullBits[2] & 64) != 0) { int fieldOffset20 = buf.getIntLE(offset + 110); + if (fieldOffset20 < 0 || fieldOffset20 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("ScreenEffectColors", fieldOffset20, maxEnd); + } + int pos20 = offset + 126 + fieldOffset20; int dictLen = VarInt.peek(buf, pos20); - pos20 += VarInt.length(buf, pos20); + pos20 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos20 += 4; @@ -1065,9 +1292,13 @@ public class Weather { if ((nullBits[2] & 128) != 0) { int fieldOffset21 = buf.getIntLE(offset + 114); + if (fieldOffset21 < 0 || fieldOffset21 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("ColorFilters", fieldOffset21, maxEnd); + } + int pos21 = offset + 126 + fieldOffset21; int dictLen = VarInt.peek(buf, pos21); - pos21 += VarInt.length(buf, pos21); + pos21 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos21 += 4; @@ -1081,9 +1312,13 @@ public class Weather { if ((nullBits[3] & 1) != 0) { int fieldOffset22 = buf.getIntLE(offset + 118); + if (fieldOffset22 < 0 || fieldOffset22 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("WaterTints", fieldOffset22, maxEnd); + } + int pos22 = offset + 126 + fieldOffset22; int dictLen = VarInt.peek(buf, pos22); - pos22 += VarInt.length(buf, pos22); + pos22 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos22 += 4; @@ -1097,6 +1332,10 @@ public class Weather { if ((nullBits[3] & 2) != 0) { int fieldOffset23 = buf.getIntLE(offset + 122); + if (fieldOffset23 < 0 || fieldOffset23 > buf.writerIndex() - offset - 126) { + throw ProtocolException.invalidOffset("Particle", fieldOffset23, maxEnd); + } + int pos23 = offset + 126 + fieldOffset23; pos23 += WeatherParticle.computeBytesConsumed(buf, pos23); if (pos23 - offset > maxEnd) { @@ -1743,15 +1982,11 @@ public class Weather { byte[] nullBits = PacketIO.readBytes(buffer, offset, 4); if ((nullBits[0] & 4) != 0) { int idOffset = buffer.getIntLE(offset + 30); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 126 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -1761,7 +1996,7 @@ public class Weather { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -1770,15 +2005,11 @@ public class Weather { if ((nullBits[0] & 8) != 0) { int tagIndexesOffset = buffer.getIntLE(offset + 34); - if (tagIndexesOffset < 0) { + if (tagIndexesOffset < 0 || tagIndexesOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for TagIndexes"); } int posx = offset + 126 + tagIndexesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TagIndexes"); - } - int tagIndexesCount = VarInt.peek(buffer, posx); if (tagIndexesCount < 0) { return ValidationResult.error("Invalid array count for TagIndexes"); @@ -1788,7 +2019,7 @@ public class Weather { return ValidationResult.error("TagIndexes exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(tagIndexesCount); posx += tagIndexesCount * 4; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading TagIndexes"); @@ -1797,15 +2028,11 @@ public class Weather { if ((nullBits[0] & 16) != 0) { int starsOffset = buffer.getIntLE(offset + 38); - if (starsOffset < 0) { + if (starsOffset < 0 || starsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for Stars"); } int posxx = offset + 126 + starsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Stars"); - } - int starsLen = VarInt.peek(buffer, posxx); if (starsLen < 0) { return ValidationResult.error("Invalid string length for Stars"); @@ -1815,7 +2042,7 @@ public class Weather { return ValidationResult.error("Stars exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(starsLen); posxx += starsLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Stars"); @@ -1824,15 +2051,11 @@ public class Weather { if ((nullBits[0] & 32) != 0) { int moonsOffset = buffer.getIntLE(offset + 42); - if (moonsOffset < 0) { + if (moonsOffset < 0 || moonsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for Moons"); } int posxxx = offset + 126 + moonsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Moons"); - } - int moonsCount = VarInt.peek(buffer, posxxx); if (moonsCount < 0) { return ValidationResult.error("Invalid dictionary count for Moons"); @@ -1842,7 +2065,7 @@ public class Weather { return ValidationResult.error("Moons exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(moonsCount); for (int i = 0; i < moonsCount; i++) { posxxx += 4; @@ -1859,7 +2082,7 @@ public class Weather { return ValidationResult.error("value exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(valueLen); posxxx += valueLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); @@ -1869,15 +2092,11 @@ public class Weather { if ((nullBits[0] & 64) != 0) { int cloudsOffset = buffer.getIntLE(offset + 46); - if (cloudsOffset < 0) { + if (cloudsOffset < 0 || cloudsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for Clouds"); } int posxxxx = offset + 126 + cloudsOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Clouds"); - } - int cloudsCount = VarInt.peek(buffer, posxxxx); if (cloudsCount < 0) { return ValidationResult.error("Invalid array count for Clouds"); @@ -1887,7 +2106,7 @@ public class Weather { return ValidationResult.error("Clouds exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(cloudsCount); for (int i = 0; i < cloudsCount; i++) { ValidationResult structResult = Cloud.validateStructure(buffer, posxxxx); @@ -1901,15 +2120,11 @@ public class Weather { if ((nullBits[0] & 128) != 0) { int sunlightDampingMultiplierOffset = buffer.getIntLE(offset + 50); - if (sunlightDampingMultiplierOffset < 0) { + if (sunlightDampingMultiplierOffset < 0 || sunlightDampingMultiplierOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for SunlightDampingMultiplier"); } int posxxxxx = offset + 126 + sunlightDampingMultiplierOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SunlightDampingMultiplier"); - } - int sunlightDampingMultiplierCount = VarInt.peek(buffer, posxxxxx); if (sunlightDampingMultiplierCount < 0) { return ValidationResult.error("Invalid dictionary count for SunlightDampingMultiplier"); @@ -1919,7 +2134,7 @@ public class Weather { return ValidationResult.error("SunlightDampingMultiplier exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); + posxxxxx += VarInt.size(sunlightDampingMultiplierCount); for (int i = 0; i < sunlightDampingMultiplierCount; i++) { posxxxxx += 4; @@ -1936,15 +2151,11 @@ public class Weather { if ((nullBits[1] & 1) != 0) { int sunlightColorsOffset = buffer.getIntLE(offset + 54); - if (sunlightColorsOffset < 0) { + if (sunlightColorsOffset < 0 || sunlightColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for SunlightColors"); } int posxxxxxx = offset + 126 + sunlightColorsOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SunlightColors"); - } - int sunlightColorsCount = VarInt.peek(buffer, posxxxxxx); if (sunlightColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for SunlightColors"); @@ -1954,7 +2165,7 @@ public class Weather { return ValidationResult.error("SunlightColors exceeds max length 4096000"); } - posxxxxxx += VarInt.length(buffer, posxxxxxx); + posxxxxxx += VarInt.size(sunlightColorsCount); for (int i = 0; i < sunlightColorsCount; i++) { posxxxxxx += 4; @@ -1968,15 +2179,11 @@ public class Weather { if ((nullBits[1] & 2) != 0) { int skyTopColorsOffset = buffer.getIntLE(offset + 58); - if (skyTopColorsOffset < 0) { + if (skyTopColorsOffset < 0 || skyTopColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for SkyTopColors"); } int posxxxxxxx = offset + 126 + skyTopColorsOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SkyTopColors"); - } - int skyTopColorsCount = VarInt.peek(buffer, posxxxxxxx); if (skyTopColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for SkyTopColors"); @@ -1986,7 +2193,7 @@ public class Weather { return ValidationResult.error("SkyTopColors exceeds max length 4096000"); } - posxxxxxxx += VarInt.length(buffer, posxxxxxxx); + posxxxxxxx += VarInt.size(skyTopColorsCount); for (int i = 0; i < skyTopColorsCount; i++) { posxxxxxxx += 4; @@ -2000,15 +2207,11 @@ public class Weather { if ((nullBits[1] & 4) != 0) { int skyBottomColorsOffset = buffer.getIntLE(offset + 62); - if (skyBottomColorsOffset < 0) { + if (skyBottomColorsOffset < 0 || skyBottomColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for SkyBottomColors"); } int posxxxxxxxx = offset + 126 + skyBottomColorsOffset; - if (posxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SkyBottomColors"); - } - int skyBottomColorsCount = VarInt.peek(buffer, posxxxxxxxx); if (skyBottomColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for SkyBottomColors"); @@ -2018,7 +2221,7 @@ public class Weather { return ValidationResult.error("SkyBottomColors exceeds max length 4096000"); } - posxxxxxxxx += VarInt.length(buffer, posxxxxxxxx); + posxxxxxxxx += VarInt.size(skyBottomColorsCount); for (int i = 0; i < skyBottomColorsCount; i++) { posxxxxxxxx += 4; @@ -2032,15 +2235,11 @@ public class Weather { if ((nullBits[1] & 8) != 0) { int skySunsetColorsOffset = buffer.getIntLE(offset + 66); - if (skySunsetColorsOffset < 0) { + if (skySunsetColorsOffset < 0 || skySunsetColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for SkySunsetColors"); } int posxxxxxxxxx = offset + 126 + skySunsetColorsOffset; - if (posxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SkySunsetColors"); - } - int skySunsetColorsCount = VarInt.peek(buffer, posxxxxxxxxx); if (skySunsetColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for SkySunsetColors"); @@ -2050,7 +2249,7 @@ public class Weather { return ValidationResult.error("SkySunsetColors exceeds max length 4096000"); } - posxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxx); + posxxxxxxxxx += VarInt.size(skySunsetColorsCount); for (int i = 0; i < skySunsetColorsCount; i++) { posxxxxxxxxx += 4; @@ -2064,15 +2263,11 @@ public class Weather { if ((nullBits[1] & 16) != 0) { int sunColorsOffset = buffer.getIntLE(offset + 70); - if (sunColorsOffset < 0) { + if (sunColorsOffset < 0 || sunColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for SunColors"); } int posxxxxxxxxxx = offset + 126 + sunColorsOffset; - if (posxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SunColors"); - } - int sunColorsCount = VarInt.peek(buffer, posxxxxxxxxxx); if (sunColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for SunColors"); @@ -2082,7 +2277,7 @@ public class Weather { return ValidationResult.error("SunColors exceeds max length 4096000"); } - posxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxx); + posxxxxxxxxxx += VarInt.size(sunColorsCount); for (int i = 0; i < sunColorsCount; i++) { posxxxxxxxxxx += 4; @@ -2096,15 +2291,11 @@ public class Weather { if ((nullBits[1] & 32) != 0) { int sunScalesOffset = buffer.getIntLE(offset + 74); - if (sunScalesOffset < 0) { + if (sunScalesOffset < 0 || sunScalesOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for SunScales"); } int posxxxxxxxxxxx = offset + 126 + sunScalesOffset; - if (posxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SunScales"); - } - int sunScalesCount = VarInt.peek(buffer, posxxxxxxxxxxx); if (sunScalesCount < 0) { return ValidationResult.error("Invalid dictionary count for SunScales"); @@ -2114,7 +2305,7 @@ public class Weather { return ValidationResult.error("SunScales exceeds max length 4096000"); } - posxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxx); + posxxxxxxxxxxx += VarInt.size(sunScalesCount); for (int i = 0; i < sunScalesCount; i++) { posxxxxxxxxxxx += 4; @@ -2131,15 +2322,11 @@ public class Weather { if ((nullBits[1] & 64) != 0) { int sunGlowColorsOffset = buffer.getIntLE(offset + 78); - if (sunGlowColorsOffset < 0) { + if (sunGlowColorsOffset < 0 || sunGlowColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for SunGlowColors"); } int posxxxxxxxxxxxx = offset + 126 + sunGlowColorsOffset; - if (posxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SunGlowColors"); - } - int sunGlowColorsCount = VarInt.peek(buffer, posxxxxxxxxxxxx); if (sunGlowColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for SunGlowColors"); @@ -2149,7 +2336,7 @@ public class Weather { return ValidationResult.error("SunGlowColors exceeds max length 4096000"); } - posxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxx); + posxxxxxxxxxxxx += VarInt.size(sunGlowColorsCount); for (int i = 0; i < sunGlowColorsCount; i++) { posxxxxxxxxxxxx += 4; @@ -2163,15 +2350,11 @@ public class Weather { if ((nullBits[1] & 128) != 0) { int moonColorsOffset = buffer.getIntLE(offset + 82); - if (moonColorsOffset < 0) { + if (moonColorsOffset < 0 || moonColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for MoonColors"); } int posxxxxxxxxxxxxx = offset + 126 + moonColorsOffset; - if (posxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MoonColors"); - } - int moonColorsCount = VarInt.peek(buffer, posxxxxxxxxxxxxx); if (moonColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for MoonColors"); @@ -2181,7 +2364,7 @@ public class Weather { return ValidationResult.error("MoonColors exceeds max length 4096000"); } - posxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxx); + posxxxxxxxxxxxxx += VarInt.size(moonColorsCount); for (int i = 0; i < moonColorsCount; i++) { posxxxxxxxxxxxxx += 4; @@ -2195,15 +2378,11 @@ public class Weather { if ((nullBits[2] & 1) != 0) { int moonScalesOffset = buffer.getIntLE(offset + 86); - if (moonScalesOffset < 0) { + if (moonScalesOffset < 0 || moonScalesOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for MoonScales"); } int posxxxxxxxxxxxxxx = offset + 126 + moonScalesOffset; - if (posxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MoonScales"); - } - int moonScalesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxx); if (moonScalesCount < 0) { return ValidationResult.error("Invalid dictionary count for MoonScales"); @@ -2213,7 +2392,7 @@ public class Weather { return ValidationResult.error("MoonScales exceeds max length 4096000"); } - posxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxx += VarInt.size(moonScalesCount); for (int i = 0; i < moonScalesCount; i++) { posxxxxxxxxxxxxxx += 4; @@ -2230,15 +2409,11 @@ public class Weather { if ((nullBits[2] & 2) != 0) { int moonGlowColorsOffset = buffer.getIntLE(offset + 90); - if (moonGlowColorsOffset < 0) { + if (moonGlowColorsOffset < 0 || moonGlowColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for MoonGlowColors"); } int posxxxxxxxxxxxxxxx = offset + 126 + moonGlowColorsOffset; - if (posxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MoonGlowColors"); - } - int moonGlowColorsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxx); if (moonGlowColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for MoonGlowColors"); @@ -2248,7 +2423,7 @@ public class Weather { return ValidationResult.error("MoonGlowColors exceeds max length 4096000"); } - posxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxx += VarInt.size(moonGlowColorsCount); for (int i = 0; i < moonGlowColorsCount; i++) { posxxxxxxxxxxxxxxx += 4; @@ -2262,15 +2437,11 @@ public class Weather { if ((nullBits[2] & 4) != 0) { int fogColorsOffset = buffer.getIntLE(offset + 94); - if (fogColorsOffset < 0) { + if (fogColorsOffset < 0 || fogColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for FogColors"); } int posxxxxxxxxxxxxxxxx = offset + 126 + fogColorsOffset; - if (posxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FogColors"); - } - int fogColorsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxx); if (fogColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for FogColors"); @@ -2280,7 +2451,7 @@ public class Weather { return ValidationResult.error("FogColors exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxx += VarInt.size(fogColorsCount); for (int i = 0; i < fogColorsCount; i++) { posxxxxxxxxxxxxxxxx += 4; @@ -2294,15 +2465,11 @@ public class Weather { if ((nullBits[2] & 8) != 0) { int fogHeightFalloffsOffset = buffer.getIntLE(offset + 98); - if (fogHeightFalloffsOffset < 0) { + if (fogHeightFalloffsOffset < 0 || fogHeightFalloffsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for FogHeightFalloffs"); } int posxxxxxxxxxxxxxxxxx = offset + 126 + fogHeightFalloffsOffset; - if (posxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FogHeightFalloffs"); - } - int fogHeightFalloffsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxx); if (fogHeightFalloffsCount < 0) { return ValidationResult.error("Invalid dictionary count for FogHeightFalloffs"); @@ -2312,7 +2479,7 @@ public class Weather { return ValidationResult.error("FogHeightFalloffs exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxx += VarInt.size(fogHeightFalloffsCount); for (int i = 0; i < fogHeightFalloffsCount; i++) { posxxxxxxxxxxxxxxxxx += 4; @@ -2329,15 +2496,11 @@ public class Weather { if ((nullBits[2] & 16) != 0) { int fogDensitiesOffset = buffer.getIntLE(offset + 102); - if (fogDensitiesOffset < 0) { + if (fogDensitiesOffset < 0 || fogDensitiesOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for FogDensities"); } int posxxxxxxxxxxxxxxxxxx = offset + 126 + fogDensitiesOffset; - if (posxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FogDensities"); - } - int fogDensitiesCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxx); if (fogDensitiesCount < 0) { return ValidationResult.error("Invalid dictionary count for FogDensities"); @@ -2347,7 +2510,7 @@ public class Weather { return ValidationResult.error("FogDensities exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxxx += VarInt.size(fogDensitiesCount); for (int i = 0; i < fogDensitiesCount; i++) { posxxxxxxxxxxxxxxxxxx += 4; @@ -2364,15 +2527,11 @@ public class Weather { if ((nullBits[2] & 32) != 0) { int screenEffectOffset = buffer.getIntLE(offset + 106); - if (screenEffectOffset < 0) { + if (screenEffectOffset < 0 || screenEffectOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for ScreenEffect"); } int posxxxxxxxxxxxxxxxxxxx = offset + 126 + screenEffectOffset; - if (posxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ScreenEffect"); - } - int screenEffectLen = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxx); if (screenEffectLen < 0) { return ValidationResult.error("Invalid string length for ScreenEffect"); @@ -2382,7 +2541,7 @@ public class Weather { return ValidationResult.error("ScreenEffect exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxxxx += VarInt.size(screenEffectLen); posxxxxxxxxxxxxxxxxxxx += screenEffectLen; if (posxxxxxxxxxxxxxxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ScreenEffect"); @@ -2391,15 +2550,11 @@ public class Weather { if ((nullBits[2] & 64) != 0) { int screenEffectColorsOffset = buffer.getIntLE(offset + 110); - if (screenEffectColorsOffset < 0) { + if (screenEffectColorsOffset < 0 || screenEffectColorsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for ScreenEffectColors"); } int posxxxxxxxxxxxxxxxxxxxx = offset + 126 + screenEffectColorsOffset; - if (posxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ScreenEffectColors"); - } - int screenEffectColorsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxx); if (screenEffectColorsCount < 0) { return ValidationResult.error("Invalid dictionary count for ScreenEffectColors"); @@ -2409,7 +2564,7 @@ public class Weather { return ValidationResult.error("ScreenEffectColors exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxxxxx += VarInt.size(screenEffectColorsCount); for (int i = 0; i < screenEffectColorsCount; i++) { posxxxxxxxxxxxxxxxxxxxx += 4; @@ -2423,15 +2578,11 @@ public class Weather { if ((nullBits[2] & 128) != 0) { int colorFiltersOffset = buffer.getIntLE(offset + 114); - if (colorFiltersOffset < 0) { + if (colorFiltersOffset < 0 || colorFiltersOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for ColorFilters"); } int posxxxxxxxxxxxxxxxxxxxxx = offset + 126 + colorFiltersOffset; - if (posxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ColorFilters"); - } - int colorFiltersCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxxx); if (colorFiltersCount < 0) { return ValidationResult.error("Invalid dictionary count for ColorFilters"); @@ -2441,7 +2592,7 @@ public class Weather { return ValidationResult.error("ColorFilters exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxxxxxx += VarInt.size(colorFiltersCount); for (int i = 0; i < colorFiltersCount; i++) { posxxxxxxxxxxxxxxxxxxxxx += 4; @@ -2455,15 +2606,11 @@ public class Weather { if ((nullBits[3] & 1) != 0) { int waterTintsOffset = buffer.getIntLE(offset + 118); - if (waterTintsOffset < 0) { + if (waterTintsOffset < 0 || waterTintsOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for WaterTints"); } int posxxxxxxxxxxxxxxxxxxxxxx = offset + 126 + waterTintsOffset; - if (posxxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for WaterTints"); - } - int waterTintsCount = VarInt.peek(buffer, posxxxxxxxxxxxxxxxxxxxxxx); if (waterTintsCount < 0) { return ValidationResult.error("Invalid dictionary count for WaterTints"); @@ -2473,7 +2620,7 @@ public class Weather { return ValidationResult.error("WaterTints exceeds max length 4096000"); } - posxxxxxxxxxxxxxxxxxxxxxx += VarInt.length(buffer, posxxxxxxxxxxxxxxxxxxxxxx); + posxxxxxxxxxxxxxxxxxxxxxx += VarInt.size(waterTintsCount); for (int i = 0; i < waterTintsCount; i++) { posxxxxxxxxxxxxxxxxxxxxxx += 4; @@ -2487,15 +2634,11 @@ public class Weather { if ((nullBits[3] & 2) != 0) { int particleOffset = buffer.getIntLE(offset + 122); - if (particleOffset < 0) { + if (particleOffset < 0 || particleOffset > buffer.writerIndex() - offset - 126) { return ValidationResult.error("Invalid offset for Particle"); } int posxxxxxxxxxxxxxxxxxxxxxxx = offset + 126 + particleOffset; - if (posxxxxxxxxxxxxxxxxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Particle"); - } - ValidationResult particleResult = WeatherParticle.validateStructure(buffer, posxxxxxxxxxxxxxxxxxxxxxxx); if (!particleResult.isValid()) { return ValidationResult.error("Invalid Particle: " + particleResult.error()); diff --git a/src/com/hypixel/hytale/protocol/WeatherParticle.java b/src/com/hypixel/hytale/protocol/WeatherParticle.java index 4ae56d4f..c245f0c6 100644 --- a/src/com/hypixel/hytale/protocol/WeatherParticle.java +++ b/src/com/hypixel/hytale/protocol/WeatherParticle.java @@ -44,32 +44,40 @@ public class WeatherParticle { @Nonnull public static WeatherParticle deserialize(@Nonnull ByteBuf buf, int offset) { - WeatherParticle obj = new WeatherParticle(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.color = Color.deserialize(buf, offset + 1); - } - - obj.scale = buf.getFloatLE(offset + 4); - obj.isOvergroundOnly = buf.getByte(offset + 8) != 0; - obj.positionOffsetMultiplier = buf.getFloatLE(offset + 9); - int pos = offset + 13; - if ((nullBits & 2) != 0) { - int systemIdLen = VarInt.peek(buf, pos); - if (systemIdLen < 0) { - throw ProtocolException.negativeLength("SystemId", systemIdLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("WeatherParticle", 13, buf.readableBytes() - offset); + } else { + WeatherParticle obj = new WeatherParticle(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.color = Color.deserialize(buf, offset + 1); } - if (systemIdLen > 4096000) { - throw ProtocolException.stringTooLong("SystemId", systemIdLen, 4096000); + obj.scale = buf.getFloatLE(offset + 4); + obj.isOvergroundOnly = buf.getByte(offset + 8) != 0; + obj.positionOffsetMultiplier = buf.getFloatLE(offset + 9); + int pos = offset + 13; + if ((nullBits & 2) != 0) { + int systemIdLen = VarInt.peek(buf, pos); + if (systemIdLen < 0) { + throw ProtocolException.invalidVarInt("SystemId"); + } + + int systemIdVarLen = VarInt.size(systemIdLen); + if (systemIdLen > 4096000) { + throw ProtocolException.stringTooLong("SystemId", systemIdLen, 4096000); + } + + if (pos + systemIdVarLen + systemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SystemId", pos + systemIdVarLen + systemIdLen, buf.readableBytes()); + } + + obj.systemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += systemIdVarLen + systemIdLen; } - int systemIdVarLen = VarInt.length(buf, pos); - obj.systemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += systemIdVarLen + systemIdLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -77,7 +85,7 @@ public class WeatherParticle { int pos = offset + 13; if ((nullBits & 2) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -133,7 +141,7 @@ public class WeatherParticle { return ValidationResult.error("SystemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(systemIdLen); pos += systemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SystemId"); diff --git a/src/com/hypixel/hytale/protocol/WieldingInteraction.java b/src/com/hypixel/hytale/protocol/WieldingInteraction.java index 74e745ef..2857efba 100644 --- a/src/com/hypixel/hytale/protocol/WieldingInteraction.java +++ b/src/com/hypixel/hytale/protocol/WieldingInteraction.java @@ -103,148 +103,192 @@ public class WieldingInteraction extends ChargingInteraction { @Nonnull public static WieldingInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - WieldingInteraction obj = new WieldingInteraction(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 2)); - obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 3); - obj.runTime = buf.getFloatLE(offset + 7); - obj.cancelOnItemChange = buf.getByte(offset + 11) != 0; - obj.failed = buf.getIntLE(offset + 12); - obj.allowIndefiniteHold = buf.getByte(offset + 16) != 0; - obj.displayProgress = buf.getByte(offset + 17) != 0; - obj.cancelOnOtherClick = buf.getByte(offset + 18) != 0; - obj.failOnDamage = buf.getByte(offset + 19) != 0; - obj.mouseSensitivityAdjustmentTarget = buf.getFloatLE(offset + 20); - obj.mouseSensitivityAdjustmentDuration = buf.getFloatLE(offset + 24); - if ((nullBits[0] & 1) != 0) { - obj.chargingDelay = ChargingDelay.deserialize(buf, offset + 28); - } - - obj.hasModifiers = buf.getByte(offset + 48) != 0; - if ((nullBits[0] & 2) != 0) { - obj.angledWielding = AngledWielding.deserialize(buf, offset + 49); - } - - if ((nullBits[0] & 4) != 0) { - int varPos0 = offset + 90 + buf.getIntLE(offset + 58); - obj.effects = InteractionEffects.deserialize(buf, varPos0); - } - - if ((nullBits[0] & 8) != 0) { - int varPos1 = offset + 90 + buf.getIntLE(offset + 62); - int settingsCount = VarInt.peek(buf, varPos1); - if (settingsCount < 0) { - throw ProtocolException.negativeLength("Settings", settingsCount); + if (buf.readableBytes() - offset < 90) { + throw ProtocolException.bufferTooSmall("WieldingInteraction", 90, buf.readableBytes() - offset); + } else { + WieldingInteraction obj = new WieldingInteraction(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 2)); + obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 3); + obj.runTime = buf.getFloatLE(offset + 7); + obj.cancelOnItemChange = buf.getByte(offset + 11) != 0; + obj.failed = buf.getIntLE(offset + 12); + obj.allowIndefiniteHold = buf.getByte(offset + 16) != 0; + obj.displayProgress = buf.getByte(offset + 17) != 0; + obj.cancelOnOtherClick = buf.getByte(offset + 18) != 0; + obj.failOnDamage = buf.getByte(offset + 19) != 0; + obj.mouseSensitivityAdjustmentTarget = buf.getFloatLE(offset + 20); + obj.mouseSensitivityAdjustmentDuration = buf.getFloatLE(offset + 24); + if ((nullBits[0] & 1) != 0) { + obj.chargingDelay = ChargingDelay.deserialize(buf, offset + 28); } - if (settingsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + obj.hasModifiers = buf.getByte(offset + 48) != 0; + if ((nullBits[0] & 2) != 0) { + obj.angledWielding = AngledWielding.deserialize(buf, offset + 49); } - int varIntLen = VarInt.length(buf, varPos1); - obj.settings = new HashMap<>(settingsCount); - int dictPos = varPos1 + varIntLen; + if ((nullBits[0] & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 58); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Effects", varPosBase0, buf.readableBytes()); + } - for (int i = 0; i < settingsCount; i++) { - GameMode key = GameMode.fromValue(buf.getByte(dictPos)); - InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); - dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); - if (obj.settings.put(key, val) != null) { - throw ProtocolException.duplicateKey("settings", key); + int varPos0 = offset + 90 + varPosBase0; + obj.effects = InteractionEffects.deserialize(buf, varPos0); + } + + if ((nullBits[0] & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 62); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Settings", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 90 + varPosBase1; + int settingsCount = VarInt.peek(buf, varPos1); + if (settingsCount < 0) { + throw ProtocolException.invalidVarInt("Settings"); + } + + int varIntLen = VarInt.size(settingsCount); + if (settingsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000); + } + + obj.settings = new HashMap<>(settingsCount); + int dictPos = varPos1 + varIntLen; + + for (int i = 0; i < settingsCount; i++) { + GameMode key = GameMode.fromValue(buf.getByte(dictPos)); + InteractionSettings val = InteractionSettings.deserialize(buf, ++dictPos); + dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos); + if (obj.settings.put(key, val) != null) { + throw ProtocolException.duplicateKey("settings", key); + } } } - } - if ((nullBits[0] & 16) != 0) { - int varPos2 = offset + 90 + buf.getIntLE(offset + 66); - obj.rules = InteractionRules.deserialize(buf, varPos2); - } + if ((nullBits[0] & 16) != 0) { + int varPosBase2 = buf.getIntLE(offset + 66); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Rules", varPosBase2, buf.readableBytes()); + } - if ((nullBits[0] & 32) != 0) { - int varPos3 = offset + 90 + buf.getIntLE(offset + 70); - int tagsCount = VarInt.peek(buf, varPos3); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); + int varPos2 = offset + 90 + varPosBase2; + obj.rules = InteractionRules.deserialize(buf, varPos2); } - if (tagsCount > 4096000) { - throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); - } + if ((nullBits[0] & 32) != 0) { + int varPosBase3 = buf.getIntLE(offset + 70); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Tags", varPosBase3, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); - } + int varPos3 = offset + 90 + varPosBase3; + int tagsCount = VarInt.peek(buf, varPos3); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); + } - obj.tags = new int[tagsCount]; + int varIntLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000); + } - for (int ix = 0; ix < tagsCount; ix++) { - obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); - } - } + if (varPos3 + varIntLen + tagsCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tags", varPos3 + varIntLen + tagsCount * 4, buf.readableBytes()); + } - if ((nullBits[0] & 64) != 0) { - int varPos4 = offset + 90 + buf.getIntLE(offset + 74); - obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); - } + obj.tags = new int[tagsCount]; - if ((nullBits[0] & 128) != 0) { - int varPos5 = offset + 90 + buf.getIntLE(offset + 78); - int chargedNextCount = VarInt.peek(buf, varPos5); - if (chargedNextCount < 0) { - throw ProtocolException.negativeLength("ChargedNext", chargedNextCount); - } - - if (chargedNextCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ChargedNext", chargedNextCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - obj.chargedNext = new HashMap<>(chargedNextCount); - int dictPos = varPos5 + varIntLen; - - for (int ix = 0; ix < chargedNextCount; ix++) { - float key = buf.getFloatLE(dictPos); - dictPos += 4; - int val = buf.getIntLE(dictPos); - dictPos += 4; - if (obj.chargedNext.put(key, val) != null) { - throw ProtocolException.duplicateKey("chargedNext", key); + for (int ix = 0; ix < tagsCount; ix++) { + obj.tags[ix] = buf.getIntLE(varPos3 + varIntLen + ix * 4); } } - } - if ((nullBits[1] & 1) != 0) { - int varPos6 = offset + 90 + buf.getIntLE(offset + 82); - int forksCount = VarInt.peek(buf, varPos6); - if (forksCount < 0) { - throw ProtocolException.negativeLength("Forks", forksCount); + if ((nullBits[0] & 64) != 0) { + int varPosBase4 = buf.getIntLE(offset + 74); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Camera", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 90 + varPosBase4; + obj.camera = InteractionCameraSettings.deserialize(buf, varPos4); } - if (forksCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Forks", forksCount, 4096000); - } + if ((nullBits[0] & 128) != 0) { + int varPosBase5 = buf.getIntLE(offset + 78); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("ChargedNext", varPosBase5, buf.readableBytes()); + } - int varIntLen = VarInt.length(buf, varPos6); - obj.forks = new HashMap<>(forksCount); - int dictPos = varPos6 + varIntLen; + int varPos5 = offset + 90 + varPosBase5; + int chargedNextCount = VarInt.peek(buf, varPos5); + if (chargedNextCount < 0) { + throw ProtocolException.invalidVarInt("ChargedNext"); + } - for (int ixx = 0; ixx < forksCount; ixx++) { - InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); - int val = buf.getIntLE(++dictPos); - dictPos += 4; - if (obj.forks.put(key, val) != null) { - throw ProtocolException.duplicateKey("forks", key); + int varIntLenx = VarInt.size(chargedNextCount); + if (chargedNextCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ChargedNext", chargedNextCount, 4096000); + } + + obj.chargedNext = new HashMap<>(chargedNextCount); + int dictPos = varPos5 + varIntLenx; + + for (int ix = 0; ix < chargedNextCount; ix++) { + float key = buf.getFloatLE(dictPos); + dictPos += 4; + int val = buf.getIntLE(dictPos); + dictPos += 4; + if (obj.chargedNext.put(key, val) != null) { + throw ProtocolException.duplicateKey("chargedNext", key); + } } } - } - if ((nullBits[1] & 2) != 0) { - int varPos7 = offset + 90 + buf.getIntLE(offset + 86); - obj.blockedEffects = DamageEffects.deserialize(buf, varPos7); - } + if ((nullBits[1] & 1) != 0) { + int varPosBase6 = buf.getIntLE(offset + 82); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Forks", varPosBase6, buf.readableBytes()); + } - return obj; + int varPos6 = offset + 90 + varPosBase6; + int forksCount = VarInt.peek(buf, varPos6); + if (forksCount < 0) { + throw ProtocolException.invalidVarInt("Forks"); + } + + int varIntLenx = VarInt.size(forksCount); + if (forksCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Forks", forksCount, 4096000); + } + + obj.forks = new HashMap<>(forksCount); + int dictPos = varPos6 + varIntLenx; + + for (int ixx = 0; ixx < forksCount; ixx++) { + InteractionType key = InteractionType.fromValue(buf.getByte(dictPos)); + int val = buf.getIntLE(++dictPos); + dictPos += 4; + if (obj.forks.put(key, val) != null) { + throw ProtocolException.duplicateKey("forks", key); + } + } + } + + if ((nullBits[1] & 2) != 0) { + int varPosBase7 = buf.getIntLE(offset + 86); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("BlockedEffects", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 90 + varPosBase7; + obj.blockedEffects = DamageEffects.deserialize(buf, varPos7); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -252,6 +296,10 @@ public class WieldingInteraction extends ChargingInteraction { int maxEnd = 90; if ((nullBits[0] & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 58); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Effects", fieldOffset0, maxEnd); + } + int pos0 = offset + 90 + fieldOffset0; pos0 += InteractionEffects.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -261,9 +309,13 @@ public class WieldingInteraction extends ChargingInteraction { if ((nullBits[0] & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 62); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Settings", fieldOffset1, maxEnd); + } + int pos1 = offset + 90 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 = ++pos1 + InteractionSettings.computeBytesConsumed(buf, pos1); @@ -276,6 +328,10 @@ public class WieldingInteraction extends ChargingInteraction { if ((nullBits[0] & 16) != 0) { int fieldOffset2 = buf.getIntLE(offset + 66); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Rules", fieldOffset2, maxEnd); + } + int pos2 = offset + 90 + fieldOffset2; pos2 += InteractionRules.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -285,9 +341,13 @@ public class WieldingInteraction extends ChargingInteraction { if ((nullBits[0] & 32) != 0) { int fieldOffset3 = buf.getIntLE(offset + 70); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Tags", fieldOffset3, maxEnd); + } + int pos3 = offset + 90 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + arrLen * 4; + pos3 += VarInt.size(arrLen) + arrLen * 4; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -295,6 +355,10 @@ public class WieldingInteraction extends ChargingInteraction { if ((nullBits[0] & 64) != 0) { int fieldOffset4 = buf.getIntLE(offset + 74); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Camera", fieldOffset4, maxEnd); + } + int pos4 = offset + 90 + fieldOffset4; pos4 += InteractionCameraSettings.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -304,9 +368,13 @@ public class WieldingInteraction extends ChargingInteraction { if ((nullBits[0] & 128) != 0) { int fieldOffset5 = buf.getIntLE(offset + 78); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("ChargedNext", fieldOffset5, maxEnd); + } + int pos5 = offset + 90 + fieldOffset5; int dictLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos5 += 4; @@ -320,9 +388,13 @@ public class WieldingInteraction extends ChargingInteraction { if ((nullBits[1] & 1) != 0) { int fieldOffset6 = buf.getIntLE(offset + 82); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("Forks", fieldOffset6, maxEnd); + } + int pos6 = offset + 90 + fieldOffset6; int dictLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos6 = ++pos6 + 4; @@ -335,6 +407,10 @@ public class WieldingInteraction extends ChargingInteraction { if ((nullBits[1] & 2) != 0) { int fieldOffset7 = buf.getIntLE(offset + 86); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 90) { + throw ProtocolException.invalidOffset("BlockedEffects", fieldOffset7, maxEnd); + } + int pos7 = offset + 90 + fieldOffset7; pos7 += DamageEffects.computeBytesConsumed(buf, pos7); if (pos7 - offset > maxEnd) { @@ -568,203 +644,186 @@ public class WieldingInteraction extends ChargingInteraction { return ValidationResult.error("Buffer too small: expected at least 90 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[0] & 4) != 0) { - int effectsOffset = buffer.getIntLE(offset + 58); - if (effectsOffset < 0) { - return ValidationResult.error("Invalid offset for Effects"); - } - - int pos = offset + 90 + effectsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Effects"); - } - - ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); - if (!effectsResult.isValid()) { - return ValidationResult.error("Invalid Effects: " + effectsResult.error()); - } - - pos += InteractionEffects.computeBytesConsumed(buffer, pos); - } - - if ((nullBits[0] & 8) != 0) { - int settingsOffset = buffer.getIntLE(offset + 62); - if (settingsOffset < 0) { - return ValidationResult.error("Invalid offset for Settings"); - } - - int posx = offset + 90 + settingsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Settings"); - } - - int settingsCount = VarInt.peek(buffer, posx); - if (settingsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Settings"); - } - - if (settingsCount > 4096000) { - return ValidationResult.error("Settings exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < settingsCount; i++) { - posx++; - posx++; - } - } - - if ((nullBits[0] & 16) != 0) { - int rulesOffset = buffer.getIntLE(offset + 66); - if (rulesOffset < 0) { - return ValidationResult.error("Invalid offset for Rules"); - } - - int posxx = offset + 90 + rulesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Rules"); - } - - ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posxx); - if (!rulesResult.isValid()) { - return ValidationResult.error("Invalid Rules: " + rulesResult.error()); - } - - posxx += InteractionRules.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits[0] & 32) != 0) { - int tagsOffset = buffer.getIntLE(offset + 70); - if (tagsOffset < 0) { - return ValidationResult.error("Invalid offset for Tags"); - } - - int posxxx = offset + 90 + tagsOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tags"); - } - - int tagsCount = VarInt.peek(buffer, posxxx); - if (tagsCount < 0) { - return ValidationResult.error("Invalid array count for Tags"); - } - - if (tagsCount > 4096000) { - return ValidationResult.error("Tags exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += tagsCount * 4; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Tags"); - } - } - - if ((nullBits[0] & 64) != 0) { - int cameraOffset = buffer.getIntLE(offset + 74); - if (cameraOffset < 0) { - return ValidationResult.error("Invalid offset for Camera"); - } - - int posxxxx = offset + 90 + cameraOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Camera"); - } - - ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxxxx); - if (!cameraResult.isValid()) { - return ValidationResult.error("Invalid Camera: " + cameraResult.error()); - } - - posxxxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits[0] & 128) != 0) { - int chargedNextOffset = buffer.getIntLE(offset + 78); - if (chargedNextOffset < 0) { - return ValidationResult.error("Invalid offset for ChargedNext"); - } - - int posxxxxx = offset + 90 + chargedNextOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ChargedNext"); - } - - int chargedNextCount = VarInt.peek(buffer, posxxxxx); - if (chargedNextCount < 0) { - return ValidationResult.error("Invalid dictionary count for ChargedNext"); - } - - if (chargedNextCount > 4096000) { - return ValidationResult.error("ChargedNext exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < chargedNextCount; i++) { - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 2) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid WaitForDataFrom value for WaitForDataFrom"); + } else { + if ((nullBits[0] & 4) != 0) { + v = buffer.getIntLE(offset + 58); + if (v < 0 || v > buffer.writerIndex() - offset - 90) { + return ValidationResult.error("Invalid offset for Effects"); } - posxxxxx += 4; - if (posxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + int pos = offset + 90 + v; + ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos); + if (!effectsResult.isValid()) { + return ValidationResult.error("Invalid Effects: " + effectsResult.error()); + } + + pos += InteractionEffects.computeBytesConsumed(buffer, pos); + } + + if ((nullBits[0] & 8) != 0) { + v = buffer.getIntLE(offset + 62); + if (v < 0 || v > buffer.writerIndex() - offset - 90) { + return ValidationResult.error("Invalid offset for Settings"); + } + + int pos = offset + 90 + v; + int settingsCount = VarInt.peek(buffer, pos); + if (settingsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Settings"); + } + + if (settingsCount > 4096000) { + return ValidationResult.error("Settings exceeds max length 4096000"); + } + + pos += VarInt.size(settingsCount); + + for (int i = 0; i < settingsCount; i++) { + int vx = buffer.getByte(pos) & 255; + if (vx >= 2) { + return ValidationResult.error("Invalid GameMode value for key"); + } + + pos++; + pos++; } } - } - if ((nullBits[1] & 1) != 0) { - int forksOffset = buffer.getIntLE(offset + 82); - if (forksOffset < 0) { - return ValidationResult.error("Invalid offset for Forks"); + if ((nullBits[0] & 16) != 0) { + v = buffer.getIntLE(offset + 66); + if (v < 0 || v > buffer.writerIndex() - offset - 90) { + return ValidationResult.error("Invalid offset for Rules"); + } + + int posx = offset + 90 + v; + ValidationResult rulesResult = InteractionRules.validateStructure(buffer, posx); + if (!rulesResult.isValid()) { + return ValidationResult.error("Invalid Rules: " + rulesResult.error()); + } + + posx += InteractionRules.computeBytesConsumed(buffer, posx); } - int posxxxxxx = offset + 90 + forksOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Forks"); - } + if ((nullBits[0] & 32) != 0) { + v = buffer.getIntLE(offset + 70); + if (v < 0 || v > buffer.writerIndex() - offset - 90) { + return ValidationResult.error("Invalid offset for Tags"); + } - int forksCount = VarInt.peek(buffer, posxxxxxx); - if (forksCount < 0) { - return ValidationResult.error("Invalid dictionary count for Forks"); - } + int posx = offset + 90 + v; + int tagsCount = VarInt.peek(buffer, posx); + if (tagsCount < 0) { + return ValidationResult.error("Invalid array count for Tags"); + } - if (forksCount > 4096000) { - return ValidationResult.error("Forks exceeds max length 4096000"); - } + if (tagsCount > 4096000) { + return ValidationResult.error("Tags exceeds max length 4096000"); + } - posxxxxxx += VarInt.length(buffer, posxxxxxx); - - for (int i = 0; i < forksCount; i++) { - posxxxxxx = ++posxxxxxx + 4; - if (posxxxxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + posx += VarInt.size(tagsCount); + posx += tagsCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Tags"); } } + + if ((nullBits[0] & 64) != 0) { + v = buffer.getIntLE(offset + 74); + if (v < 0 || v > buffer.writerIndex() - offset - 90) { + return ValidationResult.error("Invalid offset for Camera"); + } + + int posxx = offset + 90 + v; + ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, posxx); + if (!cameraResult.isValid()) { + return ValidationResult.error("Invalid Camera: " + cameraResult.error()); + } + + posxx += InteractionCameraSettings.computeBytesConsumed(buffer, posxx); + } + + if ((nullBits[0] & 128) != 0) { + v = buffer.getIntLE(offset + 78); + if (v < 0 || v > buffer.writerIndex() - offset - 90) { + return ValidationResult.error("Invalid offset for ChargedNext"); + } + + int posxx = offset + 90 + v; + int chargedNextCount = VarInt.peek(buffer, posxx); + if (chargedNextCount < 0) { + return ValidationResult.error("Invalid dictionary count for ChargedNext"); + } + + if (chargedNextCount > 4096000) { + return ValidationResult.error("ChargedNext exceeds max length 4096000"); + } + + posxx += VarInt.size(chargedNextCount); + + for (int i = 0; i < chargedNextCount; i++) { + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + posxx += 4; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 82); + if (v < 0 || v > buffer.writerIndex() - offset - 90) { + return ValidationResult.error("Invalid offset for Forks"); + } + + int posxxx = offset + 90 + v; + int forksCount = VarInt.peek(buffer, posxxx); + if (forksCount < 0) { + return ValidationResult.error("Invalid dictionary count for Forks"); + } + + if (forksCount > 4096000) { + return ValidationResult.error("Forks exceeds max length 4096000"); + } + + posxxx += VarInt.size(forksCount); + + for (int i = 0; i < forksCount; i++) { + int vx = buffer.getByte(posxxx) & 255; + if (vx >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); + } + + posxxx = ++posxxx + 4; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } + } + } + + if ((nullBits[1] & 2) != 0) { + v = buffer.getIntLE(offset + 86); + if (v < 0 || v > buffer.writerIndex() - offset - 90) { + return ValidationResult.error("Invalid offset for BlockedEffects"); + } + + int posxxxx = offset + 90 + v; + ValidationResult blockedEffectsResult = DamageEffects.validateStructure(buffer, posxxxx); + if (!blockedEffectsResult.isValid()) { + return ValidationResult.error("Invalid BlockedEffects: " + blockedEffectsResult.error()); + } + + posxxxx += DamageEffects.computeBytesConsumed(buffer, posxxxx); + } + + return ValidationResult.OK; } - - if ((nullBits[1] & 2) != 0) { - int blockedEffectsOffset = buffer.getIntLE(offset + 86); - if (blockedEffectsOffset < 0) { - return ValidationResult.error("Invalid offset for BlockedEffects"); - } - - int posxxxxxxx = offset + 90 + blockedEffectsOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockedEffects"); - } - - ValidationResult blockedEffectsResult = DamageEffects.validateStructure(buffer, posxxxxxxx); - if (!blockedEffectsResult.isValid()) { - return ValidationResult.error("Invalid BlockedEffects: " + blockedEffectsResult.error()); - } - - posxxxxxxx += DamageEffects.computeBytesConsumed(buffer, posxxxxxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/WiggleWeights.java b/src/com/hypixel/hytale/protocol/WiggleWeights.java index 2a57a55f..430d60fd 100644 --- a/src/com/hypixel/hytale/protocol/WiggleWeights.java +++ b/src/com/hypixel/hytale/protocol/WiggleWeights.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -64,18 +65,22 @@ public class WiggleWeights { @Nonnull public static WiggleWeights deserialize(@Nonnull ByteBuf buf, int offset) { - WiggleWeights obj = new WiggleWeights(); - obj.x = buf.getFloatLE(offset + 0); - obj.xDeceleration = buf.getFloatLE(offset + 4); - obj.y = buf.getFloatLE(offset + 8); - obj.yDeceleration = buf.getFloatLE(offset + 12); - obj.z = buf.getFloatLE(offset + 16); - obj.zDeceleration = buf.getFloatLE(offset + 20); - obj.roll = buf.getFloatLE(offset + 24); - obj.rollDeceleration = buf.getFloatLE(offset + 28); - obj.pitch = buf.getFloatLE(offset + 32); - obj.pitchDeceleration = buf.getFloatLE(offset + 36); - return obj; + if (buf.readableBytes() - offset < 40) { + throw ProtocolException.bufferTooSmall("WiggleWeights", 40, buf.readableBytes() - offset); + } else { + WiggleWeights obj = new WiggleWeights(); + obj.x = buf.getFloatLE(offset + 0); + obj.xDeceleration = buf.getFloatLE(offset + 4); + obj.y = buf.getFloatLE(offset + 8); + obj.yDeceleration = buf.getFloatLE(offset + 12); + obj.z = buf.getFloatLE(offset + 16); + obj.zDeceleration = buf.getFloatLE(offset + 20); + obj.roll = buf.getFloatLE(offset + 24); + obj.rollDeceleration = buf.getFloatLE(offset + 28); + obj.pitch = buf.getFloatLE(offset + 32); + obj.pitchDeceleration = buf.getFloatLE(offset + 36); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/WorldEnvironment.java b/src/com/hypixel/hytale/protocol/WorldEnvironment.java index e3bda5ae..e5e1137a 100644 --- a/src/com/hypixel/hytale/protocol/WorldEnvironment.java +++ b/src/com/hypixel/hytale/protocol/WorldEnvironment.java @@ -47,76 +47,100 @@ public class WorldEnvironment { @Nonnull public static WorldEnvironment deserialize(@Nonnull ByteBuf buf, int offset) { - WorldEnvironment obj = new WorldEnvironment(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.waterTint = Color.deserialize(buf, offset + 1); - } - - if ((nullBits & 2) != 0) { - int varPos0 = offset + 16 + buf.getIntLE(offset + 4); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("WorldEnvironment", 16, buf.readableBytes() - offset); + } else { + WorldEnvironment obj = new WorldEnvironment(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.waterTint = Color.deserialize(buf, offset + 1); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 16 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 8); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("FluidParticles", varPosBase1, buf.readableBytes()); + } - if ((nullBits & 4) != 0) { - int varPos1 = offset + 16 + buf.getIntLE(offset + 8); - int fluidParticlesCount = VarInt.peek(buf, varPos1); - if (fluidParticlesCount < 0) { - throw ProtocolException.negativeLength("FluidParticles", fluidParticlesCount); - } + int varPos1 = offset + 16 + varPosBase1; + int fluidParticlesCount = VarInt.peek(buf, varPos1); + if (fluidParticlesCount < 0) { + throw ProtocolException.invalidVarInt("FluidParticles"); + } - if (fluidParticlesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("FluidParticles", fluidParticlesCount, 4096000); - } + int varIntLen = VarInt.size(fluidParticlesCount); + if (fluidParticlesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("FluidParticles", fluidParticlesCount, 4096000); + } - int varIntLen = VarInt.length(buf, varPos1); - obj.fluidParticles = new HashMap<>(fluidParticlesCount); - int dictPos = varPos1 + varIntLen; + obj.fluidParticles = new HashMap<>(fluidParticlesCount); + int dictPos = varPos1 + varIntLen; - for (int i = 0; i < fluidParticlesCount; i++) { - int key = buf.getIntLE(dictPos); - dictPos += 4; - FluidParticle val = FluidParticle.deserialize(buf, dictPos); - dictPos += FluidParticle.computeBytesConsumed(buf, dictPos); - if (obj.fluidParticles.put(key, val) != null) { - throw ProtocolException.duplicateKey("fluidParticles", key); + for (int i = 0; i < fluidParticlesCount; i++) { + int key = buf.getIntLE(dictPos); + dictPos += 4; + FluidParticle val = FluidParticle.deserialize(buf, dictPos); + dictPos += FluidParticle.computeBytesConsumed(buf, dictPos); + if (obj.fluidParticles.put(key, val) != null) { + throw ProtocolException.duplicateKey("fluidParticles", key); + } } } + + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 12); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("TagIndexes", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 16 + varPosBase2; + int tagIndexesCount = VarInt.peek(buf, varPos2); + if (tagIndexesCount < 0) { + throw ProtocolException.invalidVarInt("TagIndexes"); + } + + int varIntLen = VarInt.size(tagIndexesCount); + if (tagIndexesCount > 4096000) { + throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); + } + + if (varPos2 + varIntLen + tagIndexesCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TagIndexes", varPos2 + varIntLen + tagIndexesCount * 4, buf.readableBytes()); + } + + obj.tagIndexes = new int[tagIndexesCount]; + + for (int ix = 0; ix < tagIndexesCount; ix++) { + obj.tagIndexes[ix] = buf.getIntLE(varPos2 + varIntLen + ix * 4); + } + } + + return obj; } - - if ((nullBits & 8) != 0) { - int varPos2 = offset + 16 + buf.getIntLE(offset + 12); - int tagIndexesCount = VarInt.peek(buf, varPos2); - if (tagIndexesCount < 0) { - throw ProtocolException.negativeLength("TagIndexes", tagIndexesCount); - } - - if (tagIndexesCount > 4096000) { - throw ProtocolException.arrayTooLong("TagIndexes", tagIndexesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + tagIndexesCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("TagIndexes", varPos2 + varIntLen + tagIndexesCount * 4, buf.readableBytes()); - } - - obj.tagIndexes = new int[tagIndexesCount]; - - for (int ix = 0; ix < tagIndexesCount; ix++) { - obj.tagIndexes[ix] = buf.getIntLE(varPos2 + varIntLen + ix * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -124,9 +148,13 @@ public class WorldEnvironment { int maxEnd = 16; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 16 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -134,9 +162,13 @@ public class WorldEnvironment { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 8); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("FluidParticles", fieldOffset1, maxEnd); + } + int pos1 = offset + 16 + fieldOffset1; int dictLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos1 += 4; @@ -150,9 +182,13 @@ public class WorldEnvironment { if ((nullBits & 8) != 0) { int fieldOffset2 = buf.getIntLE(offset + 12); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("TagIndexes", fieldOffset2, maxEnd); + } + int pos2 = offset + 16 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + arrLen * 4; + pos2 += VarInt.size(arrLen) + arrLen * 4; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -263,15 +299,11 @@ public class WorldEnvironment { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { int idOffset = buffer.getIntLE(offset + 4); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 16) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 16 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -281,7 +313,7 @@ public class WorldEnvironment { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -290,15 +322,11 @@ public class WorldEnvironment { if ((nullBits & 4) != 0) { int fluidParticlesOffset = buffer.getIntLE(offset + 8); - if (fluidParticlesOffset < 0) { + if (fluidParticlesOffset < 0 || fluidParticlesOffset > buffer.writerIndex() - offset - 16) { return ValidationResult.error("Invalid offset for FluidParticles"); } int posx = offset + 16 + fluidParticlesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FluidParticles"); - } - int fluidParticlesCount = VarInt.peek(buffer, posx); if (fluidParticlesCount < 0) { return ValidationResult.error("Invalid dictionary count for FluidParticles"); @@ -308,7 +336,7 @@ public class WorldEnvironment { return ValidationResult.error("FluidParticles exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(fluidParticlesCount); for (int i = 0; i < fluidParticlesCount; i++) { posx += 4; @@ -322,15 +350,11 @@ public class WorldEnvironment { if ((nullBits & 8) != 0) { int tagIndexesOffset = buffer.getIntLE(offset + 12); - if (tagIndexesOffset < 0) { + if (tagIndexesOffset < 0 || tagIndexesOffset > buffer.writerIndex() - offset - 16) { return ValidationResult.error("Invalid offset for TagIndexes"); } int posxx = offset + 16 + tagIndexesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for TagIndexes"); - } - int tagIndexesCount = VarInt.peek(buffer, posxx); if (tagIndexesCount < 0) { return ValidationResult.error("Invalid array count for TagIndexes"); @@ -340,7 +364,7 @@ public class WorldEnvironment { return ValidationResult.error("TagIndexes exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(tagIndexesCount); posxx += tagIndexesCount * 4; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading TagIndexes"); diff --git a/src/com/hypixel/hytale/protocol/WorldInteraction.java b/src/com/hypixel/hytale/protocol/WorldInteraction.java index 1bf9a3f8..ddbff708 100644 --- a/src/com/hypixel/hytale/protocol/WorldInteraction.java +++ b/src/com/hypixel/hytale/protocol/WorldInteraction.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -35,18 +36,22 @@ public class WorldInteraction { @Nonnull public static WorldInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - WorldInteraction obj = new WorldInteraction(); - byte nullBits = buf.getByte(offset); - obj.entityId = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.blockPosition = BlockPosition.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 20) { + throw ProtocolException.bufferTooSmall("WorldInteraction", 20, buf.readableBytes() - offset); + } else { + WorldInteraction obj = new WorldInteraction(); + byte nullBits = buf.getByte(offset); + obj.entityId = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.blockPosition = BlockPosition.deserialize(buf, offset + 5); + } - if ((nullBits & 2) != 0) { - obj.blockRotation = BlockRotation.deserialize(buf, offset + 17); - } + if ((nullBits & 2) != 0) { + obj.blockRotation = BlockRotation.deserialize(buf, offset + 17); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -83,7 +88,12 @@ public class WorldInteraction { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 20 ? ValidationResult.error("Buffer too small: expected at least 20 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 20) { + return ValidationResult.error("Buffer too small: expected at least 20 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public WorldInteraction clone() { diff --git a/src/com/hypixel/hytale/protocol/WorldParticle.java b/src/com/hypixel/hytale/protocol/WorldParticle.java index bad44ca4..a4722b3b 100644 --- a/src/com/hypixel/hytale/protocol/WorldParticle.java +++ b/src/com/hypixel/hytale/protocol/WorldParticle.java @@ -8,6 +8,7 @@ import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class WorldParticle { public static final int NULLABLE_BIT_FIELD_SIZE = 1; @@ -21,14 +22,14 @@ public class WorldParticle { @Nullable public Color color; @Nullable - public Vector3f positionOffset; + public Vector3fc positionOffset; @Nullable public Direction rotationOffset; public WorldParticle() { } - public WorldParticle(@Nullable String systemId, float scale, @Nullable Color color, @Nullable Vector3f positionOffset, @Nullable Direction rotationOffset) { + public WorldParticle(@Nullable String systemId, float scale, @Nullable Color color, @Nullable Vector3fc positionOffset, @Nullable Direction rotationOffset) { this.systemId = systemId; this.scale = scale; this.color = color; @@ -46,38 +47,46 @@ public class WorldParticle { @Nonnull public static WorldParticle deserialize(@Nonnull ByteBuf buf, int offset) { - WorldParticle obj = new WorldParticle(); - byte nullBits = buf.getByte(offset); - obj.scale = buf.getFloatLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.color = Color.deserialize(buf, offset + 5); - } - - if ((nullBits & 2) != 0) { - obj.positionOffset = Vector3f.deserialize(buf, offset + 8); - } - - if ((nullBits & 4) != 0) { - obj.rotationOffset = Direction.deserialize(buf, offset + 20); - } - - int pos = offset + 32; - if ((nullBits & 8) != 0) { - int systemIdLen = VarInt.peek(buf, pos); - if (systemIdLen < 0) { - throw ProtocolException.negativeLength("SystemId", systemIdLen); + if (buf.readableBytes() - offset < 32) { + throw ProtocolException.bufferTooSmall("WorldParticle", 32, buf.readableBytes() - offset); + } else { + WorldParticle obj = new WorldParticle(); + byte nullBits = buf.getByte(offset); + obj.scale = buf.getFloatLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.color = Color.deserialize(buf, offset + 5); } - if (systemIdLen > 4096000) { - throw ProtocolException.stringTooLong("SystemId", systemIdLen, 4096000); + if ((nullBits & 2) != 0) { + obj.positionOffset = PacketIO.readVector3f(buf, offset + 8); } - int systemIdVarLen = VarInt.length(buf, pos); - obj.systemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += systemIdVarLen + systemIdLen; - } + if ((nullBits & 4) != 0) { + obj.rotationOffset = Direction.deserialize(buf, offset + 20); + } - return obj; + int pos = offset + 32; + if ((nullBits & 8) != 0) { + int systemIdLen = VarInt.peek(buf, pos); + if (systemIdLen < 0) { + throw ProtocolException.invalidVarInt("SystemId"); + } + + int systemIdVarLen = VarInt.size(systemIdLen); + if (systemIdLen > 4096000) { + throw ProtocolException.stringTooLong("SystemId", systemIdLen, 4096000); + } + + if (pos + systemIdVarLen + systemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SystemId", pos + systemIdVarLen + systemIdLen, buf.readableBytes()); + } + + obj.systemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += systemIdVarLen + systemIdLen; + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -85,7 +94,7 @@ public class WorldParticle { int pos = offset + 32; if ((nullBits & 8) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -118,7 +127,7 @@ public class WorldParticle { } if (this.positionOffset != null) { - this.positionOffset.serialize(buf); + PacketIO.writeVector3f(buf, this.positionOffset); } else { buf.writeZero(12); } @@ -159,7 +168,7 @@ public class WorldParticle { return ValidationResult.error("SystemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(systemIdLen); pos += systemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SystemId"); @@ -175,7 +184,7 @@ public class WorldParticle { copy.systemId = this.systemId; copy.scale = this.scale; copy.color = this.color != null ? this.color.clone() : null; - copy.positionOffset = this.positionOffset != null ? this.positionOffset.clone() : null; + copy.positionOffset = this.positionOffset; copy.rotationOffset = this.rotationOffset != null ? this.rotationOffset.clone() : null; return copy; } diff --git a/src/com/hypixel/hytale/protocol/io/PacketIO.java b/src/com/hypixel/hytale/protocol/io/PacketIO.java index 623553de..6fb8a679 100644 --- a/src/com/hypixel/hytale/protocol/io/PacketIO.java +++ b/src/com/hypixel/hytale/protocol/io/PacketIO.java @@ -11,11 +11,26 @@ import java.nio.charset.StandardCharsets; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.joml.Quaternionf; +import org.joml.Quaternionfc; +import org.joml.Vector2f; +import org.joml.Vector2fc; +import org.joml.Vector3f; +import org.joml.Vector3fc; +import org.joml.Vector4f; +import org.joml.Vector4fc; public final class PacketIO { public static final int FRAME_HEADER_SIZE = 4; public static final Charset UTF8 = StandardCharsets.UTF_8; public static final Charset ASCII = StandardCharsets.US_ASCII; + public static final Vector2fc ZERO_VECTOR2 = new Vector2f(); + public static final Vector3fc ZERO_VECTOR3 = new Vector3f(); + public static final Vector4fc ZERO_VECTOR4 = new Vector4f(); + public static final Quaternionfc ZERO_QUATERNION = new Quaternionf(0.0F, 0.0F, 0.0F, 0.0F); + public static final Matrix4fc ZERO_MATRIX = new Matrix4f().zero(); private static final int COMPRESSION_LEVEL = Integer.getInteger("hytale.protocol.compressionLevel", Zstd.defaultCompressionLevel()); private PacketIO() { @@ -192,6 +207,92 @@ public final class PacketIO { } } + @Nonnull + public static Vector2f readVector2f(@Nonnull ByteBuf buf, int offset) { + return new Vector2f(buf.getFloatLE(offset), buf.getFloatLE(offset + 4)); + } + + @Nonnull + public static Vector3f readVector3f(@Nonnull ByteBuf buf, int offset) { + return new Vector3f(buf.getFloatLE(offset), buf.getFloatLE(offset + 4), buf.getFloatLE(offset + 8)); + } + + @Nonnull + public static Vector4f readVector4f(@Nonnull ByteBuf buf, int offset) { + return new Vector4f(buf.getFloatLE(offset), buf.getFloatLE(offset + 4), buf.getFloatLE(offset + 8), buf.getFloatLE(offset + 12)); + } + + @Nonnull + public static Quaternionf readQuaternionf(@Nonnull ByteBuf buf, int offset) { + return new Quaternionf(buf.getFloatLE(offset), buf.getFloatLE(offset + 4), buf.getFloatLE(offset + 8), buf.getFloatLE(offset + 12)); + } + + @Nonnull + public static Matrix4f readMatrix4f(@Nonnull ByteBuf buf, int offset) { + return new Matrix4f( + buf.getFloatLE(offset), + buf.getFloatLE(offset + 4), + buf.getFloatLE(offset + 8), + buf.getFloatLE(offset + 12), + buf.getFloatLE(offset + 16), + buf.getFloatLE(offset + 20), + buf.getFloatLE(offset + 24), + buf.getFloatLE(offset + 28), + buf.getFloatLE(offset + 32), + buf.getFloatLE(offset + 36), + buf.getFloatLE(offset + 40), + buf.getFloatLE(offset + 44), + buf.getFloatLE(offset + 48), + buf.getFloatLE(offset + 52), + buf.getFloatLE(offset + 56), + buf.getFloatLE(offset + 60) + ); + } + + public static void writeVector2f(@Nonnull ByteBuf buf, @Nonnull Vector2fc v) { + buf.writeFloatLE(v.x()); + buf.writeFloatLE(v.y()); + } + + public static void writeVector3f(@Nonnull ByteBuf buf, @Nonnull Vector3fc v) { + buf.writeFloatLE(v.x()); + buf.writeFloatLE(v.y()); + buf.writeFloatLE(v.z()); + } + + public static void writeVector4f(@Nonnull ByteBuf buf, @Nonnull Vector4fc v) { + buf.writeFloatLE(v.x()); + buf.writeFloatLE(v.y()); + buf.writeFloatLE(v.z()); + buf.writeFloatLE(v.w()); + } + + public static void writeQuaternionf(@Nonnull ByteBuf buf, @Nonnull Quaternionfc q) { + buf.writeFloatLE(q.x()); + buf.writeFloatLE(q.y()); + buf.writeFloatLE(q.z()); + buf.writeFloatLE(q.w()); + } + + public static void writeMatrix4f(@Nonnull ByteBuf buf, @Nonnull Matrix4fc m) { + buf.writeFloatLE(m.m00()); + buf.writeFloatLE(m.m10()); + buf.writeFloatLE(m.m20()); + buf.writeFloatLE(m.m30()); + buf.writeFloatLE(m.m01()); + buf.writeFloatLE(m.m11()); + buf.writeFloatLE(m.m21()); + buf.writeFloatLE(m.m31()); + buf.writeFloatLE(m.m02()); + buf.writeFloatLE(m.m12()); + buf.writeFloatLE(m.m22()); + buf.writeFloatLE(m.m32()); + buf.writeFloatLE(m.m03()); + buf.writeFloatLE(m.m13()); + buf.writeFloatLE(m.m23()); + buf.writeFloatLE(m.m33()); + } + @Nonnull public static UUID readUUID(@Nonnull ByteBuf buf, int offset) { long mostSig = buf.getLong(offset); diff --git a/src/com/hypixel/hytale/protocol/io/VarInt.java b/src/com/hypixel/hytale/protocol/io/VarInt.java index a0f43ec4..ab74aac2 100644 --- a/src/com/hypixel/hytale/protocol/io/VarInt.java +++ b/src/com/hypixel/hytale/protocol/io/VarInt.java @@ -11,67 +11,121 @@ public final class VarInt { if (value < 0) { throw new IllegalArgumentException("VarInt cannot encode negative values: " + value); } else { - while ((value & -128) != 0) { - buf.writeByte(value & 127 | 128); - value >>>= 7; + if ((value & -128) == 0) { + buf.writeByte(value); + } else if ((value & -16384) == 0) { + buf.writeShort((value & 127 | 128) << 8 | value >>> 7); + } else if ((value & -2097152) == 0) { + buf.writeMedium((value & 127 | 128) << 16 | (value >>> 7 & 127 | 128) << 8 | value >>> 14); + } else if ((value & -268435456) == 0) { + buf.writeInt((value & 127 | 128) << 24 | (value >>> 7 & 127 | 128) << 16 | (value >>> 14 & 127 | 128) << 8 | value >>> 21); + } else { + buf.writeInt((value & 127 | 128) << 24 | (value >>> 7 & 127 | 128) << 16 | (value >>> 14 & 127 | 128) << 8 | value >>> 21 & 127 | 128); + buf.writeByte(value >>> 28); } - - buf.writeByte(value); } } public static int read(@Nonnull ByteBuf buf) { - int value = 0; - int shift = 0; - - do { - byte b = buf.readByte(); - value |= (b & 127) << shift; - if ((b & 128) == 0) { + int b = buf.readByte(); + int value = b & 127; + if ((b & 128) == 0) { + return value; + } else { + int var3 = buf.readByte(); + value |= (var3 & 127) << 7; + if ((var3 & 128) == 0) { return value; + } else { + var3 = buf.readByte(); + value |= (var3 & 127) << 14; + if ((var3 & 128) == 0) { + return value; + } else { + var3 = buf.readByte(); + value |= (var3 & 127) << 21; + if ((var3 & 128) == 0) { + return value; + } else { + var3 = buf.readByte(); + value |= (var3 & 127) << 28; + if ((var3 & 128) == 0) { + return value; + } else { + throw new ProtocolException("VarInt exceeds maximum length (5 bytes)"); + } + } + } } - - shift += 7; - } while (shift <= 28); - - throw new ProtocolException("VarInt exceeds maximum length (5 bytes)"); + } } public static int peek(@Nonnull ByteBuf buf, int index) { - int value = 0; - int shift = 0; - int pos = index; - - while (pos < buf.writerIndex()) { - byte b = buf.getByte(pos++); - value |= (b & 127) << shift; + int limit = buf.writerIndex(); + if (index >= limit) { + return -1; + } else { + int b = buf.getByte(index); + int value = b & 127; if ((b & 128) == 0) { return value; - } - - shift += 7; - if (shift > 28) { + } else if (index + 1 >= limit) { return -1; + } else { + int var5 = buf.getByte(index + 1); + value |= (var5 & 127) << 7; + if ((var5 & 128) == 0) { + return value; + } else if (index + 2 >= limit) { + return -1; + } else { + var5 = buf.getByte(index + 2); + value |= (var5 & 127) << 14; + if ((var5 & 128) == 0) { + return value; + } else if (index + 3 >= limit) { + return -1; + } else { + var5 = buf.getByte(index + 3); + value |= (var5 & 127) << 21; + if ((var5 & 128) == 0) { + return value; + } else if (index + 4 >= limit) { + return -1; + } else { + var5 = buf.getByte(index + 4); + value |= (var5 & 127) << 28; + return (var5 & 128) == 0 ? value : -1; + } + } + } } } - - return -1; } public static int length(@Nonnull ByteBuf buf, int index) { - int pos = index; - - while (pos < buf.writerIndex()) { - if ((buf.getByte(pos++) & 128) == 0) { - return pos - index; - } - - if (pos - index > 5) { - return -1; - } + int limit = buf.writerIndex(); + if (index >= limit) { + return -1; + } else if ((buf.getByte(index) & 128) == 0) { + return 1; + } else if (index + 1 >= limit) { + return -1; + } else if ((buf.getByte(index + 1) & 128) == 0) { + return 2; + } else if (index + 2 >= limit) { + return -1; + } else if ((buf.getByte(index + 2) & 128) == 0) { + return 3; + } else if (index + 3 >= limit) { + return -1; + } else if ((buf.getByte(index + 3) & 128) == 0) { + return 4; + } else if (index + 4 >= limit) { + return -1; + } else { + return (buf.getByte(index + 4) & 128) == 0 ? 5 : -1; } - - return -1; } public static int size(int value) { diff --git a/src/com/hypixel/hytale/protocol/io/netty/ProtocolUtil.java b/src/com/hypixel/hytale/protocol/io/netty/ProtocolUtil.java index 2c7a80c2..0188ba78 100644 --- a/src/com/hypixel/hytale/protocol/io/netty/ProtocolUtil.java +++ b/src/com/hypixel/hytale/protocol/io/netty/ProtocolUtil.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.protocol.io.netty; import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.packets.connection.QuicApplicationErrorCode; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; @@ -16,13 +17,6 @@ import javax.annotation.Nonnull; public final class ProtocolUtil { public static final AttributeKey STREAM_CHANNEL_KEY = AttributeKey.newInstance("STREAM_CHANNEL_ID"); public static final AttributeKey PACKET_TIMEOUT_KEY = AttributeKey.newInstance("PACKET_TIMEOUT"); - public static final int APPLICATION_NO_ERROR = 0; - public static final int APPLICATION_RATE_LIMITED = 1; - public static final int APPLICATION_AUTH_FAILED = 2; - public static final int APPLICATION_INVALID_VERSION = 3; - public static final int APPLICATION_TIMEOUT = 4; - public static final int APPLICATION_CLIENT_OUTDATED = 5; - public static final int APPLICATION_SERVER_OUTDATED = 6; public static final ChannelFutureListener CLOSE_ON_COMPLETE = ProtocolUtil::closeApplicationOnComplete; private ProtocolUtil() { @@ -46,10 +40,14 @@ public final class ProtocolUtil { } public static void closeApplicationConnection(@Nonnull Channel channel) { - closeApplicationConnection(channel, 0); + closeApplicationConnection(channel, QuicApplicationErrorCode.NoError); } - public static void closeApplicationConnection(@Nonnull Channel channel, int errorCode) { + public static void closeApplicationConnection(@Nonnull Channel channel, @Nonnull QuicApplicationErrorCode errorCode) { + closeApplicationConnection(channel, errorCode.ordinal()); + } + + private static void closeApplicationConnection(@Nonnull Channel channel, int errorCode) { if (channel instanceof QuicChannel quicChannel) { quicChannel.close(true, errorCode, Unpooled.EMPTY_BUFFER); } else { @@ -61,7 +59,11 @@ public final class ProtocolUtil { } } - public static void closeApplicationConnection(@Nonnull Channel channel, int errorCode, @Nonnull String reason) { + public static void closeApplicationConnection(@Nonnull Channel channel, @Nonnull QuicApplicationErrorCode errorCode, @Nonnull String reason) { + closeApplicationConnection(channel, errorCode.ordinal(), reason); + } + + private static void closeApplicationConnection(@Nonnull Channel channel, int errorCode, @Nonnull String reason) { ByteBuf reasonBuf = Unpooled.copiedBuffer(reason, StandardCharsets.UTF_8); if (channel instanceof QuicChannel quicChannel) { quicChannel.close(true, errorCode, reasonBuf); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorActivateButton.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorActivateButton.java index d170e778..e643b995 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorActivateButton.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorActivateButton.java @@ -46,25 +46,33 @@ public class AssetEditorActivateButton implements Packet, ToServerPacket { @Nonnull public static AssetEditorActivateButton deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorActivateButton obj = new AssetEditorActivateButton(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int buttonIdLen = VarInt.peek(buf, pos); - if (buttonIdLen < 0) { - throw ProtocolException.negativeLength("ButtonId", buttonIdLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorActivateButton", 1, buf.readableBytes() - offset); + } else { + AssetEditorActivateButton obj = new AssetEditorActivateButton(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int buttonIdLen = VarInt.peek(buf, pos); + if (buttonIdLen < 0) { + throw ProtocolException.invalidVarInt("ButtonId"); + } + + int buttonIdVarLen = VarInt.size(buttonIdLen); + if (buttonIdLen > 4096000) { + throw ProtocolException.stringTooLong("ButtonId", buttonIdLen, 4096000); + } + + if (pos + buttonIdVarLen + buttonIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ButtonId", pos + buttonIdVarLen + buttonIdLen, buf.readableBytes()); + } + + obj.buttonId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += buttonIdVarLen + buttonIdLen; } - if (buttonIdLen > 4096000) { - throw ProtocolException.stringTooLong("ButtonId", buttonIdLen, 4096000); - } - - int buttonIdVarLen = VarInt.length(buf, pos); - obj.buttonId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += buttonIdVarLen + buttonIdLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +80,7 @@ public class AssetEditorActivateButton implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -117,7 +125,7 @@ public class AssetEditorActivateButton implements Packet, ToServerPacket { return ValidationResult.error("ButtonId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(buttonIdLen); pos += buttonIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ButtonId"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAsset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAsset.java index 1d07556b..1262707e 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAsset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAsset.java @@ -35,28 +35,47 @@ public class AssetEditorAsset { @Nonnull public static AssetEditorAsset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorAsset obj = new AssetEditorAsset(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int hashLen = VarInt.peek(buf, varPos0); - if (hashLen < 0) { - throw ProtocolException.negativeLength("Hash", hashLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AssetEditorAsset", 9, buf.readableBytes() - offset); + } else { + AssetEditorAsset obj = new AssetEditorAsset(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Hash", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int hashLen = VarInt.peek(buf, varPos0); + if (hashLen < 0) { + throw ProtocolException.invalidVarInt("Hash"); + } + + int hashVarIntLen = VarInt.size(hashLen); + if (hashLen > 4096000) { + throw ProtocolException.stringTooLong("Hash", hashLen, 4096000); + } + + if (varPos0 + hashVarIntLen + hashLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Hash", varPos0 + hashVarIntLen + hashLen, buf.readableBytes()); + } + + obj.hash = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (hashLen > 4096000) { - throw ProtocolException.stringTooLong("Hash", hashLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + obj.path = AssetPath.deserialize(buf, varPos1); } - obj.hash = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - obj.path = AssetPath.deserialize(buf, varPos1); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -64,9 +83,13 @@ public class AssetEditorAsset { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Hash", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -74,6 +97,10 @@ public class AssetEditorAsset { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; pos1 += AssetPath.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -136,15 +163,11 @@ public class AssetEditorAsset { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int hashOffset = buffer.getIntLE(offset + 1); - if (hashOffset < 0) { + if (hashOffset < 0 || hashOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Hash"); } int pos = offset + 9 + hashOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Hash"); - } - int hashLen = VarInt.peek(buffer, pos); if (hashLen < 0) { return ValidationResult.error("Invalid string length for Hash"); @@ -154,7 +177,7 @@ public class AssetEditorAsset { return ValidationResult.error("Hash exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(hashLen); pos += hashLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Hash"); @@ -163,15 +186,11 @@ public class AssetEditorAsset { if ((nullBits & 2) != 0) { int pathOffset = buffer.getIntLE(offset + 5); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Path"); } int posx = offset + 9 + pathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, posx); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetListSetup.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetListSetup.java index 1b128a50..48f824c6 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetListSetup.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetListSetup.java @@ -63,51 +63,70 @@ public class AssetEditorAssetListSetup implements Packet, ToClientPacket { @Nonnull public static AssetEditorAssetListSetup deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorAssetListSetup obj = new AssetEditorAssetListSetup(); - byte nullBits = buf.getByte(offset); - obj.isReadOnly = buf.getByte(offset + 1) != 0; - obj.canBeDeleted = buf.getByte(offset + 2) != 0; - obj.tree = AssetEditorFileTree.fromValue(buf.getByte(offset + 3)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 12 + buf.getIntLE(offset + 4); - int packLen = VarInt.peek(buf, varPos0); - if (packLen < 0) { - throw ProtocolException.negativeLength("Pack", packLen); + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("AssetEditorAssetListSetup", 12, buf.readableBytes() - offset); + } else { + AssetEditorAssetListSetup obj = new AssetEditorAssetListSetup(); + byte nullBits = buf.getByte(offset); + obj.isReadOnly = buf.getByte(offset + 1) != 0; + obj.canBeDeleted = buf.getByte(offset + 2) != 0; + obj.tree = AssetEditorFileTree.fromValue(buf.getByte(offset + 3)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("Pack", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 12 + varPosBase0; + int packLen = VarInt.peek(buf, varPos0); + if (packLen < 0) { + throw ProtocolException.invalidVarInt("Pack"); + } + + int packVarIntLen = VarInt.size(packLen); + if (packLen > 4096000) { + throw ProtocolException.stringTooLong("Pack", packLen, 4096000); + } + + if (varPos0 + packVarIntLen + packLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Pack", varPos0 + packVarIntLen + packLen, buf.readableBytes()); + } + + obj.pack = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (packLen > 4096000) { - throw ProtocolException.stringTooLong("Pack", packLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 8); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("Paths", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 12 + varPosBase1; + int pathsCount = VarInt.peek(buf, varPos1); + if (pathsCount < 0) { + throw ProtocolException.invalidVarInt("Paths"); + } + + int varIntLen = VarInt.size(pathsCount); + if (pathsCount > 4096000) { + throw ProtocolException.arrayTooLong("Paths", pathsCount, 4096000); + } + + if (varPos1 + varIntLen + pathsCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Paths", varPos1 + varIntLen + pathsCount * 2, buf.readableBytes()); + } + + obj.paths = new AssetEditorFileEntry[pathsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < pathsCount; i++) { + obj.paths[i] = AssetEditorFileEntry.deserialize(buf, elemPos); + elemPos += AssetEditorFileEntry.computeBytesConsumed(buf, elemPos); + } } - obj.pack = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 12 + buf.getIntLE(offset + 8); - int pathsCount = VarInt.peek(buf, varPos1); - if (pathsCount < 0) { - throw ProtocolException.negativeLength("Paths", pathsCount); - } - - if (pathsCount > 4096000) { - throw ProtocolException.arrayTooLong("Paths", pathsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + pathsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Paths", varPos1 + varIntLen + pathsCount * 2, buf.readableBytes()); - } - - obj.paths = new AssetEditorFileEntry[pathsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < pathsCount; i++) { - obj.paths[i] = AssetEditorFileEntry.deserialize(buf, elemPos); - elemPos += AssetEditorFileEntry.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -115,9 +134,13 @@ public class AssetEditorAssetListSetup implements Packet, ToClientPacket { int maxEnd = 12; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("Pack", fieldOffset0, maxEnd); + } + int pos0 = offset + 12 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -125,9 +148,13 @@ public class AssetEditorAssetListSetup implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 8); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("Paths", fieldOffset1, maxEnd); + } + int pos1 = offset + 12 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += AssetEditorFileEntry.computeBytesConsumed(buf, pos1); @@ -210,66 +237,63 @@ public class AssetEditorAssetListSetup implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 12 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int packOffset = buffer.getIntLE(offset + 4); - if (packOffset < 0) { - return ValidationResult.error("Invalid offset for Pack"); - } - - int pos = offset + 12 + packOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Pack"); - } - - int packLen = VarInt.peek(buffer, pos); - if (packLen < 0) { - return ValidationResult.error("Invalid string length for Pack"); - } - - if (packLen > 4096000) { - return ValidationResult.error("Pack exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += packLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Pack"); - } - } - - if ((nullBits & 2) != 0) { - int pathsOffset = buffer.getIntLE(offset + 8); - if (pathsOffset < 0) { - return ValidationResult.error("Invalid offset for Paths"); - } - - int posx = offset + 12 + pathsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Paths"); - } - - int pathsCount = VarInt.peek(buffer, posx); - if (pathsCount < 0) { - return ValidationResult.error("Invalid array count for Paths"); - } - - if (pathsCount > 4096000) { - return ValidationResult.error("Paths exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < pathsCount; i++) { - ValidationResult structResult = AssetEditorFileEntry.validateStructure(buffer, posx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid AssetEditorFileEntry in Paths[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 3) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid AssetEditorFileTree value for Tree"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 4); + if (v < 0 || v > buffer.writerIndex() - offset - 12) { + return ValidationResult.error("Invalid offset for Pack"); } - posx += AssetEditorFileEntry.computeBytesConsumed(buffer, posx); - } - } + int pos = offset + 12 + v; + int packLen = VarInt.peek(buffer, pos); + if (packLen < 0) { + return ValidationResult.error("Invalid string length for Pack"); + } - return ValidationResult.OK; + if (packLen > 4096000) { + return ValidationResult.error("Pack exceeds max length 4096000"); + } + + pos += VarInt.size(packLen); + pos += packLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Pack"); + } + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 8); + if (v < 0 || v > buffer.writerIndex() - offset - 12) { + return ValidationResult.error("Invalid offset for Paths"); + } + + int posx = offset + 12 + v; + int pathsCount = VarInt.peek(buffer, posx); + if (pathsCount < 0) { + return ValidationResult.error("Invalid array count for Paths"); + } + + if (pathsCount > 4096000) { + return ValidationResult.error("Paths exceeds max length 4096000"); + } + + posx += VarInt.size(pathsCount); + + for (int i = 0; i < pathsCount; i++) { + ValidationResult structResult = AssetEditorFileEntry.validateStructure(buffer, posx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid AssetEditorFileEntry in Paths[" + i + "]: " + structResult.error()); + } + + posx += AssetEditorFileEntry.computeBytesConsumed(buffer, posx); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetListUpdate.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetListUpdate.java index ee19d5ae..f2487901 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetListUpdate.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetListUpdate.java @@ -55,73 +55,97 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { @Nonnull public static AssetEditorAssetListUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorAssetListUpdate obj = new AssetEditorAssetListUpdate(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int packLen = VarInt.peek(buf, varPos0); - if (packLen < 0) { - throw ProtocolException.negativeLength("Pack", packLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("AssetEditorAssetListUpdate", 13, buf.readableBytes() - offset); + } else { + AssetEditorAssetListUpdate obj = new AssetEditorAssetListUpdate(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Pack", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int packLen = VarInt.peek(buf, varPos0); + if (packLen < 0) { + throw ProtocolException.invalidVarInt("Pack"); + } + + int packVarIntLen = VarInt.size(packLen); + if (packLen > 4096000) { + throw ProtocolException.stringTooLong("Pack", packLen, 4096000); + } + + if (varPos0 + packVarIntLen + packLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Pack", varPos0 + packVarIntLen + packLen, buf.readableBytes()); + } + + obj.pack = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (packLen > 4096000) { - throw ProtocolException.stringTooLong("Pack", packLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Additions", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int additionsCount = VarInt.peek(buf, varPos1); + if (additionsCount < 0) { + throw ProtocolException.invalidVarInt("Additions"); + } + + int varIntLen = VarInt.size(additionsCount); + if (additionsCount > 4096000) { + throw ProtocolException.arrayTooLong("Additions", additionsCount, 4096000); + } + + if (varPos1 + varIntLen + additionsCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Additions", varPos1 + varIntLen + additionsCount * 2, buf.readableBytes()); + } + + obj.additions = new AssetEditorFileEntry[additionsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < additionsCount; i++) { + obj.additions[i] = AssetEditorFileEntry.deserialize(buf, elemPos); + elemPos += AssetEditorFileEntry.computeBytesConsumed(buf, elemPos); + } } - obj.pack = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Deletions", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int deletionsCount = VarInt.peek(buf, varPos2); + if (deletionsCount < 0) { + throw ProtocolException.invalidVarInt("Deletions"); + } + + int varIntLenx = VarInt.size(deletionsCount); + if (deletionsCount > 4096000) { + throw ProtocolException.arrayTooLong("Deletions", deletionsCount, 4096000); + } + + if (varPos2 + varIntLenx + deletionsCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Deletions", varPos2 + varIntLenx + deletionsCount * 2, buf.readableBytes()); + } + + obj.deletions = new AssetEditorFileEntry[deletionsCount]; + int elemPos = varPos2 + varIntLenx; + + for (int i = 0; i < deletionsCount; i++) { + obj.deletions[i] = AssetEditorFileEntry.deserialize(buf, elemPos); + elemPos += AssetEditorFileEntry.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int additionsCount = VarInt.peek(buf, varPos1); - if (additionsCount < 0) { - throw ProtocolException.negativeLength("Additions", additionsCount); - } - - if (additionsCount > 4096000) { - throw ProtocolException.arrayTooLong("Additions", additionsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + additionsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Additions", varPos1 + varIntLen + additionsCount * 2, buf.readableBytes()); - } - - obj.additions = new AssetEditorFileEntry[additionsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < additionsCount; i++) { - obj.additions[i] = AssetEditorFileEntry.deserialize(buf, elemPos); - elemPos += AssetEditorFileEntry.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int deletionsCount = VarInt.peek(buf, varPos2); - if (deletionsCount < 0) { - throw ProtocolException.negativeLength("Deletions", deletionsCount); - } - - if (deletionsCount > 4096000) { - throw ProtocolException.arrayTooLong("Deletions", deletionsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + deletionsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Deletions", varPos2 + varIntLen + deletionsCount * 2, buf.readableBytes()); - } - - obj.deletions = new AssetEditorFileEntry[deletionsCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < deletionsCount; i++) { - obj.deletions[i] = AssetEditorFileEntry.deserialize(buf, elemPos); - elemPos += AssetEditorFileEntry.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -129,9 +153,13 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Pack", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -139,9 +167,13 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Additions", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += AssetEditorFileEntry.computeBytesConsumed(buf, pos1); @@ -154,9 +186,13 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Deletions", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += AssetEditorFileEntry.computeBytesConsumed(buf, pos2); @@ -269,15 +305,11 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int packOffset = buffer.getIntLE(offset + 1); - if (packOffset < 0) { + if (packOffset < 0 || packOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Pack"); } int pos = offset + 13 + packOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Pack"); - } - int packLen = VarInt.peek(buffer, pos); if (packLen < 0) { return ValidationResult.error("Invalid string length for Pack"); @@ -287,7 +319,7 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { return ValidationResult.error("Pack exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(packLen); pos += packLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Pack"); @@ -296,15 +328,11 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int additionsOffset = buffer.getIntLE(offset + 5); - if (additionsOffset < 0) { + if (additionsOffset < 0 || additionsOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Additions"); } int posx = offset + 13 + additionsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Additions"); - } - int additionsCount = VarInt.peek(buffer, posx); if (additionsCount < 0) { return ValidationResult.error("Invalid array count for Additions"); @@ -314,7 +342,7 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { return ValidationResult.error("Additions exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(additionsCount); for (int i = 0; i < additionsCount; i++) { ValidationResult structResult = AssetEditorFileEntry.validateStructure(buffer, posx); @@ -328,15 +356,11 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int deletionsOffset = buffer.getIntLE(offset + 9); - if (deletionsOffset < 0) { + if (deletionsOffset < 0 || deletionsOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Deletions"); } int posxx = offset + 13 + deletionsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Deletions"); - } - int deletionsCount = VarInt.peek(buffer, posxx); if (deletionsCount < 0) { return ValidationResult.error("Invalid array count for Deletions"); @@ -346,7 +370,7 @@ public class AssetEditorAssetListUpdate implements Packet, ToClientPacket { return ValidationResult.error("Deletions exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(deletionsCount); for (int i = 0; i < deletionsCount; i++) { ValidationResult structResult = AssetEditorFileEntry.validateStructure(buffer, posxx); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetPackSetup.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetPackSetup.java index f0c64314..55940cd7 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetPackSetup.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetPackSetup.java @@ -49,44 +49,53 @@ public class AssetEditorAssetPackSetup implements Packet, ToClientPacket { @Nonnull public static AssetEditorAssetPackSetup deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorAssetPackSetup obj = new AssetEditorAssetPackSetup(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int packsCount = VarInt.peek(buf, pos); - if (packsCount < 0) { - throw ProtocolException.negativeLength("Packs", packsCount); - } - - if (packsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Packs", packsCount, 4096000); - } - - pos += VarInt.size(packsCount); - obj.packs = new HashMap<>(packsCount); - - for (int i = 0; i < packsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorAssetPackSetup", 1, buf.readableBytes() - offset); + } else { + AssetEditorAssetPackSetup obj = new AssetEditorAssetPackSetup(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int packsCount = VarInt.peek(buf, pos); + if (packsCount < 0) { + throw ProtocolException.invalidVarInt("Packs"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int packsVarLen = VarInt.size(packsCount); + if (packsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Packs", packsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - AssetPackManifest val = AssetPackManifest.deserialize(buf, pos); - pos += AssetPackManifest.computeBytesConsumed(buf, pos); - if (obj.packs.put(key, val) != null) { - throw ProtocolException.duplicateKey("packs", key); + pos += packsVarLen; + obj.packs = new HashMap<>(packsCount); + + for (int i = 0; i < packsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + AssetPackManifest val = AssetPackManifest.deserialize(buf, pos); + pos += AssetPackManifest.computeBytesConsumed(buf, pos); + if (obj.packs.put(key, val) != null) { + throw ProtocolException.duplicateKey("packs", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,11 +103,11 @@ public class AssetEditorAssetPackSetup implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += AssetPackManifest.computeBytesConsumed(buf, pos); } } @@ -160,7 +169,7 @@ public class AssetEditorAssetPackSetup implements Packet, ToClientPacket { return ValidationResult.error("Packs exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(packsCount); for (int i = 0; i < packsCount; i++) { int keyLen = VarInt.peek(buffer, pos); @@ -172,7 +181,7 @@ public class AssetEditorAssetPackSetup implements Packet, ToClientPacket { return ValidationResult.error("key exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(keyLen); pos += keyLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading key"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetType.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetType.java index 584b5429..a7eb97ee 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetType.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetType.java @@ -57,67 +57,111 @@ public class AssetEditorAssetType { @Nonnull public static AssetEditorAssetType deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorAssetType obj = new AssetEditorAssetType(); - byte nullBits = buf.getByte(offset); - obj.isColoredIcon = buf.getByte(offset + 1) != 0; - obj.editorType = AssetEditorEditorType.fromValue(buf.getByte(offset + 2)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 19 + buf.getIntLE(offset + 3); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 19) { + throw ProtocolException.bufferTooSmall("AssetEditorAssetType", 19, buf.readableBytes() - offset); + } else { + AssetEditorAssetType obj = new AssetEditorAssetType(); + byte nullBits = buf.getByte(offset); + obj.isColoredIcon = buf.getByte(offset + 1) != 0; + obj.editorType = AssetEditorEditorType.fromValue(buf.getByte(offset + 2)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 3); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 19) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 19 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 7); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 19) { + throw ProtocolException.invalidOffset("Icon", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 19 + varPosBase1; + int iconLen = VarInt.peek(buf, varPos1); + if (iconLen < 0) { + throw ProtocolException.invalidVarInt("Icon"); + } + + int iconVarIntLen = VarInt.size(iconLen); + if (iconLen > 4096000) { + throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + } + + if (varPos1 + iconVarIntLen + iconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Icon", varPos1 + iconVarIntLen + iconLen, buf.readableBytes()); + } + + obj.icon = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 11); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 19) { + throw ProtocolException.invalidOffset("Path", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 19 + varPosBase2; + int pathLen = VarInt.peek(buf, varPos2); + if (pathLen < 0) { + throw ProtocolException.invalidVarInt("Path"); + } + + int pathVarIntLen = VarInt.size(pathLen); + if (pathLen > 4096000) { + throw ProtocolException.stringTooLong("Path", pathLen, 4096000); + } + + if (varPos2 + pathVarIntLen + pathLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Path", varPos2 + pathVarIntLen + pathLen, buf.readableBytes()); + } + + obj.path = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 15); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 19) { + throw ProtocolException.invalidOffset("FileExtension", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 19 + varPosBase3; + int fileExtensionLen = VarInt.peek(buf, varPos3); + if (fileExtensionLen < 0) { + throw ProtocolException.invalidVarInt("FileExtension"); + } + + int fileExtensionVarIntLen = VarInt.size(fileExtensionLen); + if (fileExtensionLen > 4096000) { + throw ProtocolException.stringTooLong("FileExtension", fileExtensionLen, 4096000); + } + + if (varPos3 + fileExtensionVarIntLen + fileExtensionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FileExtension", varPos3 + fileExtensionVarIntLen + fileExtensionLen, buf.readableBytes()); + } + + obj.fileExtension = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 19 + buf.getIntLE(offset + 7); - int iconLen = VarInt.peek(buf, varPos1); - if (iconLen < 0) { - throw ProtocolException.negativeLength("Icon", iconLen); - } - - if (iconLen > 4096000) { - throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); - } - - obj.icon = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 19 + buf.getIntLE(offset + 11); - int pathLen = VarInt.peek(buf, varPos2); - if (pathLen < 0) { - throw ProtocolException.negativeLength("Path", pathLen); - } - - if (pathLen > 4096000) { - throw ProtocolException.stringTooLong("Path", pathLen, 4096000); - } - - obj.path = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 19 + buf.getIntLE(offset + 15); - int fileExtensionLen = VarInt.peek(buf, varPos3); - if (fileExtensionLen < 0) { - throw ProtocolException.negativeLength("FileExtension", fileExtensionLen); - } - - if (fileExtensionLen > 4096000) { - throw ProtocolException.stringTooLong("FileExtension", fileExtensionLen, 4096000); - } - - obj.fileExtension = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -125,9 +169,13 @@ public class AssetEditorAssetType { int maxEnd = 19; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 3); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 19) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 19 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -135,9 +183,13 @@ public class AssetEditorAssetType { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 7); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 19) { + throw ProtocolException.invalidOffset("Icon", fieldOffset1, maxEnd); + } + int pos1 = offset + 19 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -145,9 +197,13 @@ public class AssetEditorAssetType { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 11); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 19) { + throw ProtocolException.invalidOffset("Path", fieldOffset2, maxEnd); + } + int pos2 = offset + 19 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -155,9 +211,13 @@ public class AssetEditorAssetType { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 15); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 19) { + throw ProtocolException.invalidOffset("FileExtension", fieldOffset3, maxEnd); + } + int pos3 = offset + 19 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -252,115 +312,104 @@ public class AssetEditorAssetType { return ValidationResult.error("Buffer too small: expected at least 19 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int idOffset = buffer.getIntLE(offset + 3); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); + int v = buffer.getByte(offset + 2) & 255; + if (v >= 7) { + return ValidationResult.error("Invalid AssetEditorEditorType value for EditorType"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 3); + if (v < 0 || v > buffer.writerIndex() - offset - 19) { + return ValidationResult.error("Invalid offset for Id"); + } + + int pos = offset + 19 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } } - int pos = offset + 19 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 7); + if (v < 0 || v > buffer.writerIndex() - offset - 19) { + return ValidationResult.error("Invalid offset for Icon"); + } + + int posx = offset + 19 + v; + int iconLen = VarInt.peek(buffer, posx); + if (iconLen < 0) { + return ValidationResult.error("Invalid string length for Icon"); + } + + if (iconLen > 4096000) { + return ValidationResult.error("Icon exceeds max length 4096000"); + } + + posx += VarInt.size(iconLen); + posx += iconLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Icon"); + } } - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 11); + if (v < 0 || v > buffer.writerIndex() - offset - 19) { + return ValidationResult.error("Invalid offset for Path"); + } + + int posxx = offset + 19 + v; + int pathLen = VarInt.peek(buffer, posxx); + if (pathLen < 0) { + return ValidationResult.error("Invalid string length for Path"); + } + + if (pathLen > 4096000) { + return ValidationResult.error("Path exceeds max length 4096000"); + } + + posxx += VarInt.size(pathLen); + posxx += pathLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Path"); + } } - if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 19) { + return ValidationResult.error("Invalid offset for FileExtension"); + } + + int posxxx = offset + 19 + v; + int fileExtensionLen = VarInt.peek(buffer, posxxx); + if (fileExtensionLen < 0) { + return ValidationResult.error("Invalid string length for FileExtension"); + } + + if (fileExtensionLen > 4096000) { + return ValidationResult.error("FileExtension exceeds max length 4096000"); + } + + posxxx += VarInt.size(fileExtensionLen); + posxxx += fileExtensionLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading FileExtension"); + } } - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); - } + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int iconOffset = buffer.getIntLE(offset + 7); - if (iconOffset < 0) { - return ValidationResult.error("Invalid offset for Icon"); - } - - int posx = offset + 19 + iconOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Icon"); - } - - int iconLen = VarInt.peek(buffer, posx); - if (iconLen < 0) { - return ValidationResult.error("Invalid string length for Icon"); - } - - if (iconLen > 4096000) { - return ValidationResult.error("Icon exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += iconLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Icon"); - } - } - - if ((nullBits & 4) != 0) { - int pathOffset = buffer.getIntLE(offset + 11); - if (pathOffset < 0) { - return ValidationResult.error("Invalid offset for Path"); - } - - int posxx = offset + 19 + pathOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - - int pathLen = VarInt.peek(buffer, posxx); - if (pathLen < 0) { - return ValidationResult.error("Invalid string length for Path"); - } - - if (pathLen > 4096000) { - return ValidationResult.error("Path exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += pathLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Path"); - } - } - - if ((nullBits & 8) != 0) { - int fileExtensionOffset = buffer.getIntLE(offset + 15); - if (fileExtensionOffset < 0) { - return ValidationResult.error("Invalid offset for FileExtension"); - } - - int posxxx = offset + 19 + fileExtensionOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FileExtension"); - } - - int fileExtensionLen = VarInt.peek(buffer, posxxx); - if (fileExtensionLen < 0) { - return ValidationResult.error("Invalid string length for FileExtension"); - } - - if (fileExtensionLen > 4096000) { - return ValidationResult.error("FileExtension exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - posxxx += fileExtensionLen; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading FileExtension"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetUpdated.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetUpdated.java index 0dc79b40..4673dd31 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetUpdated.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAssetUpdated.java @@ -50,37 +50,51 @@ public class AssetEditorAssetUpdated implements Packet, ToClientPacket { @Nonnull public static AssetEditorAssetUpdated deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorAssetUpdated obj = new AssetEditorAssetUpdated(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - obj.path = AssetPath.deserialize(buf, varPos0); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AssetEditorAssetUpdated", 9, buf.readableBytes() - offset); + } else { + AssetEditorAssetUpdated obj = new AssetEditorAssetUpdated(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + obj.path = AssetPath.deserialize(buf, varPos0); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Data", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int dataCount = VarInt.peek(buf, varPos1); + if (dataCount < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int varIntLen = VarInt.size(dataCount); + if (dataCount > 4096000) { + throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); + } + + if (varPos1 + varIntLen + dataCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", varPos1 + varIntLen + dataCount * 1, buf.readableBytes()); + } + + obj.data = new byte[dataCount]; + + for (int i = 0; i < dataCount; i++) { + obj.data[i] = buf.getByte(varPos1 + varIntLen + i * 1); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int dataCount = VarInt.peek(buf, varPos1); - if (dataCount < 0) { - throw ProtocolException.negativeLength("Data", dataCount); - } - - if (dataCount > 4096000) { - throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + dataCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Data", varPos1 + varIntLen + dataCount * 1, buf.readableBytes()); - } - - obj.data = new byte[dataCount]; - - for (int i = 0; i < dataCount; i++) { - obj.data[i] = buf.getByte(varPos1 + varIntLen + i * 1); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -88,6 +102,10 @@ public class AssetEditorAssetUpdated implements Packet, ToClientPacket { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -97,9 +115,13 @@ public class AssetEditorAssetUpdated implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Data", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 1; + pos1 += VarInt.size(arrLen) + arrLen * 1; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -170,15 +192,11 @@ public class AssetEditorAssetUpdated implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int pathOffset = buffer.getIntLE(offset + 1); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Path"); } int pos = offset + 9 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, pos); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -189,16 +207,12 @@ public class AssetEditorAssetUpdated implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int dataOffset = buffer.getIntLE(offset + 5); - if (dataOffset < 0) { + if (dataOffset < 0 || dataOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Data"); } - int posx = offset + 9 + dataOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Data"); - } - - int dataCount = VarInt.peek(buffer, posx); + int pos = offset + 9 + dataOffset; + int dataCount = VarInt.peek(buffer, pos); if (dataCount < 0) { return ValidationResult.error("Invalid array count for Data"); } @@ -207,9 +221,9 @@ public class AssetEditorAssetUpdated implements Packet, ToClientPacket { return ValidationResult.error("Data exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); - posx += dataCount * 1; - if (posx > buffer.writerIndex()) { + pos += VarInt.size(dataCount); + pos += dataCount * 1; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Data"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAuthorization.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAuthorization.java index 97b9c3e9..f228e667 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAuthorization.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorAuthorization.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class AssetEditorAuthorization implements Packet, ToClientPacket { @Nonnull public static AssetEditorAuthorization deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorAuthorization obj = new AssetEditorAuthorization(); - obj.canUse = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorAuthorization", 1, buf.readableBytes() - offset); + } else { + AssetEditorAuthorization obj = new AssetEditorAuthorization(); + obj.canUse = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCapabilities.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCapabilities.java index fed1e2bc..e77f756a 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCapabilities.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCapabilities.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -55,13 +56,17 @@ public class AssetEditorCapabilities implements Packet, ToClientPacket { @Nonnull public static AssetEditorCapabilities deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorCapabilities obj = new AssetEditorCapabilities(); - obj.canDiscardAssets = buf.getByte(offset + 0) != 0; - obj.canEditAssets = buf.getByte(offset + 1) != 0; - obj.canCreateAssetPacks = buf.getByte(offset + 2) != 0; - obj.canEditAssetPacks = buf.getByte(offset + 3) != 0; - obj.canDeleteAssetPacks = buf.getByte(offset + 4) != 0; - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorCapabilities", 5, buf.readableBytes() - offset); + } else { + AssetEditorCapabilities obj = new AssetEditorCapabilities(); + obj.canDiscardAssets = buf.getByte(offset + 0) != 0; + obj.canEditAssets = buf.getByte(offset + 1) != 0; + obj.canCreateAssetPacks = buf.getByte(offset + 2) != 0; + obj.canEditAssetPacks = buf.getByte(offset + 3) != 0; + obj.canDeleteAssetPacks = buf.getByte(offset + 4) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateAsset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateAsset.java index 36f4a20f..fedadf4d 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateAsset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateAsset.java @@ -64,56 +64,80 @@ public class AssetEditorCreateAsset implements Packet, ToServerPacket { @Nonnull public static AssetEditorCreateAsset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorCreateAsset obj = new AssetEditorCreateAsset(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.rebuildCaches = AssetEditorRebuildCaches.deserialize(buf, offset + 5); + if (buf.readableBytes() - offset < 22) { + throw ProtocolException.bufferTooSmall("AssetEditorCreateAsset", 22, buf.readableBytes() - offset); + } else { + AssetEditorCreateAsset obj = new AssetEditorCreateAsset(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.rebuildCaches = AssetEditorRebuildCaches.deserialize(buf, offset + 5); + } + + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 10); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 22 + varPosBase0; + obj.path = AssetPath.deserialize(buf, varPos0); + } + + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 14); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("Data", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 22 + varPosBase1; + int dataCount = VarInt.peek(buf, varPos1); + if (dataCount < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int varIntLen = VarInt.size(dataCount); + if (dataCount > 4096000) { + throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); + } + + if (varPos1 + varIntLen + dataCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", varPos1 + varIntLen + dataCount * 1, buf.readableBytes()); + } + + obj.data = new byte[dataCount]; + + for (int i = 0; i < dataCount; i++) { + obj.data[i] = buf.getByte(varPos1 + varIntLen + i * 1); + } + } + + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 18); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("ButtonId", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 22 + varPosBase2; + int buttonIdLen = VarInt.peek(buf, varPos2); + if (buttonIdLen < 0) { + throw ProtocolException.invalidVarInt("ButtonId"); + } + + int buttonIdVarIntLen = VarInt.size(buttonIdLen); + if (buttonIdLen > 4096000) { + throw ProtocolException.stringTooLong("ButtonId", buttonIdLen, 4096000); + } + + if (varPos2 + buttonIdVarIntLen + buttonIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ButtonId", varPos2 + buttonIdVarIntLen + buttonIdLen, buf.readableBytes()); + } + + obj.buttonId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos0 = offset + 22 + buf.getIntLE(offset + 10); - obj.path = AssetPath.deserialize(buf, varPos0); - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 22 + buf.getIntLE(offset + 14); - int dataCount = VarInt.peek(buf, varPos1); - if (dataCount < 0) { - throw ProtocolException.negativeLength("Data", dataCount); - } - - if (dataCount > 4096000) { - throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + dataCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Data", varPos1 + varIntLen + dataCount * 1, buf.readableBytes()); - } - - obj.data = new byte[dataCount]; - - for (int i = 0; i < dataCount; i++) { - obj.data[i] = buf.getByte(varPos1 + varIntLen + i * 1); - } - } - - if ((nullBits & 8) != 0) { - int varPos2 = offset + 22 + buf.getIntLE(offset + 18); - int buttonIdLen = VarInt.peek(buf, varPos2); - if (buttonIdLen < 0) { - throw ProtocolException.negativeLength("ButtonId", buttonIdLen); - } - - if (buttonIdLen > 4096000) { - throw ProtocolException.stringTooLong("ButtonId", buttonIdLen, 4096000); - } - - obj.buttonId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -121,6 +145,10 @@ public class AssetEditorCreateAsset implements Packet, ToServerPacket { int maxEnd = 22; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 10); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 22 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -130,9 +158,13 @@ public class AssetEditorCreateAsset implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 14); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("Data", fieldOffset1, maxEnd); + } + int pos1 = offset + 22 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 1; + pos1 += VarInt.size(arrLen) + arrLen * 1; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -140,9 +172,13 @@ public class AssetEditorCreateAsset implements Packet, ToServerPacket { if ((nullBits & 8) != 0) { int fieldOffset2 = buf.getIntLE(offset + 18); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 22) { + throw ProtocolException.invalidOffset("ButtonId", fieldOffset2, maxEnd); + } + int pos2 = offset + 22 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -241,15 +277,11 @@ public class AssetEditorCreateAsset implements Packet, ToServerPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { int pathOffset = buffer.getIntLE(offset + 10); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 22) { return ValidationResult.error("Invalid offset for Path"); } int pos = offset + 22 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, pos); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -260,16 +292,12 @@ public class AssetEditorCreateAsset implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int dataOffset = buffer.getIntLE(offset + 14); - if (dataOffset < 0) { + if (dataOffset < 0 || dataOffset > buffer.writerIndex() - offset - 22) { return ValidationResult.error("Invalid offset for Data"); } - int posx = offset + 22 + dataOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Data"); - } - - int dataCount = VarInt.peek(buffer, posx); + int pos = offset + 22 + dataOffset; + int dataCount = VarInt.peek(buffer, pos); if (dataCount < 0) { return ValidationResult.error("Invalid array count for Data"); } @@ -278,25 +306,21 @@ public class AssetEditorCreateAsset implements Packet, ToServerPacket { return ValidationResult.error("Data exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); - posx += dataCount * 1; - if (posx > buffer.writerIndex()) { + pos += VarInt.size(dataCount); + pos += dataCount * 1; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Data"); } } if ((nullBits & 8) != 0) { int buttonIdOffset = buffer.getIntLE(offset + 18); - if (buttonIdOffset < 0) { + if (buttonIdOffset < 0 || buttonIdOffset > buffer.writerIndex() - offset - 22) { return ValidationResult.error("Invalid offset for ButtonId"); } - int posxx = offset + 22 + buttonIdOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ButtonId"); - } - - int buttonIdLen = VarInt.peek(buffer, posxx); + int posx = offset + 22 + buttonIdOffset; + int buttonIdLen = VarInt.peek(buffer, posx); if (buttonIdLen < 0) { return ValidationResult.error("Invalid string length for ButtonId"); } @@ -305,9 +329,9 @@ public class AssetEditorCreateAsset implements Packet, ToServerPacket { return ValidationResult.error("ButtonId exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += buttonIdLen; - if (posxx > buffer.writerIndex()) { + posx += VarInt.size(buttonIdLen); + posx += buttonIdLen; + if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ButtonId"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateAssetPack.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateAssetPack.java index 48617296..bed5d8c4 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateAssetPack.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateAssetPack.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -13,13 +14,14 @@ public class AssetEditorCreateAssetPack implements Packet, ToServerPacket { public static final int PACKET_ID = 316; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 5; + public static final int FIXED_BLOCK_SIZE = 9; public static final int VARIABLE_FIELD_COUNT = 1; - public static final int VARIABLE_BLOCK_START = 5; + public static final int VARIABLE_BLOCK_START = 9; public static final int MAX_SIZE = 1677721600; public int token; @Nullable public AssetPackManifest manifest; + public int targetDirectoryIndex; @Override public int getId() { @@ -34,33 +36,40 @@ public class AssetEditorCreateAssetPack implements Packet, ToServerPacket { public AssetEditorCreateAssetPack() { } - public AssetEditorCreateAssetPack(int token, @Nullable AssetPackManifest manifest) { + public AssetEditorCreateAssetPack(int token, @Nullable AssetPackManifest manifest, int targetDirectoryIndex) { this.token = token; this.manifest = manifest; + this.targetDirectoryIndex = targetDirectoryIndex; } public AssetEditorCreateAssetPack(@Nonnull AssetEditorCreateAssetPack other) { this.token = other.token; this.manifest = other.manifest; + this.targetDirectoryIndex = other.targetDirectoryIndex; } @Nonnull public static AssetEditorCreateAssetPack deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorCreateAssetPack obj = new AssetEditorCreateAssetPack(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.manifest = AssetPackManifest.deserialize(buf, pos); - pos += AssetPackManifest.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AssetEditorCreateAssetPack", 9, buf.readableBytes() - offset); + } else { + AssetEditorCreateAssetPack obj = new AssetEditorCreateAssetPack(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + obj.targetDirectoryIndex = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + obj.manifest = AssetPackManifest.deserialize(buf, pos); + pos += AssetPackManifest.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int pos = offset + 5; + int pos = offset + 9; if ((nullBits & 1) != 0) { pos += AssetPackManifest.computeBytesConsumed(buf, pos); } @@ -77,6 +86,7 @@ public class AssetEditorCreateAssetPack implements Packet, ToServerPacket { buf.writeByte(nullBits); buf.writeIntLE(this.token); + buf.writeIntLE(this.targetDirectoryIndex); if (this.manifest != null) { this.manifest.serialize(buf); } @@ -84,7 +94,7 @@ public class AssetEditorCreateAssetPack implements Packet, ToServerPacket { @Override public int computeSize() { - int size = 5; + int size = 9; if (this.manifest != null) { size += this.manifest.computeSize(); } @@ -93,11 +103,11 @@ public class AssetEditorCreateAssetPack implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 5) { - return ValidationResult.error("Buffer too small: expected at least 5 bytes"); + if (buffer.readableBytes() - offset < 9) { + return ValidationResult.error("Buffer too small: expected at least 9 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 5; + int pos = offset + 9; if ((nullBits & 1) != 0) { ValidationResult manifestResult = AssetPackManifest.validateStructure(buffer, pos); if (!manifestResult.isValid()) { @@ -115,6 +125,7 @@ public class AssetEditorCreateAssetPack implements Packet, ToServerPacket { AssetEditorCreateAssetPack copy = new AssetEditorCreateAssetPack(); copy.token = this.token; copy.manifest = this.manifest != null ? this.manifest.clone() : null; + copy.targetDirectoryIndex = this.targetDirectoryIndex; return copy; } @@ -123,12 +134,14 @@ public class AssetEditorCreateAssetPack implements Packet, ToServerPacket { if (this == obj) { return true; } else { - return !(obj instanceof AssetEditorCreateAssetPack other) ? false : this.token == other.token && Objects.equals(this.manifest, other.manifest); + return !(obj instanceof AssetEditorCreateAssetPack other) + ? false + : this.token == other.token && Objects.equals(this.manifest, other.manifest) && this.targetDirectoryIndex == other.targetDirectoryIndex; } } @Override public int hashCode() { - return Objects.hash(this.token, this.manifest); + return Objects.hash(this.token, this.manifest, this.targetDirectoryIndex); } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateDirectory.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateDirectory.java index 6aa5c436..b32d63cf 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateDirectory.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorCreateDirectory.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -46,16 +47,20 @@ public class AssetEditorCreateDirectory implements Packet, ToServerPacket { @Nonnull public static AssetEditorCreateDirectory deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorCreateDirectory obj = new AssetEditorCreateDirectory(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorCreateDirectory", 5, buf.readableBytes() - offset); + } else { + AssetEditorCreateDirectory obj = new AssetEditorCreateDirectory(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteAsset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteAsset.java index 3a3930fe..8b55c076 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteAsset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteAsset.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -46,16 +47,20 @@ public class AssetEditorDeleteAsset implements Packet, ToServerPacket { @Nonnull public static AssetEditorDeleteAsset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorDeleteAsset obj = new AssetEditorDeleteAsset(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorDeleteAsset", 5, buf.readableBytes() - offset); + } else { + AssetEditorDeleteAsset obj = new AssetEditorDeleteAsset(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteAssetPack.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteAssetPack.java index 1fa57061..8d595f56 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteAssetPack.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteAssetPack.java @@ -47,25 +47,33 @@ public class AssetEditorDeleteAssetPack implements Packet, ToServerPacket, ToCli @Nonnull public static AssetEditorDeleteAssetPack deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorDeleteAssetPack obj = new AssetEditorDeleteAssetPack(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int idLen = VarInt.peek(buf, pos); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorDeleteAssetPack", 1, buf.readableBytes() - offset); + } else { + AssetEditorDeleteAssetPack obj = new AssetEditorDeleteAssetPack(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int idLen = VarInt.peek(buf, pos); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (pos + idVarLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", pos + idVarLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += idVarLen + idLen; } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - int idVarLen = VarInt.length(buf, pos); - obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += idVarLen + idLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,7 +81,7 @@ public class AssetEditorDeleteAssetPack implements Packet, ToServerPacket, ToCli int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -118,7 +126,7 @@ public class AssetEditorDeleteAssetPack implements Packet, ToServerPacket, ToCli return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteDirectory.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteDirectory.java index d15087f9..3ef7fe9f 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteDirectory.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDeleteDirectory.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -46,16 +47,20 @@ public class AssetEditorDeleteDirectory implements Packet, ToServerPacket { @Nonnull public static AssetEditorDeleteDirectory deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorDeleteDirectory obj = new AssetEditorDeleteDirectory(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorDeleteDirectory", 5, buf.readableBytes() - offset); + } else { + AssetEditorDeleteDirectory obj = new AssetEditorDeleteDirectory(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDiscardChanges.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDiscardChanges.java index 919db322..0029baba 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDiscardChanges.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorDiscardChanges.java @@ -45,34 +45,38 @@ public class AssetEditorDiscardChanges implements Packet, ToServerPacket { @Nonnull public static AssetEditorDiscardChanges deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorDiscardChanges obj = new AssetEditorDiscardChanges(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int assetsCount = VarInt.peek(buf, pos); - if (assetsCount < 0) { - throw ProtocolException.negativeLength("Assets", assetsCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorDiscardChanges", 1, buf.readableBytes() - offset); + } else { + AssetEditorDiscardChanges obj = new AssetEditorDiscardChanges(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int assetsCount = VarInt.peek(buf, pos); + if (assetsCount < 0) { + throw ProtocolException.invalidVarInt("Assets"); + } + + int assetsVarLen = VarInt.size(assetsCount); + if (assetsCount > 4096000) { + throw ProtocolException.arrayTooLong("Assets", assetsCount, 4096000); + } + + if (pos + assetsVarLen + assetsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Assets", pos + assetsVarLen + assetsCount * 1, buf.readableBytes()); + } + + pos += assetsVarLen; + obj.assets = new TimestampedAssetReference[assetsCount]; + + for (int i = 0; i < assetsCount; i++) { + obj.assets[i] = TimestampedAssetReference.deserialize(buf, pos); + pos += TimestampedAssetReference.computeBytesConsumed(buf, pos); + } } - if (assetsCount > 4096000) { - throw ProtocolException.arrayTooLong("Assets", assetsCount, 4096000); - } - - int assetsVarLen = VarInt.size(assetsCount); - if (pos + assetsVarLen + assetsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Assets", pos + assetsVarLen + assetsCount * 1, buf.readableBytes()); - } - - pos += assetsVarLen; - obj.assets = new TimestampedAssetReference[assetsCount]; - - for (int i = 0; i < assetsCount; i++) { - obj.assets[i] = TimestampedAssetReference.deserialize(buf, pos); - pos += TimestampedAssetReference.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class AssetEditorDiscardChanges implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += TimestampedAssetReference.computeBytesConsumed(buf, pos); @@ -143,7 +147,7 @@ public class AssetEditorDiscardChanges implements Packet, ToServerPacket { return ValidationResult.error("Assets exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetsCount); for (int i = 0; i < assetsCount; i++) { ValidationResult structResult = TimestampedAssetReference.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssetInitialize.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssetInitialize.java index 809dbf40..709fcf60 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssetInitialize.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssetInitialize.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -53,21 +54,35 @@ public class AssetEditorExportAssetInitialize implements Packet, ToClientPacket @Nonnull public static AssetEditorExportAssetInitialize deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorExportAssetInitialize obj = new AssetEditorExportAssetInitialize(); - byte nullBits = buf.getByte(offset); - obj.size = buf.getIntLE(offset + 1); - obj.failed = buf.getByte(offset + 5) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 14 + buf.getIntLE(offset + 6); - obj.asset = AssetEditorAsset.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("AssetEditorExportAssetInitialize", 14, buf.readableBytes() - offset); + } else { + AssetEditorExportAssetInitialize obj = new AssetEditorExportAssetInitialize(); + byte nullBits = buf.getByte(offset); + obj.size = buf.getIntLE(offset + 1); + obj.failed = buf.getByte(offset + 5) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Asset", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 14 + buf.getIntLE(offset + 10); - obj.oldPath = AssetPath.deserialize(buf, varPos1); - } + int varPos0 = offset + 14 + varPosBase0; + obj.asset = AssetEditorAsset.deserialize(buf, varPos0); + } - return obj; + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 10); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("OldPath", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 14 + varPosBase1; + obj.oldPath = AssetPath.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -75,6 +90,10 @@ public class AssetEditorExportAssetInitialize implements Packet, ToClientPacket int maxEnd = 14; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Asset", fieldOffset0, maxEnd); + } + int pos0 = offset + 14 + fieldOffset0; pos0 += AssetEditorAsset.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -84,6 +103,10 @@ public class AssetEditorExportAssetInitialize implements Packet, ToClientPacket if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 10); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("OldPath", fieldOffset1, maxEnd); + } + int pos1 = offset + 14 + fieldOffset1; pos1 += AssetPath.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -150,15 +173,11 @@ public class AssetEditorExportAssetInitialize implements Packet, ToClientPacket byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int assetOffset = buffer.getIntLE(offset + 6); - if (assetOffset < 0) { + if (assetOffset < 0 || assetOffset > buffer.writerIndex() - offset - 14) { return ValidationResult.error("Invalid offset for Asset"); } int pos = offset + 14 + assetOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Asset"); - } - ValidationResult assetResult = AssetEditorAsset.validateStructure(buffer, pos); if (!assetResult.isValid()) { return ValidationResult.error("Invalid Asset: " + assetResult.error()); @@ -169,21 +188,17 @@ public class AssetEditorExportAssetInitialize implements Packet, ToClientPacket if ((nullBits & 2) != 0) { int oldPathOffset = buffer.getIntLE(offset + 10); - if (oldPathOffset < 0) { + if (oldPathOffset < 0 || oldPathOffset > buffer.writerIndex() - offset - 14) { return ValidationResult.error("Invalid offset for OldPath"); } - int posx = offset + 14 + oldPathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for OldPath"); - } - - ValidationResult oldPathResult = AssetPath.validateStructure(buffer, posx); + int pos = offset + 14 + oldPathOffset; + ValidationResult oldPathResult = AssetPath.validateStructure(buffer, pos); if (!oldPathResult.isValid()) { return ValidationResult.error("Invalid OldPath: " + oldPathResult.error()); } - posx += AssetPath.computeBytesConsumed(buffer, posx); + pos += AssetPath.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssetPart.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssetPart.java index fcf9cbfa..dda8afa3 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssetPart.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssetPart.java @@ -45,35 +45,39 @@ public class AssetEditorExportAssetPart implements Packet, ToClientPacket { @Nonnull public static AssetEditorExportAssetPart deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorExportAssetPart obj = new AssetEditorExportAssetPart(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int partCount = VarInt.peek(buf, pos); - if (partCount < 0) { - throw ProtocolException.negativeLength("Part", partCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorExportAssetPart", 1, buf.readableBytes() - offset); + } else { + AssetEditorExportAssetPart obj = new AssetEditorExportAssetPart(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int partCount = VarInt.peek(buf, pos); + if (partCount < 0) { + throw ProtocolException.invalidVarInt("Part"); + } + + int partVarLen = VarInt.size(partCount); + if (partCount > 4096000) { + throw ProtocolException.arrayTooLong("Part", partCount, 4096000); + } + + if (pos + partVarLen + partCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Part", pos + partVarLen + partCount * 1, buf.readableBytes()); + } + + pos += partVarLen; + obj.part = new byte[partCount]; + + for (int i = 0; i < partCount; i++) { + obj.part[i] = buf.getByte(pos + i * 1); + } + + pos += partCount * 1; } - if (partCount > 4096000) { - throw ProtocolException.arrayTooLong("Part", partCount, 4096000); - } - - int partVarLen = VarInt.size(partCount); - if (pos + partVarLen + partCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Part", pos + partVarLen + partCount * 1, buf.readableBytes()); - } - - pos += partVarLen; - obj.part = new byte[partCount]; - - for (int i = 0; i < partCount; i++) { - obj.part[i] = buf.getByte(pos + i * 1); - } - - pos += partCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,7 +85,7 @@ public class AssetEditorExportAssetPart implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -134,7 +138,7 @@ public class AssetEditorExportAssetPart implements Packet, ToClientPacket { return ValidationResult.error("Part exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(partCount); pos += partCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Part"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssets.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssets.java index d920088a..6fb5f852 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssets.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportAssets.java @@ -45,34 +45,38 @@ public class AssetEditorExportAssets implements Packet, ToServerPacket { @Nonnull public static AssetEditorExportAssets deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorExportAssets obj = new AssetEditorExportAssets(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int pathsCount = VarInt.peek(buf, pos); - if (pathsCount < 0) { - throw ProtocolException.negativeLength("Paths", pathsCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorExportAssets", 1, buf.readableBytes() - offset); + } else { + AssetEditorExportAssets obj = new AssetEditorExportAssets(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int pathsCount = VarInt.peek(buf, pos); + if (pathsCount < 0) { + throw ProtocolException.invalidVarInt("Paths"); + } + + int pathsVarLen = VarInt.size(pathsCount); + if (pathsCount > 4096000) { + throw ProtocolException.arrayTooLong("Paths", pathsCount, 4096000); + } + + if (pos + pathsVarLen + pathsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Paths", pos + pathsVarLen + pathsCount * 1, buf.readableBytes()); + } + + pos += pathsVarLen; + obj.paths = new AssetPath[pathsCount]; + + for (int i = 0; i < pathsCount; i++) { + obj.paths[i] = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } } - if (pathsCount > 4096000) { - throw ProtocolException.arrayTooLong("Paths", pathsCount, 4096000); - } - - int pathsVarLen = VarInt.size(pathsCount); - if (pos + pathsVarLen + pathsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Paths", pos + pathsVarLen + pathsCount * 1, buf.readableBytes()); - } - - pos += pathsVarLen; - obj.paths = new AssetPath[pathsCount]; - - for (int i = 0; i < pathsCount; i++) { - obj.paths[i] = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class AssetEditorExportAssets implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += AssetPath.computeBytesConsumed(buf, pos); @@ -143,7 +147,7 @@ public class AssetEditorExportAssets implements Packet, ToServerPacket { return ValidationResult.error("Paths exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(pathsCount); for (int i = 0; i < pathsCount; i++) { ValidationResult structResult = AssetPath.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportComplete.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportComplete.java index eddb8431..ff374e78 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportComplete.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportComplete.java @@ -45,34 +45,38 @@ public class AssetEditorExportComplete implements Packet, ToClientPacket { @Nonnull public static AssetEditorExportComplete deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorExportComplete obj = new AssetEditorExportComplete(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int assetsCount = VarInt.peek(buf, pos); - if (assetsCount < 0) { - throw ProtocolException.negativeLength("Assets", assetsCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorExportComplete", 1, buf.readableBytes() - offset); + } else { + AssetEditorExportComplete obj = new AssetEditorExportComplete(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int assetsCount = VarInt.peek(buf, pos); + if (assetsCount < 0) { + throw ProtocolException.invalidVarInt("Assets"); + } + + int assetsVarLen = VarInt.size(assetsCount); + if (assetsCount > 4096000) { + throw ProtocolException.arrayTooLong("Assets", assetsCount, 4096000); + } + + if (pos + assetsVarLen + assetsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Assets", pos + assetsVarLen + assetsCount * 1, buf.readableBytes()); + } + + pos += assetsVarLen; + obj.assets = new TimestampedAssetReference[assetsCount]; + + for (int i = 0; i < assetsCount; i++) { + obj.assets[i] = TimestampedAssetReference.deserialize(buf, pos); + pos += TimestampedAssetReference.computeBytesConsumed(buf, pos); + } } - if (assetsCount > 4096000) { - throw ProtocolException.arrayTooLong("Assets", assetsCount, 4096000); - } - - int assetsVarLen = VarInt.size(assetsCount); - if (pos + assetsVarLen + assetsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Assets", pos + assetsVarLen + assetsCount * 1, buf.readableBytes()); - } - - pos += assetsVarLen; - obj.assets = new TimestampedAssetReference[assetsCount]; - - for (int i = 0; i < assetsCount; i++) { - obj.assets[i] = TimestampedAssetReference.deserialize(buf, pos); - pos += TimestampedAssetReference.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class AssetEditorExportComplete implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += TimestampedAssetReference.computeBytesConsumed(buf, pos); @@ -143,7 +147,7 @@ public class AssetEditorExportComplete implements Packet, ToClientPacket { return ValidationResult.error("Assets exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetsCount); for (int i = 0; i < assetsCount; i++) { ValidationResult structResult = TimestampedAssetReference.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportDeleteAssets.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportDeleteAssets.java index ca60537c..621edb62 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportDeleteAssets.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorExportDeleteAssets.java @@ -45,34 +45,38 @@ public class AssetEditorExportDeleteAssets implements Packet, ToClientPacket { @Nonnull public static AssetEditorExportDeleteAssets deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorExportDeleteAssets obj = new AssetEditorExportDeleteAssets(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int assetCount = VarInt.peek(buf, pos); - if (assetCount < 0) { - throw ProtocolException.negativeLength("Asset", assetCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorExportDeleteAssets", 1, buf.readableBytes() - offset); + } else { + AssetEditorExportDeleteAssets obj = new AssetEditorExportDeleteAssets(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int assetCount = VarInt.peek(buf, pos); + if (assetCount < 0) { + throw ProtocolException.invalidVarInt("Asset"); + } + + int assetVarLen = VarInt.size(assetCount); + if (assetCount > 4096000) { + throw ProtocolException.arrayTooLong("Asset", assetCount, 4096000); + } + + if (pos + assetVarLen + assetCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Asset", pos + assetVarLen + assetCount * 1, buf.readableBytes()); + } + + pos += assetVarLen; + obj.asset = new AssetEditorAsset[assetCount]; + + for (int i = 0; i < assetCount; i++) { + obj.asset[i] = AssetEditorAsset.deserialize(buf, pos); + pos += AssetEditorAsset.computeBytesConsumed(buf, pos); + } } - if (assetCount > 4096000) { - throw ProtocolException.arrayTooLong("Asset", assetCount, 4096000); - } - - int assetVarLen = VarInt.size(assetCount); - if (pos + assetVarLen + assetCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Asset", pos + assetVarLen + assetCount * 1, buf.readableBytes()); - } - - pos += assetVarLen; - obj.asset = new AssetEditorAsset[assetCount]; - - for (int i = 0; i < assetCount; i++) { - obj.asset[i] = AssetEditorAsset.deserialize(buf, pos); - pos += AssetEditorAsset.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class AssetEditorExportDeleteAssets implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += AssetEditorAsset.computeBytesConsumed(buf, pos); @@ -143,7 +147,7 @@ public class AssetEditorExportDeleteAssets implements Packet, ToClientPacket { return ValidationResult.error("Asset exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetCount); for (int i = 0; i < assetCount; i++) { ValidationResult structResult = AssetEditorAsset.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAsset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAsset.java index 5889a915..22c6f766 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAsset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAsset.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -49,17 +50,21 @@ public class AssetEditorFetchAsset implements Packet, ToServerPacket { @Nonnull public static AssetEditorFetchAsset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorFetchAsset obj = new AssetEditorFetchAsset(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - obj.isFromOpenedTab = buf.getByte(offset + 5) != 0; - int pos = offset + 6; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("AssetEditorFetchAsset", 6, buf.readableBytes() - offset); + } else { + AssetEditorFetchAsset obj = new AssetEditorFetchAsset(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + obj.isFromOpenedTab = buf.getByte(offset + 5) != 0; + int pos = offset + 6; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAssetReply.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAssetReply.java index 35bfaae7..5afa8a05 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAssetReply.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAssetReply.java @@ -48,36 +48,40 @@ public class AssetEditorFetchAssetReply implements Packet, ToClientPacket { @Nonnull public static AssetEditorFetchAssetReply deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorFetchAssetReply obj = new AssetEditorFetchAssetReply(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int contentsCount = VarInt.peek(buf, pos); - if (contentsCount < 0) { - throw ProtocolException.negativeLength("Contents", contentsCount); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorFetchAssetReply", 5, buf.readableBytes() - offset); + } else { + AssetEditorFetchAssetReply obj = new AssetEditorFetchAssetReply(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int contentsCount = VarInt.peek(buf, pos); + if (contentsCount < 0) { + throw ProtocolException.invalidVarInt("Contents"); + } + + int contentsVarLen = VarInt.size(contentsCount); + if (contentsCount > 4096000) { + throw ProtocolException.arrayTooLong("Contents", contentsCount, 4096000); + } + + if (pos + contentsVarLen + contentsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Contents", pos + contentsVarLen + contentsCount * 1, buf.readableBytes()); + } + + pos += contentsVarLen; + obj.contents = new byte[contentsCount]; + + for (int i = 0; i < contentsCount; i++) { + obj.contents[i] = buf.getByte(pos + i * 1); + } + + pos += contentsCount * 1; } - if (contentsCount > 4096000) { - throw ProtocolException.arrayTooLong("Contents", contentsCount, 4096000); - } - - int contentsVarLen = VarInt.size(contentsCount); - if (pos + contentsVarLen + contentsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Contents", pos + contentsVarLen + contentsCount * 1, buf.readableBytes()); - } - - pos += contentsVarLen; - obj.contents = new byte[contentsCount]; - - for (int i = 0; i < contentsCount; i++) { - obj.contents[i] = buf.getByte(pos + i * 1); - } - - pos += contentsCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -85,7 +89,7 @@ public class AssetEditorFetchAssetReply implements Packet, ToClientPacket { int pos = offset + 5; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -139,7 +143,7 @@ public class AssetEditorFetchAssetReply implements Packet, ToClientPacket { return ValidationResult.error("Contents exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(contentsCount); pos += contentsCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Contents"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAutoCompleteData.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAutoCompleteData.java index de412387..0dc315b7 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAutoCompleteData.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAutoCompleteData.java @@ -53,38 +53,62 @@ public class AssetEditorFetchAutoCompleteData implements Packet, ToServerPacket @Nonnull public static AssetEditorFetchAutoCompleteData deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorFetchAutoCompleteData obj = new AssetEditorFetchAutoCompleteData(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 5); - int datasetLen = VarInt.peek(buf, varPos0); - if (datasetLen < 0) { - throw ProtocolException.negativeLength("Dataset", datasetLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("AssetEditorFetchAutoCompleteData", 13, buf.readableBytes() - offset); + } else { + AssetEditorFetchAutoCompleteData obj = new AssetEditorFetchAutoCompleteData(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Dataset", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int datasetLen = VarInt.peek(buf, varPos0); + if (datasetLen < 0) { + throw ProtocolException.invalidVarInt("Dataset"); + } + + int datasetVarIntLen = VarInt.size(datasetLen); + if (datasetLen > 4096000) { + throw ProtocolException.stringTooLong("Dataset", datasetLen, 4096000); + } + + if (varPos0 + datasetVarIntLen + datasetLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Dataset", varPos0 + datasetVarIntLen + datasetLen, buf.readableBytes()); + } + + obj.dataset = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (datasetLen > 4096000) { - throw ProtocolException.stringTooLong("Dataset", datasetLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Query", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int queryLen = VarInt.peek(buf, varPos1); + if (queryLen < 0) { + throw ProtocolException.invalidVarInt("Query"); + } + + int queryVarIntLen = VarInt.size(queryLen); + if (queryLen > 4096000) { + throw ProtocolException.stringTooLong("Query", queryLen, 4096000); + } + + if (varPos1 + queryVarIntLen + queryLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Query", varPos1 + queryVarIntLen + queryLen, buf.readableBytes()); + } + + obj.query = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.dataset = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 9); - int queryLen = VarInt.peek(buf, varPos1); - if (queryLen < 0) { - throw ProtocolException.negativeLength("Query", queryLen); - } - - if (queryLen > 4096000) { - throw ProtocolException.stringTooLong("Query", queryLen, 4096000); - } - - obj.query = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -92,9 +116,13 @@ public class AssetEditorFetchAutoCompleteData implements Packet, ToServerPacket int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Dataset", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -102,9 +130,13 @@ public class AssetEditorFetchAutoCompleteData implements Packet, ToServerPacket if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Query", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -168,15 +200,11 @@ public class AssetEditorFetchAutoCompleteData implements Packet, ToServerPacket byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int datasetOffset = buffer.getIntLE(offset + 5); - if (datasetOffset < 0) { + if (datasetOffset < 0 || datasetOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Dataset"); } int pos = offset + 13 + datasetOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Dataset"); - } - int datasetLen = VarInt.peek(buffer, pos); if (datasetLen < 0) { return ValidationResult.error("Invalid string length for Dataset"); @@ -186,7 +214,7 @@ public class AssetEditorFetchAutoCompleteData implements Packet, ToServerPacket return ValidationResult.error("Dataset exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(datasetLen); pos += datasetLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Dataset"); @@ -195,15 +223,11 @@ public class AssetEditorFetchAutoCompleteData implements Packet, ToServerPacket if ((nullBits & 2) != 0) { int queryOffset = buffer.getIntLE(offset + 9); - if (queryOffset < 0) { + if (queryOffset < 0 || queryOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Query"); } int posx = offset + 13 + queryOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Query"); - } - int queryLen = VarInt.peek(buffer, posx); if (queryLen < 0) { return ValidationResult.error("Invalid string length for Query"); @@ -213,7 +237,7 @@ public class AssetEditorFetchAutoCompleteData implements Packet, ToServerPacket return ValidationResult.error("Query exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(queryLen); posx += queryLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Query"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAutoCompleteDataReply.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAutoCompleteDataReply.java index 95f27b4f..f66eb68d 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAutoCompleteDataReply.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchAutoCompleteDataReply.java @@ -49,45 +49,53 @@ public class AssetEditorFetchAutoCompleteDataReply implements Packet, ToClientPa @Nonnull public static AssetEditorFetchAutoCompleteDataReply deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorFetchAutoCompleteDataReply obj = new AssetEditorFetchAutoCompleteDataReply(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int resultsCount = VarInt.peek(buf, pos); - if (resultsCount < 0) { - throw ProtocolException.negativeLength("Results", resultsCount); - } - - if (resultsCount > 4096000) { - throw ProtocolException.arrayTooLong("Results", resultsCount, 4096000); - } - - int resultsVarLen = VarInt.size(resultsCount); - if (pos + resultsVarLen + resultsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Results", pos + resultsVarLen + resultsCount * 1, buf.readableBytes()); - } - - pos += resultsVarLen; - obj.results = new String[resultsCount]; - - for (int i = 0; i < resultsCount; i++) { - int strLen = VarInt.peek(buf, pos); - if (strLen < 0) { - throw ProtocolException.negativeLength("results[" + i + "]", strLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorFetchAutoCompleteDataReply", 5, buf.readableBytes() - offset); + } else { + AssetEditorFetchAutoCompleteDataReply obj = new AssetEditorFetchAutoCompleteDataReply(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int resultsCount = VarInt.peek(buf, pos); + if (resultsCount < 0) { + throw ProtocolException.invalidVarInt("Results"); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("results[" + i + "]", strLen, 4096000); + int resultsVarLen = VarInt.size(resultsCount); + if (resultsCount > 4096000) { + throw ProtocolException.arrayTooLong("Results", resultsCount, 4096000); } - int strVarLen = VarInt.length(buf, pos); - obj.results[i] = PacketIO.readVarString(buf, pos); - pos += strVarLen + strLen; + if (pos + resultsVarLen + resultsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Results", pos + resultsVarLen + resultsCount * 1, buf.readableBytes()); + } + + pos += resultsVarLen; + obj.results = new String[resultsCount]; + + for (int i = 0; i < resultsCount; i++) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("results[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("results[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("results[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.results[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -95,11 +103,11 @@ public class AssetEditorFetchAutoCompleteDataReply implements Packet, ToClientPa int pos = offset + 5; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -160,7 +168,7 @@ public class AssetEditorFetchAutoCompleteDataReply implements Packet, ToClientPa return ValidationResult.error("Results exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(resultsCount); for (int i = 0; i < resultsCount; i++) { int strLen = VarInt.peek(buffer, pos); @@ -168,7 +176,7 @@ public class AssetEditorFetchAutoCompleteDataReply implements Packet, ToClientPa return ValidationResult.error("Invalid string length in Results"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(strLen); pos += strLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Results"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchJsonAssetWithParents.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchJsonAssetWithParents.java index 61a54f68..c5cbb3b6 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchJsonAssetWithParents.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchJsonAssetWithParents.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -49,17 +50,21 @@ public class AssetEditorFetchJsonAssetWithParents implements Packet, ToServerPac @Nonnull public static AssetEditorFetchJsonAssetWithParents deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorFetchJsonAssetWithParents obj = new AssetEditorFetchJsonAssetWithParents(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - obj.isFromOpenedTab = buf.getByte(offset + 5) != 0; - int pos = offset + 6; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("AssetEditorFetchJsonAssetWithParents", 6, buf.readableBytes() - offset); + } else { + AssetEditorFetchJsonAssetWithParents obj = new AssetEditorFetchJsonAssetWithParents(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + obj.isFromOpenedTab = buf.getByte(offset + 5) != 0; + int pos = offset + 6; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchJsonAssetWithParentsReply.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchJsonAssetWithParentsReply.java index 3052b8c5..21598fbe 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchJsonAssetWithParentsReply.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFetchJsonAssetWithParentsReply.java @@ -52,45 +52,54 @@ public class AssetEditorFetchJsonAssetWithParentsReply implements Packet, ToClie @Nonnull public static AssetEditorFetchJsonAssetWithParentsReply deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorFetchJsonAssetWithParentsReply obj = new AssetEditorFetchJsonAssetWithParentsReply(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int assetsCount = VarInt.peek(buf, pos); - if (assetsCount < 0) { - throw ProtocolException.negativeLength("Assets", assetsCount); - } - - if (assetsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Assets", assetsCount, 4096000); - } - - pos += VarInt.size(assetsCount); - obj.assets = new HashMap<>(assetsCount); - - for (int i = 0; i < assetsCount; i++) { - AssetPath key = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - int valLen = VarInt.peek(buf, pos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorFetchJsonAssetWithParentsReply", 5, buf.readableBytes() - offset); + } else { + AssetEditorFetchJsonAssetWithParentsReply obj = new AssetEditorFetchJsonAssetWithParentsReply(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int assetsCount = VarInt.peek(buf, pos); + if (assetsCount < 0) { + throw ProtocolException.invalidVarInt("Assets"); } - if (valLen > 4096000) { - throw ProtocolException.stringTooLong("val", valLen, 4096000); + int assetsVarLen = VarInt.size(assetsCount); + if (assetsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Assets", assetsCount, 4096000); } - int valVarLen = VarInt.length(buf, pos); - String val = PacketIO.readVarString(buf, pos); - pos += valVarLen + valLen; - if (obj.assets.put(key, val) != null) { - throw ProtocolException.duplicateKey("assets", key); + pos += assetsVarLen; + obj.assets = new HashMap<>(assetsCount); + + for (int i = 0; i < assetsCount; i++) { + AssetPath key = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + int valLen = VarInt.peek(buf, pos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 4096000) { + throw ProtocolException.stringTooLong("val", valLen, 4096000); + } + + if (pos + valVarLen + valLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", pos + valVarLen + valLen, buf.readableBytes()); + } + + String val = PacketIO.readVarString(buf, pos); + pos += valVarLen + valLen; + if (obj.assets.put(key, val) != null) { + throw ProtocolException.duplicateKey("assets", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -98,12 +107,12 @@ public class AssetEditorFetchJsonAssetWithParentsReply implements Packet, ToClie int pos = offset + 5; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += AssetPath.computeBytesConsumed(buf, pos); int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -165,7 +174,7 @@ public class AssetEditorFetchJsonAssetWithParentsReply implements Packet, ToClie return ValidationResult.error("Assets exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetsCount); for (int i = 0; i < assetsCount; i++) { pos += AssetPath.computeBytesConsumed(buffer, pos); @@ -178,7 +187,7 @@ public class AssetEditorFetchJsonAssetWithParentsReply implements Packet, ToClie return ValidationResult.error("value exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(valueLen); pos += valueLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFileEntry.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFileEntry.java index 5896de8a..4abaeaaa 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFileEntry.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorFileEntry.java @@ -34,26 +34,34 @@ public class AssetEditorFileEntry { @Nonnull public static AssetEditorFileEntry deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorFileEntry obj = new AssetEditorFileEntry(); - byte nullBits = buf.getByte(offset); - obj.isDirectory = buf.getByte(offset + 1) != 0; - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int pathLen = VarInt.peek(buf, pos); - if (pathLen < 0) { - throw ProtocolException.negativeLength("Path", pathLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("AssetEditorFileEntry", 2, buf.readableBytes() - offset); + } else { + AssetEditorFileEntry obj = new AssetEditorFileEntry(); + byte nullBits = buf.getByte(offset); + obj.isDirectory = buf.getByte(offset + 1) != 0; + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int pathLen = VarInt.peek(buf, pos); + if (pathLen < 0) { + throw ProtocolException.invalidVarInt("Path"); + } + + int pathVarLen = VarInt.size(pathLen); + if (pathLen > 4096000) { + throw ProtocolException.stringTooLong("Path", pathLen, 4096000); + } + + if (pos + pathVarLen + pathLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Path", pos + pathVarLen + pathLen, buf.readableBytes()); + } + + obj.path = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += pathVarLen + pathLen; } - if (pathLen > 4096000) { - throw ProtocolException.stringTooLong("Path", pathLen, 4096000); - } - - int pathVarLen = VarInt.length(buf, pos); - obj.path = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += pathVarLen + pathLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,7 +69,7 @@ public class AssetEditorFileEntry { int pos = offset + 2; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -105,7 +113,7 @@ public class AssetEditorFileEntry { return ValidationResult.error("Path exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(pathLen); pos += pathLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Path"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorJsonAssetUpdated.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorJsonAssetUpdated.java index a7b93808..d3657a3b 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorJsonAssetUpdated.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorJsonAssetUpdated.java @@ -50,39 +50,53 @@ public class AssetEditorJsonAssetUpdated implements Packet, ToClientPacket { @Nonnull public static AssetEditorJsonAssetUpdated deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorJsonAssetUpdated obj = new AssetEditorJsonAssetUpdated(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - obj.path = AssetPath.deserialize(buf, varPos0); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AssetEditorJsonAssetUpdated", 9, buf.readableBytes() - offset); + } else { + AssetEditorJsonAssetUpdated obj = new AssetEditorJsonAssetUpdated(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + obj.path = AssetPath.deserialize(buf, varPos0); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Commands", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int commandsCount = VarInt.peek(buf, varPos1); + if (commandsCount < 0) { + throw ProtocolException.invalidVarInt("Commands"); + } + + int varIntLen = VarInt.size(commandsCount); + if (commandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); + } + + if (varPos1 + varIntLen + commandsCount * 7L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Commands", varPos1 + varIntLen + commandsCount * 7, buf.readableBytes()); + } + + obj.commands = new JsonUpdateCommand[commandsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < commandsCount; i++) { + obj.commands[i] = JsonUpdateCommand.deserialize(buf, elemPos); + elemPos += JsonUpdateCommand.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int commandsCount = VarInt.peek(buf, varPos1); - if (commandsCount < 0) { - throw ProtocolException.negativeLength("Commands", commandsCount); - } - - if (commandsCount > 4096000) { - throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + commandsCount * 7L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Commands", varPos1 + varIntLen + commandsCount * 7, buf.readableBytes()); - } - - obj.commands = new JsonUpdateCommand[commandsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < commandsCount; i++) { - obj.commands[i] = JsonUpdateCommand.deserialize(buf, elemPos); - elemPos += JsonUpdateCommand.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,6 +104,10 @@ public class AssetEditorJsonAssetUpdated implements Packet, ToClientPacket { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -99,9 +117,13 @@ public class AssetEditorJsonAssetUpdated implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Commands", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += JsonUpdateCommand.computeBytesConsumed(buf, pos1); @@ -183,15 +205,11 @@ public class AssetEditorJsonAssetUpdated implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int pathOffset = buffer.getIntLE(offset + 1); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Path"); } int pos = offset + 9 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, pos); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -202,16 +220,12 @@ public class AssetEditorJsonAssetUpdated implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int commandsOffset = buffer.getIntLE(offset + 5); - if (commandsOffset < 0) { + if (commandsOffset < 0 || commandsOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Commands"); } - int posx = offset + 9 + commandsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Commands"); - } - - int commandsCount = VarInt.peek(buffer, posx); + int pos = offset + 9 + commandsOffset; + int commandsCount = VarInt.peek(buffer, pos); if (commandsCount < 0) { return ValidationResult.error("Invalid array count for Commands"); } @@ -220,15 +234,15 @@ public class AssetEditorJsonAssetUpdated implements Packet, ToClientPacket { return ValidationResult.error("Commands exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + pos += VarInt.size(commandsCount); for (int i = 0; i < commandsCount; i++) { - ValidationResult structResult = JsonUpdateCommand.validateStructure(buffer, posx); + ValidationResult structResult = JsonUpdateCommand.validateStructure(buffer, pos); if (!structResult.isValid()) { return ValidationResult.error("Invalid JsonUpdateCommand in Commands[" + i + "]: " + structResult.error()); } - posx += JsonUpdateCommand.computeBytesConsumed(buffer, posx); + pos += JsonUpdateCommand.computeBytesConsumed(buffer, pos); } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorLastModifiedAssets.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorLastModifiedAssets.java index 73c2fb8f..a0522916 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorLastModifiedAssets.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorLastModifiedAssets.java @@ -45,34 +45,38 @@ public class AssetEditorLastModifiedAssets implements Packet, ToClientPacket { @Nonnull public static AssetEditorLastModifiedAssets deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorLastModifiedAssets obj = new AssetEditorLastModifiedAssets(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int assetsCount = VarInt.peek(buf, pos); - if (assetsCount < 0) { - throw ProtocolException.negativeLength("Assets", assetsCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorLastModifiedAssets", 1, buf.readableBytes() - offset); + } else { + AssetEditorLastModifiedAssets obj = new AssetEditorLastModifiedAssets(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int assetsCount = VarInt.peek(buf, pos); + if (assetsCount < 0) { + throw ProtocolException.invalidVarInt("Assets"); + } + + int assetsVarLen = VarInt.size(assetsCount); + if (assetsCount > 4096000) { + throw ProtocolException.arrayTooLong("Assets", assetsCount, 4096000); + } + + if (pos + assetsVarLen + assetsCount * 11L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Assets", pos + assetsVarLen + assetsCount * 11, buf.readableBytes()); + } + + pos += assetsVarLen; + obj.assets = new AssetInfo[assetsCount]; + + for (int i = 0; i < assetsCount; i++) { + obj.assets[i] = AssetInfo.deserialize(buf, pos); + pos += AssetInfo.computeBytesConsumed(buf, pos); + } } - if (assetsCount > 4096000) { - throw ProtocolException.arrayTooLong("Assets", assetsCount, 4096000); - } - - int assetsVarLen = VarInt.size(assetsCount); - if (pos + assetsVarLen + assetsCount * 11L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Assets", pos + assetsVarLen + assetsCount * 11, buf.readableBytes()); - } - - pos += assetsVarLen; - obj.assets = new AssetInfo[assetsCount]; - - for (int i = 0; i < assetsCount; i++) { - obj.assets[i] = AssetInfo.deserialize(buf, pos); - pos += AssetInfo.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class AssetEditorLastModifiedAssets implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += AssetInfo.computeBytesConsumed(buf, pos); @@ -143,7 +147,7 @@ public class AssetEditorLastModifiedAssets implements Packet, ToClientPacket { return ValidationResult.error("Assets exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetsCount); for (int i = 0; i < assetsCount; i++) { ValidationResult structResult = AssetInfo.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorModifiedAssetsCount.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorModifiedAssetsCount.java index 56b21588..b1970961 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorModifiedAssetsCount.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorModifiedAssetsCount.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class AssetEditorModifiedAssetsCount implements Packet, ToClientPacket { @Nonnull public static AssetEditorModifiedAssetsCount deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorModifiedAssetsCount obj = new AssetEditorModifiedAssetsCount(); - obj.count = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("AssetEditorModifiedAssetsCount", 4, buf.readableBytes() - offset); + } else { + AssetEditorModifiedAssetsCount obj = new AssetEditorModifiedAssetsCount(); + obj.count = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorModsDirectories.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorModsDirectories.java new file mode 100644 index 00000000..91558150 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorModsDirectories.java @@ -0,0 +1,206 @@ +package com.hypixel.hytale.protocol.packets.asseteditor; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class AssetEditorModsDirectories implements Packet, ToClientPacket { + public static final int PACKET_ID = 356; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 1; + public static final int MAX_SIZE = 1677721600; + @Nullable + public String[] directories; + + @Override + public int getId() { + return 356; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public AssetEditorModsDirectories() { + } + + public AssetEditorModsDirectories(@Nullable String[] directories) { + this.directories = directories; + } + + public AssetEditorModsDirectories(@Nonnull AssetEditorModsDirectories other) { + this.directories = other.directories; + } + + @Nonnull + public static AssetEditorModsDirectories deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorModsDirectories", 1, buf.readableBytes() - offset); + } else { + AssetEditorModsDirectories obj = new AssetEditorModsDirectories(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int directoriesCount = VarInt.peek(buf, pos); + if (directoriesCount < 0) { + throw ProtocolException.invalidVarInt("Directories"); + } + + int directoriesVarLen = VarInt.size(directoriesCount); + if (directoriesCount > 4096000) { + throw ProtocolException.arrayTooLong("Directories", directoriesCount, 4096000); + } + + if (pos + directoriesVarLen + directoriesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Directories", pos + directoriesVarLen + directoriesCount * 1, buf.readableBytes()); + } + + pos += directoriesVarLen; + obj.directories = new String[directoriesCount]; + + for (int i = 0; i < directoriesCount; i++) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("directories[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("directories[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("directories[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.directories[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int arrLen = VarInt.peek(buf, pos); + pos += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + } + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.directories != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + if (this.directories != null) { + if (this.directories.length > 4096000) { + throw ProtocolException.arrayTooLong("Directories", this.directories.length, 4096000); + } + + VarInt.write(buf, this.directories.length); + + for (String item : this.directories) { + PacketIO.writeVarString(buf, item, 4096000); + } + } + } + + @Override + public int computeSize() { + int size = 1; + if (this.directories != null) { + int directoriesSize = 0; + + for (String elem : this.directories) { + directoriesSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.directories.length) + directoriesSize; + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int directoriesCount = VarInt.peek(buffer, pos); + if (directoriesCount < 0) { + return ValidationResult.error("Invalid array count for Directories"); + } + + if (directoriesCount > 4096000) { + return ValidationResult.error("Directories exceeds max length 4096000"); + } + + pos += VarInt.size(directoriesCount); + + for (int i = 0; i < directoriesCount; i++) { + int strLen = VarInt.peek(buffer, pos); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in Directories"); + } + + pos += VarInt.size(strLen); + pos += strLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in Directories"); + } + } + } + + return ValidationResult.OK; + } + } + + public AssetEditorModsDirectories clone() { + AssetEditorModsDirectories copy = new AssetEditorModsDirectories(); + copy.directories = this.directories != null ? Arrays.copyOf(this.directories, this.directories.length) : null; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof AssetEditorModsDirectories other ? Arrays.equals((Object[])this.directories, (Object[])other.directories) : false; + } + } + + @Override + public int hashCode() { + int result = 1; + return 31 * result + Arrays.hashCode((Object[])this.directories); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorPopupNotification.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorPopupNotification.java index df4d1840..94167fec 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorPopupNotification.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorPopupNotification.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -48,16 +49,20 @@ public class AssetEditorPopupNotification implements Packet, ToClientPacket { @Nonnull public static AssetEditorPopupNotification deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorPopupNotification obj = new AssetEditorPopupNotification(); - byte nullBits = buf.getByte(offset); - obj.type = AssetEditorPopupNotificationType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - obj.message = FormattedMessage.deserialize(buf, pos); - pos += FormattedMessage.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("AssetEditorPopupNotification", 2, buf.readableBytes() - offset); + } else { + AssetEditorPopupNotification obj = new AssetEditorPopupNotification(); + byte nullBits = buf.getByte(offset); + obj.type = AssetEditorPopupNotificationType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + obj.message = FormattedMessage.deserialize(buf, pos); + pos += FormattedMessage.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -99,17 +104,22 @@ public class AssetEditorPopupNotification implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - ValidationResult messageResult = FormattedMessage.validateStructure(buffer, pos); - if (!messageResult.isValid()) { - return ValidationResult.error("Invalid Message: " + messageResult.error()); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid AssetEditorPopupNotificationType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + ValidationResult messageResult = FormattedMessage.validateStructure(buffer, v); + if (!messageResult.isValid()) { + return ValidationResult.error("Invalid Message: " + messageResult.error()); + } + + v += FormattedMessage.computeBytesConsumed(buffer, v); } - pos += FormattedMessage.computeBytesConsumed(buffer, pos); + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorPreviewCameraSettings.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorPreviewCameraSettings.java index 1be1483c..7006856c 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorPreviewCameraSettings.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorPreviewCameraSettings.java @@ -1,28 +1,29 @@ package com.hypixel.hytale.protocol.packets.asseteditor; -import com.hypixel.hytale.protocol.Vector3f; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import org.joml.Vector3fc; public class AssetEditorPreviewCameraSettings { - public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 29; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 28; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 29; - public static final int MAX_SIZE = 29; + public static final int VARIABLE_BLOCK_START = 28; + public static final int MAX_SIZE = 28; public float modelScale; - @Nullable - public Vector3f cameraPosition; - @Nullable - public Vector3f cameraOrientation; + @Nonnull + public Vector3fc cameraPosition = PacketIO.ZERO_VECTOR3; + @Nonnull + public Vector3fc cameraOrientation = PacketIO.ZERO_VECTOR3; public AssetEditorPreviewCameraSettings() { } - public AssetEditorPreviewCameraSettings(float modelScale, @Nullable Vector3f cameraPosition, @Nullable Vector3f cameraOrientation) { + public AssetEditorPreviewCameraSettings(float modelScale, @Nonnull Vector3fc cameraPosition, @Nonnull Vector3fc cameraOrientation) { this.modelScale = modelScale; this.cameraPosition = cameraPosition; this.cameraOrientation = cameraOrientation; @@ -36,62 +37,40 @@ public class AssetEditorPreviewCameraSettings { @Nonnull public static AssetEditorPreviewCameraSettings deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorPreviewCameraSettings obj = new AssetEditorPreviewCameraSettings(); - byte nullBits = buf.getByte(offset); - obj.modelScale = buf.getFloatLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.cameraPosition = Vector3f.deserialize(buf, offset + 5); + if (buf.readableBytes() - offset < 28) { + throw ProtocolException.bufferTooSmall("AssetEditorPreviewCameraSettings", 28, buf.readableBytes() - offset); + } else { + AssetEditorPreviewCameraSettings obj = new AssetEditorPreviewCameraSettings(); + obj.modelScale = buf.getFloatLE(offset + 0); + obj.cameraPosition = PacketIO.readVector3f(buf, offset + 4); + obj.cameraOrientation = PacketIO.readVector3f(buf, offset + 16); + return obj; } - - if ((nullBits & 2) != 0) { - obj.cameraOrientation = Vector3f.deserialize(buf, offset + 17); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 29; + return 28; } public void serialize(@Nonnull ByteBuf buf) { - byte nullBits = 0; - if (this.cameraPosition != null) { - nullBits = (byte)(nullBits | 1); - } - - if (this.cameraOrientation != null) { - nullBits = (byte)(nullBits | 2); - } - - buf.writeByte(nullBits); buf.writeFloatLE(this.modelScale); - if (this.cameraPosition != null) { - this.cameraPosition.serialize(buf); - } else { - buf.writeZero(12); - } - - if (this.cameraOrientation != null) { - this.cameraOrientation.serialize(buf); - } else { - buf.writeZero(12); - } + PacketIO.writeVector3f(buf, this.cameraPosition); + PacketIO.writeVector3f(buf, this.cameraOrientation); } public int computeSize() { - return 29; + return 28; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 29 ? ValidationResult.error("Buffer too small: expected at least 29 bytes") : ValidationResult.OK; + return buffer.readableBytes() - offset < 28 ? ValidationResult.error("Buffer too small: expected at least 28 bytes") : ValidationResult.OK; } public AssetEditorPreviewCameraSettings clone() { AssetEditorPreviewCameraSettings copy = new AssetEditorPreviewCameraSettings(); copy.modelScale = this.modelScale; - copy.cameraPosition = this.cameraPosition != null ? this.cameraPosition.clone() : null; - copy.cameraOrientation = this.cameraOrientation != null ? this.cameraOrientation.clone() : null; + copy.cameraPosition = this.cameraPosition; + copy.cameraOrientation = this.cameraOrientation; return copy; } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRebuildCaches.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRebuildCaches.java index 3f21bfc3..92d613d7 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRebuildCaches.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRebuildCaches.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.asseteditor; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -38,13 +39,17 @@ public class AssetEditorRebuildCaches { @Nonnull public static AssetEditorRebuildCaches deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorRebuildCaches obj = new AssetEditorRebuildCaches(); - obj.blockTextures = buf.getByte(offset + 0) != 0; - obj.models = buf.getByte(offset + 1) != 0; - obj.modelTextures = buf.getByte(offset + 2) != 0; - obj.mapGeometry = buf.getByte(offset + 3) != 0; - obj.itemIcons = buf.getByte(offset + 4) != 0; - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorRebuildCaches", 5, buf.readableBytes() - offset); + } else { + AssetEditorRebuildCaches obj = new AssetEditorRebuildCaches(); + obj.blockTextures = buf.getByte(offset + 0) != 0; + obj.models = buf.getByte(offset + 1) != 0; + obj.modelTextures = buf.getByte(offset + 2) != 0; + obj.mapGeometry = buf.getByte(offset + 3) != 0; + obj.itemIcons = buf.getByte(offset + 4) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRedoChanges.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRedoChanges.java index 1578cd41..938ef004 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRedoChanges.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRedoChanges.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -46,16 +47,20 @@ public class AssetEditorRedoChanges implements Packet, ToServerPacket { @Nonnull public static AssetEditorRedoChanges deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorRedoChanges obj = new AssetEditorRedoChanges(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorRedoChanges", 5, buf.readableBytes() - offset); + } else { + AssetEditorRedoChanges obj = new AssetEditorRedoChanges(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRenameAsset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRenameAsset.java index 5d308b08..ce9e5f6e 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRenameAsset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRenameAsset.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,20 +51,34 @@ public class AssetEditorRenameAsset implements Packet, ToServerPacket { @Nonnull public static AssetEditorRenameAsset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorRenameAsset obj = new AssetEditorRenameAsset(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 5); - obj.path = AssetPath.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("AssetEditorRenameAsset", 13, buf.readableBytes() - offset); + } else { + AssetEditorRenameAsset obj = new AssetEditorRenameAsset(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 9); - obj.newPath = AssetPath.deserialize(buf, varPos1); - } + int varPos0 = offset + 13 + varPosBase0; + obj.path = AssetPath.deserialize(buf, varPos0); + } - return obj; + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("NewPath", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + obj.newPath = AssetPath.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -71,6 +86,10 @@ public class AssetEditorRenameAsset implements Packet, ToServerPacket { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -80,6 +99,10 @@ public class AssetEditorRenameAsset implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("NewPath", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; pos1 += AssetPath.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -145,15 +168,11 @@ public class AssetEditorRenameAsset implements Packet, ToServerPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int pathOffset = buffer.getIntLE(offset + 5); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Path"); } int pos = offset + 13 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, pos); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -164,21 +183,17 @@ public class AssetEditorRenameAsset implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int newPathOffset = buffer.getIntLE(offset + 9); - if (newPathOffset < 0) { + if (newPathOffset < 0 || newPathOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for NewPath"); } - int posx = offset + 13 + newPathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for NewPath"); - } - - ValidationResult newPathResult = AssetPath.validateStructure(buffer, posx); + int pos = offset + 13 + newPathOffset; + ValidationResult newPathResult = AssetPath.validateStructure(buffer, pos); if (!newPathResult.isValid()) { return ValidationResult.error("Invalid NewPath: " + newPathResult.error()); } - posx += AssetPath.computeBytesConsumed(buffer, posx); + pos += AssetPath.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRenameDirectory.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRenameDirectory.java index e07c03a4..ba8d6d79 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRenameDirectory.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRenameDirectory.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,20 +51,34 @@ public class AssetEditorRenameDirectory implements Packet, ToServerPacket { @Nonnull public static AssetEditorRenameDirectory deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorRenameDirectory obj = new AssetEditorRenameDirectory(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 5); - obj.path = AssetPath.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("AssetEditorRenameDirectory", 13, buf.readableBytes() - offset); + } else { + AssetEditorRenameDirectory obj = new AssetEditorRenameDirectory(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 9); - obj.newPath = AssetPath.deserialize(buf, varPos1); - } + int varPos0 = offset + 13 + varPosBase0; + obj.path = AssetPath.deserialize(buf, varPos0); + } - return obj; + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("NewPath", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + obj.newPath = AssetPath.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -71,6 +86,10 @@ public class AssetEditorRenameDirectory implements Packet, ToServerPacket { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -80,6 +99,10 @@ public class AssetEditorRenameDirectory implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("NewPath", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; pos1 += AssetPath.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -145,15 +168,11 @@ public class AssetEditorRenameDirectory implements Packet, ToServerPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int pathOffset = buffer.getIntLE(offset + 5); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Path"); } int pos = offset + 13 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, pos); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -164,21 +183,17 @@ public class AssetEditorRenameDirectory implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int newPathOffset = buffer.getIntLE(offset + 9); - if (newPathOffset < 0) { + if (newPathOffset < 0 || newPathOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for NewPath"); } - int posx = offset + 13 + newPathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for NewPath"); - } - - ValidationResult newPathResult = AssetPath.validateStructure(buffer, posx); + int pos = offset + 13 + newPathOffset; + ValidationResult newPathResult = AssetPath.validateStructure(buffer, pos); if (!newPathResult.isValid()) { return ValidationResult.error("Invalid NewPath: " + newPathResult.error()); } - posx += AssetPath.computeBytesConsumed(buffer, posx); + pos += AssetPath.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestChildrenList.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestChildrenList.java index e2d6e73c..287111f2 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestChildrenList.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestChildrenList.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -43,15 +44,19 @@ public class AssetEditorRequestChildrenList implements Packet, ToServerPacket { @Nonnull public static AssetEditorRequestChildrenList deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorRequestChildrenList obj = new AssetEditorRequestChildrenList(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorRequestChildrenList", 1, buf.readableBytes() - offset); + } else { + AssetEditorRequestChildrenList obj = new AssetEditorRequestChildrenList(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestChildrenListReply.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestChildrenListReply.java index 1d21db7c..adb342a6 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestChildrenListReply.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestChildrenListReply.java @@ -51,49 +51,67 @@ public class AssetEditorRequestChildrenListReply implements Packet, ToClientPack @Nonnull public static AssetEditorRequestChildrenListReply deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorRequestChildrenListReply obj = new AssetEditorRequestChildrenListReply(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - obj.path = AssetPath.deserialize(buf, varPos0); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int childrenIdsCount = VarInt.peek(buf, varPos1); - if (childrenIdsCount < 0) { - throw ProtocolException.negativeLength("ChildrenIds", childrenIdsCount); - } - - if (childrenIdsCount > 4096000) { - throw ProtocolException.arrayTooLong("ChildrenIds", childrenIdsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + childrenIdsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ChildrenIds", varPos1 + varIntLen + childrenIdsCount * 1, buf.readableBytes()); - } - - obj.childrenIds = new String[childrenIdsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < childrenIdsCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("childrenIds[" + i + "]", strLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AssetEditorRequestChildrenListReply", 9, buf.readableBytes() - offset); + } else { + AssetEditorRequestChildrenListReply obj = new AssetEditorRequestChildrenListReply(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("childrenIds[" + i + "]", strLen, 4096000); + int varPos0 = offset + 9 + varPosBase0; + obj.path = AssetPath.deserialize(buf, varPos0); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ChildrenIds", varPosBase1, buf.readableBytes()); } - int strVarLen = VarInt.length(buf, elemPos); - obj.childrenIds[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } + int varPos1 = offset + 9 + varPosBase1; + int childrenIdsCount = VarInt.peek(buf, varPos1); + if (childrenIdsCount < 0) { + throw ProtocolException.invalidVarInt("ChildrenIds"); + } - return obj; + int varIntLen = VarInt.size(childrenIdsCount); + if (childrenIdsCount > 4096000) { + throw ProtocolException.arrayTooLong("ChildrenIds", childrenIdsCount, 4096000); + } + + if (varPos1 + varIntLen + childrenIdsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ChildrenIds", varPos1 + varIntLen + childrenIdsCount * 1, buf.readableBytes()); + } + + obj.childrenIds = new String[childrenIdsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < childrenIdsCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("childrenIds[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("childrenIds[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("childrenIds[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.childrenIds[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,6 +119,10 @@ public class AssetEditorRequestChildrenListReply implements Packet, ToClientPack int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -110,13 +132,17 @@ public class AssetEditorRequestChildrenListReply implements Packet, ToClientPack if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ChildrenIds", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -195,15 +221,11 @@ public class AssetEditorRequestChildrenListReply implements Packet, ToClientPack byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int pathOffset = buffer.getIntLE(offset + 1); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Path"); } int pos = offset + 9 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, pos); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -214,16 +236,12 @@ public class AssetEditorRequestChildrenListReply implements Packet, ToClientPack if ((nullBits & 2) != 0) { int childrenIdsOffset = buffer.getIntLE(offset + 5); - if (childrenIdsOffset < 0) { + if (childrenIdsOffset < 0 || childrenIdsOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for ChildrenIds"); } - int posx = offset + 9 + childrenIdsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ChildrenIds"); - } - - int childrenIdsCount = VarInt.peek(buffer, posx); + int pos = offset + 9 + childrenIdsOffset; + int childrenIdsCount = VarInt.peek(buffer, pos); if (childrenIdsCount < 0) { return ValidationResult.error("Invalid array count for ChildrenIds"); } @@ -232,17 +250,17 @@ public class AssetEditorRequestChildrenListReply implements Packet, ToClientPack return ValidationResult.error("ChildrenIds exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + pos += VarInt.size(childrenIdsCount); for (int i = 0; i < childrenIdsCount; i++) { - int strLen = VarInt.peek(buffer, posx); + int strLen = VarInt.peek(buffer, pos); if (strLen < 0) { return ValidationResult.error("Invalid string length in ChildrenIds"); } - posx += VarInt.length(buffer, posx); - posx += strLen; - if (posx > buffer.writerIndex()) { + pos += VarInt.size(strLen); + pos += strLen; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in ChildrenIds"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestDataset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestDataset.java index 8dc77d61..b1d203af 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestDataset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestDataset.java @@ -46,25 +46,33 @@ public class AssetEditorRequestDataset implements Packet, ToServerPacket { @Nonnull public static AssetEditorRequestDataset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorRequestDataset obj = new AssetEditorRequestDataset(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int nameLen = VarInt.peek(buf, pos); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorRequestDataset", 1, buf.readableBytes() - offset); + } else { + AssetEditorRequestDataset obj = new AssetEditorRequestDataset(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int nameLen = VarInt.peek(buf, pos); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (pos + nameVarLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", pos + nameVarLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += nameVarLen + nameLen; } - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); - } - - int nameVarLen = VarInt.length(buf, pos); - obj.name = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += nameVarLen + nameLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +80,7 @@ public class AssetEditorRequestDataset implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -117,7 +125,7 @@ public class AssetEditorRequestDataset implements Packet, ToServerPacket { return ValidationResult.error("Name exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Name"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestDatasetReply.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestDatasetReply.java index cfe9d75e..7d0deef4 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestDatasetReply.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorRequestDatasetReply.java @@ -51,58 +51,81 @@ public class AssetEditorRequestDatasetReply implements Packet, ToClientPacket { @Nonnull public static AssetEditorRequestDatasetReply deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorRequestDatasetReply obj = new AssetEditorRequestDatasetReply(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); - } - - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); - } - - obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int idsCount = VarInt.peek(buf, varPos1); - if (idsCount < 0) { - throw ProtocolException.negativeLength("Ids", idsCount); - } - - if (idsCount > 4096000) { - throw ProtocolException.arrayTooLong("Ids", idsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + idsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Ids", varPos1 + varIntLen + idsCount * 1, buf.readableBytes()); - } - - obj.ids = new String[idsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < idsCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("ids[" + i + "]", strLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AssetEditorRequestDatasetReply", 9, buf.readableBytes() - offset); + } else { + AssetEditorRequestDatasetReply obj = new AssetEditorRequestDatasetReply(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("ids[" + i + "]", strLen, 4096000); + int varPos0 = offset + 9 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); } - int strVarLen = VarInt.length(buf, elemPos); - obj.ids[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } - return obj; + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Ids", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int idsCount = VarInt.peek(buf, varPos1); + if (idsCount < 0) { + throw ProtocolException.invalidVarInt("Ids"); + } + + int varIntLen = VarInt.size(idsCount); + if (idsCount > 4096000) { + throw ProtocolException.arrayTooLong("Ids", idsCount, 4096000); + } + + if (varPos1 + varIntLen + idsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Ids", varPos1 + varIntLen + idsCount * 1, buf.readableBytes()); + } + + obj.ids = new String[idsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < idsCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("ids[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("ids[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ids[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.ids[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -110,9 +133,13 @@ public class AssetEditorRequestDatasetReply implements Packet, ToClientPacket { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -120,13 +147,17 @@ public class AssetEditorRequestDatasetReply implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Ids", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -205,15 +236,11 @@ public class AssetEditorRequestDatasetReply implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int nameOffset = buffer.getIntLE(offset + 1); - if (nameOffset < 0) { + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Name"); } int pos = offset + 9 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - int nameLen = VarInt.peek(buffer, pos); if (nameLen < 0) { return ValidationResult.error("Invalid string length for Name"); @@ -223,7 +250,7 @@ public class AssetEditorRequestDatasetReply implements Packet, ToClientPacket { return ValidationResult.error("Name exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Name"); @@ -232,15 +259,11 @@ public class AssetEditorRequestDatasetReply implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int idsOffset = buffer.getIntLE(offset + 5); - if (idsOffset < 0) { + if (idsOffset < 0 || idsOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Ids"); } int posx = offset + 9 + idsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Ids"); - } - int idsCount = VarInt.peek(buffer, posx); if (idsCount < 0) { return ValidationResult.error("Invalid array count for Ids"); @@ -250,7 +273,7 @@ public class AssetEditorRequestDatasetReply implements Packet, ToClientPacket { return ValidationResult.error("Ids exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(idsCount); for (int i = 0; i < idsCount; i++) { int strLen = VarInt.peek(buffer, posx); @@ -258,7 +281,7 @@ public class AssetEditorRequestDatasetReply implements Packet, ToClientPacket { return ValidationResult.error("Invalid string length in Ids"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(strLen); posx += strLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Ids"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSelectAsset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSelectAsset.java index 9c1ac256..ef39a5d7 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSelectAsset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSelectAsset.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -43,15 +44,19 @@ public class AssetEditorSelectAsset implements Packet, ToServerPacket { @Nonnull public static AssetEditorSelectAsset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorSelectAsset obj = new AssetEditorSelectAsset(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorSelectAsset", 1, buf.readableBytes() - offset); + } else { + AssetEditorSelectAsset obj = new AssetEditorSelectAsset(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetGameTime.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetGameTime.java index 5fd2a2a8..f090a6e3 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetGameTime.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetGameTime.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.InstantData; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,14 +48,18 @@ public class AssetEditorSetGameTime implements Packet, ToServerPacket { @Nonnull public static AssetEditorSetGameTime deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorSetGameTime obj = new AssetEditorSetGameTime(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.gameTime = InstantData.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("AssetEditorSetGameTime", 14, buf.readableBytes() - offset); + } else { + AssetEditorSetGameTime obj = new AssetEditorSetGameTime(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.gameTime = InstantData.deserialize(buf, offset + 1); + } - obj.paused = buf.getByte(offset + 13) != 0; - return obj; + obj.paused = buf.getByte(offset + 13) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -84,7 +89,12 @@ public class AssetEditorSetGameTime implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 14 ? ValidationResult.error("Buffer too small: expected at least 14 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 14) { + return ValidationResult.error("Buffer too small: expected at least 14 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public AssetEditorSetGameTime clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetupAssetTypes.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetupAssetTypes.java index 07a5d072..2ea81610 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetupAssetTypes.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetupAssetTypes.java @@ -45,34 +45,38 @@ public class AssetEditorSetupAssetTypes implements Packet, ToClientPacket { @Nonnull public static AssetEditorSetupAssetTypes deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorSetupAssetTypes obj = new AssetEditorSetupAssetTypes(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int assetTypesCount = VarInt.peek(buf, pos); - if (assetTypesCount < 0) { - throw ProtocolException.negativeLength("AssetTypes", assetTypesCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorSetupAssetTypes", 1, buf.readableBytes() - offset); + } else { + AssetEditorSetupAssetTypes obj = new AssetEditorSetupAssetTypes(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int assetTypesCount = VarInt.peek(buf, pos); + if (assetTypesCount < 0) { + throw ProtocolException.invalidVarInt("AssetTypes"); + } + + int assetTypesVarLen = VarInt.size(assetTypesCount); + if (assetTypesCount > 4096000) { + throw ProtocolException.arrayTooLong("AssetTypes", assetTypesCount, 4096000); + } + + if (pos + assetTypesVarLen + assetTypesCount * 3L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AssetTypes", pos + assetTypesVarLen + assetTypesCount * 3, buf.readableBytes()); + } + + pos += assetTypesVarLen; + obj.assetTypes = new AssetEditorAssetType[assetTypesCount]; + + for (int i = 0; i < assetTypesCount; i++) { + obj.assetTypes[i] = AssetEditorAssetType.deserialize(buf, pos); + pos += AssetEditorAssetType.computeBytesConsumed(buf, pos); + } } - if (assetTypesCount > 4096000) { - throw ProtocolException.arrayTooLong("AssetTypes", assetTypesCount, 4096000); - } - - int assetTypesVarLen = VarInt.size(assetTypesCount); - if (pos + assetTypesVarLen + assetTypesCount * 3L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("AssetTypes", pos + assetTypesVarLen + assetTypesCount * 3, buf.readableBytes()); - } - - pos += assetTypesVarLen; - obj.assetTypes = new AssetEditorAssetType[assetTypesCount]; - - for (int i = 0; i < assetTypesCount; i++) { - obj.assetTypes[i] = AssetEditorAssetType.deserialize(buf, pos); - pos += AssetEditorAssetType.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class AssetEditorSetupAssetTypes implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += AssetEditorAssetType.computeBytesConsumed(buf, pos); @@ -143,7 +147,7 @@ public class AssetEditorSetupAssetTypes implements Packet, ToClientPacket { return ValidationResult.error("AssetTypes exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetTypesCount); for (int i = 0; i < assetTypesCount; i++) { ValidationResult structResult = AssetEditorAssetType.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetupSchemas.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetupSchemas.java index f637cf99..fdaf6d18 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetupSchemas.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSetupSchemas.java @@ -45,34 +45,38 @@ public class AssetEditorSetupSchemas implements Packet, ToClientPacket { @Nonnull public static AssetEditorSetupSchemas deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorSetupSchemas obj = new AssetEditorSetupSchemas(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int schemasCount = VarInt.peek(buf, pos); - if (schemasCount < 0) { - throw ProtocolException.negativeLength("Schemas", schemasCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorSetupSchemas", 1, buf.readableBytes() - offset); + } else { + AssetEditorSetupSchemas obj = new AssetEditorSetupSchemas(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int schemasCount = VarInt.peek(buf, pos); + if (schemasCount < 0) { + throw ProtocolException.invalidVarInt("Schemas"); + } + + int schemasVarLen = VarInt.size(schemasCount); + if (schemasCount > 4096000) { + throw ProtocolException.arrayTooLong("Schemas", schemasCount, 4096000); + } + + if (pos + schemasVarLen + schemasCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Schemas", pos + schemasVarLen + schemasCount * 1, buf.readableBytes()); + } + + pos += schemasVarLen; + obj.schemas = new SchemaFile[schemasCount]; + + for (int i = 0; i < schemasCount; i++) { + obj.schemas[i] = SchemaFile.deserialize(buf, pos); + pos += SchemaFile.computeBytesConsumed(buf, pos); + } } - if (schemasCount > 4096000) { - throw ProtocolException.arrayTooLong("Schemas", schemasCount, 4096000); - } - - int schemasVarLen = VarInt.size(schemasCount); - if (pos + schemasVarLen + schemasCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Schemas", pos + schemasVarLen + schemasCount * 1, buf.readableBytes()); - } - - pos += schemasVarLen; - obj.schemas = new SchemaFile[schemasCount]; - - for (int i = 0; i < schemasCount; i++) { - obj.schemas[i] = SchemaFile.deserialize(buf, pos); - pos += SchemaFile.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class AssetEditorSetupSchemas implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += SchemaFile.computeBytesConsumed(buf, pos); @@ -143,7 +147,7 @@ public class AssetEditorSetupSchemas implements Packet, ToClientPacket { return ValidationResult.error("Schemas exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(schemasCount); for (int i = 0; i < schemasCount; i++) { ValidationResult structResult = SchemaFile.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSubscribeModifiedAssetsChanges.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSubscribeModifiedAssetsChanges.java index 7e8d10b0..b4236404 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSubscribeModifiedAssetsChanges.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorSubscribeModifiedAssetsChanges.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class AssetEditorSubscribeModifiedAssetsChanges implements Packet, ToServ @Nonnull public static AssetEditorSubscribeModifiedAssetsChanges deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorSubscribeModifiedAssetsChanges obj = new AssetEditorSubscribeModifiedAssetsChanges(); - obj.subscribe = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorSubscribeModifiedAssetsChanges", 1, buf.readableBytes() - offset); + } else { + AssetEditorSubscribeModifiedAssetsChanges obj = new AssetEditorSubscribeModifiedAssetsChanges(); + obj.subscribe = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUndoChanges.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUndoChanges.java index 09197daf..09916f08 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUndoChanges.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUndoChanges.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -46,16 +47,20 @@ public class AssetEditorUndoChanges implements Packet, ToServerPacket { @Nonnull public static AssetEditorUndoChanges deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorUndoChanges obj = new AssetEditorUndoChanges(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.path = AssetPath.deserialize(buf, pos); - pos += AssetPath.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorUndoChanges", 5, buf.readableBytes() - offset); + } else { + AssetEditorUndoChanges obj = new AssetEditorUndoChanges(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.path = AssetPath.deserialize(buf, pos); + pos += AssetPath.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUndoRedoReply.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUndoRedoReply.java index 36347d5d..b1ea9195 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUndoRedoReply.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUndoRedoReply.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -46,16 +47,20 @@ public class AssetEditorUndoRedoReply implements Packet, ToClientPacket { @Nonnull public static AssetEditorUndoRedoReply deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorUndoRedoReply obj = new AssetEditorUndoRedoReply(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.command = JsonUpdateCommand.deserialize(buf, pos); - pos += JsonUpdateCommand.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("AssetEditorUndoRedoReply", 5, buf.readableBytes() - offset); + } else { + AssetEditorUndoRedoReply obj = new AssetEditorUndoRedoReply(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.command = JsonUpdateCommand.deserialize(buf, pos); + pos += JsonUpdateCommand.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateAsset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateAsset.java index 89b37e8b..21fecfd3 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateAsset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateAsset.java @@ -61,53 +61,77 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { @Nonnull public static AssetEditorUpdateAsset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorUpdateAsset obj = new AssetEditorUpdateAsset(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - obj.assetIndex = buf.getIntLE(offset + 5); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 21 + buf.getIntLE(offset + 9); - int assetTypeLen = VarInt.peek(buf, varPos0); - if (assetTypeLen < 0) { - throw ProtocolException.negativeLength("AssetType", assetTypeLen); + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("AssetEditorUpdateAsset", 21, buf.readableBytes() - offset); + } else { + AssetEditorUpdateAsset obj = new AssetEditorUpdateAsset(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + obj.assetIndex = buf.getIntLE(offset + 5); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("AssetType", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 21 + varPosBase0; + int assetTypeLen = VarInt.peek(buf, varPos0); + if (assetTypeLen < 0) { + throw ProtocolException.invalidVarInt("AssetType"); + } + + int assetTypeVarIntLen = VarInt.size(assetTypeLen); + if (assetTypeLen > 4096000) { + throw ProtocolException.stringTooLong("AssetType", assetTypeLen, 4096000); + } + + if (varPos0 + assetTypeVarIntLen + assetTypeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AssetType", varPos0 + assetTypeVarIntLen + assetTypeLen, buf.readableBytes()); + } + + obj.assetType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (assetTypeLen > 4096000) { - throw ProtocolException.stringTooLong("AssetType", assetTypeLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 13); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Path", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + obj.path = AssetPath.deserialize(buf, varPos1); } - obj.assetType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 17); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Data", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 21 + varPosBase2; + int dataCount = VarInt.peek(buf, varPos2); + if (dataCount < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int varIntLen = VarInt.size(dataCount); + if (dataCount > 4096000) { + throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); + } + + if (varPos2 + varIntLen + dataCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", varPos2 + varIntLen + dataCount * 1, buf.readableBytes()); + } + + obj.data = new byte[dataCount]; + + for (int i = 0; i < dataCount; i++) { + obj.data[i] = buf.getByte(varPos2 + varIntLen + i * 1); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 21 + buf.getIntLE(offset + 13); - obj.path = AssetPath.deserialize(buf, varPos1); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 21 + buf.getIntLE(offset + 17); - int dataCount = VarInt.peek(buf, varPos2); - if (dataCount < 0) { - throw ProtocolException.negativeLength("Data", dataCount); - } - - if (dataCount > 4096000) { - throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + dataCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Data", varPos2 + varIntLen + dataCount * 1, buf.readableBytes()); - } - - obj.data = new byte[dataCount]; - - for (int i = 0; i < dataCount; i++) { - obj.data[i] = buf.getByte(varPos2 + varIntLen + i * 1); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -115,9 +139,13 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { int maxEnd = 21; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("AssetType", fieldOffset0, maxEnd); + } + int pos0 = offset + 21 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -125,6 +153,10 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 13); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Path", fieldOffset1, maxEnd); + } + int pos1 = offset + 21 + fieldOffset1; pos1 += AssetPath.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -134,9 +166,13 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 17); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Data", fieldOffset2, maxEnd); + } + int pos2 = offset + 21 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + arrLen * 1; + pos2 += VarInt.size(arrLen) + arrLen * 1; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -226,15 +262,11 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int assetTypeOffset = buffer.getIntLE(offset + 9); - if (assetTypeOffset < 0) { + if (assetTypeOffset < 0 || assetTypeOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for AssetType"); } int pos = offset + 21 + assetTypeOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AssetType"); - } - int assetTypeLen = VarInt.peek(buffer, pos); if (assetTypeLen < 0) { return ValidationResult.error("Invalid string length for AssetType"); @@ -244,7 +276,7 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { return ValidationResult.error("AssetType exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetTypeLen); pos += assetTypeLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading AssetType"); @@ -253,15 +285,11 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int pathOffset = buffer.getIntLE(offset + 13); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Path"); } int posx = offset + 21 + pathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, posx); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -272,16 +300,12 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int dataOffset = buffer.getIntLE(offset + 17); - if (dataOffset < 0) { + if (dataOffset < 0 || dataOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Data"); } - int posxx = offset + 21 + dataOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Data"); - } - - int dataCount = VarInt.peek(buffer, posxx); + int posx = offset + 21 + dataOffset; + int dataCount = VarInt.peek(buffer, posx); if (dataCount < 0) { return ValidationResult.error("Invalid array count for Data"); } @@ -290,9 +314,9 @@ public class AssetEditorUpdateAsset implements Packet, ToServerPacket { return ValidationResult.error("Data exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += dataCount * 1; - if (posxx > buffer.writerIndex()) { + posx += VarInt.size(dataCount); + posx += dataCount * 1; + if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Data"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateAssetPack.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateAssetPack.java index d62e6128..d778605a 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateAssetPack.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateAssetPack.java @@ -51,28 +51,47 @@ public class AssetEditorUpdateAssetPack implements Packet, ToServerPacket, ToCli @Nonnull public static AssetEditorUpdateAssetPack deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorUpdateAssetPack obj = new AssetEditorUpdateAssetPack(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AssetEditorUpdateAssetPack", 9, buf.readableBytes() - offset); + } else { + AssetEditorUpdateAssetPack obj = new AssetEditorUpdateAssetPack(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Manifest", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + obj.manifest = AssetPackManifest.deserialize(buf, varPos1); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - obj.manifest = AssetPackManifest.deserialize(buf, varPos1); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,9 +99,13 @@ public class AssetEditorUpdateAssetPack implements Packet, ToServerPacket, ToCli int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -90,6 +113,10 @@ public class AssetEditorUpdateAssetPack implements Packet, ToServerPacket, ToCli if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Manifest", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; pos1 += AssetPackManifest.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -154,15 +181,11 @@ public class AssetEditorUpdateAssetPack implements Packet, ToServerPacket, ToCli byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 1); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Id"); } int pos = offset + 9 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -172,7 +195,7 @@ public class AssetEditorUpdateAssetPack implements Packet, ToServerPacket, ToCli return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -181,15 +204,11 @@ public class AssetEditorUpdateAssetPack implements Packet, ToServerPacket, ToCli if ((nullBits & 2) != 0) { int manifestOffset = buffer.getIntLE(offset + 5); - if (manifestOffset < 0) { + if (manifestOffset < 0 || manifestOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Manifest"); } int posx = offset + 9 + manifestOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Manifest"); - } - ValidationResult manifestResult = AssetPackManifest.validateStructure(buffer, posx); if (!manifestResult.isValid()) { return ValidationResult.error("Invalid Manifest: " + manifestResult.error()); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateJsonAsset.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateJsonAsset.java index d087aafe..437e90a0 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateJsonAsset.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateJsonAsset.java @@ -61,55 +61,79 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { @Nonnull public static AssetEditorUpdateJsonAsset deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorUpdateJsonAsset obj = new AssetEditorUpdateJsonAsset(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - obj.assetIndex = buf.getIntLE(offset + 5); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 21 + buf.getIntLE(offset + 9); - int assetTypeLen = VarInt.peek(buf, varPos0); - if (assetTypeLen < 0) { - throw ProtocolException.negativeLength("AssetType", assetTypeLen); + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("AssetEditorUpdateJsonAsset", 21, buf.readableBytes() - offset); + } else { + AssetEditorUpdateJsonAsset obj = new AssetEditorUpdateJsonAsset(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + obj.assetIndex = buf.getIntLE(offset + 5); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("AssetType", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 21 + varPosBase0; + int assetTypeLen = VarInt.peek(buf, varPos0); + if (assetTypeLen < 0) { + throw ProtocolException.invalidVarInt("AssetType"); + } + + int assetTypeVarIntLen = VarInt.size(assetTypeLen); + if (assetTypeLen > 4096000) { + throw ProtocolException.stringTooLong("AssetType", assetTypeLen, 4096000); + } + + if (varPos0 + assetTypeVarIntLen + assetTypeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AssetType", varPos0 + assetTypeVarIntLen + assetTypeLen, buf.readableBytes()); + } + + obj.assetType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (assetTypeLen > 4096000) { - throw ProtocolException.stringTooLong("AssetType", assetTypeLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 13); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Path", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + obj.path = AssetPath.deserialize(buf, varPos1); } - obj.assetType = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 17); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Commands", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 21 + varPosBase2; + int commandsCount = VarInt.peek(buf, varPos2); + if (commandsCount < 0) { + throw ProtocolException.invalidVarInt("Commands"); + } + + int varIntLen = VarInt.size(commandsCount); + if (commandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); + } + + if (varPos2 + varIntLen + commandsCount * 7L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Commands", varPos2 + varIntLen + commandsCount * 7, buf.readableBytes()); + } + + obj.commands = new JsonUpdateCommand[commandsCount]; + int elemPos = varPos2 + varIntLen; + + for (int i = 0; i < commandsCount; i++) { + obj.commands[i] = JsonUpdateCommand.deserialize(buf, elemPos); + elemPos += JsonUpdateCommand.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 21 + buf.getIntLE(offset + 13); - obj.path = AssetPath.deserialize(buf, varPos1); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 21 + buf.getIntLE(offset + 17); - int commandsCount = VarInt.peek(buf, varPos2); - if (commandsCount < 0) { - throw ProtocolException.negativeLength("Commands", commandsCount); - } - - if (commandsCount > 4096000) { - throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + commandsCount * 7L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Commands", varPos2 + varIntLen + commandsCount * 7, buf.readableBytes()); - } - - obj.commands = new JsonUpdateCommand[commandsCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < commandsCount; i++) { - obj.commands[i] = JsonUpdateCommand.deserialize(buf, elemPos); - elemPos += JsonUpdateCommand.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -117,9 +141,13 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { int maxEnd = 21; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("AssetType", fieldOffset0, maxEnd); + } + int pos0 = offset + 21 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -127,6 +155,10 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 13); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Path", fieldOffset1, maxEnd); + } + int pos1 = offset + 21 + fieldOffset1; pos1 += AssetPath.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -136,9 +168,13 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 17); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Commands", fieldOffset2, maxEnd); + } + int pos2 = offset + 21 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += JsonUpdateCommand.computeBytesConsumed(buf, pos2); @@ -239,15 +275,11 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int assetTypeOffset = buffer.getIntLE(offset + 9); - if (assetTypeOffset < 0) { + if (assetTypeOffset < 0 || assetTypeOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for AssetType"); } int pos = offset + 21 + assetTypeOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AssetType"); - } - int assetTypeLen = VarInt.peek(buffer, pos); if (assetTypeLen < 0) { return ValidationResult.error("Invalid string length for AssetType"); @@ -257,7 +289,7 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { return ValidationResult.error("AssetType exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetTypeLen); pos += assetTypeLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading AssetType"); @@ -266,15 +298,11 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int pathOffset = buffer.getIntLE(offset + 13); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Path"); } int posx = offset + 21 + pathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, posx); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -285,16 +313,12 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int commandsOffset = buffer.getIntLE(offset + 17); - if (commandsOffset < 0) { + if (commandsOffset < 0 || commandsOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Commands"); } - int posxx = offset + 21 + commandsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Commands"); - } - - int commandsCount = VarInt.peek(buffer, posxx); + int posx = offset + 21 + commandsOffset; + int commandsCount = VarInt.peek(buffer, posx); if (commandsCount < 0) { return ValidationResult.error("Invalid array count for Commands"); } @@ -303,15 +327,15 @@ public class AssetEditorUpdateJsonAsset implements Packet, ToServerPacket { return ValidationResult.error("Commands exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posx += VarInt.size(commandsCount); for (int i = 0; i < commandsCount; i++) { - ValidationResult structResult = JsonUpdateCommand.validateStructure(buffer, posxx); + ValidationResult structResult = JsonUpdateCommand.validateStructure(buffer, posx); if (!structResult.isValid()) { return ValidationResult.error("Invalid JsonUpdateCommand in Commands[" + i + "]: " + structResult.error()); } - posxx += JsonUpdateCommand.computeBytesConsumed(buffer, posxx); + posx += JsonUpdateCommand.computeBytesConsumed(buffer, posx); } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateModelPreview.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateModelPreview.java index 9afce37c..c6260172 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateModelPreview.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateModelPreview.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.Model; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -15,9 +16,9 @@ public class AssetEditorUpdateModelPreview implements Packet, ToClientPacket { public static final int PACKET_ID = 355; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 30; + public static final int FIXED_BLOCK_SIZE = 29; public static final int VARIABLE_FIELD_COUNT = 3; - public static final int VARIABLE_BLOCK_START = 42; + public static final int VARIABLE_BLOCK_START = 41; public static final int MAX_SIZE = 1677721600; @Nullable public AssetPath assetPath; @@ -59,36 +60,59 @@ public class AssetEditorUpdateModelPreview implements Packet, ToClientPacket { @Nonnull public static AssetEditorUpdateModelPreview deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorUpdateModelPreview obj = new AssetEditorUpdateModelPreview(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.camera = AssetEditorPreviewCameraSettings.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 41) { + throw ProtocolException.bufferTooSmall("AssetEditorUpdateModelPreview", 41, buf.readableBytes() - offset); + } else { + AssetEditorUpdateModelPreview obj = new AssetEditorUpdateModelPreview(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.camera = AssetEditorPreviewCameraSettings.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - int varPos0 = offset + 42 + buf.getIntLE(offset + 30); - obj.assetPath = AssetPath.deserialize(buf, varPos0); - } + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 29); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("AssetPath", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 4) != 0) { - int varPos1 = offset + 42 + buf.getIntLE(offset + 34); - obj.model = Model.deserialize(buf, varPos1); - } + int varPos0 = offset + 41 + varPosBase0; + obj.assetPath = AssetPath.deserialize(buf, varPos0); + } - if ((nullBits & 8) != 0) { - int varPos2 = offset + 42 + buf.getIntLE(offset + 38); - obj.block = BlockType.deserialize(buf, varPos2); - } + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 33); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Model", varPosBase1, buf.readableBytes()); + } - return obj; + int varPos1 = offset + 41 + varPosBase1; + obj.model = Model.deserialize(buf, varPos1); + } + + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 37); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Block", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 41 + varPosBase2; + obj.block = BlockType.deserialize(buf, varPos2); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 42; + int maxEnd = 41; if ((nullBits & 2) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 30); - int pos0 = offset + 42 + fieldOffset0; + int fieldOffset0 = buf.getIntLE(offset + 29); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("AssetPath", fieldOffset0, maxEnd); + } + + int pos0 = offset + 41 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; @@ -96,8 +120,12 @@ public class AssetEditorUpdateModelPreview implements Packet, ToClientPacket { } if ((nullBits & 4) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 34); - int pos1 = offset + 42 + fieldOffset1; + int fieldOffset1 = buf.getIntLE(offset + 33); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Model", fieldOffset1, maxEnd); + } + + int pos1 = offset + 41 + fieldOffset1; pos1 += Model.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; @@ -105,8 +133,12 @@ public class AssetEditorUpdateModelPreview implements Packet, ToClientPacket { } if ((nullBits & 8) != 0) { - int fieldOffset2 = buf.getIntLE(offset + 38); - int pos2 = offset + 42 + fieldOffset2; + int fieldOffset2 = buf.getIntLE(offset + 37); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 41) { + throw ProtocolException.invalidOffset("Block", fieldOffset2, maxEnd); + } + + int pos2 = offset + 41 + fieldOffset2; pos2 += BlockType.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; @@ -140,7 +172,7 @@ public class AssetEditorUpdateModelPreview implements Packet, ToClientPacket { if (this.camera != null) { this.camera.serialize(buf); } else { - buf.writeZero(29); + buf.writeZero(28); } int assetPathOffsetSlot = buf.writerIndex(); @@ -174,7 +206,7 @@ public class AssetEditorUpdateModelPreview implements Packet, ToClientPacket { @Override public int computeSize() { - int size = 42; + int size = 41; if (this.assetPath != null) { size += this.assetPath.computeSize(); } @@ -191,21 +223,17 @@ public class AssetEditorUpdateModelPreview implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 42) { - return ValidationResult.error("Buffer too small: expected at least 42 bytes"); + if (buffer.readableBytes() - offset < 41) { + return ValidationResult.error("Buffer too small: expected at least 41 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { - int assetPathOffset = buffer.getIntLE(offset + 30); - if (assetPathOffset < 0) { + int assetPathOffset = buffer.getIntLE(offset + 29); + if (assetPathOffset < 0 || assetPathOffset > buffer.writerIndex() - offset - 41) { return ValidationResult.error("Invalid offset for AssetPath"); } - int pos = offset + 42 + assetPathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AssetPath"); - } - + int pos = offset + 41 + assetPathOffset; ValidationResult assetPathResult = AssetPath.validateStructure(buffer, pos); if (!assetPathResult.isValid()) { return ValidationResult.error("Invalid AssetPath: " + assetPathResult.error()); @@ -215,41 +243,33 @@ public class AssetEditorUpdateModelPreview implements Packet, ToClientPacket { } if ((nullBits & 4) != 0) { - int modelOffset = buffer.getIntLE(offset + 34); - if (modelOffset < 0) { + int modelOffset = buffer.getIntLE(offset + 33); + if (modelOffset < 0 || modelOffset > buffer.writerIndex() - offset - 41) { return ValidationResult.error("Invalid offset for Model"); } - int posx = offset + 42 + modelOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } - - ValidationResult modelResult = Model.validateStructure(buffer, posx); + int pos = offset + 41 + modelOffset; + ValidationResult modelResult = Model.validateStructure(buffer, pos); if (!modelResult.isValid()) { return ValidationResult.error("Invalid Model: " + modelResult.error()); } - posx += Model.computeBytesConsumed(buffer, posx); + pos += Model.computeBytesConsumed(buffer, pos); } if ((nullBits & 8) != 0) { - int blockOffset = buffer.getIntLE(offset + 38); - if (blockOffset < 0) { + int blockOffset = buffer.getIntLE(offset + 37); + if (blockOffset < 0 || blockOffset > buffer.writerIndex() - offset - 41) { return ValidationResult.error("Invalid offset for Block"); } - int posxx = offset + 42 + blockOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Block"); - } - - ValidationResult blockResult = BlockType.validateStructure(buffer, posxx); + int pos = offset + 41 + blockOffset; + ValidationResult blockResult = BlockType.validateStructure(buffer, pos); if (!blockResult.isValid()) { return ValidationResult.error("Invalid Block: " + blockResult.error()); } - posxx += BlockType.computeBytesConsumed(buffer, posxx); + pos += BlockType.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateSecondsPerGameDay.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateSecondsPerGameDay.java index 712c89af..52cf3f0c 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateSecondsPerGameDay.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateSecondsPerGameDay.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class AssetEditorUpdateSecondsPerGameDay implements Packet, ToClientPacke @Nonnull public static AssetEditorUpdateSecondsPerGameDay deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorUpdateSecondsPerGameDay obj = new AssetEditorUpdateSecondsPerGameDay(); - obj.daytimeDurationSeconds = buf.getIntLE(offset + 0); - obj.nighttimeDurationSeconds = buf.getIntLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("AssetEditorUpdateSecondsPerGameDay", 8, buf.readableBytes() - offset); + } else { + AssetEditorUpdateSecondsPerGameDay obj = new AssetEditorUpdateSecondsPerGameDay(); + obj.daytimeDurationSeconds = buf.getIntLE(offset + 0); + obj.nighttimeDurationSeconds = buf.getIntLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateWeatherPreviewLock.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateWeatherPreviewLock.java index 81113e8d..6bbf6fac 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateWeatherPreviewLock.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetEditorUpdateWeatherPreviewLock.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.asseteditor; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class AssetEditorUpdateWeatherPreviewLock implements Packet, ToServerPack @Nonnull public static AssetEditorUpdateWeatherPreviewLock deserialize(@Nonnull ByteBuf buf, int offset) { - AssetEditorUpdateWeatherPreviewLock obj = new AssetEditorUpdateWeatherPreviewLock(); - obj.locked = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetEditorUpdateWeatherPreviewLock", 1, buf.readableBytes() - offset); + } else { + AssetEditorUpdateWeatherPreviewLock obj = new AssetEditorUpdateWeatherPreviewLock(); + obj.locked = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetInfo.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetInfo.java index d3065531..d92c38ee 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetInfo.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetInfo.java @@ -55,36 +55,62 @@ public class AssetInfo { @Nonnull public static AssetInfo deserialize(@Nonnull ByteBuf buf, int offset) { - AssetInfo obj = new AssetInfo(); - byte nullBits = buf.getByte(offset); - obj.isDeleted = buf.getByte(offset + 1) != 0; - obj.isNew = buf.getByte(offset + 2) != 0; - obj.lastModificationDate = buf.getLongLE(offset + 3); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 23 + buf.getIntLE(offset + 11); - obj.path = AssetPath.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 23) { + throw ProtocolException.bufferTooSmall("AssetInfo", 23, buf.readableBytes() - offset); + } else { + AssetInfo obj = new AssetInfo(); + byte nullBits = buf.getByte(offset); + obj.isDeleted = buf.getByte(offset + 1) != 0; + obj.isNew = buf.getByte(offset + 2) != 0; + obj.lastModificationDate = buf.getLongLE(offset + 3); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 11); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 23 + buf.getIntLE(offset + 15); - obj.oldPath = AssetPath.deserialize(buf, varPos1); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 23 + buf.getIntLE(offset + 19); - int lastModificationUsernameLen = VarInt.peek(buf, varPos2); - if (lastModificationUsernameLen < 0) { - throw ProtocolException.negativeLength("LastModificationUsername", lastModificationUsernameLen); + int varPos0 = offset + 23 + varPosBase0; + obj.path = AssetPath.deserialize(buf, varPos0); } - if (lastModificationUsernameLen > 4096000) { - throw ProtocolException.stringTooLong("LastModificationUsername", lastModificationUsernameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 15); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("OldPath", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 23 + varPosBase1; + obj.oldPath = AssetPath.deserialize(buf, varPos1); } - obj.lastModificationUsername = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 19); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("LastModificationUsername", varPosBase2, buf.readableBytes()); + } - return obj; + int varPos2 = offset + 23 + varPosBase2; + int lastModificationUsernameLen = VarInt.peek(buf, varPos2); + if (lastModificationUsernameLen < 0) { + throw ProtocolException.invalidVarInt("LastModificationUsername"); + } + + int lastModificationUsernameVarIntLen = VarInt.size(lastModificationUsernameLen); + if (lastModificationUsernameLen > 4096000) { + throw ProtocolException.stringTooLong("LastModificationUsername", lastModificationUsernameLen, 4096000); + } + + if (varPos2 + lastModificationUsernameVarIntLen + lastModificationUsernameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "LastModificationUsername", varPos2 + lastModificationUsernameVarIntLen + lastModificationUsernameLen, buf.readableBytes() + ); + } + + obj.lastModificationUsername = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -92,6 +118,10 @@ public class AssetInfo { int maxEnd = 23; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 11); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 23 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -101,6 +131,10 @@ public class AssetInfo { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 15); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("OldPath", fieldOffset1, maxEnd); + } + int pos1 = offset + 23 + fieldOffset1; pos1 += AssetPath.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -110,9 +144,13 @@ public class AssetInfo { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 19); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("LastModificationUsername", fieldOffset2, maxEnd); + } + int pos2 = offset + 23 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -193,15 +231,11 @@ public class AssetInfo { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int pathOffset = buffer.getIntLE(offset + 11); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 23) { return ValidationResult.error("Invalid offset for Path"); } int pos = offset + 23 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, pos); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -212,35 +246,27 @@ public class AssetInfo { if ((nullBits & 2) != 0) { int oldPathOffset = buffer.getIntLE(offset + 15); - if (oldPathOffset < 0) { + if (oldPathOffset < 0 || oldPathOffset > buffer.writerIndex() - offset - 23) { return ValidationResult.error("Invalid offset for OldPath"); } - int posx = offset + 23 + oldPathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for OldPath"); - } - - ValidationResult oldPathResult = AssetPath.validateStructure(buffer, posx); + int pos = offset + 23 + oldPathOffset; + ValidationResult oldPathResult = AssetPath.validateStructure(buffer, pos); if (!oldPathResult.isValid()) { return ValidationResult.error("Invalid OldPath: " + oldPathResult.error()); } - posx += AssetPath.computeBytesConsumed(buffer, posx); + pos += AssetPath.computeBytesConsumed(buffer, pos); } if ((nullBits & 4) != 0) { int lastModificationUsernameOffset = buffer.getIntLE(offset + 19); - if (lastModificationUsernameOffset < 0) { + if (lastModificationUsernameOffset < 0 || lastModificationUsernameOffset > buffer.writerIndex() - offset - 23) { return ValidationResult.error("Invalid offset for LastModificationUsername"); } - int posxx = offset + 23 + lastModificationUsernameOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for LastModificationUsername"); - } - - int lastModificationUsernameLen = VarInt.peek(buffer, posxx); + int pos = offset + 23 + lastModificationUsernameOffset; + int lastModificationUsernameLen = VarInt.peek(buffer, pos); if (lastModificationUsernameLen < 0) { return ValidationResult.error("Invalid string length for LastModificationUsername"); } @@ -249,9 +275,9 @@ public class AssetInfo { return ValidationResult.error("LastModificationUsername exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += lastModificationUsernameLen; - if (posxx > buffer.writerIndex()) { + pos += VarInt.size(lastModificationUsernameLen); + pos += lastModificationUsernameLen; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading LastModificationUsername"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetPackManifest.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetPackManifest.java index b507b2dd..74f7eb44 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetPackManifest.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetPackManifest.java @@ -64,118 +64,187 @@ public class AssetPackManifest { @Nonnull public static AssetPackManifest deserialize(@Nonnull ByteBuf buf, int offset) { - AssetPackManifest obj = new AssetPackManifest(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 29 + buf.getIntLE(offset + 1); - int nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); + if (buf.readableBytes() - offset < 29) { + throw ProtocolException.bufferTooSmall("AssetPackManifest", 29, buf.readableBytes() - offset); + } else { + AssetPackManifest obj = new AssetPackManifest(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 29 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Group", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 29 + varPosBase1; + int groupLen = VarInt.peek(buf, varPos1); + if (groupLen < 0) { + throw ProtocolException.invalidVarInt("Group"); + } + + int groupVarIntLen = VarInt.size(groupLen); + if (groupLen > 4096000) { + throw ProtocolException.stringTooLong("Group", groupLen, 4096000); + } + + if (varPos1 + groupVarIntLen + groupLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Group", varPos1 + groupVarIntLen + groupLen, buf.readableBytes()); + } + + obj.group = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Website", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 29 + varPosBase2; + int websiteLen = VarInt.peek(buf, varPos2); + if (websiteLen < 0) { + throw ProtocolException.invalidVarInt("Website"); + } + + int websiteVarIntLen = VarInt.size(websiteLen); + if (websiteLen > 4096000) { + throw ProtocolException.stringTooLong("Website", websiteLen, 4096000); + } + + if (varPos2 + websiteVarIntLen + websiteLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Website", varPos2 + websiteVarIntLen + websiteLen, buf.readableBytes()); + } + + obj.website = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 13); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Description", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 29 + varPosBase3; + int descriptionLen = VarInt.peek(buf, varPos3); + if (descriptionLen < 0) { + throw ProtocolException.invalidVarInt("Description"); + } + + int descriptionVarIntLen = VarInt.size(descriptionLen); + if (descriptionLen > 4096000) { + throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); + } + + if (varPos3 + descriptionVarIntLen + descriptionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Description", varPos3 + descriptionVarIntLen + descriptionLen, buf.readableBytes()); + } + + obj.description = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 17); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Version", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 29 + varPosBase4; + int versionLen = VarInt.peek(buf, varPos4); + if (versionLen < 0) { + throw ProtocolException.invalidVarInt("Version"); + } + + int versionVarIntLen = VarInt.size(versionLen); + if (versionLen > 4096000) { + throw ProtocolException.stringTooLong("Version", versionLen, 4096000); + } + + if (varPos4 + versionVarIntLen + versionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Version", varPos4 + versionVarIntLen + versionLen, buf.readableBytes()); + } + + obj.version = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 21); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Authors", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 29 + varPosBase5; + int authorsCount = VarInt.peek(buf, varPos5); + if (authorsCount < 0) { + throw ProtocolException.invalidVarInt("Authors"); + } + + int varIntLen = VarInt.size(authorsCount); + if (authorsCount > 4096000) { + throw ProtocolException.arrayTooLong("Authors", authorsCount, 4096000); + } + + if (varPos5 + varIntLen + authorsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Authors", varPos5 + varIntLen + authorsCount * 1, buf.readableBytes()); + } + + obj.authors = new AuthorInfo[authorsCount]; + int elemPos = varPos5 + varIntLen; + + for (int i = 0; i < authorsCount; i++) { + obj.authors[i] = AuthorInfo.deserialize(buf, elemPos); + elemPos += AuthorInfo.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 25); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("ServerVersion", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 29 + varPosBase6; + int serverVersionLen = VarInt.peek(buf, varPos6); + if (serverVersionLen < 0) { + throw ProtocolException.invalidVarInt("ServerVersion"); + } + + int serverVersionVarIntLen = VarInt.size(serverVersionLen); + if (serverVersionLen > 4096000) { + throw ProtocolException.stringTooLong("ServerVersion", serverVersionLen, 4096000); + } + + if (varPos6 + serverVersionVarIntLen + serverVersionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ServerVersion", varPos6 + serverVersionVarIntLen + serverVersionLen, buf.readableBytes()); + } + + obj.serverVersion = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 29 + buf.getIntLE(offset + 5); - int groupLen = VarInt.peek(buf, varPos1); - if (groupLen < 0) { - throw ProtocolException.negativeLength("Group", groupLen); - } - - if (groupLen > 4096000) { - throw ProtocolException.stringTooLong("Group", groupLen, 4096000); - } - - obj.group = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 29 + buf.getIntLE(offset + 9); - int websiteLen = VarInt.peek(buf, varPos2); - if (websiteLen < 0) { - throw ProtocolException.negativeLength("Website", websiteLen); - } - - if (websiteLen > 4096000) { - throw ProtocolException.stringTooLong("Website", websiteLen, 4096000); - } - - obj.website = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos3 = offset + 29 + buf.getIntLE(offset + 13); - int descriptionLen = VarInt.peek(buf, varPos3); - if (descriptionLen < 0) { - throw ProtocolException.negativeLength("Description", descriptionLen); - } - - if (descriptionLen > 4096000) { - throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); - } - - obj.description = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); - } - - if ((nullBits & 16) != 0) { - int varPos4 = offset + 29 + buf.getIntLE(offset + 17); - int versionLen = VarInt.peek(buf, varPos4); - if (versionLen < 0) { - throw ProtocolException.negativeLength("Version", versionLen); - } - - if (versionLen > 4096000) { - throw ProtocolException.stringTooLong("Version", versionLen, 4096000); - } - - obj.version = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); - } - - if ((nullBits & 32) != 0) { - int varPos5 = offset + 29 + buf.getIntLE(offset + 21); - int authorsCount = VarInt.peek(buf, varPos5); - if (authorsCount < 0) { - throw ProtocolException.negativeLength("Authors", authorsCount); - } - - if (authorsCount > 4096000) { - throw ProtocolException.arrayTooLong("Authors", authorsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + authorsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Authors", varPos5 + varIntLen + authorsCount * 1, buf.readableBytes()); - } - - obj.authors = new AuthorInfo[authorsCount]; - int elemPos = varPos5 + varIntLen; - - for (int i = 0; i < authorsCount; i++) { - obj.authors[i] = AuthorInfo.deserialize(buf, elemPos); - elemPos += AuthorInfo.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 64) != 0) { - int varPos6 = offset + 29 + buf.getIntLE(offset + 25); - int serverVersionLen = VarInt.peek(buf, varPos6); - if (serverVersionLen < 0) { - throw ProtocolException.negativeLength("ServerVersion", serverVersionLen); - } - - if (serverVersionLen > 4096000) { - throw ProtocolException.stringTooLong("ServerVersion", serverVersionLen, 4096000); - } - - obj.serverVersion = PacketIO.readVarString(buf, varPos6, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -183,9 +252,13 @@ public class AssetPackManifest { int maxEnd = 29; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + int pos0 = offset + 29 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -193,9 +266,13 @@ public class AssetPackManifest { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Group", fieldOffset1, maxEnd); + } + int pos1 = offset + 29 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -203,9 +280,13 @@ public class AssetPackManifest { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Website", fieldOffset2, maxEnd); + } + int pos2 = offset + 29 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -213,9 +294,13 @@ public class AssetPackManifest { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 13); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Description", fieldOffset3, maxEnd); + } + int pos3 = offset + 29 + fieldOffset3; int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } @@ -223,9 +308,13 @@ public class AssetPackManifest { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 17); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Version", fieldOffset4, maxEnd); + } + int pos4 = offset + 29 + fieldOffset4; int sl = VarInt.peek(buf, pos4); - pos4 += VarInt.length(buf, pos4) + sl; + pos4 += VarInt.size(sl) + sl; if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; } @@ -233,9 +322,13 @@ public class AssetPackManifest { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 21); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("Authors", fieldOffset5, maxEnd); + } + int pos5 = offset + 29 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos5 += AuthorInfo.computeBytesConsumed(buf, pos5); @@ -248,9 +341,13 @@ public class AssetPackManifest { if ((nullBits & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 25); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 29) { + throw ProtocolException.invalidOffset("ServerVersion", fieldOffset6, maxEnd); + } + int pos6 = offset + 29 + fieldOffset6; int sl = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6) + sl; + pos6 += VarInt.size(sl) + sl; if (pos6 - offset > maxEnd) { maxEnd = pos6 - offset; } @@ -410,15 +507,11 @@ public class AssetPackManifest { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int nameOffset = buffer.getIntLE(offset + 1); - if (nameOffset < 0) { + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Name"); } int pos = offset + 29 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - int nameLen = VarInt.peek(buffer, pos); if (nameLen < 0) { return ValidationResult.error("Invalid string length for Name"); @@ -428,7 +521,7 @@ public class AssetPackManifest { return ValidationResult.error("Name exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Name"); @@ -437,15 +530,11 @@ public class AssetPackManifest { if ((nullBits & 2) != 0) { int groupOffset = buffer.getIntLE(offset + 5); - if (groupOffset < 0) { + if (groupOffset < 0 || groupOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Group"); } int posx = offset + 29 + groupOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Group"); - } - int groupLen = VarInt.peek(buffer, posx); if (groupLen < 0) { return ValidationResult.error("Invalid string length for Group"); @@ -455,7 +544,7 @@ public class AssetPackManifest { return ValidationResult.error("Group exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(groupLen); posx += groupLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Group"); @@ -464,15 +553,11 @@ public class AssetPackManifest { if ((nullBits & 4) != 0) { int websiteOffset = buffer.getIntLE(offset + 9); - if (websiteOffset < 0) { + if (websiteOffset < 0 || websiteOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Website"); } int posxx = offset + 29 + websiteOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Website"); - } - int websiteLen = VarInt.peek(buffer, posxx); if (websiteLen < 0) { return ValidationResult.error("Invalid string length for Website"); @@ -482,7 +567,7 @@ public class AssetPackManifest { return ValidationResult.error("Website exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(websiteLen); posxx += websiteLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Website"); @@ -491,15 +576,11 @@ public class AssetPackManifest { if ((nullBits & 8) != 0) { int descriptionOffset = buffer.getIntLE(offset + 13); - if (descriptionOffset < 0) { + if (descriptionOffset < 0 || descriptionOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Description"); } int posxxx = offset + 29 + descriptionOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Description"); - } - int descriptionLen = VarInt.peek(buffer, posxxx); if (descriptionLen < 0) { return ValidationResult.error("Invalid string length for Description"); @@ -509,7 +590,7 @@ public class AssetPackManifest { return ValidationResult.error("Description exceeds max length 4096000"); } - posxxx += VarInt.length(buffer, posxxx); + posxxx += VarInt.size(descriptionLen); posxxx += descriptionLen; if (posxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Description"); @@ -518,15 +599,11 @@ public class AssetPackManifest { if ((nullBits & 16) != 0) { int versionOffset = buffer.getIntLE(offset + 17); - if (versionOffset < 0) { + if (versionOffset < 0 || versionOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Version"); } int posxxxx = offset + 29 + versionOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Version"); - } - int versionLen = VarInt.peek(buffer, posxxxx); if (versionLen < 0) { return ValidationResult.error("Invalid string length for Version"); @@ -536,7 +613,7 @@ public class AssetPackManifest { return ValidationResult.error("Version exceeds max length 4096000"); } - posxxxx += VarInt.length(buffer, posxxxx); + posxxxx += VarInt.size(versionLen); posxxxx += versionLen; if (posxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Version"); @@ -545,15 +622,11 @@ public class AssetPackManifest { if ((nullBits & 32) != 0) { int authorsOffset = buffer.getIntLE(offset + 21); - if (authorsOffset < 0) { + if (authorsOffset < 0 || authorsOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for Authors"); } int posxxxxx = offset + 29 + authorsOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Authors"); - } - int authorsCount = VarInt.peek(buffer, posxxxxx); if (authorsCount < 0) { return ValidationResult.error("Invalid array count for Authors"); @@ -563,7 +636,7 @@ public class AssetPackManifest { return ValidationResult.error("Authors exceeds max length 4096000"); } - posxxxxx += VarInt.length(buffer, posxxxxx); + posxxxxx += VarInt.size(authorsCount); for (int i = 0; i < authorsCount; i++) { ValidationResult structResult = AuthorInfo.validateStructure(buffer, posxxxxx); @@ -577,15 +650,11 @@ public class AssetPackManifest { if ((nullBits & 64) != 0) { int serverVersionOffset = buffer.getIntLE(offset + 25); - if (serverVersionOffset < 0) { + if (serverVersionOffset < 0 || serverVersionOffset > buffer.writerIndex() - offset - 29) { return ValidationResult.error("Invalid offset for ServerVersion"); } int posxxxxxx = offset + 29 + serverVersionOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ServerVersion"); - } - int serverVersionLen = VarInt.peek(buffer, posxxxxxx); if (serverVersionLen < 0) { return ValidationResult.error("Invalid string length for ServerVersion"); @@ -595,7 +664,7 @@ public class AssetPackManifest { return ValidationResult.error("ServerVersion exceeds max length 4096000"); } - posxxxxxx += VarInt.length(buffer, posxxxxxx); + posxxxxxx += VarInt.size(serverVersionLen); posxxxxxx += serverVersionLen; if (posxxxxxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ServerVersion"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetPath.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetPath.java index 7e5d2350..05d0d202 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetPath.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AssetPath.java @@ -35,37 +35,61 @@ public class AssetPath { @Nonnull public static AssetPath deserialize(@Nonnull ByteBuf buf, int offset) { - AssetPath obj = new AssetPath(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int packLen = VarInt.peek(buf, varPos0); - if (packLen < 0) { - throw ProtocolException.negativeLength("Pack", packLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AssetPath", 9, buf.readableBytes() - offset); + } else { + AssetPath obj = new AssetPath(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Pack", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int packLen = VarInt.peek(buf, varPos0); + if (packLen < 0) { + throw ProtocolException.invalidVarInt("Pack"); + } + + int packVarIntLen = VarInt.size(packLen); + if (packLen > 4096000) { + throw ProtocolException.stringTooLong("Pack", packLen, 4096000); + } + + if (varPos0 + packVarIntLen + packLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Pack", varPos0 + packVarIntLen + packLen, buf.readableBytes()); + } + + obj.pack = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (packLen > 4096000) { - throw ProtocolException.stringTooLong("Pack", packLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int pathLen = VarInt.peek(buf, varPos1); + if (pathLen < 0) { + throw ProtocolException.invalidVarInt("Path"); + } + + int pathVarIntLen = VarInt.size(pathLen); + if (pathLen > 4096000) { + throw ProtocolException.stringTooLong("Path", pathLen, 4096000); + } + + if (varPos1 + pathVarIntLen + pathLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Path", varPos1 + pathVarIntLen + pathLen, buf.readableBytes()); + } + + obj.path = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.pack = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int pathLen = VarInt.peek(buf, varPos1); - if (pathLen < 0) { - throw ProtocolException.negativeLength("Path", pathLen); - } - - if (pathLen > 4096000) { - throw ProtocolException.stringTooLong("Path", pathLen, 4096000); - } - - obj.path = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,9 +97,13 @@ public class AssetPath { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Pack", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -83,9 +111,13 @@ public class AssetPath { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -146,15 +178,11 @@ public class AssetPath { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int packOffset = buffer.getIntLE(offset + 1); - if (packOffset < 0) { + if (packOffset < 0 || packOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Pack"); } int pos = offset + 9 + packOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Pack"); - } - int packLen = VarInt.peek(buffer, pos); if (packLen < 0) { return ValidationResult.error("Invalid string length for Pack"); @@ -164,7 +192,7 @@ public class AssetPath { return ValidationResult.error("Pack exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(packLen); pos += packLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Pack"); @@ -173,15 +201,11 @@ public class AssetPath { if ((nullBits & 2) != 0) { int pathOffset = buffer.getIntLE(offset + 5); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Path"); } int posx = offset + 9 + pathOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - int pathLen = VarInt.peek(buffer, posx); if (pathLen < 0) { return ValidationResult.error("Invalid string length for Path"); @@ -191,7 +215,7 @@ public class AssetPath { return ValidationResult.error("Path exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(pathLen); posx += pathLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Path"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/AuthorInfo.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/AuthorInfo.java index 68c2963a..e7d6489e 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/AuthorInfo.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/AuthorInfo.java @@ -39,51 +39,85 @@ public class AuthorInfo { @Nonnull public static AuthorInfo deserialize(@Nonnull ByteBuf buf, int offset) { - AuthorInfo obj = new AuthorInfo(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("AuthorInfo", 13, buf.readableBytes() - offset); + } else { + AuthorInfo obj = new AuthorInfo(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Email", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int emailLen = VarInt.peek(buf, varPos1); + if (emailLen < 0) { + throw ProtocolException.invalidVarInt("Email"); + } + + int emailVarIntLen = VarInt.size(emailLen); + if (emailLen > 4096000) { + throw ProtocolException.stringTooLong("Email", emailLen, 4096000); + } + + if (varPos1 + emailVarIntLen + emailLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Email", varPos1 + emailVarIntLen + emailLen, buf.readableBytes()); + } + + obj.email = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Url", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int urlLen = VarInt.peek(buf, varPos2); + if (urlLen < 0) { + throw ProtocolException.invalidVarInt("Url"); + } + + int urlVarIntLen = VarInt.size(urlLen); + if (urlLen > 4096000) { + throw ProtocolException.stringTooLong("Url", urlLen, 4096000); + } + + if (varPos2 + urlVarIntLen + urlLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Url", varPos2 + urlVarIntLen + urlLen, buf.readableBytes()); + } + + obj.url = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int emailLen = VarInt.peek(buf, varPos1); - if (emailLen < 0) { - throw ProtocolException.negativeLength("Email", emailLen); - } - - if (emailLen > 4096000) { - throw ProtocolException.stringTooLong("Email", emailLen, 4096000); - } - - obj.email = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int urlLen = VarInt.peek(buf, varPos2); - if (urlLen < 0) { - throw ProtocolException.negativeLength("Url", urlLen); - } - - if (urlLen > 4096000) { - throw ProtocolException.stringTooLong("Url", urlLen, 4096000); - } - - obj.url = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -91,9 +125,13 @@ public class AuthorInfo { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -101,9 +139,13 @@ public class AuthorInfo { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Email", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -111,9 +153,13 @@ public class AuthorInfo { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Url", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -191,15 +237,11 @@ public class AuthorInfo { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int nameOffset = buffer.getIntLE(offset + 1); - if (nameOffset < 0) { + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Name"); } int pos = offset + 13 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - int nameLen = VarInt.peek(buffer, pos); if (nameLen < 0) { return ValidationResult.error("Invalid string length for Name"); @@ -209,7 +251,7 @@ public class AuthorInfo { return ValidationResult.error("Name exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Name"); @@ -218,15 +260,11 @@ public class AuthorInfo { if ((nullBits & 2) != 0) { int emailOffset = buffer.getIntLE(offset + 5); - if (emailOffset < 0) { + if (emailOffset < 0 || emailOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Email"); } int posx = offset + 13 + emailOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Email"); - } - int emailLen = VarInt.peek(buffer, posx); if (emailLen < 0) { return ValidationResult.error("Invalid string length for Email"); @@ -236,7 +274,7 @@ public class AuthorInfo { return ValidationResult.error("Email exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(emailLen); posx += emailLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Email"); @@ -245,15 +283,11 @@ public class AuthorInfo { if ((nullBits & 4) != 0) { int urlOffset = buffer.getIntLE(offset + 9); - if (urlOffset < 0) { + if (urlOffset < 0 || urlOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Url"); } int posxx = offset + 13 + urlOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Url"); - } - int urlLen = VarInt.peek(buffer, posxx); if (urlLen < 0) { return ValidationResult.error("Invalid string length for Url"); @@ -263,7 +297,7 @@ public class AuthorInfo { return ValidationResult.error("Url exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(urlLen); posxx += urlLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Url"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/FailureReply.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/FailureReply.java index 474b1e9e..58a05e84 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/FailureReply.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/FailureReply.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -48,16 +49,20 @@ public class FailureReply implements Packet, ToServerPacket, ToClientPacket { @Nonnull public static FailureReply deserialize(@Nonnull ByteBuf buf, int offset) { - FailureReply obj = new FailureReply(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.message = FormattedMessage.deserialize(buf, pos); - pos += FormattedMessage.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("FailureReply", 5, buf.readableBytes() - offset); + } else { + FailureReply obj = new FailureReply(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.message = FormattedMessage.deserialize(buf, pos); + pos += FormattedMessage.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/JsonUpdateCommand.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/JsonUpdateCommand.java index a2310b65..dfab028e 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/JsonUpdateCommand.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/JsonUpdateCommand.java @@ -59,112 +59,154 @@ public class JsonUpdateCommand { @Nonnull public static JsonUpdateCommand deserialize(@Nonnull ByteBuf buf, int offset) { - JsonUpdateCommand obj = new JsonUpdateCommand(); - byte nullBits = buf.getByte(offset); - obj.type = JsonUpdateType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - obj.rebuildCaches = AssetEditorRebuildCaches.deserialize(buf, offset + 2); - } - - if ((nullBits & 2) != 0) { - int varPos0 = offset + 23 + buf.getIntLE(offset + 7); - int pathCount = VarInt.peek(buf, varPos0); - if (pathCount < 0) { - throw ProtocolException.negativeLength("Path", pathCount); + if (buf.readableBytes() - offset < 23) { + throw ProtocolException.bufferTooSmall("JsonUpdateCommand", 23, buf.readableBytes() - offset); + } else { + JsonUpdateCommand obj = new JsonUpdateCommand(); + byte nullBits = buf.getByte(offset); + obj.type = JsonUpdateType.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + obj.rebuildCaches = AssetEditorRebuildCaches.deserialize(buf, offset + 2); } - if (pathCount > 4096000) { - throw ProtocolException.arrayTooLong("Path", pathCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + pathCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Path", varPos0 + varIntLen + pathCount * 1, buf.readableBytes()); - } - - obj.path = new String[pathCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < pathCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("path[" + i + "]", strLen); + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 7); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("path[" + i + "]", strLen, 4096000); + int varPos0 = offset + 23 + varPosBase0; + int pathCount = VarInt.peek(buf, varPos0); + if (pathCount < 0) { + throw ProtocolException.invalidVarInt("Path"); } - int strVarLen = VarInt.length(buf, elemPos); - obj.path[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 23 + buf.getIntLE(offset + 11); - int valueLen = VarInt.peek(buf, varPos1); - if (valueLen < 0) { - throw ProtocolException.negativeLength("Value", valueLen); - } - - if (valueLen > 4096000) { - throw ProtocolException.stringTooLong("Value", valueLen, 4096000); - } - - obj.value = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 8) != 0) { - int varPos2 = offset + 23 + buf.getIntLE(offset + 15); - int previousValueLen = VarInt.peek(buf, varPos2); - if (previousValueLen < 0) { - throw ProtocolException.negativeLength("PreviousValue", previousValueLen); - } - - if (previousValueLen > 4096000) { - throw ProtocolException.stringTooLong("PreviousValue", previousValueLen, 4096000); - } - - obj.previousValue = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - if ((nullBits & 16) != 0) { - int varPos3 = offset + 23 + buf.getIntLE(offset + 19); - int firstCreatedPropertyCount = VarInt.peek(buf, varPos3); - if (firstCreatedPropertyCount < 0) { - throw ProtocolException.negativeLength("FirstCreatedProperty", firstCreatedPropertyCount); - } - - if (firstCreatedPropertyCount > 4096000) { - throw ProtocolException.arrayTooLong("FirstCreatedProperty", firstCreatedPropertyCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos3); - if (varPos3 + varIntLen + firstCreatedPropertyCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FirstCreatedProperty", varPos3 + varIntLen + firstCreatedPropertyCount * 1, buf.readableBytes()); - } - - obj.firstCreatedProperty = new String[firstCreatedPropertyCount]; - int elemPos = varPos3 + varIntLen; - - for (int i = 0; i < firstCreatedPropertyCount; i++) { - int strLenx = VarInt.peek(buf, elemPos); - if (strLenx < 0) { - throw ProtocolException.negativeLength("firstCreatedProperty[" + i + "]", strLenx); + int varIntLen = VarInt.size(pathCount); + if (pathCount > 4096000) { + throw ProtocolException.arrayTooLong("Path", pathCount, 4096000); } - if (strLenx > 4096000) { - throw ProtocolException.stringTooLong("firstCreatedProperty[" + i + "]", strLenx, 4096000); + if (varPos0 + varIntLen + pathCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Path", varPos0 + varIntLen + pathCount * 1, buf.readableBytes()); } - int strVarLen = VarInt.length(buf, elemPos); - obj.firstCreatedProperty[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLenx; - } - } + obj.path = new String[pathCount]; + int elemPos = varPos0 + varIntLen; - return obj; + for (int i = 0; i < pathCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("path[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("path[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("path[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.path[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 11); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("Value", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 23 + varPosBase1; + int valueLen = VarInt.peek(buf, varPos1); + if (valueLen < 0) { + throw ProtocolException.invalidVarInt("Value"); + } + + int valueVarIntLen = VarInt.size(valueLen); + if (valueLen > 4096000) { + throw ProtocolException.stringTooLong("Value", valueLen, 4096000); + } + + if (varPos1 + valueVarIntLen + valueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Value", varPos1 + valueVarIntLen + valueLen, buf.readableBytes()); + } + + obj.value = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 15); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("PreviousValue", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 23 + varPosBase2; + int previousValueLen = VarInt.peek(buf, varPos2); + if (previousValueLen < 0) { + throw ProtocolException.invalidVarInt("PreviousValue"); + } + + int previousValueVarIntLen = VarInt.size(previousValueLen); + if (previousValueLen > 4096000) { + throw ProtocolException.stringTooLong("PreviousValue", previousValueLen, 4096000); + } + + if (varPos2 + previousValueVarIntLen + previousValueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("PreviousValue", varPos2 + previousValueVarIntLen + previousValueLen, buf.readableBytes()); + } + + obj.previousValue = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 16) != 0) { + int varPosBase3 = buf.getIntLE(offset + 19); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("FirstCreatedProperty", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 23 + varPosBase3; + int firstCreatedPropertyCount = VarInt.peek(buf, varPos3); + if (firstCreatedPropertyCount < 0) { + throw ProtocolException.invalidVarInt("FirstCreatedProperty"); + } + + int varIntLenx = VarInt.size(firstCreatedPropertyCount); + if (firstCreatedPropertyCount > 4096000) { + throw ProtocolException.arrayTooLong("FirstCreatedProperty", firstCreatedPropertyCount, 4096000); + } + + if (varPos3 + varIntLenx + firstCreatedPropertyCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FirstCreatedProperty", varPos3 + varIntLenx + firstCreatedPropertyCount * 1, buf.readableBytes()); + } + + obj.firstCreatedProperty = new String[firstCreatedPropertyCount]; + int elemPos = varPos3 + varIntLenx; + + for (int i = 0; i < firstCreatedPropertyCount; i++) { + int strLenx = VarInt.peek(buf, elemPos); + if (strLenx < 0) { + throw ProtocolException.invalidVarInt("firstCreatedProperty[" + i + "]"); + } + + int strVarLenx = VarInt.size(strLenx); + if (strLenx > 4096000) { + throw ProtocolException.stringTooLong("firstCreatedProperty[" + i + "]", strLenx, 4096000); + } + + if (elemPos + strVarLenx + strLenx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("firstCreatedProperty[" + i + "]", elemPos + strVarLenx + strLenx, buf.readableBytes()); + } + + obj.firstCreatedProperty[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLenx + strLenx; + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -172,13 +214,17 @@ public class JsonUpdateCommand { int maxEnd = 23; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 7); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 23 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; } if (pos0 - offset > maxEnd) { @@ -188,9 +234,13 @@ public class JsonUpdateCommand { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 11); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("Value", fieldOffset1, maxEnd); + } + int pos1 = offset + 23 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -198,9 +248,13 @@ public class JsonUpdateCommand { if ((nullBits & 8) != 0) { int fieldOffset2 = buf.getIntLE(offset + 15); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("PreviousValue", fieldOffset2, maxEnd); + } + int pos2 = offset + 23 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -208,13 +262,17 @@ public class JsonUpdateCommand { if ((nullBits & 16) != 0) { int fieldOffset3 = buf.getIntLE(offset + 19); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 23) { + throw ProtocolException.invalidOffset("FirstCreatedProperty", fieldOffset3, maxEnd); + } + int pos3 = offset + 23 + fieldOffset3; int arrLen = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3); + pos3 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos3); - pos3 += VarInt.length(buf, pos3) + sl; + pos3 += VarInt.size(sl) + sl; } if (pos3 - offset > maxEnd) { @@ -348,133 +406,122 @@ public class JsonUpdateCommand { return ValidationResult.error("Buffer too small: expected at least 23 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 2) != 0) { - int pathOffset = buffer.getIntLE(offset + 7); - if (pathOffset < 0) { - return ValidationResult.error("Invalid offset for Path"); - } - - int pos = offset + 23 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - - int pathCount = VarInt.peek(buffer, pos); - if (pathCount < 0) { - return ValidationResult.error("Invalid array count for Path"); - } - - if (pathCount > 4096000) { - return ValidationResult.error("Path exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < pathCount; i++) { - int strLen = VarInt.peek(buffer, pos); - if (strLen < 0) { - return ValidationResult.error("Invalid string length in Path"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid JsonUpdateType value for Type"); + } else { + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 7); + if (v < 0 || v > buffer.writerIndex() - offset - 23) { + return ValidationResult.error("Invalid offset for Path"); } - pos += VarInt.length(buffer, pos); - pos += strLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in Path"); - } - } - } - - if ((nullBits & 4) != 0) { - int valueOffset = buffer.getIntLE(offset + 11); - if (valueOffset < 0) { - return ValidationResult.error("Invalid offset for Value"); - } - - int posx = offset + 23 + valueOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Value"); - } - - int valueLen = VarInt.peek(buffer, posx); - if (valueLen < 0) { - return ValidationResult.error("Invalid string length for Value"); - } - - if (valueLen > 4096000) { - return ValidationResult.error("Value exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += valueLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Value"); - } - } - - if ((nullBits & 8) != 0) { - int previousValueOffset = buffer.getIntLE(offset + 15); - if (previousValueOffset < 0) { - return ValidationResult.error("Invalid offset for PreviousValue"); - } - - int posxx = offset + 23 + previousValueOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for PreviousValue"); - } - - int previousValueLen = VarInt.peek(buffer, posxx); - if (previousValueLen < 0) { - return ValidationResult.error("Invalid string length for PreviousValue"); - } - - if (previousValueLen > 4096000) { - return ValidationResult.error("PreviousValue exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += previousValueLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading PreviousValue"); - } - } - - if ((nullBits & 16) != 0) { - int firstCreatedPropertyOffset = buffer.getIntLE(offset + 19); - if (firstCreatedPropertyOffset < 0) { - return ValidationResult.error("Invalid offset for FirstCreatedProperty"); - } - - int posxxx = offset + 23 + firstCreatedPropertyOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FirstCreatedProperty"); - } - - int firstCreatedPropertyCount = VarInt.peek(buffer, posxxx); - if (firstCreatedPropertyCount < 0) { - return ValidationResult.error("Invalid array count for FirstCreatedProperty"); - } - - if (firstCreatedPropertyCount > 4096000) { - return ValidationResult.error("FirstCreatedProperty exceeds max length 4096000"); - } - - posxxx += VarInt.length(buffer, posxxx); - - for (int i = 0; i < firstCreatedPropertyCount; i++) { - int strLenx = VarInt.peek(buffer, posxxx); - if (strLenx < 0) { - return ValidationResult.error("Invalid string length in FirstCreatedProperty"); + int pos = offset + 23 + v; + int pathCount = VarInt.peek(buffer, pos); + if (pathCount < 0) { + return ValidationResult.error("Invalid array count for Path"); } - posxxx += VarInt.length(buffer, posxxx); - posxxx += strLenx; - if (posxxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in FirstCreatedProperty"); + if (pathCount > 4096000) { + return ValidationResult.error("Path exceeds max length 4096000"); + } + + pos += VarInt.size(pathCount); + + for (int i = 0; i < pathCount; i++) { + int strLen = VarInt.peek(buffer, pos); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in Path"); + } + + pos += VarInt.size(strLen); + pos += strLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in Path"); + } } } - } - return ValidationResult.OK; + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 11); + if (v < 0 || v > buffer.writerIndex() - offset - 23) { + return ValidationResult.error("Invalid offset for Value"); + } + + int posx = offset + 23 + v; + int valueLen = VarInt.peek(buffer, posx); + if (valueLen < 0) { + return ValidationResult.error("Invalid string length for Value"); + } + + if (valueLen > 4096000) { + return ValidationResult.error("Value exceeds max length 4096000"); + } + + posx += VarInt.size(valueLen); + posx += valueLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Value"); + } + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 15); + if (v < 0 || v > buffer.writerIndex() - offset - 23) { + return ValidationResult.error("Invalid offset for PreviousValue"); + } + + int posxx = offset + 23 + v; + int previousValueLen = VarInt.peek(buffer, posxx); + if (previousValueLen < 0) { + return ValidationResult.error("Invalid string length for PreviousValue"); + } + + if (previousValueLen > 4096000) { + return ValidationResult.error("PreviousValue exceeds max length 4096000"); + } + + posxx += VarInt.size(previousValueLen); + posxx += previousValueLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading PreviousValue"); + } + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 23) { + return ValidationResult.error("Invalid offset for FirstCreatedProperty"); + } + + int posxxx = offset + 23 + v; + int firstCreatedPropertyCount = VarInt.peek(buffer, posxxx); + if (firstCreatedPropertyCount < 0) { + return ValidationResult.error("Invalid array count for FirstCreatedProperty"); + } + + if (firstCreatedPropertyCount > 4096000) { + return ValidationResult.error("FirstCreatedProperty exceeds max length 4096000"); + } + + posxxx += VarInt.size(firstCreatedPropertyCount); + + for (int i = 0; i < firstCreatedPropertyCount; i++) { + int strLenx = VarInt.peek(buffer, posxxx); + if (strLenx < 0) { + return ValidationResult.error("Invalid string length in FirstCreatedProperty"); + } + + posxxx += VarInt.size(strLenx); + posxxx += strLenx; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in FirstCreatedProperty"); + } + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/SchemaFile.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/SchemaFile.java index b9583847..74b195d0 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/SchemaFile.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/SchemaFile.java @@ -31,25 +31,33 @@ public class SchemaFile { @Nonnull public static SchemaFile deserialize(@Nonnull ByteBuf buf, int offset) { - SchemaFile obj = new SchemaFile(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int contentLen = VarInt.peek(buf, pos); - if (contentLen < 0) { - throw ProtocolException.negativeLength("Content", contentLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SchemaFile", 1, buf.readableBytes() - offset); + } else { + SchemaFile obj = new SchemaFile(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int contentLen = VarInt.peek(buf, pos); + if (contentLen < 0) { + throw ProtocolException.invalidVarInt("Content"); + } + + int contentVarLen = VarInt.size(contentLen); + if (contentLen > 16777215) { + throw ProtocolException.stringTooLong("Content", contentLen, 16777215); + } + + if (pos + contentVarLen + contentLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Content", pos + contentVarLen + contentLen, buf.readableBytes()); + } + + obj.content = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += contentVarLen + contentLen; } - if (contentLen > 16777215) { - throw ProtocolException.stringTooLong("Content", contentLen, 16777215); - } - - int contentVarLen = VarInt.length(buf, pos); - obj.content = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += contentVarLen + contentLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -57,7 +65,7 @@ public class SchemaFile { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -100,7 +108,7 @@ public class SchemaFile { return ValidationResult.error("Content exceeds max length 16777215"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(contentLen); pos += contentLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Content"); diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/SuccessReply.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/SuccessReply.java index bd0e9334..3739fe35 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/SuccessReply.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/SuccessReply.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -48,16 +49,20 @@ public class SuccessReply implements Packet, ToServerPacket, ToClientPacket { @Nonnull public static SuccessReply deserialize(@Nonnull ByteBuf buf, int offset) { - SuccessReply obj = new SuccessReply(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.message = FormattedMessage.deserialize(buf, pos); - pos += FormattedMessage.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("SuccessReply", 5, buf.readableBytes() - offset); + } else { + SuccessReply obj = new SuccessReply(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.message = FormattedMessage.deserialize(buf, pos); + pos += FormattedMessage.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/asseteditor/TimestampedAssetReference.java b/src/com/hypixel/hytale/protocol/packets/asseteditor/TimestampedAssetReference.java index e9aabcd1..495db405 100644 --- a/src/com/hypixel/hytale/protocol/packets/asseteditor/TimestampedAssetReference.java +++ b/src/com/hypixel/hytale/protocol/packets/asseteditor/TimestampedAssetReference.java @@ -35,28 +35,47 @@ public class TimestampedAssetReference { @Nonnull public static TimestampedAssetReference deserialize(@Nonnull ByteBuf buf, int offset) { - TimestampedAssetReference obj = new TimestampedAssetReference(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - obj.path = AssetPath.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("TimestampedAssetReference", 9, buf.readableBytes() - offset); + } else { + TimestampedAssetReference obj = new TimestampedAssetReference(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int timestampLen = VarInt.peek(buf, varPos1); - if (timestampLen < 0) { - throw ProtocolException.negativeLength("Timestamp", timestampLen); + int varPos0 = offset + 9 + varPosBase0; + obj.path = AssetPath.deserialize(buf, varPos0); } - if (timestampLen > 4096000) { - throw ProtocolException.stringTooLong("Timestamp", timestampLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Timestamp", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int timestampLen = VarInt.peek(buf, varPos1); + if (timestampLen < 0) { + throw ProtocolException.invalidVarInt("Timestamp"); + } + + int timestampVarIntLen = VarInt.size(timestampLen); + if (timestampLen > 4096000) { + throw ProtocolException.stringTooLong("Timestamp", timestampLen, 4096000); + } + + if (varPos1 + timestampVarIntLen + timestampLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Timestamp", varPos1 + timestampVarIntLen + timestampLen, buf.readableBytes()); + } + + obj.timestamp = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.timestamp = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -64,6 +83,10 @@ public class TimestampedAssetReference { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Path", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; pos0 += AssetPath.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -73,9 +96,13 @@ public class TimestampedAssetReference { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Timestamp", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -136,15 +163,11 @@ public class TimestampedAssetReference { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int pathOffset = buffer.getIntLE(offset + 1); - if (pathOffset < 0) { + if (pathOffset < 0 || pathOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Path"); } int pos = offset + 9 + pathOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Path"); - } - ValidationResult pathResult = AssetPath.validateStructure(buffer, pos); if (!pathResult.isValid()) { return ValidationResult.error("Invalid Path: " + pathResult.error()); @@ -155,16 +178,12 @@ public class TimestampedAssetReference { if ((nullBits & 2) != 0) { int timestampOffset = buffer.getIntLE(offset + 5); - if (timestampOffset < 0) { + if (timestampOffset < 0 || timestampOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Timestamp"); } - int posx = offset + 9 + timestampOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Timestamp"); - } - - int timestampLen = VarInt.peek(buffer, posx); + int pos = offset + 9 + timestampOffset; + int timestampLen = VarInt.peek(buffer, pos); if (timestampLen < 0) { return ValidationResult.error("Invalid string length for Timestamp"); } @@ -173,9 +192,9 @@ public class TimestampedAssetReference { return ValidationResult.error("Timestamp exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); - posx += timestampLen; - if (posx > buffer.writerIndex()) { + pos += VarInt.size(timestampLen); + pos += timestampLen; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Timestamp"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/TrackOrUpdateObjective.java b/src/com/hypixel/hytale/protocol/packets/assets/TrackOrUpdateObjective.java index bd66420d..fdb21003 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/TrackOrUpdateObjective.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/TrackOrUpdateObjective.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Objective; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,15 +45,19 @@ public class TrackOrUpdateObjective implements Packet, ToClientPacket { @Nonnull public static TrackOrUpdateObjective deserialize(@Nonnull ByteBuf buf, int offset) { - TrackOrUpdateObjective obj = new TrackOrUpdateObjective(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - obj.objective = Objective.deserialize(buf, pos); - pos += Objective.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("TrackOrUpdateObjective", 1, buf.readableBytes() - offset); + } else { + TrackOrUpdateObjective obj = new TrackOrUpdateObjective(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + obj.objective = Objective.deserialize(buf, pos); + pos += Objective.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UntrackObjective.java b/src/com/hypixel/hytale/protocol/packets/assets/UntrackObjective.java index c5981c25..d0184dbb 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UntrackObjective.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UntrackObjective.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,9 +45,13 @@ public class UntrackObjective implements Packet, ToClientPacket { @Nonnull public static UntrackObjective deserialize(@Nonnull ByteBuf buf, int offset) { - UntrackObjective obj = new UntrackObjective(); - obj.objectiveUuid = PacketIO.readUUID(buf, offset + 0); - return obj; + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("UntrackObjective", 16, buf.readableBytes() - offset); + } else { + UntrackObjective obj = new UntrackObjective(); + obj.objectiveUuid = PacketIO.readUUID(buf, offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateAmbienceFX.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateAmbienceFX.java index 8eff1826..2cfdafe6 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateAmbienceFX.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateAmbienceFX.java @@ -57,36 +57,41 @@ public class UpdateAmbienceFX implements Packet, ToClientPacket { @Nonnull public static UpdateAmbienceFX deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateAmbienceFX obj = new UpdateAmbienceFX(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int ambienceFXCount = VarInt.peek(buf, pos); - if (ambienceFXCount < 0) { - throw ProtocolException.negativeLength("AmbienceFX", ambienceFXCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateAmbienceFX", 6, buf.readableBytes() - offset); + } else { + UpdateAmbienceFX obj = new UpdateAmbienceFX(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int ambienceFXCount = VarInt.peek(buf, pos); + if (ambienceFXCount < 0) { + throw ProtocolException.invalidVarInt("AmbienceFX"); + } - if (ambienceFXCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("AmbienceFX", ambienceFXCount, 4096000); - } + int ambienceFXVarLen = VarInt.size(ambienceFXCount); + if (ambienceFXCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("AmbienceFX", ambienceFXCount, 4096000); + } - pos += VarInt.size(ambienceFXCount); - obj.ambienceFX = new HashMap<>(ambienceFXCount); + pos += ambienceFXVarLen; + obj.ambienceFX = new HashMap<>(ambienceFXCount); - for (int i = 0; i < ambienceFXCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - AmbienceFX val = AmbienceFX.deserialize(buf, pos); - pos += AmbienceFX.computeBytesConsumed(buf, pos); - if (obj.ambienceFX.put(key, val) != null) { - throw ProtocolException.duplicateKey("ambienceFX", key); + for (int i = 0; i < ambienceFXCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + AmbienceFX val = AmbienceFX.deserialize(buf, pos); + pos += AmbienceFX.computeBytesConsumed(buf, pos); + if (obj.ambienceFX.put(key, val) != null) { + throw ProtocolException.duplicateKey("ambienceFX", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateAmbienceFX implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateAmbienceFX implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int ambienceFXCount = VarInt.peek(buffer, pos); - if (ambienceFXCount < 0) { - return ValidationResult.error("Invalid dictionary count for AmbienceFX"); - } - - if (ambienceFXCount > 4096000) { - return ValidationResult.error("AmbienceFX exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < ambienceFXCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int ambienceFXCount = VarInt.peek(buffer, v); + if (ambienceFXCount < 0) { + return ValidationResult.error("Invalid dictionary count for AmbienceFX"); } - pos += AmbienceFX.computeBytesConsumed(buffer, pos); - } - } + if (ambienceFXCount > 4096000) { + return ValidationResult.error("AmbienceFX exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(ambienceFXCount); + + for (int i = 0; i < ambienceFXCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += AmbienceFX.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateAudioCategories.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateAudioCategories.java index 3174f2ad..0299283d 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateAudioCategories.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateAudioCategories.java @@ -57,36 +57,41 @@ public class UpdateAudioCategories implements Packet, ToClientPacket { @Nonnull public static UpdateAudioCategories deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateAudioCategories obj = new UpdateAudioCategories(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int categoriesCount = VarInt.peek(buf, pos); - if (categoriesCount < 0) { - throw ProtocolException.negativeLength("Categories", categoriesCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateAudioCategories", 6, buf.readableBytes() - offset); + } else { + UpdateAudioCategories obj = new UpdateAudioCategories(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int categoriesCount = VarInt.peek(buf, pos); + if (categoriesCount < 0) { + throw ProtocolException.invalidVarInt("Categories"); + } - if (categoriesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Categories", categoriesCount, 4096000); - } + int categoriesVarLen = VarInt.size(categoriesCount); + if (categoriesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Categories", categoriesCount, 4096000); + } - pos += VarInt.size(categoriesCount); - obj.categories = new HashMap<>(categoriesCount); + pos += categoriesVarLen; + obj.categories = new HashMap<>(categoriesCount); - for (int i = 0; i < categoriesCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - AudioCategory val = AudioCategory.deserialize(buf, pos); - pos += AudioCategory.computeBytesConsumed(buf, pos); - if (obj.categories.put(key, val) != null) { - throw ProtocolException.duplicateKey("categories", key); + for (int i = 0; i < categoriesCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + AudioCategory val = AudioCategory.deserialize(buf, pos); + pos += AudioCategory.computeBytesConsumed(buf, pos); + if (obj.categories.put(key, val) != null) { + throw ProtocolException.duplicateKey("categories", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateAudioCategories implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateAudioCategories implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int categoriesCount = VarInt.peek(buffer, pos); - if (categoriesCount < 0) { - return ValidationResult.error("Invalid dictionary count for Categories"); - } - - if (categoriesCount > 4096000) { - return ValidationResult.error("Categories exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < categoriesCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int categoriesCount = VarInt.peek(buffer, v); + if (categoriesCount < 0) { + return ValidationResult.error("Invalid dictionary count for Categories"); } - pos += AudioCategory.computeBytesConsumed(buffer, pos); - } - } + if (categoriesCount > 4096000) { + return ValidationResult.error("Categories exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(categoriesCount); + + for (int i = 0; i < categoriesCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += AudioCategory.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockBreakingDecals.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockBreakingDecals.java index a1ffe042..7f72a0b6 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockBreakingDecals.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockBreakingDecals.java @@ -55,45 +55,54 @@ public class UpdateBlockBreakingDecals implements Packet, ToClientPacket { @Nonnull public static UpdateBlockBreakingDecals deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateBlockBreakingDecals obj = new UpdateBlockBreakingDecals(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int blockBreakingDecalsCount = VarInt.peek(buf, pos); - if (blockBreakingDecalsCount < 0) { - throw ProtocolException.negativeLength("BlockBreakingDecals", blockBreakingDecalsCount); - } - - if (blockBreakingDecalsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("BlockBreakingDecals", blockBreakingDecalsCount, 4096000); - } - - pos += VarInt.size(blockBreakingDecalsCount); - obj.blockBreakingDecals = new HashMap<>(blockBreakingDecalsCount); - - for (int i = 0; i < blockBreakingDecalsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateBlockBreakingDecals", 2, buf.readableBytes() - offset); + } else { + UpdateBlockBreakingDecals obj = new UpdateBlockBreakingDecals(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int blockBreakingDecalsCount = VarInt.peek(buf, pos); + if (blockBreakingDecalsCount < 0) { + throw ProtocolException.invalidVarInt("BlockBreakingDecals"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int blockBreakingDecalsVarLen = VarInt.size(blockBreakingDecalsCount); + if (blockBreakingDecalsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("BlockBreakingDecals", blockBreakingDecalsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - BlockBreakingDecal val = BlockBreakingDecal.deserialize(buf, pos); - pos += BlockBreakingDecal.computeBytesConsumed(buf, pos); - if (obj.blockBreakingDecals.put(key, val) != null) { - throw ProtocolException.duplicateKey("blockBreakingDecals", key); + pos += blockBreakingDecalsVarLen; + obj.blockBreakingDecals = new HashMap<>(blockBreakingDecalsCount); + + for (int i = 0; i < blockBreakingDecalsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + BlockBreakingDecal val = BlockBreakingDecal.deserialize(buf, pos); + pos += BlockBreakingDecal.computeBytesConsumed(buf, pos); + if (obj.blockBreakingDecals.put(key, val) != null) { + throw ProtocolException.duplicateKey("blockBreakingDecals", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,11 +110,11 @@ public class UpdateBlockBreakingDecals implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += BlockBreakingDecal.computeBytesConsumed(buf, pos); } } @@ -157,40 +166,45 @@ public class UpdateBlockBreakingDecals implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int blockBreakingDecalsCount = VarInt.peek(buffer, pos); - if (blockBreakingDecalsCount < 0) { - return ValidationResult.error("Invalid dictionary count for BlockBreakingDecals"); - } - - if (blockBreakingDecalsCount > 4096000) { - return ValidationResult.error("BlockBreakingDecals exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < blockBreakingDecalsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int blockBreakingDecalsCount = VarInt.peek(buffer, v); + if (blockBreakingDecalsCount < 0) { + return ValidationResult.error("Invalid dictionary count for BlockBreakingDecals"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + if (blockBreakingDecalsCount > 4096000) { + return ValidationResult.error("BlockBreakingDecals exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } + v += VarInt.size(blockBreakingDecalsCount); - pos += BlockBreakingDecal.computeBytesConsumed(buffer, pos); + for (int i = 0; i < blockBreakingDecalsCount; i++) { + int keyLen = VarInt.peek(buffer, v); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + v += VarInt.size(keyLen); + v += keyLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += BlockBreakingDecal.computeBytesConsumed(buffer, v); + } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockGroups.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockGroups.java index da0f71c8..19f4e3d7 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockGroups.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockGroups.java @@ -55,45 +55,54 @@ public class UpdateBlockGroups implements Packet, ToClientPacket { @Nonnull public static UpdateBlockGroups deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateBlockGroups obj = new UpdateBlockGroups(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int groupsCount = VarInt.peek(buf, pos); - if (groupsCount < 0) { - throw ProtocolException.negativeLength("Groups", groupsCount); - } - - if (groupsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Groups", groupsCount, 4096000); - } - - pos += VarInt.size(groupsCount); - obj.groups = new HashMap<>(groupsCount); - - for (int i = 0; i < groupsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateBlockGroups", 2, buf.readableBytes() - offset); + } else { + UpdateBlockGroups obj = new UpdateBlockGroups(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int groupsCount = VarInt.peek(buf, pos); + if (groupsCount < 0) { + throw ProtocolException.invalidVarInt("Groups"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int groupsVarLen = VarInt.size(groupsCount); + if (groupsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Groups", groupsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - BlockGroup val = BlockGroup.deserialize(buf, pos); - pos += BlockGroup.computeBytesConsumed(buf, pos); - if (obj.groups.put(key, val) != null) { - throw ProtocolException.duplicateKey("groups", key); + pos += groupsVarLen; + obj.groups = new HashMap<>(groupsCount); + + for (int i = 0; i < groupsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + BlockGroup val = BlockGroup.deserialize(buf, pos); + pos += BlockGroup.computeBytesConsumed(buf, pos); + if (obj.groups.put(key, val) != null) { + throw ProtocolException.duplicateKey("groups", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,11 +110,11 @@ public class UpdateBlockGroups implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += BlockGroup.computeBytesConsumed(buf, pos); } } @@ -157,40 +166,45 @@ public class UpdateBlockGroups implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int groupsCount = VarInt.peek(buffer, pos); - if (groupsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Groups"); - } - - if (groupsCount > 4096000) { - return ValidationResult.error("Groups exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < groupsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int groupsCount = VarInt.peek(buffer, v); + if (groupsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Groups"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + if (groupsCount > 4096000) { + return ValidationResult.error("Groups exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } + v += VarInt.size(groupsCount); - pos += BlockGroup.computeBytesConsumed(buffer, pos); + for (int i = 0; i < groupsCount; i++) { + int keyLen = VarInt.peek(buffer, v); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + v += VarInt.size(keyLen); + v += keyLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += BlockGroup.computeBytesConsumed(buffer, v); + } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockHitboxes.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockHitboxes.java index 293cb77a..9155715d 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockHitboxes.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockHitboxes.java @@ -58,56 +58,61 @@ public class UpdateBlockHitboxes implements Packet, ToClientPacket { @Nonnull public static UpdateBlockHitboxes deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateBlockHitboxes obj = new UpdateBlockHitboxes(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int blockBaseHitboxesCount = VarInt.peek(buf, pos); - if (blockBaseHitboxesCount < 0) { - throw ProtocolException.negativeLength("BlockBaseHitboxes", blockBaseHitboxesCount); - } - - if (blockBaseHitboxesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("BlockBaseHitboxes", blockBaseHitboxesCount, 4096000); - } - - pos += VarInt.size(blockBaseHitboxesCount); - obj.blockBaseHitboxes = new HashMap<>(blockBaseHitboxesCount); - - for (int i = 0; i < blockBaseHitboxesCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - int valLen = VarInt.peek(buf, pos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateBlockHitboxes", 6, buf.readableBytes() - offset); + } else { + UpdateBlockHitboxes obj = new UpdateBlockHitboxes(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int blockBaseHitboxesCount = VarInt.peek(buf, pos); + if (blockBaseHitboxesCount < 0) { + throw ProtocolException.invalidVarInt("BlockBaseHitboxes"); } - if (valLen > 64) { - throw ProtocolException.arrayTooLong("val", valLen, 64); + int blockBaseHitboxesVarLen = VarInt.size(blockBaseHitboxesCount); + if (blockBaseHitboxesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("BlockBaseHitboxes", blockBaseHitboxesCount, 4096000); } - int valVarLen = VarInt.length(buf, pos); - if (pos + valVarLen + valLen * 24L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("val", pos + valVarLen + valLen * 24, buf.readableBytes()); - } + pos += blockBaseHitboxesVarLen; + obj.blockBaseHitboxes = new HashMap<>(blockBaseHitboxesCount); - pos += valVarLen; - Hitbox[] val = new Hitbox[valLen]; + for (int i = 0; i < blockBaseHitboxesCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + int valLen = VarInt.peek(buf, pos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } - for (int valIdx = 0; valIdx < valLen; valIdx++) { - val[valIdx] = Hitbox.deserialize(buf, pos); - pos += Hitbox.computeBytesConsumed(buf, pos); - } + int valVarLen = VarInt.size(valLen); + if (valLen > 64) { + throw ProtocolException.arrayTooLong("val", valLen, 64); + } - if (obj.blockBaseHitboxes.put(key, val) != null) { - throw ProtocolException.duplicateKey("blockBaseHitboxes", key); + if (pos + valVarLen + valLen * 24L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", pos + valVarLen + valLen * 24, buf.readableBytes()); + } + + pos += valVarLen; + Hitbox[] val = new Hitbox[valLen]; + + for (int valIdx = 0; valIdx < valLen; valIdx++) { + val[valIdx] = Hitbox.deserialize(buf, pos); + pos += Hitbox.computeBytesConsumed(buf, pos); + } + + if (obj.blockBaseHitboxes.put(key, val) != null) { + throw ProtocolException.duplicateKey("blockBaseHitboxes", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -115,12 +120,12 @@ public class UpdateBlockHitboxes implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; int al = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(al); for (int j = 0; j < al; j++) { pos += Hitbox.computeBytesConsumed(buf, pos); @@ -180,39 +185,44 @@ public class UpdateBlockHitboxes implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int blockBaseHitboxesCount = VarInt.peek(buffer, pos); - if (blockBaseHitboxesCount < 0) { - return ValidationResult.error("Invalid dictionary count for BlockBaseHitboxes"); - } - - if (blockBaseHitboxesCount > 4096000) { - return ValidationResult.error("BlockBaseHitboxes exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < blockBaseHitboxesCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int blockBaseHitboxesCount = VarInt.peek(buffer, v); + if (blockBaseHitboxesCount < 0) { + return ValidationResult.error("Invalid dictionary count for BlockBaseHitboxes"); } - int valueArrCount = VarInt.peek(buffer, pos); - if (valueArrCount < 0) { - return ValidationResult.error("Invalid array count for value"); + if (blockBaseHitboxesCount > 4096000) { + return ValidationResult.error("BlockBaseHitboxes exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + v += VarInt.size(blockBaseHitboxesCount); - for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { - pos += 24; + for (int i = 0; i < blockBaseHitboxesCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + int valueArrCount = VarInt.peek(buffer, v); + if (valueArrCount < 0) { + return ValidationResult.error("Invalid array count for value"); + } + + v += VarInt.size(valueArrCount); + + for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) { + v += 24; + } } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockParticleSets.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockParticleSets.java index 93dfb887..bb72ddf5 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockParticleSets.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockParticleSets.java @@ -55,45 +55,54 @@ public class UpdateBlockParticleSets implements Packet, ToClientPacket { @Nonnull public static UpdateBlockParticleSets deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateBlockParticleSets obj = new UpdateBlockParticleSets(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int blockParticleSetsCount = VarInt.peek(buf, pos); - if (blockParticleSetsCount < 0) { - throw ProtocolException.negativeLength("BlockParticleSets", blockParticleSetsCount); - } - - if (blockParticleSetsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("BlockParticleSets", blockParticleSetsCount, 4096000); - } - - pos += VarInt.size(blockParticleSetsCount); - obj.blockParticleSets = new HashMap<>(blockParticleSetsCount); - - for (int i = 0; i < blockParticleSetsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateBlockParticleSets", 2, buf.readableBytes() - offset); + } else { + UpdateBlockParticleSets obj = new UpdateBlockParticleSets(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int blockParticleSetsCount = VarInt.peek(buf, pos); + if (blockParticleSetsCount < 0) { + throw ProtocolException.invalidVarInt("BlockParticleSets"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int blockParticleSetsVarLen = VarInt.size(blockParticleSetsCount); + if (blockParticleSetsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("BlockParticleSets", blockParticleSetsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - BlockParticleSet val = BlockParticleSet.deserialize(buf, pos); - pos += BlockParticleSet.computeBytesConsumed(buf, pos); - if (obj.blockParticleSets.put(key, val) != null) { - throw ProtocolException.duplicateKey("blockParticleSets", key); + pos += blockParticleSetsVarLen; + obj.blockParticleSets = new HashMap<>(blockParticleSetsCount); + + for (int i = 0; i < blockParticleSetsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + BlockParticleSet val = BlockParticleSet.deserialize(buf, pos); + pos += BlockParticleSet.computeBytesConsumed(buf, pos); + if (obj.blockParticleSets.put(key, val) != null) { + throw ProtocolException.duplicateKey("blockParticleSets", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,11 +110,11 @@ public class UpdateBlockParticleSets implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += BlockParticleSet.computeBytesConsumed(buf, pos); } } @@ -157,40 +166,45 @@ public class UpdateBlockParticleSets implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int blockParticleSetsCount = VarInt.peek(buffer, pos); - if (blockParticleSetsCount < 0) { - return ValidationResult.error("Invalid dictionary count for BlockParticleSets"); - } - - if (blockParticleSetsCount > 4096000) { - return ValidationResult.error("BlockParticleSets exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < blockParticleSetsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int blockParticleSetsCount = VarInt.peek(buffer, v); + if (blockParticleSetsCount < 0) { + return ValidationResult.error("Invalid dictionary count for BlockParticleSets"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + if (blockParticleSetsCount > 4096000) { + return ValidationResult.error("BlockParticleSets exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } + v += VarInt.size(blockParticleSetsCount); - pos += BlockParticleSet.computeBytesConsumed(buffer, pos); + for (int i = 0; i < blockParticleSetsCount; i++) { + int keyLen = VarInt.peek(buffer, v); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + v += VarInt.size(keyLen); + v += keyLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += BlockParticleSet.computeBytesConsumed(buffer, v); + } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockSets.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockSets.java index eeae520e..f5d30007 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockSets.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockSets.java @@ -55,45 +55,54 @@ public class UpdateBlockSets implements Packet, ToClientPacket { @Nonnull public static UpdateBlockSets deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateBlockSets obj = new UpdateBlockSets(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int blockSetsCount = VarInt.peek(buf, pos); - if (blockSetsCount < 0) { - throw ProtocolException.negativeLength("BlockSets", blockSetsCount); - } - - if (blockSetsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("BlockSets", blockSetsCount, 4096000); - } - - pos += VarInt.size(blockSetsCount); - obj.blockSets = new HashMap<>(blockSetsCount); - - for (int i = 0; i < blockSetsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateBlockSets", 2, buf.readableBytes() - offset); + } else { + UpdateBlockSets obj = new UpdateBlockSets(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int blockSetsCount = VarInt.peek(buf, pos); + if (blockSetsCount < 0) { + throw ProtocolException.invalidVarInt("BlockSets"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int blockSetsVarLen = VarInt.size(blockSetsCount); + if (blockSetsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("BlockSets", blockSetsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - BlockSet val = BlockSet.deserialize(buf, pos); - pos += BlockSet.computeBytesConsumed(buf, pos); - if (obj.blockSets.put(key, val) != null) { - throw ProtocolException.duplicateKey("blockSets", key); + pos += blockSetsVarLen; + obj.blockSets = new HashMap<>(blockSetsCount); + + for (int i = 0; i < blockSetsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + BlockSet val = BlockSet.deserialize(buf, pos); + pos += BlockSet.computeBytesConsumed(buf, pos); + if (obj.blockSets.put(key, val) != null) { + throw ProtocolException.duplicateKey("blockSets", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,11 +110,11 @@ public class UpdateBlockSets implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += BlockSet.computeBytesConsumed(buf, pos); } } @@ -157,40 +166,45 @@ public class UpdateBlockSets implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int blockSetsCount = VarInt.peek(buffer, pos); - if (blockSetsCount < 0) { - return ValidationResult.error("Invalid dictionary count for BlockSets"); - } - - if (blockSetsCount > 4096000) { - return ValidationResult.error("BlockSets exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < blockSetsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int blockSetsCount = VarInt.peek(buffer, v); + if (blockSetsCount < 0) { + return ValidationResult.error("Invalid dictionary count for BlockSets"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + if (blockSetsCount > 4096000) { + return ValidationResult.error("BlockSets exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } + v += VarInt.size(blockSetsCount); - pos += BlockSet.computeBytesConsumed(buffer, pos); + for (int i = 0; i < blockSetsCount; i++) { + int keyLen = VarInt.peek(buffer, v); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + v += VarInt.size(keyLen); + v += keyLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += BlockSet.computeBytesConsumed(buffer, v); + } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockSoundSets.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockSoundSets.java index cd2626dd..fbbbc346 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockSoundSets.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockSoundSets.java @@ -57,36 +57,41 @@ public class UpdateBlockSoundSets implements Packet, ToClientPacket { @Nonnull public static UpdateBlockSoundSets deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateBlockSoundSets obj = new UpdateBlockSoundSets(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int blockSoundSetsCount = VarInt.peek(buf, pos); - if (blockSoundSetsCount < 0) { - throw ProtocolException.negativeLength("BlockSoundSets", blockSoundSetsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateBlockSoundSets", 6, buf.readableBytes() - offset); + } else { + UpdateBlockSoundSets obj = new UpdateBlockSoundSets(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int blockSoundSetsCount = VarInt.peek(buf, pos); + if (blockSoundSetsCount < 0) { + throw ProtocolException.invalidVarInt("BlockSoundSets"); + } - if (blockSoundSetsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("BlockSoundSets", blockSoundSetsCount, 4096000); - } + int blockSoundSetsVarLen = VarInt.size(blockSoundSetsCount); + if (blockSoundSetsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("BlockSoundSets", blockSoundSetsCount, 4096000); + } - pos += VarInt.size(blockSoundSetsCount); - obj.blockSoundSets = new HashMap<>(blockSoundSetsCount); + pos += blockSoundSetsVarLen; + obj.blockSoundSets = new HashMap<>(blockSoundSetsCount); - for (int i = 0; i < blockSoundSetsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - BlockSoundSet val = BlockSoundSet.deserialize(buf, pos); - pos += BlockSoundSet.computeBytesConsumed(buf, pos); - if (obj.blockSoundSets.put(key, val) != null) { - throw ProtocolException.duplicateKey("blockSoundSets", key); + for (int i = 0; i < blockSoundSetsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + BlockSoundSet val = BlockSoundSet.deserialize(buf, pos); + pos += BlockSoundSet.computeBytesConsumed(buf, pos); + if (obj.blockSoundSets.put(key, val) != null) { + throw ProtocolException.duplicateKey("blockSoundSets", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateBlockSoundSets implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateBlockSoundSets implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int blockSoundSetsCount = VarInt.peek(buffer, pos); - if (blockSoundSetsCount < 0) { - return ValidationResult.error("Invalid dictionary count for BlockSoundSets"); - } - - if (blockSoundSetsCount > 4096000) { - return ValidationResult.error("BlockSoundSets exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < blockSoundSetsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int blockSoundSetsCount = VarInt.peek(buffer, v); + if (blockSoundSetsCount < 0) { + return ValidationResult.error("Invalid dictionary count for BlockSoundSets"); } - pos += BlockSoundSet.computeBytesConsumed(buffer, pos); - } - } + if (blockSoundSetsCount > 4096000) { + return ValidationResult.error("BlockSoundSets exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(blockSoundSetsCount); + + for (int i = 0; i < blockSoundSetsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += BlockSoundSet.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockTypes.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockTypes.java index 313e52ae..be889796 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockTypes.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateBlockTypes.java @@ -77,40 +77,45 @@ public class UpdateBlockTypes implements Packet, ToClientPacket { @Nonnull public static UpdateBlockTypes deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateBlockTypes obj = new UpdateBlockTypes(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - obj.updateBlockTextures = buf.getByte(offset + 6) != 0; - obj.updateModelTextures = buf.getByte(offset + 7) != 0; - obj.updateModels = buf.getByte(offset + 8) != 0; - obj.updateMapGeometry = buf.getByte(offset + 9) != 0; - int pos = offset + 10; - if ((nullBits & 1) != 0) { - int blockTypesCount = VarInt.peek(buf, pos); - if (blockTypesCount < 0) { - throw ProtocolException.negativeLength("BlockTypes", blockTypesCount); - } + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("UpdateBlockTypes", 10, buf.readableBytes() - offset); + } else { + UpdateBlockTypes obj = new UpdateBlockTypes(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + obj.updateBlockTextures = buf.getByte(offset + 6) != 0; + obj.updateModelTextures = buf.getByte(offset + 7) != 0; + obj.updateModels = buf.getByte(offset + 8) != 0; + obj.updateMapGeometry = buf.getByte(offset + 9) != 0; + int pos = offset + 10; + if ((nullBits & 1) != 0) { + int blockTypesCount = VarInt.peek(buf, pos); + if (blockTypesCount < 0) { + throw ProtocolException.invalidVarInt("BlockTypes"); + } - if (blockTypesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("BlockTypes", blockTypesCount, 4096000); - } + int blockTypesVarLen = VarInt.size(blockTypesCount); + if (blockTypesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("BlockTypes", blockTypesCount, 4096000); + } - pos += VarInt.size(blockTypesCount); - obj.blockTypes = new HashMap<>(blockTypesCount); + pos += blockTypesVarLen; + obj.blockTypes = new HashMap<>(blockTypesCount); - for (int i = 0; i < blockTypesCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - BlockType val = BlockType.deserialize(buf, pos); - pos += BlockType.computeBytesConsumed(buf, pos); - if (obj.blockTypes.put(key, val) != null) { - throw ProtocolException.duplicateKey("blockTypes", key); + for (int i = 0; i < blockTypesCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + BlockType val = BlockType.deserialize(buf, pos); + pos += BlockType.computeBytesConsumed(buf, pos); + if (obj.blockTypes.put(key, val) != null) { + throw ProtocolException.duplicateKey("blockTypes", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -118,7 +123,7 @@ public class UpdateBlockTypes implements Packet, ToClientPacket { int pos = offset + 10; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -178,30 +183,35 @@ public class UpdateBlockTypes implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 10 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 10; - if ((nullBits & 1) != 0) { - int blockTypesCount = VarInt.peek(buffer, pos); - if (blockTypesCount < 0) { - return ValidationResult.error("Invalid dictionary count for BlockTypes"); - } - - if (blockTypesCount > 4096000) { - return ValidationResult.error("BlockTypes exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < blockTypesCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 10; + if ((nullBits & 1) != 0) { + int blockTypesCount = VarInt.peek(buffer, v); + if (blockTypesCount < 0) { + return ValidationResult.error("Invalid dictionary count for BlockTypes"); } - pos += BlockType.computeBytesConsumed(buffer, pos); - } - } + if (blockTypesCount > 4096000) { + return ValidationResult.error("BlockTypes exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(blockTypesCount); + + for (int i = 0; i < blockTypesCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += BlockType.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateCameraShake.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateCameraShake.java index e58d0e64..ad47953c 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateCameraShake.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateCameraShake.java @@ -54,35 +54,40 @@ public class UpdateCameraShake implements Packet, ToClientPacket { @Nonnull public static UpdateCameraShake deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateCameraShake obj = new UpdateCameraShake(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int profilesCount = VarInt.peek(buf, pos); - if (profilesCount < 0) { - throw ProtocolException.negativeLength("Profiles", profilesCount); - } + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateCameraShake", 2, buf.readableBytes() - offset); + } else { + UpdateCameraShake obj = new UpdateCameraShake(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int profilesCount = VarInt.peek(buf, pos); + if (profilesCount < 0) { + throw ProtocolException.invalidVarInt("Profiles"); + } - if (profilesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Profiles", profilesCount, 4096000); - } + int profilesVarLen = VarInt.size(profilesCount); + if (profilesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Profiles", profilesCount, 4096000); + } - pos += VarInt.size(profilesCount); - obj.profiles = new HashMap<>(profilesCount); + pos += profilesVarLen; + obj.profiles = new HashMap<>(profilesCount); - for (int i = 0; i < profilesCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - CameraShake val = CameraShake.deserialize(buf, pos); - pos += CameraShake.computeBytesConsumed(buf, pos); - if (obj.profiles.put(key, val) != null) { - throw ProtocolException.duplicateKey("profiles", key); + for (int i = 0; i < profilesCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + CameraShake val = CameraShake.deserialize(buf, pos); + pos += CameraShake.computeBytesConsumed(buf, pos); + if (obj.profiles.put(key, val) != null) { + throw ProtocolException.duplicateKey("profiles", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,7 +95,7 @@ public class UpdateCameraShake implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -145,30 +150,35 @@ public class UpdateCameraShake implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int profilesCount = VarInt.peek(buffer, pos); - if (profilesCount < 0) { - return ValidationResult.error("Invalid dictionary count for Profiles"); - } - - if (profilesCount > 4096000) { - return ValidationResult.error("Profiles exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < profilesCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int profilesCount = VarInt.peek(buffer, v); + if (profilesCount < 0) { + return ValidationResult.error("Invalid dictionary count for Profiles"); } - pos += CameraShake.computeBytesConsumed(buffer, pos); - } - } + if (profilesCount > 4096000) { + return ValidationResult.error("Profiles exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(profilesCount); + + for (int i = 0; i < profilesCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += CameraShake.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEmotes.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEmotes.java new file mode 100644 index 00000000..b7181634 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEmotes.java @@ -0,0 +1,222 @@ +package com.hypixel.hytale.protocol.packets.assets; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ProtocolEmote; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.UpdateType; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Map.Entry; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class UpdateEmotes implements Packet, ToClientPacket { + public static final int PACKET_ID = 86; + public static final boolean IS_COMPRESSED = true; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 6; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 6; + public static final int MAX_SIZE = 1677721600; + @Nonnull + public UpdateType type = UpdateType.Init; + public int maxId; + @Nullable + public Map emotes; + + @Override + public int getId() { + return 86; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public UpdateEmotes() { + } + + public UpdateEmotes(@Nonnull UpdateType type, int maxId, @Nullable Map emotes) { + this.type = type; + this.maxId = maxId; + this.emotes = emotes; + } + + public UpdateEmotes(@Nonnull UpdateEmotes other) { + this.type = other.type; + this.maxId = other.maxId; + this.emotes = other.emotes; + } + + @Nonnull + public static UpdateEmotes deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateEmotes", 6, buf.readableBytes() - offset); + } else { + UpdateEmotes obj = new UpdateEmotes(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int emotesCount = VarInt.peek(buf, pos); + if (emotesCount < 0) { + throw ProtocolException.invalidVarInt("Emotes"); + } + + int emotesVarLen = VarInt.size(emotesCount); + if (emotesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Emotes", emotesCount, 4096000); + } + + pos += emotesVarLen; + obj.emotes = new HashMap<>(emotesCount); + + for (int i = 0; i < emotesCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + ProtocolEmote val = ProtocolEmote.deserialize(buf, pos); + pos += ProtocolEmote.computeBytesConsumed(buf, pos); + if (obj.emotes.put(key, val) != null) { + throw ProtocolException.duplicateKey("emotes", key); + } + } + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int dictLen = VarInt.peek(buf, pos); + pos += VarInt.size(dictLen); + + for (int i = 0; i < dictLen; i++) { + pos += 4; + pos += ProtocolEmote.computeBytesConsumed(buf, pos); + } + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.emotes != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + buf.writeByte(this.type.getValue()); + buf.writeIntLE(this.maxId); + if (this.emotes != null) { + if (this.emotes.size() > 4096000) { + throw ProtocolException.dictionaryTooLarge("Emotes", this.emotes.size(), 4096000); + } + + VarInt.write(buf, this.emotes.size()); + + for (Entry e : this.emotes.entrySet()) { + buf.writeIntLE(e.getKey()); + e.getValue().serialize(buf); + } + } + } + + @Override + public int computeSize() { + int size = 6; + if (this.emotes != null) { + int emotesSize = 0; + + for (Entry kvp : this.emotes.entrySet()) { + emotesSize += 4 + kvp.getValue().computeSize(); + } + + size += VarInt.size(this.emotes.size()) + emotesSize; + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 6) { + return ValidationResult.error("Buffer too small: expected at least 6 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int emotesCount = VarInt.peek(buffer, v); + if (emotesCount < 0) { + return ValidationResult.error("Invalid dictionary count for Emotes"); + } + + if (emotesCount > 4096000) { + return ValidationResult.error("Emotes exceeds max length 4096000"); + } + + v += VarInt.size(emotesCount); + + for (int i = 0; i < emotesCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += ProtocolEmote.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } + } + } + + public UpdateEmotes clone() { + UpdateEmotes copy = new UpdateEmotes(); + copy.type = this.type; + copy.maxId = this.maxId; + if (this.emotes != null) { + Map m = new HashMap<>(); + + for (Entry e : this.emotes.entrySet()) { + m.put(e.getKey(), e.getValue().clone()); + } + + copy.emotes = m; + } + + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof UpdateEmotes other) + ? false + : Objects.equals(this.type, other.type) && this.maxId == other.maxId && Objects.equals(this.emotes, other.emotes); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.type, this.maxId, this.emotes); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityEffects.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityEffects.java index ac3a9774..41759b49 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityEffects.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityEffects.java @@ -57,36 +57,41 @@ public class UpdateEntityEffects implements Packet, ToClientPacket { @Nonnull public static UpdateEntityEffects deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateEntityEffects obj = new UpdateEntityEffects(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int entityEffectsCount = VarInt.peek(buf, pos); - if (entityEffectsCount < 0) { - throw ProtocolException.negativeLength("EntityEffects", entityEffectsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateEntityEffects", 6, buf.readableBytes() - offset); + } else { + UpdateEntityEffects obj = new UpdateEntityEffects(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int entityEffectsCount = VarInt.peek(buf, pos); + if (entityEffectsCount < 0) { + throw ProtocolException.invalidVarInt("EntityEffects"); + } - if (entityEffectsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("EntityEffects", entityEffectsCount, 4096000); - } + int entityEffectsVarLen = VarInt.size(entityEffectsCount); + if (entityEffectsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("EntityEffects", entityEffectsCount, 4096000); + } - pos += VarInt.size(entityEffectsCount); - obj.entityEffects = new HashMap<>(entityEffectsCount); + pos += entityEffectsVarLen; + obj.entityEffects = new HashMap<>(entityEffectsCount); - for (int i = 0; i < entityEffectsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - EntityEffect val = EntityEffect.deserialize(buf, pos); - pos += EntityEffect.computeBytesConsumed(buf, pos); - if (obj.entityEffects.put(key, val) != null) { - throw ProtocolException.duplicateKey("entityEffects", key); + for (int i = 0; i < entityEffectsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + EntityEffect val = EntityEffect.deserialize(buf, pos); + pos += EntityEffect.computeBytesConsumed(buf, pos); + if (obj.entityEffects.put(key, val) != null) { + throw ProtocolException.duplicateKey("entityEffects", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateEntityEffects implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateEntityEffects implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int entityEffectsCount = VarInt.peek(buffer, pos); - if (entityEffectsCount < 0) { - return ValidationResult.error("Invalid dictionary count for EntityEffects"); - } - - if (entityEffectsCount > 4096000) { - return ValidationResult.error("EntityEffects exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < entityEffectsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int entityEffectsCount = VarInt.peek(buffer, v); + if (entityEffectsCount < 0) { + return ValidationResult.error("Invalid dictionary count for EntityEffects"); } - pos += EntityEffect.computeBytesConsumed(buffer, pos); - } - } + if (entityEffectsCount > 4096000) { + return ValidationResult.error("EntityEffects exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(entityEffectsCount); + + for (int i = 0; i < entityEffectsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += EntityEffect.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityStatTypes.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityStatTypes.java index 03c95bb1..0e8d8340 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityStatTypes.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityStatTypes.java @@ -57,36 +57,41 @@ public class UpdateEntityStatTypes implements Packet, ToClientPacket { @Nonnull public static UpdateEntityStatTypes deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateEntityStatTypes obj = new UpdateEntityStatTypes(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int typesCount = VarInt.peek(buf, pos); - if (typesCount < 0) { - throw ProtocolException.negativeLength("Types", typesCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateEntityStatTypes", 6, buf.readableBytes() - offset); + } else { + UpdateEntityStatTypes obj = new UpdateEntityStatTypes(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int typesCount = VarInt.peek(buf, pos); + if (typesCount < 0) { + throw ProtocolException.invalidVarInt("Types"); + } - if (typesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Types", typesCount, 4096000); - } + int typesVarLen = VarInt.size(typesCount); + if (typesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Types", typesCount, 4096000); + } - pos += VarInt.size(typesCount); - obj.types = new HashMap<>(typesCount); + pos += typesVarLen; + obj.types = new HashMap<>(typesCount); - for (int i = 0; i < typesCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - EntityStatType val = EntityStatType.deserialize(buf, pos); - pos += EntityStatType.computeBytesConsumed(buf, pos); - if (obj.types.put(key, val) != null) { - throw ProtocolException.duplicateKey("types", key); + for (int i = 0; i < typesCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + EntityStatType val = EntityStatType.deserialize(buf, pos); + pos += EntityStatType.computeBytesConsumed(buf, pos); + if (obj.types.put(key, val) != null) { + throw ProtocolException.duplicateKey("types", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateEntityStatTypes implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateEntityStatTypes implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int typesCount = VarInt.peek(buffer, pos); - if (typesCount < 0) { - return ValidationResult.error("Invalid dictionary count for Types"); - } - - if (typesCount > 4096000) { - return ValidationResult.error("Types exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < typesCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int typesCount = VarInt.peek(buffer, v); + if (typesCount < 0) { + return ValidationResult.error("Invalid dictionary count for Types"); } - pos += EntityStatType.computeBytesConsumed(buffer, pos); - } - } + if (typesCount > 4096000) { + return ValidationResult.error("Types exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(typesCount); + + for (int i = 0; i < typesCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += EntityStatType.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityUIComponents.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityUIComponents.java index 1fefe3c4..931993f2 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityUIComponents.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEntityUIComponents.java @@ -57,36 +57,41 @@ public class UpdateEntityUIComponents implements Packet, ToClientPacket { @Nonnull public static UpdateEntityUIComponents deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateEntityUIComponents obj = new UpdateEntityUIComponents(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int componentsCount = VarInt.peek(buf, pos); - if (componentsCount < 0) { - throw ProtocolException.negativeLength("Components", componentsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateEntityUIComponents", 6, buf.readableBytes() - offset); + } else { + UpdateEntityUIComponents obj = new UpdateEntityUIComponents(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int componentsCount = VarInt.peek(buf, pos); + if (componentsCount < 0) { + throw ProtocolException.invalidVarInt("Components"); + } - if (componentsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Components", componentsCount, 4096000); - } + int componentsVarLen = VarInt.size(componentsCount); + if (componentsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Components", componentsCount, 4096000); + } - pos += VarInt.size(componentsCount); - obj.components = new HashMap<>(componentsCount); + pos += componentsVarLen; + obj.components = new HashMap<>(componentsCount); - for (int i = 0; i < componentsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - EntityUIComponent val = EntityUIComponent.deserialize(buf, pos); - pos += EntityUIComponent.computeBytesConsumed(buf, pos); - if (obj.components.put(key, val) != null) { - throw ProtocolException.duplicateKey("components", key); + for (int i = 0; i < componentsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + EntityUIComponent val = EntityUIComponent.deserialize(buf, pos); + pos += EntityUIComponent.computeBytesConsumed(buf, pos); + if (obj.components.put(key, val) != null) { + throw ProtocolException.duplicateKey("components", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateEntityUIComponents implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateEntityUIComponents implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int componentsCount = VarInt.peek(buffer, pos); - if (componentsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Components"); - } - - if (componentsCount > 4096000) { - return ValidationResult.error("Components exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < componentsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int componentsCount = VarInt.peek(buffer, v); + if (componentsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Components"); } - pos += EntityUIComponent.computeBytesConsumed(buffer, pos); - } - } + if (componentsCount > 4096000) { + return ValidationResult.error("Components exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(componentsCount); + + for (int i = 0; i < componentsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += EntityUIComponent.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEnvironments.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEnvironments.java index 63722fae..de2f5724 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEnvironments.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEnvironments.java @@ -60,37 +60,42 @@ public class UpdateEnvironments implements Packet, ToClientPacket { @Nonnull public static UpdateEnvironments deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateEnvironments obj = new UpdateEnvironments(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - obj.rebuildMapGeometry = buf.getByte(offset + 6) != 0; - int pos = offset + 7; - if ((nullBits & 1) != 0) { - int environmentsCount = VarInt.peek(buf, pos); - if (environmentsCount < 0) { - throw ProtocolException.negativeLength("Environments", environmentsCount); - } + if (buf.readableBytes() - offset < 7) { + throw ProtocolException.bufferTooSmall("UpdateEnvironments", 7, buf.readableBytes() - offset); + } else { + UpdateEnvironments obj = new UpdateEnvironments(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + obj.rebuildMapGeometry = buf.getByte(offset + 6) != 0; + int pos = offset + 7; + if ((nullBits & 1) != 0) { + int environmentsCount = VarInt.peek(buf, pos); + if (environmentsCount < 0) { + throw ProtocolException.invalidVarInt("Environments"); + } - if (environmentsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Environments", environmentsCount, 4096000); - } + int environmentsVarLen = VarInt.size(environmentsCount); + if (environmentsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Environments", environmentsCount, 4096000); + } - pos += VarInt.size(environmentsCount); - obj.environments = new HashMap<>(environmentsCount); + pos += environmentsVarLen; + obj.environments = new HashMap<>(environmentsCount); - for (int i = 0; i < environmentsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - WorldEnvironment val = WorldEnvironment.deserialize(buf, pos); - pos += WorldEnvironment.computeBytesConsumed(buf, pos); - if (obj.environments.put(key, val) != null) { - throw ProtocolException.duplicateKey("environments", key); + for (int i = 0; i < environmentsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + WorldEnvironment val = WorldEnvironment.deserialize(buf, pos); + pos += WorldEnvironment.computeBytesConsumed(buf, pos); + if (obj.environments.put(key, val) != null) { + throw ProtocolException.duplicateKey("environments", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -98,7 +103,7 @@ public class UpdateEnvironments implements Packet, ToClientPacket { int pos = offset + 7; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -155,30 +160,35 @@ public class UpdateEnvironments implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 7 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 7; - if ((nullBits & 1) != 0) { - int environmentsCount = VarInt.peek(buffer, pos); - if (environmentsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Environments"); - } - - if (environmentsCount > 4096000) { - return ValidationResult.error("Environments exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < environmentsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 7; + if ((nullBits & 1) != 0) { + int environmentsCount = VarInt.peek(buffer, v); + if (environmentsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Environments"); } - pos += WorldEnvironment.computeBytesConsumed(buffer, pos); - } - } + if (environmentsCount > 4096000) { + return ValidationResult.error("Environments exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(environmentsCount); + + for (int i = 0; i < environmentsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += WorldEnvironment.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEqualizerEffects.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEqualizerEffects.java index 51b0e2d6..328ad92b 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateEqualizerEffects.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateEqualizerEffects.java @@ -57,36 +57,41 @@ public class UpdateEqualizerEffects implements Packet, ToClientPacket { @Nonnull public static UpdateEqualizerEffects deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateEqualizerEffects obj = new UpdateEqualizerEffects(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int effectsCount = VarInt.peek(buf, pos); - if (effectsCount < 0) { - throw ProtocolException.negativeLength("Effects", effectsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateEqualizerEffects", 6, buf.readableBytes() - offset); + } else { + UpdateEqualizerEffects obj = new UpdateEqualizerEffects(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int effectsCount = VarInt.peek(buf, pos); + if (effectsCount < 0) { + throw ProtocolException.invalidVarInt("Effects"); + } - if (effectsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Effects", effectsCount, 4096000); - } + int effectsVarLen = VarInt.size(effectsCount); + if (effectsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Effects", effectsCount, 4096000); + } - pos += VarInt.size(effectsCount); - obj.effects = new HashMap<>(effectsCount); + pos += effectsVarLen; + obj.effects = new HashMap<>(effectsCount); - for (int i = 0; i < effectsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - EqualizerEffect val = EqualizerEffect.deserialize(buf, pos); - pos += EqualizerEffect.computeBytesConsumed(buf, pos); - if (obj.effects.put(key, val) != null) { - throw ProtocolException.duplicateKey("effects", key); + for (int i = 0; i < effectsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + EqualizerEffect val = EqualizerEffect.deserialize(buf, pos); + pos += EqualizerEffect.computeBytesConsumed(buf, pos); + if (obj.effects.put(key, val) != null) { + throw ProtocolException.duplicateKey("effects", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateEqualizerEffects implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateEqualizerEffects implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int effectsCount = VarInt.peek(buffer, pos); - if (effectsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Effects"); - } - - if (effectsCount > 4096000) { - return ValidationResult.error("Effects exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < effectsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int effectsCount = VarInt.peek(buffer, v); + if (effectsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Effects"); } - pos += EqualizerEffect.computeBytesConsumed(buffer, pos); - } - } + if (effectsCount > 4096000) { + return ValidationResult.error("Effects exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(effectsCount); + + for (int i = 0; i < effectsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += EqualizerEffect.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateFieldcraftCategories.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateFieldcraftCategories.java index 23a4497a..784d074a 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateFieldcraftCategories.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateFieldcraftCategories.java @@ -52,35 +52,39 @@ public class UpdateFieldcraftCategories implements Packet, ToClientPacket { @Nonnull public static UpdateFieldcraftCategories deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateFieldcraftCategories obj = new UpdateFieldcraftCategories(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int itemCategoriesCount = VarInt.peek(buf, pos); - if (itemCategoriesCount < 0) { - throw ProtocolException.negativeLength("ItemCategories", itemCategoriesCount); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateFieldcraftCategories", 2, buf.readableBytes() - offset); + } else { + UpdateFieldcraftCategories obj = new UpdateFieldcraftCategories(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int itemCategoriesCount = VarInt.peek(buf, pos); + if (itemCategoriesCount < 0) { + throw ProtocolException.invalidVarInt("ItemCategories"); + } + + int itemCategoriesVarLen = VarInt.size(itemCategoriesCount); + if (itemCategoriesCount > 4096000) { + throw ProtocolException.arrayTooLong("ItemCategories", itemCategoriesCount, 4096000); + } + + if (pos + itemCategoriesVarLen + itemCategoriesCount * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemCategories", pos + itemCategoriesVarLen + itemCategoriesCount * 6, buf.readableBytes()); + } + + pos += itemCategoriesVarLen; + obj.itemCategories = new ItemCategory[itemCategoriesCount]; + + for (int i = 0; i < itemCategoriesCount; i++) { + obj.itemCategories[i] = ItemCategory.deserialize(buf, pos); + pos += ItemCategory.computeBytesConsumed(buf, pos); + } } - if (itemCategoriesCount > 4096000) { - throw ProtocolException.arrayTooLong("ItemCategories", itemCategoriesCount, 4096000); - } - - int itemCategoriesVarLen = VarInt.size(itemCategoriesCount); - if (pos + itemCategoriesVarLen + itemCategoriesCount * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ItemCategories", pos + itemCategoriesVarLen + itemCategoriesCount * 6, buf.readableBytes()); - } - - pos += itemCategoriesVarLen; - obj.itemCategories = new ItemCategory[itemCategoriesCount]; - - for (int i = 0; i < itemCategoriesCount; i++) { - obj.itemCategories[i] = ItemCategory.deserialize(buf, pos); - pos += ItemCategory.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -88,7 +92,7 @@ public class UpdateFieldcraftCategories implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += ItemCategory.computeBytesConsumed(buf, pos); @@ -141,30 +145,35 @@ public class UpdateFieldcraftCategories implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int itemCategoriesCount = VarInt.peek(buffer, pos); - if (itemCategoriesCount < 0) { - return ValidationResult.error("Invalid array count for ItemCategories"); - } - - if (itemCategoriesCount > 4096000) { - return ValidationResult.error("ItemCategories exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < itemCategoriesCount; i++) { - ValidationResult structResult = ItemCategory.validateStructure(buffer, pos); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ItemCategory in ItemCategories[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int itemCategoriesCount = VarInt.peek(buffer, v); + if (itemCategoriesCount < 0) { + return ValidationResult.error("Invalid array count for ItemCategories"); } - pos += ItemCategory.computeBytesConsumed(buffer, pos); - } - } + if (itemCategoriesCount > 4096000) { + return ValidationResult.error("ItemCategories exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(itemCategoriesCount); + + for (int i = 0; i < itemCategoriesCount; i++) { + ValidationResult structResult = ItemCategory.validateStructure(buffer, v); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ItemCategory in ItemCategories[" + i + "]: " + structResult.error()); + } + + v += ItemCategory.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateFluidFX.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateFluidFX.java index 70720883..c4ea6473 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateFluidFX.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateFluidFX.java @@ -57,36 +57,41 @@ public class UpdateFluidFX implements Packet, ToClientPacket { @Nonnull public static UpdateFluidFX deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateFluidFX obj = new UpdateFluidFX(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int fluidFXCount = VarInt.peek(buf, pos); - if (fluidFXCount < 0) { - throw ProtocolException.negativeLength("FluidFX", fluidFXCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateFluidFX", 6, buf.readableBytes() - offset); + } else { + UpdateFluidFX obj = new UpdateFluidFX(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int fluidFXCount = VarInt.peek(buf, pos); + if (fluidFXCount < 0) { + throw ProtocolException.invalidVarInt("FluidFX"); + } - if (fluidFXCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("FluidFX", fluidFXCount, 4096000); - } + int fluidFXVarLen = VarInt.size(fluidFXCount); + if (fluidFXCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("FluidFX", fluidFXCount, 4096000); + } - pos += VarInt.size(fluidFXCount); - obj.fluidFX = new HashMap<>(fluidFXCount); + pos += fluidFXVarLen; + obj.fluidFX = new HashMap<>(fluidFXCount); - for (int i = 0; i < fluidFXCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - FluidFX val = FluidFX.deserialize(buf, pos); - pos += FluidFX.computeBytesConsumed(buf, pos); - if (obj.fluidFX.put(key, val) != null) { - throw ProtocolException.duplicateKey("fluidFX", key); + for (int i = 0; i < fluidFXCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + FluidFX val = FluidFX.deserialize(buf, pos); + pos += FluidFX.computeBytesConsumed(buf, pos); + if (obj.fluidFX.put(key, val) != null) { + throw ProtocolException.duplicateKey("fluidFX", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateFluidFX implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateFluidFX implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int fluidFXCount = VarInt.peek(buffer, pos); - if (fluidFXCount < 0) { - return ValidationResult.error("Invalid dictionary count for FluidFX"); - } - - if (fluidFXCount > 4096000) { - return ValidationResult.error("FluidFX exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < fluidFXCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int fluidFXCount = VarInt.peek(buffer, v); + if (fluidFXCount < 0) { + return ValidationResult.error("Invalid dictionary count for FluidFX"); } - pos += FluidFX.computeBytesConsumed(buffer, pos); - } - } + if (fluidFXCount > 4096000) { + return ValidationResult.error("FluidFX exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(fluidFXCount); + + for (int i = 0; i < fluidFXCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += FluidFX.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateFluids.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateFluids.java index 64a2f8c4..e7a4d6d1 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateFluids.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateFluids.java @@ -57,36 +57,41 @@ public class UpdateFluids implements Packet, ToClientPacket { @Nonnull public static UpdateFluids deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateFluids obj = new UpdateFluids(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int fluidsCount = VarInt.peek(buf, pos); - if (fluidsCount < 0) { - throw ProtocolException.negativeLength("Fluids", fluidsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateFluids", 6, buf.readableBytes() - offset); + } else { + UpdateFluids obj = new UpdateFluids(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int fluidsCount = VarInt.peek(buf, pos); + if (fluidsCount < 0) { + throw ProtocolException.invalidVarInt("Fluids"); + } - if (fluidsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Fluids", fluidsCount, 4096000); - } + int fluidsVarLen = VarInt.size(fluidsCount); + if (fluidsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Fluids", fluidsCount, 4096000); + } - pos += VarInt.size(fluidsCount); - obj.fluids = new HashMap<>(fluidsCount); + pos += fluidsVarLen; + obj.fluids = new HashMap<>(fluidsCount); - for (int i = 0; i < fluidsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - Fluid val = Fluid.deserialize(buf, pos); - pos += Fluid.computeBytesConsumed(buf, pos); - if (obj.fluids.put(key, val) != null) { - throw ProtocolException.duplicateKey("fluids", key); + for (int i = 0; i < fluidsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + Fluid val = Fluid.deserialize(buf, pos); + pos += Fluid.computeBytesConsumed(buf, pos); + if (obj.fluids.put(key, val) != null) { + throw ProtocolException.duplicateKey("fluids", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateFluids implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateFluids implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int fluidsCount = VarInt.peek(buffer, pos); - if (fluidsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Fluids"); - } - - if (fluidsCount > 4096000) { - return ValidationResult.error("Fluids exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < fluidsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int fluidsCount = VarInt.peek(buffer, v); + if (fluidsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Fluids"); } - pos += Fluid.computeBytesConsumed(buffer, pos); - } - } + if (fluidsCount > 4096000) { + return ValidationResult.error("Fluids exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(fluidsCount); + + for (int i = 0; i < fluidsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += Fluid.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateHitboxCollisionConfig.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateHitboxCollisionConfig.java index 6c7044be..450a0016 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateHitboxCollisionConfig.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateHitboxCollisionConfig.java @@ -57,36 +57,41 @@ public class UpdateHitboxCollisionConfig implements Packet, ToClientPacket { @Nonnull public static UpdateHitboxCollisionConfig deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateHitboxCollisionConfig obj = new UpdateHitboxCollisionConfig(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int hitboxCollisionConfigsCount = VarInt.peek(buf, pos); - if (hitboxCollisionConfigsCount < 0) { - throw ProtocolException.negativeLength("HitboxCollisionConfigs", hitboxCollisionConfigsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateHitboxCollisionConfig", 6, buf.readableBytes() - offset); + } else { + UpdateHitboxCollisionConfig obj = new UpdateHitboxCollisionConfig(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int hitboxCollisionConfigsCount = VarInt.peek(buf, pos); + if (hitboxCollisionConfigsCount < 0) { + throw ProtocolException.invalidVarInt("HitboxCollisionConfigs"); + } - if (hitboxCollisionConfigsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("HitboxCollisionConfigs", hitboxCollisionConfigsCount, 4096000); - } + int hitboxCollisionConfigsVarLen = VarInt.size(hitboxCollisionConfigsCount); + if (hitboxCollisionConfigsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("HitboxCollisionConfigs", hitboxCollisionConfigsCount, 4096000); + } - pos += VarInt.size(hitboxCollisionConfigsCount); - obj.hitboxCollisionConfigs = new HashMap<>(hitboxCollisionConfigsCount); + pos += hitboxCollisionConfigsVarLen; + obj.hitboxCollisionConfigs = new HashMap<>(hitboxCollisionConfigsCount); - for (int i = 0; i < hitboxCollisionConfigsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - HitboxCollisionConfig val = HitboxCollisionConfig.deserialize(buf, pos); - pos += HitboxCollisionConfig.computeBytesConsumed(buf, pos); - if (obj.hitboxCollisionConfigs.put(key, val) != null) { - throw ProtocolException.duplicateKey("hitboxCollisionConfigs", key); + for (int i = 0; i < hitboxCollisionConfigsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + HitboxCollisionConfig val = HitboxCollisionConfig.deserialize(buf, pos); + pos += HitboxCollisionConfig.computeBytesConsumed(buf, pos); + if (obj.hitboxCollisionConfigs.put(key, val) != null) { + throw ProtocolException.duplicateKey("hitboxCollisionConfigs", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateHitboxCollisionConfig implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -144,30 +149,35 @@ public class UpdateHitboxCollisionConfig implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int hitboxCollisionConfigsCount = VarInt.peek(buffer, pos); - if (hitboxCollisionConfigsCount < 0) { - return ValidationResult.error("Invalid dictionary count for HitboxCollisionConfigs"); - } - - if (hitboxCollisionConfigsCount > 4096000) { - return ValidationResult.error("HitboxCollisionConfigs exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < hitboxCollisionConfigsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int hitboxCollisionConfigsCount = VarInt.peek(buffer, v); + if (hitboxCollisionConfigsCount < 0) { + return ValidationResult.error("Invalid dictionary count for HitboxCollisionConfigs"); } - pos += 5; - } - } + if (hitboxCollisionConfigsCount > 4096000) { + return ValidationResult.error("HitboxCollisionConfigs exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(hitboxCollisionConfigsCount); + + for (int i = 0; i < hitboxCollisionConfigsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += 5; + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateInteractions.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateInteractions.java index c7a53570..848d83c8 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateInteractions.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateInteractions.java @@ -57,36 +57,41 @@ public class UpdateInteractions implements Packet, ToClientPacket { @Nonnull public static UpdateInteractions deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateInteractions obj = new UpdateInteractions(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int interactionsCount = VarInt.peek(buf, pos); - if (interactionsCount < 0) { - throw ProtocolException.negativeLength("Interactions", interactionsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateInteractions", 6, buf.readableBytes() - offset); + } else { + UpdateInteractions obj = new UpdateInteractions(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int interactionsCount = VarInt.peek(buf, pos); + if (interactionsCount < 0) { + throw ProtocolException.invalidVarInt("Interactions"); + } - if (interactionsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); - } + int interactionsVarLen = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + } - pos += VarInt.size(interactionsCount); - obj.interactions = new HashMap<>(interactionsCount); + pos += interactionsVarLen; + obj.interactions = new HashMap<>(interactionsCount); - for (int i = 0; i < interactionsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - Interaction val = Interaction.deserialize(buf, pos); - pos += Interaction.computeBytesConsumed(buf, pos); - if (obj.interactions.put(key, val) != null) { - throw ProtocolException.duplicateKey("interactions", key); + for (int i = 0; i < interactionsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + Interaction val = Interaction.deserialize(buf, pos); + pos += Interaction.computeBytesConsumed(buf, pos); + if (obj.interactions.put(key, val) != null) { + throw ProtocolException.duplicateKey("interactions", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateInteractions implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateInteractions implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int interactionsCount = VarInt.peek(buffer, pos); - if (interactionsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Interactions"); - } - - if (interactionsCount > 4096000) { - return ValidationResult.error("Interactions exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < interactionsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int interactionsCount = VarInt.peek(buffer, v); + if (interactionsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Interactions"); } - pos += Interaction.computeBytesConsumed(buffer, pos); - } - } + if (interactionsCount > 4096000) { + return ValidationResult.error("Interactions exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(interactionsCount); + + for (int i = 0; i < interactionsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += Interaction.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemCategories.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemCategories.java index 1f034175..540ecc30 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemCategories.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemCategories.java @@ -52,35 +52,39 @@ public class UpdateItemCategories implements Packet, ToClientPacket { @Nonnull public static UpdateItemCategories deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateItemCategories obj = new UpdateItemCategories(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int itemCategoriesCount = VarInt.peek(buf, pos); - if (itemCategoriesCount < 0) { - throw ProtocolException.negativeLength("ItemCategories", itemCategoriesCount); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateItemCategories", 2, buf.readableBytes() - offset); + } else { + UpdateItemCategories obj = new UpdateItemCategories(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int itemCategoriesCount = VarInt.peek(buf, pos); + if (itemCategoriesCount < 0) { + throw ProtocolException.invalidVarInt("ItemCategories"); + } + + int itemCategoriesVarLen = VarInt.size(itemCategoriesCount); + if (itemCategoriesCount > 4096000) { + throw ProtocolException.arrayTooLong("ItemCategories", itemCategoriesCount, 4096000); + } + + if (pos + itemCategoriesVarLen + itemCategoriesCount * 6L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemCategories", pos + itemCategoriesVarLen + itemCategoriesCount * 6, buf.readableBytes()); + } + + pos += itemCategoriesVarLen; + obj.itemCategories = new ItemCategory[itemCategoriesCount]; + + for (int i = 0; i < itemCategoriesCount; i++) { + obj.itemCategories[i] = ItemCategory.deserialize(buf, pos); + pos += ItemCategory.computeBytesConsumed(buf, pos); + } } - if (itemCategoriesCount > 4096000) { - throw ProtocolException.arrayTooLong("ItemCategories", itemCategoriesCount, 4096000); - } - - int itemCategoriesVarLen = VarInt.size(itemCategoriesCount); - if (pos + itemCategoriesVarLen + itemCategoriesCount * 6L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ItemCategories", pos + itemCategoriesVarLen + itemCategoriesCount * 6, buf.readableBytes()); - } - - pos += itemCategoriesVarLen; - obj.itemCategories = new ItemCategory[itemCategoriesCount]; - - for (int i = 0; i < itemCategoriesCount; i++) { - obj.itemCategories[i] = ItemCategory.deserialize(buf, pos); - pos += ItemCategory.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -88,7 +92,7 @@ public class UpdateItemCategories implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += ItemCategory.computeBytesConsumed(buf, pos); @@ -141,30 +145,35 @@ public class UpdateItemCategories implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int itemCategoriesCount = VarInt.peek(buffer, pos); - if (itemCategoriesCount < 0) { - return ValidationResult.error("Invalid array count for ItemCategories"); - } - - if (itemCategoriesCount > 4096000) { - return ValidationResult.error("ItemCategories exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < itemCategoriesCount; i++) { - ValidationResult structResult = ItemCategory.validateStructure(buffer, pos); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ItemCategory in ItemCategories[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int itemCategoriesCount = VarInt.peek(buffer, v); + if (itemCategoriesCount < 0) { + return ValidationResult.error("Invalid array count for ItemCategories"); } - pos += ItemCategory.computeBytesConsumed(buffer, pos); - } - } + if (itemCategoriesCount > 4096000) { + return ValidationResult.error("ItemCategories exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(itemCategoriesCount); + + for (int i = 0; i < itemCategoriesCount; i++) { + ValidationResult structResult = ItemCategory.validateStructure(buffer, v); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ItemCategory in ItemCategories[" + i + "]: " + structResult.error()); + } + + v += ItemCategory.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemPlayerAnimations.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemPlayerAnimations.java index 9b91d872..a5b91b6a 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemPlayerAnimations.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemPlayerAnimations.java @@ -55,45 +55,54 @@ public class UpdateItemPlayerAnimations implements Packet, ToClientPacket { @Nonnull public static UpdateItemPlayerAnimations deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateItemPlayerAnimations obj = new UpdateItemPlayerAnimations(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int itemPlayerAnimationsCount = VarInt.peek(buf, pos); - if (itemPlayerAnimationsCount < 0) { - throw ProtocolException.negativeLength("ItemPlayerAnimations", itemPlayerAnimationsCount); - } - - if (itemPlayerAnimationsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ItemPlayerAnimations", itemPlayerAnimationsCount, 4096000); - } - - pos += VarInt.size(itemPlayerAnimationsCount); - obj.itemPlayerAnimations = new HashMap<>(itemPlayerAnimationsCount); - - for (int i = 0; i < itemPlayerAnimationsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateItemPlayerAnimations", 2, buf.readableBytes() - offset); + } else { + UpdateItemPlayerAnimations obj = new UpdateItemPlayerAnimations(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int itemPlayerAnimationsCount = VarInt.peek(buf, pos); + if (itemPlayerAnimationsCount < 0) { + throw ProtocolException.invalidVarInt("ItemPlayerAnimations"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int itemPlayerAnimationsVarLen = VarInt.size(itemPlayerAnimationsCount); + if (itemPlayerAnimationsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ItemPlayerAnimations", itemPlayerAnimationsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - ItemPlayerAnimations val = ItemPlayerAnimations.deserialize(buf, pos); - pos += ItemPlayerAnimations.computeBytesConsumed(buf, pos); - if (obj.itemPlayerAnimations.put(key, val) != null) { - throw ProtocolException.duplicateKey("itemPlayerAnimations", key); + pos += itemPlayerAnimationsVarLen; + obj.itemPlayerAnimations = new HashMap<>(itemPlayerAnimationsCount); + + for (int i = 0; i < itemPlayerAnimationsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + ItemPlayerAnimations val = ItemPlayerAnimations.deserialize(buf, pos); + pos += ItemPlayerAnimations.computeBytesConsumed(buf, pos); + if (obj.itemPlayerAnimations.put(key, val) != null) { + throw ProtocolException.duplicateKey("itemPlayerAnimations", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,11 +110,11 @@ public class UpdateItemPlayerAnimations implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += ItemPlayerAnimations.computeBytesConsumed(buf, pos); } } @@ -157,40 +166,45 @@ public class UpdateItemPlayerAnimations implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int itemPlayerAnimationsCount = VarInt.peek(buffer, pos); - if (itemPlayerAnimationsCount < 0) { - return ValidationResult.error("Invalid dictionary count for ItemPlayerAnimations"); - } - - if (itemPlayerAnimationsCount > 4096000) { - return ValidationResult.error("ItemPlayerAnimations exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < itemPlayerAnimationsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int itemPlayerAnimationsCount = VarInt.peek(buffer, v); + if (itemPlayerAnimationsCount < 0) { + return ValidationResult.error("Invalid dictionary count for ItemPlayerAnimations"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + if (itemPlayerAnimationsCount > 4096000) { + return ValidationResult.error("ItemPlayerAnimations exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } + v += VarInt.size(itemPlayerAnimationsCount); - pos += ItemPlayerAnimations.computeBytesConsumed(buffer, pos); + for (int i = 0; i < itemPlayerAnimationsCount; i++) { + int keyLen = VarInt.peek(buffer, v); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + v += VarInt.size(keyLen); + v += keyLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += ItemPlayerAnimations.computeBytesConsumed(buffer, v); + } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemQualities.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemQualities.java index 65dde823..0c680254 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemQualities.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemQualities.java @@ -57,36 +57,41 @@ public class UpdateItemQualities implements Packet, ToClientPacket { @Nonnull public static UpdateItemQualities deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateItemQualities obj = new UpdateItemQualities(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int itemQualitiesCount = VarInt.peek(buf, pos); - if (itemQualitiesCount < 0) { - throw ProtocolException.negativeLength("ItemQualities", itemQualitiesCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateItemQualities", 6, buf.readableBytes() - offset); + } else { + UpdateItemQualities obj = new UpdateItemQualities(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int itemQualitiesCount = VarInt.peek(buf, pos); + if (itemQualitiesCount < 0) { + throw ProtocolException.invalidVarInt("ItemQualities"); + } - if (itemQualitiesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ItemQualities", itemQualitiesCount, 4096000); - } + int itemQualitiesVarLen = VarInt.size(itemQualitiesCount); + if (itemQualitiesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ItemQualities", itemQualitiesCount, 4096000); + } - pos += VarInt.size(itemQualitiesCount); - obj.itemQualities = new HashMap<>(itemQualitiesCount); + pos += itemQualitiesVarLen; + obj.itemQualities = new HashMap<>(itemQualitiesCount); - for (int i = 0; i < itemQualitiesCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - ItemQuality val = ItemQuality.deserialize(buf, pos); - pos += ItemQuality.computeBytesConsumed(buf, pos); - if (obj.itemQualities.put(key, val) != null) { - throw ProtocolException.duplicateKey("itemQualities", key); + for (int i = 0; i < itemQualitiesCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + ItemQuality val = ItemQuality.deserialize(buf, pos); + pos += ItemQuality.computeBytesConsumed(buf, pos); + if (obj.itemQualities.put(key, val) != null) { + throw ProtocolException.duplicateKey("itemQualities", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateItemQualities implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateItemQualities implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int itemQualitiesCount = VarInt.peek(buffer, pos); - if (itemQualitiesCount < 0) { - return ValidationResult.error("Invalid dictionary count for ItemQualities"); - } - - if (itemQualitiesCount > 4096000) { - return ValidationResult.error("ItemQualities exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < itemQualitiesCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int itemQualitiesCount = VarInt.peek(buffer, v); + if (itemQualitiesCount < 0) { + return ValidationResult.error("Invalid dictionary count for ItemQualities"); } - pos += ItemQuality.computeBytesConsumed(buffer, pos); - } - } + if (itemQualitiesCount > 4096000) { + return ValidationResult.error("ItemQualities exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(itemQualitiesCount); + + for (int i = 0; i < itemQualitiesCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += ItemQuality.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemReticles.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemReticles.java index db638e17..a7856682 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemReticles.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemReticles.java @@ -57,36 +57,41 @@ public class UpdateItemReticles implements Packet, ToClientPacket { @Nonnull public static UpdateItemReticles deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateItemReticles obj = new UpdateItemReticles(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int itemReticleConfigsCount = VarInt.peek(buf, pos); - if (itemReticleConfigsCount < 0) { - throw ProtocolException.negativeLength("ItemReticleConfigs", itemReticleConfigsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateItemReticles", 6, buf.readableBytes() - offset); + } else { + UpdateItemReticles obj = new UpdateItemReticles(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int itemReticleConfigsCount = VarInt.peek(buf, pos); + if (itemReticleConfigsCount < 0) { + throw ProtocolException.invalidVarInt("ItemReticleConfigs"); + } - if (itemReticleConfigsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ItemReticleConfigs", itemReticleConfigsCount, 4096000); - } + int itemReticleConfigsVarLen = VarInt.size(itemReticleConfigsCount); + if (itemReticleConfigsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ItemReticleConfigs", itemReticleConfigsCount, 4096000); + } - pos += VarInt.size(itemReticleConfigsCount); - obj.itemReticleConfigs = new HashMap<>(itemReticleConfigsCount); + pos += itemReticleConfigsVarLen; + obj.itemReticleConfigs = new HashMap<>(itemReticleConfigsCount); - for (int i = 0; i < itemReticleConfigsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - ItemReticleConfig val = ItemReticleConfig.deserialize(buf, pos); - pos += ItemReticleConfig.computeBytesConsumed(buf, pos); - if (obj.itemReticleConfigs.put(key, val) != null) { - throw ProtocolException.duplicateKey("itemReticleConfigs", key); + for (int i = 0; i < itemReticleConfigsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + ItemReticleConfig val = ItemReticleConfig.deserialize(buf, pos); + pos += ItemReticleConfig.computeBytesConsumed(buf, pos); + if (obj.itemReticleConfigs.put(key, val) != null) { + throw ProtocolException.duplicateKey("itemReticleConfigs", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateItemReticles implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateItemReticles implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int itemReticleConfigsCount = VarInt.peek(buffer, pos); - if (itemReticleConfigsCount < 0) { - return ValidationResult.error("Invalid dictionary count for ItemReticleConfigs"); - } - - if (itemReticleConfigsCount > 4096000) { - return ValidationResult.error("ItemReticleConfigs exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < itemReticleConfigsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int itemReticleConfigsCount = VarInt.peek(buffer, v); + if (itemReticleConfigsCount < 0) { + return ValidationResult.error("Invalid dictionary count for ItemReticleConfigs"); } - pos += ItemReticleConfig.computeBytesConsumed(buffer, pos); - } - } + if (itemReticleConfigsCount > 4096000) { + return ValidationResult.error("ItemReticleConfigs exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(itemReticleConfigsCount); + + for (int i = 0; i < itemReticleConfigsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += ItemReticleConfig.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemSoundSets.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemSoundSets.java index 6d85ae14..9ceeacf4 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemSoundSets.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItemSoundSets.java @@ -57,36 +57,41 @@ public class UpdateItemSoundSets implements Packet, ToClientPacket { @Nonnull public static UpdateItemSoundSets deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateItemSoundSets obj = new UpdateItemSoundSets(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int itemSoundSetsCount = VarInt.peek(buf, pos); - if (itemSoundSetsCount < 0) { - throw ProtocolException.negativeLength("ItemSoundSets", itemSoundSetsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateItemSoundSets", 6, buf.readableBytes() - offset); + } else { + UpdateItemSoundSets obj = new UpdateItemSoundSets(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int itemSoundSetsCount = VarInt.peek(buf, pos); + if (itemSoundSetsCount < 0) { + throw ProtocolException.invalidVarInt("ItemSoundSets"); + } - if (itemSoundSetsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ItemSoundSets", itemSoundSetsCount, 4096000); - } + int itemSoundSetsVarLen = VarInt.size(itemSoundSetsCount); + if (itemSoundSetsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ItemSoundSets", itemSoundSetsCount, 4096000); + } - pos += VarInt.size(itemSoundSetsCount); - obj.itemSoundSets = new HashMap<>(itemSoundSetsCount); + pos += itemSoundSetsVarLen; + obj.itemSoundSets = new HashMap<>(itemSoundSetsCount); - for (int i = 0; i < itemSoundSetsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - ItemSoundSet val = ItemSoundSet.deserialize(buf, pos); - pos += ItemSoundSet.computeBytesConsumed(buf, pos); - if (obj.itemSoundSets.put(key, val) != null) { - throw ProtocolException.duplicateKey("itemSoundSets", key); + for (int i = 0; i < itemSoundSetsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + ItemSoundSet val = ItemSoundSet.deserialize(buf, pos); + pos += ItemSoundSet.computeBytesConsumed(buf, pos); + if (obj.itemSoundSets.put(key, val) != null) { + throw ProtocolException.duplicateKey("itemSoundSets", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateItemSoundSets implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateItemSoundSets implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int itemSoundSetsCount = VarInt.peek(buffer, pos); - if (itemSoundSetsCount < 0) { - return ValidationResult.error("Invalid dictionary count for ItemSoundSets"); - } - - if (itemSoundSetsCount > 4096000) { - return ValidationResult.error("ItemSoundSets exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < itemSoundSetsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int itemSoundSetsCount = VarInt.peek(buffer, v); + if (itemSoundSetsCount < 0) { + return ValidationResult.error("Invalid dictionary count for ItemSoundSets"); } - pos += ItemSoundSet.computeBytesConsumed(buffer, pos); - } - } + if (itemSoundSetsCount > 4096000) { + return ValidationResult.error("ItemSoundSets exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(itemSoundSetsCount); + + for (int i = 0; i < itemSoundSetsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += ItemSoundSet.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItems.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItems.java index 4c50ffc5..0ecf9b45 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateItems.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateItems.java @@ -68,83 +68,105 @@ public class UpdateItems implements Packet, ToClientPacket { @Nonnull public static UpdateItems deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateItems obj = new UpdateItems(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.updateModels = buf.getByte(offset + 2) != 0; - obj.updateIcons = buf.getByte(offset + 3) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 12 + buf.getIntLE(offset + 4); - int itemsCount = VarInt.peek(buf, varPos0); - if (itemsCount < 0) { - throw ProtocolException.negativeLength("Items", itemsCount); - } - - if (itemsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Items", itemsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - obj.items = new HashMap<>(itemsCount); - int dictPos = varPos0 + varIntLen; - - for (int i = 0; i < itemsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("UpdateItems", 12, buf.readableBytes() - offset); + } else { + UpdateItems obj = new UpdateItems(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.updateModels = buf.getByte(offset + 2) != 0; + obj.updateIcons = buf.getByte(offset + 3) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("Items", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 12 + varPosBase0; + int itemsCount = VarInt.peek(buf, varPos0); + if (itemsCount < 0) { + throw ProtocolException.invalidVarInt("Items"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - ItemBase val = ItemBase.deserialize(buf, dictPos); - dictPos += ItemBase.computeBytesConsumed(buf, dictPos); - if (obj.items.put(key, val) != null) { - throw ProtocolException.duplicateKey("items", key); + int varIntLen = VarInt.size(itemsCount); + if (itemsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Items", itemsCount, 4096000); + } + + obj.items = new HashMap<>(itemsCount); + int dictPos = varPos0 + varIntLen; + + for (int i = 0; i < itemsCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + ItemBase val = ItemBase.deserialize(buf, dictPos); + dictPos += ItemBase.computeBytesConsumed(buf, dictPos); + if (obj.items.put(key, val) != null) { + throw ProtocolException.duplicateKey("items", key); + } } } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 8); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("RemovedItems", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 12 + varPosBase1; + int removedItemsCount = VarInt.peek(buf, varPos1); + if (removedItemsCount < 0) { + throw ProtocolException.invalidVarInt("RemovedItems"); + } + + int varIntLen = VarInt.size(removedItemsCount); + if (removedItemsCount > 4096000) { + throw ProtocolException.arrayTooLong("RemovedItems", removedItemsCount, 4096000); + } + + if (varPos1 + varIntLen + removedItemsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RemovedItems", varPos1 + varIntLen + removedItemsCount * 1, buf.readableBytes()); + } + + obj.removedItems = new String[removedItemsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < removedItemsCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("removedItems[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("removedItems[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("removedItems[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.removedItems[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 12 + buf.getIntLE(offset + 8); - int removedItemsCount = VarInt.peek(buf, varPos1); - if (removedItemsCount < 0) { - throw ProtocolException.negativeLength("RemovedItems", removedItemsCount); - } - - if (removedItemsCount > 4096000) { - throw ProtocolException.arrayTooLong("RemovedItems", removedItemsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + removedItemsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("RemovedItems", varPos1 + varIntLen + removedItemsCount * 1, buf.readableBytes()); - } - - obj.removedItems = new String[removedItemsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < removedItemsCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("removedItems[" + i + "]", strLen); - } - - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("removedItems[" + i + "]", strLen, 4096000); - } - - int strVarLen = VarInt.length(buf, elemPos); - obj.removedItems[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -152,13 +174,17 @@ public class UpdateItems implements Packet, ToClientPacket { int maxEnd = 12; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("Items", fieldOffset0, maxEnd); + } + int pos0 = offset + 12 + fieldOffset0; int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; pos0 += ItemBase.computeBytesConsumed(buf, pos0); } @@ -169,13 +195,17 @@ public class UpdateItems implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 8); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 12) { + throw ProtocolException.invalidOffset("RemovedItems", fieldOffset1, maxEnd); + } + int pos1 = offset + 12 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -270,85 +300,82 @@ public class UpdateItems implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 12 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int itemsOffset = buffer.getIntLE(offset + 4); - if (itemsOffset < 0) { - return ValidationResult.error("Invalid offset for Items"); - } - - int pos = offset + 12 + itemsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Items"); - } - - int itemsCount = VarInt.peek(buffer, pos); - if (itemsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Items"); - } - - if (itemsCount > 4096000) { - return ValidationResult.error("Items exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < itemsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 4); + if (v < 0 || v > buffer.writerIndex() - offset - 12) { + return ValidationResult.error("Invalid offset for Items"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 12 + v; + int itemsCount = VarInt.peek(buffer, pos); + if (itemsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Items"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + if (itemsCount > 4096000) { + return ValidationResult.error("Items exceeds max length 4096000"); } - pos += ItemBase.computeBytesConsumed(buffer, pos); + pos += VarInt.size(itemsCount); + + for (int i = 0; i < itemsCount; i++) { + int keyLen = VarInt.peek(buffer, pos); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + pos += VarInt.size(keyLen); + pos += keyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + pos += ItemBase.computeBytesConsumed(buffer, pos); + } } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 8); + if (v < 0 || v > buffer.writerIndex() - offset - 12) { + return ValidationResult.error("Invalid offset for RemovedItems"); + } + + int posx = offset + 12 + v; + int removedItemsCount = VarInt.peek(buffer, posx); + if (removedItemsCount < 0) { + return ValidationResult.error("Invalid array count for RemovedItems"); + } + + if (removedItemsCount > 4096000) { + return ValidationResult.error("RemovedItems exceeds max length 4096000"); + } + + posx += VarInt.size(removedItemsCount); + + for (int i = 0; i < removedItemsCount; i++) { + int strLen = VarInt.peek(buffer, posx); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in RemovedItems"); + } + + posx += VarInt.size(strLen); + posx += strLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in RemovedItems"); + } + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int removedItemsOffset = buffer.getIntLE(offset + 8); - if (removedItemsOffset < 0) { - return ValidationResult.error("Invalid offset for RemovedItems"); - } - - int posx = offset + 12 + removedItemsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for RemovedItems"); - } - - int removedItemsCount = VarInt.peek(buffer, posx); - if (removedItemsCount < 0) { - return ValidationResult.error("Invalid array count for RemovedItems"); - } - - if (removedItemsCount > 4096000) { - return ValidationResult.error("RemovedItems exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < removedItemsCount; i++) { - int strLen = VarInt.peek(buffer, posx); - if (strLen < 0) { - return ValidationResult.error("Invalid string length in RemovedItems"); - } - - posx += VarInt.length(buffer, posx); - posx += strLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in RemovedItems"); - } - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateModelvfxs.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateModelvfxs.java index b3c18635..6ecd2410 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateModelvfxs.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateModelvfxs.java @@ -57,36 +57,41 @@ public class UpdateModelvfxs implements Packet, ToClientPacket { @Nonnull public static UpdateModelvfxs deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateModelvfxs obj = new UpdateModelvfxs(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int modelVFXsCount = VarInt.peek(buf, pos); - if (modelVFXsCount < 0) { - throw ProtocolException.negativeLength("ModelVFXs", modelVFXsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateModelvfxs", 6, buf.readableBytes() - offset); + } else { + UpdateModelvfxs obj = new UpdateModelvfxs(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int modelVFXsCount = VarInt.peek(buf, pos); + if (modelVFXsCount < 0) { + throw ProtocolException.invalidVarInt("ModelVFXs"); + } - if (modelVFXsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ModelVFXs", modelVFXsCount, 4096000); - } + int modelVFXsVarLen = VarInt.size(modelVFXsCount); + if (modelVFXsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ModelVFXs", modelVFXsCount, 4096000); + } - pos += VarInt.size(modelVFXsCount); - obj.modelVFXs = new HashMap<>(modelVFXsCount); + pos += modelVFXsVarLen; + obj.modelVFXs = new HashMap<>(modelVFXsCount); - for (int i = 0; i < modelVFXsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - ModelVFX val = ModelVFX.deserialize(buf, pos); - pos += ModelVFX.computeBytesConsumed(buf, pos); - if (obj.modelVFXs.put(key, val) != null) { - throw ProtocolException.duplicateKey("modelVFXs", key); + for (int i = 0; i < modelVFXsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + ModelVFX val = ModelVFX.deserialize(buf, pos); + pos += ModelVFX.computeBytesConsumed(buf, pos); + if (obj.modelVFXs.put(key, val) != null) { + throw ProtocolException.duplicateKey("modelVFXs", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateModelvfxs implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateModelvfxs implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int modelVFXsCount = VarInt.peek(buffer, pos); - if (modelVFXsCount < 0) { - return ValidationResult.error("Invalid dictionary count for ModelVFXs"); - } - - if (modelVFXsCount > 4096000) { - return ValidationResult.error("ModelVFXs exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < modelVFXsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int modelVFXsCount = VarInt.peek(buffer, v); + if (modelVFXsCount < 0) { + return ValidationResult.error("Invalid dictionary count for ModelVFXs"); } - pos += ModelVFX.computeBytesConsumed(buffer, pos); - } - } + if (modelVFXsCount > 4096000) { + return ValidationResult.error("ModelVFXs exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(modelVFXsCount); + + for (int i = 0; i < modelVFXsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += ModelVFX.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateObjectiveTask.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateObjectiveTask.java index eef4b7c2..8586c86a 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateObjectiveTask.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateObjectiveTask.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.ObjectiveTask; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -53,17 +54,21 @@ public class UpdateObjectiveTask implements Packet, ToClientPacket { @Nonnull public static UpdateObjectiveTask deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateObjectiveTask obj = new UpdateObjectiveTask(); - byte nullBits = buf.getByte(offset); - obj.objectiveUuid = PacketIO.readUUID(buf, offset + 1); - obj.taskIndex = buf.getIntLE(offset + 17); - int pos = offset + 21; - if ((nullBits & 1) != 0) { - obj.task = ObjectiveTask.deserialize(buf, pos); - pos += ObjectiveTask.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("UpdateObjectiveTask", 21, buf.readableBytes() - offset); + } else { + UpdateObjectiveTask obj = new UpdateObjectiveTask(); + byte nullBits = buf.getByte(offset); + obj.objectiveUuid = PacketIO.readUUID(buf, offset + 1); + obj.taskIndex = buf.getIntLE(offset + 17); + int pos = offset + 21; + if ((nullBits & 1) != 0) { + obj.task = ObjectiveTask.deserialize(buf, pos); + pos += ObjectiveTask.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateParticleSpawners.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateParticleSpawners.java index 9a908379..da4f7a20 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateParticleSpawners.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateParticleSpawners.java @@ -60,81 +60,103 @@ public class UpdateParticleSpawners implements Packet, ToClientPacket { @Nonnull public static UpdateParticleSpawners deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateParticleSpawners obj = new UpdateParticleSpawners(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - int particleSpawnersCount = VarInt.peek(buf, varPos0); - if (particleSpawnersCount < 0) { - throw ProtocolException.negativeLength("ParticleSpawners", particleSpawnersCount); - } - - if (particleSpawnersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ParticleSpawners", particleSpawnersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - obj.particleSpawners = new HashMap<>(particleSpawnersCount); - int dictPos = varPos0 + varIntLen; - - for (int i = 0; i < particleSpawnersCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("UpdateParticleSpawners", 10, buf.readableBytes() - offset); + } else { + UpdateParticleSpawners obj = new UpdateParticleSpawners(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("ParticleSpawners", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 10 + varPosBase0; + int particleSpawnersCount = VarInt.peek(buf, varPos0); + if (particleSpawnersCount < 0) { + throw ProtocolException.invalidVarInt("ParticleSpawners"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - ParticleSpawner val = ParticleSpawner.deserialize(buf, dictPos); - dictPos += ParticleSpawner.computeBytesConsumed(buf, dictPos); - if (obj.particleSpawners.put(key, val) != null) { - throw ProtocolException.duplicateKey("particleSpawners", key); + int varIntLen = VarInt.size(particleSpawnersCount); + if (particleSpawnersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ParticleSpawners", particleSpawnersCount, 4096000); + } + + obj.particleSpawners = new HashMap<>(particleSpawnersCount); + int dictPos = varPos0 + varIntLen; + + for (int i = 0; i < particleSpawnersCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + ParticleSpawner val = ParticleSpawner.deserialize(buf, dictPos); + dictPos += ParticleSpawner.computeBytesConsumed(buf, dictPos); + if (obj.particleSpawners.put(key, val) != null) { + throw ProtocolException.duplicateKey("particleSpawners", key); + } } } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("RemovedParticleSpawners", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + int removedParticleSpawnersCount = VarInt.peek(buf, varPos1); + if (removedParticleSpawnersCount < 0) { + throw ProtocolException.invalidVarInt("RemovedParticleSpawners"); + } + + int varIntLen = VarInt.size(removedParticleSpawnersCount); + if (removedParticleSpawnersCount > 4096000) { + throw ProtocolException.arrayTooLong("RemovedParticleSpawners", removedParticleSpawnersCount, 4096000); + } + + if (varPos1 + varIntLen + removedParticleSpawnersCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RemovedParticleSpawners", varPos1 + varIntLen + removedParticleSpawnersCount * 1, buf.readableBytes()); + } + + obj.removedParticleSpawners = new String[removedParticleSpawnersCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < removedParticleSpawnersCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("removedParticleSpawners[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("removedParticleSpawners[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("removedParticleSpawners[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.removedParticleSpawners[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - int removedParticleSpawnersCount = VarInt.peek(buf, varPos1); - if (removedParticleSpawnersCount < 0) { - throw ProtocolException.negativeLength("RemovedParticleSpawners", removedParticleSpawnersCount); - } - - if (removedParticleSpawnersCount > 4096000) { - throw ProtocolException.arrayTooLong("RemovedParticleSpawners", removedParticleSpawnersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + removedParticleSpawnersCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("RemovedParticleSpawners", varPos1 + varIntLen + removedParticleSpawnersCount * 1, buf.readableBytes()); - } - - obj.removedParticleSpawners = new String[removedParticleSpawnersCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < removedParticleSpawnersCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("removedParticleSpawners[" + i + "]", strLen); - } - - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("removedParticleSpawners[" + i + "]", strLen, 4096000); - } - - int strVarLen = VarInt.length(buf, elemPos); - obj.removedParticleSpawners[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -142,13 +164,17 @@ public class UpdateParticleSpawners implements Packet, ToClientPacket { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("ParticleSpawners", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; pos0 += ParticleSpawner.computeBytesConsumed(buf, pos0); } @@ -159,13 +185,17 @@ public class UpdateParticleSpawners implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("RemovedParticleSpawners", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -258,85 +288,82 @@ public class UpdateParticleSpawners implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 10 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int particleSpawnersOffset = buffer.getIntLE(offset + 2); - if (particleSpawnersOffset < 0) { - return ValidationResult.error("Invalid offset for ParticleSpawners"); - } - - int pos = offset + 10 + particleSpawnersOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ParticleSpawners"); - } - - int particleSpawnersCount = VarInt.peek(buffer, pos); - if (particleSpawnersCount < 0) { - return ValidationResult.error("Invalid dictionary count for ParticleSpawners"); - } - - if (particleSpawnersCount > 4096000) { - return ValidationResult.error("ParticleSpawners exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < particleSpawnersCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 2); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for ParticleSpawners"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 10 + v; + int particleSpawnersCount = VarInt.peek(buffer, pos); + if (particleSpawnersCount < 0) { + return ValidationResult.error("Invalid dictionary count for ParticleSpawners"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + if (particleSpawnersCount > 4096000) { + return ValidationResult.error("ParticleSpawners exceeds max length 4096000"); } - pos += ParticleSpawner.computeBytesConsumed(buffer, pos); + pos += VarInt.size(particleSpawnersCount); + + for (int i = 0; i < particleSpawnersCount; i++) { + int keyLen = VarInt.peek(buffer, pos); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + pos += VarInt.size(keyLen); + pos += keyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + pos += ParticleSpawner.computeBytesConsumed(buffer, pos); + } } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for RemovedParticleSpawners"); + } + + int posx = offset + 10 + v; + int removedParticleSpawnersCount = VarInt.peek(buffer, posx); + if (removedParticleSpawnersCount < 0) { + return ValidationResult.error("Invalid array count for RemovedParticleSpawners"); + } + + if (removedParticleSpawnersCount > 4096000) { + return ValidationResult.error("RemovedParticleSpawners exceeds max length 4096000"); + } + + posx += VarInt.size(removedParticleSpawnersCount); + + for (int i = 0; i < removedParticleSpawnersCount; i++) { + int strLen = VarInt.peek(buffer, posx); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in RemovedParticleSpawners"); + } + + posx += VarInt.size(strLen); + posx += strLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in RemovedParticleSpawners"); + } + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int removedParticleSpawnersOffset = buffer.getIntLE(offset + 6); - if (removedParticleSpawnersOffset < 0) { - return ValidationResult.error("Invalid offset for RemovedParticleSpawners"); - } - - int posx = offset + 10 + removedParticleSpawnersOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for RemovedParticleSpawners"); - } - - int removedParticleSpawnersCount = VarInt.peek(buffer, posx); - if (removedParticleSpawnersCount < 0) { - return ValidationResult.error("Invalid array count for RemovedParticleSpawners"); - } - - if (removedParticleSpawnersCount > 4096000) { - return ValidationResult.error("RemovedParticleSpawners exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < removedParticleSpawnersCount; i++) { - int strLen = VarInt.peek(buffer, posx); - if (strLen < 0) { - return ValidationResult.error("Invalid string length in RemovedParticleSpawners"); - } - - posx += VarInt.length(buffer, posx); - posx += strLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in RemovedParticleSpawners"); - } - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateParticleSystems.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateParticleSystems.java index 6f28645c..d90d88ac 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateParticleSystems.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateParticleSystems.java @@ -60,81 +60,103 @@ public class UpdateParticleSystems implements Packet, ToClientPacket { @Nonnull public static UpdateParticleSystems deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateParticleSystems obj = new UpdateParticleSystems(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - int particleSystemsCount = VarInt.peek(buf, varPos0); - if (particleSystemsCount < 0) { - throw ProtocolException.negativeLength("ParticleSystems", particleSystemsCount); - } - - if (particleSystemsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ParticleSystems", particleSystemsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - obj.particleSystems = new HashMap<>(particleSystemsCount); - int dictPos = varPos0 + varIntLen; - - for (int i = 0; i < particleSystemsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("UpdateParticleSystems", 10, buf.readableBytes() - offset); + } else { + UpdateParticleSystems obj = new UpdateParticleSystems(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("ParticleSystems", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 10 + varPosBase0; + int particleSystemsCount = VarInt.peek(buf, varPos0); + if (particleSystemsCount < 0) { + throw ProtocolException.invalidVarInt("ParticleSystems"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - ParticleSystem val = ParticleSystem.deserialize(buf, dictPos); - dictPos += ParticleSystem.computeBytesConsumed(buf, dictPos); - if (obj.particleSystems.put(key, val) != null) { - throw ProtocolException.duplicateKey("particleSystems", key); + int varIntLen = VarInt.size(particleSystemsCount); + if (particleSystemsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ParticleSystems", particleSystemsCount, 4096000); + } + + obj.particleSystems = new HashMap<>(particleSystemsCount); + int dictPos = varPos0 + varIntLen; + + for (int i = 0; i < particleSystemsCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + ParticleSystem val = ParticleSystem.deserialize(buf, dictPos); + dictPos += ParticleSystem.computeBytesConsumed(buf, dictPos); + if (obj.particleSystems.put(key, val) != null) { + throw ProtocolException.duplicateKey("particleSystems", key); + } } } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("RemovedParticleSystems", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + int removedParticleSystemsCount = VarInt.peek(buf, varPos1); + if (removedParticleSystemsCount < 0) { + throw ProtocolException.invalidVarInt("RemovedParticleSystems"); + } + + int varIntLen = VarInt.size(removedParticleSystemsCount); + if (removedParticleSystemsCount > 4096000) { + throw ProtocolException.arrayTooLong("RemovedParticleSystems", removedParticleSystemsCount, 4096000); + } + + if (varPos1 + varIntLen + removedParticleSystemsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RemovedParticleSystems", varPos1 + varIntLen + removedParticleSystemsCount * 1, buf.readableBytes()); + } + + obj.removedParticleSystems = new String[removedParticleSystemsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < removedParticleSystemsCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("removedParticleSystems[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("removedParticleSystems[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("removedParticleSystems[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.removedParticleSystems[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - int removedParticleSystemsCount = VarInt.peek(buf, varPos1); - if (removedParticleSystemsCount < 0) { - throw ProtocolException.negativeLength("RemovedParticleSystems", removedParticleSystemsCount); - } - - if (removedParticleSystemsCount > 4096000) { - throw ProtocolException.arrayTooLong("RemovedParticleSystems", removedParticleSystemsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + removedParticleSystemsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("RemovedParticleSystems", varPos1 + varIntLen + removedParticleSystemsCount * 1, buf.readableBytes()); - } - - obj.removedParticleSystems = new String[removedParticleSystemsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < removedParticleSystemsCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("removedParticleSystems[" + i + "]", strLen); - } - - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("removedParticleSystems[" + i + "]", strLen, 4096000); - } - - int strVarLen = VarInt.length(buf, elemPos); - obj.removedParticleSystems[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -142,13 +164,17 @@ public class UpdateParticleSystems implements Packet, ToClientPacket { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("ParticleSystems", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; pos0 += ParticleSystem.computeBytesConsumed(buf, pos0); } @@ -159,13 +185,17 @@ public class UpdateParticleSystems implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("RemovedParticleSystems", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -258,85 +288,82 @@ public class UpdateParticleSystems implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 10 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int particleSystemsOffset = buffer.getIntLE(offset + 2); - if (particleSystemsOffset < 0) { - return ValidationResult.error("Invalid offset for ParticleSystems"); - } - - int pos = offset + 10 + particleSystemsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ParticleSystems"); - } - - int particleSystemsCount = VarInt.peek(buffer, pos); - if (particleSystemsCount < 0) { - return ValidationResult.error("Invalid dictionary count for ParticleSystems"); - } - - if (particleSystemsCount > 4096000) { - return ValidationResult.error("ParticleSystems exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < particleSystemsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 2); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for ParticleSystems"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 10 + v; + int particleSystemsCount = VarInt.peek(buffer, pos); + if (particleSystemsCount < 0) { + return ValidationResult.error("Invalid dictionary count for ParticleSystems"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + if (particleSystemsCount > 4096000) { + return ValidationResult.error("ParticleSystems exceeds max length 4096000"); } - pos += ParticleSystem.computeBytesConsumed(buffer, pos); + pos += VarInt.size(particleSystemsCount); + + for (int i = 0; i < particleSystemsCount; i++) { + int keyLen = VarInt.peek(buffer, pos); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + pos += VarInt.size(keyLen); + pos += keyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + pos += ParticleSystem.computeBytesConsumed(buffer, pos); + } } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for RemovedParticleSystems"); + } + + int posx = offset + 10 + v; + int removedParticleSystemsCount = VarInt.peek(buffer, posx); + if (removedParticleSystemsCount < 0) { + return ValidationResult.error("Invalid array count for RemovedParticleSystems"); + } + + if (removedParticleSystemsCount > 4096000) { + return ValidationResult.error("RemovedParticleSystems exceeds max length 4096000"); + } + + posx += VarInt.size(removedParticleSystemsCount); + + for (int i = 0; i < removedParticleSystemsCount; i++) { + int strLen = VarInt.peek(buffer, posx); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in RemovedParticleSystems"); + } + + posx += VarInt.size(strLen); + posx += strLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in RemovedParticleSystems"); + } + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int removedParticleSystemsOffset = buffer.getIntLE(offset + 6); - if (removedParticleSystemsOffset < 0) { - return ValidationResult.error("Invalid offset for RemovedParticleSystems"); - } - - int posx = offset + 10 + removedParticleSystemsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for RemovedParticleSystems"); - } - - int removedParticleSystemsCount = VarInt.peek(buffer, posx); - if (removedParticleSystemsCount < 0) { - return ValidationResult.error("Invalid array count for RemovedParticleSystems"); - } - - if (removedParticleSystemsCount > 4096000) { - return ValidationResult.error("RemovedParticleSystems exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < removedParticleSystemsCount; i++) { - int strLen = VarInt.peek(buffer, posx); - if (strLen < 0) { - return ValidationResult.error("Invalid string length in RemovedParticleSystems"); - } - - posx += VarInt.length(buffer, posx); - posx += strLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in RemovedParticleSystems"); - } - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdatePhysicalMaterials.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdatePhysicalMaterials.java new file mode 100644 index 00000000..12a93487 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdatePhysicalMaterials.java @@ -0,0 +1,222 @@ +package com.hypixel.hytale.protocol.packets.assets; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.PhysicalMaterial; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.UpdateType; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Map.Entry; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class UpdatePhysicalMaterials implements Packet, ToClientPacket { + public static final int PACKET_ID = 87; + public static final boolean IS_COMPRESSED = true; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 6; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 6; + public static final int MAX_SIZE = 1677721600; + @Nonnull + public UpdateType type = UpdateType.Init; + public int maxId; + @Nullable + public Map physicalMaterials; + + @Override + public int getId() { + return 87; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public UpdatePhysicalMaterials() { + } + + public UpdatePhysicalMaterials(@Nonnull UpdateType type, int maxId, @Nullable Map physicalMaterials) { + this.type = type; + this.maxId = maxId; + this.physicalMaterials = physicalMaterials; + } + + public UpdatePhysicalMaterials(@Nonnull UpdatePhysicalMaterials other) { + this.type = other.type; + this.maxId = other.maxId; + this.physicalMaterials = other.physicalMaterials; + } + + @Nonnull + public static UpdatePhysicalMaterials deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdatePhysicalMaterials", 6, buf.readableBytes() - offset); + } else { + UpdatePhysicalMaterials obj = new UpdatePhysicalMaterials(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int physicalMaterialsCount = VarInt.peek(buf, pos); + if (physicalMaterialsCount < 0) { + throw ProtocolException.invalidVarInt("PhysicalMaterials"); + } + + int physicalMaterialsVarLen = VarInt.size(physicalMaterialsCount); + if (physicalMaterialsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("PhysicalMaterials", physicalMaterialsCount, 4096000); + } + + pos += physicalMaterialsVarLen; + obj.physicalMaterials = new HashMap<>(physicalMaterialsCount); + + for (int i = 0; i < physicalMaterialsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + PhysicalMaterial val = PhysicalMaterial.deserialize(buf, pos); + pos += PhysicalMaterial.computeBytesConsumed(buf, pos); + if (obj.physicalMaterials.put(key, val) != null) { + throw ProtocolException.duplicateKey("physicalMaterials", key); + } + } + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int dictLen = VarInt.peek(buf, pos); + pos += VarInt.size(dictLen); + + for (int i = 0; i < dictLen; i++) { + pos += 4; + pos += PhysicalMaterial.computeBytesConsumed(buf, pos); + } + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.physicalMaterials != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + buf.writeByte(this.type.getValue()); + buf.writeIntLE(this.maxId); + if (this.physicalMaterials != null) { + if (this.physicalMaterials.size() > 4096000) { + throw ProtocolException.dictionaryTooLarge("PhysicalMaterials", this.physicalMaterials.size(), 4096000); + } + + VarInt.write(buf, this.physicalMaterials.size()); + + for (Entry e : this.physicalMaterials.entrySet()) { + buf.writeIntLE(e.getKey()); + e.getValue().serialize(buf); + } + } + } + + @Override + public int computeSize() { + int size = 6; + if (this.physicalMaterials != null) { + int physicalMaterialsSize = 0; + + for (Entry kvp : this.physicalMaterials.entrySet()) { + physicalMaterialsSize += 4 + kvp.getValue().computeSize(); + } + + size += VarInt.size(this.physicalMaterials.size()) + physicalMaterialsSize; + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 6) { + return ValidationResult.error("Buffer too small: expected at least 6 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int physicalMaterialsCount = VarInt.peek(buffer, v); + if (physicalMaterialsCount < 0) { + return ValidationResult.error("Invalid dictionary count for PhysicalMaterials"); + } + + if (physicalMaterialsCount > 4096000) { + return ValidationResult.error("PhysicalMaterials exceeds max length 4096000"); + } + + v += VarInt.size(physicalMaterialsCount); + + for (int i = 0; i < physicalMaterialsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += PhysicalMaterial.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } + } + } + + public UpdatePhysicalMaterials clone() { + UpdatePhysicalMaterials copy = new UpdatePhysicalMaterials(); + copy.type = this.type; + copy.maxId = this.maxId; + if (this.physicalMaterials != null) { + Map m = new HashMap<>(); + + for (Entry e : this.physicalMaterials.entrySet()) { + m.put(e.getKey(), e.getValue().clone()); + } + + copy.physicalMaterials = m; + } + + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof UpdatePhysicalMaterials other) + ? false + : Objects.equals(this.type, other.type) && this.maxId == other.maxId && Objects.equals(this.physicalMaterials, other.physicalMaterials); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.type, this.maxId, this.physicalMaterials); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateProjectileConfigs.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateProjectileConfigs.java index 435a6a1b..22f88418 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateProjectileConfigs.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateProjectileConfigs.java @@ -60,81 +60,103 @@ public class UpdateProjectileConfigs implements Packet, ToClientPacket { @Nonnull public static UpdateProjectileConfigs deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateProjectileConfigs obj = new UpdateProjectileConfigs(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - int configsCount = VarInt.peek(buf, varPos0); - if (configsCount < 0) { - throw ProtocolException.negativeLength("Configs", configsCount); - } - - if (configsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Configs", configsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - obj.configs = new HashMap<>(configsCount); - int dictPos = varPos0 + varIntLen; - - for (int i = 0; i < configsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("UpdateProjectileConfigs", 10, buf.readableBytes() - offset); + } else { + UpdateProjectileConfigs obj = new UpdateProjectileConfigs(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Configs", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 10 + varPosBase0; + int configsCount = VarInt.peek(buf, varPos0); + if (configsCount < 0) { + throw ProtocolException.invalidVarInt("Configs"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - ProjectileConfig val = ProjectileConfig.deserialize(buf, dictPos); - dictPos += ProjectileConfig.computeBytesConsumed(buf, dictPos); - if (obj.configs.put(key, val) != null) { - throw ProtocolException.duplicateKey("configs", key); + int varIntLen = VarInt.size(configsCount); + if (configsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Configs", configsCount, 4096000); + } + + obj.configs = new HashMap<>(configsCount); + int dictPos = varPos0 + varIntLen; + + for (int i = 0; i < configsCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + ProjectileConfig val = ProjectileConfig.deserialize(buf, dictPos); + dictPos += ProjectileConfig.computeBytesConsumed(buf, dictPos); + if (obj.configs.put(key, val) != null) { + throw ProtocolException.duplicateKey("configs", key); + } } } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("RemovedConfigs", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + int removedConfigsCount = VarInt.peek(buf, varPos1); + if (removedConfigsCount < 0) { + throw ProtocolException.invalidVarInt("RemovedConfigs"); + } + + int varIntLen = VarInt.size(removedConfigsCount); + if (removedConfigsCount > 4096000) { + throw ProtocolException.arrayTooLong("RemovedConfigs", removedConfigsCount, 4096000); + } + + if (varPos1 + varIntLen + removedConfigsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RemovedConfigs", varPos1 + varIntLen + removedConfigsCount * 1, buf.readableBytes()); + } + + obj.removedConfigs = new String[removedConfigsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < removedConfigsCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("removedConfigs[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("removedConfigs[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("removedConfigs[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.removedConfigs[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - int removedConfigsCount = VarInt.peek(buf, varPos1); - if (removedConfigsCount < 0) { - throw ProtocolException.negativeLength("RemovedConfigs", removedConfigsCount); - } - - if (removedConfigsCount > 4096000) { - throw ProtocolException.arrayTooLong("RemovedConfigs", removedConfigsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + removedConfigsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("RemovedConfigs", varPos1 + varIntLen + removedConfigsCount * 1, buf.readableBytes()); - } - - obj.removedConfigs = new String[removedConfigsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < removedConfigsCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("removedConfigs[" + i + "]", strLen); - } - - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("removedConfigs[" + i + "]", strLen, 4096000); - } - - int strVarLen = VarInt.length(buf, elemPos); - obj.removedConfigs[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -142,13 +164,17 @@ public class UpdateProjectileConfigs implements Packet, ToClientPacket { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Configs", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; pos0 += ProjectileConfig.computeBytesConsumed(buf, pos0); } @@ -159,13 +185,17 @@ public class UpdateProjectileConfigs implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("RemovedConfigs", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -258,85 +288,82 @@ public class UpdateProjectileConfigs implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 10 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int configsOffset = buffer.getIntLE(offset + 2); - if (configsOffset < 0) { - return ValidationResult.error("Invalid offset for Configs"); - } - - int pos = offset + 10 + configsOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Configs"); - } - - int configsCount = VarInt.peek(buffer, pos); - if (configsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Configs"); - } - - if (configsCount > 4096000) { - return ValidationResult.error("Configs exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < configsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 2); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for Configs"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 10 + v; + int configsCount = VarInt.peek(buffer, pos); + if (configsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Configs"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + if (configsCount > 4096000) { + return ValidationResult.error("Configs exceeds max length 4096000"); } - pos += ProjectileConfig.computeBytesConsumed(buffer, pos); + pos += VarInt.size(configsCount); + + for (int i = 0; i < configsCount; i++) { + int keyLen = VarInt.peek(buffer, pos); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + pos += VarInt.size(keyLen); + pos += keyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + pos += ProjectileConfig.computeBytesConsumed(buffer, pos); + } } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for RemovedConfigs"); + } + + int posx = offset + 10 + v; + int removedConfigsCount = VarInt.peek(buffer, posx); + if (removedConfigsCount < 0) { + return ValidationResult.error("Invalid array count for RemovedConfigs"); + } + + if (removedConfigsCount > 4096000) { + return ValidationResult.error("RemovedConfigs exceeds max length 4096000"); + } + + posx += VarInt.size(removedConfigsCount); + + for (int i = 0; i < removedConfigsCount; i++) { + int strLen = VarInt.peek(buffer, posx); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in RemovedConfigs"); + } + + posx += VarInt.size(strLen); + posx += strLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in RemovedConfigs"); + } + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int removedConfigsOffset = buffer.getIntLE(offset + 6); - if (removedConfigsOffset < 0) { - return ValidationResult.error("Invalid offset for RemovedConfigs"); - } - - int posx = offset + 10 + removedConfigsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for RemovedConfigs"); - } - - int removedConfigsCount = VarInt.peek(buffer, posx); - if (removedConfigsCount < 0) { - return ValidationResult.error("Invalid array count for RemovedConfigs"); - } - - if (removedConfigsCount > 4096000) { - return ValidationResult.error("RemovedConfigs exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < removedConfigsCount; i++) { - int strLen = VarInt.peek(buffer, posx); - if (strLen < 0) { - return ValidationResult.error("Invalid string length in RemovedConfigs"); - } - - posx += VarInt.length(buffer, posx); - posx += strLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in RemovedConfigs"); - } - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateRecipes.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateRecipes.java index bd476d1c..cbb97079 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateRecipes.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateRecipes.java @@ -60,81 +60,103 @@ public class UpdateRecipes implements Packet, ToClientPacket { @Nonnull public static UpdateRecipes deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateRecipes obj = new UpdateRecipes(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 10 + buf.getIntLE(offset + 2); - int recipesCount = VarInt.peek(buf, varPos0); - if (recipesCount < 0) { - throw ProtocolException.negativeLength("Recipes", recipesCount); - } - - if (recipesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Recipes", recipesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - obj.recipes = new HashMap<>(recipesCount); - int dictPos = varPos0 + varIntLen; - - for (int i = 0; i < recipesCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("UpdateRecipes", 10, buf.readableBytes() - offset); + } else { + UpdateRecipes obj = new UpdateRecipes(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Recipes", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 10 + varPosBase0; + int recipesCount = VarInt.peek(buf, varPos0); + if (recipesCount < 0) { + throw ProtocolException.invalidVarInt("Recipes"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - CraftingRecipe val = CraftingRecipe.deserialize(buf, dictPos); - dictPos += CraftingRecipe.computeBytesConsumed(buf, dictPos); - if (obj.recipes.put(key, val) != null) { - throw ProtocolException.duplicateKey("recipes", key); + int varIntLen = VarInt.size(recipesCount); + if (recipesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Recipes", recipesCount, 4096000); + } + + obj.recipes = new HashMap<>(recipesCount); + int dictPos = varPos0 + varIntLen; + + for (int i = 0; i < recipesCount; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + CraftingRecipe val = CraftingRecipe.deserialize(buf, dictPos); + dictPos += CraftingRecipe.computeBytesConsumed(buf, dictPos); + if (obj.recipes.put(key, val) != null) { + throw ProtocolException.duplicateKey("recipes", key); + } } } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("RemovedRecipes", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + int removedRecipesCount = VarInt.peek(buf, varPos1); + if (removedRecipesCount < 0) { + throw ProtocolException.invalidVarInt("RemovedRecipes"); + } + + int varIntLen = VarInt.size(removedRecipesCount); + if (removedRecipesCount > 4096000) { + throw ProtocolException.arrayTooLong("RemovedRecipes", removedRecipesCount, 4096000); + } + + if (varPos1 + varIntLen + removedRecipesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RemovedRecipes", varPos1 + varIntLen + removedRecipesCount * 1, buf.readableBytes()); + } + + obj.removedRecipes = new String[removedRecipesCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < removedRecipesCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("removedRecipes[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("removedRecipes[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("removedRecipes[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.removedRecipes[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 10 + buf.getIntLE(offset + 6); - int removedRecipesCount = VarInt.peek(buf, varPos1); - if (removedRecipesCount < 0) { - throw ProtocolException.negativeLength("RemovedRecipes", removedRecipesCount); - } - - if (removedRecipesCount > 4096000) { - throw ProtocolException.arrayTooLong("RemovedRecipes", removedRecipesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + removedRecipesCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("RemovedRecipes", varPos1 + varIntLen + removedRecipesCount * 1, buf.readableBytes()); - } - - obj.removedRecipes = new String[removedRecipesCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < removedRecipesCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("removedRecipes[" + i + "]", strLen); - } - - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("removedRecipes[" + i + "]", strLen, 4096000); - } - - int strVarLen = VarInt.length(buf, elemPos); - obj.removedRecipes[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -142,13 +164,17 @@ public class UpdateRecipes implements Packet, ToClientPacket { int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Recipes", fieldOffset0, maxEnd); + } + int pos0 = offset + 10 + fieldOffset0; int dictLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; pos0 += CraftingRecipe.computeBytesConsumed(buf, pos0); } @@ -159,13 +185,17 @@ public class UpdateRecipes implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("RemovedRecipes", fieldOffset1, maxEnd); + } + int pos1 = offset + 10 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -258,85 +288,82 @@ public class UpdateRecipes implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 10 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int recipesOffset = buffer.getIntLE(offset + 2); - if (recipesOffset < 0) { - return ValidationResult.error("Invalid offset for Recipes"); - } - - int pos = offset + 10 + recipesOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Recipes"); - } - - int recipesCount = VarInt.peek(buffer, pos); - if (recipesCount < 0) { - return ValidationResult.error("Invalid dictionary count for Recipes"); - } - - if (recipesCount > 4096000) { - return ValidationResult.error("Recipes exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < recipesCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 2); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for Recipes"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + int pos = offset + 10 + v; + int recipesCount = VarInt.peek(buffer, pos); + if (recipesCount < 0) { + return ValidationResult.error("Invalid dictionary count for Recipes"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + if (recipesCount > 4096000) { + return ValidationResult.error("Recipes exceeds max length 4096000"); } - pos += CraftingRecipe.computeBytesConsumed(buffer, pos); + pos += VarInt.size(recipesCount); + + for (int i = 0; i < recipesCount; i++) { + int keyLen = VarInt.peek(buffer, pos); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + pos += VarInt.size(keyLen); + pos += keyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + pos += CraftingRecipe.computeBytesConsumed(buffer, pos); + } } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 10) { + return ValidationResult.error("Invalid offset for RemovedRecipes"); + } + + int posx = offset + 10 + v; + int removedRecipesCount = VarInt.peek(buffer, posx); + if (removedRecipesCount < 0) { + return ValidationResult.error("Invalid array count for RemovedRecipes"); + } + + if (removedRecipesCount > 4096000) { + return ValidationResult.error("RemovedRecipes exceeds max length 4096000"); + } + + posx += VarInt.size(removedRecipesCount); + + for (int i = 0; i < removedRecipesCount; i++) { + int strLen = VarInt.peek(buffer, posx); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in RemovedRecipes"); + } + + posx += VarInt.size(strLen); + posx += strLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in RemovedRecipes"); + } + } + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int removedRecipesOffset = buffer.getIntLE(offset + 6); - if (removedRecipesOffset < 0) { - return ValidationResult.error("Invalid offset for RemovedRecipes"); - } - - int posx = offset + 10 + removedRecipesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for RemovedRecipes"); - } - - int removedRecipesCount = VarInt.peek(buffer, posx); - if (removedRecipesCount < 0) { - return ValidationResult.error("Invalid array count for RemovedRecipes"); - } - - if (removedRecipesCount > 4096000) { - return ValidationResult.error("RemovedRecipes exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < removedRecipesCount; i++) { - int strLen = VarInt.peek(buffer, posx); - if (strLen < 0) { - return ValidationResult.error("Invalid string length in RemovedRecipes"); - } - - posx += VarInt.length(buffer, posx); - posx += strLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading string in RemovedRecipes"); - } - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateRepulsionConfig.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateRepulsionConfig.java index ab3a5075..df1e7631 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateRepulsionConfig.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateRepulsionConfig.java @@ -57,36 +57,41 @@ public class UpdateRepulsionConfig implements Packet, ToClientPacket { @Nonnull public static UpdateRepulsionConfig deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateRepulsionConfig obj = new UpdateRepulsionConfig(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int repulsionConfigsCount = VarInt.peek(buf, pos); - if (repulsionConfigsCount < 0) { - throw ProtocolException.negativeLength("RepulsionConfigs", repulsionConfigsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateRepulsionConfig", 6, buf.readableBytes() - offset); + } else { + UpdateRepulsionConfig obj = new UpdateRepulsionConfig(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int repulsionConfigsCount = VarInt.peek(buf, pos); + if (repulsionConfigsCount < 0) { + throw ProtocolException.invalidVarInt("RepulsionConfigs"); + } - if (repulsionConfigsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("RepulsionConfigs", repulsionConfigsCount, 4096000); - } + int repulsionConfigsVarLen = VarInt.size(repulsionConfigsCount); + if (repulsionConfigsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("RepulsionConfigs", repulsionConfigsCount, 4096000); + } - pos += VarInt.size(repulsionConfigsCount); - obj.repulsionConfigs = new HashMap<>(repulsionConfigsCount); + pos += repulsionConfigsVarLen; + obj.repulsionConfigs = new HashMap<>(repulsionConfigsCount); - for (int i = 0; i < repulsionConfigsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - RepulsionConfig val = RepulsionConfig.deserialize(buf, pos); - pos += RepulsionConfig.computeBytesConsumed(buf, pos); - if (obj.repulsionConfigs.put(key, val) != null) { - throw ProtocolException.duplicateKey("repulsionConfigs", key); + for (int i = 0; i < repulsionConfigsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + RepulsionConfig val = RepulsionConfig.deserialize(buf, pos); + pos += RepulsionConfig.computeBytesConsumed(buf, pos); + if (obj.repulsionConfigs.put(key, val) != null) { + throw ProtocolException.duplicateKey("repulsionConfigs", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateRepulsionConfig implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -144,30 +149,35 @@ public class UpdateRepulsionConfig implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int repulsionConfigsCount = VarInt.peek(buffer, pos); - if (repulsionConfigsCount < 0) { - return ValidationResult.error("Invalid dictionary count for RepulsionConfigs"); - } - - if (repulsionConfigsCount > 4096000) { - return ValidationResult.error("RepulsionConfigs exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < repulsionConfigsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int repulsionConfigsCount = VarInt.peek(buffer, v); + if (repulsionConfigsCount < 0) { + return ValidationResult.error("Invalid dictionary count for RepulsionConfigs"); } - pos += 12; - } - } + if (repulsionConfigsCount > 4096000) { + return ValidationResult.error("RepulsionConfigs exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(repulsionConfigsCount); + + for (int i = 0; i < repulsionConfigsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += 12; + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateResourceTypes.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateResourceTypes.java index a98a8eda..e9a1c778 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateResourceTypes.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateResourceTypes.java @@ -55,45 +55,54 @@ public class UpdateResourceTypes implements Packet, ToClientPacket { @Nonnull public static UpdateResourceTypes deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateResourceTypes obj = new UpdateResourceTypes(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int resourceTypesCount = VarInt.peek(buf, pos); - if (resourceTypesCount < 0) { - throw ProtocolException.negativeLength("ResourceTypes", resourceTypesCount); - } - - if (resourceTypesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("ResourceTypes", resourceTypesCount, 4096000); - } - - pos += VarInt.size(resourceTypesCount); - obj.resourceTypes = new HashMap<>(resourceTypesCount); - - for (int i = 0; i < resourceTypesCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateResourceTypes", 2, buf.readableBytes() - offset); + } else { + UpdateResourceTypes obj = new UpdateResourceTypes(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int resourceTypesCount = VarInt.peek(buf, pos); + if (resourceTypesCount < 0) { + throw ProtocolException.invalidVarInt("ResourceTypes"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int resourceTypesVarLen = VarInt.size(resourceTypesCount); + if (resourceTypesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("ResourceTypes", resourceTypesCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - ResourceType val = ResourceType.deserialize(buf, pos); - pos += ResourceType.computeBytesConsumed(buf, pos); - if (obj.resourceTypes.put(key, val) != null) { - throw ProtocolException.duplicateKey("resourceTypes", key); + pos += resourceTypesVarLen; + obj.resourceTypes = new HashMap<>(resourceTypesCount); + + for (int i = 0; i < resourceTypesCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + ResourceType val = ResourceType.deserialize(buf, pos); + pos += ResourceType.computeBytesConsumed(buf, pos); + if (obj.resourceTypes.put(key, val) != null) { + throw ProtocolException.duplicateKey("resourceTypes", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,11 +110,11 @@ public class UpdateResourceTypes implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += ResourceType.computeBytesConsumed(buf, pos); } } @@ -157,40 +166,45 @@ public class UpdateResourceTypes implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int resourceTypesCount = VarInt.peek(buffer, pos); - if (resourceTypesCount < 0) { - return ValidationResult.error("Invalid dictionary count for ResourceTypes"); - } - - if (resourceTypesCount > 4096000) { - return ValidationResult.error("ResourceTypes exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < resourceTypesCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int resourceTypesCount = VarInt.peek(buffer, v); + if (resourceTypesCount < 0) { + return ValidationResult.error("Invalid dictionary count for ResourceTypes"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + if (resourceTypesCount > 4096000) { + return ValidationResult.error("ResourceTypes exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } + v += VarInt.size(resourceTypesCount); - pos += ResourceType.computeBytesConsumed(buffer, pos); + for (int i = 0; i < resourceTypesCount; i++) { + int keyLen = VarInt.peek(buffer, v); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + v += VarInt.size(keyLen); + v += keyLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += ResourceType.computeBytesConsumed(buffer, v); + } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateReverbEffects.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateReverbEffects.java index 3515e545..379da724 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateReverbEffects.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateReverbEffects.java @@ -57,36 +57,41 @@ public class UpdateReverbEffects implements Packet, ToClientPacket { @Nonnull public static UpdateReverbEffects deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateReverbEffects obj = new UpdateReverbEffects(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int effectsCount = VarInt.peek(buf, pos); - if (effectsCount < 0) { - throw ProtocolException.negativeLength("Effects", effectsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateReverbEffects", 6, buf.readableBytes() - offset); + } else { + UpdateReverbEffects obj = new UpdateReverbEffects(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int effectsCount = VarInt.peek(buf, pos); + if (effectsCount < 0) { + throw ProtocolException.invalidVarInt("Effects"); + } - if (effectsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Effects", effectsCount, 4096000); - } + int effectsVarLen = VarInt.size(effectsCount); + if (effectsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Effects", effectsCount, 4096000); + } - pos += VarInt.size(effectsCount); - obj.effects = new HashMap<>(effectsCount); + pos += effectsVarLen; + obj.effects = new HashMap<>(effectsCount); - for (int i = 0; i < effectsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - ReverbEffect val = ReverbEffect.deserialize(buf, pos); - pos += ReverbEffect.computeBytesConsumed(buf, pos); - if (obj.effects.put(key, val) != null) { - throw ProtocolException.duplicateKey("effects", key); + for (int i = 0; i < effectsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + ReverbEffect val = ReverbEffect.deserialize(buf, pos); + pos += ReverbEffect.computeBytesConsumed(buf, pos); + if (obj.effects.put(key, val) != null) { + throw ProtocolException.duplicateKey("effects", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateReverbEffects implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateReverbEffects implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int effectsCount = VarInt.peek(buffer, pos); - if (effectsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Effects"); - } - - if (effectsCount > 4096000) { - return ValidationResult.error("Effects exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < effectsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int effectsCount = VarInt.peek(buffer, v); + if (effectsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Effects"); } - pos += ReverbEffect.computeBytesConsumed(buffer, pos); - } - } + if (effectsCount > 4096000) { + return ValidationResult.error("Effects exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(effectsCount); + + for (int i = 0; i < effectsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += ReverbEffect.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateRootInteractions.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateRootInteractions.java index 8f51e798..d8e59307 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateRootInteractions.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateRootInteractions.java @@ -57,36 +57,41 @@ public class UpdateRootInteractions implements Packet, ToClientPacket { @Nonnull public static UpdateRootInteractions deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateRootInteractions obj = new UpdateRootInteractions(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int interactionsCount = VarInt.peek(buf, pos); - if (interactionsCount < 0) { - throw ProtocolException.negativeLength("Interactions", interactionsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateRootInteractions", 6, buf.readableBytes() - offset); + } else { + UpdateRootInteractions obj = new UpdateRootInteractions(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int interactionsCount = VarInt.peek(buf, pos); + if (interactionsCount < 0) { + throw ProtocolException.invalidVarInt("Interactions"); + } - if (interactionsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); - } + int interactionsVarLen = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + } - pos += VarInt.size(interactionsCount); - obj.interactions = new HashMap<>(interactionsCount); + pos += interactionsVarLen; + obj.interactions = new HashMap<>(interactionsCount); - for (int i = 0; i < interactionsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - RootInteraction val = RootInteraction.deserialize(buf, pos); - pos += RootInteraction.computeBytesConsumed(buf, pos); - if (obj.interactions.put(key, val) != null) { - throw ProtocolException.duplicateKey("interactions", key); + for (int i = 0; i < interactionsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + RootInteraction val = RootInteraction.deserialize(buf, pos); + pos += RootInteraction.computeBytesConsumed(buf, pos); + if (obj.interactions.put(key, val) != null) { + throw ProtocolException.duplicateKey("interactions", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateRootInteractions implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateRootInteractions implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int interactionsCount = VarInt.peek(buffer, pos); - if (interactionsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Interactions"); - } - - if (interactionsCount > 4096000) { - return ValidationResult.error("Interactions exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < interactionsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int interactionsCount = VarInt.peek(buffer, v); + if (interactionsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Interactions"); } - pos += RootInteraction.computeBytesConsumed(buffer, pos); - } - } + if (interactionsCount > 4096000) { + return ValidationResult.error("Interactions exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(interactionsCount); + + for (int i = 0; i < interactionsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += RootInteraction.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateSoundEvents.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateSoundEvents.java index 2cc08c88..e676ddac 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateSoundEvents.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateSoundEvents.java @@ -57,36 +57,41 @@ public class UpdateSoundEvents implements Packet, ToClientPacket { @Nonnull public static UpdateSoundEvents deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateSoundEvents obj = new UpdateSoundEvents(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int soundEventsCount = VarInt.peek(buf, pos); - if (soundEventsCount < 0) { - throw ProtocolException.negativeLength("SoundEvents", soundEventsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateSoundEvents", 6, buf.readableBytes() - offset); + } else { + UpdateSoundEvents obj = new UpdateSoundEvents(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int soundEventsCount = VarInt.peek(buf, pos); + if (soundEventsCount < 0) { + throw ProtocolException.invalidVarInt("SoundEvents"); + } - if (soundEventsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SoundEvents", soundEventsCount, 4096000); - } + int soundEventsVarLen = VarInt.size(soundEventsCount); + if (soundEventsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SoundEvents", soundEventsCount, 4096000); + } - pos += VarInt.size(soundEventsCount); - obj.soundEvents = new HashMap<>(soundEventsCount); + pos += soundEventsVarLen; + obj.soundEvents = new HashMap<>(soundEventsCount); - for (int i = 0; i < soundEventsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - SoundEvent val = SoundEvent.deserialize(buf, pos); - pos += SoundEvent.computeBytesConsumed(buf, pos); - if (obj.soundEvents.put(key, val) != null) { - throw ProtocolException.duplicateKey("soundEvents", key); + for (int i = 0; i < soundEventsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + SoundEvent val = SoundEvent.deserialize(buf, pos); + pos += SoundEvent.computeBytesConsumed(buf, pos); + if (obj.soundEvents.put(key, val) != null) { + throw ProtocolException.duplicateKey("soundEvents", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateSoundEvents implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateSoundEvents implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int soundEventsCount = VarInt.peek(buffer, pos); - if (soundEventsCount < 0) { - return ValidationResult.error("Invalid dictionary count for SoundEvents"); - } - - if (soundEventsCount > 4096000) { - return ValidationResult.error("SoundEvents exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < soundEventsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int soundEventsCount = VarInt.peek(buffer, v); + if (soundEventsCount < 0) { + return ValidationResult.error("Invalid dictionary count for SoundEvents"); } - pos += SoundEvent.computeBytesConsumed(buffer, pos); - } - } + if (soundEventsCount > 4096000) { + return ValidationResult.error("SoundEvents exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(soundEventsCount); + + for (int i = 0; i < soundEventsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += SoundEvent.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateSoundSets.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateSoundSets.java index 941eb103..10979c68 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateSoundSets.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateSoundSets.java @@ -57,36 +57,41 @@ public class UpdateSoundSets implements Packet, ToClientPacket { @Nonnull public static UpdateSoundSets deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateSoundSets obj = new UpdateSoundSets(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int soundSetsCount = VarInt.peek(buf, pos); - if (soundSetsCount < 0) { - throw ProtocolException.negativeLength("SoundSets", soundSetsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateSoundSets", 6, buf.readableBytes() - offset); + } else { + UpdateSoundSets obj = new UpdateSoundSets(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int soundSetsCount = VarInt.peek(buf, pos); + if (soundSetsCount < 0) { + throw ProtocolException.invalidVarInt("SoundSets"); + } - if (soundSetsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("SoundSets", soundSetsCount, 4096000); - } + int soundSetsVarLen = VarInt.size(soundSetsCount); + if (soundSetsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("SoundSets", soundSetsCount, 4096000); + } - pos += VarInt.size(soundSetsCount); - obj.soundSets = new HashMap<>(soundSetsCount); + pos += soundSetsVarLen; + obj.soundSets = new HashMap<>(soundSetsCount); - for (int i = 0; i < soundSetsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - SoundSet val = SoundSet.deserialize(buf, pos); - pos += SoundSet.computeBytesConsumed(buf, pos); - if (obj.soundSets.put(key, val) != null) { - throw ProtocolException.duplicateKey("soundSets", key); + for (int i = 0; i < soundSetsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + SoundSet val = SoundSet.deserialize(buf, pos); + pos += SoundSet.computeBytesConsumed(buf, pos); + if (obj.soundSets.put(key, val) != null) { + throw ProtocolException.duplicateKey("soundSets", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateSoundSets implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateSoundSets implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int soundSetsCount = VarInt.peek(buffer, pos); - if (soundSetsCount < 0) { - return ValidationResult.error("Invalid dictionary count for SoundSets"); - } - - if (soundSetsCount > 4096000) { - return ValidationResult.error("SoundSets exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < soundSetsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int soundSetsCount = VarInt.peek(buffer, v); + if (soundSetsCount < 0) { + return ValidationResult.error("Invalid dictionary count for SoundSets"); } - pos += SoundSet.computeBytesConsumed(buffer, pos); - } - } + if (soundSetsCount > 4096000) { + return ValidationResult.error("SoundSets exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(soundSetsCount); + + for (int i = 0; i < soundSetsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += SoundSet.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateTagPatterns.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateTagPatterns.java index b29d51cc..f47b0b69 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateTagPatterns.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateTagPatterns.java @@ -57,36 +57,41 @@ public class UpdateTagPatterns implements Packet, ToClientPacket { @Nonnull public static UpdateTagPatterns deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateTagPatterns obj = new UpdateTagPatterns(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int patternsCount = VarInt.peek(buf, pos); - if (patternsCount < 0) { - throw ProtocolException.negativeLength("Patterns", patternsCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateTagPatterns", 6, buf.readableBytes() - offset); + } else { + UpdateTagPatterns obj = new UpdateTagPatterns(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int patternsCount = VarInt.peek(buf, pos); + if (patternsCount < 0) { + throw ProtocolException.invalidVarInt("Patterns"); + } - if (patternsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Patterns", patternsCount, 4096000); - } + int patternsVarLen = VarInt.size(patternsCount); + if (patternsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Patterns", patternsCount, 4096000); + } - pos += VarInt.size(patternsCount); - obj.patterns = new HashMap<>(patternsCount); + pos += patternsVarLen; + obj.patterns = new HashMap<>(patternsCount); - for (int i = 0; i < patternsCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - TagPattern val = TagPattern.deserialize(buf, pos); - pos += TagPattern.computeBytesConsumed(buf, pos); - if (obj.patterns.put(key, val) != null) { - throw ProtocolException.duplicateKey("patterns", key); + for (int i = 0; i < patternsCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + TagPattern val = TagPattern.deserialize(buf, pos); + pos += TagPattern.computeBytesConsumed(buf, pos); + if (obj.patterns.put(key, val) != null) { + throw ProtocolException.duplicateKey("patterns", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateTagPatterns implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateTagPatterns implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int patternsCount = VarInt.peek(buffer, pos); - if (patternsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Patterns"); - } - - if (patternsCount > 4096000) { - return ValidationResult.error("Patterns exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < patternsCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int patternsCount = VarInt.peek(buffer, v); + if (patternsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Patterns"); } - pos += TagPattern.computeBytesConsumed(buffer, pos); - } - } + if (patternsCount > 4096000) { + return ValidationResult.error("Patterns exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(patternsCount); + + for (int i = 0; i < patternsCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += TagPattern.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateTrails.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateTrails.java index 48dc2f8d..7969ad1b 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateTrails.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateTrails.java @@ -55,45 +55,54 @@ public class UpdateTrails implements Packet, ToClientPacket { @Nonnull public static UpdateTrails deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateTrails obj = new UpdateTrails(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int trailsCount = VarInt.peek(buf, pos); - if (trailsCount < 0) { - throw ProtocolException.negativeLength("Trails", trailsCount); - } - - if (trailsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Trails", trailsCount, 4096000); - } - - pos += VarInt.size(trailsCount); - obj.trails = new HashMap<>(trailsCount); - - for (int i = 0; i < trailsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateTrails", 2, buf.readableBytes() - offset); + } else { + UpdateTrails obj = new UpdateTrails(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int trailsCount = VarInt.peek(buf, pos); + if (trailsCount < 0) { + throw ProtocolException.invalidVarInt("Trails"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int trailsVarLen = VarInt.size(trailsCount); + if (trailsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Trails", trailsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - Trail val = Trail.deserialize(buf, pos); - pos += Trail.computeBytesConsumed(buf, pos); - if (obj.trails.put(key, val) != null) { - throw ProtocolException.duplicateKey("trails", key); + pos += trailsVarLen; + obj.trails = new HashMap<>(trailsCount); + + for (int i = 0; i < trailsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + Trail val = Trail.deserialize(buf, pos); + pos += Trail.computeBytesConsumed(buf, pos); + if (obj.trails.put(key, val) != null) { + throw ProtocolException.duplicateKey("trails", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,11 +110,11 @@ public class UpdateTrails implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += Trail.computeBytesConsumed(buf, pos); } } @@ -157,40 +166,45 @@ public class UpdateTrails implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int trailsCount = VarInt.peek(buffer, pos); - if (trailsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Trails"); - } - - if (trailsCount > 4096000) { - return ValidationResult.error("Trails exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < trailsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int trailsCount = VarInt.peek(buffer, v); + if (trailsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Trails"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + if (trailsCount > 4096000) { + return ValidationResult.error("Trails exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } + v += VarInt.size(trailsCount); - pos += Trail.computeBytesConsumed(buffer, pos); + for (int i = 0; i < trailsCount; i++) { + int keyLen = VarInt.peek(buffer, v); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + v += VarInt.size(keyLen); + v += keyLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += Trail.computeBytesConsumed(buffer, v); + } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateTranslations.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateTranslations.java index 911cba01..464cd4b1 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateTranslations.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateTranslations.java @@ -54,55 +54,68 @@ public class UpdateTranslations implements Packet, ToClientPacket { @Nonnull public static UpdateTranslations deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateTranslations obj = new UpdateTranslations(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int translationsCount = VarInt.peek(buf, pos); - if (translationsCount < 0) { - throw ProtocolException.negativeLength("Translations", translationsCount); - } - - if (translationsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Translations", translationsCount, 4096000); - } - - pos += VarInt.size(translationsCount); - obj.translations = new HashMap<>(translationsCount); - - for (int i = 0; i < translationsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateTranslations", 2, buf.readableBytes() - offset); + } else { + UpdateTranslations obj = new UpdateTranslations(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int translationsCount = VarInt.peek(buf, pos); + if (translationsCount < 0) { + throw ProtocolException.invalidVarInt("Translations"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int translationsVarLen = VarInt.size(translationsCount); + if (translationsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Translations", translationsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - int valLen = VarInt.peek(buf, pos); - if (valLen < 0) { - throw ProtocolException.negativeLength("val", valLen); - } + pos += translationsVarLen; + obj.translations = new HashMap<>(translationsCount); - if (valLen > 4096000) { - throw ProtocolException.stringTooLong("val", valLen, 4096000); - } + for (int i = 0; i < translationsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } - int valVarLen = VarInt.length(buf, pos); - String val = PacketIO.readVarString(buf, pos); - pos += valVarLen + valLen; - if (obj.translations.put(key, val) != null) { - throw ProtocolException.duplicateKey("translations", key); + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + int valLen = VarInt.peek(buf, pos); + if (valLen < 0) { + throw ProtocolException.invalidVarInt("val"); + } + + int valVarLen = VarInt.size(valLen); + if (valLen > 4096000) { + throw ProtocolException.stringTooLong("val", valLen, 4096000); + } + + if (pos + valVarLen + valLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("val", pos + valVarLen + valLen, buf.readableBytes()); + } + + String val = PacketIO.readVarString(buf, pos); + pos += valVarLen + valLen; + if (obj.translations.put(key, val) != null) { + throw ProtocolException.duplicateKey("translations", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -110,13 +123,13 @@ public class UpdateTranslations implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } } @@ -167,53 +180,58 @@ public class UpdateTranslations implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int translationsCount = VarInt.peek(buffer, pos); - if (translationsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Translations"); - } - - if (translationsCount > 4096000) { - return ValidationResult.error("Translations exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < translationsCount; i++) { - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int translationsCount = VarInt.peek(buffer, v); + if (translationsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Translations"); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); + if (translationsCount > 4096000) { + return ValidationResult.error("Translations exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } + v += VarInt.size(translationsCount); - int valueLen = VarInt.peek(buffer, pos); - if (valueLen < 0) { - return ValidationResult.error("Invalid string length for value"); - } + for (int i = 0; i < translationsCount; i++) { + int keyLen = VarInt.peek(buffer, v); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } - if (valueLen > 4096000) { - return ValidationResult.error("value exceeds max length 4096000"); - } + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } - pos += VarInt.length(buffer, pos); - pos += valueLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + v += VarInt.size(keyLen); + v += keyLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + int valueLen = VarInt.peek(buffer, v); + if (valueLen < 0) { + return ValidationResult.error("Invalid string length for value"); + } + + if (valueLen > 4096000) { + return ValidationResult.error("value exceeds max length 4096000"); + } + + v += VarInt.size(valueLen); + v += valueLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } } } + + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateUnarmedInteractions.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateUnarmedInteractions.java index f096076c..9818bb71 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateUnarmedInteractions.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateUnarmedInteractions.java @@ -54,34 +54,39 @@ public class UpdateUnarmedInteractions implements Packet, ToClientPacket { @Nonnull public static UpdateUnarmedInteractions deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateUnarmedInteractions obj = new UpdateUnarmedInteractions(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int interactionsCount = VarInt.peek(buf, pos); - if (interactionsCount < 0) { - throw ProtocolException.negativeLength("Interactions", interactionsCount); - } + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateUnarmedInteractions", 2, buf.readableBytes() - offset); + } else { + UpdateUnarmedInteractions obj = new UpdateUnarmedInteractions(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int interactionsCount = VarInt.peek(buf, pos); + if (interactionsCount < 0) { + throw ProtocolException.invalidVarInt("Interactions"); + } - if (interactionsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); - } + int interactionsVarLen = VarInt.size(interactionsCount); + if (interactionsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000); + } - pos += VarInt.size(interactionsCount); - obj.interactions = new HashMap<>(interactionsCount); + pos += interactionsVarLen; + obj.interactions = new HashMap<>(interactionsCount); - for (int i = 0; i < interactionsCount; i++) { - InteractionType key = InteractionType.fromValue(buf.getByte(pos)); - int val = buf.getIntLE(++pos); - pos += 4; - if (obj.interactions.put(key, val) != null) { - throw ProtocolException.duplicateKey("interactions", key); + for (int i = 0; i < interactionsCount; i++) { + InteractionType key = InteractionType.fromValue(buf.getByte(pos)); + int val = buf.getIntLE(++pos); + pos += 4; + if (obj.interactions.put(key, val) != null) { + throw ProtocolException.duplicateKey("interactions", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -89,7 +94,7 @@ public class UpdateUnarmedInteractions implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos = ++pos + 4; @@ -137,28 +142,38 @@ public class UpdateUnarmedInteractions implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int interactionsCount = VarInt.peek(buffer, pos); - if (interactionsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Interactions"); - } + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int interactionsCount = VarInt.peek(buffer, v); + if (interactionsCount < 0) { + return ValidationResult.error("Invalid dictionary count for Interactions"); + } - if (interactionsCount > 4096000) { - return ValidationResult.error("Interactions exceeds max length 4096000"); - } + if (interactionsCount > 4096000) { + return ValidationResult.error("Interactions exceeds max length 4096000"); + } - pos += VarInt.length(buffer, pos); + v += VarInt.size(interactionsCount); - for (int i = 0; i < interactionsCount; i++) { - pos = ++pos + 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading value"); + for (int i = 0; i < interactionsCount; i++) { + int vx = buffer.getByte(v) & 255; + if (vx >= 25) { + return ValidationResult.error("Invalid InteractionType value for key"); + } + + v = ++v + 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading value"); + } } } - } - return ValidationResult.OK; + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateViewBobbing.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateViewBobbing.java index 3e642ea9..ae580c0b 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateViewBobbing.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateViewBobbing.java @@ -55,34 +55,39 @@ public class UpdateViewBobbing implements Packet, ToClientPacket { @Nonnull public static UpdateViewBobbing deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateViewBobbing obj = new UpdateViewBobbing(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int profilesCount = VarInt.peek(buf, pos); - if (profilesCount < 0) { - throw ProtocolException.negativeLength("Profiles", profilesCount); - } + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateViewBobbing", 2, buf.readableBytes() - offset); + } else { + UpdateViewBobbing obj = new UpdateViewBobbing(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int profilesCount = VarInt.peek(buf, pos); + if (profilesCount < 0) { + throw ProtocolException.invalidVarInt("Profiles"); + } - if (profilesCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Profiles", profilesCount, 4096000); - } + int profilesVarLen = VarInt.size(profilesCount); + if (profilesCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Profiles", profilesCount, 4096000); + } - pos += VarInt.size(profilesCount); - obj.profiles = new HashMap<>(profilesCount); + pos += profilesVarLen; + obj.profiles = new HashMap<>(profilesCount); - for (int i = 0; i < profilesCount; i++) { - MovementType key = MovementType.fromValue(buf.getByte(pos)); - ViewBobbing val = ViewBobbing.deserialize(buf, ++pos); - pos += ViewBobbing.computeBytesConsumed(buf, pos); - if (obj.profiles.put(key, val) != null) { - throw ProtocolException.duplicateKey("profiles", key); + for (int i = 0; i < profilesCount; i++) { + MovementType key = MovementType.fromValue(buf.getByte(pos)); + ViewBobbing val = ViewBobbing.deserialize(buf, ++pos); + pos += ViewBobbing.computeBytesConsumed(buf, pos); + if (obj.profiles.put(key, val) != null) { + throw ProtocolException.duplicateKey("profiles", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,7 +95,7 @@ public class UpdateViewBobbing implements Packet, ToClientPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos = ++pos + ViewBobbing.computeBytesConsumed(buf, pos); @@ -144,25 +149,35 @@ public class UpdateViewBobbing implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int profilesCount = VarInt.peek(buffer, pos); - if (profilesCount < 0) { - return ValidationResult.error("Invalid dictionary count for Profiles"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int profilesCount = VarInt.peek(buffer, v); + if (profilesCount < 0) { + return ValidationResult.error("Invalid dictionary count for Profiles"); + } + + if (profilesCount > 4096000) { + return ValidationResult.error("Profiles exceeds max length 4096000"); + } + + v += VarInt.size(profilesCount); + + for (int i = 0; i < profilesCount; i++) { + int vx = buffer.getByte(v) & 255; + if (vx >= 13) { + return ValidationResult.error("Invalid MovementType value for key"); + } + + v = ++v + ViewBobbing.computeBytesConsumed(buffer, v); + } } - if (profilesCount > 4096000) { - return ValidationResult.error("Profiles exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < profilesCount; i++) { - pos = ++pos + ViewBobbing.computeBytesConsumed(buffer, pos); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/assets/UpdateWeathers.java b/src/com/hypixel/hytale/protocol/packets/assets/UpdateWeathers.java index dd3cbbd3..cf05eb93 100644 --- a/src/com/hypixel/hytale/protocol/packets/assets/UpdateWeathers.java +++ b/src/com/hypixel/hytale/protocol/packets/assets/UpdateWeathers.java @@ -57,36 +57,41 @@ public class UpdateWeathers implements Packet, ToClientPacket { @Nonnull public static UpdateWeathers deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateWeathers obj = new UpdateWeathers(); - byte nullBits = buf.getByte(offset); - obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); - obj.maxId = buf.getIntLE(offset + 2); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int weathersCount = VarInt.peek(buf, pos); - if (weathersCount < 0) { - throw ProtocolException.negativeLength("Weathers", weathersCount); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdateWeathers", 6, buf.readableBytes() - offset); + } else { + UpdateWeathers obj = new UpdateWeathers(); + byte nullBits = buf.getByte(offset); + obj.type = UpdateType.fromValue(buf.getByte(offset + 1)); + obj.maxId = buf.getIntLE(offset + 2); + int pos = offset + 6; + if ((nullBits & 1) != 0) { + int weathersCount = VarInt.peek(buf, pos); + if (weathersCount < 0) { + throw ProtocolException.invalidVarInt("Weathers"); + } - if (weathersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Weathers", weathersCount, 4096000); - } + int weathersVarLen = VarInt.size(weathersCount); + if (weathersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Weathers", weathersCount, 4096000); + } - pos += VarInt.size(weathersCount); - obj.weathers = new HashMap<>(weathersCount); + pos += weathersVarLen; + obj.weathers = new HashMap<>(weathersCount); - for (int i = 0; i < weathersCount; i++) { - int key = buf.getIntLE(pos); - pos += 4; - Weather val = Weather.deserialize(buf, pos); - pos += Weather.computeBytesConsumed(buf, pos); - if (obj.weathers.put(key, val) != null) { - throw ProtocolException.duplicateKey("weathers", key); + for (int i = 0; i < weathersCount; i++) { + int key = buf.getIntLE(pos); + pos += 4; + Weather val = Weather.deserialize(buf, pos); + pos += Weather.computeBytesConsumed(buf, pos); + if (obj.weathers.put(key, val) != null) { + throw ProtocolException.duplicateKey("weathers", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,7 +99,7 @@ public class UpdateWeathers implements Packet, ToClientPacket { int pos = offset + 6; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 4; @@ -150,30 +155,35 @@ public class UpdateWeathers implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 6 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 6; - if ((nullBits & 1) != 0) { - int weathersCount = VarInt.peek(buffer, pos); - if (weathersCount < 0) { - return ValidationResult.error("Invalid dictionary count for Weathers"); - } - - if (weathersCount > 4096000) { - return ValidationResult.error("Weathers exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < weathersCount; i++) { - pos += 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid UpdateType value for Type"); + } else { + v = offset + 6; + if ((nullBits & 1) != 0) { + int weathersCount = VarInt.peek(buffer, v); + if (weathersCount < 0) { + return ValidationResult.error("Invalid dictionary count for Weathers"); } - pos += Weather.computeBytesConsumed(buffer, pos); - } - } + if (weathersCount > 4096000) { + return ValidationResult.error("Weathers exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(weathersCount); + + for (int i = 0; i < weathersCount; i++) { + v += 4; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + v += Weather.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/auth/AuthGrant.java b/src/com/hypixel/hytale/protocol/packets/auth/AuthGrant.java index caa34a8c..d96ace1c 100644 --- a/src/com/hypixel/hytale/protocol/packets/auth/AuthGrant.java +++ b/src/com/hypixel/hytale/protocol/packets/auth/AuthGrant.java @@ -50,37 +50,63 @@ public class AuthGrant implements Packet, ToClientPacket { @Nonnull public static AuthGrant deserialize(@Nonnull ByteBuf buf, int offset) { - AuthGrant obj = new AuthGrant(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int authorizationGrantLen = VarInt.peek(buf, varPos0); - if (authorizationGrantLen < 0) { - throw ProtocolException.negativeLength("AuthorizationGrant", authorizationGrantLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AuthGrant", 9, buf.readableBytes() - offset); + } else { + AuthGrant obj = new AuthGrant(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("AuthorizationGrant", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int authorizationGrantLen = VarInt.peek(buf, varPos0); + if (authorizationGrantLen < 0) { + throw ProtocolException.invalidVarInt("AuthorizationGrant"); + } + + int authorizationGrantVarIntLen = VarInt.size(authorizationGrantLen); + if (authorizationGrantLen > 4096) { + throw ProtocolException.stringTooLong("AuthorizationGrant", authorizationGrantLen, 4096); + } + + if (varPos0 + authorizationGrantVarIntLen + authorizationGrantLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AuthorizationGrant", varPos0 + authorizationGrantVarIntLen + authorizationGrantLen, buf.readableBytes()); + } + + obj.authorizationGrant = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (authorizationGrantLen > 4096) { - throw ProtocolException.stringTooLong("AuthorizationGrant", authorizationGrantLen, 4096); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ServerIdentityToken", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int serverIdentityTokenLen = VarInt.peek(buf, varPos1); + if (serverIdentityTokenLen < 0) { + throw ProtocolException.invalidVarInt("ServerIdentityToken"); + } + + int serverIdentityTokenVarIntLen = VarInt.size(serverIdentityTokenLen); + if (serverIdentityTokenLen > 8192) { + throw ProtocolException.stringTooLong("ServerIdentityToken", serverIdentityTokenLen, 8192); + } + + if (varPos1 + serverIdentityTokenVarIntLen + serverIdentityTokenLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "ServerIdentityToken", varPos1 + serverIdentityTokenVarIntLen + serverIdentityTokenLen, buf.readableBytes() + ); + } + + obj.serverIdentityToken = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.authorizationGrant = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int serverIdentityTokenLen = VarInt.peek(buf, varPos1); - if (serverIdentityTokenLen < 0) { - throw ProtocolException.negativeLength("ServerIdentityToken", serverIdentityTokenLen); - } - - if (serverIdentityTokenLen > 8192) { - throw ProtocolException.stringTooLong("ServerIdentityToken", serverIdentityTokenLen, 8192); - } - - obj.serverIdentityToken = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -88,9 +114,13 @@ public class AuthGrant implements Packet, ToClientPacket { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("AuthorizationGrant", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -98,9 +128,13 @@ public class AuthGrant implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ServerIdentityToken", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -163,15 +197,11 @@ public class AuthGrant implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int authorizationGrantOffset = buffer.getIntLE(offset + 1); - if (authorizationGrantOffset < 0) { + if (authorizationGrantOffset < 0 || authorizationGrantOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for AuthorizationGrant"); } int pos = offset + 9 + authorizationGrantOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AuthorizationGrant"); - } - int authorizationGrantLen = VarInt.peek(buffer, pos); if (authorizationGrantLen < 0) { return ValidationResult.error("Invalid string length for AuthorizationGrant"); @@ -181,7 +211,7 @@ public class AuthGrant implements Packet, ToClientPacket { return ValidationResult.error("AuthorizationGrant exceeds max length 4096"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(authorizationGrantLen); pos += authorizationGrantLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading AuthorizationGrant"); @@ -190,15 +220,11 @@ public class AuthGrant implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int serverIdentityTokenOffset = buffer.getIntLE(offset + 5); - if (serverIdentityTokenOffset < 0) { + if (serverIdentityTokenOffset < 0 || serverIdentityTokenOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for ServerIdentityToken"); } int posx = offset + 9 + serverIdentityTokenOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ServerIdentityToken"); - } - int serverIdentityTokenLen = VarInt.peek(buffer, posx); if (serverIdentityTokenLen < 0) { return ValidationResult.error("Invalid string length for ServerIdentityToken"); @@ -208,7 +234,7 @@ public class AuthGrant implements Packet, ToClientPacket { return ValidationResult.error("ServerIdentityToken exceeds max length 8192"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(serverIdentityTokenLen); posx += serverIdentityTokenLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ServerIdentityToken"); diff --git a/src/com/hypixel/hytale/protocol/packets/auth/AuthToken.java b/src/com/hypixel/hytale/protocol/packets/auth/AuthToken.java index d9d9ad1a..873d78b8 100644 --- a/src/com/hypixel/hytale/protocol/packets/auth/AuthToken.java +++ b/src/com/hypixel/hytale/protocol/packets/auth/AuthToken.java @@ -50,37 +50,63 @@ public class AuthToken implements Packet, ToServerPacket { @Nonnull public static AuthToken deserialize(@Nonnull ByteBuf buf, int offset) { - AuthToken obj = new AuthToken(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int accessTokenLen = VarInt.peek(buf, varPos0); - if (accessTokenLen < 0) { - throw ProtocolException.negativeLength("AccessToken", accessTokenLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("AuthToken", 9, buf.readableBytes() - offset); + } else { + AuthToken obj = new AuthToken(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("AccessToken", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int accessTokenLen = VarInt.peek(buf, varPos0); + if (accessTokenLen < 0) { + throw ProtocolException.invalidVarInt("AccessToken"); + } + + int accessTokenVarIntLen = VarInt.size(accessTokenLen); + if (accessTokenLen > 8192) { + throw ProtocolException.stringTooLong("AccessToken", accessTokenLen, 8192); + } + + if (varPos0 + accessTokenVarIntLen + accessTokenLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AccessToken", varPos0 + accessTokenVarIntLen + accessTokenLen, buf.readableBytes()); + } + + obj.accessToken = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (accessTokenLen > 8192) { - throw ProtocolException.stringTooLong("AccessToken", accessTokenLen, 8192); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ServerAuthorizationGrant", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int serverAuthorizationGrantLen = VarInt.peek(buf, varPos1); + if (serverAuthorizationGrantLen < 0) { + throw ProtocolException.invalidVarInt("ServerAuthorizationGrant"); + } + + int serverAuthorizationGrantVarIntLen = VarInt.size(serverAuthorizationGrantLen); + if (serverAuthorizationGrantLen > 4096) { + throw ProtocolException.stringTooLong("ServerAuthorizationGrant", serverAuthorizationGrantLen, 4096); + } + + if (varPos1 + serverAuthorizationGrantVarIntLen + serverAuthorizationGrantLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall( + "ServerAuthorizationGrant", varPos1 + serverAuthorizationGrantVarIntLen + serverAuthorizationGrantLen, buf.readableBytes() + ); + } + + obj.serverAuthorizationGrant = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.accessToken = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int serverAuthorizationGrantLen = VarInt.peek(buf, varPos1); - if (serverAuthorizationGrantLen < 0) { - throw ProtocolException.negativeLength("ServerAuthorizationGrant", serverAuthorizationGrantLen); - } - - if (serverAuthorizationGrantLen > 4096) { - throw ProtocolException.stringTooLong("ServerAuthorizationGrant", serverAuthorizationGrantLen, 4096); - } - - obj.serverAuthorizationGrant = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -88,9 +114,13 @@ public class AuthToken implements Packet, ToServerPacket { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("AccessToken", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -98,9 +128,13 @@ public class AuthToken implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ServerAuthorizationGrant", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -163,15 +197,11 @@ public class AuthToken implements Packet, ToServerPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int accessTokenOffset = buffer.getIntLE(offset + 1); - if (accessTokenOffset < 0) { + if (accessTokenOffset < 0 || accessTokenOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for AccessToken"); } int pos = offset + 9 + accessTokenOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AccessToken"); - } - int accessTokenLen = VarInt.peek(buffer, pos); if (accessTokenLen < 0) { return ValidationResult.error("Invalid string length for AccessToken"); @@ -181,7 +211,7 @@ public class AuthToken implements Packet, ToServerPacket { return ValidationResult.error("AccessToken exceeds max length 8192"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(accessTokenLen); pos += accessTokenLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading AccessToken"); @@ -190,15 +220,11 @@ public class AuthToken implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int serverAuthorizationGrantOffset = buffer.getIntLE(offset + 5); - if (serverAuthorizationGrantOffset < 0) { + if (serverAuthorizationGrantOffset < 0 || serverAuthorizationGrantOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for ServerAuthorizationGrant"); } int posx = offset + 9 + serverAuthorizationGrantOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ServerAuthorizationGrant"); - } - int serverAuthorizationGrantLen = VarInt.peek(buffer, posx); if (serverAuthorizationGrantLen < 0) { return ValidationResult.error("Invalid string length for ServerAuthorizationGrant"); @@ -208,7 +234,7 @@ public class AuthToken implements Packet, ToServerPacket { return ValidationResult.error("ServerAuthorizationGrant exceeds max length 4096"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(serverAuthorizationGrantLen); posx += serverAuthorizationGrantLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ServerAuthorizationGrant"); diff --git a/src/com/hypixel/hytale/protocol/packets/auth/ClientReferral.java b/src/com/hypixel/hytale/protocol/packets/auth/ClientReferral.java index a9ec6a1f..c74729bd 100644 --- a/src/com/hypixel/hytale/protocol/packets/auth/ClientReferral.java +++ b/src/com/hypixel/hytale/protocol/packets/auth/ClientReferral.java @@ -51,37 +51,51 @@ public class ClientReferral implements Packet, ToClientPacket { @Nonnull public static ClientReferral deserialize(@Nonnull ByteBuf buf, int offset) { - ClientReferral obj = new ClientReferral(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - obj.hostTo = HostAddress.deserialize(buf, varPos0); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ClientReferral", 9, buf.readableBytes() - offset); + } else { + ClientReferral obj = new ClientReferral(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("HostTo", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + obj.hostTo = HostAddress.deserialize(buf, varPos0); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Data", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int dataCount = VarInt.peek(buf, varPos1); + if (dataCount < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int varIntLen = VarInt.size(dataCount); + if (dataCount > 4096) { + throw ProtocolException.arrayTooLong("Data", dataCount, 4096); + } + + if (varPos1 + varIntLen + dataCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", varPos1 + varIntLen + dataCount * 1, buf.readableBytes()); + } + + obj.data = new byte[dataCount]; + + for (int i = 0; i < dataCount; i++) { + obj.data[i] = buf.getByte(varPos1 + varIntLen + i * 1); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int dataCount = VarInt.peek(buf, varPos1); - if (dataCount < 0) { - throw ProtocolException.negativeLength("Data", dataCount); - } - - if (dataCount > 4096) { - throw ProtocolException.arrayTooLong("Data", dataCount, 4096); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + dataCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Data", varPos1 + varIntLen + dataCount * 1, buf.readableBytes()); - } - - obj.data = new byte[dataCount]; - - for (int i = 0; i < dataCount; i++) { - obj.data[i] = buf.getByte(varPos1 + varIntLen + i * 1); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -89,6 +103,10 @@ public class ClientReferral implements Packet, ToClientPacket { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("HostTo", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; pos0 += HostAddress.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -98,9 +116,13 @@ public class ClientReferral implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Data", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 1; + pos1 += VarInt.size(arrLen) + arrLen * 1; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -171,15 +193,11 @@ public class ClientReferral implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int hostToOffset = buffer.getIntLE(offset + 1); - if (hostToOffset < 0) { + if (hostToOffset < 0 || hostToOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for HostTo"); } int pos = offset + 9 + hostToOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for HostTo"); - } - ValidationResult hostToResult = HostAddress.validateStructure(buffer, pos); if (!hostToResult.isValid()) { return ValidationResult.error("Invalid HostTo: " + hostToResult.error()); @@ -190,16 +208,12 @@ public class ClientReferral implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int dataOffset = buffer.getIntLE(offset + 5); - if (dataOffset < 0) { + if (dataOffset < 0 || dataOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Data"); } - int posx = offset + 9 + dataOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Data"); - } - - int dataCount = VarInt.peek(buffer, posx); + int pos = offset + 9 + dataOffset; + int dataCount = VarInt.peek(buffer, pos); if (dataCount < 0) { return ValidationResult.error("Invalid array count for Data"); } @@ -208,9 +222,9 @@ public class ClientReferral implements Packet, ToClientPacket { return ValidationResult.error("Data exceeds max length 4096"); } - posx += VarInt.length(buffer, posx); - posx += dataCount * 1; - if (posx > buffer.writerIndex()) { + pos += VarInt.size(dataCount); + pos += dataCount * 1; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Data"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/auth/ConnectAccept.java b/src/com/hypixel/hytale/protocol/packets/auth/ConnectAccept.java index 1f91d700..967ebc53 100644 --- a/src/com/hypixel/hytale/protocol/packets/auth/ConnectAccept.java +++ b/src/com/hypixel/hytale/protocol/packets/auth/ConnectAccept.java @@ -45,35 +45,39 @@ public class ConnectAccept implements Packet, ToClientPacket { @Nonnull public static ConnectAccept deserialize(@Nonnull ByteBuf buf, int offset) { - ConnectAccept obj = new ConnectAccept(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int passwordChallengeCount = VarInt.peek(buf, pos); - if (passwordChallengeCount < 0) { - throw ProtocolException.negativeLength("PasswordChallenge", passwordChallengeCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ConnectAccept", 1, buf.readableBytes() - offset); + } else { + ConnectAccept obj = new ConnectAccept(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int passwordChallengeCount = VarInt.peek(buf, pos); + if (passwordChallengeCount < 0) { + throw ProtocolException.invalidVarInt("PasswordChallenge"); + } + + int passwordChallengeVarLen = VarInt.size(passwordChallengeCount); + if (passwordChallengeCount > 64) { + throw ProtocolException.arrayTooLong("PasswordChallenge", passwordChallengeCount, 64); + } + + if (pos + passwordChallengeVarLen + passwordChallengeCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("PasswordChallenge", pos + passwordChallengeVarLen + passwordChallengeCount * 1, buf.readableBytes()); + } + + pos += passwordChallengeVarLen; + obj.passwordChallenge = new byte[passwordChallengeCount]; + + for (int i = 0; i < passwordChallengeCount; i++) { + obj.passwordChallenge[i] = buf.getByte(pos + i * 1); + } + + pos += passwordChallengeCount * 1; } - if (passwordChallengeCount > 64) { - throw ProtocolException.arrayTooLong("PasswordChallenge", passwordChallengeCount, 64); - } - - int passwordChallengeVarLen = VarInt.size(passwordChallengeCount); - if (pos + passwordChallengeVarLen + passwordChallengeCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("PasswordChallenge", pos + passwordChallengeVarLen + passwordChallengeCount * 1, buf.readableBytes()); - } - - pos += passwordChallengeVarLen; - obj.passwordChallenge = new byte[passwordChallengeCount]; - - for (int i = 0; i < passwordChallengeCount; i++) { - obj.passwordChallenge[i] = buf.getByte(pos + i * 1); - } - - pos += passwordChallengeCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,7 +85,7 @@ public class ConnectAccept implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -134,7 +138,7 @@ public class ConnectAccept implements Packet, ToClientPacket { return ValidationResult.error("PasswordChallenge exceeds max length 64"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(passwordChallengeCount); pos += passwordChallengeCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading PasswordChallenge"); diff --git a/src/com/hypixel/hytale/protocol/packets/auth/PasswordRejected.java b/src/com/hypixel/hytale/protocol/packets/auth/PasswordRejected.java index 86cb4856..cd07a1c0 100644 --- a/src/com/hypixel/hytale/protocol/packets/auth/PasswordRejected.java +++ b/src/com/hypixel/hytale/protocol/packets/auth/PasswordRejected.java @@ -48,36 +48,40 @@ public class PasswordRejected implements Packet, ToClientPacket { @Nonnull public static PasswordRejected deserialize(@Nonnull ByteBuf buf, int offset) { - PasswordRejected obj = new PasswordRejected(); - byte nullBits = buf.getByte(offset); - obj.attemptsRemaining = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int newChallengeCount = VarInt.peek(buf, pos); - if (newChallengeCount < 0) { - throw ProtocolException.negativeLength("NewChallenge", newChallengeCount); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("PasswordRejected", 5, buf.readableBytes() - offset); + } else { + PasswordRejected obj = new PasswordRejected(); + byte nullBits = buf.getByte(offset); + obj.attemptsRemaining = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int newChallengeCount = VarInt.peek(buf, pos); + if (newChallengeCount < 0) { + throw ProtocolException.invalidVarInt("NewChallenge"); + } + + int newChallengeVarLen = VarInt.size(newChallengeCount); + if (newChallengeCount > 64) { + throw ProtocolException.arrayTooLong("NewChallenge", newChallengeCount, 64); + } + + if (pos + newChallengeVarLen + newChallengeCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("NewChallenge", pos + newChallengeVarLen + newChallengeCount * 1, buf.readableBytes()); + } + + pos += newChallengeVarLen; + obj.newChallenge = new byte[newChallengeCount]; + + for (int i = 0; i < newChallengeCount; i++) { + obj.newChallenge[i] = buf.getByte(pos + i * 1); + } + + pos += newChallengeCount * 1; } - if (newChallengeCount > 64) { - throw ProtocolException.arrayTooLong("NewChallenge", newChallengeCount, 64); - } - - int newChallengeVarLen = VarInt.size(newChallengeCount); - if (pos + newChallengeVarLen + newChallengeCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("NewChallenge", pos + newChallengeVarLen + newChallengeCount * 1, buf.readableBytes()); - } - - pos += newChallengeVarLen; - obj.newChallenge = new byte[newChallengeCount]; - - for (int i = 0; i < newChallengeCount; i++) { - obj.newChallenge[i] = buf.getByte(pos + i * 1); - } - - pos += newChallengeCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -85,7 +89,7 @@ public class PasswordRejected implements Packet, ToClientPacket { int pos = offset + 5; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -139,7 +143,7 @@ public class PasswordRejected implements Packet, ToClientPacket { return ValidationResult.error("NewChallenge exceeds max length 64"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(newChallengeCount); pos += newChallengeCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading NewChallenge"); diff --git a/src/com/hypixel/hytale/protocol/packets/auth/PasswordResponse.java b/src/com/hypixel/hytale/protocol/packets/auth/PasswordResponse.java index 2b8b3ef0..ac8618bf 100644 --- a/src/com/hypixel/hytale/protocol/packets/auth/PasswordResponse.java +++ b/src/com/hypixel/hytale/protocol/packets/auth/PasswordResponse.java @@ -45,35 +45,39 @@ public class PasswordResponse implements Packet, ToServerPacket { @Nonnull public static PasswordResponse deserialize(@Nonnull ByteBuf buf, int offset) { - PasswordResponse obj = new PasswordResponse(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int hashCount = VarInt.peek(buf, pos); - if (hashCount < 0) { - throw ProtocolException.negativeLength("Hash", hashCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("PasswordResponse", 1, buf.readableBytes() - offset); + } else { + PasswordResponse obj = new PasswordResponse(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int hashCount = VarInt.peek(buf, pos); + if (hashCount < 0) { + throw ProtocolException.invalidVarInt("Hash"); + } + + int hashVarLen = VarInt.size(hashCount); + if (hashCount > 64) { + throw ProtocolException.arrayTooLong("Hash", hashCount, 64); + } + + if (pos + hashVarLen + hashCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Hash", pos + hashVarLen + hashCount * 1, buf.readableBytes()); + } + + pos += hashVarLen; + obj.hash = new byte[hashCount]; + + for (int i = 0; i < hashCount; i++) { + obj.hash[i] = buf.getByte(pos + i * 1); + } + + pos += hashCount * 1; } - if (hashCount > 64) { - throw ProtocolException.arrayTooLong("Hash", hashCount, 64); - } - - int hashVarLen = VarInt.size(hashCount); - if (pos + hashVarLen + hashCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Hash", pos + hashVarLen + hashCount * 1, buf.readableBytes()); - } - - pos += hashVarLen; - obj.hash = new byte[hashCount]; - - for (int i = 0; i < hashCount; i++) { - obj.hash[i] = buf.getByte(pos + i * 1); - } - - pos += hashCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,7 +85,7 @@ public class PasswordResponse implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -134,7 +138,7 @@ public class PasswordResponse implements Packet, ToServerPacket { return ValidationResult.error("Hash exceeds max length 64"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(hashCount); pos += hashCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Hash"); diff --git a/src/com/hypixel/hytale/protocol/packets/auth/ServerAuthToken.java b/src/com/hypixel/hytale/protocol/packets/auth/ServerAuthToken.java index 4e5e5ae2..2e984638 100644 --- a/src/com/hypixel/hytale/protocol/packets/auth/ServerAuthToken.java +++ b/src/com/hypixel/hytale/protocol/packets/auth/ServerAuthToken.java @@ -51,46 +51,65 @@ public class ServerAuthToken implements Packet, ToClientPacket { @Nonnull public static ServerAuthToken deserialize(@Nonnull ByteBuf buf, int offset) { - ServerAuthToken obj = new ServerAuthToken(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int serverAccessTokenLen = VarInt.peek(buf, varPos0); - if (serverAccessTokenLen < 0) { - throw ProtocolException.negativeLength("ServerAccessToken", serverAccessTokenLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ServerAuthToken", 9, buf.readableBytes() - offset); + } else { + ServerAuthToken obj = new ServerAuthToken(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ServerAccessToken", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int serverAccessTokenLen = VarInt.peek(buf, varPos0); + if (serverAccessTokenLen < 0) { + throw ProtocolException.invalidVarInt("ServerAccessToken"); + } + + int serverAccessTokenVarIntLen = VarInt.size(serverAccessTokenLen); + if (serverAccessTokenLen > 8192) { + throw ProtocolException.stringTooLong("ServerAccessToken", serverAccessTokenLen, 8192); + } + + if (varPos0 + serverAccessTokenVarIntLen + serverAccessTokenLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ServerAccessToken", varPos0 + serverAccessTokenVarIntLen + serverAccessTokenLen, buf.readableBytes()); + } + + obj.serverAccessToken = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (serverAccessTokenLen > 8192) { - throw ProtocolException.stringTooLong("ServerAccessToken", serverAccessTokenLen, 8192); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("PasswordChallenge", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int passwordChallengeCount = VarInt.peek(buf, varPos1); + if (passwordChallengeCount < 0) { + throw ProtocolException.invalidVarInt("PasswordChallenge"); + } + + int varIntLen = VarInt.size(passwordChallengeCount); + if (passwordChallengeCount > 64) { + throw ProtocolException.arrayTooLong("PasswordChallenge", passwordChallengeCount, 64); + } + + if (varPos1 + varIntLen + passwordChallengeCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("PasswordChallenge", varPos1 + varIntLen + passwordChallengeCount * 1, buf.readableBytes()); + } + + obj.passwordChallenge = new byte[passwordChallengeCount]; + + for (int i = 0; i < passwordChallengeCount; i++) { + obj.passwordChallenge[i] = buf.getByte(varPos1 + varIntLen + i * 1); + } } - obj.serverAccessToken = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int passwordChallengeCount = VarInt.peek(buf, varPos1); - if (passwordChallengeCount < 0) { - throw ProtocolException.negativeLength("PasswordChallenge", passwordChallengeCount); - } - - if (passwordChallengeCount > 64) { - throw ProtocolException.arrayTooLong("PasswordChallenge", passwordChallengeCount, 64); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + passwordChallengeCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("PasswordChallenge", varPos1 + varIntLen + passwordChallengeCount * 1, buf.readableBytes()); - } - - obj.passwordChallenge = new byte[passwordChallengeCount]; - - for (int i = 0; i < passwordChallengeCount; i++) { - obj.passwordChallenge[i] = buf.getByte(varPos1 + varIntLen + i * 1); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -98,9 +117,13 @@ public class ServerAuthToken implements Packet, ToClientPacket { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ServerAccessToken", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -108,9 +131,13 @@ public class ServerAuthToken implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("PasswordChallenge", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 1; + pos1 += VarInt.size(arrLen) + arrLen * 1; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -181,15 +208,11 @@ public class ServerAuthToken implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int serverAccessTokenOffset = buffer.getIntLE(offset + 1); - if (serverAccessTokenOffset < 0) { + if (serverAccessTokenOffset < 0 || serverAccessTokenOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for ServerAccessToken"); } int pos = offset + 9 + serverAccessTokenOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ServerAccessToken"); - } - int serverAccessTokenLen = VarInt.peek(buffer, pos); if (serverAccessTokenLen < 0) { return ValidationResult.error("Invalid string length for ServerAccessToken"); @@ -199,7 +222,7 @@ public class ServerAuthToken implements Packet, ToClientPacket { return ValidationResult.error("ServerAccessToken exceeds max length 8192"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(serverAccessTokenLen); pos += serverAccessTokenLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ServerAccessToken"); @@ -208,15 +231,11 @@ public class ServerAuthToken implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int passwordChallengeOffset = buffer.getIntLE(offset + 5); - if (passwordChallengeOffset < 0) { + if (passwordChallengeOffset < 0 || passwordChallengeOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for PasswordChallenge"); } int posx = offset + 9 + passwordChallengeOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for PasswordChallenge"); - } - int passwordChallengeCount = VarInt.peek(buffer, posx); if (passwordChallengeCount < 0) { return ValidationResult.error("Invalid array count for PasswordChallenge"); @@ -226,7 +245,7 @@ public class ServerAuthToken implements Packet, ToClientPacket { return ValidationResult.error("PasswordChallenge exceeds max length 64"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(passwordChallengeCount); posx += passwordChallengeCount * 1; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading PasswordChallenge"); diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BrushOrigin.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BrushOrigin.java index 4e528e3c..5acab756 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BrushOrigin.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BrushOrigin.java @@ -5,7 +5,9 @@ import com.hypixel.hytale.protocol.io.ProtocolException; public enum BrushOrigin { Center(0), Bottom(1), - Top(2); + Top(2), + Lowest(3), + Highest(4); public static final BrushOrigin[] VALUES = values(); private final int value; diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArg.java index 5ffc82a2..22440d7c 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArg.java @@ -1,7 +1,9 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; @@ -10,10 +12,12 @@ import javax.annotation.Nullable; public class BuilderToolArg { public static final int NULLABLE_BIT_FIELD_SIZE = 2; public static final int FIXED_BLOCK_SIZE = 33; - public static final int VARIABLE_FIELD_COUNT = 4; - public static final int VARIABLE_BLOCK_START = 49; + public static final int VARIABLE_FIELD_COUNT = 5; + public static final int VARIABLE_BLOCK_START = 53; public static final int MAX_SIZE = 1677721600; public boolean required; + @Nullable + public String id; @Nonnull public BuilderToolArgType argType = BuilderToolArgType.Bool; @Nullable @@ -44,6 +48,7 @@ public class BuilderToolArg { public BuilderToolArg( boolean required, + @Nullable String id, @Nonnull BuilderToolArgType argType, @Nullable BuilderToolBoolArg boolArg, @Nullable BuilderToolFloatArg floatArg, @@ -58,6 +63,7 @@ public class BuilderToolArg { @Nullable BuilderToolOptionArg optionArg ) { this.required = required; + this.id = id; this.argType = argType; this.boolArg = boolArg; this.floatArg = floatArg; @@ -74,6 +80,7 @@ public class BuilderToolArg { public BuilderToolArg(@Nonnull BuilderToolArg other) { this.required = other.required; + this.id = other.id; this.argType = other.argType; this.boolArg = other.boolArg; this.floatArg = other.floatArg; @@ -90,68 +97,121 @@ public class BuilderToolArg { @Nonnull public static BuilderToolArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolArg obj = new BuilderToolArg(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - obj.required = buf.getByte(offset + 2) != 0; - obj.argType = BuilderToolArgType.fromValue(buf.getByte(offset + 3)); - if ((nullBits[0] & 1) != 0) { - obj.boolArg = BuilderToolBoolArg.deserialize(buf, offset + 4); - } + if (buf.readableBytes() - offset < 53) { + throw ProtocolException.bufferTooSmall("BuilderToolArg", 53, buf.readableBytes() - offset); + } else { + BuilderToolArg obj = new BuilderToolArg(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + obj.required = buf.getByte(offset + 2) != 0; + obj.argType = BuilderToolArgType.fromValue(buf.getByte(offset + 3)); + if ((nullBits[0] & 1) != 0) { + obj.boolArg = BuilderToolBoolArg.deserialize(buf, offset + 4); + } - if ((nullBits[0] & 2) != 0) { - obj.floatArg = BuilderToolFloatArg.deserialize(buf, offset + 5); - } + if ((nullBits[0] & 2) != 0) { + obj.floatArg = BuilderToolFloatArg.deserialize(buf, offset + 5); + } - if ((nullBits[0] & 4) != 0) { - obj.intArg = BuilderToolIntArg.deserialize(buf, offset + 17); - } + if ((nullBits[0] & 4) != 0) { + obj.intArg = BuilderToolIntArg.deserialize(buf, offset + 17); + } - if ((nullBits[0] & 8) != 0) { - obj.brushShapeArg = BuilderToolBrushShapeArg.deserialize(buf, offset + 29); - } + if ((nullBits[0] & 8) != 0) { + obj.brushShapeArg = BuilderToolBrushShapeArg.deserialize(buf, offset + 29); + } - if ((nullBits[0] & 16) != 0) { - obj.brushOriginArg = BuilderToolBrushOriginArg.deserialize(buf, offset + 30); - } + if ((nullBits[0] & 16) != 0) { + obj.brushOriginArg = BuilderToolBrushOriginArg.deserialize(buf, offset + 30); + } - if ((nullBits[0] & 32) != 0) { - obj.brushAxisArg = BuilderToolBrushAxisArg.deserialize(buf, offset + 31); - } + if ((nullBits[0] & 32) != 0) { + obj.brushAxisArg = BuilderToolBrushAxisArg.deserialize(buf, offset + 31); + } - if ((nullBits[0] & 64) != 0) { - obj.rotationArg = BuilderToolRotationArg.deserialize(buf, offset + 32); - } + if ((nullBits[0] & 64) != 0) { + obj.rotationArg = BuilderToolRotationArg.deserialize(buf, offset + 32); + } - if ((nullBits[0] & 128) != 0) { - int varPos0 = offset + 49 + buf.getIntLE(offset + 33); - obj.stringArg = BuilderToolStringArg.deserialize(buf, varPos0); - } + if ((nullBits[0] & 128) != 0) { + int varPosBase0 = buf.getIntLE(offset + 33); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } - if ((nullBits[1] & 1) != 0) { - int varPos1 = offset + 49 + buf.getIntLE(offset + 37); - obj.blockArg = BuilderToolBlockArg.deserialize(buf, varPos1); - } + int varPos0 = offset + 53 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } - if ((nullBits[1] & 2) != 0) { - int varPos2 = offset + 49 + buf.getIntLE(offset + 41); - obj.maskArg = BuilderToolMaskArg.deserialize(buf, varPos2); - } + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } - if ((nullBits[1] & 4) != 0) { - int varPos3 = offset + 49 + buf.getIntLE(offset + 45); - obj.optionArg = BuilderToolOptionArg.deserialize(buf, varPos3); - } + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } - return obj; + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase1 = buf.getIntLE(offset + 37); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("StringArg", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 53 + varPosBase1; + obj.stringArg = BuilderToolStringArg.deserialize(buf, varPos1); + } + + if ((nullBits[1] & 2) != 0) { + int varPosBase2 = buf.getIntLE(offset + 41); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("BlockArg", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 53 + varPosBase2; + obj.blockArg = BuilderToolBlockArg.deserialize(buf, varPos2); + } + + if ((nullBits[1] & 4) != 0) { + int varPosBase3 = buf.getIntLE(offset + 45); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("MaskArg", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 53 + varPosBase3; + obj.maskArg = BuilderToolMaskArg.deserialize(buf, varPos3); + } + + if ((nullBits[1] & 8) != 0) { + int varPosBase4 = buf.getIntLE(offset + 49); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("OptionArg", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 53 + varPosBase4; + obj.optionArg = BuilderToolOptionArg.deserialize(buf, varPos4); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - int maxEnd = 49; + int maxEnd = 53; if ((nullBits[0] & 128) != 0) { int fieldOffset0 = buf.getIntLE(offset + 33); - int pos0 = offset + 49 + fieldOffset0; - pos0 += BuilderToolStringArg.computeBytesConsumed(buf, pos0); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + + int pos0 = offset + 53 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -159,8 +219,12 @@ public class BuilderToolArg { if ((nullBits[1] & 1) != 0) { int fieldOffset1 = buf.getIntLE(offset + 37); - int pos1 = offset + 49 + fieldOffset1; - pos1 += BuilderToolBlockArg.computeBytesConsumed(buf, pos1); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("StringArg", fieldOffset1, maxEnd); + } + + int pos1 = offset + 53 + fieldOffset1; + pos1 += BuilderToolStringArg.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -168,8 +232,12 @@ public class BuilderToolArg { if ((nullBits[1] & 2) != 0) { int fieldOffset2 = buf.getIntLE(offset + 41); - int pos2 = offset + 49 + fieldOffset2; - pos2 += BuilderToolMaskArg.computeBytesConsumed(buf, pos2); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("BlockArg", fieldOffset2, maxEnd); + } + + int pos2 = offset + 53 + fieldOffset2; + pos2 += BuilderToolBlockArg.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -177,13 +245,30 @@ public class BuilderToolArg { if ((nullBits[1] & 4) != 0) { int fieldOffset3 = buf.getIntLE(offset + 45); - int pos3 = offset + 49 + fieldOffset3; - pos3 += BuilderToolOptionArg.computeBytesConsumed(buf, pos3); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("MaskArg", fieldOffset3, maxEnd); + } + + int pos3 = offset + 53 + fieldOffset3; + pos3 += BuilderToolMaskArg.computeBytesConsumed(buf, pos3); if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; } } + if ((nullBits[1] & 8) != 0) { + int fieldOffset4 = buf.getIntLE(offset + 49); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("OptionArg", fieldOffset4, maxEnd); + } + + int pos4 = offset + 53 + fieldOffset4; + pos4 += BuilderToolOptionArg.computeBytesConsumed(buf, pos4); + if (pos4 - offset > maxEnd) { + maxEnd = pos4 - offset; + } + } + return maxEnd; } @@ -218,22 +303,26 @@ public class BuilderToolArg { nullBits[0] = (byte)(nullBits[0] | 64); } - if (this.stringArg != null) { + if (this.id != null) { nullBits[0] = (byte)(nullBits[0] | 128); } - if (this.blockArg != null) { + if (this.stringArg != null) { nullBits[1] = (byte)(nullBits[1] | 1); } - if (this.maskArg != null) { + if (this.blockArg != null) { nullBits[1] = (byte)(nullBits[1] | 2); } - if (this.optionArg != null) { + if (this.maskArg != null) { nullBits[1] = (byte)(nullBits[1] | 4); } + if (this.optionArg != null) { + nullBits[1] = (byte)(nullBits[1] | 8); + } + buf.writeBytes(nullBits); buf.writeByte(this.required ? 1 : 0); buf.writeByte(this.argType.getValue()); @@ -279,6 +368,8 @@ public class BuilderToolArg { buf.writeZero(1); } + int idOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int stringArgOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int blockArgOffsetSlot = buf.writerIndex(); @@ -288,6 +379,13 @@ public class BuilderToolArg { int optionArgOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); + if (this.id != null) { + buf.setIntLE(idOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.id, 4096000); + } else { + buf.setIntLE(idOffsetSlot, -1); + } + if (this.stringArg != null) { buf.setIntLE(stringArgOffsetSlot, buf.writerIndex() - varBlockStart); this.stringArg.serialize(buf); @@ -318,7 +416,11 @@ public class BuilderToolArg { } public int computeSize() { - int size = 49; + int size = 53; + if (this.id != null) { + size += PacketIO.stringSize(this.id); + } + if (this.stringArg != null) { size += this.stringArg.computeSize(); } @@ -339,93 +441,106 @@ public class BuilderToolArg { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 49) { - return ValidationResult.error("Buffer too small: expected at least 49 bytes"); + if (buffer.readableBytes() - offset < 53) { + return ValidationResult.error("Buffer too small: expected at least 53 bytes"); } else { byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); - if ((nullBits[0] & 128) != 0) { - int stringArgOffset = buffer.getIntLE(offset + 33); - if (stringArgOffset < 0) { - return ValidationResult.error("Invalid offset for StringArg"); + int v = buffer.getByte(offset + 3) & 255; + if (v >= 11) { + return ValidationResult.error("Invalid BuilderToolArgType value for ArgType"); + } else { + if ((nullBits[0] & 128) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for Id"); + } + + int pos = offset + 53 + v; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } } - int pos = offset + 49 + stringArgOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for StringArg"); + if ((nullBits[1] & 1) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for StringArg"); + } + + int posx = offset + 53 + v; + ValidationResult stringArgResult = BuilderToolStringArg.validateStructure(buffer, posx); + if (!stringArgResult.isValid()) { + return ValidationResult.error("Invalid StringArg: " + stringArgResult.error()); + } + + posx += BuilderToolStringArg.computeBytesConsumed(buffer, posx); } - ValidationResult stringArgResult = BuilderToolStringArg.validateStructure(buffer, pos); - if (!stringArgResult.isValid()) { - return ValidationResult.error("Invalid StringArg: " + stringArgResult.error()); + if ((nullBits[1] & 2) != 0) { + v = buffer.getIntLE(offset + 41); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for BlockArg"); + } + + int posx = offset + 53 + v; + ValidationResult blockArgResult = BuilderToolBlockArg.validateStructure(buffer, posx); + if (!blockArgResult.isValid()) { + return ValidationResult.error("Invalid BlockArg: " + blockArgResult.error()); + } + + posx += BuilderToolBlockArg.computeBytesConsumed(buffer, posx); } - pos += BuilderToolStringArg.computeBytesConsumed(buffer, pos); + if ((nullBits[1] & 4) != 0) { + v = buffer.getIntLE(offset + 45); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for MaskArg"); + } + + int posx = offset + 53 + v; + ValidationResult maskArgResult = BuilderToolMaskArg.validateStructure(buffer, posx); + if (!maskArgResult.isValid()) { + return ValidationResult.error("Invalid MaskArg: " + maskArgResult.error()); + } + + posx += BuilderToolMaskArg.computeBytesConsumed(buffer, posx); + } + + if ((nullBits[1] & 8) != 0) { + v = buffer.getIntLE(offset + 49); + if (v < 0 || v > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for OptionArg"); + } + + int posx = offset + 53 + v; + ValidationResult optionArgResult = BuilderToolOptionArg.validateStructure(buffer, posx); + if (!optionArgResult.isValid()) { + return ValidationResult.error("Invalid OptionArg: " + optionArgResult.error()); + } + + posx += BuilderToolOptionArg.computeBytesConsumed(buffer, posx); + } + + return ValidationResult.OK; } - - if ((nullBits[1] & 1) != 0) { - int blockArgOffset = buffer.getIntLE(offset + 37); - if (blockArgOffset < 0) { - return ValidationResult.error("Invalid offset for BlockArg"); - } - - int posx = offset + 49 + blockArgOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlockArg"); - } - - ValidationResult blockArgResult = BuilderToolBlockArg.validateStructure(buffer, posx); - if (!blockArgResult.isValid()) { - return ValidationResult.error("Invalid BlockArg: " + blockArgResult.error()); - } - - posx += BuilderToolBlockArg.computeBytesConsumed(buffer, posx); - } - - if ((nullBits[1] & 2) != 0) { - int maskArgOffset = buffer.getIntLE(offset + 41); - if (maskArgOffset < 0) { - return ValidationResult.error("Invalid offset for MaskArg"); - } - - int posxx = offset + 49 + maskArgOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaskArg"); - } - - ValidationResult maskArgResult = BuilderToolMaskArg.validateStructure(buffer, posxx); - if (!maskArgResult.isValid()) { - return ValidationResult.error("Invalid MaskArg: " + maskArgResult.error()); - } - - posxx += BuilderToolMaskArg.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits[1] & 4) != 0) { - int optionArgOffset = buffer.getIntLE(offset + 45); - if (optionArgOffset < 0) { - return ValidationResult.error("Invalid offset for OptionArg"); - } - - int posxxx = offset + 49 + optionArgOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for OptionArg"); - } - - ValidationResult optionArgResult = BuilderToolOptionArg.validateStructure(buffer, posxxx); - if (!optionArgResult.isValid()) { - return ValidationResult.error("Invalid OptionArg: " + optionArgResult.error()); - } - - posxxx += BuilderToolOptionArg.computeBytesConsumed(buffer, posxxx); - } - - return ValidationResult.OK; } } public BuilderToolArg clone() { BuilderToolArg copy = new BuilderToolArg(); copy.required = this.required; + copy.id = this.id; copy.argType = this.argType; copy.boolArg = this.boolArg != null ? this.boolArg.clone() : null; copy.floatArg = this.floatArg != null ? this.floatArg.clone() : null; @@ -449,6 +564,7 @@ public class BuilderToolArg { return !(obj instanceof BuilderToolArg other) ? false : this.required == other.required + && Objects.equals(this.id, other.id) && Objects.equals(this.argType, other.argType) && Objects.equals(this.boolArg, other.boolArg) && Objects.equals(this.floatArg, other.floatArg) @@ -468,6 +584,7 @@ public class BuilderToolArg { public int hashCode() { return Objects.hash( this.required, + this.id, this.argType, this.boolArg, this.floatArg, diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArgUpdate.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArgUpdate.java index c59ace76..6fbcf7fc 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArgUpdate.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArgUpdate.java @@ -16,15 +16,13 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { public static final int PACKET_ID = 400; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 14; + public static final int FIXED_BLOCK_SIZE = 13; public static final int VARIABLE_FIELD_COUNT = 2; - public static final int VARIABLE_BLOCK_START = 22; - public static final int MAX_SIZE = 32768032; + public static final int VARIABLE_BLOCK_START = 21; + public static final int MAX_SIZE = 32768031; public int token; public int section; public int slot; - @Nonnull - public BuilderToolArgGroup group = BuilderToolArgGroup.Tool; @Nullable public String id; @Nullable @@ -43,11 +41,10 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { public BuilderToolArgUpdate() { } - public BuilderToolArgUpdate(int token, int section, int slot, @Nonnull BuilderToolArgGroup group, @Nullable String id, @Nullable String value) { + public BuilderToolArgUpdate(int token, int section, int slot, @Nullable String id, @Nullable String value) { this.token = token; this.section = section; this.slot = slot; - this.group = group; this.id = id; this.value = value; } @@ -56,68 +53,98 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { this.token = other.token; this.section = other.section; this.slot = other.slot; - this.group = other.group; this.id = other.id; this.value = other.value; } @Nonnull public static BuilderToolArgUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolArgUpdate obj = new BuilderToolArgUpdate(); - byte nullBits = buf.getByte(offset); - obj.token = buf.getIntLE(offset + 1); - obj.section = buf.getIntLE(offset + 5); - obj.slot = buf.getIntLE(offset + 9); - obj.group = BuilderToolArgGroup.fromValue(buf.getByte(offset + 13)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 22 + buf.getIntLE(offset + 14); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("BuilderToolArgUpdate", 21, buf.readableBytes() - offset); + } else { + BuilderToolArgUpdate obj = new BuilderToolArgUpdate(); + byte nullBits = buf.getByte(offset); + obj.token = buf.getIntLE(offset + 1); + obj.section = buf.getIntLE(offset + 5); + obj.slot = buf.getIntLE(offset + 9); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 13); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 21 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 17); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Value", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + int valueLen = VarInt.peek(buf, varPos1); + if (valueLen < 0) { + throw ProtocolException.invalidVarInt("Value"); + } + + int valueVarIntLen = VarInt.size(valueLen); + if (valueLen > 4096000) { + throw ProtocolException.stringTooLong("Value", valueLen, 4096000); + } + + if (varPos1 + valueVarIntLen + valueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Value", varPos1 + valueVarIntLen + valueLen, buf.readableBytes()); + } + + obj.value = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 22 + buf.getIntLE(offset + 18); - int valueLen = VarInt.peek(buf, varPos1); - if (valueLen < 0) { - throw ProtocolException.negativeLength("Value", valueLen); - } - - if (valueLen > 4096000) { - throw ProtocolException.stringTooLong("Value", valueLen, 4096000); - } - - obj.value = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 22; + int maxEnd = 21; if ((nullBits & 1) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 14); - int pos0 = offset + 22 + fieldOffset0; + int fieldOffset0 = buf.getIntLE(offset + 13); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + + int pos0 = offset + 21 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } } if ((nullBits & 2) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 18); - int pos1 = offset + 22 + fieldOffset1; + int fieldOffset1 = buf.getIntLE(offset + 17); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Value", fieldOffset1, maxEnd); + } + + int pos1 = offset + 21 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -142,7 +169,6 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { buf.writeIntLE(this.token); buf.writeIntLE(this.section); buf.writeIntLE(this.slot); - buf.writeByte(this.group.getValue()); int idOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int valueOffsetSlot = buf.writerIndex(); @@ -165,7 +191,7 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { @Override public int computeSize() { - int size = 22; + int size = 21; if (this.id != null) { size += PacketIO.stringSize(this.id); } @@ -178,21 +204,17 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 22) { - return ValidationResult.error("Buffer too small: expected at least 22 bytes"); + if (buffer.readableBytes() - offset < 21) { + return ValidationResult.error("Buffer too small: expected at least 21 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { - int idOffset = buffer.getIntLE(offset + 14); - if (idOffset < 0) { + int idOffset = buffer.getIntLE(offset + 13); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Id"); } - int pos = offset + 22 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - + int pos = offset + 21 + idOffset; int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -202,7 +224,7 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -210,16 +232,12 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { } if ((nullBits & 2) != 0) { - int valueOffset = buffer.getIntLE(offset + 18); - if (valueOffset < 0) { + int valueOffset = buffer.getIntLE(offset + 17); + if (valueOffset < 0 || valueOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Value"); } - int posx = offset + 22 + valueOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Value"); - } - + int posx = offset + 21 + valueOffset; int valueLen = VarInt.peek(buffer, posx); if (valueLen < 0) { return ValidationResult.error("Invalid string length for Value"); @@ -229,7 +247,7 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { return ValidationResult.error("Value exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(valueLen); posx += valueLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Value"); @@ -245,7 +263,6 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { copy.token = this.token; copy.section = this.section; copy.slot = this.slot; - copy.group = this.group; copy.id = this.id; copy.value = this.value; return copy; @@ -261,7 +278,6 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { : this.token == other.token && this.section == other.section && this.slot == other.slot - && Objects.equals(this.group, other.group) && Objects.equals(this.id, other.id) && Objects.equals(this.value, other.value); } @@ -269,6 +285,6 @@ public class BuilderToolArgUpdate implements Packet, ToServerPacket { @Override public int hashCode() { - return Objects.hash(this.token, this.section, this.slot, this.group, this.id, this.value); + return Objects.hash(this.token, this.section, this.slot, this.id, this.value); } } diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBlockArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBlockArg.java index ccab9449..4a66f665 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBlockArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBlockArg.java @@ -34,26 +34,34 @@ public class BuilderToolBlockArg { @Nonnull public static BuilderToolBlockArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolBlockArg obj = new BuilderToolBlockArg(); - byte nullBits = buf.getByte(offset); - obj.allowPattern = buf.getByte(offset + 1) != 0; - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int defaultValueLen = VarInt.peek(buf, pos); - if (defaultValueLen < 0) { - throw ProtocolException.negativeLength("Default", defaultValueLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("BuilderToolBlockArg", 2, buf.readableBytes() - offset); + } else { + BuilderToolBlockArg obj = new BuilderToolBlockArg(); + byte nullBits = buf.getByte(offset); + obj.allowPattern = buf.getByte(offset + 1) != 0; + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int defaultValueLen = VarInt.peek(buf, pos); + if (defaultValueLen < 0) { + throw ProtocolException.invalidVarInt("Default"); + } + + int defaultValueVarLen = VarInt.size(defaultValueLen); + if (defaultValueLen > 4096000) { + throw ProtocolException.stringTooLong("Default", defaultValueLen, 4096000); + } + + if (pos + defaultValueVarLen + defaultValueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Default", pos + defaultValueVarLen + defaultValueLen, buf.readableBytes()); + } + + obj.defaultValue = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += defaultValueVarLen + defaultValueLen; } - if (defaultValueLen > 4096000) { - throw ProtocolException.stringTooLong("Default", defaultValueLen, 4096000); - } - - int defaultValueVarLen = VarInt.length(buf, pos); - obj.defaultValue = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += defaultValueVarLen + defaultValueLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,7 +69,7 @@ public class BuilderToolBlockArg { int pos = offset + 2; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -105,7 +113,7 @@ public class BuilderToolBlockArg { return ValidationResult.error("Default exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(defaultLen); pos += defaultLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Default"); diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBoolArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBoolArg.java index dca9338c..0b7ff0fe 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBoolArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBoolArg.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.buildertools; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class BuilderToolBoolArg { @Nonnull public static BuilderToolBoolArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolBoolArg obj = new BuilderToolBoolArg(); - obj.defaultValue = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolBoolArg", 1, buf.readableBytes() - offset); + } else { + BuilderToolBoolArg obj = new BuilderToolBoolArg(); + obj.defaultValue = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushAxisArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushAxisArg.java index 0f89164f..f5f7079b 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushAxisArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushAxisArg.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.buildertools; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -27,9 +28,13 @@ public class BuilderToolBrushAxisArg { @Nonnull public static BuilderToolBrushAxisArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolBrushAxisArg obj = new BuilderToolBrushAxisArg(); - obj.defaultValue = BrushAxis.fromValue(buf.getByte(offset + 0)); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolBrushAxisArg", 1, buf.readableBytes() - offset); + } else { + BuilderToolBrushAxisArg obj = new BuilderToolBrushAxisArg(); + obj.defaultValue = BrushAxis.fromValue(buf.getByte(offset + 0)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -45,7 +50,12 @@ public class BuilderToolBrushAxisArg { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 5 ? ValidationResult.error("Invalid BrushAxis value for Default") : ValidationResult.OK; + } } public BuilderToolBrushAxisArg clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushData.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushData.java deleted file mode 100644 index ac950c42..00000000 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushData.java +++ /dev/null @@ -1,976 +0,0 @@ -package com.hypixel.hytale.protocol.packets.buildertools; - -import com.hypixel.hytale.protocol.io.PacketIO; -import com.hypixel.hytale.protocol.io.ProtocolException; -import com.hypixel.hytale.protocol.io.ValidationResult; -import com.hypixel.hytale.protocol.io.VarInt; -import io.netty.buffer.ByteBuf; -import java.util.Arrays; -import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class BuilderToolBrushData { - public static final int NULLABLE_BIT_FIELD_SIZE = 3; - public static final int FIXED_BLOCK_SIZE = 48; - public static final int VARIABLE_FIELD_COUNT = 9; - public static final int VARIABLE_BLOCK_START = 84; - public static final int MAX_SIZE = 1677721600; - @Nullable - public BuilderToolIntArg width; - @Nullable - public BuilderToolIntArg height; - @Nullable - public BuilderToolIntArg thickness; - @Nullable - public BuilderToolBoolArg capped; - @Nullable - public BuilderToolBrushShapeArg shape; - @Nullable - public BuilderToolBrushOriginArg origin; - @Nullable - public BuilderToolBoolArg originRotation; - @Nullable - public BuilderToolBrushAxisArg rotationAxis; - @Nullable - public BuilderToolRotationArg rotationAngle; - @Nullable - public BuilderToolBrushAxisArg mirrorAxis; - @Nullable - public BuilderToolBlockArg material; - @Nullable - public BuilderToolBlockArg[] favoriteMaterials; - @Nullable - public BuilderToolMaskArg mask; - @Nullable - public BuilderToolMaskArg maskAbove; - @Nullable - public BuilderToolMaskArg maskNot; - @Nullable - public BuilderToolMaskArg maskBelow; - @Nullable - public BuilderToolMaskArg maskAdjacent; - @Nullable - public BuilderToolMaskArg maskNeighbor; - @Nullable - public BuilderToolStringArg[] maskCommands; - @Nullable - public BuilderToolBoolArg useMaskCommands; - @Nullable - public BuilderToolBoolArg invertMask; - - public BuilderToolBrushData() { - } - - public BuilderToolBrushData( - @Nullable BuilderToolIntArg width, - @Nullable BuilderToolIntArg height, - @Nullable BuilderToolIntArg thickness, - @Nullable BuilderToolBoolArg capped, - @Nullable BuilderToolBrushShapeArg shape, - @Nullable BuilderToolBrushOriginArg origin, - @Nullable BuilderToolBoolArg originRotation, - @Nullable BuilderToolBrushAxisArg rotationAxis, - @Nullable BuilderToolRotationArg rotationAngle, - @Nullable BuilderToolBrushAxisArg mirrorAxis, - @Nullable BuilderToolBlockArg material, - @Nullable BuilderToolBlockArg[] favoriteMaterials, - @Nullable BuilderToolMaskArg mask, - @Nullable BuilderToolMaskArg maskAbove, - @Nullable BuilderToolMaskArg maskNot, - @Nullable BuilderToolMaskArg maskBelow, - @Nullable BuilderToolMaskArg maskAdjacent, - @Nullable BuilderToolMaskArg maskNeighbor, - @Nullable BuilderToolStringArg[] maskCommands, - @Nullable BuilderToolBoolArg useMaskCommands, - @Nullable BuilderToolBoolArg invertMask - ) { - this.width = width; - this.height = height; - this.thickness = thickness; - this.capped = capped; - this.shape = shape; - this.origin = origin; - this.originRotation = originRotation; - this.rotationAxis = rotationAxis; - this.rotationAngle = rotationAngle; - this.mirrorAxis = mirrorAxis; - this.material = material; - this.favoriteMaterials = favoriteMaterials; - this.mask = mask; - this.maskAbove = maskAbove; - this.maskNot = maskNot; - this.maskBelow = maskBelow; - this.maskAdjacent = maskAdjacent; - this.maskNeighbor = maskNeighbor; - this.maskCommands = maskCommands; - this.useMaskCommands = useMaskCommands; - this.invertMask = invertMask; - } - - public BuilderToolBrushData(@Nonnull BuilderToolBrushData other) { - this.width = other.width; - this.height = other.height; - this.thickness = other.thickness; - this.capped = other.capped; - this.shape = other.shape; - this.origin = other.origin; - this.originRotation = other.originRotation; - this.rotationAxis = other.rotationAxis; - this.rotationAngle = other.rotationAngle; - this.mirrorAxis = other.mirrorAxis; - this.material = other.material; - this.favoriteMaterials = other.favoriteMaterials; - this.mask = other.mask; - this.maskAbove = other.maskAbove; - this.maskNot = other.maskNot; - this.maskBelow = other.maskBelow; - this.maskAdjacent = other.maskAdjacent; - this.maskNeighbor = other.maskNeighbor; - this.maskCommands = other.maskCommands; - this.useMaskCommands = other.useMaskCommands; - this.invertMask = other.invertMask; - } - - @Nonnull - public static BuilderToolBrushData deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolBrushData obj = new BuilderToolBrushData(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 3); - if ((nullBits[0] & 1) != 0) { - obj.width = BuilderToolIntArg.deserialize(buf, offset + 3); - } - - if ((nullBits[0] & 2) != 0) { - obj.height = BuilderToolIntArg.deserialize(buf, offset + 15); - } - - if ((nullBits[0] & 4) != 0) { - obj.thickness = BuilderToolIntArg.deserialize(buf, offset + 27); - } - - if ((nullBits[0] & 8) != 0) { - obj.capped = BuilderToolBoolArg.deserialize(buf, offset + 39); - } - - if ((nullBits[0] & 16) != 0) { - obj.shape = BuilderToolBrushShapeArg.deserialize(buf, offset + 40); - } - - if ((nullBits[0] & 32) != 0) { - obj.origin = BuilderToolBrushOriginArg.deserialize(buf, offset + 41); - } - - if ((nullBits[0] & 64) != 0) { - obj.originRotation = BuilderToolBoolArg.deserialize(buf, offset + 42); - } - - if ((nullBits[0] & 128) != 0) { - obj.rotationAxis = BuilderToolBrushAxisArg.deserialize(buf, offset + 43); - } - - if ((nullBits[1] & 1) != 0) { - obj.rotationAngle = BuilderToolRotationArg.deserialize(buf, offset + 44); - } - - if ((nullBits[1] & 2) != 0) { - obj.mirrorAxis = BuilderToolBrushAxisArg.deserialize(buf, offset + 45); - } - - if ((nullBits[1] & 4) != 0) { - obj.useMaskCommands = BuilderToolBoolArg.deserialize(buf, offset + 46); - } - - if ((nullBits[1] & 8) != 0) { - obj.invertMask = BuilderToolBoolArg.deserialize(buf, offset + 47); - } - - if ((nullBits[1] & 16) != 0) { - int varPos0 = offset + 84 + buf.getIntLE(offset + 48); - obj.material = BuilderToolBlockArg.deserialize(buf, varPos0); - } - - if ((nullBits[1] & 32) != 0) { - int varPos1 = offset + 84 + buf.getIntLE(offset + 52); - int favoriteMaterialsCount = VarInt.peek(buf, varPos1); - if (favoriteMaterialsCount < 0) { - throw ProtocolException.negativeLength("FavoriteMaterials", favoriteMaterialsCount); - } - - if (favoriteMaterialsCount > 4096000) { - throw ProtocolException.arrayTooLong("FavoriteMaterials", favoriteMaterialsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + favoriteMaterialsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FavoriteMaterials", varPos1 + varIntLen + favoriteMaterialsCount * 2, buf.readableBytes()); - } - - obj.favoriteMaterials = new BuilderToolBlockArg[favoriteMaterialsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < favoriteMaterialsCount; i++) { - obj.favoriteMaterials[i] = BuilderToolBlockArg.deserialize(buf, elemPos); - elemPos += BuilderToolBlockArg.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits[1] & 64) != 0) { - int varPos2 = offset + 84 + buf.getIntLE(offset + 56); - obj.mask = BuilderToolMaskArg.deserialize(buf, varPos2); - } - - if ((nullBits[1] & 128) != 0) { - int varPos3 = offset + 84 + buf.getIntLE(offset + 60); - obj.maskAbove = BuilderToolMaskArg.deserialize(buf, varPos3); - } - - if ((nullBits[2] & 1) != 0) { - int varPos4 = offset + 84 + buf.getIntLE(offset + 64); - obj.maskNot = BuilderToolMaskArg.deserialize(buf, varPos4); - } - - if ((nullBits[2] & 2) != 0) { - int varPos5 = offset + 84 + buf.getIntLE(offset + 68); - obj.maskBelow = BuilderToolMaskArg.deserialize(buf, varPos5); - } - - if ((nullBits[2] & 4) != 0) { - int varPos6 = offset + 84 + buf.getIntLE(offset + 72); - obj.maskAdjacent = BuilderToolMaskArg.deserialize(buf, varPos6); - } - - if ((nullBits[2] & 8) != 0) { - int varPos7 = offset + 84 + buf.getIntLE(offset + 76); - obj.maskNeighbor = BuilderToolMaskArg.deserialize(buf, varPos7); - } - - if ((nullBits[2] & 16) != 0) { - int varPos8 = offset + 84 + buf.getIntLE(offset + 80); - int maskCommandsCount = VarInt.peek(buf, varPos8); - if (maskCommandsCount < 0) { - throw ProtocolException.negativeLength("MaskCommands", maskCommandsCount); - } - - if (maskCommandsCount > 4096000) { - throw ProtocolException.arrayTooLong("MaskCommands", maskCommandsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos8); - if (varPos8 + varIntLen + maskCommandsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("MaskCommands", varPos8 + varIntLen + maskCommandsCount * 1, buf.readableBytes()); - } - - obj.maskCommands = new BuilderToolStringArg[maskCommandsCount]; - int elemPos = varPos8 + varIntLen; - - for (int i = 0; i < maskCommandsCount; i++) { - obj.maskCommands[i] = BuilderToolStringArg.deserialize(buf, elemPos); - elemPos += BuilderToolStringArg.computeBytesConsumed(buf, elemPos); - } - } - - return obj; - } - - public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - byte[] nullBits = PacketIO.readBytes(buf, offset, 3); - int maxEnd = 84; - if ((nullBits[1] & 16) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 48); - int pos0 = offset + 84 + fieldOffset0; - pos0 += BuilderToolBlockArg.computeBytesConsumed(buf, pos0); - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - } - - if ((nullBits[1] & 32) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 52); - int pos1 = offset + 84 + fieldOffset1; - int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); - - for (int i = 0; i < arrLen; i++) { - pos1 += BuilderToolBlockArg.computeBytesConsumed(buf, pos1); - } - - if (pos1 - offset > maxEnd) { - maxEnd = pos1 - offset; - } - } - - if ((nullBits[1] & 64) != 0) { - int fieldOffset2 = buf.getIntLE(offset + 56); - int pos2 = offset + 84 + fieldOffset2; - pos2 += BuilderToolMaskArg.computeBytesConsumed(buf, pos2); - if (pos2 - offset > maxEnd) { - maxEnd = pos2 - offset; - } - } - - if ((nullBits[1] & 128) != 0) { - int fieldOffset3 = buf.getIntLE(offset + 60); - int pos3 = offset + 84 + fieldOffset3; - pos3 += BuilderToolMaskArg.computeBytesConsumed(buf, pos3); - if (pos3 - offset > maxEnd) { - maxEnd = pos3 - offset; - } - } - - if ((nullBits[2] & 1) != 0) { - int fieldOffset4 = buf.getIntLE(offset + 64); - int pos4 = offset + 84 + fieldOffset4; - pos4 += BuilderToolMaskArg.computeBytesConsumed(buf, pos4); - if (pos4 - offset > maxEnd) { - maxEnd = pos4 - offset; - } - } - - if ((nullBits[2] & 2) != 0) { - int fieldOffset5 = buf.getIntLE(offset + 68); - int pos5 = offset + 84 + fieldOffset5; - pos5 += BuilderToolMaskArg.computeBytesConsumed(buf, pos5); - if (pos5 - offset > maxEnd) { - maxEnd = pos5 - offset; - } - } - - if ((nullBits[2] & 4) != 0) { - int fieldOffset6 = buf.getIntLE(offset + 72); - int pos6 = offset + 84 + fieldOffset6; - pos6 += BuilderToolMaskArg.computeBytesConsumed(buf, pos6); - if (pos6 - offset > maxEnd) { - maxEnd = pos6 - offset; - } - } - - if ((nullBits[2] & 8) != 0) { - int fieldOffset7 = buf.getIntLE(offset + 76); - int pos7 = offset + 84 + fieldOffset7; - pos7 += BuilderToolMaskArg.computeBytesConsumed(buf, pos7); - if (pos7 - offset > maxEnd) { - maxEnd = pos7 - offset; - } - } - - if ((nullBits[2] & 16) != 0) { - int fieldOffset8 = buf.getIntLE(offset + 80); - int pos8 = offset + 84 + fieldOffset8; - int arrLen = VarInt.peek(buf, pos8); - pos8 += VarInt.length(buf, pos8); - - for (int i = 0; i < arrLen; i++) { - pos8 += BuilderToolStringArg.computeBytesConsumed(buf, pos8); - } - - if (pos8 - offset > maxEnd) { - maxEnd = pos8 - offset; - } - } - - return maxEnd; - } - - public void serialize(@Nonnull ByteBuf buf) { - int startPos = buf.writerIndex(); - byte[] nullBits = new byte[3]; - if (this.width != null) { - nullBits[0] = (byte)(nullBits[0] | 1); - } - - if (this.height != null) { - nullBits[0] = (byte)(nullBits[0] | 2); - } - - if (this.thickness != null) { - nullBits[0] = (byte)(nullBits[0] | 4); - } - - if (this.capped != null) { - nullBits[0] = (byte)(nullBits[0] | 8); - } - - if (this.shape != null) { - nullBits[0] = (byte)(nullBits[0] | 16); - } - - if (this.origin != null) { - nullBits[0] = (byte)(nullBits[0] | 32); - } - - if (this.originRotation != null) { - nullBits[0] = (byte)(nullBits[0] | 64); - } - - if (this.rotationAxis != null) { - nullBits[0] = (byte)(nullBits[0] | 128); - } - - if (this.rotationAngle != null) { - nullBits[1] = (byte)(nullBits[1] | 1); - } - - if (this.mirrorAxis != null) { - nullBits[1] = (byte)(nullBits[1] | 2); - } - - if (this.useMaskCommands != null) { - nullBits[1] = (byte)(nullBits[1] | 4); - } - - if (this.invertMask != null) { - nullBits[1] = (byte)(nullBits[1] | 8); - } - - if (this.material != null) { - nullBits[1] = (byte)(nullBits[1] | 16); - } - - if (this.favoriteMaterials != null) { - nullBits[1] = (byte)(nullBits[1] | 32); - } - - if (this.mask != null) { - nullBits[1] = (byte)(nullBits[1] | 64); - } - - if (this.maskAbove != null) { - nullBits[1] = (byte)(nullBits[1] | 128); - } - - if (this.maskNot != null) { - nullBits[2] = (byte)(nullBits[2] | 1); - } - - if (this.maskBelow != null) { - nullBits[2] = (byte)(nullBits[2] | 2); - } - - if (this.maskAdjacent != null) { - nullBits[2] = (byte)(nullBits[2] | 4); - } - - if (this.maskNeighbor != null) { - nullBits[2] = (byte)(nullBits[2] | 8); - } - - if (this.maskCommands != null) { - nullBits[2] = (byte)(nullBits[2] | 16); - } - - buf.writeBytes(nullBits); - if (this.width != null) { - this.width.serialize(buf); - } else { - buf.writeZero(12); - } - - if (this.height != null) { - this.height.serialize(buf); - } else { - buf.writeZero(12); - } - - if (this.thickness != null) { - this.thickness.serialize(buf); - } else { - buf.writeZero(12); - } - - if (this.capped != null) { - this.capped.serialize(buf); - } else { - buf.writeZero(1); - } - - if (this.shape != null) { - this.shape.serialize(buf); - } else { - buf.writeZero(1); - } - - if (this.origin != null) { - this.origin.serialize(buf); - } else { - buf.writeZero(1); - } - - if (this.originRotation != null) { - this.originRotation.serialize(buf); - } else { - buf.writeZero(1); - } - - if (this.rotationAxis != null) { - this.rotationAxis.serialize(buf); - } else { - buf.writeZero(1); - } - - if (this.rotationAngle != null) { - this.rotationAngle.serialize(buf); - } else { - buf.writeZero(1); - } - - if (this.mirrorAxis != null) { - this.mirrorAxis.serialize(buf); - } else { - buf.writeZero(1); - } - - if (this.useMaskCommands != null) { - this.useMaskCommands.serialize(buf); - } else { - buf.writeZero(1); - } - - if (this.invertMask != null) { - this.invertMask.serialize(buf); - } else { - buf.writeZero(1); - } - - int materialOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int favoriteMaterialsOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int maskOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int maskAboveOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int maskNotOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int maskBelowOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int maskAdjacentOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int maskNeighborOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int maskCommandsOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); - int varBlockStart = buf.writerIndex(); - if (this.material != null) { - buf.setIntLE(materialOffsetSlot, buf.writerIndex() - varBlockStart); - this.material.serialize(buf); - } else { - buf.setIntLE(materialOffsetSlot, -1); - } - - if (this.favoriteMaterials != null) { - buf.setIntLE(favoriteMaterialsOffsetSlot, buf.writerIndex() - varBlockStart); - if (this.favoriteMaterials.length > 4096000) { - throw ProtocolException.arrayTooLong("FavoriteMaterials", this.favoriteMaterials.length, 4096000); - } - - VarInt.write(buf, this.favoriteMaterials.length); - - for (BuilderToolBlockArg item : this.favoriteMaterials) { - item.serialize(buf); - } - } else { - buf.setIntLE(favoriteMaterialsOffsetSlot, -1); - } - - if (this.mask != null) { - buf.setIntLE(maskOffsetSlot, buf.writerIndex() - varBlockStart); - this.mask.serialize(buf); - } else { - buf.setIntLE(maskOffsetSlot, -1); - } - - if (this.maskAbove != null) { - buf.setIntLE(maskAboveOffsetSlot, buf.writerIndex() - varBlockStart); - this.maskAbove.serialize(buf); - } else { - buf.setIntLE(maskAboveOffsetSlot, -1); - } - - if (this.maskNot != null) { - buf.setIntLE(maskNotOffsetSlot, buf.writerIndex() - varBlockStart); - this.maskNot.serialize(buf); - } else { - buf.setIntLE(maskNotOffsetSlot, -1); - } - - if (this.maskBelow != null) { - buf.setIntLE(maskBelowOffsetSlot, buf.writerIndex() - varBlockStart); - this.maskBelow.serialize(buf); - } else { - buf.setIntLE(maskBelowOffsetSlot, -1); - } - - if (this.maskAdjacent != null) { - buf.setIntLE(maskAdjacentOffsetSlot, buf.writerIndex() - varBlockStart); - this.maskAdjacent.serialize(buf); - } else { - buf.setIntLE(maskAdjacentOffsetSlot, -1); - } - - if (this.maskNeighbor != null) { - buf.setIntLE(maskNeighborOffsetSlot, buf.writerIndex() - varBlockStart); - this.maskNeighbor.serialize(buf); - } else { - buf.setIntLE(maskNeighborOffsetSlot, -1); - } - - if (this.maskCommands != null) { - buf.setIntLE(maskCommandsOffsetSlot, buf.writerIndex() - varBlockStart); - if (this.maskCommands.length > 4096000) { - throw ProtocolException.arrayTooLong("MaskCommands", this.maskCommands.length, 4096000); - } - - VarInt.write(buf, this.maskCommands.length); - - for (BuilderToolStringArg item : this.maskCommands) { - item.serialize(buf); - } - } else { - buf.setIntLE(maskCommandsOffsetSlot, -1); - } - } - - public int computeSize() { - int size = 84; - if (this.material != null) { - size += this.material.computeSize(); - } - - if (this.favoriteMaterials != null) { - int favoriteMaterialsSize = 0; - - for (BuilderToolBlockArg elem : this.favoriteMaterials) { - favoriteMaterialsSize += elem.computeSize(); - } - - size += VarInt.size(this.favoriteMaterials.length) + favoriteMaterialsSize; - } - - if (this.mask != null) { - size += this.mask.computeSize(); - } - - if (this.maskAbove != null) { - size += this.maskAbove.computeSize(); - } - - if (this.maskNot != null) { - size += this.maskNot.computeSize(); - } - - if (this.maskBelow != null) { - size += this.maskBelow.computeSize(); - } - - if (this.maskAdjacent != null) { - size += this.maskAdjacent.computeSize(); - } - - if (this.maskNeighbor != null) { - size += this.maskNeighbor.computeSize(); - } - - if (this.maskCommands != null) { - int maskCommandsSize = 0; - - for (BuilderToolStringArg elem : this.maskCommands) { - maskCommandsSize += elem.computeSize(); - } - - size += VarInt.size(this.maskCommands.length) + maskCommandsSize; - } - - return size; - } - - public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 84) { - return ValidationResult.error("Buffer too small: expected at least 84 bytes"); - } else { - byte[] nullBits = PacketIO.readBytes(buffer, offset, 3); - if ((nullBits[1] & 16) != 0) { - int materialOffset = buffer.getIntLE(offset + 48); - if (materialOffset < 0) { - return ValidationResult.error("Invalid offset for Material"); - } - - int pos = offset + 84 + materialOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Material"); - } - - ValidationResult materialResult = BuilderToolBlockArg.validateStructure(buffer, pos); - if (!materialResult.isValid()) { - return ValidationResult.error("Invalid Material: " + materialResult.error()); - } - - pos += BuilderToolBlockArg.computeBytesConsumed(buffer, pos); - } - - if ((nullBits[1] & 32) != 0) { - int favoriteMaterialsOffset = buffer.getIntLE(offset + 52); - if (favoriteMaterialsOffset < 0) { - return ValidationResult.error("Invalid offset for FavoriteMaterials"); - } - - int posx = offset + 84 + favoriteMaterialsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FavoriteMaterials"); - } - - int favoriteMaterialsCount = VarInt.peek(buffer, posx); - if (favoriteMaterialsCount < 0) { - return ValidationResult.error("Invalid array count for FavoriteMaterials"); - } - - if (favoriteMaterialsCount > 4096000) { - return ValidationResult.error("FavoriteMaterials exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < favoriteMaterialsCount; i++) { - ValidationResult structResult = BuilderToolBlockArg.validateStructure(buffer, posx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid BuilderToolBlockArg in FavoriteMaterials[" + i + "]: " + structResult.error()); - } - - posx += BuilderToolBlockArg.computeBytesConsumed(buffer, posx); - } - } - - if ((nullBits[1] & 64) != 0) { - int maskOffset = buffer.getIntLE(offset + 56); - if (maskOffset < 0) { - return ValidationResult.error("Invalid offset for Mask"); - } - - int posxx = offset + 84 + maskOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Mask"); - } - - ValidationResult maskResult = BuilderToolMaskArg.validateStructure(buffer, posxx); - if (!maskResult.isValid()) { - return ValidationResult.error("Invalid Mask: " + maskResult.error()); - } - - posxx += BuilderToolMaskArg.computeBytesConsumed(buffer, posxx); - } - - if ((nullBits[1] & 128) != 0) { - int maskAboveOffset = buffer.getIntLE(offset + 60); - if (maskAboveOffset < 0) { - return ValidationResult.error("Invalid offset for MaskAbove"); - } - - int posxxx = offset + 84 + maskAboveOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaskAbove"); - } - - ValidationResult maskAboveResult = BuilderToolMaskArg.validateStructure(buffer, posxxx); - if (!maskAboveResult.isValid()) { - return ValidationResult.error("Invalid MaskAbove: " + maskAboveResult.error()); - } - - posxxx += BuilderToolMaskArg.computeBytesConsumed(buffer, posxxx); - } - - if ((nullBits[2] & 1) != 0) { - int maskNotOffset = buffer.getIntLE(offset + 64); - if (maskNotOffset < 0) { - return ValidationResult.error("Invalid offset for MaskNot"); - } - - int posxxxx = offset + 84 + maskNotOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaskNot"); - } - - ValidationResult maskNotResult = BuilderToolMaskArg.validateStructure(buffer, posxxxx); - if (!maskNotResult.isValid()) { - return ValidationResult.error("Invalid MaskNot: " + maskNotResult.error()); - } - - posxxxx += BuilderToolMaskArg.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits[2] & 2) != 0) { - int maskBelowOffset = buffer.getIntLE(offset + 68); - if (maskBelowOffset < 0) { - return ValidationResult.error("Invalid offset for MaskBelow"); - } - - int posxxxxx = offset + 84 + maskBelowOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaskBelow"); - } - - ValidationResult maskBelowResult = BuilderToolMaskArg.validateStructure(buffer, posxxxxx); - if (!maskBelowResult.isValid()) { - return ValidationResult.error("Invalid MaskBelow: " + maskBelowResult.error()); - } - - posxxxxx += BuilderToolMaskArg.computeBytesConsumed(buffer, posxxxxx); - } - - if ((nullBits[2] & 4) != 0) { - int maskAdjacentOffset = buffer.getIntLE(offset + 72); - if (maskAdjacentOffset < 0) { - return ValidationResult.error("Invalid offset for MaskAdjacent"); - } - - int posxxxxxx = offset + 84 + maskAdjacentOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaskAdjacent"); - } - - ValidationResult maskAdjacentResult = BuilderToolMaskArg.validateStructure(buffer, posxxxxxx); - if (!maskAdjacentResult.isValid()) { - return ValidationResult.error("Invalid MaskAdjacent: " + maskAdjacentResult.error()); - } - - posxxxxxx += BuilderToolMaskArg.computeBytesConsumed(buffer, posxxxxxx); - } - - if ((nullBits[2] & 8) != 0) { - int maskNeighborOffset = buffer.getIntLE(offset + 76); - if (maskNeighborOffset < 0) { - return ValidationResult.error("Invalid offset for MaskNeighbor"); - } - - int posxxxxxxx = offset + 84 + maskNeighborOffset; - if (posxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaskNeighbor"); - } - - ValidationResult maskNeighborResult = BuilderToolMaskArg.validateStructure(buffer, posxxxxxxx); - if (!maskNeighborResult.isValid()) { - return ValidationResult.error("Invalid MaskNeighbor: " + maskNeighborResult.error()); - } - - posxxxxxxx += BuilderToolMaskArg.computeBytesConsumed(buffer, posxxxxxxx); - } - - if ((nullBits[2] & 16) != 0) { - int maskCommandsOffset = buffer.getIntLE(offset + 80); - if (maskCommandsOffset < 0) { - return ValidationResult.error("Invalid offset for MaskCommands"); - } - - int posxxxxxxxx = offset + 84 + maskCommandsOffset; - if (posxxxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MaskCommands"); - } - - int maskCommandsCount = VarInt.peek(buffer, posxxxxxxxx); - if (maskCommandsCount < 0) { - return ValidationResult.error("Invalid array count for MaskCommands"); - } - - if (maskCommandsCount > 4096000) { - return ValidationResult.error("MaskCommands exceeds max length 4096000"); - } - - posxxxxxxxx += VarInt.length(buffer, posxxxxxxxx); - - for (int i = 0; i < maskCommandsCount; i++) { - ValidationResult structResult = BuilderToolStringArg.validateStructure(buffer, posxxxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid BuilderToolStringArg in MaskCommands[" + i + "]: " + structResult.error()); - } - - posxxxxxxxx += BuilderToolStringArg.computeBytesConsumed(buffer, posxxxxxxxx); - } - } - - return ValidationResult.OK; - } - } - - public BuilderToolBrushData clone() { - BuilderToolBrushData copy = new BuilderToolBrushData(); - copy.width = this.width != null ? this.width.clone() : null; - copy.height = this.height != null ? this.height.clone() : null; - copy.thickness = this.thickness != null ? this.thickness.clone() : null; - copy.capped = this.capped != null ? this.capped.clone() : null; - copy.shape = this.shape != null ? this.shape.clone() : null; - copy.origin = this.origin != null ? this.origin.clone() : null; - copy.originRotation = this.originRotation != null ? this.originRotation.clone() : null; - copy.rotationAxis = this.rotationAxis != null ? this.rotationAxis.clone() : null; - copy.rotationAngle = this.rotationAngle != null ? this.rotationAngle.clone() : null; - copy.mirrorAxis = this.mirrorAxis != null ? this.mirrorAxis.clone() : null; - copy.material = this.material != null ? this.material.clone() : null; - copy.favoriteMaterials = this.favoriteMaterials != null - ? Arrays.stream(this.favoriteMaterials).map(e -> e.clone()).toArray(BuilderToolBlockArg[]::new) - : null; - copy.mask = this.mask != null ? this.mask.clone() : null; - copy.maskAbove = this.maskAbove != null ? this.maskAbove.clone() : null; - copy.maskNot = this.maskNot != null ? this.maskNot.clone() : null; - copy.maskBelow = this.maskBelow != null ? this.maskBelow.clone() : null; - copy.maskAdjacent = this.maskAdjacent != null ? this.maskAdjacent.clone() : null; - copy.maskNeighbor = this.maskNeighbor != null ? this.maskNeighbor.clone() : null; - copy.maskCommands = this.maskCommands != null ? Arrays.stream(this.maskCommands).map(e -> e.clone()).toArray(BuilderToolStringArg[]::new) : null; - copy.useMaskCommands = this.useMaskCommands != null ? this.useMaskCommands.clone() : null; - copy.invertMask = this.invertMask != null ? this.invertMask.clone() : null; - return copy; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else { - return !(obj instanceof BuilderToolBrushData other) - ? false - : Objects.equals(this.width, other.width) - && Objects.equals(this.height, other.height) - && Objects.equals(this.thickness, other.thickness) - && Objects.equals(this.capped, other.capped) - && Objects.equals(this.shape, other.shape) - && Objects.equals(this.origin, other.origin) - && Objects.equals(this.originRotation, other.originRotation) - && Objects.equals(this.rotationAxis, other.rotationAxis) - && Objects.equals(this.rotationAngle, other.rotationAngle) - && Objects.equals(this.mirrorAxis, other.mirrorAxis) - && Objects.equals(this.material, other.material) - && Arrays.equals((Object[])this.favoriteMaterials, (Object[])other.favoriteMaterials) - && Objects.equals(this.mask, other.mask) - && Objects.equals(this.maskAbove, other.maskAbove) - && Objects.equals(this.maskNot, other.maskNot) - && Objects.equals(this.maskBelow, other.maskBelow) - && Objects.equals(this.maskAdjacent, other.maskAdjacent) - && Objects.equals(this.maskNeighbor, other.maskNeighbor) - && Arrays.equals((Object[])this.maskCommands, (Object[])other.maskCommands) - && Objects.equals(this.useMaskCommands, other.useMaskCommands) - && Objects.equals(this.invertMask, other.invertMask); - } - } - - @Override - public int hashCode() { - int result = 1; - result = 31 * result + Objects.hashCode(this.width); - result = 31 * result + Objects.hashCode(this.height); - result = 31 * result + Objects.hashCode(this.thickness); - result = 31 * result + Objects.hashCode(this.capped); - result = 31 * result + Objects.hashCode(this.shape); - result = 31 * result + Objects.hashCode(this.origin); - result = 31 * result + Objects.hashCode(this.originRotation); - result = 31 * result + Objects.hashCode(this.rotationAxis); - result = 31 * result + Objects.hashCode(this.rotationAngle); - result = 31 * result + Objects.hashCode(this.mirrorAxis); - result = 31 * result + Objects.hashCode(this.material); - result = 31 * result + Arrays.hashCode((Object[])this.favoriteMaterials); - result = 31 * result + Objects.hashCode(this.mask); - result = 31 * result + Objects.hashCode(this.maskAbove); - result = 31 * result + Objects.hashCode(this.maskNot); - result = 31 * result + Objects.hashCode(this.maskBelow); - result = 31 * result + Objects.hashCode(this.maskAdjacent); - result = 31 * result + Objects.hashCode(this.maskNeighbor); - result = 31 * result + Arrays.hashCode((Object[])this.maskCommands); - result = 31 * result + Objects.hashCode(this.useMaskCommands); - return 31 * result + Objects.hashCode(this.invertMask); - } -} diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushOriginArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushOriginArg.java index 3c1459e0..2d9a8bd7 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushOriginArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushOriginArg.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.buildertools; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -27,9 +28,13 @@ public class BuilderToolBrushOriginArg { @Nonnull public static BuilderToolBrushOriginArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolBrushOriginArg obj = new BuilderToolBrushOriginArg(); - obj.defaultValue = BrushOrigin.fromValue(buf.getByte(offset + 0)); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolBrushOriginArg", 1, buf.readableBytes() - offset); + } else { + BuilderToolBrushOriginArg obj = new BuilderToolBrushOriginArg(); + obj.defaultValue = BrushOrigin.fromValue(buf.getByte(offset + 0)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -45,7 +50,12 @@ public class BuilderToolBrushOriginArg { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 5 ? ValidationResult.error("Invalid BrushOrigin value for Default") : ValidationResult.OK; + } } public BuilderToolBrushOriginArg clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushShapeArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushShapeArg.java index f3e20a7a..d48bd8b2 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushShapeArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolBrushShapeArg.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.buildertools; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -27,9 +28,13 @@ public class BuilderToolBrushShapeArg { @Nonnull public static BuilderToolBrushShapeArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolBrushShapeArg obj = new BuilderToolBrushShapeArg(); - obj.defaultValue = BrushShape.fromValue(buf.getByte(offset + 0)); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolBrushShapeArg", 1, buf.readableBytes() - offset); + } else { + BuilderToolBrushShapeArg obj = new BuilderToolBrushShapeArg(); + obj.defaultValue = BrushShape.fromValue(buf.getByte(offset + 0)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -45,7 +50,12 @@ public class BuilderToolBrushShapeArg { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 11 ? ValidationResult.error("Invalid BrushShape value for Default") : ValidationResult.OK; + } } public BuilderToolBrushShapeArg clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolEntityAction.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolEntityAction.java index 85cfc6f3..0a4d4f4b 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolEntityAction.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolEntityAction.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -45,10 +46,14 @@ public class BuilderToolEntityAction implements Packet, ToServerPacket { @Nonnull public static BuilderToolEntityAction deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolEntityAction obj = new BuilderToolEntityAction(); - obj.entityId = buf.getIntLE(offset + 0); - obj.action = EntityToolAction.fromValue(buf.getByte(offset + 4)); - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("BuilderToolEntityAction", 5, buf.readableBytes() - offset); + } else { + BuilderToolEntityAction obj = new BuilderToolEntityAction(); + obj.entityId = buf.getIntLE(offset + 0); + obj.action = EntityToolAction.fromValue(buf.getByte(offset + 4)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -67,7 +72,12 @@ public class BuilderToolEntityAction implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 5 ? ValidationResult.error("Buffer too small: expected at least 5 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 5) { + return ValidationResult.error("Buffer too small: expected at least 5 bytes"); + } else { + int v = buffer.getByte(offset + 4) & 255; + return v >= 3 ? ValidationResult.error("Invalid EntityToolAction value for Action") : ValidationResult.OK; + } } public BuilderToolEntityAction clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolExtrudeAction.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolExtrudeAction.java index e144185f..f19e1447 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolExtrudeAction.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolExtrudeAction.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -56,14 +57,18 @@ public class BuilderToolExtrudeAction implements Packet, ToServerPacket { @Nonnull public static BuilderToolExtrudeAction deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolExtrudeAction obj = new BuilderToolExtrudeAction(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - obj.xNormal = buf.getIntLE(offset + 12); - obj.yNormal = buf.getIntLE(offset + 16); - obj.zNormal = buf.getIntLE(offset + 20); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("BuilderToolExtrudeAction", 24, buf.readableBytes() - offset); + } else { + BuilderToolExtrudeAction obj = new BuilderToolExtrudeAction(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + obj.xNormal = buf.getIntLE(offset + 12); + obj.yNormal = buf.getIntLE(offset + 16); + obj.zNormal = buf.getIntLE(offset + 20); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolFloatArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolFloatArg.java index 8b5687ce..0b18eabc 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolFloatArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolFloatArg.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.buildertools; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class BuilderToolFloatArg { @Nonnull public static BuilderToolFloatArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolFloatArg obj = new BuilderToolFloatArg(); - obj.defaultValue = buf.getFloatLE(offset + 0); - obj.min = buf.getFloatLE(offset + 4); - obj.max = buf.getFloatLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("BuilderToolFloatArg", 12, buf.readableBytes() - offset); + } else { + BuilderToolFloatArg obj = new BuilderToolFloatArg(); + obj.defaultValue = buf.getFloatLE(offset + 0); + obj.min = buf.getFloatLE(offset + 4); + obj.max = buf.getFloatLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolGeneralAction.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolGeneralAction.java index b8391b95..b046fd46 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolGeneralAction.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolGeneralAction.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -42,9 +43,13 @@ public class BuilderToolGeneralAction implements Packet, ToServerPacket { @Nonnull public static BuilderToolGeneralAction deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolGeneralAction obj = new BuilderToolGeneralAction(); - obj.action = BuilderToolAction.fromValue(buf.getByte(offset + 0)); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolGeneralAction", 1, buf.readableBytes() - offset); + } else { + BuilderToolGeneralAction obj = new BuilderToolGeneralAction(); + obj.action = BuilderToolAction.fromValue(buf.getByte(offset + 0)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -62,7 +67,12 @@ public class BuilderToolGeneralAction implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 7 ? ValidationResult.error("Invalid BuilderToolAction value for Action") : ValidationResult.OK; + } } public BuilderToolGeneralAction clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolIntArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolIntArg.java index 65495ab1..50893d1e 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolIntArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolIntArg.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.buildertools; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class BuilderToolIntArg { @Nonnull public static BuilderToolIntArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolIntArg obj = new BuilderToolIntArg(); - obj.defaultValue = buf.getIntLE(offset + 0); - obj.min = buf.getIntLE(offset + 4); - obj.max = buf.getIntLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("BuilderToolIntArg", 12, buf.readableBytes() - offset); + } else { + BuilderToolIntArg obj = new BuilderToolIntArg(); + obj.defaultValue = buf.getIntLE(offset + 0); + obj.min = buf.getIntLE(offset + 4); + obj.max = buf.getIntLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolLaserPointer.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolLaserPointer.java index d9b16025..3c3ee078 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolLaserPointer.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolLaserPointer.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -65,17 +66,21 @@ public class BuilderToolLaserPointer implements Packet, ToClientPacket { @Nonnull public static BuilderToolLaserPointer deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolLaserPointer obj = new BuilderToolLaserPointer(); - obj.playerNetworkId = buf.getIntLE(offset + 0); - obj.startX = buf.getFloatLE(offset + 4); - obj.startY = buf.getFloatLE(offset + 8); - obj.startZ = buf.getFloatLE(offset + 12); - obj.endX = buf.getFloatLE(offset + 16); - obj.endY = buf.getFloatLE(offset + 20); - obj.endZ = buf.getFloatLE(offset + 24); - obj.color = buf.getIntLE(offset + 28); - obj.durationMs = buf.getIntLE(offset + 32); - return obj; + if (buf.readableBytes() - offset < 36) { + throw ProtocolException.bufferTooSmall("BuilderToolLaserPointer", 36, buf.readableBytes() - offset); + } else { + BuilderToolLaserPointer obj = new BuilderToolLaserPointer(); + obj.playerNetworkId = buf.getIntLE(offset + 0); + obj.startX = buf.getFloatLE(offset + 4); + obj.startY = buf.getFloatLE(offset + 8); + obj.startZ = buf.getFloatLE(offset + 12); + obj.endX = buf.getFloatLE(offset + 16); + obj.endY = buf.getFloatLE(offset + 20); + obj.endZ = buf.getFloatLE(offset + 24); + obj.color = buf.getIntLE(offset + 28); + obj.durationMs = buf.getIntLE(offset + 32); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolLineAction.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolLineAction.java index 4469fb57..7d56bb84 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolLineAction.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolLineAction.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -56,14 +57,18 @@ public class BuilderToolLineAction implements Packet, ToServerPacket { @Nonnull public static BuilderToolLineAction deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolLineAction obj = new BuilderToolLineAction(); - obj.xStart = buf.getIntLE(offset + 0); - obj.yStart = buf.getIntLE(offset + 4); - obj.zStart = buf.getIntLE(offset + 8); - obj.xEnd = buf.getIntLE(offset + 12); - obj.yEnd = buf.getIntLE(offset + 16); - obj.zEnd = buf.getIntLE(offset + 20); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("BuilderToolLineAction", 24, buf.readableBytes() - offset); + } else { + BuilderToolLineAction obj = new BuilderToolLineAction(); + obj.xStart = buf.getIntLE(offset + 0); + obj.yStart = buf.getIntLE(offset + 4); + obj.zStart = buf.getIntLE(offset + 8); + obj.xEnd = buf.getIntLE(offset + 12); + obj.yEnd = buf.getIntLE(offset + 16); + obj.zEnd = buf.getIntLE(offset + 20); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolMaskArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolMaskArg.java index 7453bf4c..092deaf6 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolMaskArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolMaskArg.java @@ -31,25 +31,33 @@ public class BuilderToolMaskArg { @Nonnull public static BuilderToolMaskArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolMaskArg obj = new BuilderToolMaskArg(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int defaultValueLen = VarInt.peek(buf, pos); - if (defaultValueLen < 0) { - throw ProtocolException.negativeLength("Default", defaultValueLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolMaskArg", 1, buf.readableBytes() - offset); + } else { + BuilderToolMaskArg obj = new BuilderToolMaskArg(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int defaultValueLen = VarInt.peek(buf, pos); + if (defaultValueLen < 0) { + throw ProtocolException.invalidVarInt("Default"); + } + + int defaultValueVarLen = VarInt.size(defaultValueLen); + if (defaultValueLen > 4096000) { + throw ProtocolException.stringTooLong("Default", defaultValueLen, 4096000); + } + + if (pos + defaultValueVarLen + defaultValueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Default", pos + defaultValueVarLen + defaultValueLen, buf.readableBytes()); + } + + obj.defaultValue = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += defaultValueVarLen + defaultValueLen; } - if (defaultValueLen > 4096000) { - throw ProtocolException.stringTooLong("Default", defaultValueLen, 4096000); - } - - int defaultValueVarLen = VarInt.length(buf, pos); - obj.defaultValue = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += defaultValueVarLen + defaultValueLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -57,7 +65,7 @@ public class BuilderToolMaskArg { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -100,7 +108,7 @@ public class BuilderToolMaskArg { return ValidationResult.error("Default exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(defaultLen); pos += defaultLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Default"); diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolOnUseInteraction.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolOnUseInteraction.java index 9262fdfa..195bbf49 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolOnUseInteraction.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolOnUseInteraction.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -13,10 +14,10 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { public static final int PACKET_ID = 413; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 0; - public static final int FIXED_BLOCK_SIZE = 57; + public static final int FIXED_BLOCK_SIZE = 61; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 57; - public static final int MAX_SIZE = 57; + public static final int VARIABLE_BLOCK_START = 61; + public static final int MAX_SIZE = 61; @Nonnull public InteractionType type = InteractionType.Primary; public int x; @@ -36,6 +37,7 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { public float raycastDirectionX; public float raycastDirectionY; public float raycastDirectionZ; + public int undoGroupSize; @Override public int getId() { @@ -68,7 +70,8 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { float raycastOriginZ, float raycastDirectionX, float raycastDirectionY, - float raycastDirectionZ + float raycastDirectionZ, + int undoGroupSize ) { this.type = type; this.x = x; @@ -88,6 +91,7 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { this.raycastDirectionX = raycastDirectionX; this.raycastDirectionY = raycastDirectionY; this.raycastDirectionZ = raycastDirectionZ; + this.undoGroupSize = undoGroupSize; } public BuilderToolOnUseInteraction(@Nonnull BuilderToolOnUseInteraction other) { @@ -109,34 +113,40 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { this.raycastDirectionX = other.raycastDirectionX; this.raycastDirectionY = other.raycastDirectionY; this.raycastDirectionZ = other.raycastDirectionZ; + this.undoGroupSize = other.undoGroupSize; } @Nonnull public static BuilderToolOnUseInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolOnUseInteraction obj = new BuilderToolOnUseInteraction(); - obj.type = InteractionType.fromValue(buf.getByte(offset + 0)); - obj.x = buf.getIntLE(offset + 1); - obj.y = buf.getIntLE(offset + 5); - obj.z = buf.getIntLE(offset + 9); - obj.offsetForPaintModeX = buf.getIntLE(offset + 13); - obj.offsetForPaintModeY = buf.getIntLE(offset + 17); - obj.offsetForPaintModeZ = buf.getIntLE(offset + 21); - obj.isAltPlaySculptBrushModDown = buf.getByte(offset + 25) != 0; - obj.isHoldDownInteraction = buf.getByte(offset + 26) != 0; - obj.isDoServerRaytraceForPosition = buf.getByte(offset + 27) != 0; - obj.isShowEditNotifications = buf.getByte(offset + 28) != 0; - obj.maxLengthToolIgnoreHistory = buf.getIntLE(offset + 29); - obj.raycastOriginX = buf.getFloatLE(offset + 33); - obj.raycastOriginY = buf.getFloatLE(offset + 37); - obj.raycastOriginZ = buf.getFloatLE(offset + 41); - obj.raycastDirectionX = buf.getFloatLE(offset + 45); - obj.raycastDirectionY = buf.getFloatLE(offset + 49); - obj.raycastDirectionZ = buf.getFloatLE(offset + 53); - return obj; + if (buf.readableBytes() - offset < 61) { + throw ProtocolException.bufferTooSmall("BuilderToolOnUseInteraction", 61, buf.readableBytes() - offset); + } else { + BuilderToolOnUseInteraction obj = new BuilderToolOnUseInteraction(); + obj.type = InteractionType.fromValue(buf.getByte(offset + 0)); + obj.x = buf.getIntLE(offset + 1); + obj.y = buf.getIntLE(offset + 5); + obj.z = buf.getIntLE(offset + 9); + obj.offsetForPaintModeX = buf.getIntLE(offset + 13); + obj.offsetForPaintModeY = buf.getIntLE(offset + 17); + obj.offsetForPaintModeZ = buf.getIntLE(offset + 21); + obj.isAltPlaySculptBrushModDown = buf.getByte(offset + 25) != 0; + obj.isHoldDownInteraction = buf.getByte(offset + 26) != 0; + obj.isDoServerRaytraceForPosition = buf.getByte(offset + 27) != 0; + obj.isShowEditNotifications = buf.getByte(offset + 28) != 0; + obj.maxLengthToolIgnoreHistory = buf.getIntLE(offset + 29); + obj.raycastOriginX = buf.getFloatLE(offset + 33); + obj.raycastOriginY = buf.getFloatLE(offset + 37); + obj.raycastOriginZ = buf.getFloatLE(offset + 41); + obj.raycastDirectionX = buf.getFloatLE(offset + 45); + obj.raycastDirectionY = buf.getFloatLE(offset + 49); + obj.raycastDirectionZ = buf.getFloatLE(offset + 53); + obj.undoGroupSize = buf.getIntLE(offset + 57); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 57; + return 61; } @Override @@ -159,15 +169,21 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { buf.writeFloatLE(this.raycastDirectionX); buf.writeFloatLE(this.raycastDirectionY); buf.writeFloatLE(this.raycastDirectionZ); + buf.writeIntLE(this.undoGroupSize); } @Override public int computeSize() { - return 57; + return 61; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 57 ? ValidationResult.error("Buffer too small: expected at least 57 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 61) { + return ValidationResult.error("Buffer too small: expected at least 61 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 25 ? ValidationResult.error("Invalid InteractionType value for Type") : ValidationResult.OK; + } } public BuilderToolOnUseInteraction clone() { @@ -190,6 +206,7 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { copy.raycastDirectionX = this.raycastDirectionX; copy.raycastDirectionY = this.raycastDirectionY; copy.raycastDirectionZ = this.raycastDirectionZ; + copy.undoGroupSize = this.undoGroupSize; return copy; } @@ -217,7 +234,8 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { && this.raycastOriginZ == other.raycastOriginZ && this.raycastDirectionX == other.raycastDirectionX && this.raycastDirectionY == other.raycastDirectionY - && this.raycastDirectionZ == other.raycastDirectionZ; + && this.raycastDirectionZ == other.raycastDirectionZ + && this.undoGroupSize == other.undoGroupSize; } } @@ -241,7 +259,8 @@ public class BuilderToolOnUseInteraction implements Packet, ToServerPacket { this.raycastOriginZ, this.raycastDirectionX, this.raycastDirectionY, - this.raycastDirectionZ + this.raycastDirectionZ, + this.undoGroupSize ); } } diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolOptionArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolOptionArg.java index dcd92b83..0a3ca180 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolOptionArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolOptionArg.java @@ -36,58 +36,81 @@ public class BuilderToolOptionArg { @Nonnull public static BuilderToolOptionArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolOptionArg obj = new BuilderToolOptionArg(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int defaultValueLen = VarInt.peek(buf, varPos0); - if (defaultValueLen < 0) { - throw ProtocolException.negativeLength("Default", defaultValueLen); - } - - if (defaultValueLen > 4096000) { - throw ProtocolException.stringTooLong("Default", defaultValueLen, 4096000); - } - - obj.defaultValue = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int optionsCount = VarInt.peek(buf, varPos1); - if (optionsCount < 0) { - throw ProtocolException.negativeLength("Options", optionsCount); - } - - if (optionsCount > 4096000) { - throw ProtocolException.arrayTooLong("Options", optionsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + optionsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Options", varPos1 + varIntLen + optionsCount * 1, buf.readableBytes()); - } - - obj.options = new String[optionsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < optionsCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("options[" + i + "]", strLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("BuilderToolOptionArg", 9, buf.readableBytes() - offset); + } else { + BuilderToolOptionArg obj = new BuilderToolOptionArg(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Default", varPosBase0, buf.readableBytes()); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("options[" + i + "]", strLen, 4096000); + int varPos0 = offset + 9 + varPosBase0; + int defaultValueLen = VarInt.peek(buf, varPos0); + if (defaultValueLen < 0) { + throw ProtocolException.invalidVarInt("Default"); } - int strVarLen = VarInt.length(buf, elemPos); - obj.options[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } + int defaultValueVarIntLen = VarInt.size(defaultValueLen); + if (defaultValueLen > 4096000) { + throw ProtocolException.stringTooLong("Default", defaultValueLen, 4096000); + } - return obj; + if (varPos0 + defaultValueVarIntLen + defaultValueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Default", varPos0 + defaultValueVarIntLen + defaultValueLen, buf.readableBytes()); + } + + obj.defaultValue = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Options", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int optionsCount = VarInt.peek(buf, varPos1); + if (optionsCount < 0) { + throw ProtocolException.invalidVarInt("Options"); + } + + int varIntLen = VarInt.size(optionsCount); + if (optionsCount > 4096000) { + throw ProtocolException.arrayTooLong("Options", optionsCount, 4096000); + } + + if (varPos1 + varIntLen + optionsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Options", varPos1 + varIntLen + optionsCount * 1, buf.readableBytes()); + } + + obj.options = new String[optionsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < optionsCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("options[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("options[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("options[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.options[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -95,9 +118,13 @@ public class BuilderToolOptionArg { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Default", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -105,13 +132,17 @@ public class BuilderToolOptionArg { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Options", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; } if (pos1 - offset > maxEnd) { @@ -188,15 +219,11 @@ public class BuilderToolOptionArg { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int defaultOffset = buffer.getIntLE(offset + 1); - if (defaultOffset < 0) { + if (defaultOffset < 0 || defaultOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Default"); } int pos = offset + 9 + defaultOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Default"); - } - int defaultLen = VarInt.peek(buffer, pos); if (defaultLen < 0) { return ValidationResult.error("Invalid string length for Default"); @@ -206,7 +233,7 @@ public class BuilderToolOptionArg { return ValidationResult.error("Default exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(defaultLen); pos += defaultLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Default"); @@ -215,15 +242,11 @@ public class BuilderToolOptionArg { if ((nullBits & 2) != 0) { int optionsOffset = buffer.getIntLE(offset + 5); - if (optionsOffset < 0) { + if (optionsOffset < 0 || optionsOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Options"); } int posx = offset + 9 + optionsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Options"); - } - int optionsCount = VarInt.peek(buffer, posx); if (optionsCount < 0) { return ValidationResult.error("Invalid array count for Options"); @@ -233,7 +256,7 @@ public class BuilderToolOptionArg { return ValidationResult.error("Options exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(optionsCount); for (int i = 0; i < optionsCount; i++) { int strLen = VarInt.peek(buffer, posx); @@ -241,7 +264,7 @@ public class BuilderToolOptionArg { return ValidationResult.error("Invalid string length in Options"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(strLen); posx += strLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in Options"); diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolPasteClipboard.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolPasteClipboard.java index 19e06cf8..d5cc82e6 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolPasteClipboard.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolPasteClipboard.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,11 +48,15 @@ public class BuilderToolPasteClipboard implements Packet, ToServerPacket { @Nonnull public static BuilderToolPasteClipboard deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolPasteClipboard obj = new BuilderToolPasteClipboard(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("BuilderToolPasteClipboard", 12, buf.readableBytes() - offset); + } else { + BuilderToolPasteClipboard obj = new BuilderToolPasteClipboard(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolResetClipboardRotation.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolResetClipboardRotation.java new file mode 100644 index 00000000..8ca988d3 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolResetClipboardRotation.java @@ -0,0 +1,64 @@ +package com.hypixel.hytale.protocol.packets.buildertools; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import javax.annotation.Nonnull; + +public class BuilderToolResetClipboardRotation implements Packet, ToServerPacket { + public static final int PACKET_ID = 427; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 0; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 0; + public static final int MAX_SIZE = 0; + + @Override + public int getId() { + return 427; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + @Nonnull + public static BuilderToolResetClipboardRotation deserialize(@Nonnull ByteBuf buf, int offset) { + return new BuilderToolResetClipboardRotation(); + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 0; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + } + + @Override + public int computeSize() { + return 0; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 0 ? ValidationResult.error("Buffer too small: expected at least 0 bytes") : ValidationResult.OK; + } + + public BuilderToolResetClipboardRotation clone() { + return new BuilderToolResetClipboardRotation(); + } + + @Override + public boolean equals(Object obj) { + return this == obj ? true : obj instanceof BuilderToolResetClipboardRotation other; + } + + @Override + public int hashCode() { + return 0; + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolRotateClipboard.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolRotateClipboard.java index 21027121..5e383646 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolRotateClipboard.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolRotateClipboard.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -45,10 +46,14 @@ public class BuilderToolRotateClipboard implements Packet, ToServerPacket { @Nonnull public static BuilderToolRotateClipboard deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolRotateClipboard obj = new BuilderToolRotateClipboard(); - obj.angle = buf.getIntLE(offset + 0); - obj.axis = Axis.fromValue(buf.getByte(offset + 4)); - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("BuilderToolRotateClipboard", 5, buf.readableBytes() - offset); + } else { + BuilderToolRotateClipboard obj = new BuilderToolRotateClipboard(); + obj.angle = buf.getIntLE(offset + 0); + obj.axis = Axis.fromValue(buf.getByte(offset + 4)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -67,7 +72,12 @@ public class BuilderToolRotateClipboard implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 5 ? ValidationResult.error("Buffer too small: expected at least 5 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 5) { + return ValidationResult.error("Buffer too small: expected at least 5 bytes"); + } else { + int v = buffer.getByte(offset + 4) & 255; + return v >= 3 ? ValidationResult.error("Invalid Axis value for Axis") : ValidationResult.OK; + } } public BuilderToolRotateClipboard clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolRotationArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolRotationArg.java index f36fa1da..0075a7c6 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolRotationArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolRotationArg.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.Rotation; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -28,9 +29,13 @@ public class BuilderToolRotationArg { @Nonnull public static BuilderToolRotationArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolRotationArg obj = new BuilderToolRotationArg(); - obj.defaultValue = Rotation.fromValue(buf.getByte(offset + 0)); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolRotationArg", 1, buf.readableBytes() - offset); + } else { + BuilderToolRotationArg obj = new BuilderToolRotationArg(); + obj.defaultValue = Rotation.fromValue(buf.getByte(offset + 0)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -46,7 +51,12 @@ public class BuilderToolRotationArg { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 4 ? ValidationResult.error("Invalid Rotation value for Default") : ValidationResult.OK; + } } public BuilderToolRotationArg clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionToolReplyWithClipboard.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionToolReplyWithClipboard.java index 8749a812..a9749f6f 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionToolReplyWithClipboard.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionToolReplyWithClipboard.java @@ -18,13 +18,15 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli public static final boolean IS_COMPRESSED = true; public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 1; - public static final int VARIABLE_FIELD_COUNT = 2; - public static final int VARIABLE_BLOCK_START = 9; - public static final int MAX_SIZE = 139264019; + public static final int VARIABLE_FIELD_COUNT = 3; + public static final int VARIABLE_BLOCK_START = 13; + public static final int MAX_SIZE = 1677721600; @Nullable public BlockChange[] blocksChange; @Nullable public FluidChange[] fluidsChange; + @Nullable + public ClipboardEntityChange[] entityChanges; @Override public int getId() { @@ -39,81 +41,133 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli public BuilderToolSelectionToolReplyWithClipboard() { } - public BuilderToolSelectionToolReplyWithClipboard(@Nullable BlockChange[] blocksChange, @Nullable FluidChange[] fluidsChange) { + public BuilderToolSelectionToolReplyWithClipboard( + @Nullable BlockChange[] blocksChange, @Nullable FluidChange[] fluidsChange, @Nullable ClipboardEntityChange[] entityChanges + ) { this.blocksChange = blocksChange; this.fluidsChange = fluidsChange; + this.entityChanges = entityChanges; } public BuilderToolSelectionToolReplyWithClipboard(@Nonnull BuilderToolSelectionToolReplyWithClipboard other) { this.blocksChange = other.blocksChange; this.fluidsChange = other.fluidsChange; + this.entityChanges = other.entityChanges; } @Nonnull public static BuilderToolSelectionToolReplyWithClipboard deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSelectionToolReplyWithClipboard obj = new BuilderToolSelectionToolReplyWithClipboard(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int blocksChangeCount = VarInt.peek(buf, varPos0); - if (blocksChangeCount < 0) { - throw ProtocolException.negativeLength("BlocksChange", blocksChangeCount); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("BuilderToolSelectionToolReplyWithClipboard", 13, buf.readableBytes() - offset); + } else { + BuilderToolSelectionToolReplyWithClipboard obj = new BuilderToolSelectionToolReplyWithClipboard(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("BlocksChange", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int blocksChangeCount = VarInt.peek(buf, varPos0); + if (blocksChangeCount < 0) { + throw ProtocolException.invalidVarInt("BlocksChange"); + } + + int varIntLen = VarInt.size(blocksChangeCount); + if (blocksChangeCount > 4096000) { + throw ProtocolException.arrayTooLong("BlocksChange", blocksChangeCount, 4096000); + } + + if (varPos0 + varIntLen + blocksChangeCount * 17L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BlocksChange", varPos0 + varIntLen + blocksChangeCount * 17, buf.readableBytes()); + } + + obj.blocksChange = new BlockChange[blocksChangeCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < blocksChangeCount; i++) { + obj.blocksChange[i] = BlockChange.deserialize(buf, elemPos); + elemPos += BlockChange.computeBytesConsumed(buf, elemPos); + } } - if (blocksChangeCount > 4096000) { - throw ProtocolException.arrayTooLong("BlocksChange", blocksChangeCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("FluidsChange", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int fluidsChangeCount = VarInt.peek(buf, varPos1); + if (fluidsChangeCount < 0) { + throw ProtocolException.invalidVarInt("FluidsChange"); + } + + int varIntLenx = VarInt.size(fluidsChangeCount); + if (fluidsChangeCount > 4096000) { + throw ProtocolException.arrayTooLong("FluidsChange", fluidsChangeCount, 4096000); + } + + if (varPos1 + varIntLenx + fluidsChangeCount * 17L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FluidsChange", varPos1 + varIntLenx + fluidsChangeCount * 17, buf.readableBytes()); + } + + obj.fluidsChange = new FluidChange[fluidsChangeCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < fluidsChangeCount; i++) { + obj.fluidsChange[i] = FluidChange.deserialize(buf, elemPos); + elemPos += FluidChange.computeBytesConsumed(buf, elemPos); + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + blocksChangeCount * 17L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("BlocksChange", varPos0 + varIntLen + blocksChangeCount * 17, buf.readableBytes()); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("EntityChanges", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int entityChangesCount = VarInt.peek(buf, varPos2); + if (entityChangesCount < 0) { + throw ProtocolException.invalidVarInt("EntityChanges"); + } + + int varIntLenxx = VarInt.size(entityChangesCount); + if (entityChangesCount > 4096000) { + throw ProtocolException.arrayTooLong("EntityChanges", entityChangesCount, 4096000); + } + + if (varPos2 + varIntLenxx + entityChangesCount * 45L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EntityChanges", varPos2 + varIntLenxx + entityChangesCount * 45, buf.readableBytes()); + } + + obj.entityChanges = new ClipboardEntityChange[entityChangesCount]; + int elemPos = varPos2 + varIntLenxx; + + for (int i = 0; i < entityChangesCount; i++) { + obj.entityChanges[i] = ClipboardEntityChange.deserialize(buf, elemPos); + elemPos += ClipboardEntityChange.computeBytesConsumed(buf, elemPos); + } } - obj.blocksChange = new BlockChange[blocksChangeCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < blocksChangeCount; i++) { - obj.blocksChange[i] = BlockChange.deserialize(buf, elemPos); - elemPos += BlockChange.computeBytesConsumed(buf, elemPos); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int fluidsChangeCount = VarInt.peek(buf, varPos1); - if (fluidsChangeCount < 0) { - throw ProtocolException.negativeLength("FluidsChange", fluidsChangeCount); - } - - if (fluidsChangeCount > 4096000) { - throw ProtocolException.arrayTooLong("FluidsChange", fluidsChangeCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + fluidsChangeCount * 17L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FluidsChange", varPos1 + varIntLen + fluidsChangeCount * 17, buf.readableBytes()); - } - - obj.fluidsChange = new FluidChange[fluidsChangeCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < fluidsChangeCount; i++) { - obj.fluidsChange[i] = FluidChange.deserialize(buf, elemPos); - elemPos += FluidChange.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 9; + int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); - int pos0 = offset + 9 + fieldOffset0; + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("BlocksChange", fieldOffset0, maxEnd); + } + + int pos0 = offset + 13 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += BlockChange.computeBytesConsumed(buf, pos0); @@ -126,9 +180,13 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); - int pos1 = offset + 9 + fieldOffset1; + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("FluidsChange", fieldOffset1, maxEnd); + } + + int pos1 = offset + 13 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += FluidChange.computeBytesConsumed(buf, pos1); @@ -139,6 +197,25 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli } } + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("EntityChanges", fieldOffset2, maxEnd); + } + + int pos2 = offset + 13 + fieldOffset2; + int arrLen = VarInt.peek(buf, pos2); + pos2 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos2 += ClipboardEntityChange.computeBytesConsumed(buf, pos2); + } + + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + return maxEnd; } @@ -154,11 +231,17 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli nullBits = (byte)(nullBits | 2); } + if (this.entityChanges != null) { + nullBits = (byte)(nullBits | 4); + } + buf.writeByte(nullBits); int blocksChangeOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int fluidsChangeOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int entityChangesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); if (this.blocksChange != null) { buf.setIntLE(blocksChangeOffsetSlot, buf.writerIndex() - varBlockStart); @@ -189,11 +272,26 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli } else { buf.setIntLE(fluidsChangeOffsetSlot, -1); } + + if (this.entityChanges != null) { + buf.setIntLE(entityChangesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.entityChanges.length > 4096000) { + throw ProtocolException.arrayTooLong("EntityChanges", this.entityChanges.length, 4096000); + } + + VarInt.write(buf, this.entityChanges.length); + + for (ClipboardEntityChange item : this.entityChanges) { + item.serialize(buf); + } + } else { + buf.setIntLE(entityChangesOffsetSlot, -1); + } } @Override public int computeSize() { - int size = 9; + int size = 13; if (this.blocksChange != null) { size += VarInt.size(this.blocksChange.length) + this.blocksChange.length * 17; } @@ -202,25 +300,31 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli size += VarInt.size(this.fluidsChange.length) + this.fluidsChange.length * 17; } + if (this.entityChanges != null) { + int entityChangesSize = 0; + + for (ClipboardEntityChange elem : this.entityChanges) { + entityChangesSize += elem.computeSize(); + } + + size += VarInt.size(this.entityChanges.length) + entityChangesSize; + } + return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 9) { - return ValidationResult.error("Buffer too small: expected at least 9 bytes"); + if (buffer.readableBytes() - offset < 13) { + return ValidationResult.error("Buffer too small: expected at least 13 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int blocksChangeOffset = buffer.getIntLE(offset + 1); - if (blocksChangeOffset < 0) { + if (blocksChangeOffset < 0 || blocksChangeOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for BlocksChange"); } - int pos = offset + 9 + blocksChangeOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlocksChange"); - } - + int pos = offset + 13 + blocksChangeOffset; int blocksChangeCount = VarInt.peek(buffer, pos); if (blocksChangeCount < 0) { return ValidationResult.error("Invalid array count for BlocksChange"); @@ -230,7 +334,7 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli return ValidationResult.error("BlocksChange exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(blocksChangeCount); pos += blocksChangeCount * 17; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading BlocksChange"); @@ -239,15 +343,11 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli if ((nullBits & 2) != 0) { int fluidsChangeOffset = buffer.getIntLE(offset + 5); - if (fluidsChangeOffset < 0) { + if (fluidsChangeOffset < 0 || fluidsChangeOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for FluidsChange"); } - int posx = offset + 9 + fluidsChangeOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FluidsChange"); - } - + int posx = offset + 13 + fluidsChangeOffset; int fluidsChangeCount = VarInt.peek(buffer, posx); if (fluidsChangeCount < 0) { return ValidationResult.error("Invalid array count for FluidsChange"); @@ -257,13 +357,41 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli return ValidationResult.error("FluidsChange exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(fluidsChangeCount); posx += fluidsChangeCount * 17; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FluidsChange"); } } + if ((nullBits & 4) != 0) { + int entityChangesOffset = buffer.getIntLE(offset + 9); + if (entityChangesOffset < 0 || entityChangesOffset > buffer.writerIndex() - offset - 13) { + return ValidationResult.error("Invalid offset for EntityChanges"); + } + + int posxx = offset + 13 + entityChangesOffset; + int entityChangesCount = VarInt.peek(buffer, posxx); + if (entityChangesCount < 0) { + return ValidationResult.error("Invalid array count for EntityChanges"); + } + + if (entityChangesCount > 4096000) { + return ValidationResult.error("EntityChanges exceeds max length 4096000"); + } + + posxx += VarInt.size(entityChangesCount); + + for (int i = 0; i < entityChangesCount; i++) { + ValidationResult structResult = ClipboardEntityChange.validateStructure(buffer, posxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ClipboardEntityChange in EntityChanges[" + i + "]: " + structResult.error()); + } + + posxx += ClipboardEntityChange.computeBytesConsumed(buffer, posxx); + } + } + return ValidationResult.OK; } } @@ -272,6 +400,7 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli BuilderToolSelectionToolReplyWithClipboard copy = new BuilderToolSelectionToolReplyWithClipboard(); copy.blocksChange = this.blocksChange != null ? Arrays.stream(this.blocksChange).map(e -> e.clone()).toArray(BlockChange[]::new) : null; copy.fluidsChange = this.fluidsChange != null ? Arrays.stream(this.fluidsChange).map(e -> e.clone()).toArray(FluidChange[]::new) : null; + copy.entityChanges = this.entityChanges != null ? Arrays.stream(this.entityChanges).map(e -> e.clone()).toArray(ClipboardEntityChange[]::new) : null; return copy; } @@ -283,7 +412,8 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli return !(obj instanceof BuilderToolSelectionToolReplyWithClipboard other) ? false : Arrays.equals((Object[])this.blocksChange, (Object[])other.blocksChange) - && Arrays.equals((Object[])this.fluidsChange, (Object[])other.fluidsChange); + && Arrays.equals((Object[])this.fluidsChange, (Object[])other.fluidsChange) + && Arrays.equals((Object[])this.entityChanges, (Object[])other.entityChanges); } } @@ -291,6 +421,7 @@ public class BuilderToolSelectionToolReplyWithClipboard implements Packet, ToCli public int hashCode() { int result = 1; result = 31 * result + Arrays.hashCode((Object[])this.blocksChange); - return 31 * result + Arrays.hashCode((Object[])this.fluidsChange); + result = 31 * result + Arrays.hashCode((Object[])this.fluidsChange); + return 31 * result + Arrays.hashCode((Object[])this.entityChanges); } } diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionTransform.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionTransform.java index d9705638..6b6890bc 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionTransform.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionTransform.java @@ -1,16 +1,18 @@ package com.hypixel.hytale.protocol.packets.buildertools; -import com.hypixel.hytale.math.Quatf; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; -import com.hypixel.hytale.protocol.Vector3f; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Quaternionfc; +import org.joml.Vector3fc; public class BuilderToolSelectionTransform implements Packet, ToServerPacket { public static final int PACKET_ID = 405; @@ -21,15 +23,15 @@ public class BuilderToolSelectionTransform implements Packet, ToServerPacket { public static final int VARIABLE_BLOCK_START = 80; public static final int MAX_SIZE = 80; @Nullable - public Quatf rotation; + public Quaternionfc rotation; @Nullable public BlockPosition translationOffset; @Nullable public BlockPosition initialSelectionMin; @Nullable public BlockPosition initialSelectionMax; - @Nullable - public Vector3f initialRotationOrigin; + @Nonnull + public Vector3fc initialRotationOrigin = PacketIO.ZERO_VECTOR3; public boolean cutOriginal; public boolean applyTransformationToSelectionMinMax; public boolean isExitingTransformMode; @@ -50,11 +52,11 @@ public class BuilderToolSelectionTransform implements Packet, ToServerPacket { } public BuilderToolSelectionTransform( - @Nullable Quatf rotation, + @Nullable Quaternionfc rotation, @Nullable BlockPosition translationOffset, @Nullable BlockPosition initialSelectionMin, @Nullable BlockPosition initialSelectionMax, - @Nullable Vector3f initialRotationOrigin, + @Nonnull Vector3fc initialRotationOrigin, boolean cutOriginal, boolean applyTransformationToSelectionMinMax, boolean isExitingTransformMode, @@ -85,36 +87,37 @@ public class BuilderToolSelectionTransform implements Packet, ToServerPacket { @Nonnull public static BuilderToolSelectionTransform deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSelectionTransform obj = new BuilderToolSelectionTransform(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.rotation = Quatf.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 80) { + throw ProtocolException.bufferTooSmall("BuilderToolSelectionTransform", 80, buf.readableBytes() - offset); + } else { + BuilderToolSelectionTransform obj = new BuilderToolSelectionTransform(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.rotation = PacketIO.readQuaternionf(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.translationOffset = BlockPosition.deserialize(buf, offset + 17); - } + if ((nullBits & 2) != 0) { + obj.translationOffset = BlockPosition.deserialize(buf, offset + 17); + } - if ((nullBits & 4) != 0) { - obj.initialSelectionMin = BlockPosition.deserialize(buf, offset + 29); - } + if ((nullBits & 4) != 0) { + obj.initialSelectionMin = BlockPosition.deserialize(buf, offset + 29); + } - if ((nullBits & 8) != 0) { - obj.initialSelectionMax = BlockPosition.deserialize(buf, offset + 41); - } + if ((nullBits & 8) != 0) { + obj.initialSelectionMax = BlockPosition.deserialize(buf, offset + 41); + } - if ((nullBits & 16) != 0) { - obj.initialRotationOrigin = Vector3f.deserialize(buf, offset + 53); - } + obj.initialRotationOrigin = PacketIO.readVector3f(buf, offset + 53); + obj.cutOriginal = buf.getByte(offset + 65) != 0; + obj.applyTransformationToSelectionMinMax = buf.getByte(offset + 66) != 0; + obj.isExitingTransformMode = buf.getByte(offset + 67) != 0; + if ((nullBits & 16) != 0) { + obj.initialPastePointForClipboardPaste = BlockPosition.deserialize(buf, offset + 68); + } - obj.cutOriginal = buf.getByte(offset + 65) != 0; - obj.applyTransformationToSelectionMinMax = buf.getByte(offset + 66) != 0; - obj.isExitingTransformMode = buf.getByte(offset + 67) != 0; - if ((nullBits & 32) != 0) { - obj.initialPastePointForClipboardPaste = BlockPosition.deserialize(buf, offset + 68); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -140,17 +143,13 @@ public class BuilderToolSelectionTransform implements Packet, ToServerPacket { nullBits = (byte)(nullBits | 8); } - if (this.initialRotationOrigin != null) { - nullBits = (byte)(nullBits | 16); - } - if (this.initialPastePointForClipboardPaste != null) { - nullBits = (byte)(nullBits | 32); + nullBits = (byte)(nullBits | 16); } buf.writeByte(nullBits); if (this.rotation != null) { - this.rotation.serialize(buf); + PacketIO.writeQuaternionf(buf, this.rotation); } else { buf.writeZero(16); } @@ -173,12 +172,7 @@ public class BuilderToolSelectionTransform implements Packet, ToServerPacket { buf.writeZero(12); } - if (this.initialRotationOrigin != null) { - this.initialRotationOrigin.serialize(buf); - } else { - buf.writeZero(12); - } - + PacketIO.writeVector3f(buf, this.initialRotationOrigin); buf.writeByte(this.cutOriginal ? 1 : 0); buf.writeByte(this.applyTransformationToSelectionMinMax ? 1 : 0); buf.writeByte(this.isExitingTransformMode ? 1 : 0); @@ -195,7 +189,12 @@ public class BuilderToolSelectionTransform implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 80 ? ValidationResult.error("Buffer too small: expected at least 80 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 80) { + return ValidationResult.error("Buffer too small: expected at least 80 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public BuilderToolSelectionTransform clone() { @@ -204,7 +203,7 @@ public class BuilderToolSelectionTransform implements Packet, ToServerPacket { copy.translationOffset = this.translationOffset != null ? this.translationOffset.clone() : null; copy.initialSelectionMin = this.initialSelectionMin != null ? this.initialSelectionMin.clone() : null; copy.initialSelectionMax = this.initialSelectionMax != null ? this.initialSelectionMax.clone() : null; - copy.initialRotationOrigin = this.initialRotationOrigin != null ? this.initialRotationOrigin.clone() : null; + copy.initialRotationOrigin = this.initialRotationOrigin; copy.cutOriginal = this.cutOriginal; copy.applyTransformationToSelectionMinMax = this.applyTransformationToSelectionMinMax; copy.isExitingTransformMode = this.isExitingTransformMode; diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionUpdate.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionUpdate.java index 387210fd..9c96252e 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionUpdate.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSelectionUpdate.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -56,14 +57,18 @@ public class BuilderToolSelectionUpdate implements Packet, ToServerPacket { @Nonnull public static BuilderToolSelectionUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSelectionUpdate obj = new BuilderToolSelectionUpdate(); - obj.xMin = buf.getIntLE(offset + 0); - obj.yMin = buf.getIntLE(offset + 4); - obj.zMin = buf.getIntLE(offset + 8); - obj.xMax = buf.getIntLE(offset + 12); - obj.yMax = buf.getIntLE(offset + 16); - obj.zMax = buf.getIntLE(offset + 20); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("BuilderToolSelectionUpdate", 24, buf.readableBytes() - offset); + } else { + BuilderToolSelectionUpdate obj = new BuilderToolSelectionUpdate(); + obj.xMin = buf.getIntLE(offset + 0); + obj.yMin = buf.getIntLE(offset + 4); + obj.zMin = buf.getIntLE(offset + 8); + obj.xMax = buf.getIntLE(offset + 12); + obj.yMax = buf.getIntLE(offset + 16); + obj.zMax = buf.getIntLE(offset + 20); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityCollision.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityCollision.java index dafa9d82..837c0b91 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityCollision.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityCollision.java @@ -49,26 +49,34 @@ public class BuilderToolSetEntityCollision implements Packet, ToServerPacket { @Nonnull public static BuilderToolSetEntityCollision deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSetEntityCollision obj = new BuilderToolSetEntityCollision(); - byte nullBits = buf.getByte(offset); - obj.entityId = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int collisionTypeLen = VarInt.peek(buf, pos); - if (collisionTypeLen < 0) { - throw ProtocolException.negativeLength("CollisionType", collisionTypeLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("BuilderToolSetEntityCollision", 5, buf.readableBytes() - offset); + } else { + BuilderToolSetEntityCollision obj = new BuilderToolSetEntityCollision(); + byte nullBits = buf.getByte(offset); + obj.entityId = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int collisionTypeLen = VarInt.peek(buf, pos); + if (collisionTypeLen < 0) { + throw ProtocolException.invalidVarInt("CollisionType"); + } + + int collisionTypeVarLen = VarInt.size(collisionTypeLen); + if (collisionTypeLen > 4096000) { + throw ProtocolException.stringTooLong("CollisionType", collisionTypeLen, 4096000); + } + + if (pos + collisionTypeVarLen + collisionTypeLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CollisionType", pos + collisionTypeVarLen + collisionTypeLen, buf.readableBytes()); + } + + obj.collisionType = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += collisionTypeVarLen + collisionTypeLen; } - if (collisionTypeLen > 4096000) { - throw ProtocolException.stringTooLong("CollisionType", collisionTypeLen, 4096000); - } - - int collisionTypeVarLen = VarInt.length(buf, pos); - obj.collisionType = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += collisionTypeVarLen + collisionTypeLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -76,7 +84,7 @@ public class BuilderToolSetEntityCollision implements Packet, ToServerPacket { int pos = offset + 5; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -122,7 +130,7 @@ public class BuilderToolSetEntityCollision implements Packet, ToServerPacket { return ValidationResult.error("CollisionType exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(collisionTypeLen); pos += collisionTypeLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading CollisionType"); diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityLight.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityLight.java index abed5843..96f0b967 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityLight.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityLight.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.ColorLight; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,14 +48,18 @@ public class BuilderToolSetEntityLight implements Packet, ToServerPacket { @Nonnull public static BuilderToolSetEntityLight deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSetEntityLight obj = new BuilderToolSetEntityLight(); - byte nullBits = buf.getByte(offset); - obj.entityId = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.light = ColorLight.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("BuilderToolSetEntityLight", 9, buf.readableBytes() - offset); + } else { + BuilderToolSetEntityLight obj = new BuilderToolSetEntityLight(); + byte nullBits = buf.getByte(offset); + obj.entityId = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.light = ColorLight.deserialize(buf, offset + 5); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -83,7 +88,12 @@ public class BuilderToolSetEntityLight implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 9 ? ValidationResult.error("Buffer too small: expected at least 9 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 9) { + return ValidationResult.error("Buffer too small: expected at least 9 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public BuilderToolSetEntityLight clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityPickupEnabled.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityPickupEnabled.java index 8138f39d..b40fbd2a 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityPickupEnabled.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityPickupEnabled.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class BuilderToolSetEntityPickupEnabled implements Packet, ToServerPacket @Nonnull public static BuilderToolSetEntityPickupEnabled deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSetEntityPickupEnabled obj = new BuilderToolSetEntityPickupEnabled(); - obj.entityId = buf.getIntLE(offset + 0); - obj.enabled = buf.getByte(offset + 4) != 0; - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("BuilderToolSetEntityPickupEnabled", 5, buf.readableBytes() - offset); + } else { + BuilderToolSetEntityPickupEnabled obj = new BuilderToolSetEntityPickupEnabled(); + obj.entityId = buf.getIntLE(offset + 0); + obj.enabled = buf.getByte(offset + 4) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityScale.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityScale.java index b0239001..3bce05e2 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityScale.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityScale.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class BuilderToolSetEntityScale implements Packet, ToServerPacket { @Nonnull public static BuilderToolSetEntityScale deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSetEntityScale obj = new BuilderToolSetEntityScale(); - obj.entityId = buf.getIntLE(offset + 0); - obj.scale = buf.getFloatLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("BuilderToolSetEntityScale", 8, buf.readableBytes() - offset); + } else { + BuilderToolSetEntityScale obj = new BuilderToolSetEntityScale(); + obj.entityId = buf.getIntLE(offset + 0); + obj.scale = buf.getFloatLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityTransform.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityTransform.java index c3774a58..982e29a0 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityTransform.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetEntityTransform.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.ModelTransform; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,14 +48,18 @@ public class BuilderToolSetEntityTransform implements Packet, ToServerPacket { @Nonnull public static BuilderToolSetEntityTransform deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSetEntityTransform obj = new BuilderToolSetEntityTransform(); - byte nullBits = buf.getByte(offset); - obj.entityId = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.modelTransform = ModelTransform.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 54) { + throw ProtocolException.bufferTooSmall("BuilderToolSetEntityTransform", 54, buf.readableBytes() - offset); + } else { + BuilderToolSetEntityTransform obj = new BuilderToolSetEntityTransform(); + byte nullBits = buf.getByte(offset); + obj.entityId = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.modelTransform = ModelTransform.deserialize(buf, offset + 5); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -83,7 +88,12 @@ public class BuilderToolSetEntityTransform implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 54 ? ValidationResult.error("Buffer too small: expected at least 54 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 54) { + return ValidationResult.error("Buffer too small: expected at least 54 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public BuilderToolSetEntityTransform clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetNPCDebug.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetNPCDebug.java index b0000b28..cd317f44 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetNPCDebug.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetNPCDebug.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class BuilderToolSetNPCDebug implements Packet, ToServerPacket { @Nonnull public static BuilderToolSetNPCDebug deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSetNPCDebug obj = new BuilderToolSetNPCDebug(); - obj.entityId = buf.getIntLE(offset + 0); - obj.enabled = buf.getByte(offset + 4) != 0; - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("BuilderToolSetNPCDebug", 5, buf.readableBytes() - offset); + } else { + BuilderToolSetNPCDebug obj = new BuilderToolSetNPCDebug(); + obj.entityId = buf.getIntLE(offset + 0); + obj.enabled = buf.getByte(offset + 4) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetTransformationModeState.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetTransformationModeState.java index eebc9038..3e7b9ca2 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetTransformationModeState.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolSetTransformationModeState.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class BuilderToolSetTransformationModeState implements Packet, ToServerPa @Nonnull public static BuilderToolSetTransformationModeState deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolSetTransformationModeState obj = new BuilderToolSetTransformationModeState(); - obj.enabled = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolSetTransformationModeState", 1, buf.readableBytes() - offset); + } else { + BuilderToolSetTransformationModeState obj = new BuilderToolSetTransformationModeState(); + obj.enabled = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolShowAnchor.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolShowAnchor.java index 876413c1..b0b7d1c1 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolShowAnchor.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolShowAnchor.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,11 +48,15 @@ public class BuilderToolShowAnchor implements Packet, ToClientPacket { @Nonnull public static BuilderToolShowAnchor deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolShowAnchor obj = new BuilderToolShowAnchor(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("BuilderToolShowAnchor", 12, buf.readableBytes() - offset); + } else { + BuilderToolShowAnchor obj = new BuilderToolShowAnchor(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolStackArea.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolStackArea.java index 3d1ac5f6..c6657d9a 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolStackArea.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolStackArea.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -60,21 +61,25 @@ public class BuilderToolStackArea implements Packet, ToServerPacket { @Nonnull public static BuilderToolStackArea deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolStackArea obj = new BuilderToolStackArea(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.selectionMin = BlockPosition.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 41) { + throw ProtocolException.bufferTooSmall("BuilderToolStackArea", 41, buf.readableBytes() - offset); + } else { + BuilderToolStackArea obj = new BuilderToolStackArea(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.selectionMin = BlockPosition.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.selectionMax = BlockPosition.deserialize(buf, offset + 13); - } + if ((nullBits & 2) != 0) { + obj.selectionMax = BlockPosition.deserialize(buf, offset + 13); + } - obj.xNormal = buf.getIntLE(offset + 25); - obj.yNormal = buf.getIntLE(offset + 29); - obj.zNormal = buf.getIntLE(offset + 33); - obj.numStacks = buf.getIntLE(offset + 37); - return obj; + obj.xNormal = buf.getIntLE(offset + 25); + obj.yNormal = buf.getIntLE(offset + 29); + obj.zNormal = buf.getIntLE(offset + 33); + obj.numStacks = buf.getIntLE(offset + 37); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -117,7 +122,12 @@ public class BuilderToolStackArea implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 41 ? ValidationResult.error("Buffer too small: expected at least 41 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 41) { + return ValidationResult.error("Buffer too small: expected at least 41 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public BuilderToolStackArea clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolState.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolState.java index f21e7a86..605215f7 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolState.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolState.java @@ -5,115 +5,116 @@ import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.VarInt; import io.netty.buffer.ByteBuf; -import java.util.HashMap; -import java.util.Map; +import java.util.Arrays; import java.util.Objects; -import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class BuilderToolState { public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 2; - public static final int VARIABLE_FIELD_COUNT = 3; - public static final int VARIABLE_BLOCK_START = 14; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 10; public static final int MAX_SIZE = 1677721600; @Nullable public String id; public boolean isBrush; @Nullable - public BuilderToolBrushData brushData; - @Nullable - public Map args; + public BuilderToolArg[] args; public BuilderToolState() { } - public BuilderToolState(@Nullable String id, boolean isBrush, @Nullable BuilderToolBrushData brushData, @Nullable Map args) { + public BuilderToolState(@Nullable String id, boolean isBrush, @Nullable BuilderToolArg[] args) { this.id = id; this.isBrush = isBrush; - this.brushData = brushData; this.args = args; } public BuilderToolState(@Nonnull BuilderToolState other) { this.id = other.id; this.isBrush = other.isBrush; - this.brushData = other.brushData; this.args = other.args; } @Nonnull public static BuilderToolState deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolState obj = new BuilderToolState(); - byte nullBits = buf.getByte(offset); - obj.isBrush = buf.getByte(offset + 1) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 14 + buf.getIntLE(offset + 2); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); - } - - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 14 + buf.getIntLE(offset + 6); - obj.brushData = BuilderToolBrushData.deserialize(buf, varPos1); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 14 + buf.getIntLE(offset + 10); - int argsCount = VarInt.peek(buf, varPos2); - if (argsCount < 0) { - throw ProtocolException.negativeLength("Args", argsCount); - } - - if (argsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Args", argsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - obj.args = new HashMap<>(argsCount); - int dictPos = varPos2 + varIntLen; - - for (int i = 0; i < argsCount; i++) { - int keyLen = VarInt.peek(buf, dictPos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("BuilderToolState", 10, buf.readableBytes() - offset); + } else { + BuilderToolState obj = new BuilderToolState(); + byte nullBits = buf.getByte(offset); + obj.isBrush = buf.getByte(offset + 1) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int varPos0 = offset + 10 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); } - int keyVarLen = VarInt.length(buf, dictPos); - String key = PacketIO.readVarString(buf, dictPos); - dictPos += keyVarLen + keyLen; - BuilderToolArg val = BuilderToolArg.deserialize(buf, dictPos); - dictPos += BuilderToolArg.computeBytesConsumed(buf, dictPos); - if (obj.args.put(key, val) != null) { - throw ProtocolException.duplicateKey("args", key); + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Args", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 10 + varPosBase1; + int argsCount = VarInt.peek(buf, varPos1); + if (argsCount < 0) { + throw ProtocolException.invalidVarInt("Args"); + } + + int varIntLen = VarInt.size(argsCount); + if (argsCount > 4096000) { + throw ProtocolException.arrayTooLong("Args", argsCount, 4096000); + } + + if (varPos1 + varIntLen + argsCount * 33L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Args", varPos1 + varIntLen + argsCount * 33, buf.readableBytes()); + } + + obj.args = new BuilderToolArg[argsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < argsCount; i++) { + obj.args[i] = BuilderToolArg.deserialize(buf, elemPos); + elemPos += BuilderToolArg.computeBytesConsumed(buf, elemPos); } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 14; + int maxEnd = 10; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); - int pos0 = offset + 14 + fieldOffset0; + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + + int pos0 = offset + 10 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -121,30 +122,23 @@ public class BuilderToolState { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); - int pos1 = offset + 14 + fieldOffset1; - pos1 += BuilderToolBrushData.computeBytesConsumed(buf, pos1); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 10) { + throw ProtocolException.invalidOffset("Args", fieldOffset1, maxEnd); + } + + int pos1 = offset + 10 + fieldOffset1; + int arrLen = VarInt.peek(buf, pos1); + pos1 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos1 += BuilderToolArg.computeBytesConsumed(buf, pos1); + } + if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } } - if ((nullBits & 4) != 0) { - int fieldOffset2 = buf.getIntLE(offset + 10); - int pos2 = offset + 14 + fieldOffset2; - int dictLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); - - for (int i = 0; i < dictLen; i++) { - int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; - pos2 += BuilderToolArg.computeBytesConsumed(buf, pos2); - } - - if (pos2 - offset > maxEnd) { - maxEnd = pos2 - offset; - } - } - return maxEnd; } @@ -155,20 +149,14 @@ public class BuilderToolState { nullBits = (byte)(nullBits | 1); } - if (this.brushData != null) { - nullBits = (byte)(nullBits | 2); - } - if (this.args != null) { - nullBits = (byte)(nullBits | 4); + nullBits = (byte)(nullBits | 2); } buf.writeByte(nullBits); buf.writeByte(this.isBrush ? 1 : 0); int idOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); - int brushDataOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); int argsOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); @@ -179,24 +167,16 @@ public class BuilderToolState { buf.setIntLE(idOffsetSlot, -1); } - if (this.brushData != null) { - buf.setIntLE(brushDataOffsetSlot, buf.writerIndex() - varBlockStart); - this.brushData.serialize(buf); - } else { - buf.setIntLE(brushDataOffsetSlot, -1); - } - if (this.args != null) { buf.setIntLE(argsOffsetSlot, buf.writerIndex() - varBlockStart); - if (this.args.size() > 4096000) { - throw ProtocolException.dictionaryTooLarge("Args", this.args.size(), 4096000); + if (this.args.length > 4096000) { + throw ProtocolException.arrayTooLong("Args", this.args.length, 4096000); } - VarInt.write(buf, this.args.size()); + VarInt.write(buf, this.args.length); - for (Entry e : this.args.entrySet()) { - PacketIO.writeVarString(buf, e.getKey(), 4096000); - e.getValue().serialize(buf); + for (BuilderToolArg item : this.args) { + item.serialize(buf); } } else { buf.setIntLE(argsOffsetSlot, -1); @@ -204,44 +184,36 @@ public class BuilderToolState { } public int computeSize() { - int size = 14; + int size = 10; if (this.id != null) { size += PacketIO.stringSize(this.id); } - if (this.brushData != null) { - size += this.brushData.computeSize(); - } - if (this.args != null) { int argsSize = 0; - for (Entry kvp : this.args.entrySet()) { - argsSize += PacketIO.stringSize(kvp.getKey()) + kvp.getValue().computeSize(); + for (BuilderToolArg elem : this.args) { + argsSize += elem.computeSize(); } - size += VarInt.size(this.args.size()) + argsSize; + size += VarInt.size(this.args.length) + argsSize; } return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 14) { - return ValidationResult.error("Buffer too small: expected at least 14 bytes"); + if (buffer.readableBytes() - offset < 10) { + return ValidationResult.error("Buffer too small: expected at least 10 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 2); - if (idOffset < 0) { + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 10) { return ValidationResult.error("Invalid offset for Id"); } - int pos = offset + 14 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); - } - + int pos = offset + 10 + idOffset; int idLen = VarInt.peek(buffer, pos); if (idLen < 0) { return ValidationResult.error("Invalid string length for Id"); @@ -251,7 +223,7 @@ public class BuilderToolState { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); @@ -259,63 +231,30 @@ public class BuilderToolState { } if ((nullBits & 2) != 0) { - int brushDataOffset = buffer.getIntLE(offset + 6); - if (brushDataOffset < 0) { - return ValidationResult.error("Invalid offset for BrushData"); - } - - int posx = offset + 14 + brushDataOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BrushData"); - } - - ValidationResult brushDataResult = BuilderToolBrushData.validateStructure(buffer, posx); - if (!brushDataResult.isValid()) { - return ValidationResult.error("Invalid BrushData: " + brushDataResult.error()); - } - - posx += BuilderToolBrushData.computeBytesConsumed(buffer, posx); - } - - if ((nullBits & 4) != 0) { - int argsOffset = buffer.getIntLE(offset + 10); - if (argsOffset < 0) { + int argsOffset = buffer.getIntLE(offset + 6); + if (argsOffset < 0 || argsOffset > buffer.writerIndex() - offset - 10) { return ValidationResult.error("Invalid offset for Args"); } - int posxx = offset + 14 + argsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Args"); - } - - int argsCount = VarInt.peek(buffer, posxx); + int posx = offset + 10 + argsOffset; + int argsCount = VarInt.peek(buffer, posx); if (argsCount < 0) { - return ValidationResult.error("Invalid dictionary count for Args"); + return ValidationResult.error("Invalid array count for Args"); } if (argsCount > 4096000) { return ValidationResult.error("Args exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posx += VarInt.size(argsCount); for (int i = 0; i < argsCount; i++) { - int keyLen = VarInt.peek(buffer, posxx); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for key"); + ValidationResult structResult = BuilderToolArg.validateStructure(buffer, posx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid BuilderToolArg in Args[" + i + "]: " + structResult.error()); } - if (keyLen > 4096000) { - return ValidationResult.error("key exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += keyLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading key"); - } - - posxx += BuilderToolArg.computeBytesConsumed(buffer, posxx); + posx += BuilderToolArg.computeBytesConsumed(buffer, posx); } } @@ -327,17 +266,7 @@ public class BuilderToolState { BuilderToolState copy = new BuilderToolState(); copy.id = this.id; copy.isBrush = this.isBrush; - copy.brushData = this.brushData != null ? this.brushData.clone() : null; - if (this.args != null) { - Map m = new HashMap<>(); - - for (Entry e : this.args.entrySet()) { - m.put(e.getKey(), e.getValue().clone()); - } - - copy.args = m; - } - + copy.args = this.args != null ? Arrays.stream(this.args).map(e -> e.clone()).toArray(BuilderToolArg[]::new) : null; return copy; } @@ -348,15 +277,15 @@ public class BuilderToolState { } else { return !(obj instanceof BuilderToolState other) ? false - : Objects.equals(this.id, other.id) - && this.isBrush == other.isBrush - && Objects.equals(this.brushData, other.brushData) - && Objects.equals(this.args, other.args); + : Objects.equals(this.id, other.id) && this.isBrush == other.isBrush && Arrays.equals((Object[])this.args, (Object[])other.args); } } @Override public int hashCode() { - return Objects.hash(this.id, this.isBrush, this.brushData, this.args); + int result = 1; + result = 31 * result + Objects.hashCode(this.id); + result = 31 * result + Boolean.hashCode(this.isBrush); + return 31 * result + Arrays.hashCode((Object[])this.args); } } diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolStringArg.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolStringArg.java index 312d34e3..f8b26e41 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolStringArg.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolStringArg.java @@ -31,25 +31,33 @@ public class BuilderToolStringArg { @Nonnull public static BuilderToolStringArg deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolStringArg obj = new BuilderToolStringArg(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int defaultValueLen = VarInt.peek(buf, pos); - if (defaultValueLen < 0) { - throw ProtocolException.negativeLength("Default", defaultValueLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("BuilderToolStringArg", 1, buf.readableBytes() - offset); + } else { + BuilderToolStringArg obj = new BuilderToolStringArg(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int defaultValueLen = VarInt.peek(buf, pos); + if (defaultValueLen < 0) { + throw ProtocolException.invalidVarInt("Default"); + } + + int defaultValueVarLen = VarInt.size(defaultValueLen); + if (defaultValueLen > 4096000) { + throw ProtocolException.stringTooLong("Default", defaultValueLen, 4096000); + } + + if (pos + defaultValueVarLen + defaultValueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Default", pos + defaultValueVarLen + defaultValueLen, buf.readableBytes()); + } + + obj.defaultValue = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += defaultValueVarLen + defaultValueLen; } - if (defaultValueLen > 4096000) { - throw ProtocolException.stringTooLong("Default", defaultValueLen, 4096000); - } - - int defaultValueVarLen = VarInt.length(buf, pos); - obj.defaultValue = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += defaultValueVarLen + defaultValueLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -57,7 +65,7 @@ public class BuilderToolStringArg { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -100,7 +108,7 @@ public class BuilderToolStringArg { return ValidationResult.error("Default exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(defaultLen); pos += defaultLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Default"); diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolsSetSoundSet.java b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolsSetSoundSet.java index b87ea4d6..bdba848c 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolsSetSoundSet.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolsSetSoundSet.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class BuilderToolsSetSoundSet implements Packet, ToClientPacket { @Nonnull public static BuilderToolsSetSoundSet deserialize(@Nonnull ByteBuf buf, int offset) { - BuilderToolsSetSoundSet obj = new BuilderToolsSetSoundSet(); - obj.soundSetIndex = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("BuilderToolsSetSoundSet", 4, buf.readableBytes() - offset); + } else { + BuilderToolsSetSoundSet obj = new BuilderToolsSetSoundSet(); + obj.soundSetIndex = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/ClipboardEntityChange.java b/src/com/hypixel/hytale/protocol/packets/buildertools/ClipboardEntityChange.java new file mode 100644 index 00000000..10cb63ec --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/ClipboardEntityChange.java @@ -0,0 +1,316 @@ +package com.hypixel.hytale.protocol.packets.buildertools; + +import com.hypixel.hytale.protocol.Direction; +import com.hypixel.hytale.protocol.Model; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ClipboardEntityChange { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 45; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 53; + public static final int MAX_SIZE = 1677721600; + public float x; + public float y; + public float z; + public int blockId; + @Nullable + public Model model; + @Nullable + public String itemId; + @Nullable + public Direction bodyOrientation; + @Nullable + public Direction lookOrientation; + public float scale; + + public ClipboardEntityChange() { + } + + public ClipboardEntityChange( + float x, + float y, + float z, + int blockId, + @Nullable Model model, + @Nullable String itemId, + @Nullable Direction bodyOrientation, + @Nullable Direction lookOrientation, + float scale + ) { + this.x = x; + this.y = y; + this.z = z; + this.blockId = blockId; + this.model = model; + this.itemId = itemId; + this.bodyOrientation = bodyOrientation; + this.lookOrientation = lookOrientation; + this.scale = scale; + } + + public ClipboardEntityChange(@Nonnull ClipboardEntityChange other) { + this.x = other.x; + this.y = other.y; + this.z = other.z; + this.blockId = other.blockId; + this.model = other.model; + this.itemId = other.itemId; + this.bodyOrientation = other.bodyOrientation; + this.lookOrientation = other.lookOrientation; + this.scale = other.scale; + } + + @Nonnull + public static ClipboardEntityChange deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 53) { + throw ProtocolException.bufferTooSmall("ClipboardEntityChange", 53, buf.readableBytes() - offset); + } else { + ClipboardEntityChange obj = new ClipboardEntityChange(); + byte nullBits = buf.getByte(offset); + obj.x = buf.getFloatLE(offset + 1); + obj.y = buf.getFloatLE(offset + 5); + obj.z = buf.getFloatLE(offset + 9); + obj.blockId = buf.getIntLE(offset + 13); + if ((nullBits & 1) != 0) { + obj.bodyOrientation = Direction.deserialize(buf, offset + 17); + } + + if ((nullBits & 2) != 0) { + obj.lookOrientation = Direction.deserialize(buf, offset + 29); + } + + obj.scale = buf.getFloatLE(offset + 41); + if ((nullBits & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 45); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Model", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 53 + varPosBase0; + obj.model = Model.deserialize(buf, varPos0); + } + + if ((nullBits & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 49); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("ItemId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 53 + varPosBase1; + int itemIdLen = VarInt.peek(buf, varPos1); + if (itemIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemId"); + } + + int itemIdVarIntLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + } + + if (varPos1 + itemIdVarIntLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemId", varPos1 + itemIdVarIntLen + itemIdLen, buf.readableBytes()); + } + + obj.itemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 53; + if ((nullBits & 4) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 45); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("Model", fieldOffset0, maxEnd); + } + + int pos0 = offset + 53 + fieldOffset0; + pos0 += Model.computeBytesConsumed(buf, pos0); + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 8) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 49); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 53) { + throw ProtocolException.invalidOffset("ItemId", fieldOffset1, maxEnd); + } + + int pos1 = offset + 53 + fieldOffset1; + int sl = VarInt.peek(buf, pos1); + pos1 += VarInt.size(sl) + sl; + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + return maxEnd; + } + + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.bodyOrientation != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.lookOrientation != null) { + nullBits = (byte)(nullBits | 2); + } + + if (this.model != null) { + nullBits = (byte)(nullBits | 4); + } + + if (this.itemId != null) { + nullBits = (byte)(nullBits | 8); + } + + buf.writeByte(nullBits); + buf.writeFloatLE(this.x); + buf.writeFloatLE(this.y); + buf.writeFloatLE(this.z); + buf.writeIntLE(this.blockId); + if (this.bodyOrientation != null) { + this.bodyOrientation.serialize(buf); + } else { + buf.writeZero(12); + } + + if (this.lookOrientation != null) { + this.lookOrientation.serialize(buf); + } else { + buf.writeZero(12); + } + + buf.writeFloatLE(this.scale); + int modelOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int itemIdOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.model != null) { + buf.setIntLE(modelOffsetSlot, buf.writerIndex() - varBlockStart); + this.model.serialize(buf); + } else { + buf.setIntLE(modelOffsetSlot, -1); + } + + if (this.itemId != null) { + buf.setIntLE(itemIdOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.itemId, 4096000); + } else { + buf.setIntLE(itemIdOffsetSlot, -1); + } + } + + public int computeSize() { + int size = 53; + if (this.model != null) { + size += this.model.computeSize(); + } + + if (this.itemId != null) { + size += PacketIO.stringSize(this.itemId); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 53) { + return ValidationResult.error("Buffer too small: expected at least 53 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 4) != 0) { + int modelOffset = buffer.getIntLE(offset + 45); + if (modelOffset < 0 || modelOffset > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for Model"); + } + + int pos = offset + 53 + modelOffset; + ValidationResult modelResult = Model.validateStructure(buffer, pos); + if (!modelResult.isValid()) { + return ValidationResult.error("Invalid Model: " + modelResult.error()); + } + + pos += Model.computeBytesConsumed(buffer, pos); + } + + if ((nullBits & 8) != 0) { + int itemIdOffset = buffer.getIntLE(offset + 49); + if (itemIdOffset < 0 || itemIdOffset > buffer.writerIndex() - offset - 53) { + return ValidationResult.error("Invalid offset for ItemId"); + } + + int pos = offset + 53 + itemIdOffset; + int itemIdLen = VarInt.peek(buffer, pos); + if (itemIdLen < 0) { + return ValidationResult.error("Invalid string length for ItemId"); + } + + if (itemIdLen > 4096000) { + return ValidationResult.error("ItemId exceeds max length 4096000"); + } + + pos += VarInt.size(itemIdLen); + pos += itemIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ItemId"); + } + } + + return ValidationResult.OK; + } + } + + public ClipboardEntityChange clone() { + ClipboardEntityChange copy = new ClipboardEntityChange(); + copy.x = this.x; + copy.y = this.y; + copy.z = this.z; + copy.blockId = this.blockId; + copy.model = this.model != null ? this.model.clone() : null; + copy.itemId = this.itemId; + copy.bodyOrientation = this.bodyOrientation != null ? this.bodyOrientation.clone() : null; + copy.lookOrientation = this.lookOrientation != null ? this.lookOrientation.clone() : null; + copy.scale = this.scale; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof ClipboardEntityChange other) + ? false + : this.x == other.x + && this.y == other.y + && this.z == other.z + && this.blockId == other.blockId + && Objects.equals(this.model, other.model) + && Objects.equals(this.itemId, other.itemId) + && Objects.equals(this.bodyOrientation, other.bodyOrientation) + && Objects.equals(this.lookOrientation, other.lookOrientation) + && this.scale == other.scale; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y, this.z, this.blockId, this.model, this.itemId, this.bodyOrientation, this.lookOrientation, this.scale); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/PrefabSetAnchor.java b/src/com/hypixel/hytale/protocol/packets/buildertools/PrefabSetAnchor.java new file mode 100644 index 00000000..adb872b3 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/PrefabSetAnchor.java @@ -0,0 +1,103 @@ +package com.hypixel.hytale.protocol.packets.buildertools; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class PrefabSetAnchor implements Packet, ToServerPacket { + public static final int PACKET_ID = 426; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 12; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 12; + public static final int MAX_SIZE = 12; + public int x; + public int y; + public int z; + + @Override + public int getId() { + return 426; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public PrefabSetAnchor() { + } + + public PrefabSetAnchor(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public PrefabSetAnchor(@Nonnull PrefabSetAnchor other) { + this.x = other.x; + this.y = other.y; + this.z = other.z; + } + + @Nonnull + public static PrefabSetAnchor deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("PrefabSetAnchor", 12, buf.readableBytes() - offset); + } else { + PrefabSetAnchor obj = new PrefabSetAnchor(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 12; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + buf.writeIntLE(this.x); + buf.writeIntLE(this.y); + buf.writeIntLE(this.z); + } + + @Override + public int computeSize() { + return 12; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 12 ? ValidationResult.error("Buffer too small: expected at least 12 bytes") : ValidationResult.OK; + } + + public PrefabSetAnchor clone() { + PrefabSetAnchor copy = new PrefabSetAnchor(); + copy.x = this.x; + copy.y = this.y; + copy.z = this.z; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof PrefabSetAnchor other) ? false : this.x == other.x && this.y == other.y && this.z == other.z; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y, this.z); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArgGroup.java b/src/com/hypixel/hytale/protocol/packets/buildertools/RotationFace.java similarity index 50% rename from src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArgGroup.java rename to src/com/hypixel/hytale/protocol/packets/buildertools/RotationFace.java index 9bb48401..a2f34c98 100644 --- a/src/com/hypixel/hytale/protocol/packets/buildertools/BuilderToolArgGroup.java +++ b/src/com/hypixel/hytale/protocol/packets/buildertools/RotationFace.java @@ -2,14 +2,19 @@ package com.hypixel.hytale.protocol.packets.buildertools; import com.hypixel.hytale.protocol.io.ProtocolException; -public enum BuilderToolArgGroup { - Tool(0), - Brush(1); +public enum RotationFace { + Up(0), + Down(1), + North(2), + South(3), + East(4), + West(5), + Camera(6); - public static final BuilderToolArgGroup[] VALUES = values(); + public static final RotationFace[] VALUES = values(); private final int value; - private BuilderToolArgGroup(int value) { + private RotationFace(int value) { this.value = value; } @@ -17,11 +22,11 @@ public enum BuilderToolArgGroup { return this.value; } - public static BuilderToolArgGroup fromValue(int value) { + public static RotationFace fromValue(int value) { if (value >= 0 && value < VALUES.length) { return VALUES[value]; } else { - throw ProtocolException.invalidEnumValue("BuilderToolArgGroup", value); + throw ProtocolException.invalidEnumValue("RotationFace", value); } } } diff --git a/src/com/hypixel/hytale/protocol/packets/camera/CameraShakeEffect.java b/src/com/hypixel/hytale/protocol/packets/camera/CameraShakeEffect.java index 1342cb2b..6d78cd08 100644 --- a/src/com/hypixel/hytale/protocol/packets/camera/CameraShakeEffect.java +++ b/src/com/hypixel/hytale/protocol/packets/camera/CameraShakeEffect.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.AccumulationMode; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -49,11 +50,15 @@ public class CameraShakeEffect implements Packet, ToClientPacket { @Nonnull public static CameraShakeEffect deserialize(@Nonnull ByteBuf buf, int offset) { - CameraShakeEffect obj = new CameraShakeEffect(); - obj.cameraShakeId = buf.getIntLE(offset + 0); - obj.intensity = buf.getFloatLE(offset + 4); - obj.mode = AccumulationMode.fromValue(buf.getByte(offset + 8)); - return obj; + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("CameraShakeEffect", 9, buf.readableBytes() - offset); + } else { + CameraShakeEffect obj = new CameraShakeEffect(); + obj.cameraShakeId = buf.getIntLE(offset + 0); + obj.intensity = buf.getFloatLE(offset + 4); + obj.mode = AccumulationMode.fromValue(buf.getByte(offset + 8)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,7 +78,12 @@ public class CameraShakeEffect implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 9 ? ValidationResult.error("Buffer too small: expected at least 9 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 9) { + return ValidationResult.error("Buffer too small: expected at least 9 bytes"); + } else { + int v = buffer.getByte(offset + 8) & 255; + return v >= 3 ? ValidationResult.error("Invalid AccumulationMode value for Mode") : ValidationResult.OK; + } } public CameraShakeEffect clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/camera/RequestFlyCameraMode.java b/src/com/hypixel/hytale/protocol/packets/camera/RequestFlyCameraMode.java index 910b82c3..e26293eb 100644 --- a/src/com/hypixel/hytale/protocol/packets/camera/RequestFlyCameraMode.java +++ b/src/com/hypixel/hytale/protocol/packets/camera/RequestFlyCameraMode.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.camera; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class RequestFlyCameraMode implements Packet, ToServerPacket { @Nonnull public static RequestFlyCameraMode deserialize(@Nonnull ByteBuf buf, int offset) { - RequestFlyCameraMode obj = new RequestFlyCameraMode(); - obj.entering = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("RequestFlyCameraMode", 1, buf.readableBytes() - offset); + } else { + RequestFlyCameraMode obj = new RequestFlyCameraMode(); + obj.entering = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/camera/SetFlyCameraMode.java b/src/com/hypixel/hytale/protocol/packets/camera/SetFlyCameraMode.java index 625c6408..b2909dd9 100644 --- a/src/com/hypixel/hytale/protocol/packets/camera/SetFlyCameraMode.java +++ b/src/com/hypixel/hytale/protocol/packets/camera/SetFlyCameraMode.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.camera; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class SetFlyCameraMode implements Packet, ToClientPacket { @Nonnull public static SetFlyCameraMode deserialize(@Nonnull ByteBuf buf, int offset) { - SetFlyCameraMode obj = new SetFlyCameraMode(); - obj.entering = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SetFlyCameraMode", 1, buf.readableBytes() - offset); + } else { + SetFlyCameraMode obj = new SetFlyCameraMode(); + obj.entering = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/camera/SetServerCamera.java b/src/com/hypixel/hytale/protocol/packets/camera/SetServerCamera.java index 92950eb2..313c0f1f 100644 --- a/src/com/hypixel/hytale/protocol/packets/camera/SetServerCamera.java +++ b/src/com/hypixel/hytale/protocol/packets/camera/SetServerCamera.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ServerCameraSettings; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -52,15 +53,19 @@ public class SetServerCamera implements Packet, ToClientPacket { @Nonnull public static SetServerCamera deserialize(@Nonnull ByteBuf buf, int offset) { - SetServerCamera obj = new SetServerCamera(); - byte nullBits = buf.getByte(offset); - obj.clientCameraView = ClientCameraView.fromValue(buf.getByte(offset + 1)); - obj.isLocked = buf.getByte(offset + 2) != 0; - if ((nullBits & 1) != 0) { - obj.cameraSettings = ServerCameraSettings.deserialize(buf, offset + 3); - } + if (buf.readableBytes() - offset < 157) { + throw ProtocolException.bufferTooSmall("SetServerCamera", 157, buf.readableBytes() - offset); + } else { + SetServerCamera obj = new SetServerCamera(); + byte nullBits = buf.getByte(offset); + obj.clientCameraView = ClientCameraView.fromValue(buf.getByte(offset + 1)); + obj.isLocked = buf.getByte(offset + 2) != 0; + if ((nullBits & 1) != 0) { + obj.cameraSettings = ServerCameraSettings.deserialize(buf, offset + 3); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,7 +95,13 @@ public class SetServerCamera implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 157 ? ValidationResult.error("Buffer too small: expected at least 157 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 157) { + return ValidationResult.error("Buffer too small: expected at least 157 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 1) & 255; + return v >= 3 ? ValidationResult.error("Invalid ClientCameraView value for ClientCameraView") : ValidationResult.OK; + } } public SetServerCamera clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/connection/ClientDisconnect.java b/src/com/hypixel/hytale/protocol/packets/connection/ClientDisconnect.java new file mode 100644 index 00000000..356cad47 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/connection/ClientDisconnect.java @@ -0,0 +1,109 @@ +package com.hypixel.hytale.protocol.packets.connection; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class ClientDisconnect implements Packet, ToServerPacket { + public static final int PACKET_ID = 1; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 2; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 2; + public static final int MAX_SIZE = 2; + @Nonnull + public ClientDisconnectReason reason = ClientDisconnectReason.PlayerLeave; + @Nonnull + public DisconnectType type = DisconnectType.Disconnect; + + @Override + public int getId() { + return 1; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public ClientDisconnect() { + } + + public ClientDisconnect(@Nonnull ClientDisconnectReason reason, @Nonnull DisconnectType type) { + this.reason = reason; + this.type = type; + } + + public ClientDisconnect(@Nonnull ClientDisconnect other) { + this.reason = other.reason; + this.type = other.type; + } + + @Nonnull + public static ClientDisconnect deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("ClientDisconnect", 2, buf.readableBytes() - offset); + } else { + ClientDisconnect obj = new ClientDisconnect(); + obj.reason = ClientDisconnectReason.fromValue(buf.getByte(offset + 0)); + obj.type = DisconnectType.fromValue(buf.getByte(offset + 1)); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 2; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + buf.writeByte(this.reason.getValue()); + buf.writeByte(this.type.getValue()); + } + + @Override + public int computeSize() { + return 2; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 2) { + return ValidationResult.error("Buffer too small: expected at least 2 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid ClientDisconnectReason value for Reason"); + } else { + v = buffer.getByte(offset + 1) & 255; + return v >= 2 ? ValidationResult.error("Invalid DisconnectType value for Type") : ValidationResult.OK; + } + } + } + + public ClientDisconnect clone() { + ClientDisconnect copy = new ClientDisconnect(); + copy.reason = this.reason; + copy.type = this.type; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof ClientDisconnect other) ? false : Objects.equals(this.reason, other.reason) && Objects.equals(this.type, other.type); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.reason, this.type); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/connection/ClientDisconnectReason.java b/src/com/hypixel/hytale/protocol/packets/connection/ClientDisconnectReason.java new file mode 100644 index 00000000..a4ff95db --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/connection/ClientDisconnectReason.java @@ -0,0 +1,29 @@ +package com.hypixel.hytale.protocol.packets.connection; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum ClientDisconnectReason { + PlayerLeave(0), + PlayerAbort(1), + UserLeave(2), + Crash(3); + + public static final ClientDisconnectReason[] VALUES = values(); + private final int value; + + private ClientDisconnectReason(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static ClientDisconnectReason fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("ClientDisconnectReason", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/connection/Connect.java b/src/com/hypixel/hytale/protocol/packets/connection/Connect.java index 276f6898..7b7417e7 100644 --- a/src/com/hypixel/hytale/protocol/packets/connection/Connect.java +++ b/src/com/hypixel/hytale/protocol/packets/connection/Connect.java @@ -94,72 +94,116 @@ public class Connect implements Packet, ToServerPacket { @Nonnull public static Connect deserialize(@Nonnull ByteBuf buf, int offset) { - Connect obj = new Connect(); - byte nullBits = buf.getByte(offset); - obj.protocolCrc = buf.getIntLE(offset + 1); - obj.protocolBuildNumber = buf.getIntLE(offset + 5); - obj.clientVersion = PacketIO.readFixedAsciiString(buf, offset + 9, 20); - obj.clientType = ClientType.fromValue(buf.getByte(offset + 29)); - obj.uuid = PacketIO.readUUID(buf, offset + 30); - int varPos0 = offset + 66 + buf.getIntLE(offset + 46); - int usernameLen = VarInt.peek(buf, varPos0); - if (usernameLen < 0) { - throw ProtocolException.negativeLength("Username", usernameLen); - } else if (usernameLen > 16) { - throw ProtocolException.stringTooLong("Username", usernameLen, 16); + if (buf.readableBytes() - offset < 66) { + throw ProtocolException.bufferTooSmall("Connect", 66, buf.readableBytes() - offset); } else { - obj.username = PacketIO.readVarString(buf, varPos0, PacketIO.ASCII); - if ((nullBits & 1) != 0) { - varPos0 = offset + 66 + buf.getIntLE(offset + 50); - usernameLen = VarInt.peek(buf, varPos0); + Connect obj = new Connect(); + byte nullBits = buf.getByte(offset); + obj.protocolCrc = buf.getIntLE(offset + 1); + obj.protocolBuildNumber = buf.getIntLE(offset + 5); + obj.clientVersion = PacketIO.readFixedAsciiString(buf, offset + 9, 20); + obj.clientType = ClientType.fromValue(buf.getByte(offset + 29)); + obj.uuid = PacketIO.readUUID(buf, offset + 30); + int varPosBase0 = buf.getIntLE(offset + 46); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 66) { + int varPos0 = offset + 66 + varPosBase0; + int usernameLen = VarInt.peek(buf, varPos0); if (usernameLen < 0) { - throw ProtocolException.negativeLength("IdentityToken", usernameLen); + throw ProtocolException.invalidVarInt("Username"); + } else { + int usernameVarIntLen = VarInt.size(usernameLen); + if (usernameLen > 16) { + throw ProtocolException.stringTooLong("Username", usernameLen, 16); + } else if (varPos0 + usernameVarIntLen + usernameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Username", varPos0 + usernameVarIntLen + usernameLen, buf.readableBytes()); + } else { + obj.username = PacketIO.readVarString(buf, varPos0, PacketIO.ASCII); + if ((nullBits & 1) != 0) { + varPosBase0 = buf.getIntLE(offset + 50); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 66) { + throw ProtocolException.invalidOffset("IdentityToken", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 66 + varPosBase0; + usernameLen = VarInt.peek(buf, varPos0); + if (usernameLen < 0) { + throw ProtocolException.invalidVarInt("IdentityToken"); + } + + usernameVarIntLen = VarInt.size(usernameLen); + if (usernameLen > 8192) { + throw ProtocolException.stringTooLong("IdentityToken", usernameLen, 8192); + } + + if (varPos0 + usernameVarIntLen + usernameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("IdentityToken", varPos0 + usernameVarIntLen + usernameLen, buf.readableBytes()); + } + + obj.identityToken = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + varPosBase0 = buf.getIntLE(offset + 54); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 66) { + varPos0 = offset + 66 + varPosBase0; + usernameLen = VarInt.peek(buf, varPos0); + if (usernameLen < 0) { + throw ProtocolException.invalidVarInt("Language"); + } else { + usernameVarIntLen = VarInt.size(usernameLen); + if (usernameLen > 16) { + throw ProtocolException.stringTooLong("Language", usernameLen, 16); + } else if (varPos0 + usernameVarIntLen + usernameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Language", varPos0 + usernameVarIntLen + usernameLen, buf.readableBytes()); + } else { + obj.language = PacketIO.readVarString(buf, varPos0, PacketIO.ASCII); + if ((nullBits & 2) != 0) { + varPosBase0 = buf.getIntLE(offset + 58); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 66) { + throw ProtocolException.invalidOffset("ReferralData", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 66 + varPosBase0; + usernameLen = VarInt.peek(buf, varPos0); + if (usernameLen < 0) { + throw ProtocolException.invalidVarInt("ReferralData"); + } + + usernameVarIntLen = VarInt.size(usernameLen); + if (usernameLen > 4096) { + throw ProtocolException.arrayTooLong("ReferralData", usernameLen, 4096); + } + + if (varPos0 + usernameVarIntLen + usernameLen * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ReferralData", varPos0 + usernameVarIntLen + usernameLen * 1, buf.readableBytes()); + } + + obj.referralData = new byte[usernameLen]; + + for (int i = 0; i < usernameLen; i++) { + obj.referralData[i] = buf.getByte(varPos0 + usernameVarIntLen + i * 1); + } + } + + if ((nullBits & 4) != 0) { + varPosBase0 = buf.getIntLE(offset + 62); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 66) { + throw ProtocolException.invalidOffset("ReferralSource", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 66 + varPosBase0; + obj.referralSource = HostAddress.deserialize(buf, varPos0); + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("Language", varPosBase0, buf.readableBytes()); + } + } } - - if (usernameLen > 8192) { - throw ProtocolException.stringTooLong("IdentityToken", usernameLen, 8192); - } - - obj.identityToken = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - varPos0 = offset + 66 + buf.getIntLE(offset + 54); - usernameLen = VarInt.peek(buf, varPos0); - if (usernameLen < 0) { - throw ProtocolException.negativeLength("Language", usernameLen); - } else if (usernameLen > 16) { - throw ProtocolException.stringTooLong("Language", usernameLen, 16); } else { - obj.language = PacketIO.readVarString(buf, varPos0, PacketIO.ASCII); - if ((nullBits & 2) != 0) { - varPos0 = offset + 66 + buf.getIntLE(offset + 58); - usernameLen = VarInt.peek(buf, varPos0); - if (usernameLen < 0) { - throw ProtocolException.negativeLength("ReferralData", usernameLen); - } - - if (usernameLen > 4096) { - throw ProtocolException.arrayTooLong("ReferralData", usernameLen, 4096); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + usernameLen * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ReferralData", varPos0 + varIntLen + usernameLen * 1, buf.readableBytes()); - } - - obj.referralData = new byte[usernameLen]; - - for (int i = 0; i < usernameLen; i++) { - obj.referralData[i] = buf.getByte(varPos0 + varIntLen + i * 1); - } - } - - if ((nullBits & 4) != 0) { - varPos0 = offset + 66 + buf.getIntLE(offset + 62); - obj.referralSource = HostAddress.deserialize(buf, varPos0); - } - - return obj; + throw ProtocolException.invalidOffset("Username", varPosBase0, buf.readableBytes()); } } } @@ -168,51 +212,71 @@ public class Connect implements Packet, ToServerPacket { byte nullBits = buf.getByte(offset); int maxEnd = 66; int fieldOffset0 = buf.getIntLE(offset + 46); - int pos0 = offset + 66 + fieldOffset0; - int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - - if ((nullBits & 1) != 0) { - fieldOffset0 = buf.getIntLE(offset + 50); - pos0 = offset + 66 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 66) { + int pos0 = offset + 66 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } - } - fieldOffset0 = buf.getIntLE(offset + 54); - pos0 = offset + 66 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } + if ((nullBits & 1) != 0) { + fieldOffset0 = buf.getIntLE(offset + 50); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 66) { + throw ProtocolException.invalidOffset("IdentityToken", fieldOffset0, maxEnd); + } - if ((nullBits & 2) != 0) { - fieldOffset0 = buf.getIntLE(offset + 58); - pos0 = offset + 66 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl * 1; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; + pos0 = offset + 66 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } } - } - if ((nullBits & 4) != 0) { - fieldOffset0 = buf.getIntLE(offset + 62); - pos0 = offset + 66 + fieldOffset0; - pos0 += HostAddress.computeBytesConsumed(buf, pos0); - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; + fieldOffset0 = buf.getIntLE(offset + 54); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 66) { + pos0 = offset + 66 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + if ((nullBits & 2) != 0) { + fieldOffset0 = buf.getIntLE(offset + 58); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 66) { + throw ProtocolException.invalidOffset("ReferralData", fieldOffset0, maxEnd); + } + + pos0 = offset + 66 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl * 1; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 4) != 0) { + fieldOffset0 = buf.getIntLE(offset + 62); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 66) { + throw ProtocolException.invalidOffset("ReferralSource", fieldOffset0, maxEnd); + } + + pos0 = offset + 66 + fieldOffset0; + pos0 += HostAddress.computeBytesConsumed(buf, pos0); + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Language", fieldOffset0, maxEnd); } + } else { + throw ProtocolException.invalidOffset("Username", fieldOffset0, maxEnd); } - - return maxEnd; } @Override @@ -307,36 +371,31 @@ public class Connect implements Packet, ToServerPacket { return ValidationResult.error("Buffer too small: expected at least 66 bytes"); } else { byte nullBits = buffer.getByte(offset); - int usernameOffset = buffer.getIntLE(offset + 46); - if (usernameOffset < 0) { - return ValidationResult.error("Invalid offset for Username"); + int v = buffer.getByte(offset + 29) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid ClientType value for ClientType"); } else { - int pos = offset + 66 + usernameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Username"); - } else { + v = buffer.getIntLE(offset + 46); + if (v >= 0 && v <= buffer.writerIndex() - offset - 66) { + int pos = offset + 66 + v; int usernameLen = VarInt.peek(buffer, pos); if (usernameLen < 0) { return ValidationResult.error("Invalid string length for Username"); } else if (usernameLen > 16) { return ValidationResult.error("Username exceeds max length 16"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(usernameLen); pos += usernameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Username"); } else { if ((nullBits & 1) != 0) { - usernameOffset = buffer.getIntLE(offset + 50); - if (usernameOffset < 0) { + v = buffer.getIntLE(offset + 50); + if (v < 0 || v > buffer.writerIndex() - offset - 66) { return ValidationResult.error("Invalid offset for IdentityToken"); } - pos = offset + 66 + usernameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for IdentityToken"); - } - + pos = offset + 66 + v; usernameLen = VarInt.peek(buffer, pos); if (usernameLen < 0) { return ValidationResult.error("Invalid string length for IdentityToken"); @@ -346,85 +405,75 @@ public class Connect implements Packet, ToServerPacket { return ValidationResult.error("IdentityToken exceeds max length 8192"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(usernameLen); pos += usernameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading IdentityToken"); } } - usernameOffset = buffer.getIntLE(offset + 54); - if (usernameOffset < 0) { - return ValidationResult.error("Invalid offset for Language"); - } else { - pos = offset + 66 + usernameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Language"); + v = buffer.getIntLE(offset + 54); + if (v >= 0 && v <= buffer.writerIndex() - offset - 66) { + pos = offset + 66 + v; + usernameLen = VarInt.peek(buffer, pos); + if (usernameLen < 0) { + return ValidationResult.error("Invalid string length for Language"); + } else if (usernameLen > 16) { + return ValidationResult.error("Language exceeds max length 16"); } else { - usernameLen = VarInt.peek(buffer, pos); - if (usernameLen < 0) { - return ValidationResult.error("Invalid string length for Language"); - } else if (usernameLen > 16) { - return ValidationResult.error("Language exceeds max length 16"); + pos += VarInt.size(usernameLen); + pos += usernameLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Language"); } else { - pos += VarInt.length(buffer, pos); - pos += usernameLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Language"); - } else { - if ((nullBits & 2) != 0) { - usernameOffset = buffer.getIntLE(offset + 58); - if (usernameOffset < 0) { - return ValidationResult.error("Invalid offset for ReferralData"); - } - - pos = offset + 66 + usernameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ReferralData"); - } - - usernameLen = VarInt.peek(buffer, pos); - if (usernameLen < 0) { - return ValidationResult.error("Invalid array count for ReferralData"); - } - - if (usernameLen > 4096) { - return ValidationResult.error("ReferralData exceeds max length 4096"); - } - - pos += VarInt.length(buffer, pos); - pos += usernameLen * 1; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ReferralData"); - } + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 58); + if (v < 0 || v > buffer.writerIndex() - offset - 66) { + return ValidationResult.error("Invalid offset for ReferralData"); } - if ((nullBits & 4) != 0) { - usernameOffset = buffer.getIntLE(offset + 62); - if (usernameOffset < 0) { - return ValidationResult.error("Invalid offset for ReferralSource"); - } - - pos = offset + 66 + usernameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ReferralSource"); - } - - ValidationResult referralSourceResult = HostAddress.validateStructure(buffer, pos); - if (!referralSourceResult.isValid()) { - return ValidationResult.error("Invalid ReferralSource: " + referralSourceResult.error()); - } - - pos += HostAddress.computeBytesConsumed(buffer, pos); + pos = offset + 66 + v; + usernameLen = VarInt.peek(buffer, pos); + if (usernameLen < 0) { + return ValidationResult.error("Invalid array count for ReferralData"); } - return ValidationResult.OK; + if (usernameLen > 4096) { + return ValidationResult.error("ReferralData exceeds max length 4096"); + } + + pos += VarInt.size(usernameLen); + pos += usernameLen * 1; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ReferralData"); + } } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 62); + if (v < 0 || v > buffer.writerIndex() - offset - 66) { + return ValidationResult.error("Invalid offset for ReferralSource"); + } + + pos = offset + 66 + v; + ValidationResult referralSourceResult = HostAddress.validateStructure(buffer, pos); + if (!referralSourceResult.isValid()) { + return ValidationResult.error("Invalid ReferralSource: " + referralSourceResult.error()); + } + + pos += HostAddress.computeBytesConsumed(buffer, pos); + } + + return ValidationResult.OK; } } + } else { + return ValidationResult.error("Invalid offset for Language"); } } } + } else { + return ValidationResult.error("Invalid offset for Username"); } } } diff --git a/src/com/hypixel/hytale/protocol/packets/connection/Ping.java b/src/com/hypixel/hytale/protocol/packets/connection/Ping.java index 505dc750..d157cd9c 100644 --- a/src/com/hypixel/hytale/protocol/packets/connection/Ping.java +++ b/src/com/hypixel/hytale/protocol/packets/connection/Ping.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.InstantData; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -11,7 +12,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class Ping implements Packet, ToClientPacket { - public static final int PACKET_ID = 2; + public static final int PACKET_ID = 3; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 29; @@ -27,7 +28,7 @@ public class Ping implements Packet, ToClientPacket { @Override public int getId() { - return 2; + return 3; } @Override @@ -56,17 +57,21 @@ public class Ping implements Packet, ToClientPacket { @Nonnull public static Ping deserialize(@Nonnull ByteBuf buf, int offset) { - Ping obj = new Ping(); - byte nullBits = buf.getByte(offset); - obj.id = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.time = InstantData.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 29) { + throw ProtocolException.bufferTooSmall("Ping", 29, buf.readableBytes() - offset); + } else { + Ping obj = new Ping(); + byte nullBits = buf.getByte(offset); + obj.id = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.time = InstantData.deserialize(buf, offset + 5); + } - obj.lastPingValueRaw = buf.getIntLE(offset + 17); - obj.lastPingValueDirect = buf.getIntLE(offset + 21); - obj.lastPingValueTick = buf.getIntLE(offset + 25); - return obj; + obj.lastPingValueRaw = buf.getIntLE(offset + 17); + obj.lastPingValueDirect = buf.getIntLE(offset + 21); + obj.lastPingValueTick = buf.getIntLE(offset + 25); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -99,7 +104,12 @@ public class Ping implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 29 ? ValidationResult.error("Buffer too small: expected at least 29 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 29) { + return ValidationResult.error("Buffer too small: expected at least 29 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public Ping clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/connection/Pong.java b/src/com/hypixel/hytale/protocol/packets/connection/Pong.java index 45d7882c..f39527b4 100644 --- a/src/com/hypixel/hytale/protocol/packets/connection/Pong.java +++ b/src/com/hypixel/hytale/protocol/packets/connection/Pong.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.InstantData; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -11,7 +12,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class Pong implements Packet, ToServerPacket { - public static final int PACKET_ID = 3; + public static final int PACKET_ID = 4; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 20; @@ -27,7 +28,7 @@ public class Pong implements Packet, ToServerPacket { @Override public int getId() { - return 3; + return 4; } @Override @@ -54,16 +55,20 @@ public class Pong implements Packet, ToServerPacket { @Nonnull public static Pong deserialize(@Nonnull ByteBuf buf, int offset) { - Pong obj = new Pong(); - byte nullBits = buf.getByte(offset); - obj.id = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - obj.time = InstantData.deserialize(buf, offset + 5); - } + if (buf.readableBytes() - offset < 20) { + throw ProtocolException.bufferTooSmall("Pong", 20, buf.readableBytes() - offset); + } else { + Pong obj = new Pong(); + byte nullBits = buf.getByte(offset); + obj.id = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + obj.time = InstantData.deserialize(buf, offset + 5); + } - obj.type = PongType.fromValue(buf.getByte(offset + 17)); - obj.packetQueueSize = buf.getShortLE(offset + 18); - return obj; + obj.type = PongType.fromValue(buf.getByte(offset + 17)); + obj.packetQueueSize = buf.getShortLE(offset + 18); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -95,7 +100,13 @@ public class Pong implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 20 ? ValidationResult.error("Buffer too small: expected at least 20 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 20) { + return ValidationResult.error("Buffer too small: expected at least 20 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 17) & 255; + return v >= 3 ? ValidationResult.error("Invalid PongType value for Type") : ValidationResult.OK; + } } public Pong clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/connection/QuicApplicationErrorCode.java b/src/com/hypixel/hytale/protocol/packets/connection/QuicApplicationErrorCode.java new file mode 100644 index 00000000..01477a60 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/connection/QuicApplicationErrorCode.java @@ -0,0 +1,32 @@ +package com.hypixel.hytale.protocol.packets.connection; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum QuicApplicationErrorCode { + NoError(0), + RateLimited(1), + AuthFailed(2), + InvalidVersion(3), + Timeout(4), + ClientOutdated(5), + ServerOutdated(6); + + public static final QuicApplicationErrorCode[] VALUES = values(); + private final int value; + + private QuicApplicationErrorCode(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static QuicApplicationErrorCode fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("QuicApplicationErrorCode", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/connection/Disconnect.java b/src/com/hypixel/hytale/protocol/packets/connection/ServerDisconnect.java similarity index 51% rename from src/com/hypixel/hytale/protocol/packets/connection/Disconnect.java rename to src/com/hypixel/hytale/protocol/packets/connection/ServerDisconnect.java index 3b6c0afb..c390663f 100644 --- a/src/com/hypixel/hytale/protocol/packets/connection/Disconnect.java +++ b/src/com/hypixel/hytale/protocol/packets/connection/ServerDisconnect.java @@ -1,34 +1,32 @@ package com.hypixel.hytale.protocol.packets.connection; +import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; -import com.hypixel.hytale.protocol.ToServerPacket; -import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; -import com.hypixel.hytale.protocol.io.VarInt; import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class Disconnect implements Packet, ToServerPacket, ToClientPacket { - public static final int PACKET_ID = 1; +public class ServerDisconnect implements Packet, ToClientPacket { + public static final int PACKET_ID = 2; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 2; public static final int VARIABLE_FIELD_COUNT = 1; public static final int VARIABLE_BLOCK_START = 2; - public static final int MAX_SIZE = 16384007; + public static final int MAX_SIZE = 1677721600; @Nullable - public String reason; + public FormattedMessage reason; @Nonnull public DisconnectType type = DisconnectType.Disconnect; @Override public int getId() { - return 1; + return 2; } @Override @@ -36,49 +34,42 @@ public class Disconnect implements Packet, ToServerPacket, ToClientPacket { return NetworkChannel.Default; } - public Disconnect() { + public ServerDisconnect() { } - public Disconnect(@Nullable String reason, @Nonnull DisconnectType type) { + public ServerDisconnect(@Nullable FormattedMessage reason, @Nonnull DisconnectType type) { this.reason = reason; this.type = type; } - public Disconnect(@Nonnull Disconnect other) { + public ServerDisconnect(@Nonnull ServerDisconnect other) { this.reason = other.reason; this.type = other.type; } @Nonnull - public static Disconnect deserialize(@Nonnull ByteBuf buf, int offset) { - Disconnect obj = new Disconnect(); - byte nullBits = buf.getByte(offset); - obj.type = DisconnectType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int reasonLen = VarInt.peek(buf, pos); - if (reasonLen < 0) { - throw ProtocolException.negativeLength("Reason", reasonLen); + public static ServerDisconnect deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("ServerDisconnect", 2, buf.readableBytes() - offset); + } else { + ServerDisconnect obj = new ServerDisconnect(); + byte nullBits = buf.getByte(offset); + obj.type = DisconnectType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + obj.reason = FormattedMessage.deserialize(buf, pos); + pos += FormattedMessage.computeBytesConsumed(buf, pos); } - if (reasonLen > 4096000) { - throw ProtocolException.stringTooLong("Reason", reasonLen, 4096000); - } - - int reasonVarLen = VarInt.length(buf, pos); - obj.reason = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += reasonVarLen + reasonLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); int pos = offset + 2; if ((nullBits & 1) != 0) { - int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += FormattedMessage.computeBytesConsumed(buf, pos); } return pos - offset; @@ -94,7 +85,7 @@ public class Disconnect implements Packet, ToServerPacket, ToClientPacket { buf.writeByte(nullBits); buf.writeByte(this.type.getValue()); if (this.reason != null) { - PacketIO.writeVarString(buf, this.reason, 4096000); + this.reason.serialize(buf); } } @@ -102,7 +93,7 @@ public class Disconnect implements Packet, ToServerPacket, ToClientPacket { public int computeSize() { int size = 2; if (this.reason != null) { - size += PacketIO.stringSize(this.reason); + size += this.reason.computeSize(); } return size; @@ -113,31 +104,28 @@ public class Disconnect implements Packet, ToServerPacket, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int reasonLen = VarInt.peek(buffer, pos); - if (reasonLen < 0) { - return ValidationResult.error("Invalid string length for Reason"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid DisconnectType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + ValidationResult reasonResult = FormattedMessage.validateStructure(buffer, v); + if (!reasonResult.isValid()) { + return ValidationResult.error("Invalid Reason: " + reasonResult.error()); + } + + v += FormattedMessage.computeBytesConsumed(buffer, v); } - if (reasonLen > 4096000) { - return ValidationResult.error("Reason exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += reasonLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Reason"); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } - public Disconnect clone() { - Disconnect copy = new Disconnect(); - copy.reason = this.reason; + public ServerDisconnect clone() { + ServerDisconnect copy = new ServerDisconnect(); + copy.reason = this.reason != null ? this.reason.clone() : null; copy.type = this.type; return copy; } @@ -147,7 +135,7 @@ public class Disconnect implements Packet, ToServerPacket, ToClientPacket { if (this == obj) { return true; } else { - return !(obj instanceof Disconnect other) ? false : Objects.equals(this.reason, other.reason) && Objects.equals(this.type, other.type); + return !(obj instanceof ServerDisconnect other) ? false : Objects.equals(this.reason, other.reason) && Objects.equals(this.type, other.type); } } diff --git a/src/com/hypixel/hytale/protocol/packets/entities/ApplyKnockback.java b/src/com/hypixel/hytale/protocol/packets/entities/ApplyKnockback.java index 0527c651..d8b66a20 100644 --- a/src/com/hypixel/hytale/protocol/packets/entities/ApplyKnockback.java +++ b/src/com/hypixel/hytale/protocol/packets/entities/ApplyKnockback.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -58,17 +59,21 @@ public class ApplyKnockback implements Packet, ToClientPacket { @Nonnull public static ApplyKnockback deserialize(@Nonnull ByteBuf buf, int offset) { - ApplyKnockback obj = new ApplyKnockback(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.hitPosition = Position.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 38) { + throw ProtocolException.bufferTooSmall("ApplyKnockback", 38, buf.readableBytes() - offset); + } else { + ApplyKnockback obj = new ApplyKnockback(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.hitPosition = Position.deserialize(buf, offset + 1); + } - obj.x = buf.getFloatLE(offset + 25); - obj.y = buf.getFloatLE(offset + 29); - obj.z = buf.getFloatLE(offset + 33); - obj.changeType = ChangeVelocityType.fromValue(buf.getByte(offset + 37)); - return obj; + obj.x = buf.getFloatLE(offset + 25); + obj.y = buf.getFloatLE(offset + 29); + obj.z = buf.getFloatLE(offset + 33); + obj.changeType = ChangeVelocityType.fromValue(buf.getByte(offset + 37)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,7 +106,13 @@ public class ApplyKnockback implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 38 ? ValidationResult.error("Buffer too small: expected at least 38 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 38) { + return ValidationResult.error("Buffer too small: expected at least 38 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 37) & 255; + return v >= 2 ? ValidationResult.error("Invalid ChangeVelocityType value for ChangeType") : ValidationResult.OK; + } } public ApplyKnockback clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/entities/ChangeVelocity.java b/src/com/hypixel/hytale/protocol/packets/entities/ChangeVelocity.java index c315653c..8a477df8 100644 --- a/src/com/hypixel/hytale/protocol/packets/entities/ChangeVelocity.java +++ b/src/com/hypixel/hytale/protocol/packets/entities/ChangeVelocity.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.VelocityConfig; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -58,17 +59,21 @@ public class ChangeVelocity implements Packet, ToClientPacket { @Nonnull public static ChangeVelocity deserialize(@Nonnull ByteBuf buf, int offset) { - ChangeVelocity obj = new ChangeVelocity(); - byte nullBits = buf.getByte(offset); - obj.x = buf.getFloatLE(offset + 1); - obj.y = buf.getFloatLE(offset + 5); - obj.z = buf.getFloatLE(offset + 9); - obj.changeType = ChangeVelocityType.fromValue(buf.getByte(offset + 13)); - if ((nullBits & 1) != 0) { - obj.config = VelocityConfig.deserialize(buf, offset + 14); - } + if (buf.readableBytes() - offset < 35) { + throw ProtocolException.bufferTooSmall("ChangeVelocity", 35, buf.readableBytes() - offset); + } else { + ChangeVelocity obj = new ChangeVelocity(); + byte nullBits = buf.getByte(offset); + obj.x = buf.getFloatLE(offset + 1); + obj.y = buf.getFloatLE(offset + 5); + obj.z = buf.getFloatLE(offset + 9); + obj.changeType = ChangeVelocityType.fromValue(buf.getByte(offset + 13)); + if ((nullBits & 1) != 0) { + obj.config = VelocityConfig.deserialize(buf, offset + 14); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -100,7 +105,13 @@ public class ChangeVelocity implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 35 ? ValidationResult.error("Buffer too small: expected at least 35 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 35) { + return ValidationResult.error("Buffer too small: expected at least 35 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 13) & 255; + return v >= 2 ? ValidationResult.error("Invalid ChangeVelocityType value for ChangeType") : ValidationResult.OK; + } } public ChangeVelocity clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/entities/EntityUpdates.java b/src/com/hypixel/hytale/protocol/packets/entities/EntityUpdates.java index 1d9d42cc..d727792f 100644 --- a/src/com/hypixel/hytale/protocol/packets/entities/EntityUpdates.java +++ b/src/com/hypixel/hytale/protocol/packets/entities/EntityUpdates.java @@ -50,57 +50,71 @@ public class EntityUpdates implements Packet, ToClientPacket { @Nonnull public static EntityUpdates deserialize(@Nonnull ByteBuf buf, int offset) { - EntityUpdates obj = new EntityUpdates(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 9 + buf.getIntLE(offset + 1); - int removedCount = VarInt.peek(buf, varPos0); - if (removedCount < 0) { - throw ProtocolException.negativeLength("Removed", removedCount); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("EntityUpdates", 9, buf.readableBytes() - offset); + } else { + EntityUpdates obj = new EntityUpdates(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Removed", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int removedCount = VarInt.peek(buf, varPos0); + if (removedCount < 0) { + throw ProtocolException.invalidVarInt("Removed"); + } + + int varIntLen = VarInt.size(removedCount); + if (removedCount > 4096000) { + throw ProtocolException.arrayTooLong("Removed", removedCount, 4096000); + } + + if (varPos0 + varIntLen + removedCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Removed", varPos0 + varIntLen + removedCount * 4, buf.readableBytes()); + } + + obj.removed = new int[removedCount]; + + for (int i = 0; i < removedCount; i++) { + obj.removed[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); + } } - if (removedCount > 4096000) { - throw ProtocolException.arrayTooLong("Removed", removedCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Updates", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int updatesCount = VarInt.peek(buf, varPos1); + if (updatesCount < 0) { + throw ProtocolException.invalidVarInt("Updates"); + } + + int varIntLenx = VarInt.size(updatesCount); + if (updatesCount > 4096000) { + throw ProtocolException.arrayTooLong("Updates", updatesCount, 4096000); + } + + if (varPos1 + varIntLenx + updatesCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Updates", varPos1 + varIntLenx + updatesCount * 5, buf.readableBytes()); + } + + obj.updates = new EntityUpdate[updatesCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < updatesCount; i++) { + obj.updates[i] = EntityUpdate.deserialize(buf, elemPos); + elemPos += EntityUpdate.computeBytesConsumed(buf, elemPos); + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + removedCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Removed", varPos0 + varIntLen + removedCount * 4, buf.readableBytes()); - } - - obj.removed = new int[removedCount]; - - for (int i = 0; i < removedCount; i++) { - obj.removed[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 9 + buf.getIntLE(offset + 5); - int updatesCount = VarInt.peek(buf, varPos1); - if (updatesCount < 0) { - throw ProtocolException.negativeLength("Updates", updatesCount); - } - - if (updatesCount > 4096000) { - throw ProtocolException.arrayTooLong("Updates", updatesCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + updatesCount * 5L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Updates", varPos1 + varIntLen + updatesCount * 5, buf.readableBytes()); - } - - obj.updates = new EntityUpdate[updatesCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < updatesCount; i++) { - obj.updates[i] = EntityUpdate.deserialize(buf, elemPos); - elemPos += EntityUpdate.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -108,9 +122,13 @@ public class EntityUpdates implements Packet, ToClientPacket { int maxEnd = 9; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Removed", fieldOffset0, maxEnd); + } + int pos0 = offset + 9 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 4; + pos0 += VarInt.size(arrLen) + arrLen * 4; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -118,9 +136,13 @@ public class EntityUpdates implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Updates", fieldOffset1, maxEnd); + } + int pos1 = offset + 9 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += EntityUpdate.computeBytesConsumed(buf, pos1); @@ -210,15 +232,11 @@ public class EntityUpdates implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int removedOffset = buffer.getIntLE(offset + 1); - if (removedOffset < 0) { + if (removedOffset < 0 || removedOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Removed"); } int pos = offset + 9 + removedOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Removed"); - } - int removedCount = VarInt.peek(buffer, pos); if (removedCount < 0) { return ValidationResult.error("Invalid array count for Removed"); @@ -228,7 +246,7 @@ public class EntityUpdates implements Packet, ToClientPacket { return ValidationResult.error("Removed exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(removedCount); pos += removedCount * 4; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Removed"); @@ -237,15 +255,11 @@ public class EntityUpdates implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int updatesOffset = buffer.getIntLE(offset + 5); - if (updatesOffset < 0) { + if (updatesOffset < 0 || updatesOffset > buffer.writerIndex() - offset - 9) { return ValidationResult.error("Invalid offset for Updates"); } int posx = offset + 9 + updatesOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Updates"); - } - int updatesCount = VarInt.peek(buffer, posx); if (updatesCount < 0) { return ValidationResult.error("Invalid array count for Updates"); @@ -255,7 +269,7 @@ public class EntityUpdates implements Packet, ToClientPacket { return ValidationResult.error("Updates exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(updatesCount); for (int i = 0; i < updatesCount; i++) { ValidationResult structResult = EntityUpdate.validateStructure(buffer, posx); diff --git a/src/com/hypixel/hytale/protocol/packets/entities/MountMovement.java b/src/com/hypixel/hytale/protocol/packets/entities/MountMovement.java index 3a2ce771..da51c77a 100644 --- a/src/com/hypixel/hytale/protocol/packets/entities/MountMovement.java +++ b/src/com/hypixel/hytale/protocol/packets/entities/MountMovement.java @@ -6,6 +6,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -16,10 +17,10 @@ public class MountMovement implements Packet, ToServerPacket { public static final int PACKET_ID = 166; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 59; + public static final int FIXED_BLOCK_SIZE = 60; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 59; - public static final int MAX_SIZE = 59; + public static final int VARIABLE_BLOCK_START = 60; + public static final int MAX_SIZE = 60; @Nullable public Position absolutePosition; @Nullable @@ -54,25 +55,29 @@ public class MountMovement implements Packet, ToServerPacket { @Nonnull public static MountMovement deserialize(@Nonnull ByteBuf buf, int offset) { - MountMovement obj = new MountMovement(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.absolutePosition = Position.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 60) { + throw ProtocolException.bufferTooSmall("MountMovement", 60, buf.readableBytes() - offset); + } else { + MountMovement obj = new MountMovement(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.absolutePosition = Position.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.bodyOrientation = Direction.deserialize(buf, offset + 25); - } + if ((nullBits & 2) != 0) { + obj.bodyOrientation = Direction.deserialize(buf, offset + 25); + } - if ((nullBits & 4) != 0) { - obj.movementStates = MovementStates.deserialize(buf, offset + 37); - } + if ((nullBits & 4) != 0) { + obj.movementStates = MovementStates.deserialize(buf, offset + 37); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 59; + return 60; } @Override @@ -106,17 +111,22 @@ public class MountMovement implements Packet, ToServerPacket { if (this.movementStates != null) { this.movementStates.serialize(buf); } else { - buf.writeZero(22); + buf.writeZero(23); } } @Override public int computeSize() { - return 59; + return 60; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 59 ? ValidationResult.error("Buffer too small: expected at least 59 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 60) { + return ValidationResult.error("Buffer too small: expected at least 60 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public MountMovement clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/entities/PlayAnimation.java b/src/com/hypixel/hytale/protocol/packets/entities/PlayAnimation.java index ed23390c..e3e536d4 100644 --- a/src/com/hypixel/hytale/protocol/packets/entities/PlayAnimation.java +++ b/src/com/hypixel/hytale/protocol/packets/entities/PlayAnimation.java @@ -58,39 +58,63 @@ public class PlayAnimation implements Packet, ToClientPacket { @Nonnull public static PlayAnimation deserialize(@Nonnull ByteBuf buf, int offset) { - PlayAnimation obj = new PlayAnimation(); - byte nullBits = buf.getByte(offset); - obj.entityId = buf.getIntLE(offset + 1); - obj.slot = AnimationSlot.fromValue(buf.getByte(offset + 5)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 14 + buf.getIntLE(offset + 6); - int itemAnimationsIdLen = VarInt.peek(buf, varPos0); - if (itemAnimationsIdLen < 0) { - throw ProtocolException.negativeLength("ItemAnimationsId", itemAnimationsIdLen); + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("PlayAnimation", 14, buf.readableBytes() - offset); + } else { + PlayAnimation obj = new PlayAnimation(); + byte nullBits = buf.getByte(offset); + obj.entityId = buf.getIntLE(offset + 1); + obj.slot = AnimationSlot.fromValue(buf.getByte(offset + 5)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("ItemAnimationsId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 14 + varPosBase0; + int itemAnimationsIdLen = VarInt.peek(buf, varPos0); + if (itemAnimationsIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemAnimationsId"); + } + + int itemAnimationsIdVarIntLen = VarInt.size(itemAnimationsIdLen); + if (itemAnimationsIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemAnimationsId", itemAnimationsIdLen, 4096000); + } + + if (varPos0 + itemAnimationsIdVarIntLen + itemAnimationsIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemAnimationsId", varPos0 + itemAnimationsIdVarIntLen + itemAnimationsIdLen, buf.readableBytes()); + } + + obj.itemAnimationsId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (itemAnimationsIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemAnimationsId", itemAnimationsIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 10); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("AnimationId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 14 + varPosBase1; + int animationIdLen = VarInt.peek(buf, varPos1); + if (animationIdLen < 0) { + throw ProtocolException.invalidVarInt("AnimationId"); + } + + int animationIdVarIntLen = VarInt.size(animationIdLen); + if (animationIdLen > 4096000) { + throw ProtocolException.stringTooLong("AnimationId", animationIdLen, 4096000); + } + + if (varPos1 + animationIdVarIntLen + animationIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AnimationId", varPos1 + animationIdVarIntLen + animationIdLen, buf.readableBytes()); + } + + obj.animationId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.itemAnimationsId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 14 + buf.getIntLE(offset + 10); - int animationIdLen = VarInt.peek(buf, varPos1); - if (animationIdLen < 0) { - throw ProtocolException.negativeLength("AnimationId", animationIdLen); - } - - if (animationIdLen > 4096000) { - throw ProtocolException.stringTooLong("AnimationId", animationIdLen, 4096000); - } - - obj.animationId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -98,9 +122,13 @@ public class PlayAnimation implements Packet, ToClientPacket { int maxEnd = 14; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("ItemAnimationsId", fieldOffset0, maxEnd); + } + int pos0 = offset + 14 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -108,9 +136,13 @@ public class PlayAnimation implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 10); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("AnimationId", fieldOffset1, maxEnd); + } + int pos1 = offset + 14 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -173,61 +205,58 @@ public class PlayAnimation implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 14 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int itemAnimationsIdOffset = buffer.getIntLE(offset + 6); - if (itemAnimationsIdOffset < 0) { - return ValidationResult.error("Invalid offset for ItemAnimationsId"); + int v = buffer.getByte(offset + 5) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid AnimationSlot value for Slot"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for ItemAnimationsId"); + } + + int pos = offset + 14 + v; + int itemAnimationsIdLen = VarInt.peek(buffer, pos); + if (itemAnimationsIdLen < 0) { + return ValidationResult.error("Invalid string length for ItemAnimationsId"); + } + + if (itemAnimationsIdLen > 4096000) { + return ValidationResult.error("ItemAnimationsId exceeds max length 4096000"); + } + + pos += VarInt.size(itemAnimationsIdLen); + pos += itemAnimationsIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ItemAnimationsId"); + } } - int pos = offset + 14 + itemAnimationsIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemAnimationsId"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for AnimationId"); + } + + int posx = offset + 14 + v; + int animationIdLen = VarInt.peek(buffer, posx); + if (animationIdLen < 0) { + return ValidationResult.error("Invalid string length for AnimationId"); + } + + if (animationIdLen > 4096000) { + return ValidationResult.error("AnimationId exceeds max length 4096000"); + } + + posx += VarInt.size(animationIdLen); + posx += animationIdLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading AnimationId"); + } } - int itemAnimationsIdLen = VarInt.peek(buffer, pos); - if (itemAnimationsIdLen < 0) { - return ValidationResult.error("Invalid string length for ItemAnimationsId"); - } - - if (itemAnimationsIdLen > 4096000) { - return ValidationResult.error("ItemAnimationsId exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += itemAnimationsIdLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ItemAnimationsId"); - } + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int animationIdOffset = buffer.getIntLE(offset + 10); - if (animationIdOffset < 0) { - return ValidationResult.error("Invalid offset for AnimationId"); - } - - int posx = offset + 14 + animationIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AnimationId"); - } - - int animationIdLen = VarInt.peek(buffer, posx); - if (animationIdLen < 0) { - return ValidationResult.error("Invalid string length for AnimationId"); - } - - if (animationIdLen > 4096000) { - return ValidationResult.error("AnimationId exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += animationIdLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading AnimationId"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/entities/PlayEmote.java b/src/com/hypixel/hytale/protocol/packets/entities/PlayEmote.java new file mode 100644 index 00000000..629c6be6 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/entities/PlayEmote.java @@ -0,0 +1,158 @@ +package com.hypixel.hytale.protocol.packets.entities; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class PlayEmote implements Packet, ToServerPacket { + public static final int PACKET_ID = 167; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 1; + public static final int MAX_SIZE = 16384006; + @Nullable + public String emoteId; + + @Override + public int getId() { + return 167; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public PlayEmote() { + } + + public PlayEmote(@Nullable String emoteId) { + this.emoteId = emoteId; + } + + public PlayEmote(@Nonnull PlayEmote other) { + this.emoteId = other.emoteId; + } + + @Nonnull + public static PlayEmote deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("PlayEmote", 1, buf.readableBytes() - offset); + } else { + PlayEmote obj = new PlayEmote(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int emoteIdLen = VarInt.peek(buf, pos); + if (emoteIdLen < 0) { + throw ProtocolException.invalidVarInt("EmoteId"); + } + + int emoteIdVarLen = VarInt.size(emoteIdLen); + if (emoteIdLen > 4096000) { + throw ProtocolException.stringTooLong("EmoteId", emoteIdLen, 4096000); + } + + if (pos + emoteIdVarLen + emoteIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EmoteId", pos + emoteIdVarLen + emoteIdLen, buf.readableBytes()); + } + + obj.emoteId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += emoteIdVarLen + emoteIdLen; + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.emoteId != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + if (this.emoteId != null) { + PacketIO.writeVarString(buf, this.emoteId, 4096000); + } + } + + @Override + public int computeSize() { + int size = 1; + if (this.emoteId != null) { + size += PacketIO.stringSize(this.emoteId); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int emoteIdLen = VarInt.peek(buffer, pos); + if (emoteIdLen < 0) { + return ValidationResult.error("Invalid string length for EmoteId"); + } + + if (emoteIdLen > 4096000) { + return ValidationResult.error("EmoteId exceeds max length 4096000"); + } + + pos += VarInt.size(emoteIdLen); + pos += emoteIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading EmoteId"); + } + } + + return ValidationResult.OK; + } + } + + public PlayEmote clone() { + PlayEmote copy = new PlayEmote(); + copy.emoteId = this.emoteId; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof PlayEmote other ? Objects.equals(this.emoteId, other.emoteId) : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.emoteId); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/entities/SetEntitySeed.java b/src/com/hypixel/hytale/protocol/packets/entities/SetEntitySeed.java index 02b93b96..a78d35c7 100644 --- a/src/com/hypixel/hytale/protocol/packets/entities/SetEntitySeed.java +++ b/src/com/hypixel/hytale/protocol/packets/entities/SetEntitySeed.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.entities; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class SetEntitySeed implements Packet, ToClientPacket { @Nonnull public static SetEntitySeed deserialize(@Nonnull ByteBuf buf, int offset) { - SetEntitySeed obj = new SetEntitySeed(); - obj.entitySeed = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("SetEntitySeed", 4, buf.readableBytes() - offset); + } else { + SetEntitySeed obj = new SetEntitySeed(); + obj.entitySeed = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/entities/SpawnModelParticles.java b/src/com/hypixel/hytale/protocol/packets/entities/SpawnModelParticles.java index 5e60745c..3af7d4cc 100644 --- a/src/com/hypixel/hytale/protocol/packets/entities/SpawnModelParticles.java +++ b/src/com/hypixel/hytale/protocol/packets/entities/SpawnModelParticles.java @@ -49,35 +49,39 @@ public class SpawnModelParticles implements Packet, ToClientPacket { @Nonnull public static SpawnModelParticles deserialize(@Nonnull ByteBuf buf, int offset) { - SpawnModelParticles obj = new SpawnModelParticles(); - byte nullBits = buf.getByte(offset); - obj.entityId = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int modelParticlesCount = VarInt.peek(buf, pos); - if (modelParticlesCount < 0) { - throw ProtocolException.negativeLength("ModelParticles", modelParticlesCount); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("SpawnModelParticles", 5, buf.readableBytes() - offset); + } else { + SpawnModelParticles obj = new SpawnModelParticles(); + byte nullBits = buf.getByte(offset); + obj.entityId = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int modelParticlesCount = VarInt.peek(buf, pos); + if (modelParticlesCount < 0) { + throw ProtocolException.invalidVarInt("ModelParticles"); + } + + int modelParticlesVarLen = VarInt.size(modelParticlesCount); + if (modelParticlesCount > 4096000) { + throw ProtocolException.arrayTooLong("ModelParticles", modelParticlesCount, 4096000); + } + + if (pos + modelParticlesVarLen + modelParticlesCount * 34L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ModelParticles", pos + modelParticlesVarLen + modelParticlesCount * 34, buf.readableBytes()); + } + + pos += modelParticlesVarLen; + obj.modelParticles = new ModelParticle[modelParticlesCount]; + + for (int i = 0; i < modelParticlesCount; i++) { + obj.modelParticles[i] = ModelParticle.deserialize(buf, pos); + pos += ModelParticle.computeBytesConsumed(buf, pos); + } } - if (modelParticlesCount > 4096000) { - throw ProtocolException.arrayTooLong("ModelParticles", modelParticlesCount, 4096000); - } - - int modelParticlesVarLen = VarInt.size(modelParticlesCount); - if (pos + modelParticlesVarLen + modelParticlesCount * 34L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ModelParticles", pos + modelParticlesVarLen + modelParticlesCount * 34, buf.readableBytes()); - } - - pos += modelParticlesVarLen; - obj.modelParticles = new ModelParticle[modelParticlesCount]; - - for (int i = 0; i < modelParticlesCount; i++) { - obj.modelParticles[i] = ModelParticle.deserialize(buf, pos); - pos += ModelParticle.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -85,7 +89,7 @@ public class SpawnModelParticles implements Packet, ToClientPacket { int pos = offset + 5; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += ModelParticle.computeBytesConsumed(buf, pos); @@ -149,7 +153,7 @@ public class SpawnModelParticles implements Packet, ToClientPacket { return ValidationResult.error("ModelParticles exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(modelParticlesCount); for (int i = 0; i < modelParticlesCount; i++) { ValidationResult structResult = ModelParticle.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/interaction/CancelInteractionChain.java b/src/com/hypixel/hytale/protocol/packets/interaction/CancelInteractionChain.java index e1755121..7a3e177a 100644 --- a/src/com/hypixel/hytale/protocol/packets/interaction/CancelInteractionChain.java +++ b/src/com/hypixel/hytale/protocol/packets/interaction/CancelInteractionChain.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.ForkedChainId; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,16 +48,20 @@ public class CancelInteractionChain implements Packet, ToClientPacket { @Nonnull public static CancelInteractionChain deserialize(@Nonnull ByteBuf buf, int offset) { - CancelInteractionChain obj = new CancelInteractionChain(); - byte nullBits = buf.getByte(offset); - obj.chainId = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - obj.forkedId = ForkedChainId.deserialize(buf, pos); - pos += ForkedChainId.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("CancelInteractionChain", 5, buf.readableBytes() - offset); + } else { + CancelInteractionChain obj = new CancelInteractionChain(); + byte nullBits = buf.getByte(offset); + obj.chainId = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + obj.forkedId = ForkedChainId.deserialize(buf, pos); + pos += ForkedChainId.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interaction/MountNPC.java b/src/com/hypixel/hytale/protocol/packets/interaction/MountNPC.java index 9e0f9cd7..66af7d38 100644 --- a/src/com/hypixel/hytale/protocol/packets/interaction/MountNPC.java +++ b/src/com/hypixel/hytale/protocol/packets/interaction/MountNPC.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.interaction; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,12 +51,16 @@ public class MountNPC implements Packet, ToClientPacket { @Nonnull public static MountNPC deserialize(@Nonnull ByteBuf buf, int offset) { - MountNPC obj = new MountNPC(); - obj.anchorX = buf.getFloatLE(offset + 0); - obj.anchorY = buf.getFloatLE(offset + 4); - obj.anchorZ = buf.getFloatLE(offset + 8); - obj.entityId = buf.getIntLE(offset + 12); - return obj; + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("MountNPC", 16, buf.readableBytes() - offset); + } else { + MountNPC obj = new MountNPC(); + obj.anchorX = buf.getFloatLE(offset + 0); + obj.anchorY = buf.getFloatLE(offset + 4); + obj.anchorZ = buf.getFloatLE(offset + 8); + obj.entityId = buf.getIntLE(offset + 12); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interaction/PlayInteractionFor.java b/src/com/hypixel/hytale/protocol/packets/interaction/PlayInteractionFor.java index 43bd467e..9d45cb8e 100644 --- a/src/com/hypixel/hytale/protocol/packets/interaction/PlayInteractionFor.java +++ b/src/com/hypixel/hytale/protocol/packets/interaction/PlayInteractionFor.java @@ -80,34 +80,53 @@ public class PlayInteractionFor implements Packet, ToClientPacket { @Nonnull public static PlayInteractionFor deserialize(@Nonnull ByteBuf buf, int offset) { - PlayInteractionFor obj = new PlayInteractionFor(); - byte nullBits = buf.getByte(offset); - obj.entityId = buf.getIntLE(offset + 1); - obj.chainId = buf.getIntLE(offset + 5); - obj.operationIndex = buf.getIntLE(offset + 9); - obj.interactionId = buf.getIntLE(offset + 13); - obj.interactionType = InteractionType.fromValue(buf.getByte(offset + 17)); - obj.cancel = buf.getByte(offset + 18) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 27 + buf.getIntLE(offset + 19); - obj.forkedId = ForkedChainId.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 27) { + throw ProtocolException.bufferTooSmall("PlayInteractionFor", 27, buf.readableBytes() - offset); + } else { + PlayInteractionFor obj = new PlayInteractionFor(); + byte nullBits = buf.getByte(offset); + obj.entityId = buf.getIntLE(offset + 1); + obj.chainId = buf.getIntLE(offset + 5); + obj.operationIndex = buf.getIntLE(offset + 9); + obj.interactionId = buf.getIntLE(offset + 13); + obj.interactionType = InteractionType.fromValue(buf.getByte(offset + 17)); + obj.cancel = buf.getByte(offset + 18) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 19); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("ForkedId", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 27 + buf.getIntLE(offset + 23); - int interactedItemIdLen = VarInt.peek(buf, varPos1); - if (interactedItemIdLen < 0) { - throw ProtocolException.negativeLength("InteractedItemId", interactedItemIdLen); + int varPos0 = offset + 27 + varPosBase0; + obj.forkedId = ForkedChainId.deserialize(buf, varPos0); } - if (interactedItemIdLen > 4096000) { - throw ProtocolException.stringTooLong("InteractedItemId", interactedItemIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 23); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("InteractedItemId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 27 + varPosBase1; + int interactedItemIdLen = VarInt.peek(buf, varPos1); + if (interactedItemIdLen < 0) { + throw ProtocolException.invalidVarInt("InteractedItemId"); + } + + int interactedItemIdVarIntLen = VarInt.size(interactedItemIdLen); + if (interactedItemIdLen > 4096000) { + throw ProtocolException.stringTooLong("InteractedItemId", interactedItemIdLen, 4096000); + } + + if (varPos1 + interactedItemIdVarIntLen + interactedItemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("InteractedItemId", varPos1 + interactedItemIdVarIntLen + interactedItemIdLen, buf.readableBytes()); + } + + obj.interactedItemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.interactedItemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -115,6 +134,10 @@ public class PlayInteractionFor implements Packet, ToClientPacket { int maxEnd = 27; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 19); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("ForkedId", fieldOffset0, maxEnd); + } + int pos0 = offset + 27 + fieldOffset0; pos0 += ForkedChainId.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -124,9 +147,13 @@ public class PlayInteractionFor implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 23); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 27) { + throw ProtocolException.invalidOffset("InteractedItemId", fieldOffset1, maxEnd); + } + int pos1 = offset + 27 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -193,53 +220,50 @@ public class PlayInteractionFor implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 27 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int forkedIdOffset = buffer.getIntLE(offset + 19); - if (forkedIdOffset < 0) { - return ValidationResult.error("Invalid offset for ForkedId"); + int v = buffer.getByte(offset + 17) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for InteractionType"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 19); + if (v < 0 || v > buffer.writerIndex() - offset - 27) { + return ValidationResult.error("Invalid offset for ForkedId"); + } + + int pos = offset + 27 + v; + ValidationResult forkedIdResult = ForkedChainId.validateStructure(buffer, pos); + if (!forkedIdResult.isValid()) { + return ValidationResult.error("Invalid ForkedId: " + forkedIdResult.error()); + } + + pos += ForkedChainId.computeBytesConsumed(buffer, pos); } - int pos = offset + 27 + forkedIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ForkedId"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 27) { + return ValidationResult.error("Invalid offset for InteractedItemId"); + } + + int pos = offset + 27 + v; + int interactedItemIdLen = VarInt.peek(buffer, pos); + if (interactedItemIdLen < 0) { + return ValidationResult.error("Invalid string length for InteractedItemId"); + } + + if (interactedItemIdLen > 4096000) { + return ValidationResult.error("InteractedItemId exceeds max length 4096000"); + } + + pos += VarInt.size(interactedItemIdLen); + pos += interactedItemIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading InteractedItemId"); + } } - ValidationResult forkedIdResult = ForkedChainId.validateStructure(buffer, pos); - if (!forkedIdResult.isValid()) { - return ValidationResult.error("Invalid ForkedId: " + forkedIdResult.error()); - } - - pos += ForkedChainId.computeBytesConsumed(buffer, pos); + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int interactedItemIdOffset = buffer.getIntLE(offset + 23); - if (interactedItemIdOffset < 0) { - return ValidationResult.error("Invalid offset for InteractedItemId"); - } - - int posx = offset + 27 + interactedItemIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for InteractedItemId"); - } - - int interactedItemIdLen = VarInt.peek(buffer, posx); - if (interactedItemIdLen < 0) { - return ValidationResult.error("Invalid string length for InteractedItemId"); - } - - if (interactedItemIdLen > 4096000) { - return ValidationResult.error("InteractedItemId exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += interactedItemIdLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading InteractedItemId"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/interaction/SyncInteractionChain.java b/src/com/hypixel/hytale/protocol/packets/interaction/SyncInteractionChain.java index bd98345b..45800c19 100644 --- a/src/com/hypixel/hytale/protocol/packets/interaction/SyncInteractionChain.java +++ b/src/com/hypixel/hytale/protocol/packets/interaction/SyncInteractionChain.java @@ -115,122 +115,180 @@ public class SyncInteractionChain { @Nonnull public static SyncInteractionChain deserialize(@Nonnull ByteBuf buf, int offset) { - SyncInteractionChain obj = new SyncInteractionChain(); - byte nullBits = buf.getByte(offset); - obj.activeHotbarSlot = buf.getIntLE(offset + 1); - obj.activeUtilitySlot = buf.getIntLE(offset + 5); - obj.activeToolsSlot = buf.getIntLE(offset + 9); - obj.initial = buf.getByte(offset + 13) != 0; - obj.desync = buf.getByte(offset + 14) != 0; - obj.overrideRootInteraction = buf.getIntLE(offset + 15); - obj.interactionType = InteractionType.fromValue(buf.getByte(offset + 19)); - obj.equipSlot = buf.getIntLE(offset + 20); - obj.chainId = buf.getIntLE(offset + 24); - obj.state = InteractionState.fromValue(buf.getByte(offset + 28)); - obj.operationBaseIndex = buf.getIntLE(offset + 29); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 61 + buf.getIntLE(offset + 33); - int itemInHandIdLen = VarInt.peek(buf, varPos0); - if (itemInHandIdLen < 0) { - throw ProtocolException.negativeLength("ItemInHandId", itemInHandIdLen); + if (buf.readableBytes() - offset < 61) { + throw ProtocolException.bufferTooSmall("SyncInteractionChain", 61, buf.readableBytes() - offset); + } else { + SyncInteractionChain obj = new SyncInteractionChain(); + byte nullBits = buf.getByte(offset); + obj.activeHotbarSlot = buf.getIntLE(offset + 1); + obj.activeUtilitySlot = buf.getIntLE(offset + 5); + obj.activeToolsSlot = buf.getIntLE(offset + 9); + obj.initial = buf.getByte(offset + 13) != 0; + obj.desync = buf.getByte(offset + 14) != 0; + obj.overrideRootInteraction = buf.getIntLE(offset + 15); + obj.interactionType = InteractionType.fromValue(buf.getByte(offset + 19)); + obj.equipSlot = buf.getIntLE(offset + 20); + obj.chainId = buf.getIntLE(offset + 24); + obj.state = InteractionState.fromValue(buf.getByte(offset + 28)); + obj.operationBaseIndex = buf.getIntLE(offset + 29); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 33); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("ItemInHandId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 61 + varPosBase0; + int itemInHandIdLen = VarInt.peek(buf, varPos0); + if (itemInHandIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemInHandId"); + } + + int itemInHandIdVarIntLen = VarInt.size(itemInHandIdLen); + if (itemInHandIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemInHandId", itemInHandIdLen, 4096000); + } + + if (varPos0 + itemInHandIdVarIntLen + itemInHandIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemInHandId", varPos0 + itemInHandIdVarIntLen + itemInHandIdLen, buf.readableBytes()); + } + + obj.itemInHandId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (itemInHandIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemInHandId", itemInHandIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 37); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("UtilityItemId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 61 + varPosBase1; + int utilityItemIdLen = VarInt.peek(buf, varPos1); + if (utilityItemIdLen < 0) { + throw ProtocolException.invalidVarInt("UtilityItemId"); + } + + int utilityItemIdVarIntLen = VarInt.size(utilityItemIdLen); + if (utilityItemIdLen > 4096000) { + throw ProtocolException.stringTooLong("UtilityItemId", utilityItemIdLen, 4096000); + } + + if (varPos1 + utilityItemIdVarIntLen + utilityItemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("UtilityItemId", varPos1 + utilityItemIdVarIntLen + utilityItemIdLen, buf.readableBytes()); + } + + obj.utilityItemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.itemInHandId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 41); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("ToolsItemId", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 61 + buf.getIntLE(offset + 37); - int utilityItemIdLen = VarInt.peek(buf, varPos1); - if (utilityItemIdLen < 0) { - throw ProtocolException.negativeLength("UtilityItemId", utilityItemIdLen); + int varPos2 = offset + 61 + varPosBase2; + int toolsItemIdLen = VarInt.peek(buf, varPos2); + if (toolsItemIdLen < 0) { + throw ProtocolException.invalidVarInt("ToolsItemId"); + } + + int toolsItemIdVarIntLen = VarInt.size(toolsItemIdLen); + if (toolsItemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ToolsItemId", toolsItemIdLen, 4096000); + } + + if (varPos2 + toolsItemIdVarIntLen + toolsItemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ToolsItemId", varPos2 + toolsItemIdVarIntLen + toolsItemIdLen, buf.readableBytes()); + } + + obj.toolsItemId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); } - if (utilityItemIdLen > 4096000) { - throw ProtocolException.stringTooLong("UtilityItemId", utilityItemIdLen, 4096000); + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 45); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("ForkedId", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 61 + varPosBase3; + obj.forkedId = ForkedChainId.deserialize(buf, varPos3); } - obj.utilityItemId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 49); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("Data", varPosBase4, buf.readableBytes()); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 61 + buf.getIntLE(offset + 41); - int toolsItemIdLen = VarInt.peek(buf, varPos2); - if (toolsItemIdLen < 0) { - throw ProtocolException.negativeLength("ToolsItemId", toolsItemIdLen); + int varPos4 = offset + 61 + varPosBase4; + obj.data = InteractionChainData.deserialize(buf, varPos4); } - if (toolsItemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ToolsItemId", toolsItemIdLen, 4096000); - } + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 53); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("NewForks", varPosBase5, buf.readableBytes()); + } - obj.toolsItemId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } + int varPos5 = offset + 61 + varPosBase5; + int newForksCount = VarInt.peek(buf, varPos5); + if (newForksCount < 0) { + throw ProtocolException.invalidVarInt("NewForks"); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 61 + buf.getIntLE(offset + 45); - obj.forkedId = ForkedChainId.deserialize(buf, varPos3); - } + int varIntLen = VarInt.size(newForksCount); + if (newForksCount > 4096000) { + throw ProtocolException.arrayTooLong("NewForks", newForksCount, 4096000); + } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 61 + buf.getIntLE(offset + 49); - obj.data = InteractionChainData.deserialize(buf, varPos4); - } + if (varPos5 + varIntLen + newForksCount * 33L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("NewForks", varPos5 + varIntLen + newForksCount * 33, buf.readableBytes()); + } - if ((nullBits & 32) != 0) { - int varPos5 = offset + 61 + buf.getIntLE(offset + 53); - int newForksCount = VarInt.peek(buf, varPos5); - if (newForksCount < 0) { - throw ProtocolException.negativeLength("NewForks", newForksCount); - } + obj.newForks = new SyncInteractionChain[newForksCount]; + int elemPos = varPos5 + varIntLen; - if (newForksCount > 4096000) { - throw ProtocolException.arrayTooLong("NewForks", newForksCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos5); - if (varPos5 + varIntLen + newForksCount * 33L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("NewForks", varPos5 + varIntLen + newForksCount * 33, buf.readableBytes()); - } - - obj.newForks = new SyncInteractionChain[newForksCount]; - int elemPos = varPos5 + varIntLen; - - for (int i = 0; i < newForksCount; i++) { - obj.newForks[i] = deserialize(buf, elemPos); - elemPos += computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 64) != 0) { - int varPos6 = offset + 61 + buf.getIntLE(offset + 57); - int interactionDataCount = VarInt.peek(buf, varPos6); - if (interactionDataCount < 0) { - throw ProtocolException.negativeLength("InteractionData", interactionDataCount); - } - - if (interactionDataCount > 4096000) { - throw ProtocolException.arrayTooLong("InteractionData", interactionDataCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos6); - int interactionDataBitfieldSize = (interactionDataCount + 7) / 8; - byte[] interactionDataBitfield = PacketIO.readBytes(buf, varPos6 + varIntLen, interactionDataBitfieldSize); - obj.interactionData = new InteractionSyncData[interactionDataCount]; - int elemPos = varPos6 + varIntLen + interactionDataBitfieldSize; - - for (int i = 0; i < interactionDataCount; i++) { - if ((interactionDataBitfield[i / 8] & 1 << i % 8) != 0) { - obj.interactionData[i] = InteractionSyncData.deserialize(buf, elemPos); - elemPos += InteractionSyncData.computeBytesConsumed(buf, elemPos); + for (int i = 0; i < newForksCount; i++) { + obj.newForks[i] = deserialize(buf, elemPos); + elemPos += computeBytesConsumed(buf, elemPos); } } - } - return obj; + if ((nullBits & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 57); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("InteractionData", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 61 + varPosBase6; + int interactionDataCount = VarInt.peek(buf, varPos6); + if (interactionDataCount < 0) { + throw ProtocolException.invalidVarInt("InteractionData"); + } + + int varIntLenx = VarInt.size(interactionDataCount); + if (interactionDataCount > 4096000) { + throw ProtocolException.arrayTooLong("InteractionData", interactionDataCount, 4096000); + } + + int interactionDataBitfieldSize = (interactionDataCount + 7) / 8; + if (varPos6 + varIntLenx + interactionDataBitfieldSize > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("InteractionData", varPos6 + varIntLenx + interactionDataBitfieldSize, buf.readableBytes()); + } + + byte[] interactionDataBitfield = PacketIO.readBytes(buf, varPos6 + varIntLenx, interactionDataBitfieldSize); + obj.interactionData = new InteractionSyncData[interactionDataCount]; + int elemPos = varPos6 + varIntLenx + interactionDataBitfieldSize; + + for (int i = 0; i < interactionDataCount; i++) { + if ((interactionDataBitfield[i / 8] & 1 << i % 8) != 0) { + obj.interactionData[i] = InteractionSyncData.deserialize(buf, elemPos); + elemPos += InteractionSyncData.computeBytesConsumed(buf, elemPos); + } + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -238,9 +296,13 @@ public class SyncInteractionChain { int maxEnd = 61; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 33); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("ItemInHandId", fieldOffset0, maxEnd); + } + int pos0 = offset + 61 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -248,9 +310,13 @@ public class SyncInteractionChain { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 37); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("UtilityItemId", fieldOffset1, maxEnd); + } + int pos1 = offset + 61 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -258,9 +324,13 @@ public class SyncInteractionChain { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 41); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("ToolsItemId", fieldOffset2, maxEnd); + } + int pos2 = offset + 61 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -268,6 +338,10 @@ public class SyncInteractionChain { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 45); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("ForkedId", fieldOffset3, maxEnd); + } + int pos3 = offset + 61 + fieldOffset3; pos3 += ForkedChainId.computeBytesConsumed(buf, pos3); if (pos3 - offset > maxEnd) { @@ -277,6 +351,10 @@ public class SyncInteractionChain { if ((nullBits & 16) != 0) { int fieldOffset4 = buf.getIntLE(offset + 49); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("Data", fieldOffset4, maxEnd); + } + int pos4 = offset + 61 + fieldOffset4; pos4 += InteractionChainData.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { @@ -286,9 +364,13 @@ public class SyncInteractionChain { if ((nullBits & 32) != 0) { int fieldOffset5 = buf.getIntLE(offset + 53); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("NewForks", fieldOffset5, maxEnd); + } + int pos5 = offset + 61 + fieldOffset5; int arrLen = VarInt.peek(buf, pos5); - pos5 += VarInt.length(buf, pos5); + pos5 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos5 += computeBytesConsumed(buf, pos5); @@ -301,9 +383,13 @@ public class SyncInteractionChain { if ((nullBits & 64) != 0) { int fieldOffset6 = buf.getIntLE(offset + 57); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 61) { + throw ProtocolException.invalidOffset("InteractionData", fieldOffset6, maxEnd); + } + int pos6 = offset + 61 + fieldOffset6; int arrLen = VarInt.peek(buf, pos6); - pos6 += VarInt.length(buf, pos6); + pos6 += VarInt.size(arrLen); int bitfieldSize = (arrLen + 7) / 8; byte[] bitfield = PacketIO.readBytes(buf, pos6, bitfieldSize); pos6 += bitfieldSize; @@ -510,190 +596,181 @@ public class SyncInteractionChain { return ValidationResult.error("Buffer too small: expected at least 61 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int itemInHandIdOffset = buffer.getIntLE(offset + 33); - if (itemInHandIdOffset < 0) { - return ValidationResult.error("Invalid offset for ItemInHandId"); - } + int v = buffer.getByte(offset + 19) & 255; + if (v >= 25) { + return ValidationResult.error("Invalid InteractionType value for InteractionType"); + } else { + v = buffer.getByte(offset + 28) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid InteractionState value for State"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 33); + if (v < 0 || v > buffer.writerIndex() - offset - 61) { + return ValidationResult.error("Invalid offset for ItemInHandId"); + } - int pos = offset + 61 + itemInHandIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemInHandId"); - } + int pos = offset + 61 + v; + int itemInHandIdLen = VarInt.peek(buffer, pos); + if (itemInHandIdLen < 0) { + return ValidationResult.error("Invalid string length for ItemInHandId"); + } - int itemInHandIdLen = VarInt.peek(buffer, pos); - if (itemInHandIdLen < 0) { - return ValidationResult.error("Invalid string length for ItemInHandId"); - } + if (itemInHandIdLen > 4096000) { + return ValidationResult.error("ItemInHandId exceeds max length 4096000"); + } - if (itemInHandIdLen > 4096000) { - return ValidationResult.error("ItemInHandId exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += itemInHandIdLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ItemInHandId"); - } - } - - if ((nullBits & 2) != 0) { - int utilityItemIdOffset = buffer.getIntLE(offset + 37); - if (utilityItemIdOffset < 0) { - return ValidationResult.error("Invalid offset for UtilityItemId"); - } - - int posx = offset + 61 + utilityItemIdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for UtilityItemId"); - } - - int utilityItemIdLen = VarInt.peek(buffer, posx); - if (utilityItemIdLen < 0) { - return ValidationResult.error("Invalid string length for UtilityItemId"); - } - - if (utilityItemIdLen > 4096000) { - return ValidationResult.error("UtilityItemId exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += utilityItemIdLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading UtilityItemId"); - } - } - - if ((nullBits & 4) != 0) { - int toolsItemIdOffset = buffer.getIntLE(offset + 41); - if (toolsItemIdOffset < 0) { - return ValidationResult.error("Invalid offset for ToolsItemId"); - } - - int posxx = offset + 61 + toolsItemIdOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ToolsItemId"); - } - - int toolsItemIdLen = VarInt.peek(buffer, posxx); - if (toolsItemIdLen < 0) { - return ValidationResult.error("Invalid string length for ToolsItemId"); - } - - if (toolsItemIdLen > 4096000) { - return ValidationResult.error("ToolsItemId exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += toolsItemIdLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading ToolsItemId"); - } - } - - if ((nullBits & 8) != 0) { - int forkedIdOffset = buffer.getIntLE(offset + 45); - if (forkedIdOffset < 0) { - return ValidationResult.error("Invalid offset for ForkedId"); - } - - int posxxx = offset + 61 + forkedIdOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ForkedId"); - } - - ValidationResult forkedIdResult = ForkedChainId.validateStructure(buffer, posxxx); - if (!forkedIdResult.isValid()) { - return ValidationResult.error("Invalid ForkedId: " + forkedIdResult.error()); - } - - posxxx += ForkedChainId.computeBytesConsumed(buffer, posxxx); - } - - if ((nullBits & 16) != 0) { - int dataOffset = buffer.getIntLE(offset + 49); - if (dataOffset < 0) { - return ValidationResult.error("Invalid offset for Data"); - } - - int posxxxx = offset + 61 + dataOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Data"); - } - - ValidationResult dataResult = InteractionChainData.validateStructure(buffer, posxxxx); - if (!dataResult.isValid()) { - return ValidationResult.error("Invalid Data: " + dataResult.error()); - } - - posxxxx += InteractionChainData.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int newForksOffset = buffer.getIntLE(offset + 53); - if (newForksOffset < 0) { - return ValidationResult.error("Invalid offset for NewForks"); - } - - int posxxxxx = offset + 61 + newForksOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for NewForks"); - } - - int newForksCount = VarInt.peek(buffer, posxxxxx); - if (newForksCount < 0) { - return ValidationResult.error("Invalid array count for NewForks"); - } - - if (newForksCount > 4096000) { - return ValidationResult.error("NewForks exceeds max length 4096000"); - } - - posxxxxx += VarInt.length(buffer, posxxxxx); - - for (int i = 0; i < newForksCount; i++) { - ValidationResult structResult = validateStructure(buffer, posxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid SyncInteractionChain in NewForks[" + i + "]: " + structResult.error()); + pos += VarInt.size(itemInHandIdLen); + pos += itemInHandIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ItemInHandId"); + } } - posxxxxx += computeBytesConsumed(buffer, posxxxxx); - } - } + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 37); + if (v < 0 || v > buffer.writerIndex() - offset - 61) { + return ValidationResult.error("Invalid offset for UtilityItemId"); + } - if ((nullBits & 64) != 0) { - int interactionDataOffset = buffer.getIntLE(offset + 57); - if (interactionDataOffset < 0) { - return ValidationResult.error("Invalid offset for InteractionData"); - } + int posx = offset + 61 + v; + int utilityItemIdLen = VarInt.peek(buffer, posx); + if (utilityItemIdLen < 0) { + return ValidationResult.error("Invalid string length for UtilityItemId"); + } - int posxxxxxx = offset + 61 + interactionDataOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for InteractionData"); - } + if (utilityItemIdLen > 4096000) { + return ValidationResult.error("UtilityItemId exceeds max length 4096000"); + } - int interactionDataCount = VarInt.peek(buffer, posxxxxxx); - if (interactionDataCount < 0) { - return ValidationResult.error("Invalid array count for InteractionData"); - } - - if (interactionDataCount > 4096000) { - return ValidationResult.error("InteractionData exceeds max length 4096000"); - } - - posxxxxxx += VarInt.length(buffer, posxxxxxx); - - for (int i = 0; i < interactionDataCount; i++) { - ValidationResult structResult = InteractionSyncData.validateStructure(buffer, posxxxxxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid InteractionSyncData in InteractionData[" + i + "]: " + structResult.error()); + posx += VarInt.size(utilityItemIdLen); + posx += utilityItemIdLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading UtilityItemId"); + } } - posxxxxxx += InteractionSyncData.computeBytesConsumed(buffer, posxxxxxx); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 41); + if (v < 0 || v > buffer.writerIndex() - offset - 61) { + return ValidationResult.error("Invalid offset for ToolsItemId"); + } + + int posxx = offset + 61 + v; + int toolsItemIdLen = VarInt.peek(buffer, posxx); + if (toolsItemIdLen < 0) { + return ValidationResult.error("Invalid string length for ToolsItemId"); + } + + if (toolsItemIdLen > 4096000) { + return ValidationResult.error("ToolsItemId exceeds max length 4096000"); + } + + posxx += VarInt.size(toolsItemIdLen); + posxx += toolsItemIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ToolsItemId"); + } + } + + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 45); + if (v < 0 || v > buffer.writerIndex() - offset - 61) { + return ValidationResult.error("Invalid offset for ForkedId"); + } + + int posxxx = offset + 61 + v; + ValidationResult forkedIdResult = ForkedChainId.validateStructure(buffer, posxxx); + if (!forkedIdResult.isValid()) { + return ValidationResult.error("Invalid ForkedId: " + forkedIdResult.error()); + } + + posxxx += ForkedChainId.computeBytesConsumed(buffer, posxxx); + } + + if ((nullBits & 16) != 0) { + v = buffer.getIntLE(offset + 49); + if (v < 0 || v > buffer.writerIndex() - offset - 61) { + return ValidationResult.error("Invalid offset for Data"); + } + + int posxxx = offset + 61 + v; + ValidationResult dataResult = InteractionChainData.validateStructure(buffer, posxxx); + if (!dataResult.isValid()) { + return ValidationResult.error("Invalid Data: " + dataResult.error()); + } + + posxxx += InteractionChainData.computeBytesConsumed(buffer, posxxx); + } + + if ((nullBits & 32) != 0) { + v = buffer.getIntLE(offset + 53); + if (v < 0 || v > buffer.writerIndex() - offset - 61) { + return ValidationResult.error("Invalid offset for NewForks"); + } + + int posxxx = offset + 61 + v; + int newForksCount = VarInt.peek(buffer, posxxx); + if (newForksCount < 0) { + return ValidationResult.error("Invalid array count for NewForks"); + } + + if (newForksCount > 4096000) { + return ValidationResult.error("NewForks exceeds max length 4096000"); + } + + posxxx += VarInt.size(newForksCount); + + for (int i = 0; i < newForksCount; i++) { + ValidationResult structResult = validateStructure(buffer, posxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid SyncInteractionChain in NewForks[" + i + "]: " + structResult.error()); + } + + posxxx += computeBytesConsumed(buffer, posxxx); + } + } + + if ((nullBits & 64) != 0) { + v = buffer.getIntLE(offset + 57); + if (v < 0 || v > buffer.writerIndex() - offset - 61) { + return ValidationResult.error("Invalid offset for InteractionData"); + } + + int posxxxx = offset + 61 + v; + int interactionDataCount = VarInt.peek(buffer, posxxxx); + if (interactionDataCount < 0) { + return ValidationResult.error("Invalid array count for InteractionData"); + } + + if (interactionDataCount > 4096000) { + return ValidationResult.error("InteractionData exceeds max length 4096000"); + } + + posxxxx += VarInt.size(interactionDataCount); + int interactionDataBitfieldSize = (interactionDataCount + 7) / 8; + if (posxxxx + interactionDataBitfieldSize > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading bitfield for InteractionData"); + } + + byte[] interactionDataBitfield = PacketIO.readBytes(buffer, posxxxx, interactionDataBitfieldSize); + posxxxx += interactionDataBitfieldSize; + + for (int i = 0; i < interactionDataCount; i++) { + if ((interactionDataBitfield[i / 8] & 1 << i % 8) != 0) { + ValidationResult structResult = InteractionSyncData.validateStructure(buffer, posxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid InteractionSyncData in interactionData[" + i + "]: " + structResult.error()); + } + + posxxxx += InteractionSyncData.computeBytesConsumed(buffer, posxxxx); + } + } + } + + return ValidationResult.OK; } } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/interaction/SyncInteractionChains.java b/src/com/hypixel/hytale/protocol/packets/interaction/SyncInteractionChains.java index 55fe6b04..c43c0cb6 100644 --- a/src/com/hypixel/hytale/protocol/packets/interaction/SyncInteractionChains.java +++ b/src/com/hypixel/hytale/protocol/packets/interaction/SyncInteractionChains.java @@ -49,12 +49,12 @@ public class SyncInteractionChains implements Packet, ToServerPacket, ToClientPa int pos = offset + 0; int updatesCount = VarInt.peek(buf, pos); if (updatesCount < 0) { - throw ProtocolException.negativeLength("Updates", updatesCount); - } else if (updatesCount > 4096000) { - throw ProtocolException.arrayTooLong("Updates", updatesCount, 4096000); + throw ProtocolException.invalidVarInt("Updates"); } else { int updatesVarLen = VarInt.size(updatesCount); - if (pos + updatesVarLen + updatesCount * 33L > buf.readableBytes()) { + if (updatesCount > 128) { + throw ProtocolException.arrayTooLong("Updates", updatesCount, 128); + } else if (pos + updatesVarLen + updatesCount * 33L > buf.readableBytes()) { throw ProtocolException.bufferTooSmall("Updates", pos + updatesVarLen + updatesCount * 33, buf.readableBytes()); } else { pos += updatesVarLen; @@ -73,7 +73,7 @@ public class SyncInteractionChains implements Packet, ToServerPacket, ToClientPa public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 0; int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += SyncInteractionChain.computeBytesConsumed(buf, pos); @@ -84,8 +84,8 @@ public class SyncInteractionChains implements Packet, ToServerPacket, ToClientPa @Override public void serialize(@Nonnull ByteBuf buf) { - if (this.updates.length > 4096000) { - throw ProtocolException.arrayTooLong("Updates", this.updates.length, 4096000); + if (this.updates.length > 128) { + throw ProtocolException.arrayTooLong("Updates", this.updates.length, 128); } else { VarInt.write(buf, this.updates.length); @@ -115,10 +115,10 @@ public class SyncInteractionChains implements Packet, ToServerPacket, ToClientPa int updatesCount = VarInt.peek(buffer, pos); if (updatesCount < 0) { return ValidationResult.error("Invalid array count for Updates"); - } else if (updatesCount > 4096000) { - return ValidationResult.error("Updates exceeds max length 4096000"); + } else if (updatesCount > 128) { + return ValidationResult.error("Updates exceeds max length 128"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(updatesCount); for (int i = 0; i < updatesCount; i++) { ValidationResult structResult = SyncInteractionChain.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/AddToServerPlayerList.java b/src/com/hypixel/hytale/protocol/packets/interface_/AddToServerPlayerList.java index a9dd955c..808dd120 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/AddToServerPlayerList.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/AddToServerPlayerList.java @@ -45,34 +45,38 @@ public class AddToServerPlayerList implements Packet, ToClientPacket { @Nonnull public static AddToServerPlayerList deserialize(@Nonnull ByteBuf buf, int offset) { - AddToServerPlayerList obj = new AddToServerPlayerList(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int playersCount = VarInt.peek(buf, pos); - if (playersCount < 0) { - throw ProtocolException.negativeLength("Players", playersCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AddToServerPlayerList", 1, buf.readableBytes() - offset); + } else { + AddToServerPlayerList obj = new AddToServerPlayerList(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int playersCount = VarInt.peek(buf, pos); + if (playersCount < 0) { + throw ProtocolException.invalidVarInt("Players"); + } + + int playersVarLen = VarInt.size(playersCount); + if (playersCount > 4096000) { + throw ProtocolException.arrayTooLong("Players", playersCount, 4096000); + } + + if (pos + playersVarLen + playersCount * 37L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Players", pos + playersVarLen + playersCount * 37, buf.readableBytes()); + } + + pos += playersVarLen; + obj.players = new ServerPlayerListPlayer[playersCount]; + + for (int i = 0; i < playersCount; i++) { + obj.players[i] = ServerPlayerListPlayer.deserialize(buf, pos); + pos += ServerPlayerListPlayer.computeBytesConsumed(buf, pos); + } } - if (playersCount > 4096000) { - throw ProtocolException.arrayTooLong("Players", playersCount, 4096000); - } - - int playersVarLen = VarInt.size(playersCount); - if (pos + playersVarLen + playersCount * 37L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Players", pos + playersVarLen + playersCount * 37, buf.readableBytes()); - } - - pos += playersVarLen; - obj.players = new ServerPlayerListPlayer[playersCount]; - - for (int i = 0; i < playersCount; i++) { - obj.players[i] = ServerPlayerListPlayer.deserialize(buf, pos); - pos += ServerPlayerListPlayer.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class AddToServerPlayerList implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += ServerPlayerListPlayer.computeBytesConsumed(buf, pos); @@ -143,7 +147,7 @@ public class AddToServerPlayerList implements Packet, ToClientPacket { return ValidationResult.error("Players exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(playersCount); for (int i = 0; i < playersCount; i++) { ValidationResult structResult = ServerPlayerListPlayer.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ArgCacheInvalidation.java b/src/com/hypixel/hytale/protocol/packets/interface_/ArgCacheInvalidation.java new file mode 100644 index 00000000..ef151d26 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ArgCacheInvalidation.java @@ -0,0 +1,206 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ArgCacheInvalidation implements Packet, ToClientPacket { + public static final int PACKET_ID = 248; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 1; + public static final int MAX_SIZE = 1677721600; + @Nullable + public String[] argTypeIds; + + @Override + public int getId() { + return 248; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public ArgCacheInvalidation() { + } + + public ArgCacheInvalidation(@Nullable String[] argTypeIds) { + this.argTypeIds = argTypeIds; + } + + public ArgCacheInvalidation(@Nonnull ArgCacheInvalidation other) { + this.argTypeIds = other.argTypeIds; + } + + @Nonnull + public static ArgCacheInvalidation deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ArgCacheInvalidation", 1, buf.readableBytes() - offset); + } else { + ArgCacheInvalidation obj = new ArgCacheInvalidation(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int argTypeIdsCount = VarInt.peek(buf, pos); + if (argTypeIdsCount < 0) { + throw ProtocolException.invalidVarInt("ArgTypeIds"); + } + + int argTypeIdsVarLen = VarInt.size(argTypeIdsCount); + if (argTypeIdsCount > 4096000) { + throw ProtocolException.arrayTooLong("ArgTypeIds", argTypeIdsCount, 4096000); + } + + if (pos + argTypeIdsVarLen + argTypeIdsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ArgTypeIds", pos + argTypeIdsVarLen + argTypeIdsCount * 1, buf.readableBytes()); + } + + pos += argTypeIdsVarLen; + obj.argTypeIds = new String[argTypeIdsCount]; + + for (int i = 0; i < argTypeIdsCount; i++) { + int strLen = VarInt.peek(buf, pos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("argTypeIds[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("argTypeIds[" + i + "]", strLen, 4096000); + } + + if (pos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("argTypeIds[" + i + "]", pos + strVarLen + strLen, buf.readableBytes()); + } + + obj.argTypeIds[i] = PacketIO.readVarString(buf, pos); + pos += strVarLen + strLen; + } + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int arrLen = VarInt.peek(buf, pos); + pos += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + } + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.argTypeIds != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + if (this.argTypeIds != null) { + if (this.argTypeIds.length > 4096000) { + throw ProtocolException.arrayTooLong("ArgTypeIds", this.argTypeIds.length, 4096000); + } + + VarInt.write(buf, this.argTypeIds.length); + + for (String item : this.argTypeIds) { + PacketIO.writeVarString(buf, item, 4096000); + } + } + } + + @Override + public int computeSize() { + int size = 1; + if (this.argTypeIds != null) { + int argTypeIdsSize = 0; + + for (String elem : this.argTypeIds) { + argTypeIdsSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.argTypeIds.length) + argTypeIdsSize; + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int argTypeIdsCount = VarInt.peek(buffer, pos); + if (argTypeIdsCount < 0) { + return ValidationResult.error("Invalid array count for ArgTypeIds"); + } + + if (argTypeIdsCount > 4096000) { + return ValidationResult.error("ArgTypeIds exceeds max length 4096000"); + } + + pos += VarInt.size(argTypeIdsCount); + + for (int i = 0; i < argTypeIdsCount; i++) { + int strLen = VarInt.peek(buffer, pos); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in ArgTypeIds"); + } + + pos += VarInt.size(strLen); + pos += strLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in ArgTypeIds"); + } + } + } + + return ValidationResult.OK; + } + } + + public ArgCacheInvalidation clone() { + ArgCacheInvalidation copy = new ArgCacheInvalidation(); + copy.argTypeIds = this.argTypeIds != null ? Arrays.copyOf(this.argTypeIds, this.argTypeIds.length) : null; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof ArgCacheInvalidation other ? Arrays.equals((Object[])this.argTypeIds, (Object[])other.argTypeIds) : false; + } + } + + @Override + public int hashCode() { + int result = 1; + return 31 * result + Arrays.hashCode((Object[])this.argTypeIds); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ArgValuesRequest.java b/src/com/hypixel/hytale/protocol/packets/interface_/ArgValuesRequest.java new file mode 100644 index 00000000..ce9d292a --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ArgValuesRequest.java @@ -0,0 +1,268 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ArgValuesRequest implements Packet, ToServerPacket { + public static final int PACKET_ID = 239; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 9; + public static final int MAX_SIZE = 32768019; + @Nullable + public String argTypeId; + @Nullable + public String partial; + + @Override + public int getId() { + return 239; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public ArgValuesRequest() { + } + + public ArgValuesRequest(@Nullable String argTypeId, @Nullable String partial) { + this.argTypeId = argTypeId; + this.partial = partial; + } + + public ArgValuesRequest(@Nonnull ArgValuesRequest other) { + this.argTypeId = other.argTypeId; + this.partial = other.partial; + } + + @Nonnull + public static ArgValuesRequest deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("ArgValuesRequest", 9, buf.readableBytes() - offset); + } else { + ArgValuesRequest obj = new ArgValuesRequest(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ArgTypeId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 9 + varPosBase0; + int argTypeIdLen = VarInt.peek(buf, varPos0); + if (argTypeIdLen < 0) { + throw ProtocolException.invalidVarInt("ArgTypeId"); + } + + int argTypeIdVarIntLen = VarInt.size(argTypeIdLen); + if (argTypeIdLen > 4096000) { + throw ProtocolException.stringTooLong("ArgTypeId", argTypeIdLen, 4096000); + } + + if (varPos0 + argTypeIdVarIntLen + argTypeIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ArgTypeId", varPos0 + argTypeIdVarIntLen + argTypeIdLen, buf.readableBytes()); + } + + obj.argTypeId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Partial", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 9 + varPosBase1; + int partialLen = VarInt.peek(buf, varPos1); + if (partialLen < 0) { + throw ProtocolException.invalidVarInt("Partial"); + } + + int partialVarIntLen = VarInt.size(partialLen); + if (partialLen > 4096000) { + throw ProtocolException.stringTooLong("Partial", partialLen, 4096000); + } + + if (varPos1 + partialVarIntLen + partialLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Partial", varPos1 + partialVarIntLen + partialLen, buf.readableBytes()); + } + + obj.partial = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 9; + if ((nullBits & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("ArgTypeId", fieldOffset0, maxEnd); + } + + int pos0 = offset + 9 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 9) { + throw ProtocolException.invalidOffset("Partial", fieldOffset1, maxEnd); + } + + int pos1 = offset + 9 + fieldOffset1; + int sl = VarInt.peek(buf, pos1); + pos1 += VarInt.size(sl) + sl; + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + return maxEnd; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.argTypeId != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.partial != null) { + nullBits = (byte)(nullBits | 2); + } + + buf.writeByte(nullBits); + int argTypeIdOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int partialOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.argTypeId != null) { + buf.setIntLE(argTypeIdOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.argTypeId, 4096000); + } else { + buf.setIntLE(argTypeIdOffsetSlot, -1); + } + + if (this.partial != null) { + buf.setIntLE(partialOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.partial, 4096000); + } else { + buf.setIntLE(partialOffsetSlot, -1); + } + } + + @Override + public int computeSize() { + int size = 9; + if (this.argTypeId != null) { + size += PacketIO.stringSize(this.argTypeId); + } + + if (this.partial != null) { + size += PacketIO.stringSize(this.partial); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 9) { + return ValidationResult.error("Buffer too small: expected at least 9 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 1) != 0) { + int argTypeIdOffset = buffer.getIntLE(offset + 1); + if (argTypeIdOffset < 0 || argTypeIdOffset > buffer.writerIndex() - offset - 9) { + return ValidationResult.error("Invalid offset for ArgTypeId"); + } + + int pos = offset + 9 + argTypeIdOffset; + int argTypeIdLen = VarInt.peek(buffer, pos); + if (argTypeIdLen < 0) { + return ValidationResult.error("Invalid string length for ArgTypeId"); + } + + if (argTypeIdLen > 4096000) { + return ValidationResult.error("ArgTypeId exceeds max length 4096000"); + } + + pos += VarInt.size(argTypeIdLen); + pos += argTypeIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ArgTypeId"); + } + } + + if ((nullBits & 2) != 0) { + int partialOffset = buffer.getIntLE(offset + 5); + if (partialOffset < 0 || partialOffset > buffer.writerIndex() - offset - 9) { + return ValidationResult.error("Invalid offset for Partial"); + } + + int posx = offset + 9 + partialOffset; + int partialLen = VarInt.peek(buffer, posx); + if (partialLen < 0) { + return ValidationResult.error("Invalid string length for Partial"); + } + + if (partialLen > 4096000) { + return ValidationResult.error("Partial exceeds max length 4096000"); + } + + posx += VarInt.size(partialLen); + posx += partialLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Partial"); + } + } + + return ValidationResult.OK; + } + } + + public ArgValuesRequest clone() { + ArgValuesRequest copy = new ArgValuesRequest(); + copy.argTypeId = this.argTypeId; + copy.partial = this.partial; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof ArgValuesRequest other) + ? false + : Objects.equals(this.argTypeId, other.argTypeId) && Objects.equals(this.partial, other.partial); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.argTypeId, this.partial); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ArgValuesResponse.java b/src/com/hypixel/hytale/protocol/packets/interface_/ArgValuesResponse.java new file mode 100644 index 00000000..d3043982 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ArgValuesResponse.java @@ -0,0 +1,426 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ArgValuesResponse implements Packet, ToClientPacket { + public static final int PACKET_ID = 247; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 2; + public static final int VARIABLE_FIELD_COUNT = 3; + public static final int VARIABLE_BLOCK_START = 14; + public static final int MAX_SIZE = 1677721600; + @Nullable + public String argTypeId; + @Nullable + public String[] values; + @Nullable + public boolean[] continuations; + public boolean isComplete; + + @Override + public int getId() { + return 247; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public ArgValuesResponse() { + } + + public ArgValuesResponse(@Nullable String argTypeId, @Nullable String[] values, @Nullable boolean[] continuations, boolean isComplete) { + this.argTypeId = argTypeId; + this.values = values; + this.continuations = continuations; + this.isComplete = isComplete; + } + + public ArgValuesResponse(@Nonnull ArgValuesResponse other) { + this.argTypeId = other.argTypeId; + this.values = other.values; + this.continuations = other.continuations; + this.isComplete = other.isComplete; + } + + @Nonnull + public static ArgValuesResponse deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("ArgValuesResponse", 14, buf.readableBytes() - offset); + } else { + ArgValuesResponse obj = new ArgValuesResponse(); + byte nullBits = buf.getByte(offset); + obj.isComplete = buf.getByte(offset + 1) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("ArgTypeId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 14 + varPosBase0; + int argTypeIdLen = VarInt.peek(buf, varPos0); + if (argTypeIdLen < 0) { + throw ProtocolException.invalidVarInt("ArgTypeId"); + } + + int argTypeIdVarIntLen = VarInt.size(argTypeIdLen); + if (argTypeIdLen > 4096000) { + throw ProtocolException.stringTooLong("ArgTypeId", argTypeIdLen, 4096000); + } + + if (varPos0 + argTypeIdVarIntLen + argTypeIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ArgTypeId", varPos0 + argTypeIdVarIntLen + argTypeIdLen, buf.readableBytes()); + } + + obj.argTypeId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Values", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 14 + varPosBase1; + int valuesCount = VarInt.peek(buf, varPos1); + if (valuesCount < 0) { + throw ProtocolException.invalidVarInt("Values"); + } + + int varIntLen = VarInt.size(valuesCount); + if (valuesCount > 4096000) { + throw ProtocolException.arrayTooLong("Values", valuesCount, 4096000); + } + + if (varPos1 + varIntLen + valuesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Values", varPos1 + varIntLen + valuesCount * 1, buf.readableBytes()); + } + + obj.values = new String[valuesCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < valuesCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("values[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("values[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("values[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.values[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 10); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Continuations", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 14 + varPosBase2; + int continuationsCount = VarInt.peek(buf, varPos2); + if (continuationsCount < 0) { + throw ProtocolException.invalidVarInt("Continuations"); + } + + int varIntLenx = VarInt.size(continuationsCount); + if (continuationsCount > 4096000) { + throw ProtocolException.arrayTooLong("Continuations", continuationsCount, 4096000); + } + + if (varPos2 + varIntLenx + continuationsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Continuations", varPos2 + varIntLenx + continuationsCount * 1, buf.readableBytes()); + } + + obj.continuations = new boolean[continuationsCount]; + + for (int i = 0; i < continuationsCount; i++) { + obj.continuations[i] = buf.getByte(varPos2 + varIntLenx + i * 1) != 0; + } + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 14; + if ((nullBits & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("ArgTypeId", fieldOffset0, maxEnd); + } + + int pos0 = offset + 14 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Values", fieldOffset1, maxEnd); + } + + int pos1 = offset + 14 + fieldOffset1; + int arrLen = VarInt.peek(buf, pos1); + pos1 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos1); + pos1 += VarInt.size(sl) + sl; + } + + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 10); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Continuations", fieldOffset2, maxEnd); + } + + int pos2 = offset + 14 + fieldOffset2; + int arrLen = VarInt.peek(buf, pos2); + pos2 += VarInt.size(arrLen) + arrLen * 1; + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + + return maxEnd; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.argTypeId != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.values != null) { + nullBits = (byte)(nullBits | 2); + } + + if (this.continuations != null) { + nullBits = (byte)(nullBits | 4); + } + + buf.writeByte(nullBits); + buf.writeByte(this.isComplete ? 1 : 0); + int argTypeIdOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int valuesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int continuationsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.argTypeId != null) { + buf.setIntLE(argTypeIdOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.argTypeId, 4096000); + } else { + buf.setIntLE(argTypeIdOffsetSlot, -1); + } + + if (this.values != null) { + buf.setIntLE(valuesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.values.length > 4096000) { + throw ProtocolException.arrayTooLong("Values", this.values.length, 4096000); + } + + VarInt.write(buf, this.values.length); + + for (String item : this.values) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(valuesOffsetSlot, -1); + } + + if (this.continuations != null) { + buf.setIntLE(continuationsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.continuations.length > 4096000) { + throw ProtocolException.arrayTooLong("Continuations", this.continuations.length, 4096000); + } + + VarInt.write(buf, this.continuations.length); + + for (boolean item : this.continuations) { + buf.writeByte(item ? 1 : 0); + } + } else { + buf.setIntLE(continuationsOffsetSlot, -1); + } + } + + @Override + public int computeSize() { + int size = 14; + if (this.argTypeId != null) { + size += PacketIO.stringSize(this.argTypeId); + } + + if (this.values != null) { + int valuesSize = 0; + + for (String elem : this.values) { + valuesSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.values.length) + valuesSize; + } + + if (this.continuations != null) { + size += VarInt.size(this.continuations.length) + this.continuations.length * 1; + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 14) { + return ValidationResult.error("Buffer too small: expected at least 14 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 1) != 0) { + int argTypeIdOffset = buffer.getIntLE(offset + 2); + if (argTypeIdOffset < 0 || argTypeIdOffset > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for ArgTypeId"); + } + + int pos = offset + 14 + argTypeIdOffset; + int argTypeIdLen = VarInt.peek(buffer, pos); + if (argTypeIdLen < 0) { + return ValidationResult.error("Invalid string length for ArgTypeId"); + } + + if (argTypeIdLen > 4096000) { + return ValidationResult.error("ArgTypeId exceeds max length 4096000"); + } + + pos += VarInt.size(argTypeIdLen); + pos += argTypeIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ArgTypeId"); + } + } + + if ((nullBits & 2) != 0) { + int valuesOffset = buffer.getIntLE(offset + 6); + if (valuesOffset < 0 || valuesOffset > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for Values"); + } + + int posx = offset + 14 + valuesOffset; + int valuesCount = VarInt.peek(buffer, posx); + if (valuesCount < 0) { + return ValidationResult.error("Invalid array count for Values"); + } + + if (valuesCount > 4096000) { + return ValidationResult.error("Values exceeds max length 4096000"); + } + + posx += VarInt.size(valuesCount); + + for (int i = 0; i < valuesCount; i++) { + int strLen = VarInt.peek(buffer, posx); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in Values"); + } + + posx += VarInt.size(strLen); + posx += strLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in Values"); + } + } + } + + if ((nullBits & 4) != 0) { + int continuationsOffset = buffer.getIntLE(offset + 10); + if (continuationsOffset < 0 || continuationsOffset > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for Continuations"); + } + + int posxx = offset + 14 + continuationsOffset; + int continuationsCount = VarInt.peek(buffer, posxx); + if (continuationsCount < 0) { + return ValidationResult.error("Invalid array count for Continuations"); + } + + if (continuationsCount > 4096000) { + return ValidationResult.error("Continuations exceeds max length 4096000"); + } + + posxx += VarInt.size(continuationsCount); + posxx += continuationsCount * 1; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Continuations"); + } + } + + return ValidationResult.OK; + } + } + + public ArgValuesResponse clone() { + ArgValuesResponse copy = new ArgValuesResponse(); + copy.argTypeId = this.argTypeId; + copy.values = this.values != null ? Arrays.copyOf(this.values, this.values.length) : null; + copy.continuations = this.continuations != null ? Arrays.copyOf(this.continuations, this.continuations.length) : null; + copy.isComplete = this.isComplete; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof ArgValuesResponse other) + ? false + : Objects.equals(this.argTypeId, other.argTypeId) + && Arrays.equals((Object[])this.values, (Object[])other.values) + && Arrays.equals(this.continuations, other.continuations) + && this.isComplete == other.isComplete; + } + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + Objects.hashCode(this.argTypeId); + result = 31 * result + Arrays.hashCode((Object[])this.values); + result = 31 * result + Arrays.hashCode(this.continuations); + return 31 * result + Boolean.hashCode(this.isComplete); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/BlockChange.java b/src/com/hypixel/hytale/protocol/packets/interface_/BlockChange.java index 6cc8f834..b6dae26f 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/BlockChange.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/BlockChange.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.interface_; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -38,13 +39,17 @@ public class BlockChange { @Nonnull public static BlockChange deserialize(@Nonnull ByteBuf buf, int offset) { - BlockChange obj = new BlockChange(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - obj.block = buf.getIntLE(offset + 12); - obj.rotation = buf.getByte(offset + 16); - return obj; + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("BlockChange", 17, buf.readableBytes() - offset); + } else { + BlockChange obj = new BlockChange(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + obj.block = buf.getIntLE(offset + 12); + obj.rotation = buf.getByte(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ChatMessage.java b/src/com/hypixel/hytale/protocol/packets/interface_/ChatMessage.java index be1142bd..504c2143 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/ChatMessage.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ChatMessage.java @@ -19,7 +19,7 @@ public class ChatMessage implements Packet, ToServerPacket { public static final int FIXED_BLOCK_SIZE = 1; public static final int VARIABLE_FIELD_COUNT = 1; public static final int VARIABLE_BLOCK_START = 1; - public static final int MAX_SIZE = 16384006; + public static final int MAX_SIZE = 1026; @Nullable public String message; @@ -46,25 +46,33 @@ public class ChatMessage implements Packet, ToServerPacket { @Nonnull public static ChatMessage deserialize(@Nonnull ByteBuf buf, int offset) { - ChatMessage obj = new ChatMessage(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int messageLen = VarInt.peek(buf, pos); - if (messageLen < 0) { - throw ProtocolException.negativeLength("Message", messageLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ChatMessage", 1, buf.readableBytes() - offset); + } else { + ChatMessage obj = new ChatMessage(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int messageLen = VarInt.peek(buf, pos); + if (messageLen < 0) { + throw ProtocolException.invalidVarInt("Message"); + } + + int messageVarLen = VarInt.size(messageLen); + if (messageLen > 255) { + throw ProtocolException.stringTooLong("Message", messageLen, 255); + } + + if (pos + messageVarLen + messageLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Message", pos + messageVarLen + messageLen, buf.readableBytes()); + } + + obj.message = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += messageVarLen + messageLen; } - if (messageLen > 4096000) { - throw ProtocolException.stringTooLong("Message", messageLen, 4096000); - } - - int messageVarLen = VarInt.length(buf, pos); - obj.message = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += messageVarLen + messageLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +80,7 @@ public class ChatMessage implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -87,7 +95,7 @@ public class ChatMessage implements Packet, ToServerPacket { buf.writeByte(nullBits); if (this.message != null) { - PacketIO.writeVarString(buf, this.message, 4096000); + PacketIO.writeVarString(buf, this.message, 255); } } @@ -113,11 +121,11 @@ public class ChatMessage implements Packet, ToServerPacket { return ValidationResult.error("Invalid string length for Message"); } - if (messageLen > 4096000) { - return ValidationResult.error("Message exceeds max length 4096000"); + if (messageLen > 255) { + return ValidationResult.error("Message exceeds max length 255"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(messageLen); pos += messageLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Message"); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CommandArgInfo.java b/src/com/hypixel/hytale/protocol/packets/interface_/CommandArgInfo.java new file mode 100644 index 00000000..52fb5bcd --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CommandArgInfo.java @@ -0,0 +1,427 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class CommandArgInfo { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 5; + public static final int VARIABLE_FIELD_COUNT = 4; + public static final int VARIABLE_BLOCK_START = 21; + public static final int MAX_SIZE = 65536041; + @Nullable + public String name; + @Nullable + public String argTypeId; + @Nullable + public String argTypeName; + public int valueCount; + @Nullable + public String description; + + public CommandArgInfo() { + } + + public CommandArgInfo(@Nullable String name, @Nullable String argTypeId, @Nullable String argTypeName, int valueCount, @Nullable String description) { + this.name = name; + this.argTypeId = argTypeId; + this.argTypeName = argTypeName; + this.valueCount = valueCount; + this.description = description; + } + + public CommandArgInfo(@Nonnull CommandArgInfo other) { + this.name = other.name; + this.argTypeId = other.argTypeId; + this.argTypeName = other.argTypeName; + this.valueCount = other.valueCount; + this.description = other.description; + } + + @Nonnull + public static CommandArgInfo deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("CommandArgInfo", 21, buf.readableBytes() - offset); + } else { + CommandArgInfo obj = new CommandArgInfo(); + byte nullBits = buf.getByte(offset); + obj.valueCount = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 21 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("ArgTypeId", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + int argTypeIdLen = VarInt.peek(buf, varPos1); + if (argTypeIdLen < 0) { + throw ProtocolException.invalidVarInt("ArgTypeId"); + } + + int argTypeIdVarIntLen = VarInt.size(argTypeIdLen); + if (argTypeIdLen > 4096000) { + throw ProtocolException.stringTooLong("ArgTypeId", argTypeIdLen, 4096000); + } + + if (varPos1 + argTypeIdVarIntLen + argTypeIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ArgTypeId", varPos1 + argTypeIdVarIntLen + argTypeIdLen, buf.readableBytes()); + } + + obj.argTypeId = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 13); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("ArgTypeName", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 21 + varPosBase2; + int argTypeNameLen = VarInt.peek(buf, varPos2); + if (argTypeNameLen < 0) { + throw ProtocolException.invalidVarInt("ArgTypeName"); + } + + int argTypeNameVarIntLen = VarInt.size(argTypeNameLen); + if (argTypeNameLen > 4096000) { + throw ProtocolException.stringTooLong("ArgTypeName", argTypeNameLen, 4096000); + } + + if (varPos2 + argTypeNameVarIntLen + argTypeNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ArgTypeName", varPos2 + argTypeNameVarIntLen + argTypeNameLen, buf.readableBytes()); + } + + obj.argTypeName = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 17); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Description", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 21 + varPosBase3; + int descriptionLen = VarInt.peek(buf, varPos3); + if (descriptionLen < 0) { + throw ProtocolException.invalidVarInt("Description"); + } + + int descriptionVarIntLen = VarInt.size(descriptionLen); + if (descriptionLen > 4096000) { + throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); + } + + if (varPos3 + descriptionVarIntLen + descriptionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Description", varPos3 + descriptionVarIntLen + descriptionLen, buf.readableBytes()); + } + + obj.description = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 21; + if ((nullBits & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + + int pos0 = offset + 21 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("ArgTypeId", fieldOffset1, maxEnd); + } + + int pos1 = offset + 21 + fieldOffset1; + int sl = VarInt.peek(buf, pos1); + pos1 += VarInt.size(sl) + sl; + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 13); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("ArgTypeName", fieldOffset2, maxEnd); + } + + int pos2 = offset + 21 + fieldOffset2; + int sl = VarInt.peek(buf, pos2); + pos2 += VarInt.size(sl) + sl; + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + + if ((nullBits & 8) != 0) { + int fieldOffset3 = buf.getIntLE(offset + 17); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Description", fieldOffset3, maxEnd); + } + + int pos3 = offset + 21 + fieldOffset3; + int sl = VarInt.peek(buf, pos3); + pos3 += VarInt.size(sl) + sl; + if (pos3 - offset > maxEnd) { + maxEnd = pos3 - offset; + } + } + + return maxEnd; + } + + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.name != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.argTypeId != null) { + nullBits = (byte)(nullBits | 2); + } + + if (this.argTypeName != null) { + nullBits = (byte)(nullBits | 4); + } + + if (this.description != null) { + nullBits = (byte)(nullBits | 8); + } + + buf.writeByte(nullBits); + buf.writeIntLE(this.valueCount); + int nameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int argTypeIdOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int argTypeNameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int descriptionOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.name != null) { + buf.setIntLE(nameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.name, 4096000); + } else { + buf.setIntLE(nameOffsetSlot, -1); + } + + if (this.argTypeId != null) { + buf.setIntLE(argTypeIdOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.argTypeId, 4096000); + } else { + buf.setIntLE(argTypeIdOffsetSlot, -1); + } + + if (this.argTypeName != null) { + buf.setIntLE(argTypeNameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.argTypeName, 4096000); + } else { + buf.setIntLE(argTypeNameOffsetSlot, -1); + } + + if (this.description != null) { + buf.setIntLE(descriptionOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.description, 4096000); + } else { + buf.setIntLE(descriptionOffsetSlot, -1); + } + } + + public int computeSize() { + int size = 21; + if (this.name != null) { + size += PacketIO.stringSize(this.name); + } + + if (this.argTypeId != null) { + size += PacketIO.stringSize(this.argTypeId); + } + + if (this.argTypeName != null) { + size += PacketIO.stringSize(this.argTypeName); + } + + if (this.description != null) { + size += PacketIO.stringSize(this.description); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 21) { + return ValidationResult.error("Buffer too small: expected at least 21 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 1) != 0) { + int nameOffset = buffer.getIntLE(offset + 5); + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for Name"); + } + + int pos = offset + 21 + nameOffset; + int nameLen = VarInt.peek(buffer, pos); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } + + if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); + } + + pos += VarInt.size(nameLen); + pos += nameLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); + } + } + + if ((nullBits & 2) != 0) { + int argTypeIdOffset = buffer.getIntLE(offset + 9); + if (argTypeIdOffset < 0 || argTypeIdOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for ArgTypeId"); + } + + int posx = offset + 21 + argTypeIdOffset; + int argTypeIdLen = VarInt.peek(buffer, posx); + if (argTypeIdLen < 0) { + return ValidationResult.error("Invalid string length for ArgTypeId"); + } + + if (argTypeIdLen > 4096000) { + return ValidationResult.error("ArgTypeId exceeds max length 4096000"); + } + + posx += VarInt.size(argTypeIdLen); + posx += argTypeIdLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ArgTypeId"); + } + } + + if ((nullBits & 4) != 0) { + int argTypeNameOffset = buffer.getIntLE(offset + 13); + if (argTypeNameOffset < 0 || argTypeNameOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for ArgTypeName"); + } + + int posxx = offset + 21 + argTypeNameOffset; + int argTypeNameLen = VarInt.peek(buffer, posxx); + if (argTypeNameLen < 0) { + return ValidationResult.error("Invalid string length for ArgTypeName"); + } + + if (argTypeNameLen > 4096000) { + return ValidationResult.error("ArgTypeName exceeds max length 4096000"); + } + + posxx += VarInt.size(argTypeNameLen); + posxx += argTypeNameLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ArgTypeName"); + } + } + + if ((nullBits & 8) != 0) { + int descriptionOffset = buffer.getIntLE(offset + 17); + if (descriptionOffset < 0 || descriptionOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for Description"); + } + + int posxxx = offset + 21 + descriptionOffset; + int descriptionLen = VarInt.peek(buffer, posxxx); + if (descriptionLen < 0) { + return ValidationResult.error("Invalid string length for Description"); + } + + if (descriptionLen > 4096000) { + return ValidationResult.error("Description exceeds max length 4096000"); + } + + posxxx += VarInt.size(descriptionLen); + posxxx += descriptionLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Description"); + } + } + + return ValidationResult.OK; + } + } + + public CommandArgInfo clone() { + CommandArgInfo copy = new CommandArgInfo(); + copy.name = this.name; + copy.argTypeId = this.argTypeId; + copy.argTypeName = this.argTypeName; + copy.valueCount = this.valueCount; + copy.description = this.description; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof CommandArgInfo other) + ? false + : Objects.equals(this.name, other.name) + && Objects.equals(this.argTypeId, other.argTypeId) + && Objects.equals(this.argTypeName, other.argTypeName) + && this.valueCount == other.valueCount + && Objects.equals(this.description, other.description); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.name, this.argTypeId, this.argTypeName, this.valueCount, this.description); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CommandOptionalArgEntry.java b/src/com/hypixel/hytale/protocol/packets/interface_/CommandOptionalArgEntry.java new file mode 100644 index 00000000..7dc46c38 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CommandOptionalArgEntry.java @@ -0,0 +1,420 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class CommandOptionalArgEntry { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 4; + public static final int VARIABLE_BLOCK_START = 17; + public static final int MAX_SIZE = 65536037; + @Nullable + public String name; + @Nullable + public String hint; + @Nullable + public String argTypeId; + @Nullable + public String description; + + public CommandOptionalArgEntry() { + } + + public CommandOptionalArgEntry(@Nullable String name, @Nullable String hint, @Nullable String argTypeId, @Nullable String description) { + this.name = name; + this.hint = hint; + this.argTypeId = argTypeId; + this.description = description; + } + + public CommandOptionalArgEntry(@Nonnull CommandOptionalArgEntry other) { + this.name = other.name; + this.hint = other.hint; + this.argTypeId = other.argTypeId; + this.description = other.description; + } + + @Nonnull + public static CommandOptionalArgEntry deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("CommandOptionalArgEntry", 17, buf.readableBytes() - offset); + } else { + CommandOptionalArgEntry obj = new CommandOptionalArgEntry(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Hint", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + int hintLen = VarInt.peek(buf, varPos1); + if (hintLen < 0) { + throw ProtocolException.invalidVarInt("Hint"); + } + + int hintVarIntLen = VarInt.size(hintLen); + if (hintLen > 4096000) { + throw ProtocolException.stringTooLong("Hint", hintLen, 4096000); + } + + if (varPos1 + hintVarIntLen + hintLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Hint", varPos1 + hintVarIntLen + hintLen, buf.readableBytes()); + } + + obj.hint = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ArgTypeId", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 17 + varPosBase2; + int argTypeIdLen = VarInt.peek(buf, varPos2); + if (argTypeIdLen < 0) { + throw ProtocolException.invalidVarInt("ArgTypeId"); + } + + int argTypeIdVarIntLen = VarInt.size(argTypeIdLen); + if (argTypeIdLen > 4096000) { + throw ProtocolException.stringTooLong("ArgTypeId", argTypeIdLen, 4096000); + } + + if (varPos2 + argTypeIdVarIntLen + argTypeIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ArgTypeId", varPos2 + argTypeIdVarIntLen + argTypeIdLen, buf.readableBytes()); + } + + obj.argTypeId = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 13); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Description", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 17 + varPosBase3; + int descriptionLen = VarInt.peek(buf, varPos3); + if (descriptionLen < 0) { + throw ProtocolException.invalidVarInt("Description"); + } + + int descriptionVarIntLen = VarInt.size(descriptionLen); + if (descriptionLen > 4096000) { + throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); + } + + if (varPos3 + descriptionVarIntLen + descriptionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Description", varPos3 + descriptionVarIntLen + descriptionLen, buf.readableBytes()); + } + + obj.description = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 17; + if ((nullBits & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + + int pos0 = offset + 17 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Hint", fieldOffset1, maxEnd); + } + + int pos1 = offset + 17 + fieldOffset1; + int sl = VarInt.peek(buf, pos1); + pos1 += VarInt.size(sl) + sl; + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ArgTypeId", fieldOffset2, maxEnd); + } + + int pos2 = offset + 17 + fieldOffset2; + int sl = VarInt.peek(buf, pos2); + pos2 += VarInt.size(sl) + sl; + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + + if ((nullBits & 8) != 0) { + int fieldOffset3 = buf.getIntLE(offset + 13); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Description", fieldOffset3, maxEnd); + } + + int pos3 = offset + 17 + fieldOffset3; + int sl = VarInt.peek(buf, pos3); + pos3 += VarInt.size(sl) + sl; + if (pos3 - offset > maxEnd) { + maxEnd = pos3 - offset; + } + } + + return maxEnd; + } + + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.name != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.hint != null) { + nullBits = (byte)(nullBits | 2); + } + + if (this.argTypeId != null) { + nullBits = (byte)(nullBits | 4); + } + + if (this.description != null) { + nullBits = (byte)(nullBits | 8); + } + + buf.writeByte(nullBits); + int nameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int hintOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int argTypeIdOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int descriptionOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.name != null) { + buf.setIntLE(nameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.name, 4096000); + } else { + buf.setIntLE(nameOffsetSlot, -1); + } + + if (this.hint != null) { + buf.setIntLE(hintOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.hint, 4096000); + } else { + buf.setIntLE(hintOffsetSlot, -1); + } + + if (this.argTypeId != null) { + buf.setIntLE(argTypeIdOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.argTypeId, 4096000); + } else { + buf.setIntLE(argTypeIdOffsetSlot, -1); + } + + if (this.description != null) { + buf.setIntLE(descriptionOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.description, 4096000); + } else { + buf.setIntLE(descriptionOffsetSlot, -1); + } + } + + public int computeSize() { + int size = 17; + if (this.name != null) { + size += PacketIO.stringSize(this.name); + } + + if (this.hint != null) { + size += PacketIO.stringSize(this.hint); + } + + if (this.argTypeId != null) { + size += PacketIO.stringSize(this.argTypeId); + } + + if (this.description != null) { + size += PacketIO.stringSize(this.description); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 1) != 0) { + int nameOffset = buffer.getIntLE(offset + 1); + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 17) { + return ValidationResult.error("Invalid offset for Name"); + } + + int pos = offset + 17 + nameOffset; + int nameLen = VarInt.peek(buffer, pos); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } + + if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); + } + + pos += VarInt.size(nameLen); + pos += nameLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); + } + } + + if ((nullBits & 2) != 0) { + int hintOffset = buffer.getIntLE(offset + 5); + if (hintOffset < 0 || hintOffset > buffer.writerIndex() - offset - 17) { + return ValidationResult.error("Invalid offset for Hint"); + } + + int posx = offset + 17 + hintOffset; + int hintLen = VarInt.peek(buffer, posx); + if (hintLen < 0) { + return ValidationResult.error("Invalid string length for Hint"); + } + + if (hintLen > 4096000) { + return ValidationResult.error("Hint exceeds max length 4096000"); + } + + posx += VarInt.size(hintLen); + posx += hintLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Hint"); + } + } + + if ((nullBits & 4) != 0) { + int argTypeIdOffset = buffer.getIntLE(offset + 9); + if (argTypeIdOffset < 0 || argTypeIdOffset > buffer.writerIndex() - offset - 17) { + return ValidationResult.error("Invalid offset for ArgTypeId"); + } + + int posxx = offset + 17 + argTypeIdOffset; + int argTypeIdLen = VarInt.peek(buffer, posxx); + if (argTypeIdLen < 0) { + return ValidationResult.error("Invalid string length for ArgTypeId"); + } + + if (argTypeIdLen > 4096000) { + return ValidationResult.error("ArgTypeId exceeds max length 4096000"); + } + + posxx += VarInt.size(argTypeIdLen); + posxx += argTypeIdLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ArgTypeId"); + } + } + + if ((nullBits & 8) != 0) { + int descriptionOffset = buffer.getIntLE(offset + 13); + if (descriptionOffset < 0 || descriptionOffset > buffer.writerIndex() - offset - 17) { + return ValidationResult.error("Invalid offset for Description"); + } + + int posxxx = offset + 17 + descriptionOffset; + int descriptionLen = VarInt.peek(buffer, posxxx); + if (descriptionLen < 0) { + return ValidationResult.error("Invalid string length for Description"); + } + + if (descriptionLen > 4096000) { + return ValidationResult.error("Description exceeds max length 4096000"); + } + + posxxx += VarInt.size(descriptionLen); + posxxx += descriptionLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Description"); + } + } + + return ValidationResult.OK; + } + } + + public CommandOptionalArgEntry clone() { + CommandOptionalArgEntry copy = new CommandOptionalArgEntry(); + copy.name = this.name; + copy.hint = this.hint; + copy.argTypeId = this.argTypeId; + copy.description = this.description; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof CommandOptionalArgEntry other) + ? false + : Objects.equals(this.name, other.name) + && Objects.equals(this.hint, other.hint) + && Objects.equals(this.argTypeId, other.argTypeId) + && Objects.equals(this.description, other.description); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.name, this.hint, this.argTypeId, this.description); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionOverride.java b/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionOverride.java new file mode 100644 index 00000000..df54ed18 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionOverride.java @@ -0,0 +1,155 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class CommandSuggestionOverride { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 9; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 9; + public static final int MAX_SIZE = 16384014; + public int argStart; + public int argCount; + @Nullable + public String argTypeId; + + public CommandSuggestionOverride() { + } + + public CommandSuggestionOverride(int argStart, int argCount, @Nullable String argTypeId) { + this.argStart = argStart; + this.argCount = argCount; + this.argTypeId = argTypeId; + } + + public CommandSuggestionOverride(@Nonnull CommandSuggestionOverride other) { + this.argStart = other.argStart; + this.argCount = other.argCount; + this.argTypeId = other.argTypeId; + } + + @Nonnull + public static CommandSuggestionOverride deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("CommandSuggestionOverride", 9, buf.readableBytes() - offset); + } else { + CommandSuggestionOverride obj = new CommandSuggestionOverride(); + byte nullBits = buf.getByte(offset); + obj.argStart = buf.getIntLE(offset + 1); + obj.argCount = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int argTypeIdLen = VarInt.peek(buf, pos); + if (argTypeIdLen < 0) { + throw ProtocolException.invalidVarInt("ArgTypeId"); + } + + int argTypeIdVarLen = VarInt.size(argTypeIdLen); + if (argTypeIdLen > 4096000) { + throw ProtocolException.stringTooLong("ArgTypeId", argTypeIdLen, 4096000); + } + + if (pos + argTypeIdVarLen + argTypeIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ArgTypeId", pos + argTypeIdVarLen + argTypeIdLen, buf.readableBytes()); + } + + obj.argTypeId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += argTypeIdVarLen + argTypeIdLen; + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + } + + return pos - offset; + } + + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.argTypeId != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + buf.writeIntLE(this.argStart); + buf.writeIntLE(this.argCount); + if (this.argTypeId != null) { + PacketIO.writeVarString(buf, this.argTypeId, 4096000); + } + } + + public int computeSize() { + int size = 9; + if (this.argTypeId != null) { + size += PacketIO.stringSize(this.argTypeId); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 9) { + return ValidationResult.error("Buffer too small: expected at least 9 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int argTypeIdLen = VarInt.peek(buffer, pos); + if (argTypeIdLen < 0) { + return ValidationResult.error("Invalid string length for ArgTypeId"); + } + + if (argTypeIdLen > 4096000) { + return ValidationResult.error("ArgTypeId exceeds max length 4096000"); + } + + pos += VarInt.size(argTypeIdLen); + pos += argTypeIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ArgTypeId"); + } + } + + return ValidationResult.OK; + } + } + + public CommandSuggestionOverride clone() { + CommandSuggestionOverride copy = new CommandSuggestionOverride(); + copy.argStart = this.argStart; + copy.argCount = this.argCount; + copy.argTypeId = this.argTypeId; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof CommandSuggestionOverride other) + ? false + : this.argStart == other.argStart && this.argCount == other.argCount && Objects.equals(this.argTypeId, other.argTypeId); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.argStart, this.argCount, this.argTypeId); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionsRequest.java b/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionsRequest.java new file mode 100644 index 00000000..c1f0b982 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionsRequest.java @@ -0,0 +1,172 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class CommandSuggestionsRequest implements Packet, ToServerPacket { + public static final int PACKET_ID = 236; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 9; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 9; + public static final int MAX_SIZE = 16384014; + @Nullable + public String command; + public int cursorPosition; + public int selectedVariant; + + @Override + public int getId() { + return 236; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public CommandSuggestionsRequest() { + } + + public CommandSuggestionsRequest(@Nullable String command, int cursorPosition, int selectedVariant) { + this.command = command; + this.cursorPosition = cursorPosition; + this.selectedVariant = selectedVariant; + } + + public CommandSuggestionsRequest(@Nonnull CommandSuggestionsRequest other) { + this.command = other.command; + this.cursorPosition = other.cursorPosition; + this.selectedVariant = other.selectedVariant; + } + + @Nonnull + public static CommandSuggestionsRequest deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("CommandSuggestionsRequest", 9, buf.readableBytes() - offset); + } else { + CommandSuggestionsRequest obj = new CommandSuggestionsRequest(); + byte nullBits = buf.getByte(offset); + obj.cursorPosition = buf.getIntLE(offset + 1); + obj.selectedVariant = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int commandLen = VarInt.peek(buf, pos); + if (commandLen < 0) { + throw ProtocolException.invalidVarInt("Command"); + } + + int commandVarLen = VarInt.size(commandLen); + if (commandLen > 4096000) { + throw ProtocolException.stringTooLong("Command", commandLen, 4096000); + } + + if (pos + commandVarLen + commandLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Command", pos + commandVarLen + commandLen, buf.readableBytes()); + } + + obj.command = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += commandVarLen + commandLen; + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.command != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + buf.writeIntLE(this.cursorPosition); + buf.writeIntLE(this.selectedVariant); + if (this.command != null) { + PacketIO.writeVarString(buf, this.command, 4096000); + } + } + + @Override + public int computeSize() { + int size = 9; + if (this.command != null) { + size += PacketIO.stringSize(this.command); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 9) { + return ValidationResult.error("Buffer too small: expected at least 9 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int commandLen = VarInt.peek(buffer, pos); + if (commandLen < 0) { + return ValidationResult.error("Invalid string length for Command"); + } + + if (commandLen > 4096000) { + return ValidationResult.error("Command exceeds max length 4096000"); + } + + pos += VarInt.size(commandLen); + pos += commandLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Command"); + } + } + + return ValidationResult.OK; + } + } + + public CommandSuggestionsRequest clone() { + CommandSuggestionsRequest copy = new CommandSuggestionsRequest(); + copy.command = this.command; + copy.cursorPosition = this.cursorPosition; + copy.selectedVariant = this.selectedVariant; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof CommandSuggestionsRequest other) + ? false + : Objects.equals(this.command, other.command) && this.cursorPosition == other.cursorPosition && this.selectedVariant == other.selectedVariant; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.command, this.cursorPosition, this.selectedVariant); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionsResponse.java b/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionsResponse.java new file mode 100644 index 00000000..cf92e525 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CommandSuggestionsResponse.java @@ -0,0 +1,1133 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class CommandSuggestionsResponse implements Packet, ToClientPacket { + public static final int PACKET_ID = 237; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 17; + public static final int VARIABLE_FIELD_COUNT = 8; + public static final int VARIABLE_BLOCK_START = 49; + public static final int MAX_SIZE = 1677721600; + public int startPosition; + public int length; + @Nullable + public String[] suggestions; + @Nullable + public boolean[] continuations; + @Nullable + public String usageText; + public int currentArgStart; + public int currentArgLength; + @Nullable + public String[] variantPatterns; + @Nullable + public String[] subcommands; + @Nullable + public String[] subcommandHints; + @Nullable + public String[] optionalArgs; + @Nullable + public String[] optionalArgHints; + + @Override + public int getId() { + return 237; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public CommandSuggestionsResponse() { + } + + public CommandSuggestionsResponse( + int startPosition, + int length, + @Nullable String[] suggestions, + @Nullable boolean[] continuations, + @Nullable String usageText, + int currentArgStart, + int currentArgLength, + @Nullable String[] variantPatterns, + @Nullable String[] subcommands, + @Nullable String[] subcommandHints, + @Nullable String[] optionalArgs, + @Nullable String[] optionalArgHints + ) { + this.startPosition = startPosition; + this.length = length; + this.suggestions = suggestions; + this.continuations = continuations; + this.usageText = usageText; + this.currentArgStart = currentArgStart; + this.currentArgLength = currentArgLength; + this.variantPatterns = variantPatterns; + this.subcommands = subcommands; + this.subcommandHints = subcommandHints; + this.optionalArgs = optionalArgs; + this.optionalArgHints = optionalArgHints; + } + + public CommandSuggestionsResponse(@Nonnull CommandSuggestionsResponse other) { + this.startPosition = other.startPosition; + this.length = other.length; + this.suggestions = other.suggestions; + this.continuations = other.continuations; + this.usageText = other.usageText; + this.currentArgStart = other.currentArgStart; + this.currentArgLength = other.currentArgLength; + this.variantPatterns = other.variantPatterns; + this.subcommands = other.subcommands; + this.subcommandHints = other.subcommandHints; + this.optionalArgs = other.optionalArgs; + this.optionalArgHints = other.optionalArgHints; + } + + @Nonnull + public static CommandSuggestionsResponse deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 49) { + throw ProtocolException.bufferTooSmall("CommandSuggestionsResponse", 49, buf.readableBytes() - offset); + } else { + CommandSuggestionsResponse obj = new CommandSuggestionsResponse(); + byte nullBits = buf.getByte(offset); + obj.startPosition = buf.getIntLE(offset + 1); + obj.length = buf.getIntLE(offset + 5); + obj.currentArgStart = buf.getIntLE(offset + 9); + obj.currentArgLength = buf.getIntLE(offset + 13); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 17); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Suggestions", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 49 + varPosBase0; + int suggestionsCount = VarInt.peek(buf, varPos0); + if (suggestionsCount < 0) { + throw ProtocolException.invalidVarInt("Suggestions"); + } + + int varIntLen = VarInt.size(suggestionsCount); + if (suggestionsCount > 4096000) { + throw ProtocolException.arrayTooLong("Suggestions", suggestionsCount, 4096000); + } + + if (varPos0 + varIntLen + suggestionsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Suggestions", varPos0 + varIntLen + suggestionsCount * 1, buf.readableBytes()); + } + + obj.suggestions = new String[suggestionsCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < suggestionsCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("suggestions[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("suggestions[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("suggestions[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.suggestions[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 21); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Continuations", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 49 + varPosBase1; + int continuationsCount = VarInt.peek(buf, varPos1); + if (continuationsCount < 0) { + throw ProtocolException.invalidVarInt("Continuations"); + } + + int varIntLenx = VarInt.size(continuationsCount); + if (continuationsCount > 4096000) { + throw ProtocolException.arrayTooLong("Continuations", continuationsCount, 4096000); + } + + if (varPos1 + varIntLenx + continuationsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Continuations", varPos1 + varIntLenx + continuationsCount * 1, buf.readableBytes()); + } + + obj.continuations = new boolean[continuationsCount]; + + for (int i = 0; i < continuationsCount; i++) { + obj.continuations[i] = buf.getByte(varPos1 + varIntLenx + i * 1) != 0; + } + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 25); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("UsageText", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 49 + varPosBase2; + int usageTextLen = VarInt.peek(buf, varPos2); + if (usageTextLen < 0) { + throw ProtocolException.invalidVarInt("UsageText"); + } + + int usageTextVarIntLen = VarInt.size(usageTextLen); + if (usageTextLen > 4096000) { + throw ProtocolException.stringTooLong("UsageText", usageTextLen, 4096000); + } + + if (varPos2 + usageTextVarIntLen + usageTextLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("UsageText", varPos2 + usageTextVarIntLen + usageTextLen, buf.readableBytes()); + } + + obj.usageText = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 29); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("VariantPatterns", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 49 + varPosBase3; + int variantPatternsCount = VarInt.peek(buf, varPos3); + if (variantPatternsCount < 0) { + throw ProtocolException.invalidVarInt("VariantPatterns"); + } + + int varIntLenxx = VarInt.size(variantPatternsCount); + if (variantPatternsCount > 4096000) { + throw ProtocolException.arrayTooLong("VariantPatterns", variantPatternsCount, 4096000); + } + + if (varPos3 + varIntLenxx + variantPatternsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("VariantPatterns", varPos3 + varIntLenxx + variantPatternsCount * 1, buf.readableBytes()); + } + + obj.variantPatterns = new String[variantPatternsCount]; + int elemPos = varPos3 + varIntLenxx; + + for (int i = 0; i < variantPatternsCount; i++) { + int strLenx = VarInt.peek(buf, elemPos); + if (strLenx < 0) { + throw ProtocolException.invalidVarInt("variantPatterns[" + i + "]"); + } + + int strVarLenx = VarInt.size(strLenx); + if (strLenx > 4096000) { + throw ProtocolException.stringTooLong("variantPatterns[" + i + "]", strLenx, 4096000); + } + + if (elemPos + strVarLenx + strLenx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("variantPatterns[" + i + "]", elemPos + strVarLenx + strLenx, buf.readableBytes()); + } + + obj.variantPatterns[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLenx + strLenx; + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 33); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Subcommands", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 49 + varPosBase4; + int subcommandsCount = VarInt.peek(buf, varPos4); + if (subcommandsCount < 0) { + throw ProtocolException.invalidVarInt("Subcommands"); + } + + int varIntLenxxx = VarInt.size(subcommandsCount); + if (subcommandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Subcommands", subcommandsCount, 4096000); + } + + if (varPos4 + varIntLenxxx + subcommandsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Subcommands", varPos4 + varIntLenxxx + subcommandsCount * 1, buf.readableBytes()); + } + + obj.subcommands = new String[subcommandsCount]; + int elemPos = varPos4 + varIntLenxxx; + + for (int i = 0; i < subcommandsCount; i++) { + int strLenxx = VarInt.peek(buf, elemPos); + if (strLenxx < 0) { + throw ProtocolException.invalidVarInt("subcommands[" + i + "]"); + } + + int strVarLenxx = VarInt.size(strLenxx); + if (strLenxx > 4096000) { + throw ProtocolException.stringTooLong("subcommands[" + i + "]", strLenxx, 4096000); + } + + if (elemPos + strVarLenxx + strLenxx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("subcommands[" + i + "]", elemPos + strVarLenxx + strLenxx, buf.readableBytes()); + } + + obj.subcommands[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLenxx + strLenxx; + } + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 37); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("SubcommandHints", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 49 + varPosBase5; + int subcommandHintsCount = VarInt.peek(buf, varPos5); + if (subcommandHintsCount < 0) { + throw ProtocolException.invalidVarInt("SubcommandHints"); + } + + int varIntLenxxxx = VarInt.size(subcommandHintsCount); + if (subcommandHintsCount > 4096000) { + throw ProtocolException.arrayTooLong("SubcommandHints", subcommandHintsCount, 4096000); + } + + if (varPos5 + varIntLenxxxx + subcommandHintsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SubcommandHints", varPos5 + varIntLenxxxx + subcommandHintsCount * 1, buf.readableBytes()); + } + + obj.subcommandHints = new String[subcommandHintsCount]; + int elemPos = varPos5 + varIntLenxxxx; + + for (int i = 0; i < subcommandHintsCount; i++) { + int strLenxxx = VarInt.peek(buf, elemPos); + if (strLenxxx < 0) { + throw ProtocolException.invalidVarInt("subcommandHints[" + i + "]"); + } + + int strVarLenxxx = VarInt.size(strLenxxx); + if (strLenxxx > 4096000) { + throw ProtocolException.stringTooLong("subcommandHints[" + i + "]", strLenxxx, 4096000); + } + + if (elemPos + strVarLenxxx + strLenxxx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("subcommandHints[" + i + "]", elemPos + strVarLenxxx + strLenxxx, buf.readableBytes()); + } + + obj.subcommandHints[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLenxxx + strLenxxx; + } + } + + if ((nullBits & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 41); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("OptionalArgs", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 49 + varPosBase6; + int optionalArgsCount = VarInt.peek(buf, varPos6); + if (optionalArgsCount < 0) { + throw ProtocolException.invalidVarInt("OptionalArgs"); + } + + int varIntLenxxxxx = VarInt.size(optionalArgsCount); + if (optionalArgsCount > 4096000) { + throw ProtocolException.arrayTooLong("OptionalArgs", optionalArgsCount, 4096000); + } + + if (varPos6 + varIntLenxxxxx + optionalArgsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("OptionalArgs", varPos6 + varIntLenxxxxx + optionalArgsCount * 1, buf.readableBytes()); + } + + obj.optionalArgs = new String[optionalArgsCount]; + int elemPos = varPos6 + varIntLenxxxxx; + + for (int i = 0; i < optionalArgsCount; i++) { + int strLenxxxx = VarInt.peek(buf, elemPos); + if (strLenxxxx < 0) { + throw ProtocolException.invalidVarInt("optionalArgs[" + i + "]"); + } + + int strVarLenxxxx = VarInt.size(strLenxxxx); + if (strLenxxxx > 4096000) { + throw ProtocolException.stringTooLong("optionalArgs[" + i + "]", strLenxxxx, 4096000); + } + + if (elemPos + strVarLenxxxx + strLenxxxx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("optionalArgs[" + i + "]", elemPos + strVarLenxxxx + strLenxxxx, buf.readableBytes()); + } + + obj.optionalArgs[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLenxxxx + strLenxxxx; + } + } + + if ((nullBits & 128) != 0) { + int varPosBase7 = buf.getIntLE(offset + 45); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("OptionalArgHints", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 49 + varPosBase7; + int optionalArgHintsCount = VarInt.peek(buf, varPos7); + if (optionalArgHintsCount < 0) { + throw ProtocolException.invalidVarInt("OptionalArgHints"); + } + + int varIntLenxxxxxx = VarInt.size(optionalArgHintsCount); + if (optionalArgHintsCount > 4096000) { + throw ProtocolException.arrayTooLong("OptionalArgHints", optionalArgHintsCount, 4096000); + } + + if (varPos7 + varIntLenxxxxxx + optionalArgHintsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("OptionalArgHints", varPos7 + varIntLenxxxxxx + optionalArgHintsCount * 1, buf.readableBytes()); + } + + obj.optionalArgHints = new String[optionalArgHintsCount]; + int elemPos = varPos7 + varIntLenxxxxxx; + + for (int i = 0; i < optionalArgHintsCount; i++) { + int strLenxxxxx = VarInt.peek(buf, elemPos); + if (strLenxxxxx < 0) { + throw ProtocolException.invalidVarInt("optionalArgHints[" + i + "]"); + } + + int strVarLenxxxxx = VarInt.size(strLenxxxxx); + if (strLenxxxxx > 4096000) { + throw ProtocolException.stringTooLong("optionalArgHints[" + i + "]", strLenxxxxx, 4096000); + } + + if (elemPos + strVarLenxxxxx + strLenxxxxx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("optionalArgHints[" + i + "]", elemPos + strVarLenxxxxx + strLenxxxxx, buf.readableBytes()); + } + + obj.optionalArgHints[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLenxxxxx + strLenxxxxx; + } + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 49; + if ((nullBits & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 17); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Suggestions", fieldOffset0, maxEnd); + } + + int pos0 = offset + 49 + fieldOffset0; + int arrLen = VarInt.peek(buf, pos0); + pos0 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + } + + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 21); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Continuations", fieldOffset1, maxEnd); + } + + int pos1 = offset + 49 + fieldOffset1; + int arrLen = VarInt.peek(buf, pos1); + pos1 += VarInt.size(arrLen) + arrLen * 1; + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 25); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("UsageText", fieldOffset2, maxEnd); + } + + int pos2 = offset + 49 + fieldOffset2; + int sl = VarInt.peek(buf, pos2); + pos2 += VarInt.size(sl) + sl; + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + + if ((nullBits & 8) != 0) { + int fieldOffset3 = buf.getIntLE(offset + 29); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("VariantPatterns", fieldOffset3, maxEnd); + } + + int pos3 = offset + 49 + fieldOffset3; + int arrLen = VarInt.peek(buf, pos3); + pos3 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos3); + pos3 += VarInt.size(sl) + sl; + } + + if (pos3 - offset > maxEnd) { + maxEnd = pos3 - offset; + } + } + + if ((nullBits & 16) != 0) { + int fieldOffset4 = buf.getIntLE(offset + 33); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("Subcommands", fieldOffset4, maxEnd); + } + + int pos4 = offset + 49 + fieldOffset4; + int arrLen = VarInt.peek(buf, pos4); + pos4 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos4); + pos4 += VarInt.size(sl) + sl; + } + + if (pos4 - offset > maxEnd) { + maxEnd = pos4 - offset; + } + } + + if ((nullBits & 32) != 0) { + int fieldOffset5 = buf.getIntLE(offset + 37); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("SubcommandHints", fieldOffset5, maxEnd); + } + + int pos5 = offset + 49 + fieldOffset5; + int arrLen = VarInt.peek(buf, pos5); + pos5 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos5); + pos5 += VarInt.size(sl) + sl; + } + + if (pos5 - offset > maxEnd) { + maxEnd = pos5 - offset; + } + } + + if ((nullBits & 64) != 0) { + int fieldOffset6 = buf.getIntLE(offset + 41); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("OptionalArgs", fieldOffset6, maxEnd); + } + + int pos6 = offset + 49 + fieldOffset6; + int arrLen = VarInt.peek(buf, pos6); + pos6 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos6); + pos6 += VarInt.size(sl) + sl; + } + + if (pos6 - offset > maxEnd) { + maxEnd = pos6 - offset; + } + } + + if ((nullBits & 128) != 0) { + int fieldOffset7 = buf.getIntLE(offset + 45); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 49) { + throw ProtocolException.invalidOffset("OptionalArgHints", fieldOffset7, maxEnd); + } + + int pos7 = offset + 49 + fieldOffset7; + int arrLen = VarInt.peek(buf, pos7); + pos7 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos7); + pos7 += VarInt.size(sl) + sl; + } + + if (pos7 - offset > maxEnd) { + maxEnd = pos7 - offset; + } + } + + return maxEnd; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.suggestions != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.continuations != null) { + nullBits = (byte)(nullBits | 2); + } + + if (this.usageText != null) { + nullBits = (byte)(nullBits | 4); + } + + if (this.variantPatterns != null) { + nullBits = (byte)(nullBits | 8); + } + + if (this.subcommands != null) { + nullBits = (byte)(nullBits | 16); + } + + if (this.subcommandHints != null) { + nullBits = (byte)(nullBits | 32); + } + + if (this.optionalArgs != null) { + nullBits = (byte)(nullBits | 64); + } + + if (this.optionalArgHints != null) { + nullBits = (byte)(nullBits | 128); + } + + buf.writeByte(nullBits); + buf.writeIntLE(this.startPosition); + buf.writeIntLE(this.length); + buf.writeIntLE(this.currentArgStart); + buf.writeIntLE(this.currentArgLength); + int suggestionsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int continuationsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int usageTextOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int variantPatternsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int subcommandsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int subcommandHintsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int optionalArgsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int optionalArgHintsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.suggestions != null) { + buf.setIntLE(suggestionsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.suggestions.length > 4096000) { + throw ProtocolException.arrayTooLong("Suggestions", this.suggestions.length, 4096000); + } + + VarInt.write(buf, this.suggestions.length); + + for (String item : this.suggestions) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(suggestionsOffsetSlot, -1); + } + + if (this.continuations != null) { + buf.setIntLE(continuationsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.continuations.length > 4096000) { + throw ProtocolException.arrayTooLong("Continuations", this.continuations.length, 4096000); + } + + VarInt.write(buf, this.continuations.length); + + for (boolean item : this.continuations) { + buf.writeByte(item ? 1 : 0); + } + } else { + buf.setIntLE(continuationsOffsetSlot, -1); + } + + if (this.usageText != null) { + buf.setIntLE(usageTextOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.usageText, 4096000); + } else { + buf.setIntLE(usageTextOffsetSlot, -1); + } + + if (this.variantPatterns != null) { + buf.setIntLE(variantPatternsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.variantPatterns.length > 4096000) { + throw ProtocolException.arrayTooLong("VariantPatterns", this.variantPatterns.length, 4096000); + } + + VarInt.write(buf, this.variantPatterns.length); + + for (String item : this.variantPatterns) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(variantPatternsOffsetSlot, -1); + } + + if (this.subcommands != null) { + buf.setIntLE(subcommandsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.subcommands.length > 4096000) { + throw ProtocolException.arrayTooLong("Subcommands", this.subcommands.length, 4096000); + } + + VarInt.write(buf, this.subcommands.length); + + for (String item : this.subcommands) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(subcommandsOffsetSlot, -1); + } + + if (this.subcommandHints != null) { + buf.setIntLE(subcommandHintsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.subcommandHints.length > 4096000) { + throw ProtocolException.arrayTooLong("SubcommandHints", this.subcommandHints.length, 4096000); + } + + VarInt.write(buf, this.subcommandHints.length); + + for (String item : this.subcommandHints) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(subcommandHintsOffsetSlot, -1); + } + + if (this.optionalArgs != null) { + buf.setIntLE(optionalArgsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.optionalArgs.length > 4096000) { + throw ProtocolException.arrayTooLong("OptionalArgs", this.optionalArgs.length, 4096000); + } + + VarInt.write(buf, this.optionalArgs.length); + + for (String item : this.optionalArgs) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(optionalArgsOffsetSlot, -1); + } + + if (this.optionalArgHints != null) { + buf.setIntLE(optionalArgHintsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.optionalArgHints.length > 4096000) { + throw ProtocolException.arrayTooLong("OptionalArgHints", this.optionalArgHints.length, 4096000); + } + + VarInt.write(buf, this.optionalArgHints.length); + + for (String item : this.optionalArgHints) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(optionalArgHintsOffsetSlot, -1); + } + } + + @Override + public int computeSize() { + int size = 49; + if (this.suggestions != null) { + int suggestionsSize = 0; + + for (String elem : this.suggestions) { + suggestionsSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.suggestions.length) + suggestionsSize; + } + + if (this.continuations != null) { + size += VarInt.size(this.continuations.length) + this.continuations.length * 1; + } + + if (this.usageText != null) { + size += PacketIO.stringSize(this.usageText); + } + + if (this.variantPatterns != null) { + int variantPatternsSize = 0; + + for (String elem : this.variantPatterns) { + variantPatternsSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.variantPatterns.length) + variantPatternsSize; + } + + if (this.subcommands != null) { + int subcommandsSize = 0; + + for (String elem : this.subcommands) { + subcommandsSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.subcommands.length) + subcommandsSize; + } + + if (this.subcommandHints != null) { + int subcommandHintsSize = 0; + + for (String elem : this.subcommandHints) { + subcommandHintsSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.subcommandHints.length) + subcommandHintsSize; + } + + if (this.optionalArgs != null) { + int optionalArgsSize = 0; + + for (String elem : this.optionalArgs) { + optionalArgsSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.optionalArgs.length) + optionalArgsSize; + } + + if (this.optionalArgHints != null) { + int optionalArgHintsSize = 0; + + for (String elem : this.optionalArgHints) { + optionalArgHintsSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.optionalArgHints.length) + optionalArgHintsSize; + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 49) { + return ValidationResult.error("Buffer too small: expected at least 49 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 1) != 0) { + int suggestionsOffset = buffer.getIntLE(offset + 17); + if (suggestionsOffset < 0 || suggestionsOffset > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Suggestions"); + } + + int pos = offset + 49 + suggestionsOffset; + int suggestionsCount = VarInt.peek(buffer, pos); + if (suggestionsCount < 0) { + return ValidationResult.error("Invalid array count for Suggestions"); + } + + if (suggestionsCount > 4096000) { + return ValidationResult.error("Suggestions exceeds max length 4096000"); + } + + pos += VarInt.size(suggestionsCount); + + for (int i = 0; i < suggestionsCount; i++) { + int strLen = VarInt.peek(buffer, pos); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in Suggestions"); + } + + pos += VarInt.size(strLen); + pos += strLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in Suggestions"); + } + } + } + + if ((nullBits & 2) != 0) { + int continuationsOffset = buffer.getIntLE(offset + 21); + if (continuationsOffset < 0 || continuationsOffset > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Continuations"); + } + + int posx = offset + 49 + continuationsOffset; + int continuationsCount = VarInt.peek(buffer, posx); + if (continuationsCount < 0) { + return ValidationResult.error("Invalid array count for Continuations"); + } + + if (continuationsCount > 4096000) { + return ValidationResult.error("Continuations exceeds max length 4096000"); + } + + posx += VarInt.size(continuationsCount); + posx += continuationsCount * 1; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Continuations"); + } + } + + if ((nullBits & 4) != 0) { + int usageTextOffset = buffer.getIntLE(offset + 25); + if (usageTextOffset < 0 || usageTextOffset > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for UsageText"); + } + + int posxx = offset + 49 + usageTextOffset; + int usageTextLen = VarInt.peek(buffer, posxx); + if (usageTextLen < 0) { + return ValidationResult.error("Invalid string length for UsageText"); + } + + if (usageTextLen > 4096000) { + return ValidationResult.error("UsageText exceeds max length 4096000"); + } + + posxx += VarInt.size(usageTextLen); + posxx += usageTextLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading UsageText"); + } + } + + if ((nullBits & 8) != 0) { + int variantPatternsOffset = buffer.getIntLE(offset + 29); + if (variantPatternsOffset < 0 || variantPatternsOffset > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for VariantPatterns"); + } + + int posxxx = offset + 49 + variantPatternsOffset; + int variantPatternsCount = VarInt.peek(buffer, posxxx); + if (variantPatternsCount < 0) { + return ValidationResult.error("Invalid array count for VariantPatterns"); + } + + if (variantPatternsCount > 4096000) { + return ValidationResult.error("VariantPatterns exceeds max length 4096000"); + } + + posxxx += VarInt.size(variantPatternsCount); + + for (int i = 0; i < variantPatternsCount; i++) { + int strLenx = VarInt.peek(buffer, posxxx); + if (strLenx < 0) { + return ValidationResult.error("Invalid string length in VariantPatterns"); + } + + posxxx += VarInt.size(strLenx); + posxxx += strLenx; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in VariantPatterns"); + } + } + } + + if ((nullBits & 16) != 0) { + int subcommandsOffset = buffer.getIntLE(offset + 33); + if (subcommandsOffset < 0 || subcommandsOffset > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for Subcommands"); + } + + int posxxxx = offset + 49 + subcommandsOffset; + int subcommandsCount = VarInt.peek(buffer, posxxxx); + if (subcommandsCount < 0) { + return ValidationResult.error("Invalid array count for Subcommands"); + } + + if (subcommandsCount > 4096000) { + return ValidationResult.error("Subcommands exceeds max length 4096000"); + } + + posxxxx += VarInt.size(subcommandsCount); + + for (int i = 0; i < subcommandsCount; i++) { + int strLenxx = VarInt.peek(buffer, posxxxx); + if (strLenxx < 0) { + return ValidationResult.error("Invalid string length in Subcommands"); + } + + posxxxx += VarInt.size(strLenxx); + posxxxx += strLenxx; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in Subcommands"); + } + } + } + + if ((nullBits & 32) != 0) { + int subcommandHintsOffset = buffer.getIntLE(offset + 37); + if (subcommandHintsOffset < 0 || subcommandHintsOffset > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for SubcommandHints"); + } + + int posxxxxx = offset + 49 + subcommandHintsOffset; + int subcommandHintsCount = VarInt.peek(buffer, posxxxxx); + if (subcommandHintsCount < 0) { + return ValidationResult.error("Invalid array count for SubcommandHints"); + } + + if (subcommandHintsCount > 4096000) { + return ValidationResult.error("SubcommandHints exceeds max length 4096000"); + } + + posxxxxx += VarInt.size(subcommandHintsCount); + + for (int i = 0; i < subcommandHintsCount; i++) { + int strLenxxx = VarInt.peek(buffer, posxxxxx); + if (strLenxxx < 0) { + return ValidationResult.error("Invalid string length in SubcommandHints"); + } + + posxxxxx += VarInt.size(strLenxxx); + posxxxxx += strLenxxx; + if (posxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in SubcommandHints"); + } + } + } + + if ((nullBits & 64) != 0) { + int optionalArgsOffset = buffer.getIntLE(offset + 41); + if (optionalArgsOffset < 0 || optionalArgsOffset > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for OptionalArgs"); + } + + int posxxxxxx = offset + 49 + optionalArgsOffset; + int optionalArgsCount = VarInt.peek(buffer, posxxxxxx); + if (optionalArgsCount < 0) { + return ValidationResult.error("Invalid array count for OptionalArgs"); + } + + if (optionalArgsCount > 4096000) { + return ValidationResult.error("OptionalArgs exceeds max length 4096000"); + } + + posxxxxxx += VarInt.size(optionalArgsCount); + + for (int i = 0; i < optionalArgsCount; i++) { + int strLenxxxx = VarInt.peek(buffer, posxxxxxx); + if (strLenxxxx < 0) { + return ValidationResult.error("Invalid string length in OptionalArgs"); + } + + posxxxxxx += VarInt.size(strLenxxxx); + posxxxxxx += strLenxxxx; + if (posxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in OptionalArgs"); + } + } + } + + if ((nullBits & 128) != 0) { + int optionalArgHintsOffset = buffer.getIntLE(offset + 45); + if (optionalArgHintsOffset < 0 || optionalArgHintsOffset > buffer.writerIndex() - offset - 49) { + return ValidationResult.error("Invalid offset for OptionalArgHints"); + } + + int posxxxxxxx = offset + 49 + optionalArgHintsOffset; + int optionalArgHintsCount = VarInt.peek(buffer, posxxxxxxx); + if (optionalArgHintsCount < 0) { + return ValidationResult.error("Invalid array count for OptionalArgHints"); + } + + if (optionalArgHintsCount > 4096000) { + return ValidationResult.error("OptionalArgHints exceeds max length 4096000"); + } + + posxxxxxxx += VarInt.size(optionalArgHintsCount); + + for (int i = 0; i < optionalArgHintsCount; i++) { + int strLenxxxxx = VarInt.peek(buffer, posxxxxxxx); + if (strLenxxxxx < 0) { + return ValidationResult.error("Invalid string length in OptionalArgHints"); + } + + posxxxxxxx += VarInt.size(strLenxxxxx); + posxxxxxxx += strLenxxxxx; + if (posxxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in OptionalArgHints"); + } + } + } + + return ValidationResult.OK; + } + } + + public CommandSuggestionsResponse clone() { + CommandSuggestionsResponse copy = new CommandSuggestionsResponse(); + copy.startPosition = this.startPosition; + copy.length = this.length; + copy.suggestions = this.suggestions != null ? Arrays.copyOf(this.suggestions, this.suggestions.length) : null; + copy.continuations = this.continuations != null ? Arrays.copyOf(this.continuations, this.continuations.length) : null; + copy.usageText = this.usageText; + copy.currentArgStart = this.currentArgStart; + copy.currentArgLength = this.currentArgLength; + copy.variantPatterns = this.variantPatterns != null ? Arrays.copyOf(this.variantPatterns, this.variantPatterns.length) : null; + copy.subcommands = this.subcommands != null ? Arrays.copyOf(this.subcommands, this.subcommands.length) : null; + copy.subcommandHints = this.subcommandHints != null ? Arrays.copyOf(this.subcommandHints, this.subcommandHints.length) : null; + copy.optionalArgs = this.optionalArgs != null ? Arrays.copyOf(this.optionalArgs, this.optionalArgs.length) : null; + copy.optionalArgHints = this.optionalArgHints != null ? Arrays.copyOf(this.optionalArgHints, this.optionalArgHints.length) : null; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof CommandSuggestionsResponse other) + ? false + : this.startPosition == other.startPosition + && this.length == other.length + && Arrays.equals((Object[])this.suggestions, (Object[])other.suggestions) + && Arrays.equals(this.continuations, other.continuations) + && Objects.equals(this.usageText, other.usageText) + && this.currentArgStart == other.currentArgStart + && this.currentArgLength == other.currentArgLength + && Arrays.equals((Object[])this.variantPatterns, (Object[])other.variantPatterns) + && Arrays.equals((Object[])this.subcommands, (Object[])other.subcommands) + && Arrays.equals((Object[])this.subcommandHints, (Object[])other.subcommandHints) + && Arrays.equals((Object[])this.optionalArgs, (Object[])other.optionalArgs) + && Arrays.equals((Object[])this.optionalArgHints, (Object[])other.optionalArgHints); + } + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + Integer.hashCode(this.startPosition); + result = 31 * result + Integer.hashCode(this.length); + result = 31 * result + Arrays.hashCode((Object[])this.suggestions); + result = 31 * result + Arrays.hashCode(this.continuations); + result = 31 * result + Objects.hashCode(this.usageText); + result = 31 * result + Integer.hashCode(this.currentArgStart); + result = 31 * result + Integer.hashCode(this.currentArgLength); + result = 31 * result + Arrays.hashCode((Object[])this.variantPatterns); + result = 31 * result + Arrays.hashCode((Object[])this.subcommands); + result = 31 * result + Arrays.hashCode((Object[])this.subcommandHints); + result = 31 * result + Arrays.hashCode((Object[])this.optionalArgs); + return 31 * result + Arrays.hashCode((Object[])this.optionalArgHints); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CommandTreeEntry.java b/src/com/hypixel/hytale/protocol/packets/interface_/CommandTreeEntry.java new file mode 100644 index 00000000..e2a18b87 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CommandTreeEntry.java @@ -0,0 +1,1196 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class CommandTreeEntry { + public static final int NULLABLE_BIT_FIELD_SIZE = 2; + public static final int FIXED_BLOCK_SIZE = 2; + public static final int VARIABLE_FIELD_COUNT = 10; + public static final int VARIABLE_BLOCK_START = 42; + public static final int MAX_SIZE = 1677721600; + @Nullable + public String name; + @Nullable + public String[] aliases; + @Nullable + public String usageText; + @Nullable + public String description; + @Nullable + public CommandVariantEntry[] variants; + @Nullable + public CommandTreeEntry[] subcommands; + @Nullable + public String[] subcommandHints; + @Nullable + public CommandArgInfo[] requiredArgs; + @Nullable + public CommandOptionalArgEntry[] optionalArgs; + @Nullable + public CommandSuggestionOverride[] suggestionOverrides; + + public CommandTreeEntry() { + } + + public CommandTreeEntry( + @Nullable String name, + @Nullable String[] aliases, + @Nullable String usageText, + @Nullable String description, + @Nullable CommandVariantEntry[] variants, + @Nullable CommandTreeEntry[] subcommands, + @Nullable String[] subcommandHints, + @Nullable CommandArgInfo[] requiredArgs, + @Nullable CommandOptionalArgEntry[] optionalArgs, + @Nullable CommandSuggestionOverride[] suggestionOverrides + ) { + this.name = name; + this.aliases = aliases; + this.usageText = usageText; + this.description = description; + this.variants = variants; + this.subcommands = subcommands; + this.subcommandHints = subcommandHints; + this.requiredArgs = requiredArgs; + this.optionalArgs = optionalArgs; + this.suggestionOverrides = suggestionOverrides; + } + + public CommandTreeEntry(@Nonnull CommandTreeEntry other) { + this.name = other.name; + this.aliases = other.aliases; + this.usageText = other.usageText; + this.description = other.description; + this.variants = other.variants; + this.subcommands = other.subcommands; + this.subcommandHints = other.subcommandHints; + this.requiredArgs = other.requiredArgs; + this.optionalArgs = other.optionalArgs; + this.suggestionOverrides = other.suggestionOverrides; + } + + @Nonnull + public static CommandTreeEntry deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 42) { + throw ProtocolException.bufferTooSmall("CommandTreeEntry", 42, buf.readableBytes() - offset); + } else { + CommandTreeEntry obj = new CommandTreeEntry(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + if ((nullBits[0] & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 42 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits[0] & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Aliases", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 42 + varPosBase1; + int aliasesCount = VarInt.peek(buf, varPos1); + if (aliasesCount < 0) { + throw ProtocolException.invalidVarInt("Aliases"); + } + + int varIntLen = VarInt.size(aliasesCount); + if (aliasesCount > 4096000) { + throw ProtocolException.arrayTooLong("Aliases", aliasesCount, 4096000); + } + + if (varPos1 + varIntLen + aliasesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Aliases", varPos1 + varIntLen + aliasesCount * 1, buf.readableBytes()); + } + + obj.aliases = new String[aliasesCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < aliasesCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("aliases[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("aliases[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("aliases[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.aliases[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + if ((nullBits[0] & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 10); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("UsageText", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 42 + varPosBase2; + int usageTextLen = VarInt.peek(buf, varPos2); + if (usageTextLen < 0) { + throw ProtocolException.invalidVarInt("UsageText"); + } + + int usageTextVarIntLen = VarInt.size(usageTextLen); + if (usageTextLen > 4096000) { + throw ProtocolException.stringTooLong("UsageText", usageTextLen, 4096000); + } + + if (varPos2 + usageTextVarIntLen + usageTextLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("UsageText", varPos2 + usageTextVarIntLen + usageTextLen, buf.readableBytes()); + } + + obj.usageText = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits[0] & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 14); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Description", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 42 + varPosBase3; + int descriptionLen = VarInt.peek(buf, varPos3); + if (descriptionLen < 0) { + throw ProtocolException.invalidVarInt("Description"); + } + + int descriptionVarIntLen = VarInt.size(descriptionLen); + if (descriptionLen > 4096000) { + throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); + } + + if (varPos3 + descriptionVarIntLen + descriptionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Description", varPos3 + descriptionVarIntLen + descriptionLen, buf.readableBytes()); + } + + obj.description = PacketIO.readVarString(buf, varPos3, PacketIO.UTF8); + } + + if ((nullBits[0] & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 18); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Variants", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 42 + varPosBase4; + int variantsCount = VarInt.peek(buf, varPos4); + if (variantsCount < 0) { + throw ProtocolException.invalidVarInt("Variants"); + } + + int varIntLenx = VarInt.size(variantsCount); + if (variantsCount > 4096000) { + throw ProtocolException.arrayTooLong("Variants", variantsCount, 4096000); + } + + if (varPos4 + varIntLenx + variantsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Variants", varPos4 + varIntLenx + variantsCount * 1, buf.readableBytes()); + } + + obj.variants = new CommandVariantEntry[variantsCount]; + int elemPos = varPos4 + varIntLenx; + + for (int i = 0; i < variantsCount; i++) { + obj.variants[i] = CommandVariantEntry.deserialize(buf, elemPos); + elemPos += CommandVariantEntry.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[0] & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 22); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Subcommands", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 42 + varPosBase5; + int subcommandsCount = VarInt.peek(buf, varPos5); + if (subcommandsCount < 0) { + throw ProtocolException.invalidVarInt("Subcommands"); + } + + int varIntLenxx = VarInt.size(subcommandsCount); + if (subcommandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Subcommands", subcommandsCount, 4096000); + } + + if (varPos5 + varIntLenxx + subcommandsCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Subcommands", varPos5 + varIntLenxx + subcommandsCount * 2, buf.readableBytes()); + } + + obj.subcommands = new CommandTreeEntry[subcommandsCount]; + int elemPos = varPos5 + varIntLenxx; + + for (int i = 0; i < subcommandsCount; i++) { + obj.subcommands[i] = deserialize(buf, elemPos); + elemPos += computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[0] & 64) != 0) { + int varPosBase6 = buf.getIntLE(offset + 26); + if (varPosBase6 < 0 || varPosBase6 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("SubcommandHints", varPosBase6, buf.readableBytes()); + } + + int varPos6 = offset + 42 + varPosBase6; + int subcommandHintsCount = VarInt.peek(buf, varPos6); + if (subcommandHintsCount < 0) { + throw ProtocolException.invalidVarInt("SubcommandHints"); + } + + int varIntLenxxx = VarInt.size(subcommandHintsCount); + if (subcommandHintsCount > 4096000) { + throw ProtocolException.arrayTooLong("SubcommandHints", subcommandHintsCount, 4096000); + } + + if (varPos6 + varIntLenxxx + subcommandHintsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SubcommandHints", varPos6 + varIntLenxxx + subcommandHintsCount * 1, buf.readableBytes()); + } + + obj.subcommandHints = new String[subcommandHintsCount]; + int elemPos = varPos6 + varIntLenxxx; + + for (int i = 0; i < subcommandHintsCount; i++) { + int strLenx = VarInt.peek(buf, elemPos); + if (strLenx < 0) { + throw ProtocolException.invalidVarInt("subcommandHints[" + i + "]"); + } + + int strVarLenx = VarInt.size(strLenx); + if (strLenx > 4096000) { + throw ProtocolException.stringTooLong("subcommandHints[" + i + "]", strLenx, 4096000); + } + + if (elemPos + strVarLenx + strLenx > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("subcommandHints[" + i + "]", elemPos + strVarLenx + strLenx, buf.readableBytes()); + } + + obj.subcommandHints[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLenx + strLenx; + } + } + + if ((nullBits[0] & 128) != 0) { + int varPosBase7 = buf.getIntLE(offset + 30); + if (varPosBase7 < 0 || varPosBase7 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("RequiredArgs", varPosBase7, buf.readableBytes()); + } + + int varPos7 = offset + 42 + varPosBase7; + int requiredArgsCount = VarInt.peek(buf, varPos7); + if (requiredArgsCount < 0) { + throw ProtocolException.invalidVarInt("RequiredArgs"); + } + + int varIntLenxxxx = VarInt.size(requiredArgsCount); + if (requiredArgsCount > 4096000) { + throw ProtocolException.arrayTooLong("RequiredArgs", requiredArgsCount, 4096000); + } + + if (varPos7 + varIntLenxxxx + requiredArgsCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RequiredArgs", varPos7 + varIntLenxxxx + requiredArgsCount * 5, buf.readableBytes()); + } + + obj.requiredArgs = new CommandArgInfo[requiredArgsCount]; + int elemPos = varPos7 + varIntLenxxxx; + + for (int i = 0; i < requiredArgsCount; i++) { + obj.requiredArgs[i] = CommandArgInfo.deserialize(buf, elemPos); + elemPos += CommandArgInfo.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[1] & 1) != 0) { + int varPosBase8 = buf.getIntLE(offset + 34); + if (varPosBase8 < 0 || varPosBase8 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("OptionalArgs", varPosBase8, buf.readableBytes()); + } + + int varPos8 = offset + 42 + varPosBase8; + int optionalArgsCount = VarInt.peek(buf, varPos8); + if (optionalArgsCount < 0) { + throw ProtocolException.invalidVarInt("OptionalArgs"); + } + + int varIntLenxxxxx = VarInt.size(optionalArgsCount); + if (optionalArgsCount > 4096000) { + throw ProtocolException.arrayTooLong("OptionalArgs", optionalArgsCount, 4096000); + } + + if (varPos8 + varIntLenxxxxx + optionalArgsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("OptionalArgs", varPos8 + varIntLenxxxxx + optionalArgsCount * 1, buf.readableBytes()); + } + + obj.optionalArgs = new CommandOptionalArgEntry[optionalArgsCount]; + int elemPos = varPos8 + varIntLenxxxxx; + + for (int i = 0; i < optionalArgsCount; i++) { + obj.optionalArgs[i] = CommandOptionalArgEntry.deserialize(buf, elemPos); + elemPos += CommandOptionalArgEntry.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits[1] & 2) != 0) { + int varPosBase9 = buf.getIntLE(offset + 38); + if (varPosBase9 < 0 || varPosBase9 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("SuggestionOverrides", varPosBase9, buf.readableBytes()); + } + + int varPos9 = offset + 42 + varPosBase9; + int suggestionOverridesCount = VarInt.peek(buf, varPos9); + if (suggestionOverridesCount < 0) { + throw ProtocolException.invalidVarInt("SuggestionOverrides"); + } + + int varIntLenxxxxxx = VarInt.size(suggestionOverridesCount); + if (suggestionOverridesCount > 4096000) { + throw ProtocolException.arrayTooLong("SuggestionOverrides", suggestionOverridesCount, 4096000); + } + + if (varPos9 + varIntLenxxxxxx + suggestionOverridesCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SuggestionOverrides", varPos9 + varIntLenxxxxxx + suggestionOverridesCount * 9, buf.readableBytes()); + } + + obj.suggestionOverrides = new CommandSuggestionOverride[suggestionOverridesCount]; + int elemPos = varPos9 + varIntLenxxxxxx; + + for (int i = 0; i < suggestionOverridesCount; i++) { + obj.suggestionOverrides[i] = CommandSuggestionOverride.deserialize(buf, elemPos); + elemPos += CommandSuggestionOverride.computeBytesConsumed(buf, elemPos); + } + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + int maxEnd = 42; + if ((nullBits[0] & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + + int pos0 = offset + 42 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits[0] & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Aliases", fieldOffset1, maxEnd); + } + + int pos1 = offset + 42 + fieldOffset1; + int arrLen = VarInt.peek(buf, pos1); + pos1 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos1); + pos1 += VarInt.size(sl) + sl; + } + + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + if ((nullBits[0] & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 10); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("UsageText", fieldOffset2, maxEnd); + } + + int pos2 = offset + 42 + fieldOffset2; + int sl = VarInt.peek(buf, pos2); + pos2 += VarInt.size(sl) + sl; + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + + if ((nullBits[0] & 8) != 0) { + int fieldOffset3 = buf.getIntLE(offset + 14); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Description", fieldOffset3, maxEnd); + } + + int pos3 = offset + 42 + fieldOffset3; + int sl = VarInt.peek(buf, pos3); + pos3 += VarInt.size(sl) + sl; + if (pos3 - offset > maxEnd) { + maxEnd = pos3 - offset; + } + } + + if ((nullBits[0] & 16) != 0) { + int fieldOffset4 = buf.getIntLE(offset + 18); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Variants", fieldOffset4, maxEnd); + } + + int pos4 = offset + 42 + fieldOffset4; + int arrLen = VarInt.peek(buf, pos4); + pos4 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos4 += CommandVariantEntry.computeBytesConsumed(buf, pos4); + } + + if (pos4 - offset > maxEnd) { + maxEnd = pos4 - offset; + } + } + + if ((nullBits[0] & 32) != 0) { + int fieldOffset5 = buf.getIntLE(offset + 22); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("Subcommands", fieldOffset5, maxEnd); + } + + int pos5 = offset + 42 + fieldOffset5; + int arrLen = VarInt.peek(buf, pos5); + pos5 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos5 += computeBytesConsumed(buf, pos5); + } + + if (pos5 - offset > maxEnd) { + maxEnd = pos5 - offset; + } + } + + if ((nullBits[0] & 64) != 0) { + int fieldOffset6 = buf.getIntLE(offset + 26); + if (fieldOffset6 < 0 || fieldOffset6 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("SubcommandHints", fieldOffset6, maxEnd); + } + + int pos6 = offset + 42 + fieldOffset6; + int arrLen = VarInt.peek(buf, pos6); + pos6 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + int sl = VarInt.peek(buf, pos6); + pos6 += VarInt.size(sl) + sl; + } + + if (pos6 - offset > maxEnd) { + maxEnd = pos6 - offset; + } + } + + if ((nullBits[0] & 128) != 0) { + int fieldOffset7 = buf.getIntLE(offset + 30); + if (fieldOffset7 < 0 || fieldOffset7 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("RequiredArgs", fieldOffset7, maxEnd); + } + + int pos7 = offset + 42 + fieldOffset7; + int arrLen = VarInt.peek(buf, pos7); + pos7 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos7 += CommandArgInfo.computeBytesConsumed(buf, pos7); + } + + if (pos7 - offset > maxEnd) { + maxEnd = pos7 - offset; + } + } + + if ((nullBits[1] & 1) != 0) { + int fieldOffset8 = buf.getIntLE(offset + 34); + if (fieldOffset8 < 0 || fieldOffset8 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("OptionalArgs", fieldOffset8, maxEnd); + } + + int pos8 = offset + 42 + fieldOffset8; + int arrLen = VarInt.peek(buf, pos8); + pos8 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos8 += CommandOptionalArgEntry.computeBytesConsumed(buf, pos8); + } + + if (pos8 - offset > maxEnd) { + maxEnd = pos8 - offset; + } + } + + if ((nullBits[1] & 2) != 0) { + int fieldOffset9 = buf.getIntLE(offset + 38); + if (fieldOffset9 < 0 || fieldOffset9 > buf.writerIndex() - offset - 42) { + throw ProtocolException.invalidOffset("SuggestionOverrides", fieldOffset9, maxEnd); + } + + int pos9 = offset + 42 + fieldOffset9; + int arrLen = VarInt.peek(buf, pos9); + pos9 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos9 += CommandSuggestionOverride.computeBytesConsumed(buf, pos9); + } + + if (pos9 - offset > maxEnd) { + maxEnd = pos9 - offset; + } + } + + return maxEnd; + } + + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte[] nullBits = new byte[2]; + if (this.name != null) { + nullBits[0] = (byte)(nullBits[0] | 1); + } + + if (this.aliases != null) { + nullBits[0] = (byte)(nullBits[0] | 2); + } + + if (this.usageText != null) { + nullBits[0] = (byte)(nullBits[0] | 4); + } + + if (this.description != null) { + nullBits[0] = (byte)(nullBits[0] | 8); + } + + if (this.variants != null) { + nullBits[0] = (byte)(nullBits[0] | 16); + } + + if (this.subcommands != null) { + nullBits[0] = (byte)(nullBits[0] | 32); + } + + if (this.subcommandHints != null) { + nullBits[0] = (byte)(nullBits[0] | 64); + } + + if (this.requiredArgs != null) { + nullBits[0] = (byte)(nullBits[0] | 128); + } + + if (this.optionalArgs != null) { + nullBits[1] = (byte)(nullBits[1] | 1); + } + + if (this.suggestionOverrides != null) { + nullBits[1] = (byte)(nullBits[1] | 2); + } + + buf.writeBytes(nullBits); + int nameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int aliasesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int usageTextOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int descriptionOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int variantsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int subcommandsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int subcommandHintsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int requiredArgsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int optionalArgsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int suggestionOverridesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.name != null) { + buf.setIntLE(nameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.name, 4096000); + } else { + buf.setIntLE(nameOffsetSlot, -1); + } + + if (this.aliases != null) { + buf.setIntLE(aliasesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.aliases.length > 4096000) { + throw ProtocolException.arrayTooLong("Aliases", this.aliases.length, 4096000); + } + + VarInt.write(buf, this.aliases.length); + + for (String item : this.aliases) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(aliasesOffsetSlot, -1); + } + + if (this.usageText != null) { + buf.setIntLE(usageTextOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.usageText, 4096000); + } else { + buf.setIntLE(usageTextOffsetSlot, -1); + } + + if (this.description != null) { + buf.setIntLE(descriptionOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.description, 4096000); + } else { + buf.setIntLE(descriptionOffsetSlot, -1); + } + + if (this.variants != null) { + buf.setIntLE(variantsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.variants.length > 4096000) { + throw ProtocolException.arrayTooLong("Variants", this.variants.length, 4096000); + } + + VarInt.write(buf, this.variants.length); + + for (CommandVariantEntry item : this.variants) { + item.serialize(buf); + } + } else { + buf.setIntLE(variantsOffsetSlot, -1); + } + + if (this.subcommands != null) { + buf.setIntLE(subcommandsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.subcommands.length > 4096000) { + throw ProtocolException.arrayTooLong("Subcommands", this.subcommands.length, 4096000); + } + + VarInt.write(buf, this.subcommands.length); + + for (CommandTreeEntry item : this.subcommands) { + item.serialize(buf); + } + } else { + buf.setIntLE(subcommandsOffsetSlot, -1); + } + + if (this.subcommandHints != null) { + buf.setIntLE(subcommandHintsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.subcommandHints.length > 4096000) { + throw ProtocolException.arrayTooLong("SubcommandHints", this.subcommandHints.length, 4096000); + } + + VarInt.write(buf, this.subcommandHints.length); + + for (String item : this.subcommandHints) { + PacketIO.writeVarString(buf, item, 4096000); + } + } else { + buf.setIntLE(subcommandHintsOffsetSlot, -1); + } + + if (this.requiredArgs != null) { + buf.setIntLE(requiredArgsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.requiredArgs.length > 4096000) { + throw ProtocolException.arrayTooLong("RequiredArgs", this.requiredArgs.length, 4096000); + } + + VarInt.write(buf, this.requiredArgs.length); + + for (CommandArgInfo item : this.requiredArgs) { + item.serialize(buf); + } + } else { + buf.setIntLE(requiredArgsOffsetSlot, -1); + } + + if (this.optionalArgs != null) { + buf.setIntLE(optionalArgsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.optionalArgs.length > 4096000) { + throw ProtocolException.arrayTooLong("OptionalArgs", this.optionalArgs.length, 4096000); + } + + VarInt.write(buf, this.optionalArgs.length); + + for (CommandOptionalArgEntry item : this.optionalArgs) { + item.serialize(buf); + } + } else { + buf.setIntLE(optionalArgsOffsetSlot, -1); + } + + if (this.suggestionOverrides != null) { + buf.setIntLE(suggestionOverridesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.suggestionOverrides.length > 4096000) { + throw ProtocolException.arrayTooLong("SuggestionOverrides", this.suggestionOverrides.length, 4096000); + } + + VarInt.write(buf, this.suggestionOverrides.length); + + for (CommandSuggestionOverride item : this.suggestionOverrides) { + item.serialize(buf); + } + } else { + buf.setIntLE(suggestionOverridesOffsetSlot, -1); + } + } + + public int computeSize() { + int size = 42; + if (this.name != null) { + size += PacketIO.stringSize(this.name); + } + + if (this.aliases != null) { + int aliasesSize = 0; + + for (String elem : this.aliases) { + aliasesSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.aliases.length) + aliasesSize; + } + + if (this.usageText != null) { + size += PacketIO.stringSize(this.usageText); + } + + if (this.description != null) { + size += PacketIO.stringSize(this.description); + } + + if (this.variants != null) { + int variantsSize = 0; + + for (CommandVariantEntry elem : this.variants) { + variantsSize += elem.computeSize(); + } + + size += VarInt.size(this.variants.length) + variantsSize; + } + + if (this.subcommands != null) { + int subcommandsSize = 0; + + for (CommandTreeEntry elem : this.subcommands) { + subcommandsSize += elem.computeSize(); + } + + size += VarInt.size(this.subcommands.length) + subcommandsSize; + } + + if (this.subcommandHints != null) { + int subcommandHintsSize = 0; + + for (String elem : this.subcommandHints) { + subcommandHintsSize += PacketIO.stringSize(elem); + } + + size += VarInt.size(this.subcommandHints.length) + subcommandHintsSize; + } + + if (this.requiredArgs != null) { + int requiredArgsSize = 0; + + for (CommandArgInfo elem : this.requiredArgs) { + requiredArgsSize += elem.computeSize(); + } + + size += VarInt.size(this.requiredArgs.length) + requiredArgsSize; + } + + if (this.optionalArgs != null) { + int optionalArgsSize = 0; + + for (CommandOptionalArgEntry elem : this.optionalArgs) { + optionalArgsSize += elem.computeSize(); + } + + size += VarInt.size(this.optionalArgs.length) + optionalArgsSize; + } + + if (this.suggestionOverrides != null) { + int suggestionOverridesSize = 0; + + for (CommandSuggestionOverride elem : this.suggestionOverrides) { + suggestionOverridesSize += elem.computeSize(); + } + + size += VarInt.size(this.suggestionOverrides.length) + suggestionOverridesSize; + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 42) { + return ValidationResult.error("Buffer too small: expected at least 42 bytes"); + } else { + byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); + if ((nullBits[0] & 1) != 0) { + int nameOffset = buffer.getIntLE(offset + 2); + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for Name"); + } + + int pos = offset + 42 + nameOffset; + int nameLen = VarInt.peek(buffer, pos); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } + + if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); + } + + pos += VarInt.size(nameLen); + pos += nameLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); + } + } + + if ((nullBits[0] & 2) != 0) { + int aliasesOffset = buffer.getIntLE(offset + 6); + if (aliasesOffset < 0 || aliasesOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for Aliases"); + } + + int posx = offset + 42 + aliasesOffset; + int aliasesCount = VarInt.peek(buffer, posx); + if (aliasesCount < 0) { + return ValidationResult.error("Invalid array count for Aliases"); + } + + if (aliasesCount > 4096000) { + return ValidationResult.error("Aliases exceeds max length 4096000"); + } + + posx += VarInt.size(aliasesCount); + + for (int i = 0; i < aliasesCount; i++) { + int strLen = VarInt.peek(buffer, posx); + if (strLen < 0) { + return ValidationResult.error("Invalid string length in Aliases"); + } + + posx += VarInt.size(strLen); + posx += strLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in Aliases"); + } + } + } + + if ((nullBits[0] & 4) != 0) { + int usageTextOffset = buffer.getIntLE(offset + 10); + if (usageTextOffset < 0 || usageTextOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for UsageText"); + } + + int posxx = offset + 42 + usageTextOffset; + int usageTextLen = VarInt.peek(buffer, posxx); + if (usageTextLen < 0) { + return ValidationResult.error("Invalid string length for UsageText"); + } + + if (usageTextLen > 4096000) { + return ValidationResult.error("UsageText exceeds max length 4096000"); + } + + posxx += VarInt.size(usageTextLen); + posxx += usageTextLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading UsageText"); + } + } + + if ((nullBits[0] & 8) != 0) { + int descriptionOffset = buffer.getIntLE(offset + 14); + if (descriptionOffset < 0 || descriptionOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for Description"); + } + + int posxxx = offset + 42 + descriptionOffset; + int descriptionLen = VarInt.peek(buffer, posxxx); + if (descriptionLen < 0) { + return ValidationResult.error("Invalid string length for Description"); + } + + if (descriptionLen > 4096000) { + return ValidationResult.error("Description exceeds max length 4096000"); + } + + posxxx += VarInt.size(descriptionLen); + posxxx += descriptionLen; + if (posxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Description"); + } + } + + if ((nullBits[0] & 16) != 0) { + int variantsOffset = buffer.getIntLE(offset + 18); + if (variantsOffset < 0 || variantsOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for Variants"); + } + + int posxxxx = offset + 42 + variantsOffset; + int variantsCount = VarInt.peek(buffer, posxxxx); + if (variantsCount < 0) { + return ValidationResult.error("Invalid array count for Variants"); + } + + if (variantsCount > 4096000) { + return ValidationResult.error("Variants exceeds max length 4096000"); + } + + posxxxx += VarInt.size(variantsCount); + + for (int i = 0; i < variantsCount; i++) { + ValidationResult structResult = CommandVariantEntry.validateStructure(buffer, posxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CommandVariantEntry in Variants[" + i + "]: " + structResult.error()); + } + + posxxxx += CommandVariantEntry.computeBytesConsumed(buffer, posxxxx); + } + } + + if ((nullBits[0] & 32) != 0) { + int subcommandsOffset = buffer.getIntLE(offset + 22); + if (subcommandsOffset < 0 || subcommandsOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for Subcommands"); + } + + int posxxxxx = offset + 42 + subcommandsOffset; + int subcommandsCount = VarInt.peek(buffer, posxxxxx); + if (subcommandsCount < 0) { + return ValidationResult.error("Invalid array count for Subcommands"); + } + + if (subcommandsCount > 4096000) { + return ValidationResult.error("Subcommands exceeds max length 4096000"); + } + + posxxxxx += VarInt.size(subcommandsCount); + + for (int i = 0; i < subcommandsCount; i++) { + ValidationResult structResult = validateStructure(buffer, posxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CommandTreeEntry in Subcommands[" + i + "]: " + structResult.error()); + } + + posxxxxx += computeBytesConsumed(buffer, posxxxxx); + } + } + + if ((nullBits[0] & 64) != 0) { + int subcommandHintsOffset = buffer.getIntLE(offset + 26); + if (subcommandHintsOffset < 0 || subcommandHintsOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for SubcommandHints"); + } + + int posxxxxxx = offset + 42 + subcommandHintsOffset; + int subcommandHintsCount = VarInt.peek(buffer, posxxxxxx); + if (subcommandHintsCount < 0) { + return ValidationResult.error("Invalid array count for SubcommandHints"); + } + + if (subcommandHintsCount > 4096000) { + return ValidationResult.error("SubcommandHints exceeds max length 4096000"); + } + + posxxxxxx += VarInt.size(subcommandHintsCount); + + for (int i = 0; i < subcommandHintsCount; i++) { + int strLenx = VarInt.peek(buffer, posxxxxxx); + if (strLenx < 0) { + return ValidationResult.error("Invalid string length in SubcommandHints"); + } + + posxxxxxx += VarInt.size(strLenx); + posxxxxxx += strLenx; + if (posxxxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading string in SubcommandHints"); + } + } + } + + if ((nullBits[0] & 128) != 0) { + int requiredArgsOffset = buffer.getIntLE(offset + 30); + if (requiredArgsOffset < 0 || requiredArgsOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for RequiredArgs"); + } + + int posxxxxxxx = offset + 42 + requiredArgsOffset; + int requiredArgsCount = VarInt.peek(buffer, posxxxxxxx); + if (requiredArgsCount < 0) { + return ValidationResult.error("Invalid array count for RequiredArgs"); + } + + if (requiredArgsCount > 4096000) { + return ValidationResult.error("RequiredArgs exceeds max length 4096000"); + } + + posxxxxxxx += VarInt.size(requiredArgsCount); + + for (int i = 0; i < requiredArgsCount; i++) { + ValidationResult structResult = CommandArgInfo.validateStructure(buffer, posxxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CommandArgInfo in RequiredArgs[" + i + "]: " + structResult.error()); + } + + posxxxxxxx += CommandArgInfo.computeBytesConsumed(buffer, posxxxxxxx); + } + } + + if ((nullBits[1] & 1) != 0) { + int optionalArgsOffset = buffer.getIntLE(offset + 34); + if (optionalArgsOffset < 0 || optionalArgsOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for OptionalArgs"); + } + + int posxxxxxxxx = offset + 42 + optionalArgsOffset; + int optionalArgsCount = VarInt.peek(buffer, posxxxxxxxx); + if (optionalArgsCount < 0) { + return ValidationResult.error("Invalid array count for OptionalArgs"); + } + + if (optionalArgsCount > 4096000) { + return ValidationResult.error("OptionalArgs exceeds max length 4096000"); + } + + posxxxxxxxx += VarInt.size(optionalArgsCount); + + for (int i = 0; i < optionalArgsCount; i++) { + ValidationResult structResult = CommandOptionalArgEntry.validateStructure(buffer, posxxxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CommandOptionalArgEntry in OptionalArgs[" + i + "]: " + structResult.error()); + } + + posxxxxxxxx += CommandOptionalArgEntry.computeBytesConsumed(buffer, posxxxxxxxx); + } + } + + if ((nullBits[1] & 2) != 0) { + int suggestionOverridesOffset = buffer.getIntLE(offset + 38); + if (suggestionOverridesOffset < 0 || suggestionOverridesOffset > buffer.writerIndex() - offset - 42) { + return ValidationResult.error("Invalid offset for SuggestionOverrides"); + } + + int posxxxxxxxxx = offset + 42 + suggestionOverridesOffset; + int suggestionOverridesCount = VarInt.peek(buffer, posxxxxxxxxx); + if (suggestionOverridesCount < 0) { + return ValidationResult.error("Invalid array count for SuggestionOverrides"); + } + + if (suggestionOverridesCount > 4096000) { + return ValidationResult.error("SuggestionOverrides exceeds max length 4096000"); + } + + posxxxxxxxxx += VarInt.size(suggestionOverridesCount); + + for (int i = 0; i < suggestionOverridesCount; i++) { + ValidationResult structResult = CommandSuggestionOverride.validateStructure(buffer, posxxxxxxxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CommandSuggestionOverride in SuggestionOverrides[" + i + "]: " + structResult.error()); + } + + posxxxxxxxxx += CommandSuggestionOverride.computeBytesConsumed(buffer, posxxxxxxxxx); + } + } + + return ValidationResult.OK; + } + } + + public CommandTreeEntry clone() { + CommandTreeEntry copy = new CommandTreeEntry(); + copy.name = this.name; + copy.aliases = this.aliases != null ? Arrays.copyOf(this.aliases, this.aliases.length) : null; + copy.usageText = this.usageText; + copy.description = this.description; + copy.variants = this.variants != null ? Arrays.stream(this.variants).map(e -> e.clone()).toArray(CommandVariantEntry[]::new) : null; + copy.subcommands = this.subcommands != null ? Arrays.stream(this.subcommands).map(e -> e.clone()).toArray(CommandTreeEntry[]::new) : null; + copy.subcommandHints = this.subcommandHints != null ? Arrays.copyOf(this.subcommandHints, this.subcommandHints.length) : null; + copy.requiredArgs = this.requiredArgs != null ? Arrays.stream(this.requiredArgs).map(e -> e.clone()).toArray(CommandArgInfo[]::new) : null; + copy.optionalArgs = this.optionalArgs != null ? Arrays.stream(this.optionalArgs).map(e -> e.clone()).toArray(CommandOptionalArgEntry[]::new) : null; + copy.suggestionOverrides = this.suggestionOverrides != null + ? Arrays.stream(this.suggestionOverrides).map(e -> e.clone()).toArray(CommandSuggestionOverride[]::new) + : null; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof CommandTreeEntry other) + ? false + : Objects.equals(this.name, other.name) + && Arrays.equals((Object[])this.aliases, (Object[])other.aliases) + && Objects.equals(this.usageText, other.usageText) + && Objects.equals(this.description, other.description) + && Arrays.equals((Object[])this.variants, (Object[])other.variants) + && Arrays.equals((Object[])this.subcommands, (Object[])other.subcommands) + && Arrays.equals((Object[])this.subcommandHints, (Object[])other.subcommandHints) + && Arrays.equals((Object[])this.requiredArgs, (Object[])other.requiredArgs) + && Arrays.equals((Object[])this.optionalArgs, (Object[])other.optionalArgs) + && Arrays.equals((Object[])this.suggestionOverrides, (Object[])other.suggestionOverrides); + } + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + Objects.hashCode(this.name); + result = 31 * result + Arrays.hashCode((Object[])this.aliases); + result = 31 * result + Objects.hashCode(this.usageText); + result = 31 * result + Objects.hashCode(this.description); + result = 31 * result + Arrays.hashCode((Object[])this.variants); + result = 31 * result + Arrays.hashCode((Object[])this.subcommands); + result = 31 * result + Arrays.hashCode((Object[])this.subcommandHints); + result = 31 * result + Arrays.hashCode((Object[])this.requiredArgs); + result = 31 * result + Arrays.hashCode((Object[])this.optionalArgs); + return 31 * result + Arrays.hashCode((Object[])this.suggestionOverrides); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CommandTreeSync.java b/src/com/hypixel/hytale/protocol/packets/interface_/CommandTreeSync.java new file mode 100644 index 00000000..d2f941ed --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CommandTreeSync.java @@ -0,0 +1,186 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class CommandTreeSync implements Packet, ToClientPacket { + public static final int PACKET_ID = 238; + public static final boolean IS_COMPRESSED = true; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 1; + public static final int MAX_SIZE = 1677721600; + @Nullable + public CommandTreeEntry[] commands; + + @Override + public int getId() { + return 238; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public CommandTreeSync() { + } + + public CommandTreeSync(@Nullable CommandTreeEntry[] commands) { + this.commands = commands; + } + + public CommandTreeSync(@Nonnull CommandTreeSync other) { + this.commands = other.commands; + } + + @Nonnull + public static CommandTreeSync deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("CommandTreeSync", 1, buf.readableBytes() - offset); + } else { + CommandTreeSync obj = new CommandTreeSync(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int commandsCount = VarInt.peek(buf, pos); + if (commandsCount < 0) { + throw ProtocolException.invalidVarInt("Commands"); + } + + int commandsVarLen = VarInt.size(commandsCount); + if (commandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); + } + + if (pos + commandsVarLen + commandsCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Commands", pos + commandsVarLen + commandsCount * 2, buf.readableBytes()); + } + + pos += commandsVarLen; + obj.commands = new CommandTreeEntry[commandsCount]; + + for (int i = 0; i < commandsCount; i++) { + obj.commands[i] = CommandTreeEntry.deserialize(buf, pos); + pos += CommandTreeEntry.computeBytesConsumed(buf, pos); + } + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int arrLen = VarInt.peek(buf, pos); + pos += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos += CommandTreeEntry.computeBytesConsumed(buf, pos); + } + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.commands != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + if (this.commands != null) { + if (this.commands.length > 4096000) { + throw ProtocolException.arrayTooLong("Commands", this.commands.length, 4096000); + } + + VarInt.write(buf, this.commands.length); + + for (CommandTreeEntry item : this.commands) { + item.serialize(buf); + } + } + } + + @Override + public int computeSize() { + int size = 1; + if (this.commands != null) { + int commandsSize = 0; + + for (CommandTreeEntry elem : this.commands) { + commandsSize += elem.computeSize(); + } + + size += VarInt.size(this.commands.length) + commandsSize; + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int commandsCount = VarInt.peek(buffer, pos); + if (commandsCount < 0) { + return ValidationResult.error("Invalid array count for Commands"); + } + + if (commandsCount > 4096000) { + return ValidationResult.error("Commands exceeds max length 4096000"); + } + + pos += VarInt.size(commandsCount); + + for (int i = 0; i < commandsCount; i++) { + ValidationResult structResult = CommandTreeEntry.validateStructure(buffer, pos); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CommandTreeEntry in Commands[" + i + "]: " + structResult.error()); + } + + pos += CommandTreeEntry.computeBytesConsumed(buffer, pos); + } + } + + return ValidationResult.OK; + } + } + + public CommandTreeSync clone() { + CommandTreeSync copy = new CommandTreeSync(); + copy.commands = this.commands != null ? Arrays.stream(this.commands).map(e -> e.clone()).toArray(CommandTreeEntry[]::new) : null; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof CommandTreeSync other ? Arrays.equals((Object[])this.commands, (Object[])other.commands) : false; + } + } + + @Override + public int hashCode() { + int result = 1; + return 31 * result + Arrays.hashCode((Object[])this.commands); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CommandVariantEntry.java b/src/com/hypixel/hytale/protocol/packets/interface_/CommandVariantEntry.java new file mode 100644 index 00000000..c6daa2b1 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CommandVariantEntry.java @@ -0,0 +1,578 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class CommandVariantEntry { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 5; + public static final int VARIABLE_BLOCK_START = 21; + public static final int MAX_SIZE = 1677721600; + @Nullable + public String pattern; + @Nullable + public CommandArgInfo[] requiredArgs; + @Nullable + public String usageText; + @Nullable + public CommandSuggestionOverride[] suggestionOverrides; + @Nullable + public String description; + + public CommandVariantEntry() { + } + + public CommandVariantEntry( + @Nullable String pattern, + @Nullable CommandArgInfo[] requiredArgs, + @Nullable String usageText, + @Nullable CommandSuggestionOverride[] suggestionOverrides, + @Nullable String description + ) { + this.pattern = pattern; + this.requiredArgs = requiredArgs; + this.usageText = usageText; + this.suggestionOverrides = suggestionOverrides; + this.description = description; + } + + public CommandVariantEntry(@Nonnull CommandVariantEntry other) { + this.pattern = other.pattern; + this.requiredArgs = other.requiredArgs; + this.usageText = other.usageText; + this.suggestionOverrides = other.suggestionOverrides; + this.description = other.description; + } + + @Nonnull + public static CommandVariantEntry deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("CommandVariantEntry", 21, buf.readableBytes() - offset); + } else { + CommandVariantEntry obj = new CommandVariantEntry(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Pattern", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 21 + varPosBase0; + int patternLen = VarInt.peek(buf, varPos0); + if (patternLen < 0) { + throw ProtocolException.invalidVarInt("Pattern"); + } + + int patternVarIntLen = VarInt.size(patternLen); + if (patternLen > 4096000) { + throw ProtocolException.stringTooLong("Pattern", patternLen, 4096000); + } + + if (varPos0 + patternVarIntLen + patternLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Pattern", varPos0 + patternVarIntLen + patternLen, buf.readableBytes()); + } + + obj.pattern = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("RequiredArgs", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + int requiredArgsCount = VarInt.peek(buf, varPos1); + if (requiredArgsCount < 0) { + throw ProtocolException.invalidVarInt("RequiredArgs"); + } + + int varIntLen = VarInt.size(requiredArgsCount); + if (requiredArgsCount > 4096000) { + throw ProtocolException.arrayTooLong("RequiredArgs", requiredArgsCount, 4096000); + } + + if (varPos1 + varIntLen + requiredArgsCount * 5L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RequiredArgs", varPos1 + varIntLen + requiredArgsCount * 5, buf.readableBytes()); + } + + obj.requiredArgs = new CommandArgInfo[requiredArgsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < requiredArgsCount; i++) { + obj.requiredArgs[i] = CommandArgInfo.deserialize(buf, elemPos); + elemPos += CommandArgInfo.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("UsageText", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 21 + varPosBase2; + int usageTextLen = VarInt.peek(buf, varPos2); + if (usageTextLen < 0) { + throw ProtocolException.invalidVarInt("UsageText"); + } + + int usageTextVarIntLen = VarInt.size(usageTextLen); + if (usageTextLen > 4096000) { + throw ProtocolException.stringTooLong("UsageText", usageTextLen, 4096000); + } + + if (varPos2 + usageTextVarIntLen + usageTextLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("UsageText", varPos2 + usageTextVarIntLen + usageTextLen, buf.readableBytes()); + } + + obj.usageText = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 13); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("SuggestionOverrides", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 21 + varPosBase3; + int suggestionOverridesCount = VarInt.peek(buf, varPos3); + if (suggestionOverridesCount < 0) { + throw ProtocolException.invalidVarInt("SuggestionOverrides"); + } + + int varIntLenx = VarInt.size(suggestionOverridesCount); + if (suggestionOverridesCount > 4096000) { + throw ProtocolException.arrayTooLong("SuggestionOverrides", suggestionOverridesCount, 4096000); + } + + if (varPos3 + varIntLenx + suggestionOverridesCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SuggestionOverrides", varPos3 + varIntLenx + suggestionOverridesCount * 9, buf.readableBytes()); + } + + obj.suggestionOverrides = new CommandSuggestionOverride[suggestionOverridesCount]; + int elemPos = varPos3 + varIntLenx; + + for (int i = 0; i < suggestionOverridesCount; i++) { + obj.suggestionOverrides[i] = CommandSuggestionOverride.deserialize(buf, elemPos); + elemPos += CommandSuggestionOverride.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 17); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Description", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 21 + varPosBase4; + int descriptionLen = VarInt.peek(buf, varPos4); + if (descriptionLen < 0) { + throw ProtocolException.invalidVarInt("Description"); + } + + int descriptionVarIntLen = VarInt.size(descriptionLen); + if (descriptionLen > 4096000) { + throw ProtocolException.stringTooLong("Description", descriptionLen, 4096000); + } + + if (varPos4 + descriptionVarIntLen + descriptionLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Description", varPos4 + descriptionVarIntLen + descriptionLen, buf.readableBytes()); + } + + obj.description = PacketIO.readVarString(buf, varPos4, PacketIO.UTF8); + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 21; + if ((nullBits & 1) != 0) { + int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Pattern", fieldOffset0, maxEnd); + } + + int pos0 = offset + 21 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("RequiredArgs", fieldOffset1, maxEnd); + } + + int pos1 = offset + 21 + fieldOffset1; + int arrLen = VarInt.peek(buf, pos1); + pos1 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos1 += CommandArgInfo.computeBytesConsumed(buf, pos1); + } + + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("UsageText", fieldOffset2, maxEnd); + } + + int pos2 = offset + 21 + fieldOffset2; + int sl = VarInt.peek(buf, pos2); + pos2 += VarInt.size(sl) + sl; + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + + if ((nullBits & 8) != 0) { + int fieldOffset3 = buf.getIntLE(offset + 13); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("SuggestionOverrides", fieldOffset3, maxEnd); + } + + int pos3 = offset + 21 + fieldOffset3; + int arrLen = VarInt.peek(buf, pos3); + pos3 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos3 += CommandSuggestionOverride.computeBytesConsumed(buf, pos3); + } + + if (pos3 - offset > maxEnd) { + maxEnd = pos3 - offset; + } + } + + if ((nullBits & 16) != 0) { + int fieldOffset4 = buf.getIntLE(offset + 17); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Description", fieldOffset4, maxEnd); + } + + int pos4 = offset + 21 + fieldOffset4; + int sl = VarInt.peek(buf, pos4); + pos4 += VarInt.size(sl) + sl; + if (pos4 - offset > maxEnd) { + maxEnd = pos4 - offset; + } + } + + return maxEnd; + } + + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.pattern != null) { + nullBits = (byte)(nullBits | 1); + } + + if (this.requiredArgs != null) { + nullBits = (byte)(nullBits | 2); + } + + if (this.usageText != null) { + nullBits = (byte)(nullBits | 4); + } + + if (this.suggestionOverrides != null) { + nullBits = (byte)(nullBits | 8); + } + + if (this.description != null) { + nullBits = (byte)(nullBits | 16); + } + + buf.writeByte(nullBits); + int patternOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int requiredArgsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int usageTextOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int suggestionOverridesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int descriptionOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.pattern != null) { + buf.setIntLE(patternOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.pattern, 4096000); + } else { + buf.setIntLE(patternOffsetSlot, -1); + } + + if (this.requiredArgs != null) { + buf.setIntLE(requiredArgsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.requiredArgs.length > 4096000) { + throw ProtocolException.arrayTooLong("RequiredArgs", this.requiredArgs.length, 4096000); + } + + VarInt.write(buf, this.requiredArgs.length); + + for (CommandArgInfo item : this.requiredArgs) { + item.serialize(buf); + } + } else { + buf.setIntLE(requiredArgsOffsetSlot, -1); + } + + if (this.usageText != null) { + buf.setIntLE(usageTextOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.usageText, 4096000); + } else { + buf.setIntLE(usageTextOffsetSlot, -1); + } + + if (this.suggestionOverrides != null) { + buf.setIntLE(suggestionOverridesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.suggestionOverrides.length > 4096000) { + throw ProtocolException.arrayTooLong("SuggestionOverrides", this.suggestionOverrides.length, 4096000); + } + + VarInt.write(buf, this.suggestionOverrides.length); + + for (CommandSuggestionOverride item : this.suggestionOverrides) { + item.serialize(buf); + } + } else { + buf.setIntLE(suggestionOverridesOffsetSlot, -1); + } + + if (this.description != null) { + buf.setIntLE(descriptionOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.description, 4096000); + } else { + buf.setIntLE(descriptionOffsetSlot, -1); + } + } + + public int computeSize() { + int size = 21; + if (this.pattern != null) { + size += PacketIO.stringSize(this.pattern); + } + + if (this.requiredArgs != null) { + int requiredArgsSize = 0; + + for (CommandArgInfo elem : this.requiredArgs) { + requiredArgsSize += elem.computeSize(); + } + + size += VarInt.size(this.requiredArgs.length) + requiredArgsSize; + } + + if (this.usageText != null) { + size += PacketIO.stringSize(this.usageText); + } + + if (this.suggestionOverrides != null) { + int suggestionOverridesSize = 0; + + for (CommandSuggestionOverride elem : this.suggestionOverrides) { + suggestionOverridesSize += elem.computeSize(); + } + + size += VarInt.size(this.suggestionOverrides.length) + suggestionOverridesSize; + } + + if (this.description != null) { + size += PacketIO.stringSize(this.description); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 21) { + return ValidationResult.error("Buffer too small: expected at least 21 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + if ((nullBits & 1) != 0) { + int patternOffset = buffer.getIntLE(offset + 1); + if (patternOffset < 0 || patternOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for Pattern"); + } + + int pos = offset + 21 + patternOffset; + int patternLen = VarInt.peek(buffer, pos); + if (patternLen < 0) { + return ValidationResult.error("Invalid string length for Pattern"); + } + + if (patternLen > 4096000) { + return ValidationResult.error("Pattern exceeds max length 4096000"); + } + + pos += VarInt.size(patternLen); + pos += patternLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Pattern"); + } + } + + if ((nullBits & 2) != 0) { + int requiredArgsOffset = buffer.getIntLE(offset + 5); + if (requiredArgsOffset < 0 || requiredArgsOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for RequiredArgs"); + } + + int posx = offset + 21 + requiredArgsOffset; + int requiredArgsCount = VarInt.peek(buffer, posx); + if (requiredArgsCount < 0) { + return ValidationResult.error("Invalid array count for RequiredArgs"); + } + + if (requiredArgsCount > 4096000) { + return ValidationResult.error("RequiredArgs exceeds max length 4096000"); + } + + posx += VarInt.size(requiredArgsCount); + + for (int i = 0; i < requiredArgsCount; i++) { + ValidationResult structResult = CommandArgInfo.validateStructure(buffer, posx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CommandArgInfo in RequiredArgs[" + i + "]: " + structResult.error()); + } + + posx += CommandArgInfo.computeBytesConsumed(buffer, posx); + } + } + + if ((nullBits & 4) != 0) { + int usageTextOffset = buffer.getIntLE(offset + 9); + if (usageTextOffset < 0 || usageTextOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for UsageText"); + } + + int posxx = offset + 21 + usageTextOffset; + int usageTextLen = VarInt.peek(buffer, posxx); + if (usageTextLen < 0) { + return ValidationResult.error("Invalid string length for UsageText"); + } + + if (usageTextLen > 4096000) { + return ValidationResult.error("UsageText exceeds max length 4096000"); + } + + posxx += VarInt.size(usageTextLen); + posxx += usageTextLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading UsageText"); + } + } + + if ((nullBits & 8) != 0) { + int suggestionOverridesOffset = buffer.getIntLE(offset + 13); + if (suggestionOverridesOffset < 0 || suggestionOverridesOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for SuggestionOverrides"); + } + + int posxxx = offset + 21 + suggestionOverridesOffset; + int suggestionOverridesCount = VarInt.peek(buffer, posxxx); + if (suggestionOverridesCount < 0) { + return ValidationResult.error("Invalid array count for SuggestionOverrides"); + } + + if (suggestionOverridesCount > 4096000) { + return ValidationResult.error("SuggestionOverrides exceeds max length 4096000"); + } + + posxxx += VarInt.size(suggestionOverridesCount); + + for (int i = 0; i < suggestionOverridesCount; i++) { + ValidationResult structResult = CommandSuggestionOverride.validateStructure(buffer, posxxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CommandSuggestionOverride in SuggestionOverrides[" + i + "]: " + structResult.error()); + } + + posxxx += CommandSuggestionOverride.computeBytesConsumed(buffer, posxxx); + } + } + + if ((nullBits & 16) != 0) { + int descriptionOffset = buffer.getIntLE(offset + 17); + if (descriptionOffset < 0 || descriptionOffset > buffer.writerIndex() - offset - 21) { + return ValidationResult.error("Invalid offset for Description"); + } + + int posxxxx = offset + 21 + descriptionOffset; + int descriptionLen = VarInt.peek(buffer, posxxxx); + if (descriptionLen < 0) { + return ValidationResult.error("Invalid string length for Description"); + } + + if (descriptionLen > 4096000) { + return ValidationResult.error("Description exceeds max length 4096000"); + } + + posxxxx += VarInt.size(descriptionLen); + posxxxx += descriptionLen; + if (posxxxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Description"); + } + } + + return ValidationResult.OK; + } + } + + public CommandVariantEntry clone() { + CommandVariantEntry copy = new CommandVariantEntry(); + copy.pattern = this.pattern; + copy.requiredArgs = this.requiredArgs != null ? Arrays.stream(this.requiredArgs).map(e -> e.clone()).toArray(CommandArgInfo[]::new) : null; + copy.usageText = this.usageText; + copy.suggestionOverrides = this.suggestionOverrides != null + ? Arrays.stream(this.suggestionOverrides).map(e -> e.clone()).toArray(CommandSuggestionOverride[]::new) + : null; + copy.description = this.description; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof CommandVariantEntry other) + ? false + : Objects.equals(this.pattern, other.pattern) + && Arrays.equals((Object[])this.requiredArgs, (Object[])other.requiredArgs) + && Objects.equals(this.usageText, other.usageText) + && Arrays.equals((Object[])this.suggestionOverrides, (Object[])other.suggestionOverrides) + && Objects.equals(this.description, other.description); + } + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + Objects.hashCode(this.pattern); + result = 31 * result + Arrays.hashCode((Object[])this.requiredArgs); + result = 31 * result + Objects.hashCode(this.usageText); + result = 31 * result + Arrays.hashCode((Object[])this.suggestionOverrides); + return 31 * result + Objects.hashCode(this.description); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CustomHud.java b/src/com/hypixel/hytale/protocol/packets/interface_/CustomHud.java index 1543da81..1bd4a357 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/CustomHud.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CustomHud.java @@ -3,11 +3,13 @@ package com.hypixel.hytale.protocol.packets.interface_; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.VarInt; import io.netty.buffer.ByteBuf; import java.util.Arrays; +import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -15,10 +17,13 @@ public class CustomHud implements Packet, ToClientPacket { public static final int PACKET_ID = 217; public static final boolean IS_COMPRESSED = true; public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 2; - public static final int VARIABLE_FIELD_COUNT = 1; - public static final int VARIABLE_BLOCK_START = 2; + public static final int FIXED_BLOCK_SIZE = 6; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 14; public static final int MAX_SIZE = 1677721600; + @Nonnull + public String hudId = ""; + public int zOrder; public boolean clear; @Nullable public CustomUICommand[] commands; @@ -36,74 +41,139 @@ public class CustomHud implements Packet, ToClientPacket { public CustomHud() { } - public CustomHud(boolean clear, @Nullable CustomUICommand[] commands) { + public CustomHud(@Nonnull String hudId, int zOrder, boolean clear, @Nullable CustomUICommand[] commands) { + this.hudId = hudId; + this.zOrder = zOrder; this.clear = clear; this.commands = commands; } public CustomHud(@Nonnull CustomHud other) { + this.hudId = other.hudId; + this.zOrder = other.zOrder; this.clear = other.clear; this.commands = other.commands; } @Nonnull public static CustomHud deserialize(@Nonnull ByteBuf buf, int offset) { - CustomHud obj = new CustomHud(); - byte nullBits = buf.getByte(offset); - obj.clear = buf.getByte(offset + 1) != 0; - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int commandsCount = VarInt.peek(buf, pos); - if (commandsCount < 0) { - throw ProtocolException.negativeLength("Commands", commandsCount); - } + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("CustomHud", 14, buf.readableBytes() - offset); + } else { + CustomHud obj = new CustomHud(); + byte nullBits = buf.getByte(offset); + obj.zOrder = buf.getIntLE(offset + 1); + obj.clear = buf.getByte(offset + 5) != 0; + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 14) { + int varPos0 = offset + 14 + varPosBase0; + int hudIdLen = VarInt.peek(buf, varPos0); + if (hudIdLen < 0) { + throw ProtocolException.invalidVarInt("HudId"); + } else { + int hudIdVarIntLen = VarInt.size(hudIdLen); + if (hudIdLen > 4096000) { + throw ProtocolException.stringTooLong("HudId", hudIdLen, 4096000); + } else if (varPos0 + hudIdVarIntLen + hudIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("HudId", varPos0 + hudIdVarIntLen + hudIdLen, buf.readableBytes()); + } else { + obj.hudId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 1) != 0) { + varPosBase0 = buf.getIntLE(offset + 10); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Commands", varPosBase0, buf.readableBytes()); + } - if (commandsCount > 4096000) { - throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); - } + varPos0 = offset + 14 + varPosBase0; + hudIdLen = VarInt.peek(buf, varPos0); + if (hudIdLen < 0) { + throw ProtocolException.invalidVarInt("Commands"); + } - int commandsVarLen = VarInt.size(commandsCount); - if (pos + commandsVarLen + commandsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Commands", pos + commandsVarLen + commandsCount * 2, buf.readableBytes()); - } + hudIdVarIntLen = VarInt.size(hudIdLen); + if (hudIdLen > 4096000) { + throw ProtocolException.arrayTooLong("Commands", hudIdLen, 4096000); + } - pos += commandsVarLen; - obj.commands = new CustomUICommand[commandsCount]; + if (varPos0 + hudIdVarIntLen + hudIdLen * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Commands", varPos0 + hudIdVarIntLen + hudIdLen * 2, buf.readableBytes()); + } - for (int i = 0; i < commandsCount; i++) { - obj.commands[i] = CustomUICommand.deserialize(buf, pos); - pos += CustomUICommand.computeBytesConsumed(buf, pos); + obj.commands = new CustomUICommand[hudIdLen]; + int elemPos = varPos0 + hudIdVarIntLen; + + for (int i = 0; i < hudIdLen; i++) { + obj.commands[i] = CustomUICommand.deserialize(buf, elemPos); + elemPos += CustomUICommand.computeBytesConsumed(buf, elemPos); + } + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("HudId", varPosBase0, buf.readableBytes()); } } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); - - for (int i = 0; i < arrLen; i++) { - pos += CustomUICommand.computeBytesConsumed(buf, pos); + int maxEnd = 14; + int fieldOffset0 = buf.getIntLE(offset + 6); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 14) { + int pos0 = offset + 14 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; } - } - return pos - offset; + if ((nullBits & 1) != 0) { + fieldOffset0 = buf.getIntLE(offset + 10); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Commands", fieldOffset0, maxEnd); + } + + pos0 = offset + 14 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl); + + for (int i = 0; i < sl; i++) { + pos0 += CustomUICommand.computeBytesConsumed(buf, pos0); + } + + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("HudId", fieldOffset0, maxEnd); + } } @Override public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); byte nullBits = 0; if (this.commands != null) { nullBits = (byte)(nullBits | 1); } buf.writeByte(nullBits); + buf.writeIntLE(this.zOrder); buf.writeByte(this.clear ? 1 : 0); + int hudIdOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int commandsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + buf.setIntLE(hudIdOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.hudId, 4096000); if (this.commands != null) { + buf.setIntLE(commandsOffsetSlot, buf.writerIndex() - varBlockStart); if (this.commands.length > 4096000) { throw ProtocolException.arrayTooLong("Commands", this.commands.length, 4096000); } @@ -113,12 +183,15 @@ public class CustomHud implements Packet, ToClientPacket { for (CustomUICommand item : this.commands) { item.serialize(buf); } + } else { + buf.setIntLE(commandsOffsetSlot, -1); } } @Override public int computeSize() { - int size = 2; + int size = 14; + size += PacketIO.stringSize(this.hudId); if (this.commands != null) { int commandsSize = 0; @@ -133,39 +206,65 @@ public class CustomHud implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 2) { - return ValidationResult.error("Buffer too small: expected at least 2 bytes"); + if (buffer.readableBytes() - offset < 14) { + return ValidationResult.error("Buffer too small: expected at least 14 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int commandsCount = VarInt.peek(buffer, pos); - if (commandsCount < 0) { - return ValidationResult.error("Invalid array count for Commands"); - } + int hudIdOffset = buffer.getIntLE(offset + 6); + if (hudIdOffset >= 0 && hudIdOffset <= buffer.writerIndex() - offset - 14) { + int pos = offset + 14 + hudIdOffset; + int hudIdLen = VarInt.peek(buffer, pos); + if (hudIdLen < 0) { + return ValidationResult.error("Invalid string length for HudId"); + } else if (hudIdLen > 4096000) { + return ValidationResult.error("HudId exceeds max length 4096000"); + } else { + pos += VarInt.size(hudIdLen); + pos += hudIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading HudId"); + } else { + if ((nullBits & 1) != 0) { + hudIdOffset = buffer.getIntLE(offset + 10); + if (hudIdOffset < 0 || hudIdOffset > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for Commands"); + } - if (commandsCount > 4096000) { - return ValidationResult.error("Commands exceeds max length 4096000"); - } + pos = offset + 14 + hudIdOffset; + hudIdLen = VarInt.peek(buffer, pos); + if (hudIdLen < 0) { + return ValidationResult.error("Invalid array count for Commands"); + } - pos += VarInt.length(buffer, pos); + if (hudIdLen > 4096000) { + return ValidationResult.error("Commands exceeds max length 4096000"); + } - for (int i = 0; i < commandsCount; i++) { - ValidationResult structResult = CustomUICommand.validateStructure(buffer, pos); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid CustomUICommand in Commands[" + i + "]: " + structResult.error()); + pos += VarInt.size(hudIdLen); + + for (int i = 0; i < hudIdLen; i++) { + ValidationResult structResult = CustomUICommand.validateStructure(buffer, pos); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CustomUICommand in Commands[" + i + "]: " + structResult.error()); + } + + pos += CustomUICommand.computeBytesConsumed(buffer, pos); + } + } + + return ValidationResult.OK; } - - pos += CustomUICommand.computeBytesConsumed(buffer, pos); } + } else { + return ValidationResult.error("Invalid offset for HudId"); } - - return ValidationResult.OK; } } public CustomHud clone() { CustomHud copy = new CustomHud(); + copy.hudId = this.hudId; + copy.zOrder = this.zOrder; copy.clear = this.clear; copy.commands = this.commands != null ? Arrays.stream(this.commands).map(e -> e.clone()).toArray(CustomUICommand[]::new) : null; return copy; @@ -176,13 +275,20 @@ public class CustomHud implements Packet, ToClientPacket { if (this == obj) { return true; } else { - return !(obj instanceof CustomHud other) ? false : this.clear == other.clear && Arrays.equals((Object[])this.commands, (Object[])other.commands); + return !(obj instanceof CustomHud other) + ? false + : Objects.equals(this.hudId, other.hudId) + && this.zOrder == other.zOrder + && this.clear == other.clear + && Arrays.equals((Object[])this.commands, (Object[])other.commands); } } @Override public int hashCode() { int result = 1; + result = 31 * result + Objects.hashCode(this.hudId); + result = 31 * result + Integer.hashCode(this.zOrder); result = 31 * result + Boolean.hashCode(this.clear); return 31 * result + Arrays.hashCode((Object[])this.commands); } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CustomPage.java b/src/com/hypixel/hytale/protocol/packets/interface_/CustomPage.java index c64e1af4..c1ff936b 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/CustomPage.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CustomPage.java @@ -72,76 +72,100 @@ public class CustomPage implements Packet, ToClientPacket { @Nonnull public static CustomPage deserialize(@Nonnull ByteBuf buf, int offset) { - CustomPage obj = new CustomPage(); - byte nullBits = buf.getByte(offset); - obj.isInitial = buf.getByte(offset + 1) != 0; - obj.clear = buf.getByte(offset + 2) != 0; - obj.lifetime = CustomPageLifetime.fromValue(buf.getByte(offset + 3)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 16 + buf.getIntLE(offset + 4); - int keyLen = VarInt.peek(buf, varPos0); - if (keyLen < 0) { - throw ProtocolException.negativeLength("Key", keyLen); + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("CustomPage", 16, buf.readableBytes() - offset); + } else { + CustomPage obj = new CustomPage(); + byte nullBits = buf.getByte(offset); + obj.isInitial = buf.getByte(offset + 1) != 0; + obj.clear = buf.getByte(offset + 2) != 0; + obj.lifetime = CustomPageLifetime.fromValue(buf.getByte(offset + 3)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("Key", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 16 + varPosBase0; + int keyLen = VarInt.peek(buf, varPos0); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("Key"); + } + + int keyVarIntLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("Key", keyLen, 4096000); + } + + if (varPos0 + keyVarIntLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Key", varPos0 + keyVarIntLen + keyLen, buf.readableBytes()); + } + + obj.key = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("Key", keyLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 8); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("Commands", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 16 + varPosBase1; + int commandsCount = VarInt.peek(buf, varPos1); + if (commandsCount < 0) { + throw ProtocolException.invalidVarInt("Commands"); + } + + int varIntLen = VarInt.size(commandsCount); + if (commandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); + } + + if (varPos1 + varIntLen + commandsCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Commands", varPos1 + varIntLen + commandsCount * 2, buf.readableBytes()); + } + + obj.commands = new CustomUICommand[commandsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < commandsCount; i++) { + obj.commands[i] = CustomUICommand.deserialize(buf, elemPos); + elemPos += CustomUICommand.computeBytesConsumed(buf, elemPos); + } } - obj.key = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 12); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("EventBindings", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 16 + varPosBase2; + int eventBindingsCount = VarInt.peek(buf, varPos2); + if (eventBindingsCount < 0) { + throw ProtocolException.invalidVarInt("EventBindings"); + } + + int varIntLenx = VarInt.size(eventBindingsCount); + if (eventBindingsCount > 4096000) { + throw ProtocolException.arrayTooLong("EventBindings", eventBindingsCount, 4096000); + } + + if (varPos2 + varIntLenx + eventBindingsCount * 3L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EventBindings", varPos2 + varIntLenx + eventBindingsCount * 3, buf.readableBytes()); + } + + obj.eventBindings = new CustomUIEventBinding[eventBindingsCount]; + int elemPos = varPos2 + varIntLenx; + + for (int i = 0; i < eventBindingsCount; i++) { + obj.eventBindings[i] = CustomUIEventBinding.deserialize(buf, elemPos); + elemPos += CustomUIEventBinding.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 16 + buf.getIntLE(offset + 8); - int commandsCount = VarInt.peek(buf, varPos1); - if (commandsCount < 0) { - throw ProtocolException.negativeLength("Commands", commandsCount); - } - - if (commandsCount > 4096000) { - throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + commandsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Commands", varPos1 + varIntLen + commandsCount * 2, buf.readableBytes()); - } - - obj.commands = new CustomUICommand[commandsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < commandsCount; i++) { - obj.commands[i] = CustomUICommand.deserialize(buf, elemPos); - elemPos += CustomUICommand.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 16 + buf.getIntLE(offset + 12); - int eventBindingsCount = VarInt.peek(buf, varPos2); - if (eventBindingsCount < 0) { - throw ProtocolException.negativeLength("EventBindings", eventBindingsCount); - } - - if (eventBindingsCount > 4096000) { - throw ProtocolException.arrayTooLong("EventBindings", eventBindingsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + eventBindingsCount * 3L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("EventBindings", varPos2 + varIntLen + eventBindingsCount * 3, buf.readableBytes()); - } - - obj.eventBindings = new CustomUIEventBinding[eventBindingsCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < eventBindingsCount; i++) { - obj.eventBindings[i] = CustomUIEventBinding.deserialize(buf, elemPos); - elemPos += CustomUIEventBinding.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -149,9 +173,13 @@ public class CustomPage implements Packet, ToClientPacket { int maxEnd = 16; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("Key", fieldOffset0, maxEnd); + } + int pos0 = offset + 16 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -159,9 +187,13 @@ public class CustomPage implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 8); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("Commands", fieldOffset1, maxEnd); + } + int pos1 = offset + 16 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += CustomUICommand.computeBytesConsumed(buf, pos1); @@ -174,9 +206,13 @@ public class CustomPage implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 12); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 16) { + throw ProtocolException.invalidOffset("EventBindings", fieldOffset2, maxEnd); + } + int pos2 = offset + 16 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += CustomUIEventBinding.computeBytesConsumed(buf, pos2); @@ -290,98 +326,91 @@ public class CustomPage implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 16 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int keyOffset = buffer.getIntLE(offset + 4); - if (keyOffset < 0) { - return ValidationResult.error("Invalid offset for Key"); - } - - int pos = offset + 16 + keyOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Key"); - } - - int keyLen = VarInt.peek(buffer, pos); - if (keyLen < 0) { - return ValidationResult.error("Invalid string length for Key"); - } - - if (keyLen > 4096000) { - return ValidationResult.error("Key exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += keyLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Key"); - } - } - - if ((nullBits & 2) != 0) { - int commandsOffset = buffer.getIntLE(offset + 8); - if (commandsOffset < 0) { - return ValidationResult.error("Invalid offset for Commands"); - } - - int posx = offset + 16 + commandsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Commands"); - } - - int commandsCount = VarInt.peek(buffer, posx); - if (commandsCount < 0) { - return ValidationResult.error("Invalid array count for Commands"); - } - - if (commandsCount > 4096000) { - return ValidationResult.error("Commands exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - - for (int i = 0; i < commandsCount; i++) { - ValidationResult structResult = CustomUICommand.validateStructure(buffer, posx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid CustomUICommand in Commands[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 3) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid CustomPageLifetime value for Lifetime"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 4); + if (v < 0 || v > buffer.writerIndex() - offset - 16) { + return ValidationResult.error("Invalid offset for Key"); } - posx += CustomUICommand.computeBytesConsumed(buffer, posx); - } - } - - if ((nullBits & 4) != 0) { - int eventBindingsOffset = buffer.getIntLE(offset + 12); - if (eventBindingsOffset < 0) { - return ValidationResult.error("Invalid offset for EventBindings"); - } - - int posxx = offset + 16 + eventBindingsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EventBindings"); - } - - int eventBindingsCount = VarInt.peek(buffer, posxx); - if (eventBindingsCount < 0) { - return ValidationResult.error("Invalid array count for EventBindings"); - } - - if (eventBindingsCount > 4096000) { - return ValidationResult.error("EventBindings exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - - for (int i = 0; i < eventBindingsCount; i++) { - ValidationResult structResult = CustomUIEventBinding.validateStructure(buffer, posxx); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid CustomUIEventBinding in EventBindings[" + i + "]: " + structResult.error()); + int pos = offset + 16 + v; + int keyLen = VarInt.peek(buffer, pos); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for Key"); } - posxx += CustomUIEventBinding.computeBytesConsumed(buffer, posxx); - } - } + if (keyLen > 4096000) { + return ValidationResult.error("Key exceeds max length 4096000"); + } - return ValidationResult.OK; + pos += VarInt.size(keyLen); + pos += keyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Key"); + } + } + + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 8); + if (v < 0 || v > buffer.writerIndex() - offset - 16) { + return ValidationResult.error("Invalid offset for Commands"); + } + + int posx = offset + 16 + v; + int commandsCount = VarInt.peek(buffer, posx); + if (commandsCount < 0) { + return ValidationResult.error("Invalid array count for Commands"); + } + + if (commandsCount > 4096000) { + return ValidationResult.error("Commands exceeds max length 4096000"); + } + + posx += VarInt.size(commandsCount); + + for (int i = 0; i < commandsCount; i++) { + ValidationResult structResult = CustomUICommand.validateStructure(buffer, posx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CustomUICommand in Commands[" + i + "]: " + structResult.error()); + } + + posx += CustomUICommand.computeBytesConsumed(buffer, posx); + } + } + + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 12); + if (v < 0 || v > buffer.writerIndex() - offset - 16) { + return ValidationResult.error("Invalid offset for EventBindings"); + } + + int posxx = offset + 16 + v; + int eventBindingsCount = VarInt.peek(buffer, posxx); + if (eventBindingsCount < 0) { + return ValidationResult.error("Invalid array count for EventBindings"); + } + + if (eventBindingsCount > 4096000) { + return ValidationResult.error("EventBindings exceeds max length 4096000"); + } + + posxx += VarInt.size(eventBindingsCount); + + for (int i = 0; i < eventBindingsCount; i++) { + ValidationResult structResult = CustomUIEventBinding.validateStructure(buffer, posxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid CustomUIEventBinding in EventBindings[" + i + "]: " + structResult.error()); + } + + posxx += CustomUIEventBinding.computeBytesConsumed(buffer, posxx); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CustomPageEvent.java b/src/com/hypixel/hytale/protocol/packets/interface_/CustomPageEvent.java index 4ac2ee99..94ee3d73 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/CustomPageEvent.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CustomPageEvent.java @@ -50,26 +50,34 @@ public class CustomPageEvent implements Packet, ToServerPacket { @Nonnull public static CustomPageEvent deserialize(@Nonnull ByteBuf buf, int offset) { - CustomPageEvent obj = new CustomPageEvent(); - byte nullBits = buf.getByte(offset); - obj.type = CustomPageEventType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int dataLen = VarInt.peek(buf, pos); - if (dataLen < 0) { - throw ProtocolException.negativeLength("Data", dataLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("CustomPageEvent", 2, buf.readableBytes() - offset); + } else { + CustomPageEvent obj = new CustomPageEvent(); + byte nullBits = buf.getByte(offset); + obj.type = CustomPageEventType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int dataLen = VarInt.peek(buf, pos); + if (dataLen < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int dataVarLen = VarInt.size(dataLen); + if (dataLen > 4096000) { + throw ProtocolException.stringTooLong("Data", dataLen, 4096000); + } + + if (pos + dataVarLen + dataLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", pos + dataVarLen + dataLen, buf.readableBytes()); + } + + obj.data = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += dataVarLen + dataLen; } - if (dataLen > 4096000) { - throw ProtocolException.stringTooLong("Data", dataLen, 4096000); - } - - int dataVarLen = VarInt.length(buf, pos); - obj.data = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += dataVarLen + dataLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -77,7 +85,7 @@ public class CustomPageEvent implements Packet, ToServerPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -112,25 +120,30 @@ public class CustomPageEvent implements Packet, ToServerPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int dataLen = VarInt.peek(buffer, pos); - if (dataLen < 0) { - return ValidationResult.error("Invalid string length for Data"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid CustomPageEventType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int dataLen = VarInt.peek(buffer, v); + if (dataLen < 0) { + return ValidationResult.error("Invalid string length for Data"); + } + + if (dataLen > 4096000) { + return ValidationResult.error("Data exceeds max length 4096000"); + } + + v += VarInt.size(dataLen); + v += dataLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Data"); + } } - if (dataLen > 4096000) { - return ValidationResult.error("Data exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += dataLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Data"); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CustomUICommand.java b/src/com/hypixel/hytale/protocol/packets/interface_/CustomUICommand.java index 64f98bda..1f3730c2 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/CustomUICommand.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CustomUICommand.java @@ -43,52 +43,86 @@ public class CustomUICommand { @Nonnull public static CustomUICommand deserialize(@Nonnull ByteBuf buf, int offset) { - CustomUICommand obj = new CustomUICommand(); - byte nullBits = buf.getByte(offset); - obj.type = CustomUICommandType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 14 + buf.getIntLE(offset + 2); - int selectorLen = VarInt.peek(buf, varPos0); - if (selectorLen < 0) { - throw ProtocolException.negativeLength("Selector", selectorLen); + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("CustomUICommand", 14, buf.readableBytes() - offset); + } else { + CustomUICommand obj = new CustomUICommand(); + byte nullBits = buf.getByte(offset); + obj.type = CustomUICommandType.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Selector", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 14 + varPosBase0; + int selectorLen = VarInt.peek(buf, varPos0); + if (selectorLen < 0) { + throw ProtocolException.invalidVarInt("Selector"); + } + + int selectorVarIntLen = VarInt.size(selectorLen); + if (selectorLen > 4096000) { + throw ProtocolException.stringTooLong("Selector", selectorLen, 4096000); + } + + if (varPos0 + selectorVarIntLen + selectorLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Selector", varPos0 + selectorVarIntLen + selectorLen, buf.readableBytes()); + } + + obj.selector = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (selectorLen > 4096000) { - throw ProtocolException.stringTooLong("Selector", selectorLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Data", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 14 + varPosBase1; + int dataLen = VarInt.peek(buf, varPos1); + if (dataLen < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int dataVarIntLen = VarInt.size(dataLen); + if (dataLen > 4096000) { + throw ProtocolException.stringTooLong("Data", dataLen, 4096000); + } + + if (varPos1 + dataVarIntLen + dataLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", varPos1 + dataVarIntLen + dataLen, buf.readableBytes()); + } + + obj.data = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.selector = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 10); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Text", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 14 + varPosBase2; + int textLen = VarInt.peek(buf, varPos2); + if (textLen < 0) { + throw ProtocolException.invalidVarInt("Text"); + } + + int textVarIntLen = VarInt.size(textLen); + if (textLen > 4096000) { + throw ProtocolException.stringTooLong("Text", textLen, 4096000); + } + + if (varPos2 + textVarIntLen + textLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Text", varPos2 + textVarIntLen + textLen, buf.readableBytes()); + } + + obj.text = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 14 + buf.getIntLE(offset + 6); - int dataLen = VarInt.peek(buf, varPos1); - if (dataLen < 0) { - throw ProtocolException.negativeLength("Data", dataLen); - } - - if (dataLen > 4096000) { - throw ProtocolException.stringTooLong("Data", dataLen, 4096000); - } - - obj.data = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 14 + buf.getIntLE(offset + 10); - int textLen = VarInt.peek(buf, varPos2); - if (textLen < 0) { - throw ProtocolException.negativeLength("Text", textLen); - } - - if (textLen > 4096000) { - throw ProtocolException.stringTooLong("Text", textLen, 4096000); - } - - obj.text = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -96,9 +130,13 @@ public class CustomUICommand { int maxEnd = 14; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Selector", fieldOffset0, maxEnd); + } + int pos0 = offset + 14 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -106,9 +144,13 @@ public class CustomUICommand { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Data", fieldOffset1, maxEnd); + } + int pos1 = offset + 14 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -116,9 +158,13 @@ public class CustomUICommand { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 10); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Text", fieldOffset2, maxEnd); + } + int pos2 = offset + 14 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -195,88 +241,81 @@ public class CustomUICommand { return ValidationResult.error("Buffer too small: expected at least 14 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int selectorOffset = buffer.getIntLE(offset + 2); - if (selectorOffset < 0) { - return ValidationResult.error("Invalid offset for Selector"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 7) { + return ValidationResult.error("Invalid CustomUICommandType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 2); + if (v < 0 || v > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for Selector"); + } + + int pos = offset + 14 + v; + int selectorLen = VarInt.peek(buffer, pos); + if (selectorLen < 0) { + return ValidationResult.error("Invalid string length for Selector"); + } + + if (selectorLen > 4096000) { + return ValidationResult.error("Selector exceeds max length 4096000"); + } + + pos += VarInt.size(selectorLen); + pos += selectorLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Selector"); + } } - int pos = offset + 14 + selectorOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Selector"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for Data"); + } + + int posx = offset + 14 + v; + int dataLen = VarInt.peek(buffer, posx); + if (dataLen < 0) { + return ValidationResult.error("Invalid string length for Data"); + } + + if (dataLen > 4096000) { + return ValidationResult.error("Data exceeds max length 4096000"); + } + + posx += VarInt.size(dataLen); + posx += dataLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Data"); + } } - int selectorLen = VarInt.peek(buffer, pos); - if (selectorLen < 0) { - return ValidationResult.error("Invalid string length for Selector"); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 14) { + return ValidationResult.error("Invalid offset for Text"); + } + + int posxx = offset + 14 + v; + int textLen = VarInt.peek(buffer, posxx); + if (textLen < 0) { + return ValidationResult.error("Invalid string length for Text"); + } + + if (textLen > 4096000) { + return ValidationResult.error("Text exceeds max length 4096000"); + } + + posxx += VarInt.size(textLen); + posxx += textLen; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Text"); + } } - if (selectorLen > 4096000) { - return ValidationResult.error("Selector exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += selectorLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Selector"); - } + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int dataOffset = buffer.getIntLE(offset + 6); - if (dataOffset < 0) { - return ValidationResult.error("Invalid offset for Data"); - } - - int posx = offset + 14 + dataOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Data"); - } - - int dataLen = VarInt.peek(buffer, posx); - if (dataLen < 0) { - return ValidationResult.error("Invalid string length for Data"); - } - - if (dataLen > 4096000) { - return ValidationResult.error("Data exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += dataLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Data"); - } - } - - if ((nullBits & 4) != 0) { - int textOffset = buffer.getIntLE(offset + 10); - if (textOffset < 0) { - return ValidationResult.error("Invalid offset for Text"); - } - - int posxx = offset + 14 + textOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Text"); - } - - int textLen = VarInt.peek(buffer, posxx); - if (textLen < 0) { - return ValidationResult.error("Invalid string length for Text"); - } - - if (textLen > 4096000) { - return ValidationResult.error("Text exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += textLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Text"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/CustomUIEventBinding.java b/src/com/hypixel/hytale/protocol/packets/interface_/CustomUIEventBinding.java index 015e560f..bc3ab818 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/CustomUIEventBinding.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/CustomUIEventBinding.java @@ -42,39 +42,63 @@ public class CustomUIEventBinding { @Nonnull public static CustomUIEventBinding deserialize(@Nonnull ByteBuf buf, int offset) { - CustomUIEventBinding obj = new CustomUIEventBinding(); - byte nullBits = buf.getByte(offset); - obj.type = CustomUIEventBindingType.fromValue(buf.getByte(offset + 1)); - obj.locksInterface = buf.getByte(offset + 2) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 11 + buf.getIntLE(offset + 3); - int selectorLen = VarInt.peek(buf, varPos0); - if (selectorLen < 0) { - throw ProtocolException.negativeLength("Selector", selectorLen); + if (buf.readableBytes() - offset < 11) { + throw ProtocolException.bufferTooSmall("CustomUIEventBinding", 11, buf.readableBytes() - offset); + } else { + CustomUIEventBinding obj = new CustomUIEventBinding(); + byte nullBits = buf.getByte(offset); + obj.type = CustomUIEventBindingType.fromValue(buf.getByte(offset + 1)); + obj.locksInterface = buf.getByte(offset + 2) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 3); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 11) { + throw ProtocolException.invalidOffset("Selector", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 11 + varPosBase0; + int selectorLen = VarInt.peek(buf, varPos0); + if (selectorLen < 0) { + throw ProtocolException.invalidVarInt("Selector"); + } + + int selectorVarIntLen = VarInt.size(selectorLen); + if (selectorLen > 4096000) { + throw ProtocolException.stringTooLong("Selector", selectorLen, 4096000); + } + + if (varPos0 + selectorVarIntLen + selectorLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Selector", varPos0 + selectorVarIntLen + selectorLen, buf.readableBytes()); + } + + obj.selector = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (selectorLen > 4096000) { - throw ProtocolException.stringTooLong("Selector", selectorLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 7); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 11) { + throw ProtocolException.invalidOffset("Data", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 11 + varPosBase1; + int dataLen = VarInt.peek(buf, varPos1); + if (dataLen < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int dataVarIntLen = VarInt.size(dataLen); + if (dataLen > 4096000) { + throw ProtocolException.stringTooLong("Data", dataLen, 4096000); + } + + if (varPos1 + dataVarIntLen + dataLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", varPos1 + dataVarIntLen + dataLen, buf.readableBytes()); + } + + obj.data = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.selector = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 11 + buf.getIntLE(offset + 7); - int dataLen = VarInt.peek(buf, varPos1); - if (dataLen < 0) { - throw ProtocolException.negativeLength("Data", dataLen); - } - - if (dataLen > 4096000) { - throw ProtocolException.stringTooLong("Data", dataLen, 4096000); - } - - obj.data = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -82,9 +106,13 @@ public class CustomUIEventBinding { int maxEnd = 11; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 3); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 11) { + throw ProtocolException.invalidOffset("Selector", fieldOffset0, maxEnd); + } + int pos0 = offset + 11 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -92,9 +120,13 @@ public class CustomUIEventBinding { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 7); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 11) { + throw ProtocolException.invalidOffset("Data", fieldOffset1, maxEnd); + } + int pos1 = offset + 11 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -155,61 +187,58 @@ public class CustomUIEventBinding { return ValidationResult.error("Buffer too small: expected at least 11 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int selectorOffset = buffer.getIntLE(offset + 3); - if (selectorOffset < 0) { - return ValidationResult.error("Invalid offset for Selector"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 24) { + return ValidationResult.error("Invalid CustomUIEventBindingType value for Type"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 3); + if (v < 0 || v > buffer.writerIndex() - offset - 11) { + return ValidationResult.error("Invalid offset for Selector"); + } + + int pos = offset + 11 + v; + int selectorLen = VarInt.peek(buffer, pos); + if (selectorLen < 0) { + return ValidationResult.error("Invalid string length for Selector"); + } + + if (selectorLen > 4096000) { + return ValidationResult.error("Selector exceeds max length 4096000"); + } + + pos += VarInt.size(selectorLen); + pos += selectorLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Selector"); + } } - int pos = offset + 11 + selectorOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Selector"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 7); + if (v < 0 || v > buffer.writerIndex() - offset - 11) { + return ValidationResult.error("Invalid offset for Data"); + } + + int posx = offset + 11 + v; + int dataLen = VarInt.peek(buffer, posx); + if (dataLen < 0) { + return ValidationResult.error("Invalid string length for Data"); + } + + if (dataLen > 4096000) { + return ValidationResult.error("Data exceeds max length 4096000"); + } + + posx += VarInt.size(dataLen); + posx += dataLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Data"); + } } - int selectorLen = VarInt.peek(buffer, pos); - if (selectorLen < 0) { - return ValidationResult.error("Invalid string length for Selector"); - } - - if (selectorLen > 4096000) { - return ValidationResult.error("Selector exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += selectorLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Selector"); - } + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int dataOffset = buffer.getIntLE(offset + 7); - if (dataOffset < 0) { - return ValidationResult.error("Invalid offset for Data"); - } - - int posx = offset + 11 + dataOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Data"); - } - - int dataLen = VarInt.peek(buffer, posx); - if (dataLen < 0) { - return ValidationResult.error("Invalid string length for Data"); - } - - if (dataLen > 4096000) { - return ValidationResult.error("Data exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += dataLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Data"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/EditorBlocksChange.java b/src/com/hypixel/hytale/protocol/packets/interface_/EditorBlocksChange.java index 780e99d0..07ddbb71 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/EditorBlocksChange.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/EditorBlocksChange.java @@ -6,6 +6,7 @@ import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.VarInt; +import com.hypixel.hytale.protocol.packets.buildertools.ClipboardEntityChange; import io.netty.buffer.ByteBuf; import java.util.Arrays; import java.util.Objects; @@ -16,18 +17,21 @@ public class EditorBlocksChange implements Packet, ToClientPacket { public static final int PACKET_ID = 222; public static final boolean IS_COMPRESSED = true; public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 30; - public static final int VARIABLE_FIELD_COUNT = 2; - public static final int VARIABLE_BLOCK_START = 38; - public static final int MAX_SIZE = 139264048; + public static final int FIXED_BLOCK_SIZE = 31; + public static final int VARIABLE_FIELD_COUNT = 3; + public static final int VARIABLE_BLOCK_START = 43; + public static final int MAX_SIZE = 1677721600; @Nullable public EditorSelection selection; @Nullable public BlockChange[] blocksChange; @Nullable public FluidChange[] fluidsChange; + @Nullable + public ClipboardEntityChange[] entityChanges; public int blocksCount; public boolean advancedPreview; + public boolean skipPreviewRebuild; @Override public int getId() { @@ -43,94 +47,153 @@ public class EditorBlocksChange implements Packet, ToClientPacket { } public EditorBlocksChange( - @Nullable EditorSelection selection, @Nullable BlockChange[] blocksChange, @Nullable FluidChange[] fluidsChange, int blocksCount, boolean advancedPreview + @Nullable EditorSelection selection, + @Nullable BlockChange[] blocksChange, + @Nullable FluidChange[] fluidsChange, + @Nullable ClipboardEntityChange[] entityChanges, + int blocksCount, + boolean advancedPreview, + boolean skipPreviewRebuild ) { this.selection = selection; this.blocksChange = blocksChange; this.fluidsChange = fluidsChange; + this.entityChanges = entityChanges; this.blocksCount = blocksCount; this.advancedPreview = advancedPreview; + this.skipPreviewRebuild = skipPreviewRebuild; } public EditorBlocksChange(@Nonnull EditorBlocksChange other) { this.selection = other.selection; this.blocksChange = other.blocksChange; this.fluidsChange = other.fluidsChange; + this.entityChanges = other.entityChanges; this.blocksCount = other.blocksCount; this.advancedPreview = other.advancedPreview; + this.skipPreviewRebuild = other.skipPreviewRebuild; } @Nonnull public static EditorBlocksChange deserialize(@Nonnull ByteBuf buf, int offset) { - EditorBlocksChange obj = new EditorBlocksChange(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.selection = EditorSelection.deserialize(buf, offset + 1); + if (buf.readableBytes() - offset < 43) { + throw ProtocolException.bufferTooSmall("EditorBlocksChange", 43, buf.readableBytes() - offset); + } else { + EditorBlocksChange obj = new EditorBlocksChange(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.selection = EditorSelection.deserialize(buf, offset + 1); + } + + obj.blocksCount = buf.getIntLE(offset + 25); + obj.advancedPreview = buf.getByte(offset + 29) != 0; + obj.skipPreviewRebuild = buf.getByte(offset + 30) != 0; + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 31); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("BlocksChange", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 43 + varPosBase0; + int blocksChangeCount = VarInt.peek(buf, varPos0); + if (blocksChangeCount < 0) { + throw ProtocolException.invalidVarInt("BlocksChange"); + } + + int varIntLen = VarInt.size(blocksChangeCount); + if (blocksChangeCount > 4096000) { + throw ProtocolException.arrayTooLong("BlocksChange", blocksChangeCount, 4096000); + } + + if (varPos0 + varIntLen + blocksChangeCount * 17L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BlocksChange", varPos0 + varIntLen + blocksChangeCount * 17, buf.readableBytes()); + } + + obj.blocksChange = new BlockChange[blocksChangeCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < blocksChangeCount; i++) { + obj.blocksChange[i] = BlockChange.deserialize(buf, elemPos); + elemPos += BlockChange.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 35); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("FluidsChange", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 43 + varPosBase1; + int fluidsChangeCount = VarInt.peek(buf, varPos1); + if (fluidsChangeCount < 0) { + throw ProtocolException.invalidVarInt("FluidsChange"); + } + + int varIntLenx = VarInt.size(fluidsChangeCount); + if (fluidsChangeCount > 4096000) { + throw ProtocolException.arrayTooLong("FluidsChange", fluidsChangeCount, 4096000); + } + + if (varPos1 + varIntLenx + fluidsChangeCount * 17L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FluidsChange", varPos1 + varIntLenx + fluidsChangeCount * 17, buf.readableBytes()); + } + + obj.fluidsChange = new FluidChange[fluidsChangeCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < fluidsChangeCount; i++) { + obj.fluidsChange[i] = FluidChange.deserialize(buf, elemPos); + elemPos += FluidChange.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 8) != 0) { + int varPosBase2 = buf.getIntLE(offset + 39); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("EntityChanges", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 43 + varPosBase2; + int entityChangesCount = VarInt.peek(buf, varPos2); + if (entityChangesCount < 0) { + throw ProtocolException.invalidVarInt("EntityChanges"); + } + + int varIntLenxx = VarInt.size(entityChangesCount); + if (entityChangesCount > 4096000) { + throw ProtocolException.arrayTooLong("EntityChanges", entityChangesCount, 4096000); + } + + if (varPos2 + varIntLenxx + entityChangesCount * 45L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EntityChanges", varPos2 + varIntLenxx + entityChangesCount * 45, buf.readableBytes()); + } + + obj.entityChanges = new ClipboardEntityChange[entityChangesCount]; + int elemPos = varPos2 + varIntLenxx; + + for (int i = 0; i < entityChangesCount; i++) { + obj.entityChanges[i] = ClipboardEntityChange.deserialize(buf, elemPos); + elemPos += ClipboardEntityChange.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - obj.blocksCount = buf.getIntLE(offset + 25); - obj.advancedPreview = buf.getByte(offset + 29) != 0; - if ((nullBits & 2) != 0) { - int varPos0 = offset + 38 + buf.getIntLE(offset + 30); - int blocksChangeCount = VarInt.peek(buf, varPos0); - if (blocksChangeCount < 0) { - throw ProtocolException.negativeLength("BlocksChange", blocksChangeCount); - } - - if (blocksChangeCount > 4096000) { - throw ProtocolException.arrayTooLong("BlocksChange", blocksChangeCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + blocksChangeCount * 17L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("BlocksChange", varPos0 + varIntLen + blocksChangeCount * 17, buf.readableBytes()); - } - - obj.blocksChange = new BlockChange[blocksChangeCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < blocksChangeCount; i++) { - obj.blocksChange[i] = BlockChange.deserialize(buf, elemPos); - elemPos += BlockChange.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 38 + buf.getIntLE(offset + 34); - int fluidsChangeCount = VarInt.peek(buf, varPos1); - if (fluidsChangeCount < 0) { - throw ProtocolException.negativeLength("FluidsChange", fluidsChangeCount); - } - - if (fluidsChangeCount > 4096000) { - throw ProtocolException.arrayTooLong("FluidsChange", fluidsChangeCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + fluidsChangeCount * 17L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FluidsChange", varPos1 + varIntLen + fluidsChangeCount * 17, buf.readableBytes()); - } - - obj.fluidsChange = new FluidChange[fluidsChangeCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < fluidsChangeCount; i++) { - obj.fluidsChange[i] = FluidChange.deserialize(buf, elemPos); - elemPos += FluidChange.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 38; + int maxEnd = 43; if ((nullBits & 2) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 30); - int pos0 = offset + 38 + fieldOffset0; + int fieldOffset0 = buf.getIntLE(offset + 31); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("BlocksChange", fieldOffset0, maxEnd); + } + + int pos0 = offset + 43 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += BlockChange.computeBytesConsumed(buf, pos0); @@ -142,10 +205,14 @@ public class EditorBlocksChange implements Packet, ToClientPacket { } if ((nullBits & 4) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 34); - int pos1 = offset + 38 + fieldOffset1; + int fieldOffset1 = buf.getIntLE(offset + 35); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("FluidsChange", fieldOffset1, maxEnd); + } + + int pos1 = offset + 43 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += FluidChange.computeBytesConsumed(buf, pos1); @@ -156,6 +223,25 @@ public class EditorBlocksChange implements Packet, ToClientPacket { } } + if ((nullBits & 8) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 39); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 43) { + throw ProtocolException.invalidOffset("EntityChanges", fieldOffset2, maxEnd); + } + + int pos2 = offset + 43 + fieldOffset2; + int arrLen = VarInt.peek(buf, pos2); + pos2 += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos2 += ClipboardEntityChange.computeBytesConsumed(buf, pos2); + } + + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + return maxEnd; } @@ -175,6 +261,10 @@ public class EditorBlocksChange implements Packet, ToClientPacket { nullBits = (byte)(nullBits | 4); } + if (this.entityChanges != null) { + nullBits = (byte)(nullBits | 8); + } + buf.writeByte(nullBits); if (this.selection != null) { this.selection.serialize(buf); @@ -184,10 +274,13 @@ public class EditorBlocksChange implements Packet, ToClientPacket { buf.writeIntLE(this.blocksCount); buf.writeByte(this.advancedPreview ? 1 : 0); + buf.writeByte(this.skipPreviewRebuild ? 1 : 0); int blocksChangeOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int fluidsChangeOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int entityChangesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); if (this.blocksChange != null) { buf.setIntLE(blocksChangeOffsetSlot, buf.writerIndex() - varBlockStart); @@ -218,11 +311,26 @@ public class EditorBlocksChange implements Packet, ToClientPacket { } else { buf.setIntLE(fluidsChangeOffsetSlot, -1); } + + if (this.entityChanges != null) { + buf.setIntLE(entityChangesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.entityChanges.length > 4096000) { + throw ProtocolException.arrayTooLong("EntityChanges", this.entityChanges.length, 4096000); + } + + VarInt.write(buf, this.entityChanges.length); + + for (ClipboardEntityChange item : this.entityChanges) { + item.serialize(buf); + } + } else { + buf.setIntLE(entityChangesOffsetSlot, -1); + } } @Override public int computeSize() { - int size = 38; + int size = 43; if (this.blocksChange != null) { size += VarInt.size(this.blocksChange.length) + this.blocksChange.length * 17; } @@ -231,25 +339,31 @@ public class EditorBlocksChange implements Packet, ToClientPacket { size += VarInt.size(this.fluidsChange.length) + this.fluidsChange.length * 17; } + if (this.entityChanges != null) { + int entityChangesSize = 0; + + for (ClipboardEntityChange elem : this.entityChanges) { + entityChangesSize += elem.computeSize(); + } + + size += VarInt.size(this.entityChanges.length) + entityChangesSize; + } + return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 38) { - return ValidationResult.error("Buffer too small: expected at least 38 bytes"); + if (buffer.readableBytes() - offset < 43) { + return ValidationResult.error("Buffer too small: expected at least 43 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { - int blocksChangeOffset = buffer.getIntLE(offset + 30); - if (blocksChangeOffset < 0) { + int blocksChangeOffset = buffer.getIntLE(offset + 31); + if (blocksChangeOffset < 0 || blocksChangeOffset > buffer.writerIndex() - offset - 43) { return ValidationResult.error("Invalid offset for BlocksChange"); } - int pos = offset + 38 + blocksChangeOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BlocksChange"); - } - + int pos = offset + 43 + blocksChangeOffset; int blocksChangeCount = VarInt.peek(buffer, pos); if (blocksChangeCount < 0) { return ValidationResult.error("Invalid array count for BlocksChange"); @@ -259,7 +373,7 @@ public class EditorBlocksChange implements Packet, ToClientPacket { return ValidationResult.error("BlocksChange exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(blocksChangeCount); pos += blocksChangeCount * 17; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading BlocksChange"); @@ -267,16 +381,12 @@ public class EditorBlocksChange implements Packet, ToClientPacket { } if ((nullBits & 4) != 0) { - int fluidsChangeOffset = buffer.getIntLE(offset + 34); - if (fluidsChangeOffset < 0) { + int fluidsChangeOffset = buffer.getIntLE(offset + 35); + if (fluidsChangeOffset < 0 || fluidsChangeOffset > buffer.writerIndex() - offset - 43) { return ValidationResult.error("Invalid offset for FluidsChange"); } - int posx = offset + 38 + fluidsChangeOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FluidsChange"); - } - + int posx = offset + 43 + fluidsChangeOffset; int fluidsChangeCount = VarInt.peek(buffer, posx); if (fluidsChangeCount < 0) { return ValidationResult.error("Invalid array count for FluidsChange"); @@ -286,13 +396,41 @@ public class EditorBlocksChange implements Packet, ToClientPacket { return ValidationResult.error("FluidsChange exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(fluidsChangeCount); posx += fluidsChangeCount * 17; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading FluidsChange"); } } + if ((nullBits & 8) != 0) { + int entityChangesOffset = buffer.getIntLE(offset + 39); + if (entityChangesOffset < 0 || entityChangesOffset > buffer.writerIndex() - offset - 43) { + return ValidationResult.error("Invalid offset for EntityChanges"); + } + + int posxx = offset + 43 + entityChangesOffset; + int entityChangesCount = VarInt.peek(buffer, posxx); + if (entityChangesCount < 0) { + return ValidationResult.error("Invalid array count for EntityChanges"); + } + + if (entityChangesCount > 4096000) { + return ValidationResult.error("EntityChanges exceeds max length 4096000"); + } + + posxx += VarInt.size(entityChangesCount); + + for (int i = 0; i < entityChangesCount; i++) { + ValidationResult structResult = ClipboardEntityChange.validateStructure(buffer, posxx); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ClipboardEntityChange in EntityChanges[" + i + "]: " + structResult.error()); + } + + posxx += ClipboardEntityChange.computeBytesConsumed(buffer, posxx); + } + } + return ValidationResult.OK; } } @@ -302,8 +440,10 @@ public class EditorBlocksChange implements Packet, ToClientPacket { copy.selection = this.selection != null ? this.selection.clone() : null; copy.blocksChange = this.blocksChange != null ? Arrays.stream(this.blocksChange).map(e -> e.clone()).toArray(BlockChange[]::new) : null; copy.fluidsChange = this.fluidsChange != null ? Arrays.stream(this.fluidsChange).map(e -> e.clone()).toArray(FluidChange[]::new) : null; + copy.entityChanges = this.entityChanges != null ? Arrays.stream(this.entityChanges).map(e -> e.clone()).toArray(ClipboardEntityChange[]::new) : null; copy.blocksCount = this.blocksCount; copy.advancedPreview = this.advancedPreview; + copy.skipPreviewRebuild = this.skipPreviewRebuild; return copy; } @@ -317,8 +457,10 @@ public class EditorBlocksChange implements Packet, ToClientPacket { : Objects.equals(this.selection, other.selection) && Arrays.equals((Object[])this.blocksChange, (Object[])other.blocksChange) && Arrays.equals((Object[])this.fluidsChange, (Object[])other.fluidsChange) + && Arrays.equals((Object[])this.entityChanges, (Object[])other.entityChanges) && this.blocksCount == other.blocksCount - && this.advancedPreview == other.advancedPreview; + && this.advancedPreview == other.advancedPreview + && this.skipPreviewRebuild == other.skipPreviewRebuild; } } @@ -328,7 +470,9 @@ public class EditorBlocksChange implements Packet, ToClientPacket { result = 31 * result + Objects.hashCode(this.selection); result = 31 * result + Arrays.hashCode((Object[])this.blocksChange); result = 31 * result + Arrays.hashCode((Object[])this.fluidsChange); + result = 31 * result + Arrays.hashCode((Object[])this.entityChanges); result = 31 * result + Integer.hashCode(this.blocksCount); - return 31 * result + Boolean.hashCode(this.advancedPreview); + result = 31 * result + Boolean.hashCode(this.advancedPreview); + return 31 * result + Boolean.hashCode(this.skipPreviewRebuild); } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/EditorSelection.java b/src/com/hypixel/hytale/protocol/packets/interface_/EditorSelection.java index f19264a9..7a78090e 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/EditorSelection.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/EditorSelection.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.interface_; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,14 +42,18 @@ public class EditorSelection { @Nonnull public static EditorSelection deserialize(@Nonnull ByteBuf buf, int offset) { - EditorSelection obj = new EditorSelection(); - obj.minX = buf.getIntLE(offset + 0); - obj.minY = buf.getIntLE(offset + 4); - obj.minZ = buf.getIntLE(offset + 8); - obj.maxX = buf.getIntLE(offset + 12); - obj.maxY = buf.getIntLE(offset + 16); - obj.maxZ = buf.getIntLE(offset + 20); - return obj; + if (buf.readableBytes() - offset < 24) { + throw ProtocolException.bufferTooSmall("EditorSelection", 24, buf.readableBytes() - offset); + } else { + EditorSelection obj = new EditorSelection(); + obj.minX = buf.getIntLE(offset + 0); + obj.minY = buf.getIntLE(offset + 4); + obj.minZ = buf.getIntLE(offset + 8); + obj.maxX = buf.getIntLE(offset + 12); + obj.maxY = buf.getIntLE(offset + 16); + obj.maxZ = buf.getIntLE(offset + 20); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ExecuteServersidePageCommand.java b/src/com/hypixel/hytale/protocol/packets/interface_/ExecuteServersidePageCommand.java new file mode 100644 index 00000000..416ddaa7 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ExecuteServersidePageCommand.java @@ -0,0 +1,282 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ExecuteServersidePageCommand implements Packet, ToServerPacket { + public static final int PACKET_ID = 1202; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 3; + public static final int VARIABLE_BLOCK_START = 13; + public static final int MAX_SIZE = 1677721600; + @Nonnull + public String pageId = ""; + @Nonnull + public String commandId = ""; + @Nullable + public UIDataValue param; + + @Override + public int getId() { + return 1202; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public ExecuteServersidePageCommand() { + } + + public ExecuteServersidePageCommand(@Nonnull String pageId, @Nonnull String commandId, @Nullable UIDataValue param) { + this.pageId = pageId; + this.commandId = commandId; + this.param = param; + } + + public ExecuteServersidePageCommand(@Nonnull ExecuteServersidePageCommand other) { + this.pageId = other.pageId; + this.commandId = other.commandId; + this.param = other.param; + } + + @Nonnull + public static ExecuteServersidePageCommand deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("ExecuteServersidePageCommand", 13, buf.readableBytes() - offset); + } else { + ExecuteServersidePageCommand obj = new ExecuteServersidePageCommand(); + byte nullBits = buf.getByte(offset); + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 13) { + int varPos0 = offset + 13 + varPosBase0; + int pageIdLen = VarInt.peek(buf, varPos0); + if (pageIdLen < 0) { + throw ProtocolException.invalidVarInt("PageId"); + } else { + int pageIdVarIntLen = VarInt.size(pageIdLen); + if (pageIdLen > 4096000) { + throw ProtocolException.stringTooLong("PageId", pageIdLen, 4096000); + } else if (varPos0 + pageIdVarIntLen + pageIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("PageId", varPos0 + pageIdVarIntLen + pageIdLen, buf.readableBytes()); + } else { + obj.pageId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 13) { + varPos0 = offset + 13 + varPosBase0; + pageIdLen = VarInt.peek(buf, varPos0); + if (pageIdLen < 0) { + throw ProtocolException.invalidVarInt("CommandId"); + } else { + pageIdVarIntLen = VarInt.size(pageIdLen); + if (pageIdLen > 4096000) { + throw ProtocolException.stringTooLong("CommandId", pageIdLen, 4096000); + } else if (varPos0 + pageIdVarIntLen + pageIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CommandId", varPos0 + pageIdVarIntLen + pageIdLen, buf.readableBytes()); + } else { + obj.commandId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 1) != 0) { + varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Param", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 13 + varPosBase0; + obj.param = UIDataValue.deserialize(buf, varPos0); + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("CommandId", varPosBase0, buf.readableBytes()); + } + } + } + } else { + throw ProtocolException.invalidOffset("PageId", varPosBase0, buf.readableBytes()); + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 13; + int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 13) { + int pos0 = offset + 13 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 13) { + pos0 = offset + 13 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + if ((nullBits & 1) != 0) { + fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Param", fieldOffset0, maxEnd); + } + + pos0 = offset + 13 + fieldOffset0; + pos0 += UIDataValue.computeBytesConsumed(buf, pos0); + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("CommandId", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("PageId", fieldOffset0, maxEnd); + } + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.param != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + int pageIdOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int commandIdOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int paramOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + buf.setIntLE(pageIdOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.pageId, 4096000); + buf.setIntLE(commandIdOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.commandId, 4096000); + if (this.param != null) { + buf.setIntLE(paramOffsetSlot, buf.writerIndex() - varBlockStart); + this.param.serializeWithTypeId(buf); + } else { + buf.setIntLE(paramOffsetSlot, -1); + } + } + + @Override + public int computeSize() { + int size = 13; + size += PacketIO.stringSize(this.pageId); + size += PacketIO.stringSize(this.commandId); + if (this.param != null) { + size += this.param.computeSizeWithTypeId(); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 13) { + return ValidationResult.error("Buffer too small: expected at least 13 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pageIdOffset = buffer.getIntLE(offset + 1); + if (pageIdOffset >= 0 && pageIdOffset <= buffer.writerIndex() - offset - 13) { + int pos = offset + 13 + pageIdOffset; + int pageIdLen = VarInt.peek(buffer, pos); + if (pageIdLen < 0) { + return ValidationResult.error("Invalid string length for PageId"); + } else if (pageIdLen > 4096000) { + return ValidationResult.error("PageId exceeds max length 4096000"); + } else { + pos += VarInt.size(pageIdLen); + pos += pageIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading PageId"); + } else { + pageIdOffset = buffer.getIntLE(offset + 5); + if (pageIdOffset >= 0 && pageIdOffset <= buffer.writerIndex() - offset - 13) { + pos = offset + 13 + pageIdOffset; + pageIdLen = VarInt.peek(buffer, pos); + if (pageIdLen < 0) { + return ValidationResult.error("Invalid string length for CommandId"); + } else if (pageIdLen > 4096000) { + return ValidationResult.error("CommandId exceeds max length 4096000"); + } else { + pos += VarInt.size(pageIdLen); + pos += pageIdLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading CommandId"); + } else { + if ((nullBits & 1) != 0) { + pageIdOffset = buffer.getIntLE(offset + 9); + if (pageIdOffset < 0 || pageIdOffset > buffer.writerIndex() - offset - 13) { + return ValidationResult.error("Invalid offset for Param"); + } + + pos = offset + 13 + pageIdOffset; + ValidationResult paramResult = UIDataValue.validateStructure(buffer, pos); + if (!paramResult.isValid()) { + return ValidationResult.error("Invalid Param: " + paramResult.error()); + } + + pos += UIDataValue.computeBytesConsumed(buffer, pos); + } + + return ValidationResult.OK; + } + } + } else { + return ValidationResult.error("Invalid offset for CommandId"); + } + } + } + } else { + return ValidationResult.error("Invalid offset for PageId"); + } + } + } + + public ExecuteServersidePageCommand clone() { + ExecuteServersidePageCommand copy = new ExecuteServersidePageCommand(); + copy.pageId = this.pageId; + copy.commandId = this.commandId; + copy.param = this.param; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof ExecuteServersidePageCommand other) + ? false + : Objects.equals(this.pageId, other.pageId) && Objects.equals(this.commandId, other.commandId) && Objects.equals(this.param, other.param); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.pageId, this.commandId, this.param); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/FluidChange.java b/src/com/hypixel/hytale/protocol/packets/interface_/FluidChange.java index 2bcc632a..41513883 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/FluidChange.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/FluidChange.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.interface_; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -38,13 +39,17 @@ public class FluidChange { @Nonnull public static FluidChange deserialize(@Nonnull ByteBuf buf, int offset) { - FluidChange obj = new FluidChange(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - obj.fluidId = buf.getIntLE(offset + 12); - obj.fluidLevel = buf.getByte(offset + 16); - return obj; + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("FluidChange", 17, buf.readableBytes() - offset); + } else { + FluidChange obj = new FluidChange(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + obj.fluidId = buf.getIntLE(offset + 12); + obj.fluidLevel = buf.getByte(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/HideEventTitle.java b/src/com/hypixel/hytale/protocol/packets/interface_/HideEventTitle.java index 38e3b433..d545ae98 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/HideEventTitle.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/HideEventTitle.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.interface_; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class HideEventTitle implements Packet, ToClientPacket { @Nonnull public static HideEventTitle deserialize(@Nonnull ByteBuf buf, int offset) { - HideEventTitle obj = new HideEventTitle(); - obj.fadeOutDuration = buf.getFloatLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("HideEventTitle", 4, buf.readableBytes() - offset); + } else { + HideEventTitle obj = new HideEventTitle(); + obj.fadeOutDuration = buf.getFloatLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/InitServersideUICommand.java b/src/com/hypixel/hytale/protocol/packets/interface_/InitServersideUICommand.java new file mode 100644 index 00000000..56eedce8 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/InitServersideUICommand.java @@ -0,0 +1,269 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class InitServersideUICommand extends ServersideUICommand { + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 3; + public static final int VARIABLE_BLOCK_START = 13; + public static final int MAX_SIZE = 1677721600; + @Nonnull + public String id = ""; + @Nonnull + public String filePath = ""; + @Nullable + public UIObjectDataValue dataContext; + + public InitServersideUICommand() { + } + + public InitServersideUICommand(@Nonnull String id, @Nonnull String filePath, @Nullable UIObjectDataValue dataContext) { + this.id = id; + this.filePath = filePath; + this.dataContext = dataContext; + } + + public InitServersideUICommand(@Nonnull InitServersideUICommand other) { + this.id = other.id; + this.filePath = other.filePath; + this.dataContext = other.dataContext; + } + + @Nonnull + public static InitServersideUICommand deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("InitServersideUICommand", 13, buf.readableBytes() - offset); + } else { + InitServersideUICommand obj = new InitServersideUICommand(); + byte nullBits = buf.getByte(offset); + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 13) { + int varPos0 = offset + 13 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } else { + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } else if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } else { + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 13) { + varPos0 = offset + 13 + varPosBase0; + idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("FilePath"); + } else { + idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("FilePath", idLen, 4096000); + } else if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FilePath", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } else { + obj.filePath = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 1) != 0) { + varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("DataContext", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 13 + varPosBase0; + obj.dataContext = UIObjectDataValue.deserialize(buf, varPos0); + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("FilePath", varPosBase0, buf.readableBytes()); + } + } + } + } else { + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int maxEnd = 13; + int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 13) { + int pos0 = offset + 13 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 13) { + pos0 = offset + 13 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + if ((nullBits & 1) != 0) { + fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("DataContext", fieldOffset0, maxEnd); + } + + pos0 = offset + 13 + fieldOffset0; + pos0 += UIObjectDataValue.computeBytesConsumed(buf, pos0); + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("FilePath", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); + } + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + byte nullBits = 0; + if (this.dataContext != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + int idOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int filePathOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int dataContextOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + buf.setIntLE(idOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.id, 4096000); + buf.setIntLE(filePathOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.filePath, 4096000); + if (this.dataContext != null) { + buf.setIntLE(dataContextOffsetSlot, buf.writerIndex() - varBlockStart); + this.dataContext.serialize(buf); + } else { + buf.setIntLE(dataContextOffsetSlot, -1); + } + + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + int size = 13; + size += PacketIO.stringSize(this.id); + size += PacketIO.stringSize(this.filePath); + if (this.dataContext != null) { + size += this.dataContext.computeSize(); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 13) { + return ValidationResult.error("Buffer too small: expected at least 13 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int idOffset = buffer.getIntLE(offset + 1); + if (idOffset >= 0 && idOffset <= buffer.writerIndex() - offset - 13) { + int pos = offset + 13 + idOffset; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } else if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); + } else { + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); + } else { + idOffset = buffer.getIntLE(offset + 5); + if (idOffset >= 0 && idOffset <= buffer.writerIndex() - offset - 13) { + pos = offset + 13 + idOffset; + idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for FilePath"); + } else if (idLen > 4096000) { + return ValidationResult.error("FilePath exceeds max length 4096000"); + } else { + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading FilePath"); + } else { + if ((nullBits & 1) != 0) { + idOffset = buffer.getIntLE(offset + 9); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 13) { + return ValidationResult.error("Invalid offset for DataContext"); + } + + pos = offset + 13 + idOffset; + ValidationResult dataContextResult = UIObjectDataValue.validateStructure(buffer, pos); + if (!dataContextResult.isValid()) { + return ValidationResult.error("Invalid DataContext: " + dataContextResult.error()); + } + + pos += UIObjectDataValue.computeBytesConsumed(buffer, pos); + } + + return ValidationResult.OK; + } + } + } else { + return ValidationResult.error("Invalid offset for FilePath"); + } + } + } + } else { + return ValidationResult.error("Invalid offset for Id"); + } + } + } + + public InitServersideUICommand clone() { + InitServersideUICommand copy = new InitServersideUICommand(); + copy.id = this.id; + copy.filePath = this.filePath; + copy.dataContext = this.dataContext != null ? this.dataContext.clone() : null; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof InitServersideUICommand other) + ? false + : Objects.equals(this.id, other.id) && Objects.equals(this.filePath, other.filePath) && Objects.equals(this.dataContext, other.dataContext); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.id, this.filePath, this.dataContext); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/InsertDataContextCollectionItemServersideUIProperty.java b/src/com/hypixel/hytale/protocol/packets/interface_/InsertDataContextCollectionItemServersideUIProperty.java new file mode 100644 index 00000000..bb619640 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/InsertDataContextCollectionItemServersideUIProperty.java @@ -0,0 +1,188 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class InsertDataContextCollectionItemServersideUIProperty extends ServersideUICommand { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 4; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 12; + public static final int MAX_SIZE = 16385041; + @Nonnull + public String property = ""; + public int index; + @Nonnull + public UIDataValue value; + + public InsertDataContextCollectionItemServersideUIProperty() { + } + + public InsertDataContextCollectionItemServersideUIProperty(@Nonnull String property, int index, @Nonnull UIDataValue value) { + this.property = property; + this.index = index; + this.value = value; + } + + public InsertDataContextCollectionItemServersideUIProperty(@Nonnull InsertDataContextCollectionItemServersideUIProperty other) { + this.property = other.property; + this.index = other.index; + this.value = other.value; + } + + @Nonnull + public static InsertDataContextCollectionItemServersideUIProperty deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("InsertDataContextCollectionItemServersideUIProperty", 12, buf.readableBytes() - offset); + } else { + InsertDataContextCollectionItemServersideUIProperty obj = new InsertDataContextCollectionItemServersideUIProperty(); + obj.index = buf.getIntLE(offset + 0); + int varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 12) { + int varPos0 = offset + 12 + varPosBase0; + int propertyLen = VarInt.peek(buf, varPos0); + if (propertyLen < 0) { + throw ProtocolException.invalidVarInt("Property"); + } else { + int propertyVarIntLen = VarInt.size(propertyLen); + if (propertyLen > 4096000) { + throw ProtocolException.stringTooLong("Property", propertyLen, 4096000); + } else if (varPos0 + propertyVarIntLen + propertyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Property", varPos0 + propertyVarIntLen + propertyLen, buf.readableBytes()); + } else { + obj.property = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 8); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 12) { + varPos0 = offset + 12 + varPosBase0; + obj.value = UIDataValue.deserialize(buf, varPos0); + return obj; + } else { + throw ProtocolException.invalidOffset("Value", varPosBase0, buf.readableBytes()); + } + } + } + } else { + throw ProtocolException.invalidOffset("Property", varPosBase0, buf.readableBytes()); + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int maxEnd = 12; + int fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 12) { + int pos0 = offset + 12 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + fieldOffset0 = buf.getIntLE(offset + 8); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 12) { + pos0 = offset + 12 + fieldOffset0; + pos0 += UIDataValue.computeBytesConsumed(buf, pos0); + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Value", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("Property", fieldOffset0, maxEnd); + } + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeIntLE(this.index); + int propertyOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int valueOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + buf.setIntLE(propertyOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.property, 4096000); + buf.setIntLE(valueOffsetSlot, buf.writerIndex() - varBlockStart); + this.value.serializeWithTypeId(buf); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + int size = 12; + size += PacketIO.stringSize(this.property); + return size + this.value.computeSizeWithTypeId(); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 12) { + return ValidationResult.error("Buffer too small: expected at least 12 bytes"); + } else { + int propertyOffset = buffer.getIntLE(offset + 4); + if (propertyOffset >= 0 && propertyOffset <= buffer.writerIndex() - offset - 12) { + int pos = offset + 12 + propertyOffset; + int propertyLen = VarInt.peek(buffer, pos); + if (propertyLen < 0) { + return ValidationResult.error("Invalid string length for Property"); + } else if (propertyLen > 4096000) { + return ValidationResult.error("Property exceeds max length 4096000"); + } else { + pos += VarInt.size(propertyLen); + pos += propertyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Property"); + } else { + propertyOffset = buffer.getIntLE(offset + 8); + if (propertyOffset >= 0 && propertyOffset <= buffer.writerIndex() - offset - 12) { + pos = offset + 12 + propertyOffset; + ValidationResult valueResult = UIDataValue.validateStructure(buffer, pos); + if (!valueResult.isValid()) { + return ValidationResult.error("Invalid Value: " + valueResult.error()); + } else { + pos += UIDataValue.computeBytesConsumed(buffer, pos); + return ValidationResult.OK; + } + } else { + return ValidationResult.error("Invalid offset for Value"); + } + } + } + } else { + return ValidationResult.error("Invalid offset for Property"); + } + } + } + + public InsertDataContextCollectionItemServersideUIProperty clone() { + InsertDataContextCollectionItemServersideUIProperty copy = new InsertDataContextCollectionItemServersideUIProperty(); + copy.property = this.property; + copy.index = this.index; + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof InsertDataContextCollectionItemServersideUIProperty other) + ? false + : Objects.equals(this.property, other.property) && this.index == other.index && Objects.equals(this.value, other.value); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.property, this.index, this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/KillFeedMessage.java b/src/com/hypixel/hytale/protocol/packets/interface_/KillFeedMessage.java index ab17bfca..062050fc 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/KillFeedMessage.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/KillFeedMessage.java @@ -55,33 +55,57 @@ public class KillFeedMessage implements Packet, ToClientPacket { @Nonnull public static KillFeedMessage deserialize(@Nonnull ByteBuf buf, int offset) { - KillFeedMessage obj = new KillFeedMessage(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - obj.killer = FormattedMessage.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("KillFeedMessage", 13, buf.readableBytes() - offset); + } else { + KillFeedMessage obj = new KillFeedMessage(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Killer", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - obj.decedent = FormattedMessage.deserialize(buf, varPos1); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int iconLen = VarInt.peek(buf, varPos2); - if (iconLen < 0) { - throw ProtocolException.negativeLength("Icon", iconLen); + int varPos0 = offset + 13 + varPosBase0; + obj.killer = FormattedMessage.deserialize(buf, varPos0); } - if (iconLen > 4096000) { - throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Decedent", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + obj.decedent = FormattedMessage.deserialize(buf, varPos1); } - obj.icon = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Icon", varPosBase2, buf.readableBytes()); + } - return obj; + int varPos2 = offset + 13 + varPosBase2; + int iconLen = VarInt.peek(buf, varPos2); + if (iconLen < 0) { + throw ProtocolException.invalidVarInt("Icon"); + } + + int iconVarIntLen = VarInt.size(iconLen); + if (iconLen > 4096000) { + throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + } + + if (varPos2 + iconVarIntLen + iconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Icon", varPos2 + iconVarIntLen + iconLen, buf.readableBytes()); + } + + obj.icon = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -89,6 +113,10 @@ public class KillFeedMessage implements Packet, ToClientPacket { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Killer", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; pos0 += FormattedMessage.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -98,6 +126,10 @@ public class KillFeedMessage implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Decedent", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; pos1 += FormattedMessage.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -107,9 +139,13 @@ public class KillFeedMessage implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Icon", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -189,15 +225,11 @@ public class KillFeedMessage implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int killerOffset = buffer.getIntLE(offset + 1); - if (killerOffset < 0) { + if (killerOffset < 0 || killerOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Killer"); } int pos = offset + 13 + killerOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Killer"); - } - ValidationResult killerResult = FormattedMessage.validateStructure(buffer, pos); if (!killerResult.isValid()) { return ValidationResult.error("Invalid Killer: " + killerResult.error()); @@ -208,35 +240,27 @@ public class KillFeedMessage implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int decedentOffset = buffer.getIntLE(offset + 5); - if (decedentOffset < 0) { + if (decedentOffset < 0 || decedentOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Decedent"); } - int posx = offset + 13 + decedentOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Decedent"); - } - - ValidationResult decedentResult = FormattedMessage.validateStructure(buffer, posx); + int pos = offset + 13 + decedentOffset; + ValidationResult decedentResult = FormattedMessage.validateStructure(buffer, pos); if (!decedentResult.isValid()) { return ValidationResult.error("Invalid Decedent: " + decedentResult.error()); } - posx += FormattedMessage.computeBytesConsumed(buffer, posx); + pos += FormattedMessage.computeBytesConsumed(buffer, pos); } if ((nullBits & 4) != 0) { int iconOffset = buffer.getIntLE(offset + 9); - if (iconOffset < 0) { + if (iconOffset < 0 || iconOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Icon"); } - int posxx = offset + 13 + iconOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Icon"); - } - - int iconLen = VarInt.peek(buffer, posxx); + int pos = offset + 13 + iconOffset; + int iconLen = VarInt.peek(buffer, pos); if (iconLen < 0) { return ValidationResult.error("Invalid string length for Icon"); } @@ -245,9 +269,9 @@ public class KillFeedMessage implements Packet, ToClientPacket { return ValidationResult.error("Icon exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += iconLen; - if (posxx > buffer.writerIndex()) { + pos += VarInt.size(iconLen); + pos += iconLen; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Icon"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/Notification.java b/src/com/hypixel/hytale/protocol/packets/interface_/Notification.java index 1359298a..b6ca38e6 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/Notification.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/Notification.java @@ -70,39 +70,68 @@ public class Notification implements Packet, ToClientPacket { @Nonnull public static Notification deserialize(@Nonnull ByteBuf buf, int offset) { - Notification obj = new Notification(); - byte nullBits = buf.getByte(offset); - obj.style = NotificationStyle.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 18 + buf.getIntLE(offset + 2); - obj.message = FormattedMessage.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 18) { + throw ProtocolException.bufferTooSmall("Notification", 18, buf.readableBytes() - offset); + } else { + Notification obj = new Notification(); + byte nullBits = buf.getByte(offset); + obj.style = NotificationStyle.fromValue(buf.getByte(offset + 1)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Message", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 18 + buf.getIntLE(offset + 6); - obj.secondaryMessage = FormattedMessage.deserialize(buf, varPos1); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 18 + buf.getIntLE(offset + 10); - int iconLen = VarInt.peek(buf, varPos2); - if (iconLen < 0) { - throw ProtocolException.negativeLength("Icon", iconLen); + int varPos0 = offset + 18 + varPosBase0; + obj.message = FormattedMessage.deserialize(buf, varPos0); } - if (iconLen > 4096000) { - throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("SecondaryMessage", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 18 + varPosBase1; + obj.secondaryMessage = FormattedMessage.deserialize(buf, varPos1); } - obj.icon = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 10); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Icon", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 18 + buf.getIntLE(offset + 14); - obj.item = ItemWithAllMetadata.deserialize(buf, varPos3); - } + int varPos2 = offset + 18 + varPosBase2; + int iconLen = VarInt.peek(buf, varPos2); + if (iconLen < 0) { + throw ProtocolException.invalidVarInt("Icon"); + } - return obj; + int iconVarIntLen = VarInt.size(iconLen); + if (iconLen > 4096000) { + throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + } + + if (varPos2 + iconVarIntLen + iconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Icon", varPos2 + iconVarIntLen + iconLen, buf.readableBytes()); + } + + obj.icon = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 14); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Item", varPosBase3, buf.readableBytes()); + } + + int varPos3 = offset + 18 + varPosBase3; + obj.item = ItemWithAllMetadata.deserialize(buf, varPos3); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -110,6 +139,10 @@ public class Notification implements Packet, ToClientPacket { int maxEnd = 18; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Message", fieldOffset0, maxEnd); + } + int pos0 = offset + 18 + fieldOffset0; pos0 += FormattedMessage.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -119,6 +152,10 @@ public class Notification implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("SecondaryMessage", fieldOffset1, maxEnd); + } + int pos1 = offset + 18 + fieldOffset1; pos1 += FormattedMessage.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -128,9 +165,13 @@ public class Notification implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 10); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Icon", fieldOffset2, maxEnd); + } + int pos2 = offset + 18 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -138,6 +179,10 @@ public class Notification implements Packet, ToClientPacket { if ((nullBits & 8) != 0) { int fieldOffset3 = buf.getIntLE(offset + 14); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Item", fieldOffset3, maxEnd); + } + int pos3 = offset + 18 + fieldOffset3; pos3 += ItemWithAllMetadata.computeBytesConsumed(buf, pos3); if (pos3 - offset > maxEnd) { @@ -235,91 +280,80 @@ public class Notification implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 18 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int messageOffset = buffer.getIntLE(offset + 2); - if (messageOffset < 0) { - return ValidationResult.error("Invalid offset for Message"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid NotificationStyle value for Style"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 2); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Message"); + } + + int pos = offset + 18 + v; + ValidationResult messageResult = FormattedMessage.validateStructure(buffer, pos); + if (!messageResult.isValid()) { + return ValidationResult.error("Invalid Message: " + messageResult.error()); + } + + pos += FormattedMessage.computeBytesConsumed(buffer, pos); } - int pos = offset + 18 + messageOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Message"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for SecondaryMessage"); + } + + int pos = offset + 18 + v; + ValidationResult secondaryMessageResult = FormattedMessage.validateStructure(buffer, pos); + if (!secondaryMessageResult.isValid()) { + return ValidationResult.error("Invalid SecondaryMessage: " + secondaryMessageResult.error()); + } + + pos += FormattedMessage.computeBytesConsumed(buffer, pos); } - ValidationResult messageResult = FormattedMessage.validateStructure(buffer, pos); - if (!messageResult.isValid()) { - return ValidationResult.error("Invalid Message: " + messageResult.error()); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Icon"); + } + + int pos = offset + 18 + v; + int iconLen = VarInt.peek(buffer, pos); + if (iconLen < 0) { + return ValidationResult.error("Invalid string length for Icon"); + } + + if (iconLen > 4096000) { + return ValidationResult.error("Icon exceeds max length 4096000"); + } + + pos += VarInt.size(iconLen); + pos += iconLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Icon"); + } } - pos += FormattedMessage.computeBytesConsumed(buffer, pos); + if ((nullBits & 8) != 0) { + v = buffer.getIntLE(offset + 14); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Item"); + } + + int posx = offset + 18 + v; + ValidationResult itemResult = ItemWithAllMetadata.validateStructure(buffer, posx); + if (!itemResult.isValid()) { + return ValidationResult.error("Invalid Item: " + itemResult.error()); + } + + posx += ItemWithAllMetadata.computeBytesConsumed(buffer, posx); + } + + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int secondaryMessageOffset = buffer.getIntLE(offset + 6); - if (secondaryMessageOffset < 0) { - return ValidationResult.error("Invalid offset for SecondaryMessage"); - } - - int posx = offset + 18 + secondaryMessageOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SecondaryMessage"); - } - - ValidationResult secondaryMessageResult = FormattedMessage.validateStructure(buffer, posx); - if (!secondaryMessageResult.isValid()) { - return ValidationResult.error("Invalid SecondaryMessage: " + secondaryMessageResult.error()); - } - - posx += FormattedMessage.computeBytesConsumed(buffer, posx); - } - - if ((nullBits & 4) != 0) { - int iconOffset = buffer.getIntLE(offset + 10); - if (iconOffset < 0) { - return ValidationResult.error("Invalid offset for Icon"); - } - - int posxx = offset + 18 + iconOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Icon"); - } - - int iconLen = VarInt.peek(buffer, posxx); - if (iconLen < 0) { - return ValidationResult.error("Invalid string length for Icon"); - } - - if (iconLen > 4096000) { - return ValidationResult.error("Icon exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += iconLen; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Icon"); - } - } - - if ((nullBits & 8) != 0) { - int itemOffset = buffer.getIntLE(offset + 14); - if (itemOffset < 0) { - return ValidationResult.error("Invalid offset for Item"); - } - - int posxxx = offset + 18 + itemOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Item"); - } - - ValidationResult itemResult = ItemWithAllMetadata.validateStructure(buffer, posxxx); - if (!itemResult.isValid()) { - return ValidationResult.error("Invalid Item: " + itemResult.error()); - } - - posxxx += ItemWithAllMetadata.computeBytesConsumed(buffer, posxxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/OpenChatWithCommand.java b/src/com/hypixel/hytale/protocol/packets/interface_/OpenChatWithCommand.java index 66148885..532e9e7e 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/OpenChatWithCommand.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/OpenChatWithCommand.java @@ -46,25 +46,33 @@ public class OpenChatWithCommand implements Packet, ToClientPacket { @Nonnull public static OpenChatWithCommand deserialize(@Nonnull ByteBuf buf, int offset) { - OpenChatWithCommand obj = new OpenChatWithCommand(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int commandLen = VarInt.peek(buf, pos); - if (commandLen < 0) { - throw ProtocolException.negativeLength("Command", commandLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("OpenChatWithCommand", 1, buf.readableBytes() - offset); + } else { + OpenChatWithCommand obj = new OpenChatWithCommand(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int commandLen = VarInt.peek(buf, pos); + if (commandLen < 0) { + throw ProtocolException.invalidVarInt("Command"); + } + + int commandVarLen = VarInt.size(commandLen); + if (commandLen > 4096000) { + throw ProtocolException.stringTooLong("Command", commandLen, 4096000); + } + + if (pos + commandVarLen + commandLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Command", pos + commandVarLen + commandLen, buf.readableBytes()); + } + + obj.command = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += commandVarLen + commandLen; } - if (commandLen > 4096000) { - throw ProtocolException.stringTooLong("Command", commandLen, 4096000); - } - - int commandVarLen = VarInt.length(buf, pos); - obj.command = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += commandVarLen + commandLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +80,7 @@ public class OpenChatWithCommand implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -117,7 +125,7 @@ public class OpenChatWithCommand implements Packet, ToClientPacket { return ValidationResult.error("Command exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(commandLen); pos += commandLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Command"); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/Page.java b/src/com/hypixel/hytale/protocol/packets/interface_/Page.java index 2e2d2741..5230b11f 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/Page.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/Page.java @@ -10,7 +10,8 @@ public enum Page { Map(4), MachinimaEditor(5), ContentCreation(6), - Custom(7); + Custom(7), + Serverside(8); public static final Page[] VALUES = values(); private final int value; diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/PortalDef.java b/src/com/hypixel/hytale/protocol/packets/interface_/PortalDef.java index 0e8e92b0..1ed2a33f 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/PortalDef.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/PortalDef.java @@ -37,27 +37,35 @@ public class PortalDef { @Nonnull public static PortalDef deserialize(@Nonnull ByteBuf buf, int offset) { - PortalDef obj = new PortalDef(); - byte nullBits = buf.getByte(offset); - obj.explorationSeconds = buf.getIntLE(offset + 1); - obj.breachSeconds = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - int nameKeyLen = VarInt.peek(buf, pos); - if (nameKeyLen < 0) { - throw ProtocolException.negativeLength("NameKey", nameKeyLen); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("PortalDef", 9, buf.readableBytes() - offset); + } else { + PortalDef obj = new PortalDef(); + byte nullBits = buf.getByte(offset); + obj.explorationSeconds = buf.getIntLE(offset + 1); + obj.breachSeconds = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int nameKeyLen = VarInt.peek(buf, pos); + if (nameKeyLen < 0) { + throw ProtocolException.invalidVarInt("NameKey"); + } + + int nameKeyVarLen = VarInt.size(nameKeyLen); + if (nameKeyLen > 4096000) { + throw ProtocolException.stringTooLong("NameKey", nameKeyLen, 4096000); + } + + if (pos + nameKeyVarLen + nameKeyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("NameKey", pos + nameKeyVarLen + nameKeyLen, buf.readableBytes()); + } + + obj.nameKey = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += nameKeyVarLen + nameKeyLen; } - if (nameKeyLen > 4096000) { - throw ProtocolException.stringTooLong("NameKey", nameKeyLen, 4096000); - } - - int nameKeyVarLen = VarInt.length(buf, pos); - obj.nameKey = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += nameKeyVarLen + nameKeyLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -65,7 +73,7 @@ public class PortalDef { int pos = offset + 9; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -110,7 +118,7 @@ public class PortalDef { return ValidationResult.error("NameKey exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameKeyLen); pos += nameKeyLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading NameKey"); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/PortalState.java b/src/com/hypixel/hytale/protocol/packets/interface_/PortalState.java index 2ea81280..58e86399 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/PortalState.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/PortalState.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.interface_; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,10 +30,14 @@ public class PortalState { @Nonnull public static PortalState deserialize(@Nonnull ByteBuf buf, int offset) { - PortalState obj = new PortalState(); - obj.remainingSeconds = buf.getIntLE(offset + 0); - obj.breaching = buf.getByte(offset + 4) != 0; - return obj; + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("PortalState", 5, buf.readableBytes() - offset); + } else { + PortalState obj = new PortalState(); + obj.remainingSeconds = buf.getIntLE(offset + 0); + obj.breaching = buf.getByte(offset + 4) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/RemoveDataContextCollectionItemServersideUIProperty.java b/src/com/hypixel/hytale/protocol/packets/interface_/RemoveDataContextCollectionItemServersideUIProperty.java new file mode 100644 index 00000000..15b87a27 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/RemoveDataContextCollectionItemServersideUIProperty.java @@ -0,0 +1,121 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class RemoveDataContextCollectionItemServersideUIProperty extends ServersideUICommand { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 4; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 4; + public static final int MAX_SIZE = 16384009; + @Nonnull + public String property = ""; + public int index; + + public RemoveDataContextCollectionItemServersideUIProperty() { + } + + public RemoveDataContextCollectionItemServersideUIProperty(@Nonnull String property, int index) { + this.property = property; + this.index = index; + } + + public RemoveDataContextCollectionItemServersideUIProperty(@Nonnull RemoveDataContextCollectionItemServersideUIProperty other) { + this.property = other.property; + this.index = other.index; + } + + @Nonnull + public static RemoveDataContextCollectionItemServersideUIProperty deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("RemoveDataContextCollectionItemServersideUIProperty", 4, buf.readableBytes() - offset); + } else { + RemoveDataContextCollectionItemServersideUIProperty obj = new RemoveDataContextCollectionItemServersideUIProperty(); + obj.index = buf.getIntLE(offset + 0); + int pos = offset + 4; + int propertyLen = VarInt.peek(buf, pos); + if (propertyLen < 0) { + throw ProtocolException.invalidVarInt("Property"); + } else { + int propertyVarLen = VarInt.size(propertyLen); + if (propertyLen > 4096000) { + throw ProtocolException.stringTooLong("Property", propertyLen, 4096000); + } else if (pos + propertyVarLen + propertyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Property", pos + propertyVarLen + propertyLen, buf.readableBytes()); + } else { + obj.property = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += propertyVarLen + propertyLen; + return obj; + } + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int pos = offset + 4; + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + return pos - offset; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeIntLE(this.index); + PacketIO.writeVarString(buf, this.property, 4096000); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + int size = 4; + return size + PacketIO.stringSize(this.property); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 4) { + return ValidationResult.error("Buffer too small: expected at least 4 bytes"); + } else { + int pos = offset + 4; + int propertyLen = VarInt.peek(buffer, pos); + if (propertyLen < 0) { + return ValidationResult.error("Invalid string length for Property"); + } else if (propertyLen > 4096000) { + return ValidationResult.error("Property exceeds max length 4096000"); + } else { + pos += VarInt.size(propertyLen); + pos += propertyLen; + return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Property") : ValidationResult.OK; + } + } + } + + public RemoveDataContextCollectionItemServersideUIProperty clone() { + RemoveDataContextCollectionItemServersideUIProperty copy = new RemoveDataContextCollectionItemServersideUIProperty(); + copy.property = this.property; + copy.index = this.index; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof RemoveDataContextCollectionItemServersideUIProperty other) + ? false + : Objects.equals(this.property, other.property) && this.index == other.index; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.property, this.index); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/RemoveFromServerPlayerList.java b/src/com/hypixel/hytale/protocol/packets/interface_/RemoveFromServerPlayerList.java index 227b9b09..6cd1d58c 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/RemoveFromServerPlayerList.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/RemoveFromServerPlayerList.java @@ -47,35 +47,39 @@ public class RemoveFromServerPlayerList implements Packet, ToClientPacket { @Nonnull public static RemoveFromServerPlayerList deserialize(@Nonnull ByteBuf buf, int offset) { - RemoveFromServerPlayerList obj = new RemoveFromServerPlayerList(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int playersCount = VarInt.peek(buf, pos); - if (playersCount < 0) { - throw ProtocolException.negativeLength("Players", playersCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("RemoveFromServerPlayerList", 1, buf.readableBytes() - offset); + } else { + RemoveFromServerPlayerList obj = new RemoveFromServerPlayerList(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int playersCount = VarInt.peek(buf, pos); + if (playersCount < 0) { + throw ProtocolException.invalidVarInt("Players"); + } + + int playersVarLen = VarInt.size(playersCount); + if (playersCount > 4096000) { + throw ProtocolException.arrayTooLong("Players", playersCount, 4096000); + } + + if (pos + playersVarLen + playersCount * 16L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Players", pos + playersVarLen + playersCount * 16, buf.readableBytes()); + } + + pos += playersVarLen; + obj.players = new UUID[playersCount]; + + for (int i = 0; i < playersCount; i++) { + obj.players[i] = PacketIO.readUUID(buf, pos + i * 16); + } + + pos += playersCount * 16; } - if (playersCount > 4096000) { - throw ProtocolException.arrayTooLong("Players", playersCount, 4096000); - } - - int playersVarLen = VarInt.size(playersCount); - if (pos + playersVarLen + playersCount * 16L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Players", pos + playersVarLen + playersCount * 16, buf.readableBytes()); - } - - pos += playersVarLen; - obj.players = new UUID[playersCount]; - - for (int i = 0; i < playersCount; i++) { - obj.players[i] = PacketIO.readUUID(buf, pos + i * 16); - } - - pos += playersCount * 16; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -83,7 +87,7 @@ public class RemoveFromServerPlayerList implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 16; + pos += VarInt.size(arrLen) + arrLen * 16; } return pos - offset; @@ -136,7 +140,7 @@ public class RemoveFromServerPlayerList implements Packet, ToClientPacket { return ValidationResult.error("Players exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(playersCount); pos += playersCount * 16; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Players"); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ServerInfo.java b/src/com/hypixel/hytale/protocol/packets/interface_/ServerInfo.java index c68f2f24..c20b4ddb 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/ServerInfo.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ServerInfo.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.interface_; +import com.hypixel.hytale.protocol.HostAddress; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; @@ -17,14 +18,16 @@ public class ServerInfo implements Packet, ToClientPacket { public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 5; - public static final int VARIABLE_FIELD_COUNT = 2; - public static final int VARIABLE_BLOCK_START = 13; - public static final int MAX_SIZE = 32768023; + public static final int VARIABLE_FIELD_COUNT = 3; + public static final int VARIABLE_BLOCK_START = 17; + public static final int MAX_SIZE = 32769058; @Nullable public String serverName; @Nullable public String motd; public int maxPlayers; + @Nullable + public HostAddress fallbackServer; @Override public int getId() { @@ -39,62 +42,102 @@ public class ServerInfo implements Packet, ToClientPacket { public ServerInfo() { } - public ServerInfo(@Nullable String serverName, @Nullable String motd, int maxPlayers) { + public ServerInfo(@Nullable String serverName, @Nullable String motd, int maxPlayers, @Nullable HostAddress fallbackServer) { this.serverName = serverName; this.motd = motd; this.maxPlayers = maxPlayers; + this.fallbackServer = fallbackServer; } public ServerInfo(@Nonnull ServerInfo other) { this.serverName = other.serverName; this.motd = other.motd; this.maxPlayers = other.maxPlayers; + this.fallbackServer = other.fallbackServer; } @Nonnull public static ServerInfo deserialize(@Nonnull ByteBuf buf, int offset) { - ServerInfo obj = new ServerInfo(); - byte nullBits = buf.getByte(offset); - obj.maxPlayers = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 5); - int serverNameLen = VarInt.peek(buf, varPos0); - if (serverNameLen < 0) { - throw ProtocolException.negativeLength("ServerName", serverNameLen); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("ServerInfo", 17, buf.readableBytes() - offset); + } else { + ServerInfo obj = new ServerInfo(); + byte nullBits = buf.getByte(offset); + obj.maxPlayers = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ServerName", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int serverNameLen = VarInt.peek(buf, varPos0); + if (serverNameLen < 0) { + throw ProtocolException.invalidVarInt("ServerName"); + } + + int serverNameVarIntLen = VarInt.size(serverNameLen); + if (serverNameLen > 4096000) { + throw ProtocolException.stringTooLong("ServerName", serverNameLen, 4096000); + } + + if (varPos0 + serverNameVarIntLen + serverNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ServerName", varPos0 + serverNameVarIntLen + serverNameLen, buf.readableBytes()); + } + + obj.serverName = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (serverNameLen > 4096000) { - throw ProtocolException.stringTooLong("ServerName", serverNameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Motd", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + int motdLen = VarInt.peek(buf, varPos1); + if (motdLen < 0) { + throw ProtocolException.invalidVarInt("Motd"); + } + + int motdVarIntLen = VarInt.size(motdLen); + if (motdLen > 4096000) { + throw ProtocolException.stringTooLong("Motd", motdLen, 4096000); + } + + if (varPos1 + motdVarIntLen + motdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Motd", varPos1 + motdVarIntLen + motdLen, buf.readableBytes()); + } + + obj.motd = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.serverName = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 13); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("FallbackServer", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 17 + varPosBase2; + obj.fallbackServer = HostAddress.deserialize(buf, varPos2); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 9); - int motdLen = VarInt.peek(buf, varPos1); - if (motdLen < 0) { - throw ProtocolException.negativeLength("Motd", motdLen); - } - - if (motdLen > 4096000) { - throw ProtocolException.stringTooLong("Motd", motdLen, 4096000); - } - - obj.motd = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 13; + int maxEnd = 17; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); - int pos0 = offset + 13 + fieldOffset0; + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ServerName", fieldOffset0, maxEnd); + } + + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -102,14 +145,31 @@ public class ServerInfo implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); - int pos1 = offset + 13 + fieldOffset1; + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Motd", fieldOffset1, maxEnd); + } + + int pos1 = offset + 17 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } } + if ((nullBits & 4) != 0) { + int fieldOffset2 = buf.getIntLE(offset + 13); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("FallbackServer", fieldOffset2, maxEnd); + } + + int pos2 = offset + 17 + fieldOffset2; + pos2 += HostAddress.computeBytesConsumed(buf, pos2); + if (pos2 - offset > maxEnd) { + maxEnd = pos2 - offset; + } + } + return maxEnd; } @@ -125,12 +185,18 @@ public class ServerInfo implements Packet, ToClientPacket { nullBits = (byte)(nullBits | 2); } + if (this.fallbackServer != null) { + nullBits = (byte)(nullBits | 4); + } + buf.writeByte(nullBits); buf.writeIntLE(this.maxPlayers); int serverNameOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int motdOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); + int fallbackServerOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); int varBlockStart = buf.writerIndex(); if (this.serverName != null) { buf.setIntLE(serverNameOffsetSlot, buf.writerIndex() - varBlockStart); @@ -145,11 +211,18 @@ public class ServerInfo implements Packet, ToClientPacket { } else { buf.setIntLE(motdOffsetSlot, -1); } + + if (this.fallbackServer != null) { + buf.setIntLE(fallbackServerOffsetSlot, buf.writerIndex() - varBlockStart); + this.fallbackServer.serialize(buf); + } else { + buf.setIntLE(fallbackServerOffsetSlot, -1); + } } @Override public int computeSize() { - int size = 13; + int size = 17; if (this.serverName != null) { size += PacketIO.stringSize(this.serverName); } @@ -158,25 +231,25 @@ public class ServerInfo implements Packet, ToClientPacket { size += PacketIO.stringSize(this.motd); } + if (this.fallbackServer != null) { + size += this.fallbackServer.computeSize(); + } + return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 13) { - return ValidationResult.error("Buffer too small: expected at least 13 bytes"); + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int serverNameOffset = buffer.getIntLE(offset + 5); - if (serverNameOffset < 0) { + if (serverNameOffset < 0 || serverNameOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for ServerName"); } - int pos = offset + 13 + serverNameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ServerName"); - } - + int pos = offset + 17 + serverNameOffset; int serverNameLen = VarInt.peek(buffer, pos); if (serverNameLen < 0) { return ValidationResult.error("Invalid string length for ServerName"); @@ -186,7 +259,7 @@ public class ServerInfo implements Packet, ToClientPacket { return ValidationResult.error("ServerName exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(serverNameLen); pos += serverNameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ServerName"); @@ -195,15 +268,11 @@ public class ServerInfo implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int motdOffset = buffer.getIntLE(offset + 9); - if (motdOffset < 0) { + if (motdOffset < 0 || motdOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Motd"); } - int posx = offset + 13 + motdOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Motd"); - } - + int posx = offset + 17 + motdOffset; int motdLen = VarInt.peek(buffer, posx); if (motdLen < 0) { return ValidationResult.error("Invalid string length for Motd"); @@ -213,13 +282,28 @@ public class ServerInfo implements Packet, ToClientPacket { return ValidationResult.error("Motd exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(motdLen); posx += motdLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Motd"); } } + if ((nullBits & 4) != 0) { + int fallbackServerOffset = buffer.getIntLE(offset + 13); + if (fallbackServerOffset < 0 || fallbackServerOffset > buffer.writerIndex() - offset - 17) { + return ValidationResult.error("Invalid offset for FallbackServer"); + } + + int posxx = offset + 17 + fallbackServerOffset; + ValidationResult fallbackServerResult = HostAddress.validateStructure(buffer, posxx); + if (!fallbackServerResult.isValid()) { + return ValidationResult.error("Invalid FallbackServer: " + fallbackServerResult.error()); + } + + posxx += HostAddress.computeBytesConsumed(buffer, posxx); + } + return ValidationResult.OK; } } @@ -229,6 +313,7 @@ public class ServerInfo implements Packet, ToClientPacket { copy.serverName = this.serverName; copy.motd = this.motd; copy.maxPlayers = this.maxPlayers; + copy.fallbackServer = this.fallbackServer != null ? this.fallbackServer.clone() : null; return copy; } @@ -239,12 +324,15 @@ public class ServerInfo implements Packet, ToClientPacket { } else { return !(obj instanceof ServerInfo other) ? false - : Objects.equals(this.serverName, other.serverName) && Objects.equals(this.motd, other.motd) && this.maxPlayers == other.maxPlayers; + : Objects.equals(this.serverName, other.serverName) + && Objects.equals(this.motd, other.motd) + && this.maxPlayers == other.maxPlayers + && Objects.equals(this.fallbackServer, other.fallbackServer); } } @Override public int hashCode() { - return Objects.hash(this.serverName, this.motd, this.maxPlayers); + return Objects.hash(this.serverName, this.motd, this.maxPlayers, this.fallbackServer); } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ServerMessage.java b/src/com/hypixel/hytale/protocol/packets/interface_/ServerMessage.java index 42d52abc..641f9aaf 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/ServerMessage.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ServerMessage.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -48,16 +49,20 @@ public class ServerMessage implements Packet, ToClientPacket { @Nonnull public static ServerMessage deserialize(@Nonnull ByteBuf buf, int offset) { - ServerMessage obj = new ServerMessage(); - byte nullBits = buf.getByte(offset); - obj.type = ChatType.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - obj.message = FormattedMessage.deserialize(buf, pos); - pos += FormattedMessage.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("ServerMessage", 2, buf.readableBytes() - offset); + } else { + ServerMessage obj = new ServerMessage(); + byte nullBits = buf.getByte(offset); + obj.type = ChatType.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + obj.message = FormattedMessage.deserialize(buf, pos); + pos += FormattedMessage.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -99,17 +104,22 @@ public class ServerMessage implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - ValidationResult messageResult = FormattedMessage.validateStructure(buffer, pos); - if (!messageResult.isValid()) { - return ValidationResult.error("Invalid Message: " + messageResult.error()); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 1) { + return ValidationResult.error("Invalid ChatType value for Type"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + ValidationResult messageResult = FormattedMessage.validateStructure(buffer, v); + if (!messageResult.isValid()) { + return ValidationResult.error("Invalid Message: " + messageResult.error()); + } + + v += FormattedMessage.computeBytesConsumed(buffer, v); } - pos += FormattedMessage.computeBytesConsumed(buffer, pos); + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ServerPlayerListPlayer.java b/src/com/hypixel/hytale/protocol/packets/interface_/ServerPlayerListPlayer.java index 9cc8dd56..3aad8a6c 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/ServerPlayerListPlayer.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ServerPlayerListPlayer.java @@ -43,31 +43,39 @@ public class ServerPlayerListPlayer { @Nonnull public static ServerPlayerListPlayer deserialize(@Nonnull ByteBuf buf, int offset) { - ServerPlayerListPlayer obj = new ServerPlayerListPlayer(); - byte nullBits = buf.getByte(offset); - obj.uuid = PacketIO.readUUID(buf, offset + 1); - if ((nullBits & 1) != 0) { - obj.worldUuid = PacketIO.readUUID(buf, offset + 17); - } - - obj.ping = buf.getIntLE(offset + 33); - int pos = offset + 37; - if ((nullBits & 2) != 0) { - int usernameLen = VarInt.peek(buf, pos); - if (usernameLen < 0) { - throw ProtocolException.negativeLength("Username", usernameLen); + if (buf.readableBytes() - offset < 37) { + throw ProtocolException.bufferTooSmall("ServerPlayerListPlayer", 37, buf.readableBytes() - offset); + } else { + ServerPlayerListPlayer obj = new ServerPlayerListPlayer(); + byte nullBits = buf.getByte(offset); + obj.uuid = PacketIO.readUUID(buf, offset + 1); + if ((nullBits & 1) != 0) { + obj.worldUuid = PacketIO.readUUID(buf, offset + 17); } - if (usernameLen > 4096000) { - throw ProtocolException.stringTooLong("Username", usernameLen, 4096000); + obj.ping = buf.getIntLE(offset + 33); + int pos = offset + 37; + if ((nullBits & 2) != 0) { + int usernameLen = VarInt.peek(buf, pos); + if (usernameLen < 0) { + throw ProtocolException.invalidVarInt("Username"); + } + + int usernameVarLen = VarInt.size(usernameLen); + if (usernameLen > 4096000) { + throw ProtocolException.stringTooLong("Username", usernameLen, 4096000); + } + + if (pos + usernameVarLen + usernameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Username", pos + usernameVarLen + usernameLen, buf.readableBytes()); + } + + obj.username = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += usernameVarLen + usernameLen; } - int usernameVarLen = VarInt.length(buf, pos); - obj.username = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += usernameVarLen + usernameLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -75,7 +83,7 @@ public class ServerPlayerListPlayer { int pos = offset + 37; if ((nullBits & 2) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -130,7 +138,7 @@ public class ServerPlayerListPlayer { return ValidationResult.error("Username exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(usernameLen); pos += usernameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Username"); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ServerPlayerListUpdate.java b/src/com/hypixel/hytale/protocol/packets/interface_/ServerPlayerListUpdate.java index 9902340b..4f0c7b04 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/ServerPlayerListUpdate.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ServerPlayerListUpdate.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.protocol.packets.interface_; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -33,10 +34,14 @@ public class ServerPlayerListUpdate { @Nonnull public static ServerPlayerListUpdate deserialize(@Nonnull ByteBuf buf, int offset) { - ServerPlayerListUpdate obj = new ServerPlayerListUpdate(); - obj.uuid = PacketIO.readUUID(buf, offset + 0); - obj.worldUuid = PacketIO.readUUID(buf, offset + 16); - return obj; + if (buf.readableBytes() - offset < 32) { + throw ProtocolException.bufferTooSmall("ServerPlayerListUpdate", 32, buf.readableBytes() - offset); + } else { + ServerPlayerListUpdate obj = new ServerPlayerListUpdate(); + obj.uuid = PacketIO.readUUID(buf, offset + 0); + obj.worldUuid = PacketIO.readUUID(buf, offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ServersideUICommand.java b/src/com/hypixel/hytale/protocol/packets/interface_/ServersideUICommand.java new file mode 100644 index 00000000..10cc8fd7 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ServersideUICommand.java @@ -0,0 +1,85 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import javax.annotation.Nonnull; + +public abstract class ServersideUICommand { + public static final int MAX_SIZE = 1677721605; + + @Nonnull + public static ServersideUICommand deserialize(@Nonnull ByteBuf buf, int offset) { + int typeId = VarInt.peek(buf, offset); + int typeIdLen = VarInt.size(typeId); + + return (ServersideUICommand)(switch (typeId) { + case 0 -> InitServersideUICommand.deserialize(buf, offset + typeIdLen); + case 1 -> SetElementPropertyServersideUICommand.deserialize(buf, offset + typeIdLen); + case 2 -> SetDataContextPropertyServersideUIProperty.deserialize(buf, offset + typeIdLen); + case 3 -> InsertDataContextCollectionItemServersideUIProperty.deserialize(buf, offset + typeIdLen); + case 4 -> RemoveDataContextCollectionItemServersideUIProperty.deserialize(buf, offset + typeIdLen); + default -> throw ProtocolException.unknownPolymorphicType("ServersideUICommand", typeId); + }); + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int typeId = VarInt.peek(buf, offset); + int typeIdLen = VarInt.size(typeId); + + return typeIdLen + switch (typeId) { + case 0 -> InitServersideUICommand.computeBytesConsumed(buf, offset + typeIdLen); + case 1 -> SetElementPropertyServersideUICommand.computeBytesConsumed(buf, offset + typeIdLen); + case 2 -> SetDataContextPropertyServersideUIProperty.computeBytesConsumed(buf, offset + typeIdLen); + case 3 -> InsertDataContextCollectionItemServersideUIProperty.computeBytesConsumed(buf, offset + typeIdLen); + case 4 -> RemoveDataContextCollectionItemServersideUIProperty.computeBytesConsumed(buf, offset + typeIdLen); + default -> throw ProtocolException.unknownPolymorphicType("ServersideUICommand", typeId); + }; + } + + public int getTypeId() { + if (this instanceof InitServersideUICommand sub) { + return 0; + } else if (this instanceof SetElementPropertyServersideUICommand sub) { + return 1; + } else if (this instanceof SetDataContextPropertyServersideUIProperty sub) { + return 2; + } else if (this instanceof InsertDataContextCollectionItemServersideUIProperty sub) { + return 3; + } else if (this instanceof RemoveDataContextCollectionItemServersideUIProperty sub) { + return 4; + } else { + throw new IllegalStateException("Unknown subtype: " + this.getClass().getName()); + } + } + + public abstract int serialize(@Nonnull ByteBuf var1); + + public abstract int computeSize(); + + public int serializeWithTypeId(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + VarInt.write(buf, this.getTypeId()); + this.serialize(buf); + return buf.writerIndex() - startPos; + } + + public int computeSizeWithTypeId() { + return VarInt.size(this.getTypeId()) + this.computeSize(); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + int typeId = VarInt.peek(buffer, offset); + int typeIdLen = VarInt.size(typeId); + + return switch (typeId) { + case 0 -> InitServersideUICommand.validateStructure(buffer, offset + typeIdLen); + case 1 -> SetElementPropertyServersideUICommand.validateStructure(buffer, offset + typeIdLen); + case 2 -> SetDataContextPropertyServersideUIProperty.validateStructure(buffer, offset + typeIdLen); + case 3 -> InsertDataContextCollectionItemServersideUIProperty.validateStructure(buffer, offset + typeIdLen); + case 4 -> RemoveDataContextCollectionItemServersideUIProperty.validateStructure(buffer, offset + typeIdLen); + default -> ValidationResult.error("Unknown polymorphic type ID " + typeId + " for ServersideUICommand"); + }; + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/SetDataContextPropertyServersideUIProperty.java b/src/com/hypixel/hytale/protocol/packets/interface_/SetDataContextPropertyServersideUIProperty.java new file mode 100644 index 00000000..a0fddccf --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/SetDataContextPropertyServersideUIProperty.java @@ -0,0 +1,182 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class SetDataContextPropertyServersideUIProperty extends ServersideUICommand { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 0; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 16385037; + @Nonnull + public String property = ""; + @Nonnull + public UIDataValue value; + + public SetDataContextPropertyServersideUIProperty() { + } + + public SetDataContextPropertyServersideUIProperty(@Nonnull String property, @Nonnull UIDataValue value) { + this.property = property; + this.value = value; + } + + public SetDataContextPropertyServersideUIProperty(@Nonnull SetDataContextPropertyServersideUIProperty other) { + this.property = other.property; + this.value = other.value; + } + + @Nonnull + public static SetDataContextPropertyServersideUIProperty deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("SetDataContextPropertyServersideUIProperty", 8, buf.readableBytes() - offset); + } else { + SetDataContextPropertyServersideUIProperty obj = new SetDataContextPropertyServersideUIProperty(); + int varPosBase0 = buf.getIntLE(offset + 0); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + int varPos0 = offset + 8 + varPosBase0; + int propertyLen = VarInt.peek(buf, varPos0); + if (propertyLen < 0) { + throw ProtocolException.invalidVarInt("Property"); + } else { + int propertyVarIntLen = VarInt.size(propertyLen); + if (propertyLen > 4096000) { + throw ProtocolException.stringTooLong("Property", propertyLen, 4096000); + } else if (varPos0 + propertyVarIntLen + propertyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Property", varPos0 + propertyVarIntLen + propertyLen, buf.readableBytes()); + } else { + obj.property = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + varPos0 = offset + 8 + varPosBase0; + obj.value = UIDataValue.deserialize(buf, varPos0); + return obj; + } else { + throw ProtocolException.invalidOffset("Value", varPosBase0, buf.readableBytes()); + } + } + } + } else { + throw ProtocolException.invalidOffset("Property", varPosBase0, buf.readableBytes()); + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int maxEnd = 8; + int fieldOffset0 = buf.getIntLE(offset + 0); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + int pos0 = offset + 8 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + pos0 = offset + 8 + fieldOffset0; + pos0 += UIDataValue.computeBytesConsumed(buf, pos0); + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Value", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("Property", fieldOffset0, maxEnd); + } + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + int propertyOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int valueOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + buf.setIntLE(propertyOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.property, 4096000); + buf.setIntLE(valueOffsetSlot, buf.writerIndex() - varBlockStart); + this.value.serializeWithTypeId(buf); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + int size = 8; + size += PacketIO.stringSize(this.property); + return size + this.value.computeSizeWithTypeId(); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 8) { + return ValidationResult.error("Buffer too small: expected at least 8 bytes"); + } else { + int propertyOffset = buffer.getIntLE(offset + 0); + if (propertyOffset >= 0 && propertyOffset <= buffer.writerIndex() - offset - 8) { + int pos = offset + 8 + propertyOffset; + int propertyLen = VarInt.peek(buffer, pos); + if (propertyLen < 0) { + return ValidationResult.error("Invalid string length for Property"); + } else if (propertyLen > 4096000) { + return ValidationResult.error("Property exceeds max length 4096000"); + } else { + pos += VarInt.size(propertyLen); + pos += propertyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Property"); + } else { + propertyOffset = buffer.getIntLE(offset + 4); + if (propertyOffset >= 0 && propertyOffset <= buffer.writerIndex() - offset - 8) { + pos = offset + 8 + propertyOffset; + ValidationResult valueResult = UIDataValue.validateStructure(buffer, pos); + if (!valueResult.isValid()) { + return ValidationResult.error("Invalid Value: " + valueResult.error()); + } else { + pos += UIDataValue.computeBytesConsumed(buffer, pos); + return ValidationResult.OK; + } + } else { + return ValidationResult.error("Invalid offset for Value"); + } + } + } + } else { + return ValidationResult.error("Invalid offset for Property"); + } + } + } + + public SetDataContextPropertyServersideUIProperty clone() { + SetDataContextPropertyServersideUIProperty copy = new SetDataContextPropertyServersideUIProperty(); + copy.property = this.property; + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof SetDataContextPropertyServersideUIProperty other) + ? false + : Objects.equals(this.property, other.property) && Objects.equals(this.value, other.value); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.property, this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/SetElementPropertyServersideUICommand.java b/src/com/hypixel/hytale/protocol/packets/interface_/SetElementPropertyServersideUICommand.java new file mode 100644 index 00000000..ad3b3973 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/SetElementPropertyServersideUICommand.java @@ -0,0 +1,242 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class SetElementPropertyServersideUICommand extends ServersideUICommand { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 0; + public static final int VARIABLE_FIELD_COUNT = 3; + public static final int VARIABLE_BLOCK_START = 12; + public static final int MAX_SIZE = 32769046; + @Nonnull + public String selector = ""; + @Nonnull + public String propertyName = ""; + @Nonnull + public UIDataValue value; + + public SetElementPropertyServersideUICommand() { + } + + public SetElementPropertyServersideUICommand(@Nonnull String selector, @Nonnull String propertyName, @Nonnull UIDataValue value) { + this.selector = selector; + this.propertyName = propertyName; + this.value = value; + } + + public SetElementPropertyServersideUICommand(@Nonnull SetElementPropertyServersideUICommand other) { + this.selector = other.selector; + this.propertyName = other.propertyName; + this.value = other.value; + } + + @Nonnull + public static SetElementPropertyServersideUICommand deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("SetElementPropertyServersideUICommand", 12, buf.readableBytes() - offset); + } else { + SetElementPropertyServersideUICommand obj = new SetElementPropertyServersideUICommand(); + int varPosBase0 = buf.getIntLE(offset + 0); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 12) { + int varPos0 = offset + 12 + varPosBase0; + int selectorLen = VarInt.peek(buf, varPos0); + if (selectorLen < 0) { + throw ProtocolException.invalidVarInt("Selector"); + } else { + int selectorVarIntLen = VarInt.size(selectorLen); + if (selectorLen > 4096000) { + throw ProtocolException.stringTooLong("Selector", selectorLen, 4096000); + } else if (varPos0 + selectorVarIntLen + selectorLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Selector", varPos0 + selectorVarIntLen + selectorLen, buf.readableBytes()); + } else { + obj.selector = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 12) { + varPos0 = offset + 12 + varPosBase0; + selectorLen = VarInt.peek(buf, varPos0); + if (selectorLen < 0) { + throw ProtocolException.invalidVarInt("PropertyName"); + } else { + selectorVarIntLen = VarInt.size(selectorLen); + if (selectorLen > 4096000) { + throw ProtocolException.stringTooLong("PropertyName", selectorLen, 4096000); + } else if (varPos0 + selectorVarIntLen + selectorLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("PropertyName", varPos0 + selectorVarIntLen + selectorLen, buf.readableBytes()); + } else { + obj.propertyName = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 8); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 12) { + varPos0 = offset + 12 + varPosBase0; + obj.value = UIDataValue.deserialize(buf, varPos0); + return obj; + } else { + throw ProtocolException.invalidOffset("Value", varPosBase0, buf.readableBytes()); + } + } + } + } else { + throw ProtocolException.invalidOffset("PropertyName", varPosBase0, buf.readableBytes()); + } + } + } + } else { + throw ProtocolException.invalidOffset("Selector", varPosBase0, buf.readableBytes()); + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int maxEnd = 12; + int fieldOffset0 = buf.getIntLE(offset + 0); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 12) { + int pos0 = offset + 12 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 12) { + pos0 = offset + 12 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + fieldOffset0 = buf.getIntLE(offset + 8); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 12) { + pos0 = offset + 12 + fieldOffset0; + pos0 += UIDataValue.computeBytesConsumed(buf, pos0); + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Value", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("PropertyName", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("Selector", fieldOffset0, maxEnd); + } + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + int selectorOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int propertyNameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int valueOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + buf.setIntLE(selectorOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.selector, 4096000); + buf.setIntLE(propertyNameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.propertyName, 4096000); + buf.setIntLE(valueOffsetSlot, buf.writerIndex() - varBlockStart); + this.value.serializeWithTypeId(buf); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + int size = 12; + size += PacketIO.stringSize(this.selector); + size += PacketIO.stringSize(this.propertyName); + return size + this.value.computeSizeWithTypeId(); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 12) { + return ValidationResult.error("Buffer too small: expected at least 12 bytes"); + } else { + int selectorOffset = buffer.getIntLE(offset + 0); + if (selectorOffset >= 0 && selectorOffset <= buffer.writerIndex() - offset - 12) { + int pos = offset + 12 + selectorOffset; + int selectorLen = VarInt.peek(buffer, pos); + if (selectorLen < 0) { + return ValidationResult.error("Invalid string length for Selector"); + } else if (selectorLen > 4096000) { + return ValidationResult.error("Selector exceeds max length 4096000"); + } else { + pos += VarInt.size(selectorLen); + pos += selectorLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Selector"); + } else { + selectorOffset = buffer.getIntLE(offset + 4); + if (selectorOffset >= 0 && selectorOffset <= buffer.writerIndex() - offset - 12) { + pos = offset + 12 + selectorOffset; + selectorLen = VarInt.peek(buffer, pos); + if (selectorLen < 0) { + return ValidationResult.error("Invalid string length for PropertyName"); + } else if (selectorLen > 4096000) { + return ValidationResult.error("PropertyName exceeds max length 4096000"); + } else { + pos += VarInt.size(selectorLen); + pos += selectorLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading PropertyName"); + } else { + selectorOffset = buffer.getIntLE(offset + 8); + if (selectorOffset >= 0 && selectorOffset <= buffer.writerIndex() - offset - 12) { + pos = offset + 12 + selectorOffset; + ValidationResult valueResult = UIDataValue.validateStructure(buffer, pos); + if (!valueResult.isValid()) { + return ValidationResult.error("Invalid Value: " + valueResult.error()); + } else { + pos += UIDataValue.computeBytesConsumed(buffer, pos); + return ValidationResult.OK; + } + } else { + return ValidationResult.error("Invalid offset for Value"); + } + } + } + } else { + return ValidationResult.error("Invalid offset for PropertyName"); + } + } + } + } else { + return ValidationResult.error("Invalid offset for Selector"); + } + } + } + + public SetElementPropertyServersideUICommand clone() { + SetElementPropertyServersideUICommand copy = new SetElementPropertyServersideUICommand(); + copy.selector = this.selector; + copy.propertyName = this.propertyName; + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof SetElementPropertyServersideUICommand other) + ? false + : Objects.equals(this.selector, other.selector) && Objects.equals(this.propertyName, other.propertyName) && Objects.equals(this.value, other.value); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.selector, this.propertyName, this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/SetPage.java b/src/com/hypixel/hytale/protocol/packets/interface_/SetPage.java index 61cdc273..9da71c3a 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/SetPage.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/SetPage.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.interface_; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -45,10 +46,14 @@ public class SetPage implements Packet, ToClientPacket { @Nonnull public static SetPage deserialize(@Nonnull ByteBuf buf, int offset) { - SetPage obj = new SetPage(); - obj.page = Page.fromValue(buf.getByte(offset + 0)); - obj.canCloseThroughInteraction = buf.getByte(offset + 1) != 0; - return obj; + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("SetPage", 2, buf.readableBytes() - offset); + } else { + SetPage obj = new SetPage(); + obj.page = Page.fromValue(buf.getByte(offset + 0)); + obj.canCloseThroughInteraction = buf.getByte(offset + 1) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -67,7 +72,12 @@ public class SetPage implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 2 ? ValidationResult.error("Buffer too small: expected at least 2 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 2) { + return ValidationResult.error("Buffer too small: expected at least 2 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 9 ? ValidationResult.error("Invalid Page value for Page") : ValidationResult.OK; + } } public SetPage clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/ShowEventTitle.java b/src/com/hypixel/hytale/protocol/packets/interface_/ShowEventTitle.java index 354a5986..c178829c 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/ShowEventTitle.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/ShowEventTitle.java @@ -75,37 +75,61 @@ public class ShowEventTitle implements Packet, ToClientPacket { @Nonnull public static ShowEventTitle deserialize(@Nonnull ByteBuf buf, int offset) { - ShowEventTitle obj = new ShowEventTitle(); - byte nullBits = buf.getByte(offset); - obj.fadeInDuration = buf.getFloatLE(offset + 1); - obj.fadeOutDuration = buf.getFloatLE(offset + 5); - obj.duration = buf.getFloatLE(offset + 9); - obj.isMajor = buf.getByte(offset + 13) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 26 + buf.getIntLE(offset + 14); - int iconLen = VarInt.peek(buf, varPos0); - if (iconLen < 0) { - throw ProtocolException.negativeLength("Icon", iconLen); + if (buf.readableBytes() - offset < 26) { + throw ProtocolException.bufferTooSmall("ShowEventTitle", 26, buf.readableBytes() - offset); + } else { + ShowEventTitle obj = new ShowEventTitle(); + byte nullBits = buf.getByte(offset); + obj.fadeInDuration = buf.getFloatLE(offset + 1); + obj.fadeOutDuration = buf.getFloatLE(offset + 5); + obj.duration = buf.getFloatLE(offset + 9); + obj.isMajor = buf.getByte(offset + 13) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 14); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Icon", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 26 + varPosBase0; + int iconLen = VarInt.peek(buf, varPos0); + if (iconLen < 0) { + throw ProtocolException.invalidVarInt("Icon"); + } + + int iconVarIntLen = VarInt.size(iconLen); + if (iconLen > 4096000) { + throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + } + + if (varPos0 + iconVarIntLen + iconLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Icon", varPos0 + iconVarIntLen + iconLen, buf.readableBytes()); + } + + obj.icon = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (iconLen > 4096000) { - throw ProtocolException.stringTooLong("Icon", iconLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 18); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("PrimaryTitle", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 26 + varPosBase1; + obj.primaryTitle = FormattedMessage.deserialize(buf, varPos1); } - obj.icon = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 22); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("SecondaryTitle", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 26 + buf.getIntLE(offset + 18); - obj.primaryTitle = FormattedMessage.deserialize(buf, varPos1); - } + int varPos2 = offset + 26 + varPosBase2; + obj.secondaryTitle = FormattedMessage.deserialize(buf, varPos2); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 26 + buf.getIntLE(offset + 22); - obj.secondaryTitle = FormattedMessage.deserialize(buf, varPos2); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -113,9 +137,13 @@ public class ShowEventTitle implements Packet, ToClientPacket { int maxEnd = 26; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 14); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("Icon", fieldOffset0, maxEnd); + } + int pos0 = offset + 26 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -123,6 +151,10 @@ public class ShowEventTitle implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 18); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("PrimaryTitle", fieldOffset1, maxEnd); + } + int pos1 = offset + 26 + fieldOffset1; pos1 += FormattedMessage.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -132,6 +164,10 @@ public class ShowEventTitle implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 22); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 26) { + throw ProtocolException.invalidOffset("SecondaryTitle", fieldOffset2, maxEnd); + } + int pos2 = offset + 26 + fieldOffset2; pos2 += FormattedMessage.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -217,15 +253,11 @@ public class ShowEventTitle implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int iconOffset = buffer.getIntLE(offset + 14); - if (iconOffset < 0) { + if (iconOffset < 0 || iconOffset > buffer.writerIndex() - offset - 26) { return ValidationResult.error("Invalid offset for Icon"); } int pos = offset + 26 + iconOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Icon"); - } - int iconLen = VarInt.peek(buffer, pos); if (iconLen < 0) { return ValidationResult.error("Invalid string length for Icon"); @@ -235,7 +267,7 @@ public class ShowEventTitle implements Packet, ToClientPacket { return ValidationResult.error("Icon exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(iconLen); pos += iconLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Icon"); @@ -244,15 +276,11 @@ public class ShowEventTitle implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int primaryTitleOffset = buffer.getIntLE(offset + 18); - if (primaryTitleOffset < 0) { + if (primaryTitleOffset < 0 || primaryTitleOffset > buffer.writerIndex() - offset - 26) { return ValidationResult.error("Invalid offset for PrimaryTitle"); } int posx = offset + 26 + primaryTitleOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for PrimaryTitle"); - } - ValidationResult primaryTitleResult = FormattedMessage.validateStructure(buffer, posx); if (!primaryTitleResult.isValid()) { return ValidationResult.error("Invalid PrimaryTitle: " + primaryTitleResult.error()); @@ -263,21 +291,17 @@ public class ShowEventTitle implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int secondaryTitleOffset = buffer.getIntLE(offset + 22); - if (secondaryTitleOffset < 0) { + if (secondaryTitleOffset < 0 || secondaryTitleOffset > buffer.writerIndex() - offset - 26) { return ValidationResult.error("Invalid offset for SecondaryTitle"); } - int posxx = offset + 26 + secondaryTitleOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SecondaryTitle"); - } - - ValidationResult secondaryTitleResult = FormattedMessage.validateStructure(buffer, posxx); + int posx = offset + 26 + secondaryTitleOffset; + ValidationResult secondaryTitleResult = FormattedMessage.validateStructure(buffer, posx); if (!secondaryTitleResult.isValid()) { return ValidationResult.error("Invalid SecondaryTitle: " + secondaryTitleResult.error()); } - posxx += FormattedMessage.computeBytesConsumed(buffer, posxx); + posx += FormattedMessage.computeBytesConsumed(buffer, posx); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIBoolDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIBoolDataValue.java new file mode 100644 index 00000000..dc39e8cd --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIBoolDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIBoolDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 1; + public static final int MAX_SIZE = 1; + public boolean value; + + public UIBoolDataValue() { + } + + public UIBoolDataValue(boolean value) { + this.value = value; + } + + public UIBoolDataValue(@Nonnull UIBoolDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIBoolDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UIBoolDataValue", 1, buf.readableBytes() - offset); + } else { + UIBoolDataValue obj = new UIBoolDataValue(); + obj.value = buf.getByte(offset + 0) != 0; + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 1; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeByte(this.value ? 1 : 0); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 1; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + } + + public UIBoolDataValue clone() { + UIBoolDataValue copy = new UIBoolDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIBoolDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIByteDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIByteDataValue.java new file mode 100644 index 00000000..f0ba0248 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIByteDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIByteDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 1; + public static final int MAX_SIZE = 1; + public byte value; + + public UIByteDataValue() { + } + + public UIByteDataValue(byte value) { + this.value = value; + } + + public UIByteDataValue(@Nonnull UIByteDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIByteDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UIByteDataValue", 1, buf.readableBytes() - offset); + } else { + UIByteDataValue obj = new UIByteDataValue(); + obj.value = buf.getByte(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 1; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeByte(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 1; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + } + + public UIByteDataValue clone() { + UIByteDataValue copy = new UIByteDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIByteDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UICommandDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UICommandDataValue.java new file mode 100644 index 00000000..c74858ea --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UICommandDataValue.java @@ -0,0 +1,109 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UICommandDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 0; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 0; + public static final int MAX_SIZE = 16384005; + @Nonnull + public String commandId = ""; + + public UICommandDataValue() { + } + + public UICommandDataValue(@Nonnull String commandId) { + this.commandId = commandId; + } + + public UICommandDataValue(@Nonnull UICommandDataValue other) { + this.commandId = other.commandId; + } + + @Nonnull + public static UICommandDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + UICommandDataValue obj = new UICommandDataValue(); + int pos = offset + 0; + int commandIdLen = VarInt.peek(buf, pos); + if (commandIdLen < 0) { + throw ProtocolException.invalidVarInt("CommandId"); + } else { + int commandIdVarLen = VarInt.size(commandIdLen); + if (commandIdLen > 4096000) { + throw ProtocolException.stringTooLong("CommandId", commandIdLen, 4096000); + } else if (pos + commandIdVarLen + commandIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("CommandId", pos + commandIdVarLen + commandIdLen, buf.readableBytes()); + } else { + obj.commandId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += commandIdVarLen + commandIdLen; + return obj; + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int pos = offset + 0; + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + return pos - offset; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + PacketIO.writeVarString(buf, this.commandId, 4096000); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + int size = 0; + return size + PacketIO.stringSize(this.commandId); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 0) { + return ValidationResult.error("Buffer too small: expected at least 0 bytes"); + } else { + int pos = offset + 0; + int commandIdLen = VarInt.peek(buffer, pos); + if (commandIdLen < 0) { + return ValidationResult.error("Invalid string length for CommandId"); + } else if (commandIdLen > 4096000) { + return ValidationResult.error("CommandId exceeds max length 4096000"); + } else { + pos += VarInt.size(commandIdLen); + pos += commandIdLen; + return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading CommandId") : ValidationResult.OK; + } + } + } + + public UICommandDataValue clone() { + UICommandDataValue copy = new UICommandDataValue(); + copy.commandId = this.commandId; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UICommandDataValue other ? Objects.equals(this.commandId, other.commandId) : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.commandId); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIDataValue.java new file mode 100644 index 00000000..1b0091d5 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIDataValue.java @@ -0,0 +1,140 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import javax.annotation.Nonnull; + +public abstract class UIDataValue { + public static final int MAX_SIZE = 1677721605; + + @Nonnull + public static UIDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + int typeId = VarInt.peek(buf, offset); + int typeIdLen = VarInt.size(typeId); + + return (UIDataValue)(switch (typeId) { + case 0 -> UIStringDataValue.deserialize(buf, offset + typeIdLen); + case 1 -> UIFloatDataValue.deserialize(buf, offset + typeIdLen); + case 2 -> UIIntDataValue.deserialize(buf, offset + typeIdLen); + case 3 -> UIBoolDataValue.deserialize(buf, offset + typeIdLen); + case 4 -> UICommandDataValue.deserialize(buf, offset + typeIdLen); + case 5 -> UIObjectDataValue.deserialize(buf, offset + typeIdLen); + case 6 -> UIEnumDataValue.deserialize(buf, offset + typeIdLen); + case 7 -> UIListDataValue.deserialize(buf, offset + typeIdLen); + case 8 -> UIByteDataValue.deserialize(buf, offset + typeIdLen); + case 9 -> UISByteDataValue.deserialize(buf, offset + typeIdLen); + case 10 -> UIShortDataValue.deserialize(buf, offset + typeIdLen); + case 11 -> UIUShortDataValue.deserialize(buf, offset + typeIdLen); + case 12 -> UIUIntDataValue.deserialize(buf, offset + typeIdLen); + case 13 -> UILongDataValue.deserialize(buf, offset + typeIdLen); + case 14 -> UIULongDataValue.deserialize(buf, offset + typeIdLen); + case 15 -> UIDoubleDataValue.deserialize(buf, offset + typeIdLen); + default -> throw ProtocolException.unknownPolymorphicType("UIDataValue", typeId); + }); + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int typeId = VarInt.peek(buf, offset); + int typeIdLen = VarInt.size(typeId); + + return typeIdLen + switch (typeId) { + case 0 -> UIStringDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 1 -> UIFloatDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 2 -> UIIntDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 3 -> UIBoolDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 4 -> UICommandDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 5 -> UIObjectDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 6 -> UIEnumDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 7 -> UIListDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 8 -> UIByteDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 9 -> UISByteDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 10 -> UIShortDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 11 -> UIUShortDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 12 -> UIUIntDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 13 -> UILongDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 14 -> UIULongDataValue.computeBytesConsumed(buf, offset + typeIdLen); + case 15 -> UIDoubleDataValue.computeBytesConsumed(buf, offset + typeIdLen); + default -> throw ProtocolException.unknownPolymorphicType("UIDataValue", typeId); + }; + } + + public int getTypeId() { + if (this instanceof UIStringDataValue sub) { + return 0; + } else if (this instanceof UIFloatDataValue sub) { + return 1; + } else if (this instanceof UIIntDataValue sub) { + return 2; + } else if (this instanceof UIBoolDataValue sub) { + return 3; + } else if (this instanceof UICommandDataValue sub) { + return 4; + } else if (this instanceof UIObjectDataValue sub) { + return 5; + } else if (this instanceof UIEnumDataValue sub) { + return 6; + } else if (this instanceof UIListDataValue sub) { + return 7; + } else if (this instanceof UIByteDataValue sub) { + return 8; + } else if (this instanceof UISByteDataValue sub) { + return 9; + } else if (this instanceof UIShortDataValue sub) { + return 10; + } else if (this instanceof UIUShortDataValue sub) { + return 11; + } else if (this instanceof UIUIntDataValue sub) { + return 12; + } else if (this instanceof UILongDataValue sub) { + return 13; + } else if (this instanceof UIULongDataValue sub) { + return 14; + } else if (this instanceof UIDoubleDataValue sub) { + return 15; + } else { + throw new IllegalStateException("Unknown subtype: " + this.getClass().getName()); + } + } + + public abstract int serialize(@Nonnull ByteBuf var1); + + public abstract int computeSize(); + + public int serializeWithTypeId(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + VarInt.write(buf, this.getTypeId()); + this.serialize(buf); + return buf.writerIndex() - startPos; + } + + public int computeSizeWithTypeId() { + return VarInt.size(this.getTypeId()) + this.computeSize(); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + int typeId = VarInt.peek(buffer, offset); + int typeIdLen = VarInt.size(typeId); + + return switch (typeId) { + case 0 -> UIStringDataValue.validateStructure(buffer, offset + typeIdLen); + case 1 -> UIFloatDataValue.validateStructure(buffer, offset + typeIdLen); + case 2 -> UIIntDataValue.validateStructure(buffer, offset + typeIdLen); + case 3 -> UIBoolDataValue.validateStructure(buffer, offset + typeIdLen); + case 4 -> UICommandDataValue.validateStructure(buffer, offset + typeIdLen); + case 5 -> UIObjectDataValue.validateStructure(buffer, offset + typeIdLen); + case 6 -> UIEnumDataValue.validateStructure(buffer, offset + typeIdLen); + case 7 -> UIListDataValue.validateStructure(buffer, offset + typeIdLen); + case 8 -> UIByteDataValue.validateStructure(buffer, offset + typeIdLen); + case 9 -> UISByteDataValue.validateStructure(buffer, offset + typeIdLen); + case 10 -> UIShortDataValue.validateStructure(buffer, offset + typeIdLen); + case 11 -> UIUShortDataValue.validateStructure(buffer, offset + typeIdLen); + case 12 -> UIUIntDataValue.validateStructure(buffer, offset + typeIdLen); + case 13 -> UILongDataValue.validateStructure(buffer, offset + typeIdLen); + case 14 -> UIULongDataValue.validateStructure(buffer, offset + typeIdLen); + case 15 -> UIDoubleDataValue.validateStructure(buffer, offset + typeIdLen); + default -> ValidationResult.error("Unknown polymorphic type ID " + typeId + " for UIDataValue"); + }; + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIDoubleDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIDoubleDataValue.java new file mode 100644 index 00000000..adcf6d20 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIDoubleDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIDoubleDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 8; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 8; + public double value; + + public UIDoubleDataValue() { + } + + public UIDoubleDataValue(double value) { + this.value = value; + } + + public UIDoubleDataValue(@Nonnull UIDoubleDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIDoubleDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UIDoubleDataValue", 8, buf.readableBytes() - offset); + } else { + UIDoubleDataValue obj = new UIDoubleDataValue(); + obj.value = buf.getDoubleLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 8; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeDoubleLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 8; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 8 ? ValidationResult.error("Buffer too small: expected at least 8 bytes") : ValidationResult.OK; + } + + public UIDoubleDataValue clone() { + UIDoubleDataValue copy = new UIDoubleDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIDoubleDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIEnumDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIEnumDataValue.java new file mode 100644 index 00000000..382f0fff --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIEnumDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIEnumDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 8; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 8; + public long value; + + public UIEnumDataValue() { + } + + public UIEnumDataValue(long value) { + this.value = value; + } + + public UIEnumDataValue(@Nonnull UIEnumDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIEnumDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UIEnumDataValue", 8, buf.readableBytes() - offset); + } else { + UIEnumDataValue obj = new UIEnumDataValue(); + obj.value = buf.getLongLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 8; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeLongLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 8; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 8 ? ValidationResult.error("Buffer too small: expected at least 8 bytes") : ValidationResult.OK; + } + + public UIEnumDataValue clone() { + UIEnumDataValue copy = new UIEnumDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIEnumDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIFloatDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIFloatDataValue.java new file mode 100644 index 00000000..15429f0b --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIFloatDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIFloatDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 4; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 4; + public static final int MAX_SIZE = 4; + public float value; + + public UIFloatDataValue() { + } + + public UIFloatDataValue(float value) { + this.value = value; + } + + public UIFloatDataValue(@Nonnull UIFloatDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIFloatDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("UIFloatDataValue", 4, buf.readableBytes() - offset); + } else { + UIFloatDataValue obj = new UIFloatDataValue(); + obj.value = buf.getFloatLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 4; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeFloatLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 4; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 4 ? ValidationResult.error("Buffer too small: expected at least 4 bytes") : ValidationResult.OK; + } + + public UIFloatDataValue clone() { + UIFloatDataValue copy = new UIFloatDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIFloatDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIIntDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIIntDataValue.java new file mode 100644 index 00000000..3ffcd6e5 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIIntDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIIntDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 4; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 4; + public static final int MAX_SIZE = 4; + public int value; + + public UIIntDataValue() { + } + + public UIIntDataValue(int value) { + this.value = value; + } + + public UIIntDataValue(@Nonnull UIIntDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIIntDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("UIIntDataValue", 4, buf.readableBytes() - offset); + } else { + UIIntDataValue obj = new UIIntDataValue(); + obj.value = buf.getIntLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 4; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeIntLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 4; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 4 ? ValidationResult.error("Buffer too small: expected at least 4 bytes") : ValidationResult.OK; + } + + public UIIntDataValue clone() { + UIIntDataValue copy = new UIIntDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIIntDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIListDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIListDataValue.java new file mode 100644 index 00000000..604664dc --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIListDataValue.java @@ -0,0 +1,237 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIListDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 0; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 1677721600; + @Nonnull + public String itemTypeName = ""; + @Nonnull + public UIDataValue[] items = new UIDataValue[0]; + + public UIListDataValue() { + } + + public UIListDataValue(@Nonnull String itemTypeName, @Nonnull UIDataValue[] items) { + this.itemTypeName = itemTypeName; + this.items = items; + } + + public UIListDataValue(@Nonnull UIListDataValue other) { + this.itemTypeName = other.itemTypeName; + this.items = other.items; + } + + @Nonnull + public static UIListDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UIListDataValue", 8, buf.readableBytes() - offset); + } else { + UIListDataValue obj = new UIListDataValue(); + int varPosBase0 = buf.getIntLE(offset + 0); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + int varPos0 = offset + 8 + varPosBase0; + int itemTypeNameLen = VarInt.peek(buf, varPos0); + if (itemTypeNameLen < 0) { + throw ProtocolException.invalidVarInt("ItemTypeName"); + } else { + int itemTypeNameVarIntLen = VarInt.size(itemTypeNameLen); + if (itemTypeNameLen > 4096000) { + throw ProtocolException.stringTooLong("ItemTypeName", itemTypeNameLen, 4096000); + } else if (varPos0 + itemTypeNameVarIntLen + itemTypeNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemTypeName", varPos0 + itemTypeNameVarIntLen + itemTypeNameLen, buf.readableBytes()); + } else { + obj.itemTypeName = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + varPos0 = offset + 8 + varPosBase0; + itemTypeNameLen = VarInt.peek(buf, varPos0); + if (itemTypeNameLen < 0) { + throw ProtocolException.invalidVarInt("Items"); + } else { + itemTypeNameVarIntLen = VarInt.size(itemTypeNameLen); + if (itemTypeNameLen > 4096000) { + throw ProtocolException.arrayTooLong("Items", itemTypeNameLen, 4096000); + } else if (varPos0 + itemTypeNameVarIntLen + itemTypeNameLen * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Items", varPos0 + itemTypeNameVarIntLen + itemTypeNameLen * 1, buf.readableBytes()); + } else { + obj.items = new UIDataValue[itemTypeNameLen]; + int elemPos = varPos0 + itemTypeNameVarIntLen; + + for (int i = 0; i < itemTypeNameLen; i++) { + obj.items[i] = UIDataValue.deserialize(buf, elemPos); + elemPos += UIDataValue.computeBytesConsumed(buf, elemPos); + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("Items", varPosBase0, buf.readableBytes()); + } + } + } + } else { + throw ProtocolException.invalidOffset("ItemTypeName", varPosBase0, buf.readableBytes()); + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int maxEnd = 8; + int fieldOffset0 = buf.getIntLE(offset + 0); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + int pos0 = offset + 8 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + pos0 = offset + 8 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl); + + for (int i = 0; i < sl; i++) { + pos0 += UIDataValue.computeBytesConsumed(buf, pos0); + } + + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Items", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("ItemTypeName", fieldOffset0, maxEnd); + } + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + int itemTypeNameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int itemsOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + buf.setIntLE(itemTypeNameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.itemTypeName, 4096000); + buf.setIntLE(itemsOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.items.length > 4096000) { + throw ProtocolException.arrayTooLong("Items", this.items.length, 4096000); + } else { + VarInt.write(buf, this.items.length); + + for (UIDataValue item : this.items) { + item.serializeWithTypeId(buf); + } + + return buf.writerIndex() - startPos; + } + } + + @Override + public int computeSize() { + int size = 8; + size += PacketIO.stringSize(this.itemTypeName); + int itemsSize = 0; + + for (UIDataValue elem : this.items) { + itemsSize += elem.computeSizeWithTypeId(); + } + + return size + VarInt.size(this.items.length) + itemsSize; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 8) { + return ValidationResult.error("Buffer too small: expected at least 8 bytes"); + } else { + int itemTypeNameOffset = buffer.getIntLE(offset + 0); + if (itemTypeNameOffset >= 0 && itemTypeNameOffset <= buffer.writerIndex() - offset - 8) { + int pos = offset + 8 + itemTypeNameOffset; + int itemTypeNameLen = VarInt.peek(buffer, pos); + if (itemTypeNameLen < 0) { + return ValidationResult.error("Invalid string length for ItemTypeName"); + } else if (itemTypeNameLen > 4096000) { + return ValidationResult.error("ItemTypeName exceeds max length 4096000"); + } else { + pos += VarInt.size(itemTypeNameLen); + pos += itemTypeNameLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading ItemTypeName"); + } else { + itemTypeNameOffset = buffer.getIntLE(offset + 4); + if (itemTypeNameOffset >= 0 && itemTypeNameOffset <= buffer.writerIndex() - offset - 8) { + pos = offset + 8 + itemTypeNameOffset; + itemTypeNameLen = VarInt.peek(buffer, pos); + if (itemTypeNameLen < 0) { + return ValidationResult.error("Invalid array count for Items"); + } else if (itemTypeNameLen > 4096000) { + return ValidationResult.error("Items exceeds max length 4096000"); + } else { + pos += VarInt.size(itemTypeNameLen); + + for (int i = 0; i < itemTypeNameLen; i++) { + ValidationResult structResult = UIDataValue.validateStructure(buffer, pos); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid UIDataValue in Items[" + i + "]: " + structResult.error()); + } + + pos += UIDataValue.computeBytesConsumed(buffer, pos); + } + + return ValidationResult.OK; + } + } else { + return ValidationResult.error("Invalid offset for Items"); + } + } + } + } else { + return ValidationResult.error("Invalid offset for ItemTypeName"); + } + } + } + + public UIListDataValue clone() { + UIListDataValue copy = new UIListDataValue(); + copy.itemTypeName = this.itemTypeName; + copy.items = Arrays.copyOf(this.items, this.items.length); + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof UIListDataValue other) + ? false + : Objects.equals(this.itemTypeName, other.itemTypeName) && Arrays.equals((Object[])this.items, (Object[])other.items); + } + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + Objects.hashCode(this.itemTypeName); + return 31 * result + Arrays.hashCode((Object[])this.items); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UILongDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UILongDataValue.java new file mode 100644 index 00000000..70221e83 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UILongDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UILongDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 8; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 8; + public long value; + + public UILongDataValue() { + } + + public UILongDataValue(long value) { + this.value = value; + } + + public UILongDataValue(@Nonnull UILongDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UILongDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UILongDataValue", 8, buf.readableBytes() - offset); + } else { + UILongDataValue obj = new UILongDataValue(); + obj.value = buf.getLongLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 8; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeLongLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 8; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 8 ? ValidationResult.error("Buffer too small: expected at least 8 bytes") : ValidationResult.OK; + } + + public UILongDataValue clone() { + UILongDataValue copy = new UILongDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UILongDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIObjectDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIObjectDataValue.java new file mode 100644 index 00000000..8754919f --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIObjectDataValue.java @@ -0,0 +1,267 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Map.Entry; +import javax.annotation.Nonnull; + +public class UIObjectDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 0; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 1677721600; + @Nonnull + public String typeName = ""; + @Nonnull + public Map properties = new HashMap<>(); + + public UIObjectDataValue() { + } + + public UIObjectDataValue(@Nonnull String typeName, @Nonnull Map properties) { + this.typeName = typeName; + this.properties = properties; + } + + public UIObjectDataValue(@Nonnull UIObjectDataValue other) { + this.typeName = other.typeName; + this.properties = other.properties; + } + + @Nonnull + public static UIObjectDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UIObjectDataValue", 8, buf.readableBytes() - offset); + } else { + UIObjectDataValue obj = new UIObjectDataValue(); + int varPosBase0 = buf.getIntLE(offset + 0); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + int varPos0 = offset + 8 + varPosBase0; + int typeNameLen = VarInt.peek(buf, varPos0); + if (typeNameLen < 0) { + throw ProtocolException.invalidVarInt("TypeName"); + } else { + int typeNameVarIntLen = VarInt.size(typeNameLen); + if (typeNameLen > 4096000) { + throw ProtocolException.stringTooLong("TypeName", typeNameLen, 4096000); + } else if (varPos0 + typeNameVarIntLen + typeNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("TypeName", varPos0 + typeNameVarIntLen + typeNameLen, buf.readableBytes()); + } else { + obj.typeName = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + varPos0 = offset + 8 + varPosBase0; + typeNameLen = VarInt.peek(buf, varPos0); + if (typeNameLen < 0) { + throw ProtocolException.invalidVarInt("Properties"); + } else { + typeNameVarIntLen = VarInt.size(typeNameLen); + if (typeNameLen > 4096000) { + throw ProtocolException.dictionaryTooLarge("Properties", typeNameLen, 4096000); + } else { + obj.properties = new HashMap<>(typeNameLen); + int dictPos = varPos0 + typeNameVarIntLen; + + for (int i = 0; i < typeNameLen; i++) { + int keyLen = VarInt.peek(buf, dictPos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (dictPos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", dictPos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, dictPos); + dictPos += keyVarLen + keyLen; + UIDataValue val = UIDataValue.deserialize(buf, dictPos); + dictPos += UIDataValue.computeBytesConsumed(buf, dictPos); + if (obj.properties.put(key, val) != null) { + throw ProtocolException.duplicateKey("properties", key); + } + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("Properties", varPosBase0, buf.readableBytes()); + } + } + } + } else { + throw ProtocolException.invalidOffset("TypeName", varPosBase0, buf.readableBytes()); + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int maxEnd = 8; + int fieldOffset0 = buf.getIntLE(offset + 0); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + int pos0 = offset + 8 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + pos0 = offset + 8 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl); + + for (int i = 0; i < sl; i++) { + int slx = VarInt.peek(buf, pos0); + pos0 += VarInt.size(slx) + slx; + pos0 += UIDataValue.computeBytesConsumed(buf, pos0); + } + + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Properties", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("TypeName", fieldOffset0, maxEnd); + } + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + int typeNameOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int propertiesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + buf.setIntLE(typeNameOffsetSlot, buf.writerIndex() - varBlockStart); + PacketIO.writeVarString(buf, this.typeName, 4096000); + buf.setIntLE(propertiesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.properties.size() > 4096000) { + throw ProtocolException.dictionaryTooLarge("Properties", this.properties.size(), 4096000); + } else { + VarInt.write(buf, this.properties.size()); + + for (Entry e : this.properties.entrySet()) { + PacketIO.writeVarString(buf, e.getKey(), 4096000); + e.getValue().serializeWithTypeId(buf); + } + + return buf.writerIndex() - startPos; + } + } + + @Override + public int computeSize() { + int size = 8; + size += PacketIO.stringSize(this.typeName); + int propertiesSize = 0; + + for (Entry kvp : this.properties.entrySet()) { + propertiesSize += PacketIO.stringSize(kvp.getKey()) + kvp.getValue().computeSizeWithTypeId(); + } + + return size + VarInt.size(this.properties.size()) + propertiesSize; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 8) { + return ValidationResult.error("Buffer too small: expected at least 8 bytes"); + } else { + int typeNameOffset = buffer.getIntLE(offset + 0); + if (typeNameOffset >= 0 && typeNameOffset <= buffer.writerIndex() - offset - 8) { + int pos = offset + 8 + typeNameOffset; + int typeNameLen = VarInt.peek(buffer, pos); + if (typeNameLen < 0) { + return ValidationResult.error("Invalid string length for TypeName"); + } else if (typeNameLen > 4096000) { + return ValidationResult.error("TypeName exceeds max length 4096000"); + } else { + pos += VarInt.size(typeNameLen); + pos += typeNameLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading TypeName"); + } else { + typeNameOffset = buffer.getIntLE(offset + 4); + if (typeNameOffset >= 0 && typeNameOffset <= buffer.writerIndex() - offset - 8) { + pos = offset + 8 + typeNameOffset; + typeNameLen = VarInt.peek(buffer, pos); + if (typeNameLen < 0) { + return ValidationResult.error("Invalid dictionary count for Properties"); + } else if (typeNameLen > 4096000) { + return ValidationResult.error("Properties exceeds max length 4096000"); + } else { + pos += VarInt.size(typeNameLen); + + for (int i = 0; i < typeNameLen; i++) { + int keyLen = VarInt.peek(buffer, pos); + if (keyLen < 0) { + return ValidationResult.error("Invalid string length for key"); + } + + if (keyLen > 4096000) { + return ValidationResult.error("key exceeds max length 4096000"); + } + + pos += VarInt.size(keyLen); + pos += keyLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading key"); + } + + pos += UIDataValue.computeBytesConsumed(buffer, pos); + } + + return ValidationResult.OK; + } + } else { + return ValidationResult.error("Invalid offset for Properties"); + } + } + } + } else { + return ValidationResult.error("Invalid offset for TypeName"); + } + } + } + + public UIObjectDataValue clone() { + UIObjectDataValue copy = new UIObjectDataValue(); + copy.typeName = this.typeName; + copy.properties = new HashMap<>(this.properties); + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof UIObjectDataValue other) + ? false + : Objects.equals(this.typeName, other.typeName) && Objects.equals(this.properties, other.properties); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.typeName, this.properties); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UISByteDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UISByteDataValue.java new file mode 100644 index 00000000..c2b5715c --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UISByteDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UISByteDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 1; + public static final int MAX_SIZE = 1; + public byte value; + + public UISByteDataValue() { + } + + public UISByteDataValue(byte value) { + this.value = value; + } + + public UISByteDataValue(@Nonnull UISByteDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UISByteDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UISByteDataValue", 1, buf.readableBytes() - offset); + } else { + UISByteDataValue obj = new UISByteDataValue(); + obj.value = buf.getByte(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 1; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeByte(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 1; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + } + + public UISByteDataValue clone() { + UISByteDataValue copy = new UISByteDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UISByteDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIShortDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIShortDataValue.java new file mode 100644 index 00000000..e3bc8650 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIShortDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIShortDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 2; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 2; + public static final int MAX_SIZE = 2; + public short value; + + public UIShortDataValue() { + } + + public UIShortDataValue(short value) { + this.value = value; + } + + public UIShortDataValue(@Nonnull UIShortDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIShortDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UIShortDataValue", 2, buf.readableBytes() - offset); + } else { + UIShortDataValue obj = new UIShortDataValue(); + obj.value = buf.getShortLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 2; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeShortLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 2; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 2 ? ValidationResult.error("Buffer too small: expected at least 2 bytes") : ValidationResult.OK; + } + + public UIShortDataValue clone() { + UIShortDataValue copy = new UIShortDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIShortDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIStringDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIStringDataValue.java new file mode 100644 index 00000000..98b5ccc0 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIStringDataValue.java @@ -0,0 +1,109 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIStringDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 0; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 0; + public static final int MAX_SIZE = 16384005; + @Nonnull + public String value = ""; + + public UIStringDataValue() { + } + + public UIStringDataValue(@Nonnull String value) { + this.value = value; + } + + public UIStringDataValue(@Nonnull UIStringDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIStringDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + UIStringDataValue obj = new UIStringDataValue(); + int pos = offset + 0; + int valueLen = VarInt.peek(buf, pos); + if (valueLen < 0) { + throw ProtocolException.invalidVarInt("Value"); + } else { + int valueVarLen = VarInt.size(valueLen); + if (valueLen > 4096000) { + throw ProtocolException.stringTooLong("Value", valueLen, 4096000); + } else if (pos + valueVarLen + valueLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Value", pos + valueVarLen + valueLen, buf.readableBytes()); + } else { + obj.value = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += valueVarLen + valueLen; + return obj; + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int pos = offset + 0; + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + return pos - offset; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + PacketIO.writeVarString(buf, this.value, 4096000); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + int size = 0; + return size + PacketIO.stringSize(this.value); + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 0) { + return ValidationResult.error("Buffer too small: expected at least 0 bytes"); + } else { + int pos = offset + 0; + int valueLen = VarInt.peek(buffer, pos); + if (valueLen < 0) { + return ValidationResult.error("Invalid string length for Value"); + } else if (valueLen > 4096000) { + return ValidationResult.error("Value exceeds max length 4096000"); + } else { + pos += VarInt.size(valueLen); + pos += valueLen; + return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Value") : ValidationResult.OK; + } + } + } + + public UIStringDataValue clone() { + UIStringDataValue copy = new UIStringDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIStringDataValue other ? Objects.equals(this.value, other.value) : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIUIntDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIUIntDataValue.java new file mode 100644 index 00000000..bd3a4db8 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIUIntDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIUIntDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 4; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 4; + public static final int MAX_SIZE = 4; + public int value; + + public UIUIntDataValue() { + } + + public UIUIntDataValue(int value) { + this.value = value; + } + + public UIUIntDataValue(@Nonnull UIUIntDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIUIntDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("UIUIntDataValue", 4, buf.readableBytes() - offset); + } else { + UIUIntDataValue obj = new UIUIntDataValue(); + obj.value = buf.getIntLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 4; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeIntLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 4; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 4 ? ValidationResult.error("Buffer too small: expected at least 4 bytes") : ValidationResult.OK; + } + + public UIUIntDataValue clone() { + UIUIntDataValue copy = new UIUIntDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIUIntDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIULongDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIULongDataValue.java new file mode 100644 index 00000000..f8cbb072 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIULongDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIULongDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 8; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 8; + public static final int MAX_SIZE = 8; + public long value; + + public UIULongDataValue() { + } + + public UIULongDataValue(long value) { + this.value = value; + } + + public UIULongDataValue(@Nonnull UIULongDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIULongDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UIULongDataValue", 8, buf.readableBytes() - offset); + } else { + UIULongDataValue obj = new UIULongDataValue(); + obj.value = buf.getLongLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 8; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeLongLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 8; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 8 ? ValidationResult.error("Buffer too small: expected at least 8 bytes") : ValidationResult.OK; + } + + public UIULongDataValue clone() { + UIULongDataValue copy = new UIULongDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIULongDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UIUShortDataValue.java b/src/com/hypixel/hytale/protocol/packets/interface_/UIUShortDataValue.java new file mode 100644 index 00000000..8051bc30 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UIUShortDataValue.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class UIUShortDataValue extends UIDataValue { + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 2; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 2; + public static final int MAX_SIZE = 2; + public short value; + + public UIUShortDataValue() { + } + + public UIUShortDataValue(short value) { + this.value = value; + } + + public UIUShortDataValue(@Nonnull UIUShortDataValue other) { + this.value = other.value; + } + + @Nonnull + public static UIUShortDataValue deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UIUShortDataValue", 2, buf.readableBytes() - offset); + } else { + UIUShortDataValue obj = new UIUShortDataValue(); + obj.value = buf.getShortLE(offset + 0); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 2; + } + + @Override + public int serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); + buf.writeShortLE(this.value); + return buf.writerIndex() - startPos; + } + + @Override + public int computeSize() { + return 2; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + return buffer.readableBytes() - offset < 2 ? ValidationResult.error("Buffer too small: expected at least 2 bytes") : ValidationResult.OK; + } + + public UIUShortDataValue clone() { + UIUShortDataValue copy = new UIUShortDataValue(); + copy.value = this.value; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UIUShortDataValue other ? this.value == other.value : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.value); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateAnchorUI.java b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateAnchorUI.java index b94fbdb7..ad98d2cb 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateAnchorUI.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateAnchorUI.java @@ -58,74 +58,98 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { @Nonnull public static UpdateAnchorUI deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateAnchorUI obj = new UpdateAnchorUI(); - byte nullBits = buf.getByte(offset); - obj.clear = buf.getByte(offset + 1) != 0; - if ((nullBits & 1) != 0) { - int varPos0 = offset + 14 + buf.getIntLE(offset + 2); - int anchorIdLen = VarInt.peek(buf, varPos0); - if (anchorIdLen < 0) { - throw ProtocolException.negativeLength("AnchorId", anchorIdLen); + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("UpdateAnchorUI", 14, buf.readableBytes() - offset); + } else { + UpdateAnchorUI obj = new UpdateAnchorUI(); + byte nullBits = buf.getByte(offset); + obj.clear = buf.getByte(offset + 1) != 0; + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 2); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("AnchorId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 14 + varPosBase0; + int anchorIdLen = VarInt.peek(buf, varPos0); + if (anchorIdLen < 0) { + throw ProtocolException.invalidVarInt("AnchorId"); + } + + int anchorIdVarIntLen = VarInt.size(anchorIdLen); + if (anchorIdLen > 4096000) { + throw ProtocolException.stringTooLong("AnchorId", anchorIdLen, 4096000); + } + + if (varPos0 + anchorIdVarIntLen + anchorIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AnchorId", varPos0 + anchorIdVarIntLen + anchorIdLen, buf.readableBytes()); + } + + obj.anchorId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (anchorIdLen > 4096000) { - throw ProtocolException.stringTooLong("AnchorId", anchorIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 6); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Commands", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 14 + varPosBase1; + int commandsCount = VarInt.peek(buf, varPos1); + if (commandsCount < 0) { + throw ProtocolException.invalidVarInt("Commands"); + } + + int varIntLen = VarInt.size(commandsCount); + if (commandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); + } + + if (varPos1 + varIntLen + commandsCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Commands", varPos1 + varIntLen + commandsCount * 2, buf.readableBytes()); + } + + obj.commands = new CustomUICommand[commandsCount]; + int elemPos = varPos1 + varIntLen; + + for (int i = 0; i < commandsCount; i++) { + obj.commands[i] = CustomUICommand.deserialize(buf, elemPos); + elemPos += CustomUICommand.computeBytesConsumed(buf, elemPos); + } } - obj.anchorId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 10); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("EventBindings", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 14 + varPosBase2; + int eventBindingsCount = VarInt.peek(buf, varPos2); + if (eventBindingsCount < 0) { + throw ProtocolException.invalidVarInt("EventBindings"); + } + + int varIntLenx = VarInt.size(eventBindingsCount); + if (eventBindingsCount > 4096000) { + throw ProtocolException.arrayTooLong("EventBindings", eventBindingsCount, 4096000); + } + + if (varPos2 + varIntLenx + eventBindingsCount * 3L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("EventBindings", varPos2 + varIntLenx + eventBindingsCount * 3, buf.readableBytes()); + } + + obj.eventBindings = new CustomUIEventBinding[eventBindingsCount]; + int elemPos = varPos2 + varIntLenx; + + for (int i = 0; i < eventBindingsCount; i++) { + obj.eventBindings[i] = CustomUIEventBinding.deserialize(buf, elemPos); + elemPos += CustomUIEventBinding.computeBytesConsumed(buf, elemPos); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 14 + buf.getIntLE(offset + 6); - int commandsCount = VarInt.peek(buf, varPos1); - if (commandsCount < 0) { - throw ProtocolException.negativeLength("Commands", commandsCount); - } - - if (commandsCount > 4096000) { - throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + commandsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Commands", varPos1 + varIntLen + commandsCount * 2, buf.readableBytes()); - } - - obj.commands = new CustomUICommand[commandsCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < commandsCount; i++) { - obj.commands[i] = CustomUICommand.deserialize(buf, elemPos); - elemPos += CustomUICommand.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 14 + buf.getIntLE(offset + 10); - int eventBindingsCount = VarInt.peek(buf, varPos2); - if (eventBindingsCount < 0) { - throw ProtocolException.negativeLength("EventBindings", eventBindingsCount); - } - - if (eventBindingsCount > 4096000) { - throw ProtocolException.arrayTooLong("EventBindings", eventBindingsCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + eventBindingsCount * 3L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("EventBindings", varPos2 + varIntLen + eventBindingsCount * 3, buf.readableBytes()); - } - - obj.eventBindings = new CustomUIEventBinding[eventBindingsCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < eventBindingsCount; i++) { - obj.eventBindings[i] = CustomUIEventBinding.deserialize(buf, elemPos); - elemPos += CustomUIEventBinding.computeBytesConsumed(buf, elemPos); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -133,9 +157,13 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { int maxEnd = 14; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 2); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("AnchorId", fieldOffset0, maxEnd); + } + int pos0 = offset + 14 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -143,9 +171,13 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 6); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("Commands", fieldOffset1, maxEnd); + } + int pos1 = offset + 14 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += CustomUICommand.computeBytesConsumed(buf, pos1); @@ -158,9 +190,13 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 10); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 14) { + throw ProtocolException.invalidOffset("EventBindings", fieldOffset2, maxEnd); + } + int pos2 = offset + 14 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos2 += CustomUIEventBinding.computeBytesConsumed(buf, pos2); @@ -274,15 +310,11 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int anchorIdOffset = buffer.getIntLE(offset + 2); - if (anchorIdOffset < 0) { + if (anchorIdOffset < 0 || anchorIdOffset > buffer.writerIndex() - offset - 14) { return ValidationResult.error("Invalid offset for AnchorId"); } int pos = offset + 14 + anchorIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AnchorId"); - } - int anchorIdLen = VarInt.peek(buffer, pos); if (anchorIdLen < 0) { return ValidationResult.error("Invalid string length for AnchorId"); @@ -292,7 +324,7 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { return ValidationResult.error("AnchorId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(anchorIdLen); pos += anchorIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading AnchorId"); @@ -301,15 +333,11 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int commandsOffset = buffer.getIntLE(offset + 6); - if (commandsOffset < 0) { + if (commandsOffset < 0 || commandsOffset > buffer.writerIndex() - offset - 14) { return ValidationResult.error("Invalid offset for Commands"); } int posx = offset + 14 + commandsOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Commands"); - } - int commandsCount = VarInt.peek(buffer, posx); if (commandsCount < 0) { return ValidationResult.error("Invalid array count for Commands"); @@ -319,7 +347,7 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { return ValidationResult.error("Commands exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(commandsCount); for (int i = 0; i < commandsCount; i++) { ValidationResult structResult = CustomUICommand.validateStructure(buffer, posx); @@ -333,15 +361,11 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int eventBindingsOffset = buffer.getIntLE(offset + 10); - if (eventBindingsOffset < 0) { + if (eventBindingsOffset < 0 || eventBindingsOffset > buffer.writerIndex() - offset - 14) { return ValidationResult.error("Invalid offset for EventBindings"); } int posxx = offset + 14 + eventBindingsOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for EventBindings"); - } - int eventBindingsCount = VarInt.peek(buffer, posxx); if (eventBindingsCount < 0) { return ValidationResult.error("Invalid array count for EventBindings"); @@ -351,7 +375,7 @@ public class UpdateAnchorUI implements Packet, ToClientPacket { return ValidationResult.error("EventBindings exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(eventBindingsCount); for (int i = 0; i < eventBindingsCount; i++) { ValidationResult structResult = CustomUIEventBinding.validateStructure(buffer, posxx); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateKnownRecipes.java b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateKnownRecipes.java index 0ae498c5..22b3cbbe 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateKnownRecipes.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateKnownRecipes.java @@ -50,44 +50,53 @@ public class UpdateKnownRecipes implements Packet, ToClientPacket { @Nonnull public static UpdateKnownRecipes deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateKnownRecipes obj = new UpdateKnownRecipes(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int knownCount = VarInt.peek(buf, pos); - if (knownCount < 0) { - throw ProtocolException.negativeLength("Known", knownCount); - } - - if (knownCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Known", knownCount, 4096000); - } - - pos += VarInt.size(knownCount); - obj.known = new HashMap<>(knownCount); - - for (int i = 0; i < knownCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UpdateKnownRecipes", 1, buf.readableBytes() - offset); + } else { + UpdateKnownRecipes obj = new UpdateKnownRecipes(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int knownCount = VarInt.peek(buf, pos); + if (knownCount < 0) { + throw ProtocolException.invalidVarInt("Known"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int knownVarLen = VarInt.size(knownCount); + if (knownCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Known", knownCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - CraftingRecipe val = CraftingRecipe.deserialize(buf, pos); - pos += CraftingRecipe.computeBytesConsumed(buf, pos); - if (obj.known.put(key, val) != null) { - throw ProtocolException.duplicateKey("known", key); + pos += knownVarLen; + obj.known = new HashMap<>(knownCount); + + for (int i = 0; i < knownCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + CraftingRecipe val = CraftingRecipe.deserialize(buf, pos); + pos += CraftingRecipe.computeBytesConsumed(buf, pos); + if (obj.known.put(key, val) != null) { + throw ProtocolException.duplicateKey("known", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -95,11 +104,11 @@ public class UpdateKnownRecipes implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += CraftingRecipe.computeBytesConsumed(buf, pos); } } @@ -161,7 +170,7 @@ public class UpdateKnownRecipes implements Packet, ToClientPacket { return ValidationResult.error("Known exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(knownCount); for (int i = 0; i < knownCount; i++) { int keyLen = VarInt.peek(buffer, pos); @@ -173,7 +182,7 @@ public class UpdateKnownRecipes implements Packet, ToClientPacket { return ValidationResult.error("key exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(keyLen); pos += keyLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading key"); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateLanguage.java b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateLanguage.java index 58a905e4..54d3540b 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateLanguage.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateLanguage.java @@ -46,25 +46,33 @@ public class UpdateLanguage implements Packet, ToServerPacket { @Nonnull public static UpdateLanguage deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateLanguage obj = new UpdateLanguage(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int languageLen = VarInt.peek(buf, pos); - if (languageLen < 0) { - throw ProtocolException.negativeLength("Language", languageLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UpdateLanguage", 1, buf.readableBytes() - offset); + } else { + UpdateLanguage obj = new UpdateLanguage(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int languageLen = VarInt.peek(buf, pos); + if (languageLen < 0) { + throw ProtocolException.invalidVarInt("Language"); + } + + int languageVarLen = VarInt.size(languageLen); + if (languageLen > 4096000) { + throw ProtocolException.stringTooLong("Language", languageLen, 4096000); + } + + if (pos + languageVarLen + languageLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Language", pos + languageVarLen + languageLen, buf.readableBytes()); + } + + obj.language = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += languageVarLen + languageLen; } - if (languageLen > 4096000) { - throw ProtocolException.stringTooLong("Language", languageLen, 4096000); - } - - int languageVarLen = VarInt.length(buf, pos); - obj.language = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += languageVarLen + languageLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +80,7 @@ public class UpdateLanguage implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -117,7 +125,7 @@ public class UpdateLanguage implements Packet, ToServerPacket { return ValidationResult.error("Language exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(languageLen); pos += languageLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Language"); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UpdatePortal.java b/src/com/hypixel/hytale/protocol/packets/interface_/UpdatePortal.java index 0fd42908..f66b8b85 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/UpdatePortal.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UpdatePortal.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.interface_; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,19 +48,23 @@ public class UpdatePortal implements Packet, ToClientPacket { @Nonnull public static UpdatePortal deserialize(@Nonnull ByteBuf buf, int offset) { - UpdatePortal obj = new UpdatePortal(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.state = PortalState.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("UpdatePortal", 6, buf.readableBytes() - offset); + } else { + UpdatePortal obj = new UpdatePortal(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.state = PortalState.deserialize(buf, offset + 1); + } - int pos = offset + 6; - if ((nullBits & 2) != 0) { - obj.definition = PortalDef.deserialize(buf, pos); - pos += PortalDef.computeBytesConsumed(buf, pos); - } + int pos = offset + 6; + if ((nullBits & 2) != 0) { + obj.definition = PortalDef.deserialize(buf, pos); + pos += PortalDef.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServerPlayerList.java b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServerPlayerList.java index 3e2ae715..4466f14d 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServerPlayerList.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServerPlayerList.java @@ -45,34 +45,38 @@ public class UpdateServerPlayerList implements Packet, ToClientPacket { @Nonnull public static UpdateServerPlayerList deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateServerPlayerList obj = new UpdateServerPlayerList(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int playersCount = VarInt.peek(buf, pos); - if (playersCount < 0) { - throw ProtocolException.negativeLength("Players", playersCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UpdateServerPlayerList", 1, buf.readableBytes() - offset); + } else { + UpdateServerPlayerList obj = new UpdateServerPlayerList(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int playersCount = VarInt.peek(buf, pos); + if (playersCount < 0) { + throw ProtocolException.invalidVarInt("Players"); + } + + int playersVarLen = VarInt.size(playersCount); + if (playersCount > 4096000) { + throw ProtocolException.arrayTooLong("Players", playersCount, 4096000); + } + + if (pos + playersVarLen + playersCount * 32L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Players", pos + playersVarLen + playersCount * 32, buf.readableBytes()); + } + + pos += playersVarLen; + obj.players = new ServerPlayerListUpdate[playersCount]; + + for (int i = 0; i < playersCount; i++) { + obj.players[i] = ServerPlayerListUpdate.deserialize(buf, pos); + pos += ServerPlayerListUpdate.computeBytesConsumed(buf, pos); + } } - if (playersCount > 4096000) { - throw ProtocolException.arrayTooLong("Players", playersCount, 4096000); - } - - int playersVarLen = VarInt.size(playersCount); - if (pos + playersVarLen + playersCount * 32L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Players", pos + playersVarLen + playersCount * 32, buf.readableBytes()); - } - - pos += playersVarLen; - obj.players = new ServerPlayerListUpdate[playersCount]; - - for (int i = 0; i < playersCount; i++) { - obj.players[i] = ServerPlayerListUpdate.deserialize(buf, pos); - pos += ServerPlayerListUpdate.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class UpdateServerPlayerList implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += ServerPlayerListUpdate.computeBytesConsumed(buf, pos); @@ -137,7 +141,7 @@ public class UpdateServerPlayerList implements Packet, ToClientPacket { return ValidationResult.error("Players exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(playersCount); pos += playersCount * 32; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Players"); diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServerPlayerListPing.java b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServerPlayerListPing.java index 59e9b3e9..95c08595 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServerPlayerListPing.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServerPlayerListPing.java @@ -50,34 +50,39 @@ public class UpdateServerPlayerListPing implements Packet, ToClientPacket { @Nonnull public static UpdateServerPlayerListPing deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateServerPlayerListPing obj = new UpdateServerPlayerListPing(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int playersCount = VarInt.peek(buf, pos); - if (playersCount < 0) { - throw ProtocolException.negativeLength("Players", playersCount); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UpdateServerPlayerListPing", 1, buf.readableBytes() - offset); + } else { + UpdateServerPlayerListPing obj = new UpdateServerPlayerListPing(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int playersCount = VarInt.peek(buf, pos); + if (playersCount < 0) { + throw ProtocolException.invalidVarInt("Players"); + } - if (playersCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Players", playersCount, 4096000); - } + int playersVarLen = VarInt.size(playersCount); + if (playersCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Players", playersCount, 4096000); + } - pos += VarInt.size(playersCount); - obj.players = new HashMap<>(playersCount); + pos += playersVarLen; + obj.players = new HashMap<>(playersCount); - for (int i = 0; i < playersCount; i++) { - UUID key = PacketIO.readUUID(buf, pos); - pos += 16; - int val = buf.getIntLE(pos); - pos += 4; - if (obj.players.put(key, val) != null) { - throw ProtocolException.duplicateKey("players", key); + for (int i = 0; i < playersCount; i++) { + UUID key = PacketIO.readUUID(buf, pos); + pos += 16; + int val = buf.getIntLE(pos); + pos += 4; + if (obj.players.put(key, val) != null) { + throw ProtocolException.duplicateKey("players", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -85,7 +90,7 @@ public class UpdateServerPlayerListPing implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 16; @@ -144,7 +149,7 @@ public class UpdateServerPlayerListPing implements Packet, ToClientPacket { return ValidationResult.error("Players exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(playersCount); for (int i = 0; i < playersCount; i++) { pos += 16; diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServersideUIPage.java b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServersideUIPage.java new file mode 100644 index 00000000..72ed630e --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateServersideUIPage.java @@ -0,0 +1,156 @@ +package com.hypixel.hytale.protocol.packets.interface_; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import javax.annotation.Nonnull; + +public class UpdateServersideUIPage implements Packet, ToClientPacket { + public static final int PACKET_ID = 1200; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 0; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 0; + public static final int MAX_SIZE = 1677721600; + @Nonnull + public ServersideUICommand[] commands = new ServersideUICommand[0]; + + @Override + public int getId() { + return 1200; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public UpdateServersideUIPage() { + } + + public UpdateServersideUIPage(@Nonnull ServersideUICommand[] commands) { + this.commands = commands; + } + + public UpdateServersideUIPage(@Nonnull UpdateServersideUIPage other) { + this.commands = other.commands; + } + + @Nonnull + public static UpdateServersideUIPage deserialize(@Nonnull ByteBuf buf, int offset) { + UpdateServersideUIPage obj = new UpdateServersideUIPage(); + int pos = offset + 0; + int commandsCount = VarInt.peek(buf, pos); + if (commandsCount < 0) { + throw ProtocolException.invalidVarInt("Commands"); + } else { + int commandsVarLen = VarInt.size(commandsCount); + if (commandsCount > 4096000) { + throw ProtocolException.arrayTooLong("Commands", commandsCount, 4096000); + } else if (pos + commandsVarLen + commandsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Commands", pos + commandsVarLen + commandsCount * 1, buf.readableBytes()); + } else { + pos += commandsVarLen; + obj.commands = new ServersideUICommand[commandsCount]; + + for (int i = 0; i < commandsCount; i++) { + obj.commands[i] = ServersideUICommand.deserialize(buf, pos); + pos += ServersideUICommand.computeBytesConsumed(buf, pos); + } + + return obj; + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int pos = offset + 0; + int arrLen = VarInt.peek(buf, pos); + pos += VarInt.size(arrLen); + + for (int i = 0; i < arrLen; i++) { + pos += ServersideUICommand.computeBytesConsumed(buf, pos); + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + if (this.commands.length > 4096000) { + throw ProtocolException.arrayTooLong("Commands", this.commands.length, 4096000); + } else { + VarInt.write(buf, this.commands.length); + + for (ServersideUICommand item : this.commands) { + item.serializeWithTypeId(buf); + } + } + } + + @Override + public int computeSize() { + int size = 0; + int commandsSize = 0; + + for (ServersideUICommand elem : this.commands) { + commandsSize += elem.computeSizeWithTypeId(); + } + + return size + VarInt.size(this.commands.length) + commandsSize; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 0) { + return ValidationResult.error("Buffer too small: expected at least 0 bytes"); + } else { + int pos = offset + 0; + int commandsCount = VarInt.peek(buffer, pos); + if (commandsCount < 0) { + return ValidationResult.error("Invalid array count for Commands"); + } else if (commandsCount > 4096000) { + return ValidationResult.error("Commands exceeds max length 4096000"); + } else { + pos += VarInt.size(commandsCount); + + for (int i = 0; i < commandsCount; i++) { + ValidationResult structResult = ServersideUICommand.validateStructure(buffer, pos); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ServersideUICommand in Commands[" + i + "]: " + structResult.error()); + } + + pos += ServersideUICommand.computeBytesConsumed(buffer, pos); + } + + return ValidationResult.OK; + } + } + } + + public UpdateServersideUIPage clone() { + UpdateServersideUIPage copy = new UpdateServersideUIPage(); + copy.commands = Arrays.copyOf(this.commands, this.commands.length); + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof UpdateServersideUIPage other ? Arrays.equals((Object[])this.commands, (Object[])other.commands) : false; + } + } + + @Override + public int hashCode() { + int result = 1; + return 31 * result + Arrays.hashCode((Object[])this.commands); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateVisibleHudComponents.java b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateVisibleHudComponents.java index 3b8c3a14..41b09d5e 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/UpdateVisibleHudComponents.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/UpdateVisibleHudComponents.java @@ -45,34 +45,38 @@ public class UpdateVisibleHudComponents implements Packet, ToClientPacket { @Nonnull public static UpdateVisibleHudComponents deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateVisibleHudComponents obj = new UpdateVisibleHudComponents(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int visibleComponentsCount = VarInt.peek(buf, pos); - if (visibleComponentsCount < 0) { - throw ProtocolException.negativeLength("VisibleComponents", visibleComponentsCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UpdateVisibleHudComponents", 1, buf.readableBytes() - offset); + } else { + UpdateVisibleHudComponents obj = new UpdateVisibleHudComponents(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int visibleComponentsCount = VarInt.peek(buf, pos); + if (visibleComponentsCount < 0) { + throw ProtocolException.invalidVarInt("VisibleComponents"); + } + + int visibleComponentsVarLen = VarInt.size(visibleComponentsCount); + if (visibleComponentsCount > 4096000) { + throw ProtocolException.arrayTooLong("VisibleComponents", visibleComponentsCount, 4096000); + } + + if (pos + visibleComponentsVarLen + visibleComponentsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("VisibleComponents", pos + visibleComponentsVarLen + visibleComponentsCount * 1, buf.readableBytes()); + } + + pos += visibleComponentsVarLen; + obj.visibleComponents = new HudComponent[visibleComponentsCount]; + + for (int i = 0; i < visibleComponentsCount; i++) { + obj.visibleComponents[i] = HudComponent.fromValue(buf.getByte(pos)); + pos++; + } } - if (visibleComponentsCount > 4096000) { - throw ProtocolException.arrayTooLong("VisibleComponents", visibleComponentsCount, 4096000); - } - - int visibleComponentsVarLen = VarInt.size(visibleComponentsCount); - if (pos + visibleComponentsVarLen + visibleComponentsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("VisibleComponents", pos + visibleComponentsVarLen + visibleComponentsCount * 1, buf.readableBytes()); - } - - pos += visibleComponentsVarLen; - obj.visibleComponents = new HudComponent[visibleComponentsCount]; - - for (int i = 0; i < visibleComponentsCount; i++) { - obj.visibleComponents[i] = HudComponent.fromValue(buf.getByte(pos)); - pos++; - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -80,7 +84,7 @@ public class UpdateVisibleHudComponents implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -133,11 +137,19 @@ public class UpdateVisibleHudComponents implements Packet, ToClientPacket { return ValidationResult.error("VisibleComponents exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); - pos += visibleComponentsCount * 1; - if (pos > buffer.writerIndex()) { + pos += VarInt.size(visibleComponentsCount); + if (pos + visibleComponentsCount * 1L > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading VisibleComponents"); } + + for (int i = 0; i < visibleComponentsCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 24) { + return ValidationResult.error("Invalid HudComponent value for VisibleComponents[i]"); + } + + pos++; + } } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/packets/interface_/WorldSavingStatus.java b/src/com/hypixel/hytale/protocol/packets/interface_/WorldSavingStatus.java index fe435b00..7c45b111 100644 --- a/src/com/hypixel/hytale/protocol/packets/interface_/WorldSavingStatus.java +++ b/src/com/hypixel/hytale/protocol/packets/interface_/WorldSavingStatus.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.interface_; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class WorldSavingStatus implements Packet, ToClientPacket { @Nonnull public static WorldSavingStatus deserialize(@Nonnull ByteBuf buf, int offset) { - WorldSavingStatus obj = new WorldSavingStatus(); - obj.isWorldSaving = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("WorldSavingStatus", 1, buf.readableBytes() - offset); + } else { + WorldSavingStatus obj = new WorldSavingStatus(); + obj.isWorldSaving = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/DropItemStack.java b/src/com/hypixel/hytale/protocol/packets/inventory/DropItemStack.java index c2a07333..45c688eb 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/DropItemStack.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/DropItemStack.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.inventory; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,11 +48,15 @@ public class DropItemStack implements Packet, ToServerPacket { @Nonnull public static DropItemStack deserialize(@Nonnull ByteBuf buf, int offset) { - DropItemStack obj = new DropItemStack(); - obj.inventorySectionId = buf.getIntLE(offset + 0); - obj.slotId = buf.getIntLE(offset + 4); - obj.quantity = buf.getIntLE(offset + 8); - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("DropItemStack", 12, buf.readableBytes() - offset); + } else { + DropItemStack obj = new DropItemStack(); + obj.inventorySectionId = buf.getIntLE(offset + 0); + obj.slotId = buf.getIntLE(offset + 4); + obj.quantity = buf.getIntLE(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/InventoryAction.java b/src/com/hypixel/hytale/protocol/packets/inventory/InventoryAction.java index 8bc329ed..cd6283f1 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/InventoryAction.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/InventoryAction.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.InventoryActionType; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -49,11 +50,15 @@ public class InventoryAction implements Packet, ToServerPacket { @Nonnull public static InventoryAction deserialize(@Nonnull ByteBuf buf, int offset) { - InventoryAction obj = new InventoryAction(); - obj.inventorySectionId = buf.getIntLE(offset + 0); - obj.inventoryActionType = InventoryActionType.fromValue(buf.getByte(offset + 4)); - obj.actionData = buf.getByte(offset + 5); - return obj; + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("InventoryAction", 6, buf.readableBytes() - offset); + } else { + InventoryAction obj = new InventoryAction(); + obj.inventorySectionId = buf.getIntLE(offset + 0); + obj.inventoryActionType = InventoryActionType.fromValue(buf.getByte(offset + 4)); + obj.actionData = buf.getByte(offset + 5); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -73,7 +78,12 @@ public class InventoryAction implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 6 ? ValidationResult.error("Buffer too small: expected at least 6 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 6) { + return ValidationResult.error("Buffer too small: expected at least 6 bytes"); + } else { + int v = buffer.getByte(offset + 4) & 255; + return v >= 4 ? ValidationResult.error("Invalid InventoryActionType value for InventoryActionType") : ValidationResult.OK; + } } public InventoryAction clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/MoveItemStack.java b/src/com/hypixel/hytale/protocol/packets/inventory/MoveItemStack.java index ef1cd823..70d85229 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/MoveItemStack.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/MoveItemStack.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.inventory; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -53,13 +54,17 @@ public class MoveItemStack implements Packet, ToServerPacket { @Nonnull public static MoveItemStack deserialize(@Nonnull ByteBuf buf, int offset) { - MoveItemStack obj = new MoveItemStack(); - obj.fromSectionId = buf.getIntLE(offset + 0); - obj.fromSlotId = buf.getIntLE(offset + 4); - obj.quantity = buf.getIntLE(offset + 8); - obj.toSectionId = buf.getIntLE(offset + 12); - obj.toSlotId = buf.getIntLE(offset + 16); - return obj; + if (buf.readableBytes() - offset < 20) { + throw ProtocolException.bufferTooSmall("MoveItemStack", 20, buf.readableBytes() - offset); + } else { + MoveItemStack obj = new MoveItemStack(); + obj.fromSectionId = buf.getIntLE(offset + 0); + obj.fromSlotId = buf.getIntLE(offset + 4); + obj.quantity = buf.getIntLE(offset + 8); + obj.toSectionId = buf.getIntLE(offset + 12); + obj.toSlotId = buf.getIntLE(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/SetActiveSlot.java b/src/com/hypixel/hytale/protocol/packets/inventory/SetActiveSlot.java index 14966a60..a66eef7b 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/SetActiveSlot.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/SetActiveSlot.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -45,10 +46,14 @@ public class SetActiveSlot implements Packet, ToServerPacket, ToClientPacket { @Nonnull public static SetActiveSlot deserialize(@Nonnull ByteBuf buf, int offset) { - SetActiveSlot obj = new SetActiveSlot(); - obj.inventorySectionId = buf.getIntLE(offset + 0); - obj.activeSlot = buf.getIntLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("SetActiveSlot", 8, buf.readableBytes() - offset); + } else { + SetActiveSlot obj = new SetActiveSlot(); + obj.inventorySectionId = buf.getIntLE(offset + 0); + obj.activeSlot = buf.getIntLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/SetCreativeItem.java b/src/com/hypixel/hytale/protocol/packets/inventory/SetCreativeItem.java index a5dc16c0..d42f54c9 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/SetCreativeItem.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/SetCreativeItem.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.ItemQuantity; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -52,14 +53,18 @@ public class SetCreativeItem implements Packet, ToServerPacket { @Nonnull public static SetCreativeItem deserialize(@Nonnull ByteBuf buf, int offset) { - SetCreativeItem obj = new SetCreativeItem(); - obj.inventorySectionId = buf.getIntLE(offset + 0); - obj.slotId = buf.getIntLE(offset + 4); - obj.override = buf.getByte(offset + 8) != 0; - int pos = offset + 9; - obj.item = ItemQuantity.deserialize(buf, pos); - pos += ItemQuantity.computeBytesConsumed(buf, pos); - return obj; + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("SetCreativeItem", 9, buf.readableBytes() - offset); + } else { + SetCreativeItem obj = new SetCreativeItem(); + obj.inventorySectionId = buf.getIntLE(offset + 0); + obj.slotId = buf.getIntLE(offset + 4); + obj.override = buf.getByte(offset + 8) != 0; + int pos = offset + 9; + obj.item = ItemQuantity.deserialize(buf, pos); + pos += ItemQuantity.computeBytesConsumed(buf, pos); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/SmartGiveCreativeItem.java b/src/com/hypixel/hytale/protocol/packets/inventory/SmartGiveCreativeItem.java index b39d230b..71347f91 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/SmartGiveCreativeItem.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/SmartGiveCreativeItem.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.SmartMoveType; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -48,12 +49,16 @@ public class SmartGiveCreativeItem implements Packet, ToServerPacket { @Nonnull public static SmartGiveCreativeItem deserialize(@Nonnull ByteBuf buf, int offset) { - SmartGiveCreativeItem obj = new SmartGiveCreativeItem(); - obj.moveType = SmartMoveType.fromValue(buf.getByte(offset + 0)); - int pos = offset + 1; - obj.item = ItemQuantity.deserialize(buf, pos); - pos += ItemQuantity.computeBytesConsumed(buf, pos); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SmartGiveCreativeItem", 1, buf.readableBytes() - offset); + } else { + SmartGiveCreativeItem obj = new SmartGiveCreativeItem(); + obj.moveType = SmartMoveType.fromValue(buf.getByte(offset + 0)); + int pos = offset + 1; + obj.item = ItemQuantity.deserialize(buf, pos); + pos += ItemQuantity.computeBytesConsumed(buf, pos); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,13 +83,18 @@ public class SmartGiveCreativeItem implements Packet, ToServerPacket { if (buffer.readableBytes() - offset < 1) { return ValidationResult.error("Buffer too small: expected at least 1 bytes"); } else { - int pos = offset + 1; - ValidationResult itemResult = ItemQuantity.validateStructure(buffer, pos); - if (!itemResult.isValid()) { - return ValidationResult.error("Invalid Item: " + itemResult.error()); + int v = buffer.getByte(offset + 0) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid SmartMoveType value for MoveType"); } else { - pos += ItemQuantity.computeBytesConsumed(buffer, pos); - return ValidationResult.OK; + v = offset + 1; + ValidationResult itemResult = ItemQuantity.validateStructure(buffer, v); + if (!itemResult.isValid()) { + return ValidationResult.error("Invalid Item: " + itemResult.error()); + } else { + v += ItemQuantity.computeBytesConsumed(buffer, v); + return ValidationResult.OK; + } } } } diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/SmartMoveItemStack.java b/src/com/hypixel/hytale/protocol/packets/inventory/SmartMoveItemStack.java index 061343bb..8bccfbe5 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/SmartMoveItemStack.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/SmartMoveItemStack.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.SmartMoveType; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -53,12 +54,16 @@ public class SmartMoveItemStack implements Packet, ToServerPacket, ToClientPacke @Nonnull public static SmartMoveItemStack deserialize(@Nonnull ByteBuf buf, int offset) { - SmartMoveItemStack obj = new SmartMoveItemStack(); - obj.fromSectionId = buf.getIntLE(offset + 0); - obj.fromSlotId = buf.getIntLE(offset + 4); - obj.quantity = buf.getIntLE(offset + 8); - obj.moveType = SmartMoveType.fromValue(buf.getByte(offset + 12)); - return obj; + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("SmartMoveItemStack", 13, buf.readableBytes() - offset); + } else { + SmartMoveItemStack obj = new SmartMoveItemStack(); + obj.fromSectionId = buf.getIntLE(offset + 0); + obj.fromSlotId = buf.getIntLE(offset + 4); + obj.quantity = buf.getIntLE(offset + 8); + obj.moveType = SmartMoveType.fromValue(buf.getByte(offset + 12)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -79,7 +84,12 @@ public class SmartMoveItemStack implements Packet, ToServerPacket, ToClientPacke } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 13 ? ValidationResult.error("Buffer too small: expected at least 13 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 13) { + return ValidationResult.error("Buffer too small: expected at least 13 bytes"); + } else { + int v = buffer.getByte(offset + 12) & 255; + return v >= 3 ? ValidationResult.error("Invalid SmartMoveType value for MoveType") : ValidationResult.OK; + } } public SmartMoveItemStack clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/SwitchHotbarBlockSet.java b/src/com/hypixel/hytale/protocol/packets/inventory/SwitchHotbarBlockSet.java index 6b21c13d..4b5bd5b4 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/SwitchHotbarBlockSet.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/SwitchHotbarBlockSet.java @@ -46,25 +46,33 @@ public class SwitchHotbarBlockSet implements Packet, ToServerPacket { @Nonnull public static SwitchHotbarBlockSet deserialize(@Nonnull ByteBuf buf, int offset) { - SwitchHotbarBlockSet obj = new SwitchHotbarBlockSet(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int itemIdLen = VarInt.peek(buf, pos); - if (itemIdLen < 0) { - throw ProtocolException.negativeLength("ItemId", itemIdLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SwitchHotbarBlockSet", 1, buf.readableBytes() - offset); + } else { + SwitchHotbarBlockSet obj = new SwitchHotbarBlockSet(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int itemIdLen = VarInt.peek(buf, pos); + if (itemIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemId"); + } + + int itemIdVarLen = VarInt.size(itemIdLen); + if (itemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); + } + + if (pos + itemIdVarLen + itemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemId", pos + itemIdVarLen + itemIdLen, buf.readableBytes()); + } + + obj.itemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += itemIdVarLen + itemIdLen; } - if (itemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemId", itemIdLen, 4096000); - } - - int itemIdVarLen = VarInt.length(buf, pos); - obj.itemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += itemIdVarLen + itemIdLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +80,7 @@ public class SwitchHotbarBlockSet implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -117,7 +125,7 @@ public class SwitchHotbarBlockSet implements Packet, ToServerPacket { return ValidationResult.error("ItemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(itemIdLen); pos += itemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemId"); diff --git a/src/com/hypixel/hytale/protocol/packets/inventory/UpdatePlayerInventory.java b/src/com/hypixel/hytale/protocol/packets/inventory/UpdatePlayerInventory.java index 72744306..9c6e0b89 100644 --- a/src/com/hypixel/hytale/protocol/packets/inventory/UpdatePlayerInventory.java +++ b/src/com/hypixel/hytale/protocol/packets/inventory/UpdatePlayerInventory.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.protocol.packets.inventory; import com.hypixel.hytale.protocol.InventorySection; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; -import com.hypixel.hytale.protocol.SortType; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -15,9 +15,9 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { public static final int PACKET_ID = 170; public static final boolean IS_COMPRESSED = true; public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 2; - public static final int VARIABLE_FIELD_COUNT = 7; - public static final int VARIABLE_BLOCK_START = 30; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 6; + public static final int VARIABLE_BLOCK_START = 25; public static final int MAX_SIZE = 1677721600; @Nullable public InventorySection storage; @@ -28,13 +28,9 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { @Nullable public InventorySection utility; @Nullable - public InventorySection builderMaterial; - @Nullable public InventorySection tools; @Nullable public InventorySection backpack; - @Nonnull - public SortType sortType = SortType.Name; @Override public int getId() { @@ -54,19 +50,15 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { @Nullable InventorySection armor, @Nullable InventorySection hotbar, @Nullable InventorySection utility, - @Nullable InventorySection builderMaterial, @Nullable InventorySection tools, - @Nullable InventorySection backpack, - @Nonnull SortType sortType + @Nullable InventorySection backpack ) { this.storage = storage; this.armor = armor; this.hotbar = hotbar; this.utility = utility; - this.builderMaterial = builderMaterial; this.tools = tools; this.backpack = backpack; - this.sortType = sortType; } public UpdatePlayerInventory(@Nonnull UpdatePlayerInventory other) { @@ -74,61 +66,91 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { this.armor = other.armor; this.hotbar = other.hotbar; this.utility = other.utility; - this.builderMaterial = other.builderMaterial; this.tools = other.tools; this.backpack = other.backpack; - this.sortType = other.sortType; } @Nonnull public static UpdatePlayerInventory deserialize(@Nonnull ByteBuf buf, int offset) { - UpdatePlayerInventory obj = new UpdatePlayerInventory(); - byte nullBits = buf.getByte(offset); - obj.sortType = SortType.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 30 + buf.getIntLE(offset + 2); - obj.storage = InventorySection.deserialize(buf, varPos0); - } + if (buf.readableBytes() - offset < 25) { + throw ProtocolException.bufferTooSmall("UpdatePlayerInventory", 25, buf.readableBytes() - offset); + } else { + UpdatePlayerInventory obj = new UpdatePlayerInventory(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Storage", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 30 + buf.getIntLE(offset + 6); - obj.armor = InventorySection.deserialize(buf, varPos1); - } + int varPos0 = offset + 25 + varPosBase0; + obj.storage = InventorySection.deserialize(buf, varPos0); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 30 + buf.getIntLE(offset + 10); - obj.hotbar = InventorySection.deserialize(buf, varPos2); - } + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Armor", varPosBase1, buf.readableBytes()); + } - if ((nullBits & 8) != 0) { - int varPos3 = offset + 30 + buf.getIntLE(offset + 14); - obj.utility = InventorySection.deserialize(buf, varPos3); - } + int varPos1 = offset + 25 + varPosBase1; + obj.armor = InventorySection.deserialize(buf, varPos1); + } - if ((nullBits & 16) != 0) { - int varPos4 = offset + 30 + buf.getIntLE(offset + 18); - obj.builderMaterial = InventorySection.deserialize(buf, varPos4); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Hotbar", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 32) != 0) { - int varPos5 = offset + 30 + buf.getIntLE(offset + 22); - obj.tools = InventorySection.deserialize(buf, varPos5); - } + int varPos2 = offset + 25 + varPosBase2; + obj.hotbar = InventorySection.deserialize(buf, varPos2); + } - if ((nullBits & 64) != 0) { - int varPos6 = offset + 30 + buf.getIntLE(offset + 26); - obj.backpack = InventorySection.deserialize(buf, varPos6); - } + if ((nullBits & 8) != 0) { + int varPosBase3 = buf.getIntLE(offset + 13); + if (varPosBase3 < 0 || varPosBase3 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Utility", varPosBase3, buf.readableBytes()); + } - return obj; + int varPos3 = offset + 25 + varPosBase3; + obj.utility = InventorySection.deserialize(buf, varPos3); + } + + if ((nullBits & 16) != 0) { + int varPosBase4 = buf.getIntLE(offset + 17); + if (varPosBase4 < 0 || varPosBase4 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Tools", varPosBase4, buf.readableBytes()); + } + + int varPos4 = offset + 25 + varPosBase4; + obj.tools = InventorySection.deserialize(buf, varPos4); + } + + if ((nullBits & 32) != 0) { + int varPosBase5 = buf.getIntLE(offset + 21); + if (varPosBase5 < 0 || varPosBase5 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Backpack", varPosBase5, buf.readableBytes()); + } + + int varPos5 = offset + 25 + varPosBase5; + obj.backpack = InventorySection.deserialize(buf, varPos5); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 30; + int maxEnd = 25; if ((nullBits & 1) != 0) { - int fieldOffset0 = buf.getIntLE(offset + 2); - int pos0 = offset + 30 + fieldOffset0; + int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Storage", fieldOffset0, maxEnd); + } + + int pos0 = offset + 25 + fieldOffset0; pos0 += InventorySection.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; @@ -136,8 +158,12 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { } if ((nullBits & 2) != 0) { - int fieldOffset1 = buf.getIntLE(offset + 6); - int pos1 = offset + 30 + fieldOffset1; + int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Armor", fieldOffset1, maxEnd); + } + + int pos1 = offset + 25 + fieldOffset1; pos1 += InventorySection.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; @@ -145,8 +171,12 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { } if ((nullBits & 4) != 0) { - int fieldOffset2 = buf.getIntLE(offset + 10); - int pos2 = offset + 30 + fieldOffset2; + int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Hotbar", fieldOffset2, maxEnd); + } + + int pos2 = offset + 25 + fieldOffset2; pos2 += InventorySection.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; @@ -154,8 +184,12 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { } if ((nullBits & 8) != 0) { - int fieldOffset3 = buf.getIntLE(offset + 14); - int pos3 = offset + 30 + fieldOffset3; + int fieldOffset3 = buf.getIntLE(offset + 13); + if (fieldOffset3 < 0 || fieldOffset3 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Utility", fieldOffset3, maxEnd); + } + + int pos3 = offset + 25 + fieldOffset3; pos3 += InventorySection.computeBytesConsumed(buf, pos3); if (pos3 - offset > maxEnd) { maxEnd = pos3 - offset; @@ -163,8 +197,12 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { } if ((nullBits & 16) != 0) { - int fieldOffset4 = buf.getIntLE(offset + 18); - int pos4 = offset + 30 + fieldOffset4; + int fieldOffset4 = buf.getIntLE(offset + 17); + if (fieldOffset4 < 0 || fieldOffset4 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Tools", fieldOffset4, maxEnd); + } + + int pos4 = offset + 25 + fieldOffset4; pos4 += InventorySection.computeBytesConsumed(buf, pos4); if (pos4 - offset > maxEnd) { maxEnd = pos4 - offset; @@ -172,23 +210,18 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { } if ((nullBits & 32) != 0) { - int fieldOffset5 = buf.getIntLE(offset + 22); - int pos5 = offset + 30 + fieldOffset5; + int fieldOffset5 = buf.getIntLE(offset + 21); + if (fieldOffset5 < 0 || fieldOffset5 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Backpack", fieldOffset5, maxEnd); + } + + int pos5 = offset + 25 + fieldOffset5; pos5 += InventorySection.computeBytesConsumed(buf, pos5); if (pos5 - offset > maxEnd) { maxEnd = pos5 - offset; } } - if ((nullBits & 64) != 0) { - int fieldOffset6 = buf.getIntLE(offset + 26); - int pos6 = offset + 30 + fieldOffset6; - pos6 += InventorySection.computeBytesConsumed(buf, pos6); - if (pos6 - offset > maxEnd) { - maxEnd = pos6 - offset; - } - } - return maxEnd; } @@ -212,20 +245,15 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { nullBits = (byte)(nullBits | 8); } - if (this.builderMaterial != null) { + if (this.tools != null) { nullBits = (byte)(nullBits | 16); } - if (this.tools != null) { + if (this.backpack != null) { nullBits = (byte)(nullBits | 32); } - if (this.backpack != null) { - nullBits = (byte)(nullBits | 64); - } - buf.writeByte(nullBits); - buf.writeByte(this.sortType.getValue()); int storageOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int armorOffsetSlot = buf.writerIndex(); @@ -234,8 +262,6 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { buf.writeIntLE(0); int utilityOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); - int builderMaterialOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); int toolsOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int backpackOffsetSlot = buf.writerIndex(); @@ -269,13 +295,6 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { buf.setIntLE(utilityOffsetSlot, -1); } - if (this.builderMaterial != null) { - buf.setIntLE(builderMaterialOffsetSlot, buf.writerIndex() - varBlockStart); - this.builderMaterial.serialize(buf); - } else { - buf.setIntLE(builderMaterialOffsetSlot, -1); - } - if (this.tools != null) { buf.setIntLE(toolsOffsetSlot, buf.writerIndex() - varBlockStart); this.tools.serialize(buf); @@ -293,7 +312,7 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { @Override public int computeSize() { - int size = 30; + int size = 25; if (this.storage != null) { size += this.storage.computeSize(); } @@ -310,10 +329,6 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { size += this.utility.computeSize(); } - if (this.builderMaterial != null) { - size += this.builderMaterial.computeSize(); - } - if (this.tools != null) { size += this.tools.computeSize(); } @@ -326,21 +341,17 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 30) { - return ValidationResult.error("Buffer too small: expected at least 30 bytes"); + if (buffer.readableBytes() - offset < 25) { + return ValidationResult.error("Buffer too small: expected at least 25 bytes"); } else { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { - int storageOffset = buffer.getIntLE(offset + 2); - if (storageOffset < 0) { + int storageOffset = buffer.getIntLE(offset + 1); + if (storageOffset < 0 || storageOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for Storage"); } - int pos = offset + 30 + storageOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Storage"); - } - + int pos = offset + 25 + storageOffset; ValidationResult storageResult = InventorySection.validateStructure(buffer, pos); if (!storageResult.isValid()) { return ValidationResult.error("Invalid Storage: " + storageResult.error()); @@ -350,117 +361,78 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { } if ((nullBits & 2) != 0) { - int armorOffset = buffer.getIntLE(offset + 6); - if (armorOffset < 0) { + int armorOffset = buffer.getIntLE(offset + 5); + if (armorOffset < 0 || armorOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for Armor"); } - int posx = offset + 30 + armorOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Armor"); - } - - ValidationResult armorResult = InventorySection.validateStructure(buffer, posx); + int pos = offset + 25 + armorOffset; + ValidationResult armorResult = InventorySection.validateStructure(buffer, pos); if (!armorResult.isValid()) { return ValidationResult.error("Invalid Armor: " + armorResult.error()); } - posx += InventorySection.computeBytesConsumed(buffer, posx); + pos += InventorySection.computeBytesConsumed(buffer, pos); } if ((nullBits & 4) != 0) { - int hotbarOffset = buffer.getIntLE(offset + 10); - if (hotbarOffset < 0) { + int hotbarOffset = buffer.getIntLE(offset + 9); + if (hotbarOffset < 0 || hotbarOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for Hotbar"); } - int posxx = offset + 30 + hotbarOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Hotbar"); - } - - ValidationResult hotbarResult = InventorySection.validateStructure(buffer, posxx); + int pos = offset + 25 + hotbarOffset; + ValidationResult hotbarResult = InventorySection.validateStructure(buffer, pos); if (!hotbarResult.isValid()) { return ValidationResult.error("Invalid Hotbar: " + hotbarResult.error()); } - posxx += InventorySection.computeBytesConsumed(buffer, posxx); + pos += InventorySection.computeBytesConsumed(buffer, pos); } if ((nullBits & 8) != 0) { - int utilityOffset = buffer.getIntLE(offset + 14); - if (utilityOffset < 0) { + int utilityOffset = buffer.getIntLE(offset + 13); + if (utilityOffset < 0 || utilityOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for Utility"); } - int posxxx = offset + 30 + utilityOffset; - if (posxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Utility"); - } - - ValidationResult utilityResult = InventorySection.validateStructure(buffer, posxxx); + int pos = offset + 25 + utilityOffset; + ValidationResult utilityResult = InventorySection.validateStructure(buffer, pos); if (!utilityResult.isValid()) { return ValidationResult.error("Invalid Utility: " + utilityResult.error()); } - posxxx += InventorySection.computeBytesConsumed(buffer, posxxx); + pos += InventorySection.computeBytesConsumed(buffer, pos); } if ((nullBits & 16) != 0) { - int builderMaterialOffset = buffer.getIntLE(offset + 18); - if (builderMaterialOffset < 0) { - return ValidationResult.error("Invalid offset for BuilderMaterial"); - } - - int posxxxx = offset + 30 + builderMaterialOffset; - if (posxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BuilderMaterial"); - } - - ValidationResult builderMaterialResult = InventorySection.validateStructure(buffer, posxxxx); - if (!builderMaterialResult.isValid()) { - return ValidationResult.error("Invalid BuilderMaterial: " + builderMaterialResult.error()); - } - - posxxxx += InventorySection.computeBytesConsumed(buffer, posxxxx); - } - - if ((nullBits & 32) != 0) { - int toolsOffset = buffer.getIntLE(offset + 22); - if (toolsOffset < 0) { + int toolsOffset = buffer.getIntLE(offset + 17); + if (toolsOffset < 0 || toolsOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for Tools"); } - int posxxxxx = offset + 30 + toolsOffset; - if (posxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Tools"); - } - - ValidationResult toolsResult = InventorySection.validateStructure(buffer, posxxxxx); + int pos = offset + 25 + toolsOffset; + ValidationResult toolsResult = InventorySection.validateStructure(buffer, pos); if (!toolsResult.isValid()) { return ValidationResult.error("Invalid Tools: " + toolsResult.error()); } - posxxxxx += InventorySection.computeBytesConsumed(buffer, posxxxxx); + pos += InventorySection.computeBytesConsumed(buffer, pos); } - if ((nullBits & 64) != 0) { - int backpackOffset = buffer.getIntLE(offset + 26); - if (backpackOffset < 0) { + if ((nullBits & 32) != 0) { + int backpackOffset = buffer.getIntLE(offset + 21); + if (backpackOffset < 0 || backpackOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for Backpack"); } - int posxxxxxx = offset + 30 + backpackOffset; - if (posxxxxxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Backpack"); - } - - ValidationResult backpackResult = InventorySection.validateStructure(buffer, posxxxxxx); + int pos = offset + 25 + backpackOffset; + ValidationResult backpackResult = InventorySection.validateStructure(buffer, pos); if (!backpackResult.isValid()) { return ValidationResult.error("Invalid Backpack: " + backpackResult.error()); } - posxxxxxx += InventorySection.computeBytesConsumed(buffer, posxxxxxx); + pos += InventorySection.computeBytesConsumed(buffer, pos); } return ValidationResult.OK; @@ -473,10 +445,8 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { copy.armor = this.armor != null ? this.armor.clone() : null; copy.hotbar = this.hotbar != null ? this.hotbar.clone() : null; copy.utility = this.utility != null ? this.utility.clone() : null; - copy.builderMaterial = this.builderMaterial != null ? this.builderMaterial.clone() : null; copy.tools = this.tools != null ? this.tools.clone() : null; copy.backpack = this.backpack != null ? this.backpack.clone() : null; - copy.sortType = this.sortType; return copy; } @@ -491,15 +461,13 @@ public class UpdatePlayerInventory implements Packet, ToClientPacket { && Objects.equals(this.armor, other.armor) && Objects.equals(this.hotbar, other.hotbar) && Objects.equals(this.utility, other.utility) - && Objects.equals(this.builderMaterial, other.builderMaterial) && Objects.equals(this.tools, other.tools) - && Objects.equals(this.backpack, other.backpack) - && Objects.equals(this.sortType, other.sortType); + && Objects.equals(this.backpack, other.backpack); } } @Override public int hashCode() { - return Objects.hash(this.storage, this.armor, this.hotbar, this.utility, this.builderMaterial, this.tools, this.backpack, this.sortType); + return Objects.hash(this.storage, this.armor, this.hotbar, this.utility, this.tools, this.backpack); } } diff --git a/src/com/hypixel/hytale/protocol/packets/machinima/RequestMachinimaActorModel.java b/src/com/hypixel/hytale/protocol/packets/machinima/RequestMachinimaActorModel.java index b419c1cc..feacfb5f 100644 --- a/src/com/hypixel/hytale/protocol/packets/machinima/RequestMachinimaActorModel.java +++ b/src/com/hypixel/hytale/protocol/packets/machinima/RequestMachinimaActorModel.java @@ -54,51 +54,85 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { @Nonnull public static RequestMachinimaActorModel deserialize(@Nonnull ByteBuf buf, int offset) { - RequestMachinimaActorModel obj = new RequestMachinimaActorModel(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int modelIdLen = VarInt.peek(buf, varPos0); - if (modelIdLen < 0) { - throw ProtocolException.negativeLength("ModelId", modelIdLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("RequestMachinimaActorModel", 13, buf.readableBytes() - offset); + } else { + RequestMachinimaActorModel obj = new RequestMachinimaActorModel(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ModelId", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + int modelIdLen = VarInt.peek(buf, varPos0); + if (modelIdLen < 0) { + throw ProtocolException.invalidVarInt("ModelId"); + } + + int modelIdVarIntLen = VarInt.size(modelIdLen); + if (modelIdLen > 4096000) { + throw ProtocolException.stringTooLong("ModelId", modelIdLen, 4096000); + } + + if (varPos0 + modelIdVarIntLen + modelIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ModelId", varPos0 + modelIdVarIntLen + modelIdLen, buf.readableBytes()); + } + + obj.modelId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (modelIdLen > 4096000) { - throw ProtocolException.stringTooLong("ModelId", modelIdLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("SceneName", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int sceneNameLen = VarInt.peek(buf, varPos1); + if (sceneNameLen < 0) { + throw ProtocolException.invalidVarInt("SceneName"); + } + + int sceneNameVarIntLen = VarInt.size(sceneNameLen); + if (sceneNameLen > 4096000) { + throw ProtocolException.stringTooLong("SceneName", sceneNameLen, 4096000); + } + + if (varPos1 + sceneNameVarIntLen + sceneNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SceneName", varPos1 + sceneNameVarIntLen + sceneNameLen, buf.readableBytes()); + } + + obj.sceneName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.modelId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ActorName", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int actorNameLen = VarInt.peek(buf, varPos2); + if (actorNameLen < 0) { + throw ProtocolException.invalidVarInt("ActorName"); + } + + int actorNameVarIntLen = VarInt.size(actorNameLen); + if (actorNameLen > 4096000) { + throw ProtocolException.stringTooLong("ActorName", actorNameLen, 4096000); + } + + if (varPos2 + actorNameVarIntLen + actorNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ActorName", varPos2 + actorNameVarIntLen + actorNameLen, buf.readableBytes()); + } + + obj.actorName = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int sceneNameLen = VarInt.peek(buf, varPos1); - if (sceneNameLen < 0) { - throw ProtocolException.negativeLength("SceneName", sceneNameLen); - } - - if (sceneNameLen > 4096000) { - throw ProtocolException.stringTooLong("SceneName", sceneNameLen, 4096000); - } - - obj.sceneName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int actorNameLen = VarInt.peek(buf, varPos2); - if (actorNameLen < 0) { - throw ProtocolException.negativeLength("ActorName", actorNameLen); - } - - if (actorNameLen > 4096000) { - throw ProtocolException.stringTooLong("ActorName", actorNameLen, 4096000); - } - - obj.actorName = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -106,9 +140,13 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ModelId", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -116,9 +154,13 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("SceneName", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -126,9 +168,13 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ActorName", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -208,15 +254,11 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int modelIdOffset = buffer.getIntLE(offset + 1); - if (modelIdOffset < 0) { + if (modelIdOffset < 0 || modelIdOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for ModelId"); } int pos = offset + 13 + modelIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ModelId"); - } - int modelIdLen = VarInt.peek(buffer, pos); if (modelIdLen < 0) { return ValidationResult.error("Invalid string length for ModelId"); @@ -226,7 +268,7 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { return ValidationResult.error("ModelId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(modelIdLen); pos += modelIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ModelId"); @@ -235,15 +277,11 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { if ((nullBits & 2) != 0) { int sceneNameOffset = buffer.getIntLE(offset + 5); - if (sceneNameOffset < 0) { + if (sceneNameOffset < 0 || sceneNameOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for SceneName"); } int posx = offset + 13 + sceneNameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SceneName"); - } - int sceneNameLen = VarInt.peek(buffer, posx); if (sceneNameLen < 0) { return ValidationResult.error("Invalid string length for SceneName"); @@ -253,7 +291,7 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { return ValidationResult.error("SceneName exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(sceneNameLen); posx += sceneNameLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SceneName"); @@ -262,15 +300,11 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int actorNameOffset = buffer.getIntLE(offset + 9); - if (actorNameOffset < 0) { + if (actorNameOffset < 0 || actorNameOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for ActorName"); } int posxx = offset + 13 + actorNameOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ActorName"); - } - int actorNameLen = VarInt.peek(buffer, posxx); if (actorNameLen < 0) { return ValidationResult.error("Invalid string length for ActorName"); @@ -280,7 +314,7 @@ public class RequestMachinimaActorModel implements Packet, ToServerPacket { return ValidationResult.error("ActorName exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(actorNameLen); posxx += actorNameLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ActorName"); diff --git a/src/com/hypixel/hytale/protocol/packets/machinima/SetMachinimaActorModel.java b/src/com/hypixel/hytale/protocol/packets/machinima/SetMachinimaActorModel.java index 5528242c..3857e6dd 100644 --- a/src/com/hypixel/hytale/protocol/packets/machinima/SetMachinimaActorModel.java +++ b/src/com/hypixel/hytale/protocol/packets/machinima/SetMachinimaActorModel.java @@ -55,42 +55,71 @@ public class SetMachinimaActorModel implements Packet, ToClientPacket { @Nonnull public static SetMachinimaActorModel deserialize(@Nonnull ByteBuf buf, int offset) { - SetMachinimaActorModel obj = new SetMachinimaActorModel(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - obj.model = Model.deserialize(buf, varPos0); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("SetMachinimaActorModel", 13, buf.readableBytes() - offset); + } else { + SetMachinimaActorModel obj = new SetMachinimaActorModel(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Model", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 13 + varPosBase0; + obj.model = Model.deserialize(buf, varPos0); + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("SceneName", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int sceneNameLen = VarInt.peek(buf, varPos1); + if (sceneNameLen < 0) { + throw ProtocolException.invalidVarInt("SceneName"); + } + + int sceneNameVarIntLen = VarInt.size(sceneNameLen); + if (sceneNameLen > 4096000) { + throw ProtocolException.stringTooLong("SceneName", sceneNameLen, 4096000); + } + + if (varPos1 + sceneNameVarIntLen + sceneNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SceneName", varPos1 + sceneNameVarIntLen + sceneNameLen, buf.readableBytes()); + } + + obj.sceneName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ActorName", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int actorNameLen = VarInt.peek(buf, varPos2); + if (actorNameLen < 0) { + throw ProtocolException.invalidVarInt("ActorName"); + } + + int actorNameVarIntLen = VarInt.size(actorNameLen); + if (actorNameLen > 4096000) { + throw ProtocolException.stringTooLong("ActorName", actorNameLen, 4096000); + } + + if (varPos2 + actorNameVarIntLen + actorNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ActorName", varPos2 + actorNameVarIntLen + actorNameLen, buf.readableBytes()); + } + + obj.actorName = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int sceneNameLen = VarInt.peek(buf, varPos1); - if (sceneNameLen < 0) { - throw ProtocolException.negativeLength("SceneName", sceneNameLen); - } - - if (sceneNameLen > 4096000) { - throw ProtocolException.stringTooLong("SceneName", sceneNameLen, 4096000); - } - - obj.sceneName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int actorNameLen = VarInt.peek(buf, varPos2); - if (actorNameLen < 0) { - throw ProtocolException.negativeLength("ActorName", actorNameLen); - } - - if (actorNameLen > 4096000) { - throw ProtocolException.stringTooLong("ActorName", actorNameLen, 4096000); - } - - obj.actorName = PacketIO.readVarString(buf, varPos2, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -98,6 +127,10 @@ public class SetMachinimaActorModel implements Packet, ToClientPacket { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Model", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; pos0 += Model.computeBytesConsumed(buf, pos0); if (pos0 - offset > maxEnd) { @@ -107,9 +140,13 @@ public class SetMachinimaActorModel implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("SceneName", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -117,9 +154,13 @@ public class SetMachinimaActorModel implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("ActorName", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -199,15 +240,11 @@ public class SetMachinimaActorModel implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int modelOffset = buffer.getIntLE(offset + 1); - if (modelOffset < 0) { + if (modelOffset < 0 || modelOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Model"); } int pos = offset + 13 + modelOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Model"); - } - ValidationResult modelResult = Model.validateStructure(buffer, pos); if (!modelResult.isValid()) { return ValidationResult.error("Invalid Model: " + modelResult.error()); @@ -218,16 +255,12 @@ public class SetMachinimaActorModel implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int sceneNameOffset = buffer.getIntLE(offset + 5); - if (sceneNameOffset < 0) { + if (sceneNameOffset < 0 || sceneNameOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for SceneName"); } - int posx = offset + 13 + sceneNameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SceneName"); - } - - int sceneNameLen = VarInt.peek(buffer, posx); + int pos = offset + 13 + sceneNameOffset; + int sceneNameLen = VarInt.peek(buffer, pos); if (sceneNameLen < 0) { return ValidationResult.error("Invalid string length for SceneName"); } @@ -236,25 +269,21 @@ public class SetMachinimaActorModel implements Packet, ToClientPacket { return ValidationResult.error("SceneName exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); - posx += sceneNameLen; - if (posx > buffer.writerIndex()) { + pos += VarInt.size(sceneNameLen); + pos += sceneNameLen; + if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading SceneName"); } } if ((nullBits & 4) != 0) { int actorNameOffset = buffer.getIntLE(offset + 9); - if (actorNameOffset < 0) { + if (actorNameOffset < 0 || actorNameOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for ActorName"); } - int posxx = offset + 13 + actorNameOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ActorName"); - } - - int actorNameLen = VarInt.peek(buffer, posxx); + int posx = offset + 13 + actorNameOffset; + int actorNameLen = VarInt.peek(buffer, posx); if (actorNameLen < 0) { return ValidationResult.error("Invalid string length for ActorName"); } @@ -263,9 +292,9 @@ public class SetMachinimaActorModel implements Packet, ToClientPacket { return ValidationResult.error("ActorName exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); - posxx += actorNameLen; - if (posxx > buffer.writerIndex()) { + posx += VarInt.size(actorNameLen); + posx += actorNameLen; + if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ActorName"); } } diff --git a/src/com/hypixel/hytale/protocol/packets/machinima/UpdateMachinimaScene.java b/src/com/hypixel/hytale/protocol/packets/machinima/UpdateMachinimaScene.java index 1fc62db0..4df5925e 100644 --- a/src/com/hypixel/hytale/protocol/packets/machinima/UpdateMachinimaScene.java +++ b/src/com/hypixel/hytale/protocol/packets/machinima/UpdateMachinimaScene.java @@ -63,62 +63,91 @@ public class UpdateMachinimaScene implements Packet, ToServerPacket, ToClientPac @Nonnull public static UpdateMachinimaScene deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateMachinimaScene obj = new UpdateMachinimaScene(); - byte nullBits = buf.getByte(offset); - obj.frame = buf.getFloatLE(offset + 1); - obj.updateType = SceneUpdateType.fromValue(buf.getByte(offset + 5)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 18 + buf.getIntLE(offset + 6); - int playerLen = VarInt.peek(buf, varPos0); - if (playerLen < 0) { - throw ProtocolException.negativeLength("Player", playerLen); + if (buf.readableBytes() - offset < 18) { + throw ProtocolException.bufferTooSmall("UpdateMachinimaScene", 18, buf.readableBytes() - offset); + } else { + UpdateMachinimaScene obj = new UpdateMachinimaScene(); + byte nullBits = buf.getByte(offset); + obj.frame = buf.getFloatLE(offset + 1); + obj.updateType = SceneUpdateType.fromValue(buf.getByte(offset + 5)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Player", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 18 + varPosBase0; + int playerLen = VarInt.peek(buf, varPos0); + if (playerLen < 0) { + throw ProtocolException.invalidVarInt("Player"); + } + + int playerVarIntLen = VarInt.size(playerLen); + if (playerLen > 4096000) { + throw ProtocolException.stringTooLong("Player", playerLen, 4096000); + } + + if (varPos0 + playerVarIntLen + playerLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Player", varPos0 + playerVarIntLen + playerLen, buf.readableBytes()); + } + + obj.player = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (playerLen > 4096000) { - throw ProtocolException.stringTooLong("Player", playerLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 10); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("SceneName", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 18 + varPosBase1; + int sceneNameLen = VarInt.peek(buf, varPos1); + if (sceneNameLen < 0) { + throw ProtocolException.invalidVarInt("SceneName"); + } + + int sceneNameVarIntLen = VarInt.size(sceneNameLen); + if (sceneNameLen > 4096000) { + throw ProtocolException.stringTooLong("SceneName", sceneNameLen, 4096000); + } + + if (varPos1 + sceneNameVarIntLen + sceneNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("SceneName", varPos1 + sceneNameVarIntLen + sceneNameLen, buf.readableBytes()); + } + + obj.sceneName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.player = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 14); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Scene", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 18 + varPosBase2; + int sceneCount = VarInt.peek(buf, varPos2); + if (sceneCount < 0) { + throw ProtocolException.invalidVarInt("Scene"); + } + + int varIntLen = VarInt.size(sceneCount); + if (sceneCount > 4096000) { + throw ProtocolException.arrayTooLong("Scene", sceneCount, 4096000); + } + + if (varPos2 + varIntLen + sceneCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Scene", varPos2 + varIntLen + sceneCount * 1, buf.readableBytes()); + } + + obj.scene = new byte[sceneCount]; + + for (int i = 0; i < sceneCount; i++) { + obj.scene[i] = buf.getByte(varPos2 + varIntLen + i * 1); + } + } + + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 18 + buf.getIntLE(offset + 10); - int sceneNameLen = VarInt.peek(buf, varPos1); - if (sceneNameLen < 0) { - throw ProtocolException.negativeLength("SceneName", sceneNameLen); - } - - if (sceneNameLen > 4096000) { - throw ProtocolException.stringTooLong("SceneName", sceneNameLen, 4096000); - } - - obj.sceneName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 18 + buf.getIntLE(offset + 14); - int sceneCount = VarInt.peek(buf, varPos2); - if (sceneCount < 0) { - throw ProtocolException.negativeLength("Scene", sceneCount); - } - - if (sceneCount > 4096000) { - throw ProtocolException.arrayTooLong("Scene", sceneCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + sceneCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Scene", varPos2 + varIntLen + sceneCount * 1, buf.readableBytes()); - } - - obj.scene = new byte[sceneCount]; - - for (int i = 0; i < sceneCount; i++) { - obj.scene[i] = buf.getByte(varPos2 + varIntLen + i * 1); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -126,9 +155,13 @@ public class UpdateMachinimaScene implements Packet, ToServerPacket, ToClientPac int maxEnd = 18; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Player", fieldOffset0, maxEnd); + } + int pos0 = offset + 18 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -136,9 +169,13 @@ public class UpdateMachinimaScene implements Packet, ToServerPacket, ToClientPac if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 10); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("SceneName", fieldOffset1, maxEnd); + } + int pos1 = offset + 18 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -146,9 +183,13 @@ public class UpdateMachinimaScene implements Packet, ToServerPacket, ToClientPac if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 14); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Scene", fieldOffset2, maxEnd); + } + int pos2 = offset + 18 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + arrLen * 1; + pos2 += VarInt.size(arrLen) + arrLen * 1; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -236,88 +277,81 @@ public class UpdateMachinimaScene implements Packet, ToServerPacket, ToClientPac return ValidationResult.error("Buffer too small: expected at least 18 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int playerOffset = buffer.getIntLE(offset + 6); - if (playerOffset < 0) { - return ValidationResult.error("Invalid offset for Player"); + int v = buffer.getByte(offset + 5) & 255; + if (v >= 5) { + return ValidationResult.error("Invalid SceneUpdateType value for UpdateType"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Player"); + } + + int pos = offset + 18 + v; + int playerLen = VarInt.peek(buffer, pos); + if (playerLen < 0) { + return ValidationResult.error("Invalid string length for Player"); + } + + if (playerLen > 4096000) { + return ValidationResult.error("Player exceeds max length 4096000"); + } + + pos += VarInt.size(playerLen); + pos += playerLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Player"); + } } - int pos = offset + 18 + playerOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Player"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for SceneName"); + } + + int posx = offset + 18 + v; + int sceneNameLen = VarInt.peek(buffer, posx); + if (sceneNameLen < 0) { + return ValidationResult.error("Invalid string length for SceneName"); + } + + if (sceneNameLen > 4096000) { + return ValidationResult.error("SceneName exceeds max length 4096000"); + } + + posx += VarInt.size(sceneNameLen); + posx += sceneNameLen; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading SceneName"); + } } - int playerLen = VarInt.peek(buffer, pos); - if (playerLen < 0) { - return ValidationResult.error("Invalid string length for Player"); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 14); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Scene"); + } + + int posxx = offset + 18 + v; + int sceneCount = VarInt.peek(buffer, posxx); + if (sceneCount < 0) { + return ValidationResult.error("Invalid array count for Scene"); + } + + if (sceneCount > 4096000) { + return ValidationResult.error("Scene exceeds max length 4096000"); + } + + posxx += VarInt.size(sceneCount); + posxx += sceneCount * 1; + if (posxx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Scene"); + } } - if (playerLen > 4096000) { - return ValidationResult.error("Player exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += playerLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Player"); - } + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int sceneNameOffset = buffer.getIntLE(offset + 10); - if (sceneNameOffset < 0) { - return ValidationResult.error("Invalid offset for SceneName"); - } - - int posx = offset + 18 + sceneNameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for SceneName"); - } - - int sceneNameLen = VarInt.peek(buffer, posx); - if (sceneNameLen < 0) { - return ValidationResult.error("Invalid string length for SceneName"); - } - - if (sceneNameLen > 4096000) { - return ValidationResult.error("SceneName exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += sceneNameLen; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading SceneName"); - } - } - - if ((nullBits & 4) != 0) { - int sceneOffset = buffer.getIntLE(offset + 14); - if (sceneOffset < 0) { - return ValidationResult.error("Invalid offset for Scene"); - } - - int posxx = offset + 18 + sceneOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Scene"); - } - - int sceneCount = VarInt.peek(buffer, posxx); - if (sceneCount < 0) { - return ValidationResult.error("Invalid array count for Scene"); - } - - if (sceneCount > 4096000) { - return ValidationResult.error("Scene exceeds max length 4096000"); - } - - posxx += VarInt.length(buffer, posxx); - posxx += sceneCount * 1; - if (posxx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Scene"); - } - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/player/ClientMovement.java b/src/com/hypixel/hytale/protocol/packets/player/ClientMovement.java index d37ec277..904380aa 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/ClientMovement.java +++ b/src/com/hypixel/hytale/protocol/packets/player/ClientMovement.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.protocol.TeleportAck; import com.hypixel.hytale.protocol.ToServerPacket; import com.hypixel.hytale.protocol.Vector3d; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -20,10 +21,10 @@ public class ClientMovement implements Packet, ToServerPacket { public static final int PACKET_ID = 108; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 2; - public static final int FIXED_BLOCK_SIZE = 153; + public static final int FIXED_BLOCK_SIZE = 155; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 153; - public static final int MAX_SIZE = 153; + public static final int VARIABLE_BLOCK_START = 155; + public static final int MAX_SIZE = 155; @Nullable public MovementStates movementStates; @Nullable @@ -96,50 +97,54 @@ public class ClientMovement implements Packet, ToServerPacket { @Nonnull public static ClientMovement deserialize(@Nonnull ByteBuf buf, int offset) { - ClientMovement obj = new ClientMovement(); - byte[] nullBits = PacketIO.readBytes(buf, offset, 2); - if ((nullBits[0] & 1) != 0) { - obj.movementStates = MovementStates.deserialize(buf, offset + 2); - } + if (buf.readableBytes() - offset < 155) { + throw ProtocolException.bufferTooSmall("ClientMovement", 155, buf.readableBytes() - offset); + } else { + ClientMovement obj = new ClientMovement(); + byte[] nullBits = PacketIO.readBytes(buf, offset, 2); + if ((nullBits[0] & 1) != 0) { + obj.movementStates = MovementStates.deserialize(buf, offset + 2); + } - if ((nullBits[0] & 2) != 0) { - obj.relativePosition = HalfFloatPosition.deserialize(buf, offset + 24); - } + if ((nullBits[0] & 2) != 0) { + obj.relativePosition = HalfFloatPosition.deserialize(buf, offset + 25); + } - if ((nullBits[0] & 4) != 0) { - obj.absolutePosition = Position.deserialize(buf, offset + 30); - } + if ((nullBits[0] & 4) != 0) { + obj.absolutePosition = Position.deserialize(buf, offset + 31); + } - if ((nullBits[0] & 8) != 0) { - obj.bodyOrientation = Direction.deserialize(buf, offset + 54); - } + if ((nullBits[0] & 8) != 0) { + obj.bodyOrientation = Direction.deserialize(buf, offset + 55); + } - if ((nullBits[0] & 16) != 0) { - obj.lookOrientation = Direction.deserialize(buf, offset + 66); - } + if ((nullBits[0] & 16) != 0) { + obj.lookOrientation = Direction.deserialize(buf, offset + 67); + } - if ((nullBits[0] & 32) != 0) { - obj.teleportAck = TeleportAck.deserialize(buf, offset + 78); - } + if ((nullBits[0] & 32) != 0) { + obj.teleportAck = TeleportAck.deserialize(buf, offset + 79); + } - if ((nullBits[0] & 64) != 0) { - obj.wishMovement = Position.deserialize(buf, offset + 79); - } + if ((nullBits[0] & 64) != 0) { + obj.wishMovement = Position.deserialize(buf, offset + 80); + } - if ((nullBits[0] & 128) != 0) { - obj.velocity = Vector3d.deserialize(buf, offset + 103); - } + if ((nullBits[0] & 128) != 0) { + obj.velocity = Vector3d.deserialize(buf, offset + 104); + } - obj.mountedTo = buf.getIntLE(offset + 127); - if ((nullBits[1] & 1) != 0) { - obj.riderMovementStates = MovementStates.deserialize(buf, offset + 131); - } + obj.mountedTo = buf.getIntLE(offset + 128); + if ((nullBits[1] & 1) != 0) { + obj.riderMovementStates = MovementStates.deserialize(buf, offset + 132); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 153; + return 155; } @Override @@ -185,7 +190,7 @@ public class ClientMovement implements Packet, ToServerPacket { if (this.movementStates != null) { this.movementStates.serialize(buf); } else { - buf.writeZero(22); + buf.writeZero(23); } if (this.relativePosition != null) { @@ -234,17 +239,22 @@ public class ClientMovement implements Packet, ToServerPacket { if (this.riderMovementStates != null) { this.riderMovementStates.serialize(buf); } else { - buf.writeZero(22); + buf.writeZero(23); } } @Override public int computeSize() { - return 153; + return 155; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 153 ? ValidationResult.error("Buffer too small: expected at least 153 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 155) { + return ValidationResult.error("Buffer too small: expected at least 155 bytes"); + } else { + byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); + return ValidationResult.OK; + } } public ClientMovement clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/player/ClientPlaceBlock.java b/src/com/hypixel/hytale/protocol/packets/player/ClientPlaceBlock.java index b7ad0c92..f98d3bdd 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/ClientPlaceBlock.java +++ b/src/com/hypixel/hytale/protocol/packets/player/ClientPlaceBlock.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.BlockRotation; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -15,15 +16,16 @@ public class ClientPlaceBlock implements Packet, ToServerPacket { public static final int PACKET_ID = 117; public static final boolean IS_COMPRESSED = false; public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 20; + public static final int FIXED_BLOCK_SIZE = 21; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 20; - public static final int MAX_SIZE = 20; + public static final int VARIABLE_BLOCK_START = 21; + public static final int MAX_SIZE = 21; @Nullable public BlockPosition position; @Nullable public BlockRotation rotation; public int placedBlockId; + public boolean quickReplace; @Override public int getId() { @@ -38,36 +40,43 @@ public class ClientPlaceBlock implements Packet, ToServerPacket { public ClientPlaceBlock() { } - public ClientPlaceBlock(@Nullable BlockPosition position, @Nullable BlockRotation rotation, int placedBlockId) { + public ClientPlaceBlock(@Nullable BlockPosition position, @Nullable BlockRotation rotation, int placedBlockId, boolean quickReplace) { this.position = position; this.rotation = rotation; this.placedBlockId = placedBlockId; + this.quickReplace = quickReplace; } public ClientPlaceBlock(@Nonnull ClientPlaceBlock other) { this.position = other.position; this.rotation = other.rotation; this.placedBlockId = other.placedBlockId; + this.quickReplace = other.quickReplace; } @Nonnull public static ClientPlaceBlock deserialize(@Nonnull ByteBuf buf, int offset) { - ClientPlaceBlock obj = new ClientPlaceBlock(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.position = BlockPosition.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("ClientPlaceBlock", 21, buf.readableBytes() - offset); + } else { + ClientPlaceBlock obj = new ClientPlaceBlock(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.position = BlockPosition.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.rotation = BlockRotation.deserialize(buf, offset + 13); - } + if ((nullBits & 2) != 0) { + obj.rotation = BlockRotation.deserialize(buf, offset + 13); + } - obj.placedBlockId = buf.getIntLE(offset + 16); - return obj; + obj.placedBlockId = buf.getIntLE(offset + 16); + obj.quickReplace = buf.getByte(offset + 20) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 20; + return 21; } @Override @@ -95,15 +104,21 @@ public class ClientPlaceBlock implements Packet, ToServerPacket { } buf.writeIntLE(this.placedBlockId); + buf.writeByte(this.quickReplace ? 1 : 0); } @Override public int computeSize() { - return 20; + return 21; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 20 ? ValidationResult.error("Buffer too small: expected at least 20 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 21) { + return ValidationResult.error("Buffer too small: expected at least 21 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public ClientPlaceBlock clone() { @@ -111,6 +126,7 @@ public class ClientPlaceBlock implements Packet, ToServerPacket { copy.position = this.position != null ? this.position.clone() : null; copy.rotation = this.rotation != null ? this.rotation.clone() : null; copy.placedBlockId = this.placedBlockId; + copy.quickReplace = this.quickReplace; return copy; } @@ -121,12 +137,15 @@ public class ClientPlaceBlock implements Packet, ToServerPacket { } else { return !(obj instanceof ClientPlaceBlock other) ? false - : Objects.equals(this.position, other.position) && Objects.equals(this.rotation, other.rotation) && this.placedBlockId == other.placedBlockId; + : Objects.equals(this.position, other.position) + && Objects.equals(this.rotation, other.rotation) + && this.placedBlockId == other.placedBlockId + && this.quickReplace == other.quickReplace; } } @Override public int hashCode() { - return Objects.hash(this.position, this.rotation, this.placedBlockId); + return Objects.hash(this.position, this.rotation, this.placedBlockId, this.quickReplace); } } diff --git a/src/com/hypixel/hytale/protocol/packets/player/ClientReady.java b/src/com/hypixel/hytale/protocol/packets/player/ClientReady.java index e500b3c7..74da3591 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/ClientReady.java +++ b/src/com/hypixel/hytale/protocol/packets/player/ClientReady.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.player; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class ClientReady implements Packet, ToServerPacket { @Nonnull public static ClientReady deserialize(@Nonnull ByteBuf buf, int offset) { - ClientReady obj = new ClientReady(); - obj.readyForChunks = buf.getByte(offset + 0) != 0; - obj.readyForGameplay = buf.getByte(offset + 1) != 0; - return obj; + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("ClientReady", 2, buf.readableBytes() - offset); + } else { + ClientReady obj = new ClientReady(); + obj.readyForChunks = buf.getByte(offset + 0) != 0; + obj.readyForGameplay = buf.getByte(offset + 1) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/ClientTeleport.java b/src/com/hypixel/hytale/protocol/packets/player/ClientTeleport.java index 665c5bc5..b8ec8bb3 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/ClientTeleport.java +++ b/src/com/hypixel/hytale/protocol/packets/player/ClientTeleport.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.ModelTransform; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,15 +51,19 @@ public class ClientTeleport implements Packet, ToClientPacket { @Nonnull public static ClientTeleport deserialize(@Nonnull ByteBuf buf, int offset) { - ClientTeleport obj = new ClientTeleport(); - byte nullBits = buf.getByte(offset); - obj.teleportId = buf.getByte(offset + 1); - if ((nullBits & 1) != 0) { - obj.modelTransform = ModelTransform.deserialize(buf, offset + 2); - } + if (buf.readableBytes() - offset < 52) { + throw ProtocolException.bufferTooSmall("ClientTeleport", 52, buf.readableBytes() - offset); + } else { + ClientTeleport obj = new ClientTeleport(); + byte nullBits = buf.getByte(offset); + obj.teleportId = buf.getByte(offset + 1); + if ((nullBits & 1) != 0) { + obj.modelTransform = ModelTransform.deserialize(buf, offset + 2); + } - obj.resetVelocity = buf.getByte(offset + 51) != 0; - return obj; + obj.resetVelocity = buf.getByte(offset + 51) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -89,7 +94,12 @@ public class ClientTeleport implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 52 ? ValidationResult.error("Buffer too small: expected at least 52 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 52) { + return ValidationResult.error("Buffer too small: expected at least 52 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public ClientTeleport clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/player/DamageInfo.java b/src/com/hypixel/hytale/protocol/packets/player/DamageInfo.java index 4871198e..3299ec5e 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/DamageInfo.java +++ b/src/com/hypixel/hytale/protocol/packets/player/DamageInfo.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.Vector3d; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -52,20 +53,24 @@ public class DamageInfo implements Packet, ToClientPacket { @Nonnull public static DamageInfo deserialize(@Nonnull ByteBuf buf, int offset) { - DamageInfo obj = new DamageInfo(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.damageSourcePosition = Vector3d.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 29) { + throw ProtocolException.bufferTooSmall("DamageInfo", 29, buf.readableBytes() - offset); + } else { + DamageInfo obj = new DamageInfo(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.damageSourcePosition = Vector3d.deserialize(buf, offset + 1); + } - obj.damageAmount = buf.getFloatLE(offset + 25); - int pos = offset + 29; - if ((nullBits & 2) != 0) { - obj.damageCause = DamageCause.deserialize(buf, pos); - pos += DamageCause.computeBytesConsumed(buf, pos); - } + obj.damageAmount = buf.getFloatLE(offset + 25); + int pos = offset + 29; + if ((nullBits & 2) != 0) { + obj.damageCause = DamageCause.deserialize(buf, pos); + pos += DamageCause.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/DisplayDebug.java b/src/com/hypixel/hytale/protocol/packets/player/DisplayDebug.java index a965a819..9b51db07 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/DisplayDebug.java +++ b/src/com/hypixel/hytale/protocol/packets/player/DisplayDebug.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.protocol.DebugShape; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; -import com.hypixel.hytale.protocol.Vector3f; +import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.VarInt; @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; public class DisplayDebug implements Packet, ToClientPacket { public static final int PACKET_ID = 114; @@ -26,10 +27,10 @@ public class DisplayDebug implements Packet, ToClientPacket { public DebugShape shape = DebugShape.Sphere; @Nullable public float[] matrix; - @Nullable - public Vector3f color; + @Nonnull + public Vector3fc color = PacketIO.ZERO_VECTOR3; public float time; - public boolean fade; + public byte flags; @Nullable public float[] frustumProjection; public float opacity; @@ -48,19 +49,13 @@ public class DisplayDebug implements Packet, ToClientPacket { } public DisplayDebug( - @Nonnull DebugShape shape, - @Nullable float[] matrix, - @Nullable Vector3f color, - float time, - boolean fade, - @Nullable float[] frustumProjection, - float opacity + @Nonnull DebugShape shape, @Nullable float[] matrix, @Nonnull Vector3fc color, float time, byte flags, @Nullable float[] frustumProjection, float opacity ) { this.shape = shape; this.matrix = matrix; this.color = color; this.time = time; - this.fade = fade; + this.flags = flags; this.frustumProjection = frustumProjection; this.opacity = opacity; } @@ -70,90 +65,109 @@ public class DisplayDebug implements Packet, ToClientPacket { this.matrix = other.matrix; this.color = other.color; this.time = other.time; - this.fade = other.fade; + this.flags = other.flags; this.frustumProjection = other.frustumProjection; this.opacity = other.opacity; } @Nonnull public static DisplayDebug deserialize(@Nonnull ByteBuf buf, int offset) { - DisplayDebug obj = new DisplayDebug(); - byte nullBits = buf.getByte(offset); - obj.shape = DebugShape.fromValue(buf.getByte(offset + 1)); - if ((nullBits & 1) != 0) { - obj.color = Vector3f.deserialize(buf, offset + 2); + if (buf.readableBytes() - offset < 31) { + throw ProtocolException.bufferTooSmall("DisplayDebug", 31, buf.readableBytes() - offset); + } else { + DisplayDebug obj = new DisplayDebug(); + byte nullBits = buf.getByte(offset); + obj.shape = DebugShape.fromValue(buf.getByte(offset + 1)); + obj.color = PacketIO.readVector3f(buf, offset + 2); + obj.time = buf.getFloatLE(offset + 14); + obj.flags = buf.getByte(offset + 18); + obj.opacity = buf.getFloatLE(offset + 19); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 23); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 31) { + throw ProtocolException.invalidOffset("Matrix", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 31 + varPosBase0; + int matrixCount = VarInt.peek(buf, varPos0); + if (matrixCount < 0) { + throw ProtocolException.invalidVarInt("Matrix"); + } + + int varIntLen = VarInt.size(matrixCount); + if (matrixCount > 4096000) { + throw ProtocolException.arrayTooLong("Matrix", matrixCount, 4096000); + } + + if (varPos0 + varIntLen + matrixCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Matrix", varPos0 + varIntLen + matrixCount * 4, buf.readableBytes()); + } + + obj.matrix = new float[matrixCount]; + + for (int i = 0; i < matrixCount; i++) { + obj.matrix[i] = buf.getFloatLE(varPos0 + varIntLen + i * 4); + } + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 27); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 31) { + throw ProtocolException.invalidOffset("FrustumProjection", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 31 + varPosBase1; + int frustumProjectionCount = VarInt.peek(buf, varPos1); + if (frustumProjectionCount < 0) { + throw ProtocolException.invalidVarInt("FrustumProjection"); + } + + int varIntLenx = VarInt.size(frustumProjectionCount); + if (frustumProjectionCount > 4096000) { + throw ProtocolException.arrayTooLong("FrustumProjection", frustumProjectionCount, 4096000); + } + + if (varPos1 + varIntLenx + frustumProjectionCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("FrustumProjection", varPos1 + varIntLenx + frustumProjectionCount * 4, buf.readableBytes()); + } + + obj.frustumProjection = new float[frustumProjectionCount]; + + for (int i = 0; i < frustumProjectionCount; i++) { + obj.frustumProjection[i] = buf.getFloatLE(varPos1 + varIntLenx + i * 4); + } + } + + return obj; } - - obj.time = buf.getFloatLE(offset + 14); - obj.fade = buf.getByte(offset + 18) != 0; - obj.opacity = buf.getFloatLE(offset + 19); - if ((nullBits & 2) != 0) { - int varPos0 = offset + 31 + buf.getIntLE(offset + 23); - int matrixCount = VarInt.peek(buf, varPos0); - if (matrixCount < 0) { - throw ProtocolException.negativeLength("Matrix", matrixCount); - } - - if (matrixCount > 4096000) { - throw ProtocolException.arrayTooLong("Matrix", matrixCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + matrixCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Matrix", varPos0 + varIntLen + matrixCount * 4, buf.readableBytes()); - } - - obj.matrix = new float[matrixCount]; - - for (int i = 0; i < matrixCount; i++) { - obj.matrix[i] = buf.getFloatLE(varPos0 + varIntLen + i * 4); - } - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 31 + buf.getIntLE(offset + 27); - int frustumProjectionCount = VarInt.peek(buf, varPos1); - if (frustumProjectionCount < 0) { - throw ProtocolException.negativeLength("FrustumProjection", frustumProjectionCount); - } - - if (frustumProjectionCount > 4096000) { - throw ProtocolException.arrayTooLong("FrustumProjection", frustumProjectionCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + frustumProjectionCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("FrustumProjection", varPos1 + varIntLen + frustumProjectionCount * 4, buf.readableBytes()); - } - - obj.frustumProjection = new float[frustumProjectionCount]; - - for (int i = 0; i < frustumProjectionCount; i++) { - obj.frustumProjection[i] = buf.getFloatLE(varPos1 + varIntLen + i * 4); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); int maxEnd = 31; - if ((nullBits & 2) != 0) { + if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 23); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 31) { + throw ProtocolException.invalidOffset("Matrix", fieldOffset0, maxEnd); + } + int pos0 = offset + 31 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 4; + pos0 += VarInt.size(arrLen) + arrLen * 4; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } } - if ((nullBits & 4) != 0) { + if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 27); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 31) { + throw ProtocolException.invalidOffset("FrustumProjection", fieldOffset1, maxEnd); + } + int pos1 = offset + 31 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 4; + pos1 += VarInt.size(arrLen) + arrLen * 4; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -166,28 +180,19 @@ public class DisplayDebug implements Packet, ToClientPacket { public void serialize(@Nonnull ByteBuf buf) { int startPos = buf.writerIndex(); byte nullBits = 0; - if (this.color != null) { + if (this.matrix != null) { nullBits = (byte)(nullBits | 1); } - if (this.matrix != null) { - nullBits = (byte)(nullBits | 2); - } - if (this.frustumProjection != null) { - nullBits = (byte)(nullBits | 4); + nullBits = (byte)(nullBits | 2); } buf.writeByte(nullBits); buf.writeByte(this.shape.getValue()); - if (this.color != null) { - this.color.serialize(buf); - } else { - buf.writeZero(12); - } - + PacketIO.writeVector3f(buf, this.color); buf.writeFloatLE(this.time); - buf.writeByte(this.fade ? 1 : 0); + buf.writeByte(this.flags); buf.writeFloatLE(this.opacity); int matrixOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); @@ -244,61 +249,58 @@ public class DisplayDebug implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 31 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 2) != 0) { - int matrixOffset = buffer.getIntLE(offset + 23); - if (matrixOffset < 0) { - return ValidationResult.error("Invalid offset for Matrix"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 7) { + return ValidationResult.error("Invalid DebugShape value for Shape"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 23); + if (v < 0 || v > buffer.writerIndex() - offset - 31) { + return ValidationResult.error("Invalid offset for Matrix"); + } + + int pos = offset + 31 + v; + int matrixCount = VarInt.peek(buffer, pos); + if (matrixCount < 0) { + return ValidationResult.error("Invalid array count for Matrix"); + } + + if (matrixCount > 4096000) { + return ValidationResult.error("Matrix exceeds max length 4096000"); + } + + pos += VarInt.size(matrixCount); + pos += matrixCount * 4; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Matrix"); + } } - int pos = offset + 31 + matrixOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Matrix"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 27); + if (v < 0 || v > buffer.writerIndex() - offset - 31) { + return ValidationResult.error("Invalid offset for FrustumProjection"); + } + + int posx = offset + 31 + v; + int frustumProjectionCount = VarInt.peek(buffer, posx); + if (frustumProjectionCount < 0) { + return ValidationResult.error("Invalid array count for FrustumProjection"); + } + + if (frustumProjectionCount > 4096000) { + return ValidationResult.error("FrustumProjection exceeds max length 4096000"); + } + + posx += VarInt.size(frustumProjectionCount); + posx += frustumProjectionCount * 4; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading FrustumProjection"); + } } - int matrixCount = VarInt.peek(buffer, pos); - if (matrixCount < 0) { - return ValidationResult.error("Invalid array count for Matrix"); - } - - if (matrixCount > 4096000) { - return ValidationResult.error("Matrix exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += matrixCount * 4; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Matrix"); - } + return ValidationResult.OK; } - - if ((nullBits & 4) != 0) { - int frustumProjectionOffset = buffer.getIntLE(offset + 27); - if (frustumProjectionOffset < 0) { - return ValidationResult.error("Invalid offset for FrustumProjection"); - } - - int posx = offset + 31 + frustumProjectionOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for FrustumProjection"); - } - - int frustumProjectionCount = VarInt.peek(buffer, posx); - if (frustumProjectionCount < 0) { - return ValidationResult.error("Invalid array count for FrustumProjection"); - } - - if (frustumProjectionCount > 4096000) { - return ValidationResult.error("FrustumProjection exceeds max length 4096000"); - } - - posx += VarInt.length(buffer, posx); - posx += frustumProjectionCount * 4; - if (posx > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading FrustumProjection"); - } - } - - return ValidationResult.OK; } } @@ -306,9 +308,9 @@ public class DisplayDebug implements Packet, ToClientPacket { DisplayDebug copy = new DisplayDebug(); copy.shape = this.shape; copy.matrix = this.matrix != null ? Arrays.copyOf(this.matrix, this.matrix.length) : null; - copy.color = this.color != null ? this.color.clone() : null; + copy.color = this.color; copy.time = this.time; - copy.fade = this.fade; + copy.flags = this.flags; copy.frustumProjection = this.frustumProjection != null ? Arrays.copyOf(this.frustumProjection, this.frustumProjection.length) : null; copy.opacity = this.opacity; return copy; @@ -325,7 +327,7 @@ public class DisplayDebug implements Packet, ToClientPacket { && Arrays.equals(this.matrix, other.matrix) && Objects.equals(this.color, other.color) && this.time == other.time - && this.fade == other.fade + && this.flags == other.flags && Arrays.equals(this.frustumProjection, other.frustumProjection) && this.opacity == other.opacity; } @@ -338,7 +340,7 @@ public class DisplayDebug implements Packet, ToClientPacket { result = 31 * result + Arrays.hashCode(this.matrix); result = 31 * result + Objects.hashCode(this.color); result = 31 * result + Float.hashCode(this.time); - result = 31 * result + Boolean.hashCode(this.fade); + result = 31 * result + Byte.hashCode(this.flags); result = 31 * result + Arrays.hashCode(this.frustumProjection); return 31 * result + Float.hashCode(this.opacity); } diff --git a/src/com/hypixel/hytale/protocol/packets/player/JoinWorld.java b/src/com/hypixel/hytale/protocol/packets/player/JoinWorld.java index 6ffd9f97..acc37c96 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/JoinWorld.java +++ b/src/com/hypixel/hytale/protocol/packets/player/JoinWorld.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,11 +51,15 @@ public class JoinWorld implements Packet, ToClientPacket { @Nonnull public static JoinWorld deserialize(@Nonnull ByteBuf buf, int offset) { - JoinWorld obj = new JoinWorld(); - obj.clearWorld = buf.getByte(offset + 0) != 0; - obj.fadeInOut = buf.getByte(offset + 1) != 0; - obj.worldUuid = PacketIO.readUUID(buf, offset + 2); - return obj; + if (buf.readableBytes() - offset < 18) { + throw ProtocolException.bufferTooSmall("JoinWorld", 18, buf.readableBytes() - offset); + } else { + JoinWorld obj = new JoinWorld(); + obj.clearWorld = buf.getByte(offset + 0) != 0; + obj.fadeInOut = buf.getByte(offset + 1) != 0; + obj.worldUuid = PacketIO.readUUID(buf, offset + 2); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/LoadHotbar.java b/src/com/hypixel/hytale/protocol/packets/player/LoadHotbar.java index e9f1fa10..2d05ccf5 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/LoadHotbar.java +++ b/src/com/hypixel/hytale/protocol/packets/player/LoadHotbar.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.player; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class LoadHotbar implements Packet, ToServerPacket { @Nonnull public static LoadHotbar deserialize(@Nonnull ByteBuf buf, int offset) { - LoadHotbar obj = new LoadHotbar(); - obj.inventoryRow = buf.getByte(offset + 0); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("LoadHotbar", 1, buf.readableBytes() - offset); + } else { + LoadHotbar obj = new LoadHotbar(); + obj.inventoryRow = buf.getByte(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/MouseInteraction.java b/src/com/hypixel/hytale/protocol/packets/player/MouseInteraction.java index d77b5deb..40f76904 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/MouseInteraction.java +++ b/src/com/hypixel/hytale/protocol/packets/player/MouseInteraction.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.protocol.MouseMotionEvent; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; -import com.hypixel.hytale.protocol.Vector2f; import com.hypixel.hytale.protocol.WorldInteraction; import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.ProtocolException; @@ -15,6 +14,7 @@ import io.netty.buffer.ByteBuf; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2fc; public class MouseInteraction implements Packet, ToServerPacket { public static final int PACKET_ID = 111; @@ -28,8 +28,8 @@ public class MouseInteraction implements Packet, ToServerPacket { public int activeSlot; @Nullable public String itemInHandId; - @Nullable - public Vector2f screenPoint; + @Nonnull + public Vector2fc screenPoint = PacketIO.ZERO_VECTOR2; @Nullable public MouseButtonEvent mouseButton; @Nullable @@ -54,7 +54,7 @@ public class MouseInteraction implements Packet, ToServerPacket { long clientTimestamp, int activeSlot, @Nullable String itemInHandId, - @Nullable Vector2f screenPoint, + @Nonnull Vector2fc screenPoint, @Nullable MouseButtonEvent mouseButton, @Nullable MouseMotionEvent mouseMotion, @Nullable WorldInteraction worldInteraction @@ -80,59 +80,83 @@ public class MouseInteraction implements Packet, ToServerPacket { @Nonnull public static MouseInteraction deserialize(@Nonnull ByteBuf buf, int offset) { - MouseInteraction obj = new MouseInteraction(); - byte nullBits = buf.getByte(offset); - obj.clientTimestamp = buf.getLongLE(offset + 1); - obj.activeSlot = buf.getIntLE(offset + 9); - if ((nullBits & 1) != 0) { - obj.screenPoint = Vector2f.deserialize(buf, offset + 13); - } - - if ((nullBits & 2) != 0) { - obj.mouseButton = MouseButtonEvent.deserialize(buf, offset + 21); - } - - if ((nullBits & 4) != 0) { - obj.worldInteraction = WorldInteraction.deserialize(buf, offset + 24); - } - - if ((nullBits & 8) != 0) { - int varPos0 = offset + 52 + buf.getIntLE(offset + 44); - int itemInHandIdLen = VarInt.peek(buf, varPos0); - if (itemInHandIdLen < 0) { - throw ProtocolException.negativeLength("ItemInHandId", itemInHandIdLen); + if (buf.readableBytes() - offset < 52) { + throw ProtocolException.bufferTooSmall("MouseInteraction", 52, buf.readableBytes() - offset); + } else { + MouseInteraction obj = new MouseInteraction(); + byte nullBits = buf.getByte(offset); + obj.clientTimestamp = buf.getLongLE(offset + 1); + obj.activeSlot = buf.getIntLE(offset + 9); + obj.screenPoint = PacketIO.readVector2f(buf, offset + 13); + if ((nullBits & 1) != 0) { + obj.mouseButton = MouseButtonEvent.deserialize(buf, offset + 21); } - if (itemInHandIdLen > 4096000) { - throw ProtocolException.stringTooLong("ItemInHandId", itemInHandIdLen, 4096000); + if ((nullBits & 2) != 0) { + obj.worldInteraction = WorldInteraction.deserialize(buf, offset + 24); } - obj.itemInHandId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase0 = buf.getIntLE(offset + 44); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("ItemInHandId", varPosBase0, buf.readableBytes()); + } - if ((nullBits & 16) != 0) { - int varPos1 = offset + 52 + buf.getIntLE(offset + 48); - obj.mouseMotion = MouseMotionEvent.deserialize(buf, varPos1); - } + int varPos0 = offset + 52 + varPosBase0; + int itemInHandIdLen = VarInt.peek(buf, varPos0); + if (itemInHandIdLen < 0) { + throw ProtocolException.invalidVarInt("ItemInHandId"); + } - return obj; + int itemInHandIdVarIntLen = VarInt.size(itemInHandIdLen); + if (itemInHandIdLen > 4096000) { + throw ProtocolException.stringTooLong("ItemInHandId", itemInHandIdLen, 4096000); + } + + if (varPos0 + itemInHandIdVarIntLen + itemInHandIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemInHandId", varPos0 + itemInHandIdVarIntLen + itemInHandIdLen, buf.readableBytes()); + } + + obj.itemInHandId = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 8) != 0) { + int varPosBase1 = buf.getIntLE(offset + 48); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("MouseMotion", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 52 + varPosBase1; + obj.mouseMotion = MouseMotionEvent.deserialize(buf, varPos1); + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); int maxEnd = 52; - if ((nullBits & 8) != 0) { + if ((nullBits & 4) != 0) { int fieldOffset0 = buf.getIntLE(offset + 44); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("ItemInHandId", fieldOffset0, maxEnd); + } + int pos0 = offset + 52 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } } - if ((nullBits & 16) != 0) { + if ((nullBits & 8) != 0) { int fieldOffset1 = buf.getIntLE(offset + 48); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 52) { + throw ProtocolException.invalidOffset("MouseMotion", fieldOffset1, maxEnd); + } + int pos1 = offset + 52 + fieldOffset1; pos1 += MouseMotionEvent.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -147,35 +171,26 @@ public class MouseInteraction implements Packet, ToServerPacket { public void serialize(@Nonnull ByteBuf buf) { int startPos = buf.writerIndex(); byte nullBits = 0; - if (this.screenPoint != null) { + if (this.mouseButton != null) { nullBits = (byte)(nullBits | 1); } - if (this.mouseButton != null) { + if (this.worldInteraction != null) { nullBits = (byte)(nullBits | 2); } - if (this.worldInteraction != null) { + if (this.itemInHandId != null) { nullBits = (byte)(nullBits | 4); } - if (this.itemInHandId != null) { - nullBits = (byte)(nullBits | 8); - } - if (this.mouseMotion != null) { - nullBits = (byte)(nullBits | 16); + nullBits = (byte)(nullBits | 8); } buf.writeByte(nullBits); buf.writeLongLE(this.clientTimestamp); buf.writeIntLE(this.activeSlot); - if (this.screenPoint != null) { - this.screenPoint.serialize(buf); - } else { - buf.writeZero(8); - } - + PacketIO.writeVector2f(buf, this.screenPoint); if (this.mouseButton != null) { this.mouseButton.serialize(buf); } else { @@ -227,17 +242,13 @@ public class MouseInteraction implements Packet, ToServerPacket { return ValidationResult.error("Buffer too small: expected at least 52 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 8) != 0) { + if ((nullBits & 4) != 0) { int itemInHandIdOffset = buffer.getIntLE(offset + 44); - if (itemInHandIdOffset < 0) { + if (itemInHandIdOffset < 0 || itemInHandIdOffset > buffer.writerIndex() - offset - 52) { return ValidationResult.error("Invalid offset for ItemInHandId"); } int pos = offset + 52 + itemInHandIdOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemInHandId"); - } - int itemInHandIdLen = VarInt.peek(buffer, pos); if (itemInHandIdLen < 0) { return ValidationResult.error("Invalid string length for ItemInHandId"); @@ -247,24 +258,20 @@ public class MouseInteraction implements Packet, ToServerPacket { return ValidationResult.error("ItemInHandId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(itemInHandIdLen); pos += itemInHandIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ItemInHandId"); } } - if ((nullBits & 16) != 0) { + if ((nullBits & 8) != 0) { int mouseMotionOffset = buffer.getIntLE(offset + 48); - if (mouseMotionOffset < 0) { + if (mouseMotionOffset < 0 || mouseMotionOffset > buffer.writerIndex() - offset - 52) { return ValidationResult.error("Invalid offset for MouseMotion"); } int posx = offset + 52 + mouseMotionOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MouseMotion"); - } - ValidationResult mouseMotionResult = MouseMotionEvent.validateStructure(buffer, posx); if (!mouseMotionResult.isValid()) { return ValidationResult.error("Invalid MouseMotion: " + mouseMotionResult.error()); @@ -282,7 +289,7 @@ public class MouseInteraction implements Packet, ToServerPacket { copy.clientTimestamp = this.clientTimestamp; copy.activeSlot = this.activeSlot; copy.itemInHandId = this.itemInHandId; - copy.screenPoint = this.screenPoint != null ? this.screenPoint.clone() : null; + copy.screenPoint = this.screenPoint; copy.mouseButton = this.mouseButton != null ? this.mouseButton.clone() : null; copy.mouseMotion = this.mouseMotion != null ? this.mouseMotion.clone() : null; copy.worldInteraction = this.worldInteraction != null ? this.worldInteraction.clone() : null; diff --git a/src/com/hypixel/hytale/protocol/packets/player/RemoveMapMarker.java b/src/com/hypixel/hytale/protocol/packets/player/RemoveMapMarker.java index bb02fc0e..8ef1e8ce 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/RemoveMapMarker.java +++ b/src/com/hypixel/hytale/protocol/packets/player/RemoveMapMarker.java @@ -46,25 +46,33 @@ public class RemoveMapMarker implements Packet, ToServerPacket { @Nonnull public static RemoveMapMarker deserialize(@Nonnull ByteBuf buf, int offset) { - RemoveMapMarker obj = new RemoveMapMarker(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int markerIdLen = VarInt.peek(buf, pos); - if (markerIdLen < 0) { - throw ProtocolException.negativeLength("MarkerId", markerIdLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("RemoveMapMarker", 1, buf.readableBytes() - offset); + } else { + RemoveMapMarker obj = new RemoveMapMarker(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int markerIdLen = VarInt.peek(buf, pos); + if (markerIdLen < 0) { + throw ProtocolException.invalidVarInt("MarkerId"); + } + + int markerIdVarLen = VarInt.size(markerIdLen); + if (markerIdLen > 4096000) { + throw ProtocolException.stringTooLong("MarkerId", markerIdLen, 4096000); + } + + if (pos + markerIdVarLen + markerIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("MarkerId", pos + markerIdVarLen + markerIdLen, buf.readableBytes()); + } + + obj.markerId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += markerIdVarLen + markerIdLen; } - if (markerIdLen > 4096000) { - throw ProtocolException.stringTooLong("MarkerId", markerIdLen, 4096000); - } - - int markerIdVarLen = VarInt.length(buf, pos); - obj.markerId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += markerIdVarLen + markerIdLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +80,7 @@ public class RemoveMapMarker implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -117,7 +125,7 @@ public class RemoveMapMarker implements Packet, ToServerPacket { return ValidationResult.error("MarkerId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(markerIdLen); pos += markerIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading MarkerId"); diff --git a/src/com/hypixel/hytale/protocol/packets/player/ReticleEvent.java b/src/com/hypixel/hytale/protocol/packets/player/ReticleEvent.java index e35e4d10..a11d142e 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/ReticleEvent.java +++ b/src/com/hypixel/hytale/protocol/packets/player/ReticleEvent.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.player; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class ReticleEvent implements Packet, ToClientPacket { @Nonnull public static ReticleEvent deserialize(@Nonnull ByteBuf buf, int offset) { - ReticleEvent obj = new ReticleEvent(); - obj.eventIndex = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("ReticleEvent", 4, buf.readableBytes() - offset); + } else { + ReticleEvent obj = new ReticleEvent(); + obj.eventIndex = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/SaveHotbar.java b/src/com/hypixel/hytale/protocol/packets/player/SaveHotbar.java index 696ca2e7..4a0028cf 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/SaveHotbar.java +++ b/src/com/hypixel/hytale/protocol/packets/player/SaveHotbar.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.player; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class SaveHotbar implements Packet, ToServerPacket { @Nonnull public static SaveHotbar deserialize(@Nonnull ByteBuf buf, int offset) { - SaveHotbar obj = new SaveHotbar(); - obj.inventoryRow = buf.getByte(offset + 0); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SaveHotbar", 1, buf.readableBytes() - offset); + } else { + SaveHotbar obj = new SaveHotbar(); + obj.inventoryRow = buf.getByte(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/SetBlockPlacementOverride.java b/src/com/hypixel/hytale/protocol/packets/player/SetBlockPlacementOverride.java index 051a7713..9ae63d60 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/SetBlockPlacementOverride.java +++ b/src/com/hypixel/hytale/protocol/packets/player/SetBlockPlacementOverride.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.player; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class SetBlockPlacementOverride implements Packet, ToClientPacket { @Nonnull public static SetBlockPlacementOverride deserialize(@Nonnull ByteBuf buf, int offset) { - SetBlockPlacementOverride obj = new SetBlockPlacementOverride(); - obj.enabled = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SetBlockPlacementOverride", 1, buf.readableBytes() - offset); + } else { + SetBlockPlacementOverride obj = new SetBlockPlacementOverride(); + obj.enabled = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/SetClientId.java b/src/com/hypixel/hytale/protocol/packets/player/SetClientId.java index 444268f1..41a8f1ec 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/SetClientId.java +++ b/src/com/hypixel/hytale/protocol/packets/player/SetClientId.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.player; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class SetClientId implements Packet, ToClientPacket { @Nonnull public static SetClientId deserialize(@Nonnull ByteBuf buf, int offset) { - SetClientId obj = new SetClientId(); - obj.clientId = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("SetClientId", 4, buf.readableBytes() - offset); + } else { + SetClientId obj = new SetClientId(); + obj.clientId = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/SetGameMode.java b/src/com/hypixel/hytale/protocol/packets/player/SetGameMode.java index 6c23fd98..d05f495e 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/SetGameMode.java +++ b/src/com/hypixel/hytale/protocol/packets/player/SetGameMode.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -43,9 +44,13 @@ public class SetGameMode implements Packet, ToClientPacket { @Nonnull public static SetGameMode deserialize(@Nonnull ByteBuf buf, int offset) { - SetGameMode obj = new SetGameMode(); - obj.gameMode = GameMode.fromValue(buf.getByte(offset + 0)); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SetGameMode", 1, buf.readableBytes() - offset); + } else { + SetGameMode obj = new SetGameMode(); + obj.gameMode = GameMode.fromValue(buf.getByte(offset + 0)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -63,7 +68,12 @@ public class SetGameMode implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 2 ? ValidationResult.error("Invalid GameMode value for GameMode") : ValidationResult.OK; + } } public SetGameMode clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/player/SetMovementStates.java b/src/com/hypixel/hytale/protocol/packets/player/SetMovementStates.java index 2a518d39..dcd249b7 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/SetMovementStates.java +++ b/src/com/hypixel/hytale/protocol/packets/player/SetMovementStates.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.SavedMovementStates; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,13 +45,17 @@ public class SetMovementStates implements Packet, ToClientPacket { @Nonnull public static SetMovementStates deserialize(@Nonnull ByteBuf buf, int offset) { - SetMovementStates obj = new SetMovementStates(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.movementStates = SavedMovementStates.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("SetMovementStates", 2, buf.readableBytes() - offset); + } else { + SetMovementStates obj = new SetMovementStates(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.movementStates = SavedMovementStates.deserialize(buf, offset + 1); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,7 +83,12 @@ public class SetMovementStates implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 2 ? ValidationResult.error("Buffer too small: expected at least 2 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 2) { + return ValidationResult.error("Buffer too small: expected at least 2 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public SetMovementStates clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/player/SyncPlayerPreferences.java b/src/com/hypixel/hytale/protocol/packets/player/SyncPlayerPreferences.java index 2d3097d6..41d308f2 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/SyncPlayerPreferences.java +++ b/src/com/hypixel/hytale/protocol/packets/player/SyncPlayerPreferences.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.PickupLocation; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -93,20 +94,24 @@ public class SyncPlayerPreferences implements Packet, ToServerPacket { @Nonnull public static SyncPlayerPreferences deserialize(@Nonnull ByteBuf buf, int offset) { - SyncPlayerPreferences obj = new SyncPlayerPreferences(); - obj.showEntityMarkers = buf.getByte(offset + 0) != 0; - obj.armorItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 1)); - obj.weaponAndToolItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 2)); - obj.usableItemsItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 3)); - obj.solidBlockItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 4)); - obj.miscItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 5)); - obj.allowNPCDetection = buf.getByte(offset + 6) != 0; - obj.respondToHit = buf.getByte(offset + 7) != 0; - obj.hideHelmet = buf.getByte(offset + 8) != 0; - obj.hideCuirass = buf.getByte(offset + 9) != 0; - obj.hideGauntlets = buf.getByte(offset + 10) != 0; - obj.hidePants = buf.getByte(offset + 11) != 0; - return obj; + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("SyncPlayerPreferences", 12, buf.readableBytes() - offset); + } else { + SyncPlayerPreferences obj = new SyncPlayerPreferences(); + obj.showEntityMarkers = buf.getByte(offset + 0) != 0; + obj.armorItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 1)); + obj.weaponAndToolItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 2)); + obj.usableItemsItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 3)); + obj.solidBlockItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 4)); + obj.miscItemsPreferredPickupLocation = PickupLocation.fromValue(buf.getByte(offset + 5)); + obj.allowNPCDetection = buf.getByte(offset + 6) != 0; + obj.respondToHit = buf.getByte(offset + 7) != 0; + obj.hideHelmet = buf.getByte(offset + 8) != 0; + obj.hideCuirass = buf.getByte(offset + 9) != 0; + obj.hideGauntlets = buf.getByte(offset + 10) != 0; + obj.hidePants = buf.getByte(offset + 11) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -135,7 +140,32 @@ public class SyncPlayerPreferences implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 12 ? ValidationResult.error("Buffer too small: expected at least 12 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 12) { + return ValidationResult.error("Buffer too small: expected at least 12 bytes"); + } else { + int v = buffer.getByte(offset + 1) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid PickupLocation value for ArmorItemsPreferredPickupLocation"); + } else { + v = buffer.getByte(offset + 2) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid PickupLocation value for WeaponAndToolItemsPreferredPickupLocation"); + } else { + v = buffer.getByte(offset + 3) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid PickupLocation value for UsableItemsItemsPreferredPickupLocation"); + } else { + v = buffer.getByte(offset + 4) & 255; + if (v >= 3) { + return ValidationResult.error("Invalid PickupLocation value for SolidBlockItemsPreferredPickupLocation"); + } else { + v = buffer.getByte(offset + 5) & 255; + return v >= 3 ? ValidationResult.error("Invalid PickupLocation value for MiscItemsPreferredPickupLocation") : ValidationResult.OK; + } + } + } + } + } } public SyncPlayerPreferences clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/player/UpdateMemoriesFeatureStatus.java b/src/com/hypixel/hytale/protocol/packets/player/UpdateMemoriesFeatureStatus.java index 45b7d9d5..26b2ec5e 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/UpdateMemoriesFeatureStatus.java +++ b/src/com/hypixel/hytale/protocol/packets/player/UpdateMemoriesFeatureStatus.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.player; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class UpdateMemoriesFeatureStatus implements Packet, ToClientPacket { @Nonnull public static UpdateMemoriesFeatureStatus deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateMemoriesFeatureStatus obj = new UpdateMemoriesFeatureStatus(); - obj.isFeatureUnlocked = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UpdateMemoriesFeatureStatus", 1, buf.readableBytes() - offset); + } else { + UpdateMemoriesFeatureStatus obj = new UpdateMemoriesFeatureStatus(); + obj.isFeatureUnlocked = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/player/UpdateMovementSettings.java b/src/com/hypixel/hytale/protocol/packets/player/UpdateMovementSettings.java index c4e2eed5..e2288b8c 100644 --- a/src/com/hypixel/hytale/protocol/packets/player/UpdateMovementSettings.java +++ b/src/com/hypixel/hytale/protocol/packets/player/UpdateMovementSettings.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.MovementSettings; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,13 +45,17 @@ public class UpdateMovementSettings implements Packet, ToClientPacket { @Nonnull public static UpdateMovementSettings deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateMovementSettings obj = new UpdateMovementSettings(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.movementSettings = MovementSettings.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 252) { + throw ProtocolException.bufferTooSmall("UpdateMovementSettings", 252, buf.readableBytes() - offset); + } else { + UpdateMovementSettings obj = new UpdateMovementSettings(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.movementSettings = MovementSettings.deserialize(buf, offset + 1); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,7 +83,12 @@ public class UpdateMovementSettings implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 252 ? ValidationResult.error("Buffer too small: expected at least 252 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 252) { + return ValidationResult.error("Buffer too small: expected at least 252 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public UpdateMovementSettings clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/serveraccess/RequestServerAccess.java b/src/com/hypixel/hytale/protocol/packets/serveraccess/RequestServerAccess.java index 716fb279..51032d0b 100644 --- a/src/com/hypixel/hytale/protocol/packets/serveraccess/RequestServerAccess.java +++ b/src/com/hypixel/hytale/protocol/packets/serveraccess/RequestServerAccess.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.serveraccess; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -45,10 +46,14 @@ public class RequestServerAccess implements Packet, ToClientPacket { @Nonnull public static RequestServerAccess deserialize(@Nonnull ByteBuf buf, int offset) { - RequestServerAccess obj = new RequestServerAccess(); - obj.access = Access.fromValue(buf.getByte(offset + 0)); - obj.externalPort = buf.getShortLE(offset + 1); - return obj; + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("RequestServerAccess", 3, buf.readableBytes() - offset); + } else { + RequestServerAccess obj = new RequestServerAccess(); + obj.access = Access.fromValue(buf.getByte(offset + 0)); + obj.externalPort = buf.getShortLE(offset + 1); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -67,7 +72,12 @@ public class RequestServerAccess implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 3 ? ValidationResult.error("Buffer too small: expected at least 3 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 3) { + return ValidationResult.error("Buffer too small: expected at least 3 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 4 ? ValidationResult.error("Invalid Access value for Access") : ValidationResult.OK; + } } public RequestServerAccess clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/serveraccess/SetServerAccess.java b/src/com/hypixel/hytale/protocol/packets/serveraccess/SetServerAccess.java index 8f89996e..69a20981 100644 --- a/src/com/hypixel/hytale/protocol/packets/serveraccess/SetServerAccess.java +++ b/src/com/hypixel/hytale/protocol/packets/serveraccess/SetServerAccess.java @@ -50,26 +50,34 @@ public class SetServerAccess implements Packet, ToServerPacket { @Nonnull public static SetServerAccess deserialize(@Nonnull ByteBuf buf, int offset) { - SetServerAccess obj = new SetServerAccess(); - byte nullBits = buf.getByte(offset); - obj.access = Access.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int passwordLen = VarInt.peek(buf, pos); - if (passwordLen < 0) { - throw ProtocolException.negativeLength("Password", passwordLen); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("SetServerAccess", 2, buf.readableBytes() - offset); + } else { + SetServerAccess obj = new SetServerAccess(); + byte nullBits = buf.getByte(offset); + obj.access = Access.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int passwordLen = VarInt.peek(buf, pos); + if (passwordLen < 0) { + throw ProtocolException.invalidVarInt("Password"); + } + + int passwordVarLen = VarInt.size(passwordLen); + if (passwordLen > 4096000) { + throw ProtocolException.stringTooLong("Password", passwordLen, 4096000); + } + + if (pos + passwordVarLen + passwordLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Password", pos + passwordVarLen + passwordLen, buf.readableBytes()); + } + + obj.password = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += passwordVarLen + passwordLen; } - if (passwordLen > 4096000) { - throw ProtocolException.stringTooLong("Password", passwordLen, 4096000); - } - - int passwordVarLen = VarInt.length(buf, pos); - obj.password = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += passwordVarLen + passwordLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -77,7 +85,7 @@ public class SetServerAccess implements Packet, ToServerPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -112,25 +120,30 @@ public class SetServerAccess implements Packet, ToServerPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int passwordLen = VarInt.peek(buffer, pos); - if (passwordLen < 0) { - return ValidationResult.error("Invalid string length for Password"); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid Access value for Access"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int passwordLen = VarInt.peek(buffer, v); + if (passwordLen < 0) { + return ValidationResult.error("Invalid string length for Password"); + } + + if (passwordLen > 4096000) { + return ValidationResult.error("Password exceeds max length 4096000"); + } + + v += VarInt.size(passwordLen); + v += passwordLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Password"); + } } - if (passwordLen > 4096000) { - return ValidationResult.error("Password exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += passwordLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Password"); - } + return ValidationResult.OK; } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/serveraccess/UpdateServerAccess.java b/src/com/hypixel/hytale/protocol/packets/serveraccess/UpdateServerAccess.java index cf78dfa3..d787add2 100644 --- a/src/com/hypixel/hytale/protocol/packets/serveraccess/UpdateServerAccess.java +++ b/src/com/hypixel/hytale/protocol/packets/serveraccess/UpdateServerAccess.java @@ -51,35 +51,39 @@ public class UpdateServerAccess implements Packet, ToServerPacket { @Nonnull public static UpdateServerAccess deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateServerAccess obj = new UpdateServerAccess(); - byte nullBits = buf.getByte(offset); - obj.access = Access.fromValue(buf.getByte(offset + 1)); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int hostsCount = VarInt.peek(buf, pos); - if (hostsCount < 0) { - throw ProtocolException.negativeLength("Hosts", hostsCount); + if (buf.readableBytes() - offset < 2) { + throw ProtocolException.bufferTooSmall("UpdateServerAccess", 2, buf.readableBytes() - offset); + } else { + UpdateServerAccess obj = new UpdateServerAccess(); + byte nullBits = buf.getByte(offset); + obj.access = Access.fromValue(buf.getByte(offset + 1)); + int pos = offset + 2; + if ((nullBits & 1) != 0) { + int hostsCount = VarInt.peek(buf, pos); + if (hostsCount < 0) { + throw ProtocolException.invalidVarInt("Hosts"); + } + + int hostsVarLen = VarInt.size(hostsCount); + if (hostsCount > 4096000) { + throw ProtocolException.arrayTooLong("Hosts", hostsCount, 4096000); + } + + if (pos + hostsVarLen + hostsCount * 2L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Hosts", pos + hostsVarLen + hostsCount * 2, buf.readableBytes()); + } + + pos += hostsVarLen; + obj.hosts = new HostAddress[hostsCount]; + + for (int i = 0; i < hostsCount; i++) { + obj.hosts[i] = HostAddress.deserialize(buf, pos); + pos += HostAddress.computeBytesConsumed(buf, pos); + } } - if (hostsCount > 4096000) { - throw ProtocolException.arrayTooLong("Hosts", hostsCount, 4096000); - } - - int hostsVarLen = VarInt.size(hostsCount); - if (pos + hostsVarLen + hostsCount * 2L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Hosts", pos + hostsVarLen + hostsCount * 2, buf.readableBytes()); - } - - pos += hostsVarLen; - obj.hosts = new HostAddress[hostsCount]; - - for (int i = 0; i < hostsCount; i++) { - obj.hosts[i] = HostAddress.deserialize(buf, pos); - pos += HostAddress.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -87,7 +91,7 @@ public class UpdateServerAccess implements Packet, ToServerPacket { int pos = offset + 2; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += HostAddress.computeBytesConsumed(buf, pos); @@ -140,30 +144,35 @@ public class UpdateServerAccess implements Packet, ToServerPacket { return ValidationResult.error("Buffer too small: expected at least 2 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 2; - if ((nullBits & 1) != 0) { - int hostsCount = VarInt.peek(buffer, pos); - if (hostsCount < 0) { - return ValidationResult.error("Invalid array count for Hosts"); - } - - if (hostsCount > 4096000) { - return ValidationResult.error("Hosts exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < hostsCount; i++) { - ValidationResult structResult = HostAddress.validateStructure(buffer, pos); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid HostAddress in Hosts[" + i + "]: " + structResult.error()); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 4) { + return ValidationResult.error("Invalid Access value for Access"); + } else { + v = offset + 2; + if ((nullBits & 1) != 0) { + int hostsCount = VarInt.peek(buffer, v); + if (hostsCount < 0) { + return ValidationResult.error("Invalid array count for Hosts"); } - pos += HostAddress.computeBytesConsumed(buffer, pos); - } - } + if (hostsCount > 4096000) { + return ValidationResult.error("Hosts exceeds max length 4096000"); + } - return ValidationResult.OK; + v += VarInt.size(hostsCount); + + for (int i = 0; i < hostsCount; i++) { + ValidationResult structResult = HostAddress.validateStructure(buffer, v); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid HostAddress in Hosts[" + i + "]: " + structResult.error()); + } + + v += HostAddress.computeBytesConsumed(buffer, v); + } + } + + return ValidationResult.OK; + } } } diff --git a/src/com/hypixel/hytale/protocol/packets/setup/AssetInitialize.java b/src/com/hypixel/hytale/protocol/packets/setup/AssetInitialize.java index 9c25d68e..bd4e4208 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/AssetInitialize.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/AssetInitialize.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.Asset; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -46,12 +47,16 @@ public class AssetInitialize implements Packet, ToClientPacket { @Nonnull public static AssetInitialize deserialize(@Nonnull ByteBuf buf, int offset) { - AssetInitialize obj = new AssetInitialize(); - obj.size = buf.getIntLE(offset + 0); - int pos = offset + 4; - obj.asset = Asset.deserialize(buf, pos); - pos += Asset.computeBytesConsumed(buf, pos); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("AssetInitialize", 4, buf.readableBytes() - offset); + } else { + AssetInitialize obj = new AssetInitialize(); + obj.size = buf.getIntLE(offset + 0); + int pos = offset + 4; + obj.asset = Asset.deserialize(buf, pos); + pos += Asset.computeBytesConsumed(buf, pos); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/setup/AssetPart.java b/src/com/hypixel/hytale/protocol/packets/setup/AssetPart.java index 5e790fc7..ca8e8a00 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/AssetPart.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/AssetPart.java @@ -45,35 +45,39 @@ public class AssetPart implements Packet, ToClientPacket { @Nonnull public static AssetPart deserialize(@Nonnull ByteBuf buf, int offset) { - AssetPart obj = new AssetPart(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int partCount = VarInt.peek(buf, pos); - if (partCount < 0) { - throw ProtocolException.negativeLength("Part", partCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("AssetPart", 1, buf.readableBytes() - offset); + } else { + AssetPart obj = new AssetPart(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int partCount = VarInt.peek(buf, pos); + if (partCount < 0) { + throw ProtocolException.invalidVarInt("Part"); + } + + int partVarLen = VarInt.size(partCount); + if (partCount > 4096000) { + throw ProtocolException.arrayTooLong("Part", partCount, 4096000); + } + + if (pos + partVarLen + partCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Part", pos + partVarLen + partCount * 1, buf.readableBytes()); + } + + pos += partVarLen; + obj.part = new byte[partCount]; + + for (int i = 0; i < partCount; i++) { + obj.part[i] = buf.getByte(pos + i * 1); + } + + pos += partCount * 1; } - if (partCount > 4096000) { - throw ProtocolException.arrayTooLong("Part", partCount, 4096000); - } - - int partVarLen = VarInt.size(partCount); - if (pos + partVarLen + partCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Part", pos + partVarLen + partCount * 1, buf.readableBytes()); - } - - pos += partVarLen; - obj.part = new byte[partCount]; - - for (int i = 0; i < partCount; i++) { - obj.part[i] = buf.getByte(pos + i * 1); - } - - pos += partCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,7 +85,7 @@ public class AssetPart implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -134,7 +138,7 @@ public class AssetPart implements Packet, ToClientPacket { return ValidationResult.error("Part exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(partCount); pos += partCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Part"); diff --git a/src/com/hypixel/hytale/protocol/packets/setup/PlayerOptions.java b/src/com/hypixel/hytale/protocol/packets/setup/PlayerOptions.java index 1d0757ee..d991b94d 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/PlayerOptions.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/PlayerOptions.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.PlayerSkin; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,15 +45,19 @@ public class PlayerOptions implements Packet, ToServerPacket { @Nonnull public static PlayerOptions deserialize(@Nonnull ByteBuf buf, int offset) { - PlayerOptions obj = new PlayerOptions(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - obj.skin = PlayerSkin.deserialize(buf, pos); - pos += PlayerSkin.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("PlayerOptions", 1, buf.readableBytes() - offset); + } else { + PlayerOptions obj = new PlayerOptions(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + obj.skin = PlayerSkin.deserialize(buf, pos); + pos += PlayerSkin.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/setup/RemoveAssets.java b/src/com/hypixel/hytale/protocol/packets/setup/RemoveAssets.java index b7ea3c02..76d42367 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/RemoveAssets.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/RemoveAssets.java @@ -46,34 +46,38 @@ public class RemoveAssets implements Packet, ToClientPacket { @Nonnull public static RemoveAssets deserialize(@Nonnull ByteBuf buf, int offset) { - RemoveAssets obj = new RemoveAssets(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int assetCount = VarInt.peek(buf, pos); - if (assetCount < 0) { - throw ProtocolException.negativeLength("Asset", assetCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("RemoveAssets", 1, buf.readableBytes() - offset); + } else { + RemoveAssets obj = new RemoveAssets(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int assetCount = VarInt.peek(buf, pos); + if (assetCount < 0) { + throw ProtocolException.invalidVarInt("Asset"); + } + + int assetVarLen = VarInt.size(assetCount); + if (assetCount > 4096000) { + throw ProtocolException.arrayTooLong("Asset", assetCount, 4096000); + } + + if (pos + assetVarLen + assetCount * 64L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Asset", pos + assetVarLen + assetCount * 64, buf.readableBytes()); + } + + pos += assetVarLen; + obj.asset = new Asset[assetCount]; + + for (int i = 0; i < assetCount; i++) { + obj.asset[i] = Asset.deserialize(buf, pos); + pos += Asset.computeBytesConsumed(buf, pos); + } } - if (assetCount > 4096000) { - throw ProtocolException.arrayTooLong("Asset", assetCount, 4096000); - } - - int assetVarLen = VarInt.size(assetCount); - if (pos + assetVarLen + assetCount * 64L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Asset", pos + assetVarLen + assetCount * 64, buf.readableBytes()); - } - - pos += assetVarLen; - obj.asset = new Asset[assetCount]; - - for (int i = 0; i < assetCount; i++) { - obj.asset[i] = Asset.deserialize(buf, pos); - pos += Asset.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,7 +85,7 @@ public class RemoveAssets implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += Asset.computeBytesConsumed(buf, pos); @@ -144,7 +148,7 @@ public class RemoveAssets implements Packet, ToClientPacket { return ValidationResult.error("Asset exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetCount); for (int i = 0; i < assetCount; i++) { ValidationResult structResult = Asset.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/setup/RequestAssets.java b/src/com/hypixel/hytale/protocol/packets/setup/RequestAssets.java index 0571b32f..250d1b07 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/RequestAssets.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/RequestAssets.java @@ -46,34 +46,38 @@ public class RequestAssets implements Packet, ToServerPacket { @Nonnull public static RequestAssets deserialize(@Nonnull ByteBuf buf, int offset) { - RequestAssets obj = new RequestAssets(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int assetsCount = VarInt.peek(buf, pos); - if (assetsCount < 0) { - throw ProtocolException.negativeLength("Assets", assetsCount); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("RequestAssets", 1, buf.readableBytes() - offset); + } else { + RequestAssets obj = new RequestAssets(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int assetsCount = VarInt.peek(buf, pos); + if (assetsCount < 0) { + throw ProtocolException.invalidVarInt("Assets"); + } + + int assetsVarLen = VarInt.size(assetsCount); + if (assetsCount > 4096000) { + throw ProtocolException.arrayTooLong("Assets", assetsCount, 4096000); + } + + if (pos + assetsVarLen + assetsCount * 64L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Assets", pos + assetsVarLen + assetsCount * 64, buf.readableBytes()); + } + + pos += assetsVarLen; + obj.assets = new Asset[assetsCount]; + + for (int i = 0; i < assetsCount; i++) { + obj.assets[i] = Asset.deserialize(buf, pos); + pos += Asset.computeBytesConsumed(buf, pos); + } } - if (assetsCount > 4096000) { - throw ProtocolException.arrayTooLong("Assets", assetsCount, 4096000); - } - - int assetsVarLen = VarInt.size(assetsCount); - if (pos + assetsVarLen + assetsCount * 64L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Assets", pos + assetsVarLen + assetsCount * 64, buf.readableBytes()); - } - - pos += assetsVarLen; - obj.assets = new Asset[assetsCount]; - - for (int i = 0; i < assetsCount; i++) { - obj.assets[i] = Asset.deserialize(buf, pos); - pos += Asset.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,7 +85,7 @@ public class RequestAssets implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += Asset.computeBytesConsumed(buf, pos); @@ -144,7 +148,7 @@ public class RequestAssets implements Packet, ToServerPacket { return ValidationResult.error("Assets exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(assetsCount); for (int i = 0; i < assetsCount; i++) { ValidationResult structResult = Asset.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/setup/ServerTags.java b/src/com/hypixel/hytale/protocol/packets/setup/ServerTags.java index 1d02d785..cf4c8f33 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/ServerTags.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/ServerTags.java @@ -49,44 +49,53 @@ public class ServerTags implements Packet, ToClientPacket { @Nonnull public static ServerTags deserialize(@Nonnull ByteBuf buf, int offset) { - ServerTags obj = new ServerTags(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int tagsCount = VarInt.peek(buf, pos); - if (tagsCount < 0) { - throw ProtocolException.negativeLength("Tags", tagsCount); - } - - if (tagsCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Tags", tagsCount, 4096000); - } - - pos += VarInt.size(tagsCount); - obj.tags = new HashMap<>(tagsCount); - - for (int i = 0; i < tagsCount; i++) { - int keyLen = VarInt.peek(buf, pos); - if (keyLen < 0) { - throw ProtocolException.negativeLength("key", keyLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ServerTags", 1, buf.readableBytes() - offset); + } else { + ServerTags obj = new ServerTags(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int tagsCount = VarInt.peek(buf, pos); + if (tagsCount < 0) { + throw ProtocolException.invalidVarInt("Tags"); } - if (keyLen > 4096000) { - throw ProtocolException.stringTooLong("key", keyLen, 4096000); + int tagsVarLen = VarInt.size(tagsCount); + if (tagsCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Tags", tagsCount, 4096000); } - int keyVarLen = VarInt.length(buf, pos); - String key = PacketIO.readVarString(buf, pos); - pos += keyVarLen + keyLen; - int val = buf.getIntLE(pos); - pos += 4; - if (obj.tags.put(key, val) != null) { - throw ProtocolException.duplicateKey("tags", key); + pos += tagsVarLen; + obj.tags = new HashMap<>(tagsCount); + + for (int i = 0; i < tagsCount; i++) { + int keyLen = VarInt.peek(buf, pos); + if (keyLen < 0) { + throw ProtocolException.invalidVarInt("key"); + } + + int keyVarLen = VarInt.size(keyLen); + if (keyLen > 4096000) { + throw ProtocolException.stringTooLong("key", keyLen, 4096000); + } + + if (pos + keyVarLen + keyLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("key", pos + keyVarLen + keyLen, buf.readableBytes()); + } + + String key = PacketIO.readVarString(buf, pos); + pos += keyVarLen + keyLen; + int val = buf.getIntLE(pos); + pos += 4; + if (obj.tags.put(key, val) != null) { + throw ProtocolException.duplicateKey("tags", key); + } } } + + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,11 +103,11 @@ public class ServerTags implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; pos += 4; } } @@ -160,7 +169,7 @@ public class ServerTags implements Packet, ToClientPacket { return ValidationResult.error("Tags exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(tagsCount); for (int i = 0; i < tagsCount; i++) { int keyLen = VarInt.peek(buffer, pos); @@ -172,7 +181,7 @@ public class ServerTags implements Packet, ToClientPacket { return ValidationResult.error("key exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(keyLen); pos += keyLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading key"); diff --git a/src/com/hypixel/hytale/protocol/packets/setup/SetTimeDilation.java b/src/com/hypixel/hytale/protocol/packets/setup/SetTimeDilation.java index 010947cd..310c15ec 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/SetTimeDilation.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/SetTimeDilation.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.setup; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class SetTimeDilation implements Packet, ToClientPacket { @Nonnull public static SetTimeDilation deserialize(@Nonnull ByteBuf buf, int offset) { - SetTimeDilation obj = new SetTimeDilation(); - obj.timeDilation = buf.getFloatLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("SetTimeDilation", 4, buf.readableBytes() - offset); + } else { + SetTimeDilation obj = new SetTimeDilation(); + obj.timeDilation = buf.getFloatLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/setup/SetUpdateRate.java b/src/com/hypixel/hytale/protocol/packets/setup/SetUpdateRate.java index fb8c04fb..b51a0f32 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/SetUpdateRate.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/SetUpdateRate.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.setup; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class SetUpdateRate implements Packet, ToClientPacket { @Nonnull public static SetUpdateRate deserialize(@Nonnull ByteBuf buf, int offset) { - SetUpdateRate obj = new SetUpdateRate(); - obj.updatesPerSecond = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("SetUpdateRate", 4, buf.readableBytes() - offset); + } else { + SetUpdateRate obj = new SetUpdateRate(); + obj.updatesPerSecond = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/setup/UpdateFeatures.java b/src/com/hypixel/hytale/protocol/packets/setup/UpdateFeatures.java index 0a2bc09c..2ee79ad7 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/UpdateFeatures.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/UpdateFeatures.java @@ -48,34 +48,39 @@ public class UpdateFeatures implements Packet, ToClientPacket { @Nonnull public static UpdateFeatures deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateFeatures obj = new UpdateFeatures(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int featuresCount = VarInt.peek(buf, pos); - if (featuresCount < 0) { - throw ProtocolException.negativeLength("Features", featuresCount); - } + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UpdateFeatures", 1, buf.readableBytes() - offset); + } else { + UpdateFeatures obj = new UpdateFeatures(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int featuresCount = VarInt.peek(buf, pos); + if (featuresCount < 0) { + throw ProtocolException.invalidVarInt("Features"); + } - if (featuresCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("Features", featuresCount, 4096000); - } + int featuresVarLen = VarInt.size(featuresCount); + if (featuresCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("Features", featuresCount, 4096000); + } - pos += VarInt.size(featuresCount); - obj.features = new HashMap<>(featuresCount); + pos += featuresVarLen; + obj.features = new HashMap<>(featuresCount); - for (int i = 0; i < featuresCount; i++) { - ClientFeature key = ClientFeature.fromValue(buf.getByte(pos)); - pos++; - boolean val = buf.getByte(pos) != 0; - pos++; - if (obj.features.put(key, val) != null) { - throw ProtocolException.duplicateKey("features", key); + for (int i = 0; i < featuresCount; i++) { + ClientFeature key = ClientFeature.fromValue(buf.getByte(pos)); + pos++; + boolean val = buf.getByte(pos) != 0; + pos++; + if (obj.features.put(key, val) != null) { + throw ProtocolException.duplicateKey("features", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -83,7 +88,7 @@ public class UpdateFeatures implements Packet, ToClientPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos++; @@ -142,9 +147,14 @@ public class UpdateFeatures implements Packet, ToClientPacket { return ValidationResult.error("Features exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(featuresCount); for (int i = 0; i < featuresCount; i++) { + int v = buffer.getByte(pos) & 255; + if (v >= 11) { + return ValidationResult.error("Invalid ClientFeature value for key"); + } + pos++; if (++pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading value"); diff --git a/src/com/hypixel/hytale/protocol/packets/setup/ViewRadius.java b/src/com/hypixel/hytale/protocol/packets/setup/ViewRadius.java index 6cc45fcc..50fc7190 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/ViewRadius.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/ViewRadius.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -42,9 +43,13 @@ public class ViewRadius implements Packet, ToServerPacket, ToClientPacket { @Nonnull public static ViewRadius deserialize(@Nonnull ByteBuf buf, int offset) { - ViewRadius obj = new ViewRadius(); - obj.value = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("ViewRadius", 4, buf.readableBytes() - offset); + } else { + ViewRadius obj = new ViewRadius(); + obj.value = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/setup/WorldLoadProgress.java b/src/com/hypixel/hytale/protocol/packets/setup/WorldLoadProgress.java index 7b84287b..8f84864e 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/WorldLoadProgress.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/WorldLoadProgress.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,17 +51,21 @@ public class WorldLoadProgress implements Packet, ToClientPacket { @Nonnull public static WorldLoadProgress deserialize(@Nonnull ByteBuf buf, int offset) { - WorldLoadProgress obj = new WorldLoadProgress(); - byte nullBits = buf.getByte(offset); - obj.percentComplete = buf.getIntLE(offset + 1); - obj.percentCompleteSubitem = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - obj.status = FormattedMessage.deserialize(buf, pos); - pos += FormattedMessage.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("WorldLoadProgress", 9, buf.readableBytes() - offset); + } else { + WorldLoadProgress obj = new WorldLoadProgress(); + byte nullBits = buf.getByte(offset); + obj.percentComplete = buf.getIntLE(offset + 1); + obj.percentCompleteSubitem = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + obj.status = FormattedMessage.deserialize(buf, pos); + pos += FormattedMessage.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/setup/WorldSettings.java b/src/com/hypixel/hytale/protocol/packets/setup/WorldSettings.java index 4b10c81c..46af00df 100644 --- a/src/com/hypixel/hytale/protocol/packets/setup/WorldSettings.java +++ b/src/com/hypixel/hytale/protocol/packets/setup/WorldSettings.java @@ -49,35 +49,39 @@ public class WorldSettings implements Packet, ToClientPacket { @Nonnull public static WorldSettings deserialize(@Nonnull ByteBuf buf, int offset) { - WorldSettings obj = new WorldSettings(); - byte nullBits = buf.getByte(offset); - obj.worldHeight = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int requiredAssetsCount = VarInt.peek(buf, pos); - if (requiredAssetsCount < 0) { - throw ProtocolException.negativeLength("RequiredAssets", requiredAssetsCount); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("WorldSettings", 5, buf.readableBytes() - offset); + } else { + WorldSettings obj = new WorldSettings(); + byte nullBits = buf.getByte(offset); + obj.worldHeight = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int requiredAssetsCount = VarInt.peek(buf, pos); + if (requiredAssetsCount < 0) { + throw ProtocolException.invalidVarInt("RequiredAssets"); + } + + int requiredAssetsVarLen = VarInt.size(requiredAssetsCount); + if (requiredAssetsCount > 4096000) { + throw ProtocolException.arrayTooLong("RequiredAssets", requiredAssetsCount, 4096000); + } + + if (pos + requiredAssetsVarLen + requiredAssetsCount * 64L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RequiredAssets", pos + requiredAssetsVarLen + requiredAssetsCount * 64, buf.readableBytes()); + } + + pos += requiredAssetsVarLen; + obj.requiredAssets = new Asset[requiredAssetsCount]; + + for (int i = 0; i < requiredAssetsCount; i++) { + obj.requiredAssets[i] = Asset.deserialize(buf, pos); + pos += Asset.computeBytesConsumed(buf, pos); + } } - if (requiredAssetsCount > 4096000) { - throw ProtocolException.arrayTooLong("RequiredAssets", requiredAssetsCount, 4096000); - } - - int requiredAssetsVarLen = VarInt.size(requiredAssetsCount); - if (pos + requiredAssetsVarLen + requiredAssetsCount * 64L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("RequiredAssets", pos + requiredAssetsVarLen + requiredAssetsCount * 64, buf.readableBytes()); - } - - pos += requiredAssetsVarLen; - obj.requiredAssets = new Asset[requiredAssetsCount]; - - for (int i = 0; i < requiredAssetsCount; i++) { - obj.requiredAssets[i] = Asset.deserialize(buf, pos); - pos += Asset.computeBytesConsumed(buf, pos); - } + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -85,7 +89,7 @@ public class WorldSettings implements Packet, ToClientPacket { int pos = offset + 5; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += Asset.computeBytesConsumed(buf, pos); @@ -149,7 +153,7 @@ public class WorldSettings implements Packet, ToClientPacket { return ValidationResult.error("RequiredAssets exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(requiredAssetsCount); for (int i = 0; i < requiredAssetsCount; i++) { ValidationResult structResult = Asset.validateStructure(buffer, pos); diff --git a/src/com/hypixel/hytale/protocol/packets/stream/StreamOpen.java b/src/com/hypixel/hytale/protocol/packets/stream/StreamOpen.java new file mode 100644 index 00000000..4dfae5b3 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/stream/StreamOpen.java @@ -0,0 +1,97 @@ +package com.hypixel.hytale.protocol.packets.stream; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class StreamOpen implements Packet, ToServerPacket { + public static final int PACKET_ID = 460; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 1; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 1; + public static final int MAX_SIZE = 1; + @Nonnull + public StreamType type = StreamType.Game; + + @Override + public int getId() { + return 460; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public StreamOpen() { + } + + public StreamOpen(@Nonnull StreamType type) { + this.type = type; + } + + public StreamOpen(@Nonnull StreamOpen other) { + this.type = other.type; + } + + @Nonnull + public static StreamOpen deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("StreamOpen", 1, buf.readableBytes() - offset); + } else { + StreamOpen obj = new StreamOpen(); + obj.type = StreamType.fromValue(buf.getByte(offset + 0)); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 1; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + buf.writeByte(this.type.getValue()); + } + + @Override + public int computeSize() { + return 1; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 2 ? ValidationResult.error("Invalid StreamType value for Type") : ValidationResult.OK; + } + } + + public StreamOpen clone() { + StreamOpen copy = new StreamOpen(); + copy.type = this.type; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return obj instanceof StreamOpen other ? Objects.equals(this.type, other.type) : false; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.type); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/stream/StreamOpenResponse.java b/src/com/hypixel/hytale/protocol/packets/stream/StreamOpenResponse.java new file mode 100644 index 00000000..d0adede1 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/stream/StreamOpenResponse.java @@ -0,0 +1,178 @@ +package com.hypixel.hytale.protocol.packets.stream; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class StreamOpenResponse implements Packet, ToClientPacket { + public static final int PACKET_ID = 461; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 3; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 3; + public static final int MAX_SIZE = 16384008; + @Nonnull + public StreamType type = StreamType.Game; + public boolean accepted; + @Nullable + public String rejectionReason; + + @Override + public int getId() { + return 461; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public StreamOpenResponse() { + } + + public StreamOpenResponse(@Nonnull StreamType type, boolean accepted, @Nullable String rejectionReason) { + this.type = type; + this.accepted = accepted; + this.rejectionReason = rejectionReason; + } + + public StreamOpenResponse(@Nonnull StreamOpenResponse other) { + this.type = other.type; + this.accepted = other.accepted; + this.rejectionReason = other.rejectionReason; + } + + @Nonnull + public static StreamOpenResponse deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("StreamOpenResponse", 3, buf.readableBytes() - offset); + } else { + StreamOpenResponse obj = new StreamOpenResponse(); + byte nullBits = buf.getByte(offset); + obj.type = StreamType.fromValue(buf.getByte(offset + 1)); + obj.accepted = buf.getByte(offset + 2) != 0; + int pos = offset + 3; + if ((nullBits & 1) != 0) { + int rejectionReasonLen = VarInt.peek(buf, pos); + if (rejectionReasonLen < 0) { + throw ProtocolException.invalidVarInt("RejectionReason"); + } + + int rejectionReasonVarLen = VarInt.size(rejectionReasonLen); + if (rejectionReasonLen > 4096000) { + throw ProtocolException.stringTooLong("RejectionReason", rejectionReasonLen, 4096000); + } + + if (pos + rejectionReasonVarLen + rejectionReasonLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RejectionReason", pos + rejectionReasonVarLen + rejectionReasonLen, buf.readableBytes()); + } + + obj.rejectionReason = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += rejectionReasonVarLen + rejectionReasonLen; + } + + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 3; + if ((nullBits & 1) != 0) { + int sl = VarInt.peek(buf, pos); + pos += VarInt.size(sl) + sl; + } + + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.rejectionReason != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + buf.writeByte(this.type.getValue()); + buf.writeByte(this.accepted ? 1 : 0); + if (this.rejectionReason != null) { + PacketIO.writeVarString(buf, this.rejectionReason, 4096000); + } + } + + @Override + public int computeSize() { + int size = 3; + if (this.rejectionReason != null) { + size += PacketIO.stringSize(this.rejectionReason); + } + + return size; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 3) { + return ValidationResult.error("Buffer too small: expected at least 3 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 1) & 255; + if (v >= 2) { + return ValidationResult.error("Invalid StreamType value for Type"); + } else { + v = offset + 3; + if ((nullBits & 1) != 0) { + int rejectionReasonLen = VarInt.peek(buffer, v); + if (rejectionReasonLen < 0) { + return ValidationResult.error("Invalid string length for RejectionReason"); + } + + if (rejectionReasonLen > 4096000) { + return ValidationResult.error("RejectionReason exceeds max length 4096000"); + } + + v += VarInt.size(rejectionReasonLen); + v += rejectionReasonLen; + if (v > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading RejectionReason"); + } + } + + return ValidationResult.OK; + } + } + } + + public StreamOpenResponse clone() { + StreamOpenResponse copy = new StreamOpenResponse(); + copy.type = this.type; + copy.accepted = this.accepted; + copy.rejectionReason = this.rejectionReason; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof StreamOpenResponse other) + ? false + : Objects.equals(this.type, other.type) && this.accepted == other.accepted && Objects.equals(this.rejectionReason, other.rejectionReason); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.type, this.accepted, this.rejectionReason); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/stream/StreamType.java b/src/com/hypixel/hytale/protocol/packets/stream/StreamType.java new file mode 100644 index 00000000..77413d09 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/stream/StreamType.java @@ -0,0 +1,27 @@ +package com.hypixel.hytale.protocol.packets.stream; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum StreamType { + Game(0), + Voice(1); + + public static final StreamType[] VALUES = values(); + private final int value; + + private StreamType(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static StreamType fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("StreamType", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/voice/RelayedVoiceData.java b/src/com/hypixel/hytale/protocol/packets/voice/RelayedVoiceData.java new file mode 100644 index 00000000..022a93a6 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/voice/RelayedVoiceData.java @@ -0,0 +1,222 @@ +package com.hypixel.hytale.protocol.packets.voice; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.Position; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import java.util.Objects; +import java.util.UUID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class RelayedVoiceData implements Packet, ToClientPacket { + public static final int PACKET_ID = 451; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 52; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 52; + public static final int MAX_SIZE = 569; + @Nonnull + public UUID speakerId = new UUID(0L, 0L); + public int entityId; + public short sequenceNumber; + public int timestamp; + @Nullable + public Position speakerPosition; + public boolean speakerIsUnderwater; + @Nonnull + public byte[] opusData = new byte[0]; + + @Override + public int getId() { + return 451; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Voice; + } + + public RelayedVoiceData() { + } + + public RelayedVoiceData( + @Nonnull UUID speakerId, + int entityId, + short sequenceNumber, + int timestamp, + @Nullable Position speakerPosition, + boolean speakerIsUnderwater, + @Nonnull byte[] opusData + ) { + this.speakerId = speakerId; + this.entityId = entityId; + this.sequenceNumber = sequenceNumber; + this.timestamp = timestamp; + this.speakerPosition = speakerPosition; + this.speakerIsUnderwater = speakerIsUnderwater; + this.opusData = opusData; + } + + public RelayedVoiceData(@Nonnull RelayedVoiceData other) { + this.speakerId = other.speakerId; + this.entityId = other.entityId; + this.sequenceNumber = other.sequenceNumber; + this.timestamp = other.timestamp; + this.speakerPosition = other.speakerPosition; + this.speakerIsUnderwater = other.speakerIsUnderwater; + this.opusData = other.opusData; + } + + @Nonnull + public static RelayedVoiceData deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 52) { + throw ProtocolException.bufferTooSmall("RelayedVoiceData", 52, buf.readableBytes() - offset); + } else { + RelayedVoiceData obj = new RelayedVoiceData(); + byte nullBits = buf.getByte(offset); + obj.speakerId = PacketIO.readUUID(buf, offset + 1); + obj.entityId = buf.getIntLE(offset + 17); + obj.sequenceNumber = buf.getShortLE(offset + 21); + obj.timestamp = buf.getIntLE(offset + 23); + if ((nullBits & 1) != 0) { + obj.speakerPosition = Position.deserialize(buf, offset + 27); + } + + obj.speakerIsUnderwater = buf.getByte(offset + 51) != 0; + int pos = offset + 52; + int opusDataCount = VarInt.peek(buf, pos); + if (opusDataCount < 0) { + throw ProtocolException.invalidVarInt("OpusData"); + } else { + int opusDataVarLen = VarInt.size(opusDataCount); + if (opusDataCount > 512) { + throw ProtocolException.arrayTooLong("OpusData", opusDataCount, 512); + } else if (pos + opusDataVarLen + opusDataCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("OpusData", pos + opusDataVarLen + opusDataCount * 1, buf.readableBytes()); + } else { + pos += opusDataVarLen; + obj.opusData = new byte[opusDataCount]; + + for (int i = 0; i < opusDataCount; i++) { + obj.opusData[i] = buf.getByte(pos + i * 1); + } + + pos += opusDataCount * 1; + return obj; + } + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + byte nullBits = buf.getByte(offset); + int pos = offset + 52; + int arrLen = VarInt.peek(buf, pos); + pos += VarInt.size(arrLen) + arrLen * 1; + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + byte nullBits = 0; + if (this.speakerPosition != null) { + nullBits = (byte)(nullBits | 1); + } + + buf.writeByte(nullBits); + PacketIO.writeUUID(buf, this.speakerId); + buf.writeIntLE(this.entityId); + buf.writeShortLE(this.sequenceNumber); + buf.writeIntLE(this.timestamp); + if (this.speakerPosition != null) { + this.speakerPosition.serialize(buf); + } else { + buf.writeZero(24); + } + + buf.writeByte(this.speakerIsUnderwater ? 1 : 0); + if (this.opusData.length > 512) { + throw ProtocolException.arrayTooLong("OpusData", this.opusData.length, 512); + } else { + VarInt.write(buf, this.opusData.length); + + for (byte item : this.opusData) { + buf.writeByte(item); + } + } + } + + @Override + public int computeSize() { + int size = 52; + return size + VarInt.size(this.opusData.length) + this.opusData.length * 1; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 52) { + return ValidationResult.error("Buffer too small: expected at least 52 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int pos = offset + 52; + int opusDataCount = VarInt.peek(buffer, pos); + if (opusDataCount < 0) { + return ValidationResult.error("Invalid array count for OpusData"); + } else if (opusDataCount > 512) { + return ValidationResult.error("OpusData exceeds max length 512"); + } else { + pos += VarInt.size(opusDataCount); + pos += opusDataCount * 1; + return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading OpusData") : ValidationResult.OK; + } + } + } + + public RelayedVoiceData clone() { + RelayedVoiceData copy = new RelayedVoiceData(); + copy.speakerId = this.speakerId; + copy.entityId = this.entityId; + copy.sequenceNumber = this.sequenceNumber; + copy.timestamp = this.timestamp; + copy.speakerPosition = this.speakerPosition != null ? this.speakerPosition.clone() : null; + copy.speakerIsUnderwater = this.speakerIsUnderwater; + copy.opusData = Arrays.copyOf(this.opusData, this.opusData.length); + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof RelayedVoiceData other) + ? false + : Objects.equals(this.speakerId, other.speakerId) + && this.entityId == other.entityId + && this.sequenceNumber == other.sequenceNumber + && this.timestamp == other.timestamp + && Objects.equals(this.speakerPosition, other.speakerPosition) + && this.speakerIsUnderwater == other.speakerIsUnderwater + && Arrays.equals(this.opusData, other.opusData); + } + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + Objects.hashCode(this.speakerId); + result = 31 * result + Integer.hashCode(this.entityId); + result = 31 * result + Short.hashCode(this.sequenceNumber); + result = 31 * result + Integer.hashCode(this.timestamp); + result = 31 * result + Objects.hashCode(this.speakerPosition); + result = 31 * result + Boolean.hashCode(this.speakerIsUnderwater); + return 31 * result + Arrays.hashCode(this.opusData); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/voice/VoiceCodec.java b/src/com/hypixel/hytale/protocol/packets/voice/VoiceCodec.java new file mode 100644 index 00000000..f42694aa --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/voice/VoiceCodec.java @@ -0,0 +1,26 @@ +package com.hypixel.hytale.protocol.packets.voice; + +import com.hypixel.hytale.protocol.io.ProtocolException; + +public enum VoiceCodec { + Opus(0); + + public static final VoiceCodec[] VALUES = values(); + private final int value; + + private VoiceCodec(int value) { + this.value = value; + } + + public int getValue() { + return this.value; + } + + public static VoiceCodec fromValue(int value) { + if (value >= 0 && value < VALUES.length) { + return VALUES[value]; + } else { + throw ProtocolException.invalidEnumValue("VoiceCodec", value); + } + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/voice/VoiceConfig.java b/src/com/hypixel/hytale/protocol/packets/voice/VoiceConfig.java new file mode 100644 index 00000000..1cfc3dae --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/voice/VoiceConfig.java @@ -0,0 +1,166 @@ +package com.hypixel.hytale.protocol.packets.voice; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class VoiceConfig implements Packet, ToClientPacket { + public static final int PACKET_ID = 452; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 17; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 17; + public static final int MAX_SIZE = 17; + public boolean voiceEnabled; + @Nonnull + public VoiceCodec codec = VoiceCodec.Opus; + public int sampleRate; + public byte channels; + public float maxHearingDistance; + public float referenceDistance; + public boolean supportsVoiceStream; + public byte maxPacketsPerSecond; + + @Override + public int getId() { + return 452; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public VoiceConfig() { + } + + public VoiceConfig( + boolean voiceEnabled, + @Nonnull VoiceCodec codec, + int sampleRate, + byte channels, + float maxHearingDistance, + float referenceDistance, + boolean supportsVoiceStream, + byte maxPacketsPerSecond + ) { + this.voiceEnabled = voiceEnabled; + this.codec = codec; + this.sampleRate = sampleRate; + this.channels = channels; + this.maxHearingDistance = maxHearingDistance; + this.referenceDistance = referenceDistance; + this.supportsVoiceStream = supportsVoiceStream; + this.maxPacketsPerSecond = maxPacketsPerSecond; + } + + public VoiceConfig(@Nonnull VoiceConfig other) { + this.voiceEnabled = other.voiceEnabled; + this.codec = other.codec; + this.sampleRate = other.sampleRate; + this.channels = other.channels; + this.maxHearingDistance = other.maxHearingDistance; + this.referenceDistance = other.referenceDistance; + this.supportsVoiceStream = other.supportsVoiceStream; + this.maxPacketsPerSecond = other.maxPacketsPerSecond; + } + + @Nonnull + public static VoiceConfig deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("VoiceConfig", 17, buf.readableBytes() - offset); + } else { + VoiceConfig obj = new VoiceConfig(); + obj.voiceEnabled = buf.getByte(offset + 0) != 0; + obj.codec = VoiceCodec.fromValue(buf.getByte(offset + 1)); + obj.sampleRate = buf.getIntLE(offset + 2); + obj.channels = buf.getByte(offset + 6); + obj.maxHearingDistance = buf.getFloatLE(offset + 7); + obj.referenceDistance = buf.getFloatLE(offset + 11); + obj.supportsVoiceStream = buf.getByte(offset + 15) != 0; + obj.maxPacketsPerSecond = buf.getByte(offset + 16); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 17; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + buf.writeByte(this.voiceEnabled ? 1 : 0); + buf.writeByte(this.codec.getValue()); + buf.writeIntLE(this.sampleRate); + buf.writeByte(this.channels); + buf.writeFloatLE(this.maxHearingDistance); + buf.writeFloatLE(this.referenceDistance); + buf.writeByte(this.supportsVoiceStream ? 1 : 0); + buf.writeByte(this.maxPacketsPerSecond); + } + + @Override + public int computeSize() { + return 17; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); + } else { + int v = buffer.getByte(offset + 1) & 255; + return v >= 1 ? ValidationResult.error("Invalid VoiceCodec value for Codec") : ValidationResult.OK; + } + } + + public VoiceConfig clone() { + VoiceConfig copy = new VoiceConfig(); + copy.voiceEnabled = this.voiceEnabled; + copy.codec = this.codec; + copy.sampleRate = this.sampleRate; + copy.channels = this.channels; + copy.maxHearingDistance = this.maxHearingDistance; + copy.referenceDistance = this.referenceDistance; + copy.supportsVoiceStream = this.supportsVoiceStream; + copy.maxPacketsPerSecond = this.maxPacketsPerSecond; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof VoiceConfig other) + ? false + : this.voiceEnabled == other.voiceEnabled + && Objects.equals(this.codec, other.codec) + && this.sampleRate == other.sampleRate + && this.channels == other.channels + && this.maxHearingDistance == other.maxHearingDistance + && this.referenceDistance == other.referenceDistance + && this.supportsVoiceStream == other.supportsVoiceStream + && this.maxPacketsPerSecond == other.maxPacketsPerSecond; + } + } + + @Override + public int hashCode() { + return Objects.hash( + this.voiceEnabled, + this.codec, + this.sampleRate, + this.channels, + this.maxHearingDistance, + this.referenceDistance, + this.supportsVoiceStream, + this.maxPacketsPerSecond + ); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/voice/VoiceData.java b/src/com/hypixel/hytale/protocol/packets/voice/VoiceData.java new file mode 100644 index 00000000..faf1be9a --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/voice/VoiceData.java @@ -0,0 +1,156 @@ +package com.hypixel.hytale.protocol.packets.voice; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import com.hypixel.hytale.protocol.io.VarInt; +import io.netty.buffer.ByteBuf; +import java.util.Arrays; +import javax.annotation.Nonnull; + +public class VoiceData implements Packet, ToServerPacket { + public static final int PACKET_ID = 450; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 6; + public static final int VARIABLE_FIELD_COUNT = 1; + public static final int VARIABLE_BLOCK_START = 6; + public static final int MAX_SIZE = 523; + public short sequenceNumber; + public int timestamp; + @Nonnull + public byte[] opusData = new byte[0]; + + @Override + public int getId() { + return 450; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Voice; + } + + public VoiceData() { + } + + public VoiceData(short sequenceNumber, int timestamp, @Nonnull byte[] opusData) { + this.sequenceNumber = sequenceNumber; + this.timestamp = timestamp; + this.opusData = opusData; + } + + public VoiceData(@Nonnull VoiceData other) { + this.sequenceNumber = other.sequenceNumber; + this.timestamp = other.timestamp; + this.opusData = other.opusData; + } + + @Nonnull + public static VoiceData deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 6) { + throw ProtocolException.bufferTooSmall("VoiceData", 6, buf.readableBytes() - offset); + } else { + VoiceData obj = new VoiceData(); + obj.sequenceNumber = buf.getShortLE(offset + 0); + obj.timestamp = buf.getIntLE(offset + 2); + int pos = offset + 6; + int opusDataCount = VarInt.peek(buf, pos); + if (opusDataCount < 0) { + throw ProtocolException.invalidVarInt("OpusData"); + } else { + int opusDataVarLen = VarInt.size(opusDataCount); + if (opusDataCount > 512) { + throw ProtocolException.arrayTooLong("OpusData", opusDataCount, 512); + } else if (pos + opusDataVarLen + opusDataCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("OpusData", pos + opusDataVarLen + opusDataCount * 1, buf.readableBytes()); + } else { + pos += opusDataVarLen; + obj.opusData = new byte[opusDataCount]; + + for (int i = 0; i < opusDataCount; i++) { + obj.opusData[i] = buf.getByte(pos + i * 1); + } + + pos += opusDataCount * 1; + return obj; + } + } + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + int pos = offset + 6; + int arrLen = VarInt.peek(buf, pos); + pos += VarInt.size(arrLen) + arrLen * 1; + return pos - offset; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + buf.writeShortLE(this.sequenceNumber); + buf.writeIntLE(this.timestamp); + if (this.opusData.length > 512) { + throw ProtocolException.arrayTooLong("OpusData", this.opusData.length, 512); + } else { + VarInt.write(buf, this.opusData.length); + + for (byte item : this.opusData) { + buf.writeByte(item); + } + } + } + + @Override + public int computeSize() { + int size = 6; + return size + VarInt.size(this.opusData.length) + this.opusData.length * 1; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 6) { + return ValidationResult.error("Buffer too small: expected at least 6 bytes"); + } else { + int pos = offset + 6; + int opusDataCount = VarInt.peek(buffer, pos); + if (opusDataCount < 0) { + return ValidationResult.error("Invalid array count for OpusData"); + } else if (opusDataCount > 512) { + return ValidationResult.error("OpusData exceeds max length 512"); + } else { + pos += VarInt.size(opusDataCount); + pos += opusDataCount * 1; + return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading OpusData") : ValidationResult.OK; + } + } + } + + public VoiceData clone() { + VoiceData copy = new VoiceData(); + copy.sequenceNumber = this.sequenceNumber; + copy.timestamp = this.timestamp; + copy.opusData = Arrays.copyOf(this.opusData, this.opusData.length); + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof VoiceData other) + ? false + : this.sequenceNumber == other.sequenceNumber && this.timestamp == other.timestamp && Arrays.equals(this.opusData, other.opusData); + } + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + Short.hashCode(this.sequenceNumber); + result = 31 * result + Integer.hashCode(this.timestamp); + return 31 * result + Arrays.hashCode(this.opusData); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/window/ChangeBlockAction.java b/src/com/hypixel/hytale/protocol/packets/window/ChangeBlockAction.java index 7b88b0ab..b5236d8b 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/ChangeBlockAction.java +++ b/src/com/hypixel/hytale/protocol/packets/window/ChangeBlockAction.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.window; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class ChangeBlockAction extends WindowAction { @Nonnull public static ChangeBlockAction deserialize(@Nonnull ByteBuf buf, int offset) { - ChangeBlockAction obj = new ChangeBlockAction(); - obj.down = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ChangeBlockAction", 1, buf.readableBytes() - offset); + } else { + ChangeBlockAction obj = new ChangeBlockAction(); + obj.down = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/window/ClientOpenWindow.java b/src/com/hypixel/hytale/protocol/packets/window/ClientOpenWindow.java index 6c04aa6c..9d1a1a16 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/ClientOpenWindow.java +++ b/src/com/hypixel/hytale/protocol/packets/window/ClientOpenWindow.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.window; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -42,9 +43,13 @@ public class ClientOpenWindow implements Packet, ToServerPacket { @Nonnull public static ClientOpenWindow deserialize(@Nonnull ByteBuf buf, int offset) { - ClientOpenWindow obj = new ClientOpenWindow(); - obj.type = WindowType.fromValue(buf.getByte(offset + 0)); - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ClientOpenWindow", 1, buf.readableBytes() - offset); + } else { + ClientOpenWindow obj = new ClientOpenWindow(); + obj.type = WindowType.fromValue(buf.getByte(offset + 0)); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -62,7 +67,12 @@ public class ClientOpenWindow implements Packet, ToServerPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 1) { + return ValidationResult.error("Buffer too small: expected at least 1 bytes"); + } else { + int v = buffer.getByte(offset + 0) & 255; + return v >= 7 ? ValidationResult.error("Invalid WindowType value for Type") : ValidationResult.OK; + } } public ClientOpenWindow clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/window/CloseWindow.java b/src/com/hypixel/hytale/protocol/packets/window/CloseWindow.java index 9ac27cc4..eba5d2e2 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/CloseWindow.java +++ b/src/com/hypixel/hytale/protocol/packets/window/CloseWindow.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -42,9 +43,13 @@ public class CloseWindow implements Packet, ToServerPacket, ToClientPacket { @Nonnull public static CloseWindow deserialize(@Nonnull ByteBuf buf, int offset) { - CloseWindow obj = new CloseWindow(); - obj.id = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("CloseWindow", 4, buf.readableBytes() - offset); + } else { + CloseWindow obj = new CloseWindow(); + obj.id = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/window/CraftRecipeAction.java b/src/com/hypixel/hytale/protocol/packets/window/CraftRecipeAction.java index a844952d..a73eb31b 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/CraftRecipeAction.java +++ b/src/com/hypixel/hytale/protocol/packets/window/CraftRecipeAction.java @@ -34,26 +34,34 @@ public class CraftRecipeAction extends WindowAction { @Nonnull public static CraftRecipeAction deserialize(@Nonnull ByteBuf buf, int offset) { - CraftRecipeAction obj = new CraftRecipeAction(); - byte nullBits = buf.getByte(offset); - obj.quantity = buf.getIntLE(offset + 1); - int pos = offset + 5; - if ((nullBits & 1) != 0) { - int recipeIdLen = VarInt.peek(buf, pos); - if (recipeIdLen < 0) { - throw ProtocolException.negativeLength("RecipeId", recipeIdLen); + if (buf.readableBytes() - offset < 5) { + throw ProtocolException.bufferTooSmall("CraftRecipeAction", 5, buf.readableBytes() - offset); + } else { + CraftRecipeAction obj = new CraftRecipeAction(); + byte nullBits = buf.getByte(offset); + obj.quantity = buf.getIntLE(offset + 1); + int pos = offset + 5; + if ((nullBits & 1) != 0) { + int recipeIdLen = VarInt.peek(buf, pos); + if (recipeIdLen < 0) { + throw ProtocolException.invalidVarInt("RecipeId"); + } + + int recipeIdVarLen = VarInt.size(recipeIdLen); + if (recipeIdLen > 4096000) { + throw ProtocolException.stringTooLong("RecipeId", recipeIdLen, 4096000); + } + + if (pos + recipeIdVarLen + recipeIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RecipeId", pos + recipeIdVarLen + recipeIdLen, buf.readableBytes()); + } + + obj.recipeId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += recipeIdVarLen + recipeIdLen; } - if (recipeIdLen > 4096000) { - throw ProtocolException.stringTooLong("RecipeId", recipeIdLen, 4096000); - } - - int recipeIdVarLen = VarInt.length(buf, pos); - obj.recipeId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += recipeIdVarLen + recipeIdLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -61,7 +69,7 @@ public class CraftRecipeAction extends WindowAction { int pos = offset + 5; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -110,7 +118,7 @@ public class CraftRecipeAction extends WindowAction { return ValidationResult.error("RecipeId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(recipeIdLen); pos += recipeIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading RecipeId"); diff --git a/src/com/hypixel/hytale/protocol/packets/window/OpenWindow.java b/src/com/hypixel/hytale/protocol/packets/window/OpenWindow.java index bde3e616..819dc326 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/OpenWindow.java +++ b/src/com/hypixel/hytale/protocol/packets/window/OpenWindow.java @@ -65,35 +65,59 @@ public class OpenWindow implements Packet, ToClientPacket { @Nonnull public static OpenWindow deserialize(@Nonnull ByteBuf buf, int offset) { - OpenWindow obj = new OpenWindow(); - byte nullBits = buf.getByte(offset); - obj.id = buf.getIntLE(offset + 1); - obj.windowType = WindowType.fromValue(buf.getByte(offset + 5)); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 18 + buf.getIntLE(offset + 6); - int windowDataLen = VarInt.peek(buf, varPos0); - if (windowDataLen < 0) { - throw ProtocolException.negativeLength("WindowData", windowDataLen); + if (buf.readableBytes() - offset < 18) { + throw ProtocolException.bufferTooSmall("OpenWindow", 18, buf.readableBytes() - offset); + } else { + OpenWindow obj = new OpenWindow(); + byte nullBits = buf.getByte(offset); + obj.id = buf.getIntLE(offset + 1); + obj.windowType = WindowType.fromValue(buf.getByte(offset + 5)); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 6); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("WindowData", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 18 + varPosBase0; + int windowDataLen = VarInt.peek(buf, varPos0); + if (windowDataLen < 0) { + throw ProtocolException.invalidVarInt("WindowData"); + } + + int windowDataVarIntLen = VarInt.size(windowDataLen); + if (windowDataLen > 4096000) { + throw ProtocolException.stringTooLong("WindowData", windowDataLen, 4096000); + } + + if (varPos0 + windowDataVarIntLen + windowDataLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("WindowData", varPos0 + windowDataVarIntLen + windowDataLen, buf.readableBytes()); + } + + obj.windowData = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (windowDataLen > 4096000) { - throw ProtocolException.stringTooLong("WindowData", windowDataLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 10); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Inventory", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 18 + varPosBase1; + obj.inventory = InventorySection.deserialize(buf, varPos1); } - obj.windowData = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 14); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("ExtraResources", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 18 + buf.getIntLE(offset + 10); - obj.inventory = InventorySection.deserialize(buf, varPos1); - } + int varPos2 = offset + 18 + varPosBase2; + obj.extraResources = ExtraResources.deserialize(buf, varPos2); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 18 + buf.getIntLE(offset + 14); - obj.extraResources = ExtraResources.deserialize(buf, varPos2); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,9 +125,13 @@ public class OpenWindow implements Packet, ToClientPacket { int maxEnd = 18; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("WindowData", fieldOffset0, maxEnd); + } + int pos0 = offset + 18 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -111,6 +139,10 @@ public class OpenWindow implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 10); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Inventory", fieldOffset1, maxEnd); + } + int pos1 = offset + 18 + fieldOffset1; pos1 += InventorySection.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -120,6 +152,10 @@ public class OpenWindow implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 14); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("ExtraResources", fieldOffset2, maxEnd); + } + int pos2 = offset + 18 + fieldOffset2; pos2 += ExtraResources.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -201,72 +237,65 @@ public class OpenWindow implements Packet, ToClientPacket { return ValidationResult.error("Buffer too small: expected at least 18 bytes"); } else { byte nullBits = buffer.getByte(offset); - if ((nullBits & 1) != 0) { - int windowDataOffset = buffer.getIntLE(offset + 6); - if (windowDataOffset < 0) { - return ValidationResult.error("Invalid offset for WindowData"); + int v = buffer.getByte(offset + 5) & 255; + if (v >= 7) { + return ValidationResult.error("Invalid WindowType value for WindowType"); + } else { + if ((nullBits & 1) != 0) { + v = buffer.getIntLE(offset + 6); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for WindowData"); + } + + int pos = offset + 18 + v; + int windowDataLen = VarInt.peek(buffer, pos); + if (windowDataLen < 0) { + return ValidationResult.error("Invalid string length for WindowData"); + } + + if (windowDataLen > 4096000) { + return ValidationResult.error("WindowData exceeds max length 4096000"); + } + + pos += VarInt.size(windowDataLen); + pos += windowDataLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading WindowData"); + } } - int pos = offset + 18 + windowDataOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for WindowData"); + if ((nullBits & 2) != 0) { + v = buffer.getIntLE(offset + 10); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Inventory"); + } + + int posx = offset + 18 + v; + ValidationResult inventoryResult = InventorySection.validateStructure(buffer, posx); + if (!inventoryResult.isValid()) { + return ValidationResult.error("Invalid Inventory: " + inventoryResult.error()); + } + + posx += InventorySection.computeBytesConsumed(buffer, posx); } - int windowDataLen = VarInt.peek(buffer, pos); - if (windowDataLen < 0) { - return ValidationResult.error("Invalid string length for WindowData"); + if ((nullBits & 4) != 0) { + v = buffer.getIntLE(offset + 14); + if (v < 0 || v > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for ExtraResources"); + } + + int posx = offset + 18 + v; + ValidationResult extraResourcesResult = ExtraResources.validateStructure(buffer, posx); + if (!extraResourcesResult.isValid()) { + return ValidationResult.error("Invalid ExtraResources: " + extraResourcesResult.error()); + } + + posx += ExtraResources.computeBytesConsumed(buffer, posx); } - if (windowDataLen > 4096000) { - return ValidationResult.error("WindowData exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - pos += windowDataLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading WindowData"); - } + return ValidationResult.OK; } - - if ((nullBits & 2) != 0) { - int inventoryOffset = buffer.getIntLE(offset + 10); - if (inventoryOffset < 0) { - return ValidationResult.error("Invalid offset for Inventory"); - } - - int posx = offset + 18 + inventoryOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Inventory"); - } - - ValidationResult inventoryResult = InventorySection.validateStructure(buffer, posx); - if (!inventoryResult.isValid()) { - return ValidationResult.error("Invalid Inventory: " + inventoryResult.error()); - } - - posx += InventorySection.computeBytesConsumed(buffer, posx); - } - - if ((nullBits & 4) != 0) { - int extraResourcesOffset = buffer.getIntLE(offset + 14); - if (extraResourcesOffset < 0) { - return ValidationResult.error("Invalid offset for ExtraResources"); - } - - int posxx = offset + 18 + extraResourcesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ExtraResources"); - } - - ValidationResult extraResourcesResult = ExtraResources.validateStructure(buffer, posxx); - if (!extraResourcesResult.isValid()) { - return ValidationResult.error("Invalid ExtraResources: " + extraResourcesResult.error()); - } - - posxx += ExtraResources.computeBytesConsumed(buffer, posxx); - } - - return ValidationResult.OK; } } diff --git a/src/com/hypixel/hytale/protocol/packets/window/SelectSlotAction.java b/src/com/hypixel/hytale/protocol/packets/window/SelectSlotAction.java index 4df6967d..f80df8a7 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/SelectSlotAction.java +++ b/src/com/hypixel/hytale/protocol/packets/window/SelectSlotAction.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.window; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class SelectSlotAction extends WindowAction { @Nonnull public static SelectSlotAction deserialize(@Nonnull ByteBuf buf, int offset) { - SelectSlotAction obj = new SelectSlotAction(); - obj.slot = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("SelectSlotAction", 4, buf.readableBytes() - offset); + } else { + SelectSlotAction obj = new SelectSlotAction(); + obj.slot = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/window/SendWindowAction.java b/src/com/hypixel/hytale/protocol/packets/window/SendWindowAction.java index 428f6fd5..4f6743bb 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/SendWindowAction.java +++ b/src/com/hypixel/hytale/protocol/packets/window/SendWindowAction.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.window; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -45,12 +46,16 @@ public class SendWindowAction implements Packet, ToServerPacket { @Nonnull public static SendWindowAction deserialize(@Nonnull ByteBuf buf, int offset) { - SendWindowAction obj = new SendWindowAction(); - obj.id = buf.getIntLE(offset + 0); - int pos = offset + 4; - obj.action = WindowAction.deserialize(buf, pos); - pos += WindowAction.computeBytesConsumed(buf, pos); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("SendWindowAction", 4, buf.readableBytes() - offset); + } else { + SendWindowAction obj = new SendWindowAction(); + obj.id = buf.getIntLE(offset + 0); + int pos = offset + 4; + obj.action = WindowAction.deserialize(buf, pos); + pos += WindowAction.computeBytesConsumed(buf, pos); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/window/SetActiveAction.java b/src/com/hypixel/hytale/protocol/packets/window/SetActiveAction.java index fc40ef4a..2f8bbbb6 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/SetActiveAction.java +++ b/src/com/hypixel/hytale/protocol/packets/window/SetActiveAction.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.window; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -26,9 +27,13 @@ public class SetActiveAction extends WindowAction { @Nonnull public static SetActiveAction deserialize(@Nonnull ByteBuf buf, int offset) { - SetActiveAction obj = new SetActiveAction(); - obj.state = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SetActiveAction", 1, buf.readableBytes() - offset); + } else { + SetActiveAction obj = new SetActiveAction(); + obj.state = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/window/SortItemsAction.java b/src/com/hypixel/hytale/protocol/packets/window/SortItemsAction.java index 20b10963..3ba65afc 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/SortItemsAction.java +++ b/src/com/hypixel/hytale/protocol/packets/window/SortItemsAction.java @@ -1,75 +1,51 @@ package com.hypixel.hytale.protocol.packets.window; -import com.hypixel.hytale.protocol.SortType; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; -import java.util.Objects; import javax.annotation.Nonnull; public class SortItemsAction extends WindowAction { public static final int NULLABLE_BIT_FIELD_SIZE = 0; - public static final int FIXED_BLOCK_SIZE = 1; + public static final int FIXED_BLOCK_SIZE = 0; public static final int VARIABLE_FIELD_COUNT = 0; - public static final int VARIABLE_BLOCK_START = 1; - public static final int MAX_SIZE = 1; - @Nonnull - public SortType sortType = SortType.Name; - - public SortItemsAction() { - } - - public SortItemsAction(@Nonnull SortType sortType) { - this.sortType = sortType; - } - - public SortItemsAction(@Nonnull SortItemsAction other) { - this.sortType = other.sortType; - } + public static final int VARIABLE_BLOCK_START = 0; + public static final int MAX_SIZE = 0; @Nonnull public static SortItemsAction deserialize(@Nonnull ByteBuf buf, int offset) { - SortItemsAction obj = new SortItemsAction(); - obj.sortType = SortType.fromValue(buf.getByte(offset + 0)); - return obj; + return new SortItemsAction(); } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { - return 1; + return 0; } @Override public int serialize(@Nonnull ByteBuf buf) { int startPos = buf.writerIndex(); - buf.writeByte(this.sortType.getValue()); return buf.writerIndex() - startPos; } @Override public int computeSize() { - return 1; + return 0; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 1 ? ValidationResult.error("Buffer too small: expected at least 1 bytes") : ValidationResult.OK; + return buffer.readableBytes() - offset < 0 ? ValidationResult.error("Buffer too small: expected at least 0 bytes") : ValidationResult.OK; } public SortItemsAction clone() { - SortItemsAction copy = new SortItemsAction(); - copy.sortType = this.sortType; - return copy; + return new SortItemsAction(); } @Override public boolean equals(Object obj) { - if (this == obj) { - return true; - } else { - return obj instanceof SortItemsAction other ? Objects.equals(this.sortType, other.sortType) : false; - } + return this == obj ? true : obj instanceof SortItemsAction other; } @Override public int hashCode() { - return Objects.hash(this.sortType); + return 0; } } diff --git a/src/com/hypixel/hytale/protocol/packets/window/UpdateCategoryAction.java b/src/com/hypixel/hytale/protocol/packets/window/UpdateCategoryAction.java index 0cc379c5..902148a3 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/UpdateCategoryAction.java +++ b/src/com/hypixel/hytale/protocol/packets/window/UpdateCategoryAction.java @@ -34,24 +34,48 @@ public class UpdateCategoryAction extends WindowAction { @Nonnull public static UpdateCategoryAction deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateCategoryAction obj = new UpdateCategoryAction(); - int varPos0 = offset + 8 + buf.getIntLE(offset + 0); - int categoryLen = VarInt.peek(buf, varPos0); - if (categoryLen < 0) { - throw ProtocolException.negativeLength("Category", categoryLen); - } else if (categoryLen > 4096000) { - throw ProtocolException.stringTooLong("Category", categoryLen, 4096000); + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UpdateCategoryAction", 8, buf.readableBytes() - offset); } else { - obj.category = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - varPos0 = offset + 8 + buf.getIntLE(offset + 4); - categoryLen = VarInt.peek(buf, varPos0); - if (categoryLen < 0) { - throw ProtocolException.negativeLength("ItemCategory", categoryLen); - } else if (categoryLen > 4096000) { - throw ProtocolException.stringTooLong("ItemCategory", categoryLen, 4096000); + UpdateCategoryAction obj = new UpdateCategoryAction(); + int varPosBase0 = buf.getIntLE(offset + 0); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + int varPos0 = offset + 8 + varPosBase0; + int categoryLen = VarInt.peek(buf, varPos0); + if (categoryLen < 0) { + throw ProtocolException.invalidVarInt("Category"); + } else { + int categoryVarIntLen = VarInt.size(categoryLen); + if (categoryLen > 4096000) { + throw ProtocolException.stringTooLong("Category", categoryLen, 4096000); + } else if (varPos0 + categoryVarIntLen + categoryLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Category", varPos0 + categoryVarIntLen + categoryLen, buf.readableBytes()); + } else { + obj.category = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + varPos0 = offset + 8 + varPosBase0; + categoryLen = VarInt.peek(buf, varPos0); + if (categoryLen < 0) { + throw ProtocolException.invalidVarInt("ItemCategory"); + } else { + categoryVarIntLen = VarInt.size(categoryLen); + if (categoryLen > 4096000) { + throw ProtocolException.stringTooLong("ItemCategory", categoryLen, 4096000); + } else if (varPos0 + categoryVarIntLen + categoryLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ItemCategory", varPos0 + categoryVarIntLen + categoryLen, buf.readableBytes()); + } else { + obj.itemCategory = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("ItemCategory", varPosBase0, buf.readableBytes()); + } + } + } } else { - obj.itemCategory = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - return obj; + throw ProtocolException.invalidOffset("Category", varPosBase0, buf.readableBytes()); } } } @@ -59,22 +83,30 @@ public class UpdateCategoryAction extends WindowAction { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int maxEnd = 8; int fieldOffset0 = buf.getIntLE(offset + 0); - int pos0 = offset + 8 + fieldOffset0; - int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + int pos0 = offset + 8 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } - fieldOffset0 = buf.getIntLE(offset + 4); - pos0 = offset + 8 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } + fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + pos0 = offset + 8 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } - return maxEnd; + return maxEnd; + } else { + throw ProtocolException.invalidOffset("ItemCategory", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("Category", fieldOffset0, maxEnd); + } } @Override @@ -104,47 +136,39 @@ public class UpdateCategoryAction extends WindowAction { return ValidationResult.error("Buffer too small: expected at least 8 bytes"); } else { int categoryOffset = buffer.getIntLE(offset + 0); - if (categoryOffset < 0) { - return ValidationResult.error("Invalid offset for Category"); - } else { + if (categoryOffset >= 0 && categoryOffset <= buffer.writerIndex() - offset - 8) { int pos = offset + 8 + categoryOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Category"); + int categoryLen = VarInt.peek(buffer, pos); + if (categoryLen < 0) { + return ValidationResult.error("Invalid string length for Category"); + } else if (categoryLen > 4096000) { + return ValidationResult.error("Category exceeds max length 4096000"); } else { - int categoryLen = VarInt.peek(buffer, pos); - if (categoryLen < 0) { - return ValidationResult.error("Invalid string length for Category"); - } else if (categoryLen > 4096000) { - return ValidationResult.error("Category exceeds max length 4096000"); + pos += VarInt.size(categoryLen); + pos += categoryLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Category"); } else { - pos += VarInt.length(buffer, pos); - pos += categoryLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Category"); - } else { - categoryOffset = buffer.getIntLE(offset + 4); - if (categoryOffset < 0) { - return ValidationResult.error("Invalid offset for ItemCategory"); + categoryOffset = buffer.getIntLE(offset + 4); + if (categoryOffset >= 0 && categoryOffset <= buffer.writerIndex() - offset - 8) { + pos = offset + 8 + categoryOffset; + categoryLen = VarInt.peek(buffer, pos); + if (categoryLen < 0) { + return ValidationResult.error("Invalid string length for ItemCategory"); + } else if (categoryLen > 4096000) { + return ValidationResult.error("ItemCategory exceeds max length 4096000"); } else { - pos = offset + 8 + categoryOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ItemCategory"); - } else { - categoryLen = VarInt.peek(buffer, pos); - if (categoryLen < 0) { - return ValidationResult.error("Invalid string length for ItemCategory"); - } else if (categoryLen > 4096000) { - return ValidationResult.error("ItemCategory exceeds max length 4096000"); - } else { - pos += VarInt.length(buffer, pos); - pos += categoryLen; - return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading ItemCategory") : ValidationResult.OK; - } - } + pos += VarInt.size(categoryLen); + pos += categoryLen; + return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading ItemCategory") : ValidationResult.OK; } + } else { + return ValidationResult.error("Invalid offset for ItemCategory"); } } } + } else { + return ValidationResult.error("Invalid offset for Category"); } } } diff --git a/src/com/hypixel/hytale/protocol/packets/window/UpdateWindow.java b/src/com/hypixel/hytale/protocol/packets/window/UpdateWindow.java index 16dd4dcf..ca771119 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/UpdateWindow.java +++ b/src/com/hypixel/hytale/protocol/packets/window/UpdateWindow.java @@ -59,34 +59,58 @@ public class UpdateWindow implements Packet, ToClientPacket { @Nonnull public static UpdateWindow deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateWindow obj = new UpdateWindow(); - byte nullBits = buf.getByte(offset); - obj.id = buf.getIntLE(offset + 1); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 17 + buf.getIntLE(offset + 5); - int windowDataLen = VarInt.peek(buf, varPos0); - if (windowDataLen < 0) { - throw ProtocolException.negativeLength("WindowData", windowDataLen); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("UpdateWindow", 17, buf.readableBytes() - offset); + } else { + UpdateWindow obj = new UpdateWindow(); + byte nullBits = buf.getByte(offset); + obj.id = buf.getIntLE(offset + 1); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 5); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("WindowData", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int windowDataLen = VarInt.peek(buf, varPos0); + if (windowDataLen < 0) { + throw ProtocolException.invalidVarInt("WindowData"); + } + + int windowDataVarIntLen = VarInt.size(windowDataLen); + if (windowDataLen > 4096000) { + throw ProtocolException.stringTooLong("WindowData", windowDataLen, 4096000); + } + + if (varPos0 + windowDataVarIntLen + windowDataLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("WindowData", varPos0 + windowDataVarIntLen + windowDataLen, buf.readableBytes()); + } + + obj.windowData = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (windowDataLen > 4096000) { - throw ProtocolException.stringTooLong("WindowData", windowDataLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 9); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Inventory", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + obj.inventory = InventorySection.deserialize(buf, varPos1); } - obj.windowData = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 13); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ExtraResources", varPosBase2, buf.readableBytes()); + } - if ((nullBits & 2) != 0) { - int varPos1 = offset + 17 + buf.getIntLE(offset + 9); - obj.inventory = InventorySection.deserialize(buf, varPos1); - } + int varPos2 = offset + 17 + varPosBase2; + obj.extraResources = ExtraResources.deserialize(buf, varPos2); + } - if ((nullBits & 4) != 0) { - int varPos2 = offset + 17 + buf.getIntLE(offset + 13); - obj.extraResources = ExtraResources.deserialize(buf, varPos2); + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -94,9 +118,13 @@ public class UpdateWindow implements Packet, ToClientPacket { int maxEnd = 17; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 5); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("WindowData", fieldOffset0, maxEnd); + } + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -104,6 +132,10 @@ public class UpdateWindow implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 9); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("Inventory", fieldOffset1, maxEnd); + } + int pos1 = offset + 17 + fieldOffset1; pos1 += InventorySection.computeBytesConsumed(buf, pos1); if (pos1 - offset > maxEnd) { @@ -113,6 +145,10 @@ public class UpdateWindow implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 13); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ExtraResources", fieldOffset2, maxEnd); + } + int pos2 = offset + 17 + fieldOffset2; pos2 += ExtraResources.computeBytesConsumed(buf, pos2); if (pos2 - offset > maxEnd) { @@ -195,15 +231,11 @@ public class UpdateWindow implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int windowDataOffset = buffer.getIntLE(offset + 5); - if (windowDataOffset < 0) { + if (windowDataOffset < 0 || windowDataOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for WindowData"); } int pos = offset + 17 + windowDataOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for WindowData"); - } - int windowDataLen = VarInt.peek(buffer, pos); if (windowDataLen < 0) { return ValidationResult.error("Invalid string length for WindowData"); @@ -213,7 +245,7 @@ public class UpdateWindow implements Packet, ToClientPacket { return ValidationResult.error("WindowData exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(windowDataLen); pos += windowDataLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading WindowData"); @@ -222,15 +254,11 @@ public class UpdateWindow implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int inventoryOffset = buffer.getIntLE(offset + 9); - if (inventoryOffset < 0) { + if (inventoryOffset < 0 || inventoryOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for Inventory"); } int posx = offset + 17 + inventoryOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Inventory"); - } - ValidationResult inventoryResult = InventorySection.validateStructure(buffer, posx); if (!inventoryResult.isValid()) { return ValidationResult.error("Invalid Inventory: " + inventoryResult.error()); @@ -241,21 +269,17 @@ public class UpdateWindow implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int extraResourcesOffset = buffer.getIntLE(offset + 13); - if (extraResourcesOffset < 0) { + if (extraResourcesOffset < 0 || extraResourcesOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for ExtraResources"); } - int posxx = offset + 17 + extraResourcesOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ExtraResources"); - } - - ValidationResult extraResourcesResult = ExtraResources.validateStructure(buffer, posxx); + int posx = offset + 17 + extraResourcesOffset; + ValidationResult extraResourcesResult = ExtraResources.validateStructure(buffer, posx); if (!extraResourcesResult.isValid()) { return ValidationResult.error("Invalid ExtraResources: " + extraResourcesResult.error()); } - posxx += ExtraResources.computeBytesConsumed(buffer, posxx); + posx += ExtraResources.computeBytesConsumed(buffer, posx); } return ValidationResult.OK; diff --git a/src/com/hypixel/hytale/protocol/packets/window/WindowAction.java b/src/com/hypixel/hytale/protocol/packets/window/WindowAction.java index db3cee85..aecb7efa 100644 --- a/src/com/hypixel/hytale/protocol/packets/window/WindowAction.java +++ b/src/com/hypixel/hytale/protocol/packets/window/WindowAction.java @@ -12,7 +12,7 @@ public abstract class WindowAction { @Nonnull public static WindowAction deserialize(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return (WindowAction)(switch (typeId) { case 0 -> CraftRecipeAction.deserialize(buf, offset + typeIdLen); @@ -30,7 +30,7 @@ public abstract class WindowAction { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return typeIdLen + switch (typeId) { case 0 -> CraftRecipeAction.computeBytesConsumed(buf, offset + typeIdLen); @@ -87,7 +87,7 @@ public abstract class WindowAction { public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { int typeId = VarInt.peek(buffer, offset); - int typeIdLen = VarInt.length(buffer, offset); + int typeIdLen = VarInt.size(typeId); return switch (typeId) { case 0 -> CraftRecipeAction.validateStructure(buffer, offset + typeIdLen); diff --git a/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEvent2D.java b/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEvent2D.java index 444f5c07..9bec974d 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEvent2D.java +++ b/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEvent2D.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -52,12 +53,16 @@ public class PlaySoundEvent2D implements Packet, ToClientPacket { @Nonnull public static PlaySoundEvent2D deserialize(@Nonnull ByteBuf buf, int offset) { - PlaySoundEvent2D obj = new PlaySoundEvent2D(); - obj.soundEventIndex = buf.getIntLE(offset + 0); - obj.category = SoundCategory.fromValue(buf.getByte(offset + 4)); - obj.volumeModifier = buf.getFloatLE(offset + 5); - obj.pitchModifier = buf.getFloatLE(offset + 9); - return obj; + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("PlaySoundEvent2D", 13, buf.readableBytes() - offset); + } else { + PlaySoundEvent2D obj = new PlaySoundEvent2D(); + obj.soundEventIndex = buf.getIntLE(offset + 0); + obj.category = SoundCategory.fromValue(buf.getByte(offset + 4)); + obj.volumeModifier = buf.getFloatLE(offset + 5); + obj.pitchModifier = buf.getFloatLE(offset + 9); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,7 +83,12 @@ public class PlaySoundEvent2D implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 13 ? ValidationResult.error("Buffer too small: expected at least 13 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 13) { + return ValidationResult.error("Buffer too small: expected at least 13 bytes"); + } else { + int v = buffer.getByte(offset + 4) & 255; + return v >= 5 ? ValidationResult.error("Invalid SoundCategory value for Category") : ValidationResult.OK; + } } public PlaySoundEvent2D clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEvent3D.java b/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEvent3D.java index 60615cbb..24d06e3b 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEvent3D.java +++ b/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEvent3D.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -58,17 +59,21 @@ public class PlaySoundEvent3D implements Packet, ToClientPacket { @Nonnull public static PlaySoundEvent3D deserialize(@Nonnull ByteBuf buf, int offset) { - PlaySoundEvent3D obj = new PlaySoundEvent3D(); - byte nullBits = buf.getByte(offset); - obj.soundEventIndex = buf.getIntLE(offset + 1); - obj.category = SoundCategory.fromValue(buf.getByte(offset + 5)); - if ((nullBits & 1) != 0) { - obj.position = Position.deserialize(buf, offset + 6); - } + if (buf.readableBytes() - offset < 38) { + throw ProtocolException.bufferTooSmall("PlaySoundEvent3D", 38, buf.readableBytes() - offset); + } else { + PlaySoundEvent3D obj = new PlaySoundEvent3D(); + byte nullBits = buf.getByte(offset); + obj.soundEventIndex = buf.getIntLE(offset + 1); + obj.category = SoundCategory.fromValue(buf.getByte(offset + 5)); + if ((nullBits & 1) != 0) { + obj.position = Position.deserialize(buf, offset + 6); + } - obj.volumeModifier = buf.getFloatLE(offset + 30); - obj.pitchModifier = buf.getFloatLE(offset + 34); - return obj; + obj.volumeModifier = buf.getFloatLE(offset + 30); + obj.pitchModifier = buf.getFloatLE(offset + 34); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -101,7 +106,13 @@ public class PlaySoundEvent3D implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 38 ? ValidationResult.error("Buffer too small: expected at least 38 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 38) { + return ValidationResult.error("Buffer too small: expected at least 38 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 5) & 255; + return v >= 5 ? ValidationResult.error("Invalid SoundCategory value for Category") : ValidationResult.OK; + } } public PlaySoundEvent3D clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEventEntity.java b/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEventEntity.java index 0e235265..12fe6bbf 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEventEntity.java +++ b/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEventEntity.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,12 +51,16 @@ public class PlaySoundEventEntity implements Packet, ToClientPacket { @Nonnull public static PlaySoundEventEntity deserialize(@Nonnull ByteBuf buf, int offset) { - PlaySoundEventEntity obj = new PlaySoundEventEntity(); - obj.soundEventIndex = buf.getIntLE(offset + 0); - obj.networkId = buf.getIntLE(offset + 4); - obj.volumeModifier = buf.getFloatLE(offset + 8); - obj.pitchModifier = buf.getFloatLE(offset + 12); - return obj; + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("PlaySoundEventEntity", 16, buf.readableBytes() - offset); + } else { + PlaySoundEventEntity obj = new PlaySoundEventEntity(); + obj.soundEventIndex = buf.getIntLE(offset + 0); + obj.networkId = buf.getIntLE(offset + 4); + obj.volumeModifier = buf.getFloatLE(offset + 8); + obj.pitchModifier = buf.getFloatLE(offset + 12); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEventLocalPlayer.java b/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEventLocalPlayer.java new file mode 100644 index 00000000..cba73082 --- /dev/null +++ b/src/com/hypixel/hytale/protocol/packets/world/PlaySoundEventLocalPlayer.java @@ -0,0 +1,130 @@ +package com.hypixel.hytale.protocol.packets.world; + +import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.SoundCategory; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.io.ValidationResult; +import io.netty.buffer.ByteBuf; +import java.util.Objects; +import javax.annotation.Nonnull; + +public class PlaySoundEventLocalPlayer implements Packet, ToClientPacket { + public static final int PACKET_ID = 362; + public static final boolean IS_COMPRESSED = false; + public static final int NULLABLE_BIT_FIELD_SIZE = 0; + public static final int FIXED_BLOCK_SIZE = 17; + public static final int VARIABLE_FIELD_COUNT = 0; + public static final int VARIABLE_BLOCK_START = 17; + public static final int MAX_SIZE = 17; + public int localSoundEventIndex; + public int worldSoundEventIndex; + @Nonnull + public SoundCategory category = SoundCategory.Music; + public float volumeModifier; + public float pitchModifier; + + @Override + public int getId() { + return 362; + } + + @Override + public NetworkChannel getChannel() { + return NetworkChannel.Default; + } + + public PlaySoundEventLocalPlayer() { + } + + public PlaySoundEventLocalPlayer( + int localSoundEventIndex, int worldSoundEventIndex, @Nonnull SoundCategory category, float volumeModifier, float pitchModifier + ) { + this.localSoundEventIndex = localSoundEventIndex; + this.worldSoundEventIndex = worldSoundEventIndex; + this.category = category; + this.volumeModifier = volumeModifier; + this.pitchModifier = pitchModifier; + } + + public PlaySoundEventLocalPlayer(@Nonnull PlaySoundEventLocalPlayer other) { + this.localSoundEventIndex = other.localSoundEventIndex; + this.worldSoundEventIndex = other.worldSoundEventIndex; + this.category = other.category; + this.volumeModifier = other.volumeModifier; + this.pitchModifier = other.pitchModifier; + } + + @Nonnull + public static PlaySoundEventLocalPlayer deserialize(@Nonnull ByteBuf buf, int offset) { + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("PlaySoundEventLocalPlayer", 17, buf.readableBytes() - offset); + } else { + PlaySoundEventLocalPlayer obj = new PlaySoundEventLocalPlayer(); + obj.localSoundEventIndex = buf.getIntLE(offset + 0); + obj.worldSoundEventIndex = buf.getIntLE(offset + 4); + obj.category = SoundCategory.fromValue(buf.getByte(offset + 8)); + obj.volumeModifier = buf.getFloatLE(offset + 9); + obj.pitchModifier = buf.getFloatLE(offset + 13); + return obj; + } + } + + public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { + return 17; + } + + @Override + public void serialize(@Nonnull ByteBuf buf) { + buf.writeIntLE(this.localSoundEventIndex); + buf.writeIntLE(this.worldSoundEventIndex); + buf.writeByte(this.category.getValue()); + buf.writeFloatLE(this.volumeModifier); + buf.writeFloatLE(this.pitchModifier); + } + + @Override + public int computeSize() { + return 17; + } + + public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { + if (buffer.readableBytes() - offset < 17) { + return ValidationResult.error("Buffer too small: expected at least 17 bytes"); + } else { + int v = buffer.getByte(offset + 8) & 255; + return v >= 5 ? ValidationResult.error("Invalid SoundCategory value for Category") : ValidationResult.OK; + } + } + + public PlaySoundEventLocalPlayer clone() { + PlaySoundEventLocalPlayer copy = new PlaySoundEventLocalPlayer(); + copy.localSoundEventIndex = this.localSoundEventIndex; + copy.worldSoundEventIndex = this.worldSoundEventIndex; + copy.category = this.category; + copy.volumeModifier = this.volumeModifier; + copy.pitchModifier = this.pitchModifier; + return copy; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else { + return !(obj instanceof PlaySoundEventLocalPlayer other) + ? false + : this.localSoundEventIndex == other.localSoundEventIndex + && this.worldSoundEventIndex == other.worldSoundEventIndex + && Objects.equals(this.category, other.category) + && this.volumeModifier == other.volumeModifier + && this.pitchModifier == other.pitchModifier; + } + } + + @Override + public int hashCode() { + return Objects.hash(this.localSoundEventIndex, this.worldSoundEventIndex, this.category, this.volumeModifier, this.pitchModifier); + } +} diff --git a/src/com/hypixel/hytale/protocol/packets/world/ServerSetBlock.java b/src/com/hypixel/hytale/protocol/packets/world/ServerSetBlock.java index c14cbaf1..8a48aa56 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/ServerSetBlock.java +++ b/src/com/hypixel/hytale/protocol/packets/world/ServerSetBlock.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -56,14 +57,18 @@ public class ServerSetBlock implements Packet, ToClientPacket { @Nonnull public static ServerSetBlock deserialize(@Nonnull ByteBuf buf, int offset) { - ServerSetBlock obj = new ServerSetBlock(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - obj.blockId = buf.getIntLE(offset + 12); - obj.filler = buf.getShortLE(offset + 16); - obj.rotation = buf.getByte(offset + 18); - return obj; + if (buf.readableBytes() - offset < 19) { + throw ProtocolException.bufferTooSmall("ServerSetBlock", 19, buf.readableBytes() - offset); + } else { + ServerSetBlock obj = new ServerSetBlock(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + obj.blockId = buf.getIntLE(offset + 12); + obj.filler = buf.getShortLE(offset + 16); + obj.rotation = buf.getByte(offset + 18); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/ServerSetBlocks.java b/src/com/hypixel/hytale/protocol/packets/world/ServerSetBlocks.java index aec276e8..6470c95f 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/ServerSetBlocks.java +++ b/src/com/hypixel/hytale/protocol/packets/world/ServerSetBlocks.java @@ -53,30 +53,34 @@ public class ServerSetBlocks implements Packet, ToClientPacket { @Nonnull public static ServerSetBlocks deserialize(@Nonnull ByteBuf buf, int offset) { - ServerSetBlocks obj = new ServerSetBlocks(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - int pos = offset + 12; - int cmdsCount = VarInt.peek(buf, pos); - if (cmdsCount < 0) { - throw ProtocolException.negativeLength("Cmds", cmdsCount); - } else if (cmdsCount > 4096000) { - throw ProtocolException.arrayTooLong("Cmds", cmdsCount, 4096000); + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("ServerSetBlocks", 12, buf.readableBytes() - offset); } else { - int cmdsVarLen = VarInt.size(cmdsCount); - if (pos + cmdsVarLen + cmdsCount * 9L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Cmds", pos + cmdsVarLen + cmdsCount * 9, buf.readableBytes()); + ServerSetBlocks obj = new ServerSetBlocks(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + int pos = offset + 12; + int cmdsCount = VarInt.peek(buf, pos); + if (cmdsCount < 0) { + throw ProtocolException.invalidVarInt("Cmds"); } else { - pos += cmdsVarLen; - obj.cmds = new SetBlockCmd[cmdsCount]; + int cmdsVarLen = VarInt.size(cmdsCount); + if (cmdsCount > 4096000) { + throw ProtocolException.arrayTooLong("Cmds", cmdsCount, 4096000); + } else if (pos + cmdsVarLen + cmdsCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Cmds", pos + cmdsVarLen + cmdsCount * 9, buf.readableBytes()); + } else { + pos += cmdsVarLen; + obj.cmds = new SetBlockCmd[cmdsCount]; - for (int i = 0; i < cmdsCount; i++) { - obj.cmds[i] = SetBlockCmd.deserialize(buf, pos); - pos += SetBlockCmd.computeBytesConsumed(buf, pos); + for (int i = 0; i < cmdsCount; i++) { + obj.cmds[i] = SetBlockCmd.deserialize(buf, pos); + pos += SetBlockCmd.computeBytesConsumed(buf, pos); + } + + return obj; } - - return obj; } } } @@ -84,7 +88,7 @@ public class ServerSetBlocks implements Packet, ToClientPacket { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 12; int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += SetBlockCmd.computeBytesConsumed(buf, pos); @@ -126,7 +130,7 @@ public class ServerSetBlocks implements Packet, ToClientPacket { } else if (cmdsCount > 4096000) { return ValidationResult.error("Cmds exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(cmdsCount); pos += cmdsCount * 9; return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Cmds") : ValidationResult.OK; } diff --git a/src/com/hypixel/hytale/protocol/packets/world/ServerSetFluid.java b/src/com/hypixel/hytale/protocol/packets/world/ServerSetFluid.java index 7b98b76d..da71ce99 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/ServerSetFluid.java +++ b/src/com/hypixel/hytale/protocol/packets/world/ServerSetFluid.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -53,13 +54,17 @@ public class ServerSetFluid implements Packet, ToClientPacket { @Nonnull public static ServerSetFluid deserialize(@Nonnull ByteBuf buf, int offset) { - ServerSetFluid obj = new ServerSetFluid(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - obj.fluidId = buf.getIntLE(offset + 12); - obj.fluidLevel = buf.getByte(offset + 16); - return obj; + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("ServerSetFluid", 17, buf.readableBytes() - offset); + } else { + ServerSetFluid obj = new ServerSetFluid(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + obj.fluidId = buf.getIntLE(offset + 12); + obj.fluidLevel = buf.getByte(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/ServerSetFluids.java b/src/com/hypixel/hytale/protocol/packets/world/ServerSetFluids.java index 1fb83c95..caf89be2 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/ServerSetFluids.java +++ b/src/com/hypixel/hytale/protocol/packets/world/ServerSetFluids.java @@ -53,30 +53,34 @@ public class ServerSetFluids implements Packet, ToClientPacket { @Nonnull public static ServerSetFluids deserialize(@Nonnull ByteBuf buf, int offset) { - ServerSetFluids obj = new ServerSetFluids(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - obj.z = buf.getIntLE(offset + 8); - int pos = offset + 12; - int cmdsCount = VarInt.peek(buf, pos); - if (cmdsCount < 0) { - throw ProtocolException.negativeLength("Cmds", cmdsCount); - } else if (cmdsCount > 4096000) { - throw ProtocolException.arrayTooLong("Cmds", cmdsCount, 4096000); + if (buf.readableBytes() - offset < 12) { + throw ProtocolException.bufferTooSmall("ServerSetFluids", 12, buf.readableBytes() - offset); } else { - int cmdsVarLen = VarInt.size(cmdsCount); - if (pos + cmdsVarLen + cmdsCount * 7L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Cmds", pos + cmdsVarLen + cmdsCount * 7, buf.readableBytes()); + ServerSetFluids obj = new ServerSetFluids(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + obj.z = buf.getIntLE(offset + 8); + int pos = offset + 12; + int cmdsCount = VarInt.peek(buf, pos); + if (cmdsCount < 0) { + throw ProtocolException.invalidVarInt("Cmds"); } else { - pos += cmdsVarLen; - obj.cmds = new SetFluidCmd[cmdsCount]; + int cmdsVarLen = VarInt.size(cmdsCount); + if (cmdsCount > 4096000) { + throw ProtocolException.arrayTooLong("Cmds", cmdsCount, 4096000); + } else if (pos + cmdsVarLen + cmdsCount * 7L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Cmds", pos + cmdsVarLen + cmdsCount * 7, buf.readableBytes()); + } else { + pos += cmdsVarLen; + obj.cmds = new SetFluidCmd[cmdsCount]; - for (int i = 0; i < cmdsCount; i++) { - obj.cmds[i] = SetFluidCmd.deserialize(buf, pos); - pos += SetFluidCmd.computeBytesConsumed(buf, pos); + for (int i = 0; i < cmdsCount; i++) { + obj.cmds[i] = SetFluidCmd.deserialize(buf, pos); + pos += SetFluidCmd.computeBytesConsumed(buf, pos); + } + + return obj; } - - return obj; } } } @@ -84,7 +88,7 @@ public class ServerSetFluids implements Packet, ToClientPacket { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int pos = offset + 12; int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos += SetFluidCmd.computeBytesConsumed(buf, pos); @@ -126,7 +130,7 @@ public class ServerSetFluids implements Packet, ToClientPacket { } else if (cmdsCount > 4096000) { return ValidationResult.error("Cmds exceeds max length 4096000"); } else { - pos += VarInt.length(buffer, pos); + pos += VarInt.size(cmdsCount); pos += cmdsCount * 7; return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Cmds") : ValidationResult.OK; } diff --git a/src/com/hypixel/hytale/protocol/packets/world/ServerSetPaused.java b/src/com/hypixel/hytale/protocol/packets/world/ServerSetPaused.java index 7d9de493..bee88b64 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/ServerSetPaused.java +++ b/src/com/hypixel/hytale/protocol/packets/world/ServerSetPaused.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class ServerSetPaused implements Packet, ToClientPacket { @Nonnull public static ServerSetPaused deserialize(@Nonnull ByteBuf buf, int offset) { - ServerSetPaused obj = new ServerSetPaused(); - obj.paused = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("ServerSetPaused", 1, buf.readableBytes() - offset); + } else { + ServerSetPaused obj = new ServerSetPaused(); + obj.paused = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/SetBlockCmd.java b/src/com/hypixel/hytale/protocol/packets/world/SetBlockCmd.java index a784eca9..21cd1cf8 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SetBlockCmd.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SetBlockCmd.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.world; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -35,12 +36,16 @@ public class SetBlockCmd { @Nonnull public static SetBlockCmd deserialize(@Nonnull ByteBuf buf, int offset) { - SetBlockCmd obj = new SetBlockCmd(); - obj.index = buf.getShortLE(offset + 0); - obj.blockId = buf.getIntLE(offset + 2); - obj.filler = buf.getShortLE(offset + 6); - obj.rotation = buf.getByte(offset + 8); - return obj; + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("SetBlockCmd", 9, buf.readableBytes() - offset); + } else { + SetBlockCmd obj = new SetBlockCmd(); + obj.index = buf.getShortLE(offset + 0); + obj.blockId = buf.getIntLE(offset + 2); + obj.filler = buf.getShortLE(offset + 6); + obj.rotation = buf.getByte(offset + 8); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/SetChunk.java b/src/com/hypixel/hytale/protocol/packets/world/SetChunk.java index 3ed1b81d..4e36d592 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SetChunk.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SetChunk.java @@ -62,81 +62,100 @@ public class SetChunk implements Packet, ToClientPacket { @Nonnull public static SetChunk deserialize(@Nonnull ByteBuf buf, int offset) { - SetChunk obj = new SetChunk(); - byte nullBits = buf.getByte(offset); - obj.x = buf.getIntLE(offset + 1); - obj.y = buf.getIntLE(offset + 5); - obj.z = buf.getIntLE(offset + 9); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 25 + buf.getIntLE(offset + 13); - int localLightCount = VarInt.peek(buf, varPos0); - if (localLightCount < 0) { - throw ProtocolException.negativeLength("LocalLight", localLightCount); + if (buf.readableBytes() - offset < 25) { + throw ProtocolException.bufferTooSmall("SetChunk", 25, buf.readableBytes() - offset); + } else { + SetChunk obj = new SetChunk(); + byte nullBits = buf.getByte(offset); + obj.x = buf.getIntLE(offset + 1); + obj.y = buf.getIntLE(offset + 5); + obj.z = buf.getIntLE(offset + 9); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 13); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("LocalLight", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 25 + varPosBase0; + int localLightCount = VarInt.peek(buf, varPos0); + if (localLightCount < 0) { + throw ProtocolException.invalidVarInt("LocalLight"); + } + + int varIntLen = VarInt.size(localLightCount); + if (localLightCount > 4096000) { + throw ProtocolException.arrayTooLong("LocalLight", localLightCount, 4096000); + } + + if (varPos0 + varIntLen + localLightCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("LocalLight", varPos0 + varIntLen + localLightCount * 1, buf.readableBytes()); + } + + obj.localLight = new byte[localLightCount]; + + for (int i = 0; i < localLightCount; i++) { + obj.localLight[i] = buf.getByte(varPos0 + varIntLen + i * 1); + } } - if (localLightCount > 4096000) { - throw ProtocolException.arrayTooLong("LocalLight", localLightCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 17); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("GlobalLight", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 25 + varPosBase1; + int globalLightCount = VarInt.peek(buf, varPos1); + if (globalLightCount < 0) { + throw ProtocolException.invalidVarInt("GlobalLight"); + } + + int varIntLenx = VarInt.size(globalLightCount); + if (globalLightCount > 4096000) { + throw ProtocolException.arrayTooLong("GlobalLight", globalLightCount, 4096000); + } + + if (varPos1 + varIntLenx + globalLightCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("GlobalLight", varPos1 + varIntLenx + globalLightCount * 1, buf.readableBytes()); + } + + obj.globalLight = new byte[globalLightCount]; + + for (int i = 0; i < globalLightCount; i++) { + obj.globalLight[i] = buf.getByte(varPos1 + varIntLenx + i * 1); + } } - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + localLightCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("LocalLight", varPos0 + varIntLen + localLightCount * 1, buf.readableBytes()); + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 21); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Data", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 25 + varPosBase2; + int dataCount = VarInt.peek(buf, varPos2); + if (dataCount < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int varIntLenxx = VarInt.size(dataCount); + if (dataCount > 4096000) { + throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); + } + + if (varPos2 + varIntLenxx + dataCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", varPos2 + varIntLenxx + dataCount * 1, buf.readableBytes()); + } + + obj.data = new byte[dataCount]; + + for (int i = 0; i < dataCount; i++) { + obj.data[i] = buf.getByte(varPos2 + varIntLenxx + i * 1); + } } - obj.localLight = new byte[localLightCount]; - - for (int i = 0; i < localLightCount; i++) { - obj.localLight[i] = buf.getByte(varPos0 + varIntLen + i * 1); - } + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 25 + buf.getIntLE(offset + 17); - int globalLightCount = VarInt.peek(buf, varPos1); - if (globalLightCount < 0) { - throw ProtocolException.negativeLength("GlobalLight", globalLightCount); - } - - if (globalLightCount > 4096000) { - throw ProtocolException.arrayTooLong("GlobalLight", globalLightCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + globalLightCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("GlobalLight", varPos1 + varIntLen + globalLightCount * 1, buf.readableBytes()); - } - - obj.globalLight = new byte[globalLightCount]; - - for (int i = 0; i < globalLightCount; i++) { - obj.globalLight[i] = buf.getByte(varPos1 + varIntLen + i * 1); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 25 + buf.getIntLE(offset + 21); - int dataCount = VarInt.peek(buf, varPos2); - if (dataCount < 0) { - throw ProtocolException.negativeLength("Data", dataCount); - } - - if (dataCount > 4096000) { - throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + dataCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Data", varPos2 + varIntLen + dataCount * 1, buf.readableBytes()); - } - - obj.data = new byte[dataCount]; - - for (int i = 0; i < dataCount; i++) { - obj.data[i] = buf.getByte(varPos2 + varIntLen + i * 1); - } - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -144,9 +163,13 @@ public class SetChunk implements Packet, ToClientPacket { int maxEnd = 25; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 13); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("LocalLight", fieldOffset0, maxEnd); + } + int pos0 = offset + 25 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + arrLen * 1; + pos0 += VarInt.size(arrLen) + arrLen * 1; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -154,9 +177,13 @@ public class SetChunk implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 17); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("GlobalLight", fieldOffset1, maxEnd); + } + int pos1 = offset + 25 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + arrLen * 1; + pos1 += VarInt.size(arrLen) + arrLen * 1; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -164,9 +191,13 @@ public class SetChunk implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 21); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 25) { + throw ProtocolException.invalidOffset("Data", fieldOffset2, maxEnd); + } + int pos2 = offset + 25 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + arrLen * 1; + pos2 += VarInt.size(arrLen) + arrLen * 1; if (pos2 - offset > maxEnd) { maxEnd = pos2 - offset; } @@ -273,15 +304,11 @@ public class SetChunk implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int localLightOffset = buffer.getIntLE(offset + 13); - if (localLightOffset < 0) { + if (localLightOffset < 0 || localLightOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for LocalLight"); } int pos = offset + 25 + localLightOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for LocalLight"); - } - int localLightCount = VarInt.peek(buffer, pos); if (localLightCount < 0) { return ValidationResult.error("Invalid array count for LocalLight"); @@ -291,7 +318,7 @@ public class SetChunk implements Packet, ToClientPacket { return ValidationResult.error("LocalLight exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(localLightCount); pos += localLightCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading LocalLight"); @@ -300,15 +327,11 @@ public class SetChunk implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int globalLightOffset = buffer.getIntLE(offset + 17); - if (globalLightOffset < 0) { + if (globalLightOffset < 0 || globalLightOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for GlobalLight"); } int posx = offset + 25 + globalLightOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for GlobalLight"); - } - int globalLightCount = VarInt.peek(buffer, posx); if (globalLightCount < 0) { return ValidationResult.error("Invalid array count for GlobalLight"); @@ -318,7 +341,7 @@ public class SetChunk implements Packet, ToClientPacket { return ValidationResult.error("GlobalLight exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(globalLightCount); posx += globalLightCount * 1; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading GlobalLight"); @@ -327,15 +350,11 @@ public class SetChunk implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int dataOffset = buffer.getIntLE(offset + 21); - if (dataOffset < 0) { + if (dataOffset < 0 || dataOffset > buffer.writerIndex() - offset - 25) { return ValidationResult.error("Invalid offset for Data"); } int posxx = offset + 25 + dataOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Data"); - } - int dataCount = VarInt.peek(buffer, posxx); if (dataCount < 0) { return ValidationResult.error("Invalid array count for Data"); @@ -345,7 +364,7 @@ public class SetChunk implements Packet, ToClientPacket { return ValidationResult.error("Data exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(dataCount); posxx += dataCount * 1; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Data"); diff --git a/src/com/hypixel/hytale/protocol/packets/world/SetChunkEnvironments.java b/src/com/hypixel/hytale/protocol/packets/world/SetChunkEnvironments.java index a65c7085..74981b98 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SetChunkEnvironments.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SetChunkEnvironments.java @@ -51,37 +51,41 @@ public class SetChunkEnvironments implements Packet, ToClientPacket { @Nonnull public static SetChunkEnvironments deserialize(@Nonnull ByteBuf buf, int offset) { - SetChunkEnvironments obj = new SetChunkEnvironments(); - byte nullBits = buf.getByte(offset); - obj.x = buf.getIntLE(offset + 1); - obj.z = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - int environmentsCount = VarInt.peek(buf, pos); - if (environmentsCount < 0) { - throw ProtocolException.negativeLength("Environments", environmentsCount); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("SetChunkEnvironments", 9, buf.readableBytes() - offset); + } else { + SetChunkEnvironments obj = new SetChunkEnvironments(); + byte nullBits = buf.getByte(offset); + obj.x = buf.getIntLE(offset + 1); + obj.z = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int environmentsCount = VarInt.peek(buf, pos); + if (environmentsCount < 0) { + throw ProtocolException.invalidVarInt("Environments"); + } + + int environmentsVarLen = VarInt.size(environmentsCount); + if (environmentsCount > 4096000) { + throw ProtocolException.arrayTooLong("Environments", environmentsCount, 4096000); + } + + if (pos + environmentsVarLen + environmentsCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Environments", pos + environmentsVarLen + environmentsCount * 1, buf.readableBytes()); + } + + pos += environmentsVarLen; + obj.environments = new byte[environmentsCount]; + + for (int i = 0; i < environmentsCount; i++) { + obj.environments[i] = buf.getByte(pos + i * 1); + } + + pos += environmentsCount * 1; } - if (environmentsCount > 4096000) { - throw ProtocolException.arrayTooLong("Environments", environmentsCount, 4096000); - } - - int environmentsVarLen = VarInt.size(environmentsCount); - if (pos + environmentsVarLen + environmentsCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Environments", pos + environmentsVarLen + environmentsCount * 1, buf.readableBytes()); - } - - pos += environmentsVarLen; - obj.environments = new byte[environmentsCount]; - - for (int i = 0; i < environmentsCount; i++) { - obj.environments[i] = buf.getByte(pos + i * 1); - } - - pos += environmentsCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -89,7 +93,7 @@ public class SetChunkEnvironments implements Packet, ToClientPacket { int pos = offset + 9; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -144,7 +148,7 @@ public class SetChunkEnvironments implements Packet, ToClientPacket { return ValidationResult.error("Environments exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(environmentsCount); pos += environmentsCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Environments"); diff --git a/src/com/hypixel/hytale/protocol/packets/world/SetChunkHeightmap.java b/src/com/hypixel/hytale/protocol/packets/world/SetChunkHeightmap.java index 7a3ca9ae..98be190b 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SetChunkHeightmap.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SetChunkHeightmap.java @@ -51,37 +51,41 @@ public class SetChunkHeightmap implements Packet, ToClientPacket { @Nonnull public static SetChunkHeightmap deserialize(@Nonnull ByteBuf buf, int offset) { - SetChunkHeightmap obj = new SetChunkHeightmap(); - byte nullBits = buf.getByte(offset); - obj.x = buf.getIntLE(offset + 1); - obj.z = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - int heightmapCount = VarInt.peek(buf, pos); - if (heightmapCount < 0) { - throw ProtocolException.negativeLength("Heightmap", heightmapCount); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("SetChunkHeightmap", 9, buf.readableBytes() - offset); + } else { + SetChunkHeightmap obj = new SetChunkHeightmap(); + byte nullBits = buf.getByte(offset); + obj.x = buf.getIntLE(offset + 1); + obj.z = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int heightmapCount = VarInt.peek(buf, pos); + if (heightmapCount < 0) { + throw ProtocolException.invalidVarInt("Heightmap"); + } + + int heightmapVarLen = VarInt.size(heightmapCount); + if (heightmapCount > 4096000) { + throw ProtocolException.arrayTooLong("Heightmap", heightmapCount, 4096000); + } + + if (pos + heightmapVarLen + heightmapCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Heightmap", pos + heightmapVarLen + heightmapCount * 1, buf.readableBytes()); + } + + pos += heightmapVarLen; + obj.heightmap = new byte[heightmapCount]; + + for (int i = 0; i < heightmapCount; i++) { + obj.heightmap[i] = buf.getByte(pos + i * 1); + } + + pos += heightmapCount * 1; } - if (heightmapCount > 4096000) { - throw ProtocolException.arrayTooLong("Heightmap", heightmapCount, 4096000); - } - - int heightmapVarLen = VarInt.size(heightmapCount); - if (pos + heightmapVarLen + heightmapCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Heightmap", pos + heightmapVarLen + heightmapCount * 1, buf.readableBytes()); - } - - pos += heightmapVarLen; - obj.heightmap = new byte[heightmapCount]; - - for (int i = 0; i < heightmapCount; i++) { - obj.heightmap[i] = buf.getByte(pos + i * 1); - } - - pos += heightmapCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -89,7 +93,7 @@ public class SetChunkHeightmap implements Packet, ToClientPacket { int pos = offset + 9; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -144,7 +148,7 @@ public class SetChunkHeightmap implements Packet, ToClientPacket { return ValidationResult.error("Heightmap exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(heightmapCount); pos += heightmapCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Heightmap"); diff --git a/src/com/hypixel/hytale/protocol/packets/world/SetChunkTintmap.java b/src/com/hypixel/hytale/protocol/packets/world/SetChunkTintmap.java index 9a8526c6..4573750d 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SetChunkTintmap.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SetChunkTintmap.java @@ -51,37 +51,41 @@ public class SetChunkTintmap implements Packet, ToClientPacket { @Nonnull public static SetChunkTintmap deserialize(@Nonnull ByteBuf buf, int offset) { - SetChunkTintmap obj = new SetChunkTintmap(); - byte nullBits = buf.getByte(offset); - obj.x = buf.getIntLE(offset + 1); - obj.z = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - int tintmapCount = VarInt.peek(buf, pos); - if (tintmapCount < 0) { - throw ProtocolException.negativeLength("Tintmap", tintmapCount); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("SetChunkTintmap", 9, buf.readableBytes() - offset); + } else { + SetChunkTintmap obj = new SetChunkTintmap(); + byte nullBits = buf.getByte(offset); + obj.x = buf.getIntLE(offset + 1); + obj.z = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int tintmapCount = VarInt.peek(buf, pos); + if (tintmapCount < 0) { + throw ProtocolException.invalidVarInt("Tintmap"); + } + + int tintmapVarLen = VarInt.size(tintmapCount); + if (tintmapCount > 4096000) { + throw ProtocolException.arrayTooLong("Tintmap", tintmapCount, 4096000); + } + + if (pos + tintmapVarLen + tintmapCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Tintmap", pos + tintmapVarLen + tintmapCount * 1, buf.readableBytes()); + } + + pos += tintmapVarLen; + obj.tintmap = new byte[tintmapCount]; + + for (int i = 0; i < tintmapCount; i++) { + obj.tintmap[i] = buf.getByte(pos + i * 1); + } + + pos += tintmapCount * 1; } - if (tintmapCount > 4096000) { - throw ProtocolException.arrayTooLong("Tintmap", tintmapCount, 4096000); - } - - int tintmapVarLen = VarInt.size(tintmapCount); - if (pos + tintmapVarLen + tintmapCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Tintmap", pos + tintmapVarLen + tintmapCount * 1, buf.readableBytes()); - } - - pos += tintmapVarLen; - obj.tintmap = new byte[tintmapCount]; - - for (int i = 0; i < tintmapCount; i++) { - obj.tintmap[i] = buf.getByte(pos + i * 1); - } - - pos += tintmapCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -89,7 +93,7 @@ public class SetChunkTintmap implements Packet, ToClientPacket { int pos = offset + 9; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -144,7 +148,7 @@ public class SetChunkTintmap implements Packet, ToClientPacket { return ValidationResult.error("Tintmap exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(tintmapCount); pos += tintmapCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Tintmap"); diff --git a/src/com/hypixel/hytale/protocol/packets/world/SetFluidCmd.java b/src/com/hypixel/hytale/protocol/packets/world/SetFluidCmd.java index 252cd78c..6b211fca 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SetFluidCmd.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SetFluidCmd.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.world; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -32,11 +33,15 @@ public class SetFluidCmd { @Nonnull public static SetFluidCmd deserialize(@Nonnull ByteBuf buf, int offset) { - SetFluidCmd obj = new SetFluidCmd(); - obj.index = buf.getShortLE(offset + 0); - obj.fluidId = buf.getIntLE(offset + 2); - obj.fluidLevel = buf.getByte(offset + 6); - return obj; + if (buf.readableBytes() - offset < 7) { + throw ProtocolException.bufferTooSmall("SetFluidCmd", 7, buf.readableBytes() - offset); + } else { + SetFluidCmd obj = new SetFluidCmd(); + obj.index = buf.getShortLE(offset + 0); + obj.fluidId = buf.getIntLE(offset + 2); + obj.fluidLevel = buf.getByte(offset + 6); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/SetFluids.java b/src/com/hypixel/hytale/protocol/packets/world/SetFluids.java index 70127003..e9f18c0d 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SetFluids.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SetFluids.java @@ -54,38 +54,42 @@ public class SetFluids implements Packet, ToClientPacket { @Nonnull public static SetFluids deserialize(@Nonnull ByteBuf buf, int offset) { - SetFluids obj = new SetFluids(); - byte nullBits = buf.getByte(offset); - obj.x = buf.getIntLE(offset + 1); - obj.y = buf.getIntLE(offset + 5); - obj.z = buf.getIntLE(offset + 9); - int pos = offset + 13; - if ((nullBits & 1) != 0) { - int dataCount = VarInt.peek(buf, pos); - if (dataCount < 0) { - throw ProtocolException.negativeLength("Data", dataCount); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("SetFluids", 13, buf.readableBytes() - offset); + } else { + SetFluids obj = new SetFluids(); + byte nullBits = buf.getByte(offset); + obj.x = buf.getIntLE(offset + 1); + obj.y = buf.getIntLE(offset + 5); + obj.z = buf.getIntLE(offset + 9); + int pos = offset + 13; + if ((nullBits & 1) != 0) { + int dataCount = VarInt.peek(buf, pos); + if (dataCount < 0) { + throw ProtocolException.invalidVarInt("Data"); + } + + int dataVarLen = VarInt.size(dataCount); + if (dataCount > 4096000) { + throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); + } + + if (pos + dataVarLen + dataCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Data", pos + dataVarLen + dataCount * 1, buf.readableBytes()); + } + + pos += dataVarLen; + obj.data = new byte[dataCount]; + + for (int i = 0; i < dataCount; i++) { + obj.data[i] = buf.getByte(pos + i * 1); + } + + pos += dataCount * 1; } - if (dataCount > 4096000) { - throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); - } - - int dataVarLen = VarInt.size(dataCount); - if (pos + dataVarLen + dataCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Data", pos + dataVarLen + dataCount * 1, buf.readableBytes()); - } - - pos += dataVarLen; - obj.data = new byte[dataCount]; - - for (int i = 0; i < dataCount; i++) { - obj.data[i] = buf.getByte(pos + i * 1); - } - - pos += dataCount * 1; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -93,7 +97,7 @@ public class SetFluids implements Packet, ToClientPacket { int pos = offset + 13; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 1; + pos += VarInt.size(arrLen) + arrLen * 1; } return pos - offset; @@ -149,7 +153,7 @@ public class SetFluids implements Packet, ToClientPacket { return ValidationResult.error("Data exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(dataCount); pos += dataCount * 1; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Data"); diff --git a/src/com/hypixel/hytale/protocol/packets/world/SetPaused.java b/src/com/hypixel/hytale/protocol/packets/world/SetPaused.java index 27abb5bd..36c03a62 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SetPaused.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SetPaused.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class SetPaused implements Packet, ToServerPacket { @Nonnull public static SetPaused deserialize(@Nonnull ByteBuf buf, int offset) { - SetPaused obj = new SetPaused(); - obj.paused = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("SetPaused", 1, buf.readableBytes() - offset); + } else { + SetPaused obj = new SetPaused(); + obj.paused = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/SleepClock.java b/src/com/hypixel/hytale/protocol/packets/world/SleepClock.java index a219a68e..46ddd381 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SleepClock.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SleepClock.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.InstantData; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -39,19 +40,23 @@ public class SleepClock { @Nonnull public static SleepClock deserialize(@Nonnull ByteBuf buf, int offset) { - SleepClock obj = new SleepClock(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.startGametime = InstantData.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 33) { + throw ProtocolException.bufferTooSmall("SleepClock", 33, buf.readableBytes() - offset); + } else { + SleepClock obj = new SleepClock(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.startGametime = InstantData.deserialize(buf, offset + 1); + } - if ((nullBits & 2) != 0) { - obj.targetGametime = InstantData.deserialize(buf, offset + 13); - } + if ((nullBits & 2) != 0) { + obj.targetGametime = InstantData.deserialize(buf, offset + 13); + } - obj.progress = buf.getFloatLE(offset + 25); - obj.durationSeconds = buf.getFloatLE(offset + 29); - return obj; + obj.progress = buf.getFloatLE(offset + 25); + obj.durationSeconds = buf.getFloatLE(offset + 29); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,7 +95,12 @@ public class SleepClock { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 33 ? ValidationResult.error("Buffer too small: expected at least 33 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 33) { + return ValidationResult.error("Buffer too small: expected at least 33 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public SleepClock clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/world/SleepMultiplayer.java b/src/com/hypixel/hytale/protocol/packets/world/SleepMultiplayer.java index 1fd9a53c..29245487 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SleepMultiplayer.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SleepMultiplayer.java @@ -38,37 +38,41 @@ public class SleepMultiplayer { @Nonnull public static SleepMultiplayer deserialize(@Nonnull ByteBuf buf, int offset) { - SleepMultiplayer obj = new SleepMultiplayer(); - byte nullBits = buf.getByte(offset); - obj.sleepersCount = buf.getIntLE(offset + 1); - obj.awakeCount = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - int awakeSampleCount = VarInt.peek(buf, pos); - if (awakeSampleCount < 0) { - throw ProtocolException.negativeLength("AwakeSample", awakeSampleCount); + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("SleepMultiplayer", 9, buf.readableBytes() - offset); + } else { + SleepMultiplayer obj = new SleepMultiplayer(); + byte nullBits = buf.getByte(offset); + obj.sleepersCount = buf.getIntLE(offset + 1); + obj.awakeCount = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + int awakeSampleCount = VarInt.peek(buf, pos); + if (awakeSampleCount < 0) { + throw ProtocolException.invalidVarInt("AwakeSample"); + } + + int awakeSampleVarLen = VarInt.size(awakeSampleCount); + if (awakeSampleCount > 4096000) { + throw ProtocolException.arrayTooLong("AwakeSample", awakeSampleCount, 4096000); + } + + if (pos + awakeSampleVarLen + awakeSampleCount * 16L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AwakeSample", pos + awakeSampleVarLen + awakeSampleCount * 16, buf.readableBytes()); + } + + pos += awakeSampleVarLen; + obj.awakeSample = new UUID[awakeSampleCount]; + + for (int i = 0; i < awakeSampleCount; i++) { + obj.awakeSample[i] = PacketIO.readUUID(buf, pos + i * 16); + } + + pos += awakeSampleCount * 16; } - if (awakeSampleCount > 4096000) { - throw ProtocolException.arrayTooLong("AwakeSample", awakeSampleCount, 4096000); - } - - int awakeSampleVarLen = VarInt.size(awakeSampleCount); - if (pos + awakeSampleVarLen + awakeSampleCount * 16L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("AwakeSample", pos + awakeSampleVarLen + awakeSampleCount * 16, buf.readableBytes()); - } - - pos += awakeSampleVarLen; - obj.awakeSample = new UUID[awakeSampleCount]; - - for (int i = 0; i < awakeSampleCount; i++) { - obj.awakeSample[i] = PacketIO.readUUID(buf, pos + i * 16); - } - - pos += awakeSampleCount * 16; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -76,7 +80,7 @@ public class SleepMultiplayer { int pos = offset + 9; if ((nullBits & 1) != 0) { int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 16; + pos += VarInt.size(arrLen) + arrLen * 16; } return pos - offset; @@ -129,7 +133,7 @@ public class SleepMultiplayer { return ValidationResult.error("AwakeSample exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(awakeSampleCount); pos += awakeSampleCount * 16; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading AwakeSample"); diff --git a/src/com/hypixel/hytale/protocol/packets/world/SpawnBlockParticleSystem.java b/src/com/hypixel/hytale/protocol/packets/world/SpawnBlockParticleSystem.java index bf65fa2c..60cf71e9 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SpawnBlockParticleSystem.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SpawnBlockParticleSystem.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -52,15 +53,19 @@ public class SpawnBlockParticleSystem implements Packet, ToClientPacket { @Nonnull public static SpawnBlockParticleSystem deserialize(@Nonnull ByteBuf buf, int offset) { - SpawnBlockParticleSystem obj = new SpawnBlockParticleSystem(); - byte nullBits = buf.getByte(offset); - obj.blockId = buf.getIntLE(offset + 1); - obj.particleType = BlockParticleEvent.fromValue(buf.getByte(offset + 5)); - if ((nullBits & 1) != 0) { - obj.position = Position.deserialize(buf, offset + 6); - } + if (buf.readableBytes() - offset < 30) { + throw ProtocolException.bufferTooSmall("SpawnBlockParticleSystem", 30, buf.readableBytes() - offset); + } else { + SpawnBlockParticleSystem obj = new SpawnBlockParticleSystem(); + byte nullBits = buf.getByte(offset); + obj.blockId = buf.getIntLE(offset + 1); + obj.particleType = BlockParticleEvent.fromValue(buf.getByte(offset + 5)); + if ((nullBits & 1) != 0) { + obj.position = Position.deserialize(buf, offset + 6); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -90,7 +95,13 @@ public class SpawnBlockParticleSystem implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 30 ? ValidationResult.error("Buffer too small: expected at least 30 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 30) { + return ValidationResult.error("Buffer too small: expected at least 30 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + int v = buffer.getByte(offset + 5) & 255; + return v >= 10 ? ValidationResult.error("Invalid BlockParticleEvent value for ParticleType") : ValidationResult.OK; + } } public SpawnBlockParticleSystem clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/world/SpawnParticleSystem.java b/src/com/hypixel/hytale/protocol/packets/world/SpawnParticleSystem.java index d26f85bc..268978d1 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/SpawnParticleSystem.java +++ b/src/com/hypixel/hytale/protocol/packets/world/SpawnParticleSystem.java @@ -64,38 +64,46 @@ public class SpawnParticleSystem implements Packet, ToClientPacket { @Nonnull public static SpawnParticleSystem deserialize(@Nonnull ByteBuf buf, int offset) { - SpawnParticleSystem obj = new SpawnParticleSystem(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.position = Position.deserialize(buf, offset + 1); - } - - if ((nullBits & 2) != 0) { - obj.rotation = Direction.deserialize(buf, offset + 25); - } - - obj.scale = buf.getFloatLE(offset + 37); - if ((nullBits & 4) != 0) { - obj.color = Color.deserialize(buf, offset + 41); - } - - int pos = offset + 44; - if ((nullBits & 8) != 0) { - int particleSystemIdLen = VarInt.peek(buf, pos); - if (particleSystemIdLen < 0) { - throw ProtocolException.negativeLength("ParticleSystemId", particleSystemIdLen); + if (buf.readableBytes() - offset < 44) { + throw ProtocolException.bufferTooSmall("SpawnParticleSystem", 44, buf.readableBytes() - offset); + } else { + SpawnParticleSystem obj = new SpawnParticleSystem(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.position = Position.deserialize(buf, offset + 1); } - if (particleSystemIdLen > 4096000) { - throw ProtocolException.stringTooLong("ParticleSystemId", particleSystemIdLen, 4096000); + if ((nullBits & 2) != 0) { + obj.rotation = Direction.deserialize(buf, offset + 25); } - int particleSystemIdVarLen = VarInt.length(buf, pos); - obj.particleSystemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += particleSystemIdVarLen + particleSystemIdLen; - } + obj.scale = buf.getFloatLE(offset + 37); + if ((nullBits & 4) != 0) { + obj.color = Color.deserialize(buf, offset + 41); + } - return obj; + int pos = offset + 44; + if ((nullBits & 8) != 0) { + int particleSystemIdLen = VarInt.peek(buf, pos); + if (particleSystemIdLen < 0) { + throw ProtocolException.invalidVarInt("ParticleSystemId"); + } + + int particleSystemIdVarLen = VarInt.size(particleSystemIdLen); + if (particleSystemIdLen > 4096000) { + throw ProtocolException.stringTooLong("ParticleSystemId", particleSystemIdLen, 4096000); + } + + if (pos + particleSystemIdVarLen + particleSystemIdLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ParticleSystemId", pos + particleSystemIdVarLen + particleSystemIdLen, buf.readableBytes()); + } + + obj.particleSystemId = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += particleSystemIdVarLen + particleSystemIdLen; + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -103,7 +111,7 @@ public class SpawnParticleSystem implements Packet, ToClientPacket { int pos = offset + 44; if ((nullBits & 8) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -179,7 +187,7 @@ public class SpawnParticleSystem implements Packet, ToClientPacket { return ValidationResult.error("ParticleSystemId exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(particleSystemIdLen); pos += particleSystemIdLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ParticleSystemId"); diff --git a/src/com/hypixel/hytale/protocol/packets/world/UnloadChunk.java b/src/com/hypixel/hytale/protocol/packets/world/UnloadChunk.java index 11f6d266..f9a64294 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UnloadChunk.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UnloadChunk.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class UnloadChunk implements Packet, ToClientPacket { @Nonnull public static UnloadChunk deserialize(@Nonnull ByteBuf buf, int offset) { - UnloadChunk obj = new UnloadChunk(); - obj.chunkX = buf.getIntLE(offset + 0); - obj.chunkZ = buf.getIntLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UnloadChunk", 8, buf.readableBytes() - offset); + } else { + UnloadChunk obj = new UnloadChunk(); + obj.chunkX = buf.getIntLE(offset + 0); + obj.chunkZ = buf.getIntLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateBlockDamage.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateBlockDamage.java index 8630af8d..66b2417b 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateBlockDamage.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateBlockDamage.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,15 +51,19 @@ public class UpdateBlockDamage implements Packet, ToClientPacket { @Nonnull public static UpdateBlockDamage deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateBlockDamage obj = new UpdateBlockDamage(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.blockPosition = BlockPosition.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("UpdateBlockDamage", 21, buf.readableBytes() - offset); + } else { + UpdateBlockDamage obj = new UpdateBlockDamage(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.blockPosition = BlockPosition.deserialize(buf, offset + 1); + } - obj.damage = buf.getFloatLE(offset + 13); - obj.delta = buf.getFloatLE(offset + 17); - return obj; + obj.damage = buf.getFloatLE(offset + 13); + obj.delta = buf.getFloatLE(offset + 17); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -89,7 +94,12 @@ public class UpdateBlockDamage implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 21 ? ValidationResult.error("Buffer too small: expected at least 21 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 21) { + return ValidationResult.error("Buffer too small: expected at least 21 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public UpdateBlockDamage clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateEditorTimeOverride.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateEditorTimeOverride.java index 1fd64083..70c57b90 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateEditorTimeOverride.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateEditorTimeOverride.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.InstantData; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -47,14 +48,18 @@ public class UpdateEditorTimeOverride implements Packet, ToClientPacket { @Nonnull public static UpdateEditorTimeOverride deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateEditorTimeOverride obj = new UpdateEditorTimeOverride(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.gameTime = InstantData.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 14) { + throw ProtocolException.bufferTooSmall("UpdateEditorTimeOverride", 14, buf.readableBytes() - offset); + } else { + UpdateEditorTimeOverride obj = new UpdateEditorTimeOverride(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.gameTime = InstantData.deserialize(buf, offset + 1); + } - obj.paused = buf.getByte(offset + 13) != 0; - return obj; + obj.paused = buf.getByte(offset + 13) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -84,7 +89,12 @@ public class UpdateEditorTimeOverride implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 14 ? ValidationResult.error("Buffer too small: expected at least 14 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 14) { + return ValidationResult.error("Buffer too small: expected at least 14 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public UpdateEditorTimeOverride clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateEditorWeatherOverride.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateEditorWeatherOverride.java index 4d3e43d7..d2362127 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateEditorWeatherOverride.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateEditorWeatherOverride.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class UpdateEditorWeatherOverride implements Packet, ToClientPacket { @Nonnull public static UpdateEditorWeatherOverride deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateEditorWeatherOverride obj = new UpdateEditorWeatherOverride(); - obj.weatherIndex = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("UpdateEditorWeatherOverride", 4, buf.readableBytes() - offset); + } else { + UpdateEditorWeatherOverride obj = new UpdateEditorWeatherOverride(); + obj.weatherIndex = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateEnvironmentMusic.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateEnvironmentMusic.java index 4d386d4a..67dcaddb 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateEnvironmentMusic.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateEnvironmentMusic.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class UpdateEnvironmentMusic implements Packet, ToClientPacket { @Nonnull public static UpdateEnvironmentMusic deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateEnvironmentMusic obj = new UpdateEnvironmentMusic(); - obj.environmentIndex = buf.getIntLE(offset + 0); - return obj; + if (buf.readableBytes() - offset < 4) { + throw ProtocolException.bufferTooSmall("UpdateEnvironmentMusic", 4, buf.readableBytes() - offset); + } else { + UpdateEnvironmentMusic obj = new UpdateEnvironmentMusic(); + obj.environmentIndex = buf.getIntLE(offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdatePostFxSettings.java b/src/com/hypixel/hytale/protocol/packets/world/UpdatePostFxSettings.java index dde35163..8f58f3ce 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdatePostFxSettings.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdatePostFxSettings.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -53,13 +54,17 @@ public class UpdatePostFxSettings implements Packet, ToClientPacket { @Nonnull public static UpdatePostFxSettings deserialize(@Nonnull ByteBuf buf, int offset) { - UpdatePostFxSettings obj = new UpdatePostFxSettings(); - obj.globalIntensity = buf.getFloatLE(offset + 0); - obj.power = buf.getFloatLE(offset + 4); - obj.sunshaftScale = buf.getFloatLE(offset + 8); - obj.sunIntensity = buf.getFloatLE(offset + 12); - obj.sunshaftIntensity = buf.getFloatLE(offset + 16); - return obj; + if (buf.readableBytes() - offset < 20) { + throw ProtocolException.bufferTooSmall("UpdatePostFxSettings", 20, buf.readableBytes() - offset); + } else { + UpdatePostFxSettings obj = new UpdatePostFxSettings(); + obj.globalIntensity = buf.getFloatLE(offset + 0); + obj.power = buf.getFloatLE(offset + 4); + obj.sunshaftScale = buf.getFloatLE(offset + 8); + obj.sunIntensity = buf.getFloatLE(offset + 12); + obj.sunshaftIntensity = buf.getFloatLE(offset + 16); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateSleepState.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateSleepState.java index 9062d23b..06e61a21 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateSleepState.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateSleepState.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -53,21 +54,25 @@ public class UpdateSleepState implements Packet, ToClientPacket { @Nonnull public static UpdateSleepState deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateSleepState obj = new UpdateSleepState(); - byte nullBits = buf.getByte(offset); - obj.grayFade = buf.getByte(offset + 1) != 0; - obj.sleepUi = buf.getByte(offset + 2) != 0; - if ((nullBits & 1) != 0) { - obj.clock = SleepClock.deserialize(buf, offset + 3); - } + if (buf.readableBytes() - offset < 36) { + throw ProtocolException.bufferTooSmall("UpdateSleepState", 36, buf.readableBytes() - offset); + } else { + UpdateSleepState obj = new UpdateSleepState(); + byte nullBits = buf.getByte(offset); + obj.grayFade = buf.getByte(offset + 1) != 0; + obj.sleepUi = buf.getByte(offset + 2) != 0; + if ((nullBits & 1) != 0) { + obj.clock = SleepClock.deserialize(buf, offset + 3); + } - int pos = offset + 36; - if ((nullBits & 2) != 0) { - obj.multiplayer = SleepMultiplayer.deserialize(buf, pos); - pos += SleepMultiplayer.computeBytesConsumed(buf, pos); - } + int pos = offset + 36; + if ((nullBits & 2) != 0) { + obj.multiplayer = SleepMultiplayer.deserialize(buf, pos); + pos += SleepMultiplayer.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateSunSettings.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateSunSettings.java index 420e9786..4ebc2202 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateSunSettings.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateSunSettings.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class UpdateSunSettings implements Packet, ToClientPacket { @Nonnull public static UpdateSunSettings deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateSunSettings obj = new UpdateSunSettings(); - obj.heightPercentage = buf.getFloatLE(offset + 0); - obj.angleRadians = buf.getFloatLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UpdateSunSettings", 8, buf.readableBytes() - offset); + } else { + UpdateSunSettings obj = new UpdateSunSettings(); + obj.heightPercentage = buf.getFloatLE(offset + 0); + obj.angleRadians = buf.getFloatLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateTime.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateTime.java index 1e919737..702ebb86 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateTime.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateTime.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.protocol.InstantData; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,13 +45,17 @@ public class UpdateTime implements Packet, ToClientPacket { @Nonnull public static UpdateTime deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateTime obj = new UpdateTime(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - obj.gameTime = InstantData.deserialize(buf, offset + 1); - } + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("UpdateTime", 13, buf.readableBytes() - offset); + } else { + UpdateTime obj = new UpdateTime(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + obj.gameTime = InstantData.deserialize(buf, offset + 1); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -78,7 +83,12 @@ public class UpdateTime implements Packet, ToClientPacket { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - return buffer.readableBytes() - offset < 13 ? ValidationResult.error("Buffer too small: expected at least 13 bytes") : ValidationResult.OK; + if (buffer.readableBytes() - offset < 13) { + return ValidationResult.error("Buffer too small: expected at least 13 bytes"); + } else { + byte nullBits = buffer.getByte(offset); + return ValidationResult.OK; + } } public UpdateTime clone() { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateTimeSettings.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateTimeSettings.java index bc27d309..aa2f9eac 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateTimeSettings.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateTimeSettings.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -50,12 +51,16 @@ public class UpdateTimeSettings implements Packet, ToClientPacket { @Nonnull public static UpdateTimeSettings deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateTimeSettings obj = new UpdateTimeSettings(); - obj.daytimeDurationSeconds = buf.getIntLE(offset + 0); - obj.nighttimeDurationSeconds = buf.getIntLE(offset + 4); - obj.totalMoonPhases = buf.getByte(offset + 8); - obj.timePaused = buf.getByte(offset + 9) != 0; - return obj; + if (buf.readableBytes() - offset < 10) { + throw ProtocolException.bufferTooSmall("UpdateTimeSettings", 10, buf.readableBytes() - offset); + } else { + UpdateTimeSettings obj = new UpdateTimeSettings(); + obj.daytimeDurationSeconds = buf.getIntLE(offset + 0); + obj.nighttimeDurationSeconds = buf.getIntLE(offset + 4); + obj.totalMoonPhases = buf.getByte(offset + 8); + obj.timePaused = buf.getByte(offset + 9) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/world/UpdateWeather.java b/src/com/hypixel/hytale/protocol/packets/world/UpdateWeather.java index 6d5113d3..a1d38022 100644 --- a/src/com/hypixel/hytale/protocol/packets/world/UpdateWeather.java +++ b/src/com/hypixel/hytale/protocol/packets/world/UpdateWeather.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.world; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class UpdateWeather implements Packet, ToClientPacket { @Nonnull public static UpdateWeather deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateWeather obj = new UpdateWeather(); - obj.weatherIndex = buf.getIntLE(offset + 0); - obj.transitionSeconds = buf.getFloatLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("UpdateWeather", 8, buf.readableBytes() - offset); + } else { + UpdateWeather obj = new UpdateWeather(); + obj.weatherIndex = buf.getIntLE(offset + 0); + obj.transitionSeconds = buf.getFloatLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/BiomeData.java b/src/com/hypixel/hytale/protocol/packets/worldmap/BiomeData.java index 14cbf58c..62680c45 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/BiomeData.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/BiomeData.java @@ -41,39 +41,63 @@ public class BiomeData { @Nonnull public static BiomeData deserialize(@Nonnull ByteBuf buf, int offset) { - BiomeData obj = new BiomeData(); - byte nullBits = buf.getByte(offset); - obj.zoneId = buf.getIntLE(offset + 1); - obj.biomeColor = buf.getIntLE(offset + 5); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 17 + buf.getIntLE(offset + 9); - int zoneNameLen = VarInt.peek(buf, varPos0); - if (zoneNameLen < 0) { - throw ProtocolException.negativeLength("ZoneName", zoneNameLen); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("BiomeData", 17, buf.readableBytes() - offset); + } else { + BiomeData obj = new BiomeData(); + byte nullBits = buf.getByte(offset); + obj.zoneId = buf.getIntLE(offset + 1); + obj.biomeColor = buf.getIntLE(offset + 5); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ZoneName", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int zoneNameLen = VarInt.peek(buf, varPos0); + if (zoneNameLen < 0) { + throw ProtocolException.invalidVarInt("ZoneName"); + } + + int zoneNameVarIntLen = VarInt.size(zoneNameLen); + if (zoneNameLen > 4096000) { + throw ProtocolException.stringTooLong("ZoneName", zoneNameLen, 4096000); + } + + if (varPos0 + zoneNameVarIntLen + zoneNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ZoneName", varPos0 + zoneNameVarIntLen + zoneNameLen, buf.readableBytes()); + } + + obj.zoneName = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (zoneNameLen > 4096000) { - throw ProtocolException.stringTooLong("ZoneName", zoneNameLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 13); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("BiomeName", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + int biomeNameLen = VarInt.peek(buf, varPos1); + if (biomeNameLen < 0) { + throw ProtocolException.invalidVarInt("BiomeName"); + } + + int biomeNameVarIntLen = VarInt.size(biomeNameLen); + if (biomeNameLen > 4096000) { + throw ProtocolException.stringTooLong("BiomeName", biomeNameLen, 4096000); + } + + if (varPos1 + biomeNameVarIntLen + biomeNameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("BiomeName", varPos1 + biomeNameVarIntLen + biomeNameLen, buf.readableBytes()); + } + + obj.biomeName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.zoneName = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 17 + buf.getIntLE(offset + 13); - int biomeNameLen = VarInt.peek(buf, varPos1); - if (biomeNameLen < 0) { - throw ProtocolException.negativeLength("BiomeName", biomeNameLen); - } - - if (biomeNameLen > 4096000) { - throw ProtocolException.stringTooLong("BiomeName", biomeNameLen, 4096000); - } - - obj.biomeName = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,9 +105,13 @@ public class BiomeData { int maxEnd = 17; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("ZoneName", fieldOffset0, maxEnd); + } + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -91,9 +119,13 @@ public class BiomeData { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 13); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("BiomeName", fieldOffset1, maxEnd); + } + int pos1 = offset + 17 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -156,15 +188,11 @@ public class BiomeData { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int zoneNameOffset = buffer.getIntLE(offset + 9); - if (zoneNameOffset < 0) { + if (zoneNameOffset < 0 || zoneNameOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for ZoneName"); } int pos = offset + 17 + zoneNameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ZoneName"); - } - int zoneNameLen = VarInt.peek(buffer, pos); if (zoneNameLen < 0) { return ValidationResult.error("Invalid string length for ZoneName"); @@ -174,7 +202,7 @@ public class BiomeData { return ValidationResult.error("ZoneName exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(zoneNameLen); pos += zoneNameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading ZoneName"); @@ -183,15 +211,11 @@ public class BiomeData { if ((nullBits & 2) != 0) { int biomeNameOffset = buffer.getIntLE(offset + 13); - if (biomeNameOffset < 0) { + if (biomeNameOffset < 0 || biomeNameOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for BiomeName"); } int posx = offset + 17 + biomeNameOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for BiomeName"); - } - int biomeNameLen = VarInt.peek(buffer, posx); if (biomeNameLen < 0) { return ValidationResult.error("Invalid string length for BiomeName"); @@ -201,7 +225,7 @@ public class BiomeData { return ValidationResult.error("BiomeName exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(biomeNameLen); posx += biomeNameLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading BiomeName"); diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/ContextMenuItem.java b/src/com/hypixel/hytale/protocol/packets/worldmap/ContextMenuItem.java index 4830f500..88ce7334 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/ContextMenuItem.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/ContextMenuItem.java @@ -34,24 +34,48 @@ public class ContextMenuItem { @Nonnull public static ContextMenuItem deserialize(@Nonnull ByteBuf buf, int offset) { - ContextMenuItem obj = new ContextMenuItem(); - int varPos0 = offset + 8 + buf.getIntLE(offset + 0); - int nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); - } else if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("ContextMenuItem", 8, buf.readableBytes() - offset); } else { - obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - varPos0 = offset + 8 + buf.getIntLE(offset + 4); - nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Command", nameLen); - } else if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Command", nameLen, 4096000); + ContextMenuItem obj = new ContextMenuItem(); + int varPosBase0 = buf.getIntLE(offset + 0); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + int varPos0 = offset + 8 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } else { + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } else if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } else { + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + varPosBase0 = buf.getIntLE(offset + 4); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 8) { + varPos0 = offset + 8 + varPosBase0; + nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Command"); + } else { + nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Command", nameLen, 4096000); + } else if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Command", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } else { + obj.command = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("Command", varPosBase0, buf.readableBytes()); + } + } + } } else { - obj.command = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - return obj; + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); } } } @@ -59,22 +83,30 @@ public class ContextMenuItem { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int maxEnd = 8; int fieldOffset0 = buf.getIntLE(offset + 0); - int pos0 = offset + 8 + fieldOffset0; - int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + int pos0 = offset + 8 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } - fieldOffset0 = buf.getIntLE(offset + 4); - pos0 = offset + 8 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } + fieldOffset0 = buf.getIntLE(offset + 4); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 8) { + pos0 = offset + 8 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } - return maxEnd; + return maxEnd; + } else { + throw ProtocolException.invalidOffset("Command", fieldOffset0, maxEnd); + } + } else { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } } public void serialize(@Nonnull ByteBuf buf) { @@ -101,47 +133,39 @@ public class ContextMenuItem { return ValidationResult.error("Buffer too small: expected at least 8 bytes"); } else { int nameOffset = buffer.getIntLE(offset + 0); - if (nameOffset < 0) { - return ValidationResult.error("Invalid offset for Name"); - } else { + if (nameOffset >= 0 && nameOffset <= buffer.writerIndex() - offset - 8) { int pos = offset + 8 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); + int nameLen = VarInt.peek(buffer, pos); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Name"); + } else if (nameLen > 4096000) { + return ValidationResult.error("Name exceeds max length 4096000"); } else { - int nameLen = VarInt.peek(buffer, pos); - if (nameLen < 0) { - return ValidationResult.error("Invalid string length for Name"); - } else if (nameLen > 4096000) { - return ValidationResult.error("Name exceeds max length 4096000"); + pos += VarInt.size(nameLen); + pos += nameLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Name"); } else { - pos += VarInt.length(buffer, pos); - pos += nameLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Name"); - } else { - nameOffset = buffer.getIntLE(offset + 4); - if (nameOffset < 0) { - return ValidationResult.error("Invalid offset for Command"); + nameOffset = buffer.getIntLE(offset + 4); + if (nameOffset >= 0 && nameOffset <= buffer.writerIndex() - offset - 8) { + pos = offset + 8 + nameOffset; + nameLen = VarInt.peek(buffer, pos); + if (nameLen < 0) { + return ValidationResult.error("Invalid string length for Command"); + } else if (nameLen > 4096000) { + return ValidationResult.error("Command exceeds max length 4096000"); } else { - pos = offset + 8 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Command"); - } else { - nameLen = VarInt.peek(buffer, pos); - if (nameLen < 0) { - return ValidationResult.error("Invalid string length for Command"); - } else if (nameLen > 4096000) { - return ValidationResult.error("Command exceeds max length 4096000"); - } else { - pos += VarInt.length(buffer, pos); - pos += nameLen; - return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Command") : ValidationResult.OK; - } - } + pos += VarInt.size(nameLen); + pos += nameLen; + return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Command") : ValidationResult.OK; } + } else { + return ValidationResult.error("Invalid offset for Command"); } } } + } else { + return ValidationResult.error("Invalid offset for Name"); } } } diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/CreateUserMarker.java b/src/com/hypixel/hytale/protocol/packets/worldmap/CreateUserMarker.java index a27c1317..03d9c55c 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/CreateUserMarker.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/CreateUserMarker.java @@ -64,44 +64,68 @@ public class CreateUserMarker implements Packet, ToServerPacket { @Nonnull public static CreateUserMarker deserialize(@Nonnull ByteBuf buf, int offset) { - CreateUserMarker obj = new CreateUserMarker(); - byte nullBits = buf.getByte(offset); - obj.x = buf.getFloatLE(offset + 1); - obj.z = buf.getFloatLE(offset + 5); - if ((nullBits & 1) != 0) { - obj.tintColor = Color.deserialize(buf, offset + 9); + if (buf.readableBytes() - offset < 21) { + throw ProtocolException.bufferTooSmall("CreateUserMarker", 21, buf.readableBytes() - offset); + } else { + CreateUserMarker obj = new CreateUserMarker(); + byte nullBits = buf.getByte(offset); + obj.x = buf.getFloatLE(offset + 1); + obj.z = buf.getFloatLE(offset + 5); + if ((nullBits & 1) != 0) { + obj.tintColor = Color.deserialize(buf, offset + 9); + } + + obj.shared = buf.getByte(offset + 12) != 0; + if ((nullBits & 2) != 0) { + int varPosBase0 = buf.getIntLE(offset + 13); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 21 + varPosBase0; + int nameLen = VarInt.peek(buf, varPos0); + if (nameLen < 0) { + throw ProtocolException.invalidVarInt("Name"); + } + + int nameVarIntLen = VarInt.size(nameLen); + if (nameLen > 4096000) { + throw ProtocolException.stringTooLong("Name", nameLen, 4096000); + } + + if (varPos0 + nameVarIntLen + nameLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Name", varPos0 + nameVarIntLen + nameLen, buf.readableBytes()); + } + + obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + } + + if ((nullBits & 4) != 0) { + int varPosBase1 = buf.getIntLE(offset + 17); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("MarkerImage", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 21 + varPosBase1; + int markerImageLen = VarInt.peek(buf, varPos1); + if (markerImageLen < 0) { + throw ProtocolException.invalidVarInt("MarkerImage"); + } + + int markerImageVarIntLen = VarInt.size(markerImageLen); + if (markerImageLen > 4096000) { + throw ProtocolException.stringTooLong("MarkerImage", markerImageLen, 4096000); + } + + if (varPos1 + markerImageVarIntLen + markerImageLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("MarkerImage", varPos1 + markerImageVarIntLen + markerImageLen, buf.readableBytes()); + } + + obj.markerImage = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); + } + + return obj; } - - obj.shared = buf.getByte(offset + 12) != 0; - if ((nullBits & 2) != 0) { - int varPos0 = offset + 21 + buf.getIntLE(offset + 13); - int nameLen = VarInt.peek(buf, varPos0); - if (nameLen < 0) { - throw ProtocolException.negativeLength("Name", nameLen); - } - - if (nameLen > 4096000) { - throw ProtocolException.stringTooLong("Name", nameLen, 4096000); - } - - obj.name = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - if ((nullBits & 4) != 0) { - int varPos1 = offset + 21 + buf.getIntLE(offset + 17); - int markerImageLen = VarInt.peek(buf, varPos1); - if (markerImageLen < 0) { - throw ProtocolException.negativeLength("MarkerImage", markerImageLen); - } - - if (markerImageLen > 4096000) { - throw ProtocolException.stringTooLong("MarkerImage", markerImageLen, 4096000); - } - - obj.markerImage = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -109,9 +133,13 @@ public class CreateUserMarker implements Packet, ToServerPacket { int maxEnd = 21; if ((nullBits & 2) != 0) { int fieldOffset0 = buf.getIntLE(offset + 13); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + int pos0 = offset + 21 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -119,9 +147,13 @@ public class CreateUserMarker implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int fieldOffset1 = buf.getIntLE(offset + 17); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 21) { + throw ProtocolException.invalidOffset("MarkerImage", fieldOffset1, maxEnd); + } + int pos1 = offset + 21 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -197,15 +229,11 @@ public class CreateUserMarker implements Packet, ToServerPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 2) != 0) { int nameOffset = buffer.getIntLE(offset + 13); - if (nameOffset < 0) { + if (nameOffset < 0 || nameOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for Name"); } int pos = offset + 21 + nameOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - int nameLen = VarInt.peek(buffer, pos); if (nameLen < 0) { return ValidationResult.error("Invalid string length for Name"); @@ -215,7 +243,7 @@ public class CreateUserMarker implements Packet, ToServerPacket { return ValidationResult.error("Name exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(nameLen); pos += nameLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Name"); @@ -224,15 +252,11 @@ public class CreateUserMarker implements Packet, ToServerPacket { if ((nullBits & 4) != 0) { int markerImageOffset = buffer.getIntLE(offset + 17); - if (markerImageOffset < 0) { + if (markerImageOffset < 0 || markerImageOffset > buffer.writerIndex() - offset - 21) { return ValidationResult.error("Invalid offset for MarkerImage"); } int posx = offset + 21 + markerImageOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MarkerImage"); - } - int markerImageLen = VarInt.peek(buffer, posx); if (markerImageLen < 0) { return ValidationResult.error("Invalid string length for MarkerImage"); @@ -242,7 +266,7 @@ public class CreateUserMarker implements Packet, ToServerPacket { return ValidationResult.error("MarkerImage exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(markerImageLen); posx += markerImageLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading MarkerImage"); diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/HeightDeltaIconComponent.java b/src/com/hypixel/hytale/protocol/packets/worldmap/HeightDeltaIconComponent.java index 99637838..b1214827 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/HeightDeltaIconComponent.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/HeightDeltaIconComponent.java @@ -41,39 +41,63 @@ public class HeightDeltaIconComponent extends MapMarkerComponent { @Nonnull public static HeightDeltaIconComponent deserialize(@Nonnull ByteBuf buf, int offset) { - HeightDeltaIconComponent obj = new HeightDeltaIconComponent(); - byte nullBits = buf.getByte(offset); - obj.upDelta = buf.getIntLE(offset + 1); - obj.downDelta = buf.getIntLE(offset + 5); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 17 + buf.getIntLE(offset + 9); - int upImageLen = VarInt.peek(buf, varPos0); - if (upImageLen < 0) { - throw ProtocolException.negativeLength("UpImage", upImageLen); + if (buf.readableBytes() - offset < 17) { + throw ProtocolException.bufferTooSmall("HeightDeltaIconComponent", 17, buf.readableBytes() - offset); + } else { + HeightDeltaIconComponent obj = new HeightDeltaIconComponent(); + byte nullBits = buf.getByte(offset); + obj.upDelta = buf.getIntLE(offset + 1); + obj.downDelta = buf.getIntLE(offset + 5); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 9); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("UpImage", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 17 + varPosBase0; + int upImageLen = VarInt.peek(buf, varPos0); + if (upImageLen < 0) { + throw ProtocolException.invalidVarInt("UpImage"); + } + + int upImageVarIntLen = VarInt.size(upImageLen); + if (upImageLen > 4096000) { + throw ProtocolException.stringTooLong("UpImage", upImageLen, 4096000); + } + + if (varPos0 + upImageVarIntLen + upImageLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("UpImage", varPos0 + upImageVarIntLen + upImageLen, buf.readableBytes()); + } + + obj.upImage = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); } - if (upImageLen > 4096000) { - throw ProtocolException.stringTooLong("UpImage", upImageLen, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 13); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("DownImage", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 17 + varPosBase1; + int downImageLen = VarInt.peek(buf, varPos1); + if (downImageLen < 0) { + throw ProtocolException.invalidVarInt("DownImage"); + } + + int downImageVarIntLen = VarInt.size(downImageLen); + if (downImageLen > 4096000) { + throw ProtocolException.stringTooLong("DownImage", downImageLen, 4096000); + } + + if (varPos1 + downImageVarIntLen + downImageLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("DownImage", varPos1 + downImageVarIntLen + downImageLen, buf.readableBytes()); + } + + obj.downImage = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); } - obj.upImage = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + return obj; } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 17 + buf.getIntLE(offset + 13); - int downImageLen = VarInt.peek(buf, varPos1); - if (downImageLen < 0) { - throw ProtocolException.negativeLength("DownImage", downImageLen); - } - - if (downImageLen > 4096000) { - throw ProtocolException.stringTooLong("DownImage", downImageLen, 4096000); - } - - obj.downImage = PacketIO.readVarString(buf, varPos1, PacketIO.UTF8); - } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -81,9 +105,13 @@ public class HeightDeltaIconComponent extends MapMarkerComponent { int maxEnd = 17; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 9); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("UpImage", fieldOffset0, maxEnd); + } + int pos0 = offset + 17 + fieldOffset0; int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } @@ -91,9 +119,13 @@ public class HeightDeltaIconComponent extends MapMarkerComponent { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 13); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 17) { + throw ProtocolException.invalidOffset("DownImage", fieldOffset1, maxEnd); + } + int pos1 = offset + 17 + fieldOffset1; int sl = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1) + sl; + pos1 += VarInt.size(sl) + sl; if (pos1 - offset > maxEnd) { maxEnd = pos1 - offset; } @@ -160,15 +192,11 @@ public class HeightDeltaIconComponent extends MapMarkerComponent { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int upImageOffset = buffer.getIntLE(offset + 9); - if (upImageOffset < 0) { + if (upImageOffset < 0 || upImageOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for UpImage"); } int pos = offset + 17 + upImageOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for UpImage"); - } - int upImageLen = VarInt.peek(buffer, pos); if (upImageLen < 0) { return ValidationResult.error("Invalid string length for UpImage"); @@ -178,7 +206,7 @@ public class HeightDeltaIconComponent extends MapMarkerComponent { return ValidationResult.error("UpImage exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(upImageLen); pos += upImageLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading UpImage"); @@ -187,15 +215,11 @@ public class HeightDeltaIconComponent extends MapMarkerComponent { if ((nullBits & 2) != 0) { int downImageOffset = buffer.getIntLE(offset + 13); - if (downImageOffset < 0) { + if (downImageOffset < 0 || downImageOffset > buffer.writerIndex() - offset - 17) { return ValidationResult.error("Invalid offset for DownImage"); } int posx = offset + 17 + downImageOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for DownImage"); - } - int downImageLen = VarInt.peek(buffer, posx); if (downImageLen < 0) { return ValidationResult.error("Invalid string length for DownImage"); @@ -205,7 +229,7 @@ public class HeightDeltaIconComponent extends MapMarkerComponent { return ValidationResult.error("DownImage exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(downImageLen); posx += downImageLen; if (posx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading DownImage"); diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/MapChunk.java b/src/com/hypixel/hytale/protocol/packets/worldmap/MapChunk.java index 09be1024..33a932c8 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/MapChunk.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/MapChunk.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.protocol.packets.worldmap; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -11,7 +12,7 @@ public class MapChunk { public static final int FIXED_BLOCK_SIZE = 9; public static final int VARIABLE_FIELD_COUNT = 1; public static final int VARIABLE_BLOCK_START = 9; - public static final int MAX_SIZE = 16384023; + public static final int MAX_SIZE = 20480037; public int chunkX; public int chunkZ; @Nullable @@ -34,17 +35,21 @@ public class MapChunk { @Nonnull public static MapChunk deserialize(@Nonnull ByteBuf buf, int offset) { - MapChunk obj = new MapChunk(); - byte nullBits = buf.getByte(offset); - obj.chunkX = buf.getIntLE(offset + 1); - obj.chunkZ = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - obj.image = MapImage.deserialize(buf, pos); - pos += MapImage.computeBytesConsumed(buf, pos); - } + if (buf.readableBytes() - offset < 9) { + throw ProtocolException.bufferTooSmall("MapChunk", 9, buf.readableBytes() - offset); + } else { + MapChunk obj = new MapChunk(); + byte nullBits = buf.getByte(offset); + obj.chunkX = buf.getIntLE(offset + 1); + obj.chunkZ = buf.getIntLE(offset + 5); + int pos = offset + 9; + if ((nullBits & 1) != 0) { + obj.image = MapImage.deserialize(buf, pos); + pos += MapImage.computeBytesConsumed(buf, pos); + } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/MapImage.java b/src/com/hypixel/hytale/protocol/packets/worldmap/MapImage.java index 82633b0c..f28fffa2 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/MapImage.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/MapImage.java @@ -10,127 +10,253 @@ import javax.annotation.Nullable; public class MapImage { public static final int NULLABLE_BIT_FIELD_SIZE = 1; - public static final int FIXED_BLOCK_SIZE = 9; - public static final int VARIABLE_FIELD_COUNT = 1; - public static final int VARIABLE_BLOCK_START = 9; - public static final int MAX_SIZE = 16384014; + public static final int FIXED_BLOCK_SIZE = 10; + public static final int VARIABLE_FIELD_COUNT = 2; + public static final int VARIABLE_BLOCK_START = 18; + public static final int MAX_SIZE = 20480028; public int width; public int height; @Nullable - public int[] data; + public int[] palette; + public byte bitsPerIndex; + @Nullable + public byte[] packedIndices; public MapImage() { } - public MapImage(int width, int height, @Nullable int[] data) { + public MapImage(int width, int height, @Nullable int[] palette, byte bitsPerIndex, @Nullable byte[] packedIndices) { this.width = width; this.height = height; - this.data = data; + this.palette = palette; + this.bitsPerIndex = bitsPerIndex; + this.packedIndices = packedIndices; } public MapImage(@Nonnull MapImage other) { this.width = other.width; this.height = other.height; - this.data = other.data; + this.palette = other.palette; + this.bitsPerIndex = other.bitsPerIndex; + this.packedIndices = other.packedIndices; } @Nonnull public static MapImage deserialize(@Nonnull ByteBuf buf, int offset) { - MapImage obj = new MapImage(); - byte nullBits = buf.getByte(offset); - obj.width = buf.getIntLE(offset + 1); - obj.height = buf.getIntLE(offset + 5); - int pos = offset + 9; - if ((nullBits & 1) != 0) { - int dataCount = VarInt.peek(buf, pos); - if (dataCount < 0) { - throw ProtocolException.negativeLength("Data", dataCount); + if (buf.readableBytes() - offset < 18) { + throw ProtocolException.bufferTooSmall("MapImage", 18, buf.readableBytes() - offset); + } else { + MapImage obj = new MapImage(); + byte nullBits = buf.getByte(offset); + obj.width = buf.getIntLE(offset + 1); + obj.height = buf.getIntLE(offset + 5); + obj.bitsPerIndex = buf.getByte(offset + 9); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 10); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Palette", varPosBase0, buf.readableBytes()); + } + + int varPos0 = offset + 18 + varPosBase0; + int paletteCount = VarInt.peek(buf, varPos0); + if (paletteCount < 0) { + throw ProtocolException.invalidVarInt("Palette"); + } + + int varIntLen = VarInt.size(paletteCount); + if (paletteCount > 4096000) { + throw ProtocolException.arrayTooLong("Palette", paletteCount, 4096000); + } + + if (varPos0 + varIntLen + paletteCount * 4L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Palette", varPos0 + varIntLen + paletteCount * 4, buf.readableBytes()); + } + + obj.palette = new int[paletteCount]; + + for (int i = 0; i < paletteCount; i++) { + obj.palette[i] = buf.getIntLE(varPos0 + varIntLen + i * 4); + } } - if (dataCount > 4096000) { - throw ProtocolException.arrayTooLong("Data", dataCount, 4096000); + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 14); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("PackedIndices", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 18 + varPosBase1; + int packedIndicesCount = VarInt.peek(buf, varPos1); + if (packedIndicesCount < 0) { + throw ProtocolException.invalidVarInt("PackedIndices"); + } + + int varIntLenx = VarInt.size(packedIndicesCount); + if (packedIndicesCount > 4096000) { + throw ProtocolException.arrayTooLong("PackedIndices", packedIndicesCount, 4096000); + } + + if (varPos1 + varIntLenx + packedIndicesCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("PackedIndices", varPos1 + varIntLenx + packedIndicesCount * 1, buf.readableBytes()); + } + + obj.packedIndices = new byte[packedIndicesCount]; + + for (int i = 0; i < packedIndicesCount; i++) { + obj.packedIndices[i] = buf.getByte(varPos1 + varIntLenx + i * 1); + } } - int dataVarLen = VarInt.size(dataCount); - if (pos + dataVarLen + dataCount * 4L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Data", pos + dataVarLen + dataCount * 4, buf.readableBytes()); - } - - pos += dataVarLen; - obj.data = new int[dataCount]; - - for (int i = 0; i < dataCount; i++) { - obj.data[i] = buf.getIntLE(pos + i * 4); - } - - pos += dataCount * 4; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int pos = offset + 9; + int maxEnd = 18; if ((nullBits & 1) != 0) { - int arrLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + arrLen * 4; + int fieldOffset0 = buf.getIntLE(offset + 10); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("Palette", fieldOffset0, maxEnd); + } + + int pos0 = offset + 18 + fieldOffset0; + int arrLen = VarInt.peek(buf, pos0); + pos0 += VarInt.size(arrLen) + arrLen * 4; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } } - return pos - offset; + if ((nullBits & 2) != 0) { + int fieldOffset1 = buf.getIntLE(offset + 14); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 18) { + throw ProtocolException.invalidOffset("PackedIndices", fieldOffset1, maxEnd); + } + + int pos1 = offset + 18 + fieldOffset1; + int arrLen = VarInt.peek(buf, pos1); + pos1 += VarInt.size(arrLen) + arrLen * 1; + if (pos1 - offset > maxEnd) { + maxEnd = pos1 - offset; + } + } + + return maxEnd; } public void serialize(@Nonnull ByteBuf buf) { + int startPos = buf.writerIndex(); byte nullBits = 0; - if (this.data != null) { + if (this.palette != null) { nullBits = (byte)(nullBits | 1); } + if (this.packedIndices != null) { + nullBits = (byte)(nullBits | 2); + } + buf.writeByte(nullBits); buf.writeIntLE(this.width); buf.writeIntLE(this.height); - if (this.data != null) { - if (this.data.length > 4096000) { - throw ProtocolException.arrayTooLong("Data", this.data.length, 4096000); + buf.writeByte(this.bitsPerIndex); + int paletteOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int packedIndicesOffsetSlot = buf.writerIndex(); + buf.writeIntLE(0); + int varBlockStart = buf.writerIndex(); + if (this.palette != null) { + buf.setIntLE(paletteOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.palette.length > 4096000) { + throw ProtocolException.arrayTooLong("Palette", this.palette.length, 4096000); } - VarInt.write(buf, this.data.length); + VarInt.write(buf, this.palette.length); - for (int item : this.data) { + for (int item : this.palette) { buf.writeIntLE(item); } + } else { + buf.setIntLE(paletteOffsetSlot, -1); + } + + if (this.packedIndices != null) { + buf.setIntLE(packedIndicesOffsetSlot, buf.writerIndex() - varBlockStart); + if (this.packedIndices.length > 4096000) { + throw ProtocolException.arrayTooLong("PackedIndices", this.packedIndices.length, 4096000); + } + + VarInt.write(buf, this.packedIndices.length); + + for (byte item : this.packedIndices) { + buf.writeByte(item); + } + } else { + buf.setIntLE(packedIndicesOffsetSlot, -1); } } public int computeSize() { - int size = 9; - if (this.data != null) { - size += VarInt.size(this.data.length) + this.data.length * 4; + int size = 18; + if (this.palette != null) { + size += VarInt.size(this.palette.length) + this.palette.length * 4; + } + + if (this.packedIndices != null) { + size += VarInt.size(this.packedIndices.length) + this.packedIndices.length * 1; } return size; } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 9) { - return ValidationResult.error("Buffer too small: expected at least 9 bytes"); + if (buffer.readableBytes() - offset < 18) { + return ValidationResult.error("Buffer too small: expected at least 18 bytes"); } else { byte nullBits = buffer.getByte(offset); - int pos = offset + 9; if ((nullBits & 1) != 0) { - int dataCount = VarInt.peek(buffer, pos); - if (dataCount < 0) { - return ValidationResult.error("Invalid array count for Data"); + int paletteOffset = buffer.getIntLE(offset + 10); + if (paletteOffset < 0 || paletteOffset > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for Palette"); } - if (dataCount > 4096000) { - return ValidationResult.error("Data exceeds max length 4096000"); + int pos = offset + 18 + paletteOffset; + int paletteCount = VarInt.peek(buffer, pos); + if (paletteCount < 0) { + return ValidationResult.error("Invalid array count for Palette"); } - pos += VarInt.length(buffer, pos); - pos += dataCount * 4; + if (paletteCount > 4096000) { + return ValidationResult.error("Palette exceeds max length 4096000"); + } + + pos += VarInt.size(paletteCount); + pos += paletteCount * 4; if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Data"); + return ValidationResult.error("Buffer overflow reading Palette"); + } + } + + if ((nullBits & 2) != 0) { + int packedIndicesOffset = buffer.getIntLE(offset + 14); + if (packedIndicesOffset < 0 || packedIndicesOffset > buffer.writerIndex() - offset - 18) { + return ValidationResult.error("Invalid offset for PackedIndices"); + } + + int posx = offset + 18 + packedIndicesOffset; + int packedIndicesCount = VarInt.peek(buffer, posx); + if (packedIndicesCount < 0) { + return ValidationResult.error("Invalid array count for PackedIndices"); + } + + if (packedIndicesCount > 4096000) { + return ValidationResult.error("PackedIndices exceeds max length 4096000"); + } + + posx += VarInt.size(packedIndicesCount); + posx += packedIndicesCount * 1; + if (posx > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading PackedIndices"); } } @@ -142,7 +268,9 @@ public class MapImage { MapImage copy = new MapImage(); copy.width = this.width; copy.height = this.height; - copy.data = this.data != null ? Arrays.copyOf(this.data, this.data.length) : null; + copy.palette = this.palette != null ? Arrays.copyOf(this.palette, this.palette.length) : null; + copy.bitsPerIndex = this.bitsPerIndex; + copy.packedIndices = this.packedIndices != null ? Arrays.copyOf(this.packedIndices, this.packedIndices.length) : null; return copy; } @@ -151,7 +279,13 @@ public class MapImage { if (this == obj) { return true; } else { - return !(obj instanceof MapImage other) ? false : this.width == other.width && this.height == other.height && Arrays.equals(this.data, other.data); + return !(obj instanceof MapImage other) + ? false + : this.width == other.width + && this.height == other.height + && Arrays.equals(this.palette, other.palette) + && this.bitsPerIndex == other.bitsPerIndex + && Arrays.equals(this.packedIndices, other.packedIndices); } } @@ -160,6 +294,8 @@ public class MapImage { int result = 1; result = 31 * result + Integer.hashCode(this.width); result = 31 * result + Integer.hashCode(this.height); - return 31 * result + Arrays.hashCode(this.data); + result = 31 * result + Arrays.hashCode(this.palette); + result = 31 * result + Byte.hashCode(this.bitsPerIndex); + return 31 * result + Arrays.hashCode(this.packedIndices); } } diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/MapMarker.java b/src/com/hypixel/hytale/protocol/packets/worldmap/MapMarker.java index f25e1c67..42956440 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/MapMarker.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/MapMarker.java @@ -15,15 +15,13 @@ import javax.annotation.Nullable; public class MapMarker { public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 38; - public static final int VARIABLE_FIELD_COUNT = 6; - public static final int VARIABLE_BLOCK_START = 62; + public static final int VARIABLE_FIELD_COUNT = 5; + public static final int VARIABLE_BLOCK_START = 58; public static final int MAX_SIZE = 1677721600; @Nonnull public String id = ""; @Nullable public FormattedMessage name; - @Nullable - public String customName; @Nonnull public String markerImage = ""; @Nonnull @@ -39,7 +37,6 @@ public class MapMarker { public MapMarker( @Nonnull String id, @Nullable FormattedMessage name, - @Nullable String customName, @Nonnull String markerImage, @Nonnull Transform transform, @Nullable ContextMenuItem[] contextMenuItems, @@ -47,7 +44,6 @@ public class MapMarker { ) { this.id = id; this.name = name; - this.customName = customName; this.markerImage = markerImage; this.transform = transform; this.contextMenuItems = contextMenuItems; @@ -57,7 +53,6 @@ public class MapMarker { public MapMarker(@Nonnull MapMarker other) { this.id = other.id; this.name = other.name; - this.customName = other.customName; this.markerImage = other.markerImage; this.transform = other.transform; this.contextMenuItems = other.contextMenuItems; @@ -66,168 +61,203 @@ public class MapMarker { @Nonnull public static MapMarker deserialize(@Nonnull ByteBuf buf, int offset) { - MapMarker obj = new MapMarker(); - byte nullBits = buf.getByte(offset); - obj.transform = Transform.deserialize(buf, offset + 1); - int varPos0 = offset + 62 + buf.getIntLE(offset + 38); - int idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); - } else if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); + if (buf.readableBytes() - offset < 58) { + throw ProtocolException.bufferTooSmall("MapMarker", 58, buf.readableBytes() - offset); } else { - obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - if ((nullBits & 1) != 0) { - varPos0 = offset + 62 + buf.getIntLE(offset + 42); - obj.name = FormattedMessage.deserialize(buf, varPos0); - } - - if ((nullBits & 2) != 0) { - varPos0 = offset + 62 + buf.getIntLE(offset + 46); - idLen = VarInt.peek(buf, varPos0); + MapMarker obj = new MapMarker(); + byte nullBits = buf.getByte(offset); + obj.transform = Transform.deserialize(buf, offset + 1); + int varPosBase0 = buf.getIntLE(offset + 38); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 58) { + int varPos0 = offset + 58 + varPosBase0; + int idLen = VarInt.peek(buf, varPos0); if (idLen < 0) { - throw ProtocolException.negativeLength("CustomName", idLen); + throw ProtocolException.invalidVarInt("Id"); + } else { + int idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } else if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } else { + obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 1) != 0) { + varPosBase0 = buf.getIntLE(offset + 42); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 58) { + throw ProtocolException.invalidOffset("Name", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 58 + varPosBase0; + obj.name = FormattedMessage.deserialize(buf, varPos0); + } + + varPosBase0 = buf.getIntLE(offset + 46); + if (varPosBase0 >= 0 && varPosBase0 <= buf.writerIndex() - offset - 58) { + varPos0 = offset + 58 + varPosBase0; + idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("MarkerImage"); + } else { + idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("MarkerImage", idLen, 4096000); + } else if (varPos0 + idVarIntLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("MarkerImage", varPos0 + idVarIntLen + idLen, buf.readableBytes()); + } else { + obj.markerImage = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); + if ((nullBits & 2) != 0) { + varPosBase0 = buf.getIntLE(offset + 50); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 58) { + throw ProtocolException.invalidOffset("ContextMenuItems", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 58 + varPosBase0; + idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("ContextMenuItems"); + } + + idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.arrayTooLong("ContextMenuItems", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen * 0L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("ContextMenuItems", varPos0 + idVarIntLen + idLen * 0, buf.readableBytes()); + } + + obj.contextMenuItems = new ContextMenuItem[idLen]; + int elemPos = varPos0 + idVarIntLen; + + for (int i = 0; i < idLen; i++) { + obj.contextMenuItems[i] = ContextMenuItem.deserialize(buf, elemPos); + elemPos += ContextMenuItem.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 4) != 0) { + varPosBase0 = buf.getIntLE(offset + 54); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 58) { + throw ProtocolException.invalidOffset("Components", varPosBase0, buf.readableBytes()); + } + + varPos0 = offset + 58 + varPosBase0; + idLen = VarInt.peek(buf, varPos0); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Components"); + } + + idVarIntLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.arrayTooLong("Components", idLen, 4096000); + } + + if (varPos0 + idVarIntLen + idLen * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Components", varPos0 + idVarIntLen + idLen * 1, buf.readableBytes()); + } + + obj.components = new MapMarkerComponent[idLen]; + int elemPos = varPos0 + idVarIntLen; + + for (int i = 0; i < idLen; i++) { + obj.components[i] = MapMarkerComponent.deserialize(buf, elemPos); + elemPos += MapMarkerComponent.computeBytesConsumed(buf, elemPos); + } + } + + return obj; + } + } + } else { + throw ProtocolException.invalidOffset("MarkerImage", varPosBase0, buf.readableBytes()); + } + } } - - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("CustomName", idLen, 4096000); - } - - obj.customName = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - } - - varPos0 = offset + 62 + buf.getIntLE(offset + 50); - idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("MarkerImage", idLen); - } else if (idLen > 4096000) { - throw ProtocolException.stringTooLong("MarkerImage", idLen, 4096000); } else { - obj.markerImage = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); - if ((nullBits & 4) != 0) { - varPos0 = offset + 62 + buf.getIntLE(offset + 54); - idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("ContextMenuItems", idLen); - } - - if (idLen > 4096000) { - throw ProtocolException.arrayTooLong("ContextMenuItems", idLen, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + idLen * 0L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("ContextMenuItems", varPos0 + varIntLen + idLen * 0, buf.readableBytes()); - } - - obj.contextMenuItems = new ContextMenuItem[idLen]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < idLen; i++) { - obj.contextMenuItems[i] = ContextMenuItem.deserialize(buf, elemPos); - elemPos += ContextMenuItem.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 8) != 0) { - varPos0 = offset + 62 + buf.getIntLE(offset + 58); - idLen = VarInt.peek(buf, varPos0); - if (idLen < 0) { - throw ProtocolException.negativeLength("Components", idLen); - } - - if (idLen > 4096000) { - throw ProtocolException.arrayTooLong("Components", idLen, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + idLen * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Components", varPos0 + varIntLen + idLen * 1, buf.readableBytes()); - } - - obj.components = new MapMarkerComponent[idLen]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < idLen; i++) { - obj.components[i] = MapMarkerComponent.deserialize(buf, elemPos); - elemPos += MapMarkerComponent.computeBytesConsumed(buf, elemPos); - } - } - - return obj; + throw ProtocolException.invalidOffset("Id", varPosBase0, buf.readableBytes()); } } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { byte nullBits = buf.getByte(offset); - int maxEnd = 62; + int maxEnd = 58; int fieldOffset0 = buf.getIntLE(offset + 38); - int pos0 = offset + 62 + fieldOffset0; - int sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - - if ((nullBits & 1) != 0) { - fieldOffset0 = buf.getIntLE(offset + 42); - pos0 = offset + 62 + fieldOffset0; - pos0 += FormattedMessage.computeBytesConsumed(buf, pos0); + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 58) { + int pos0 = offset + 58 + fieldOffset0; + int sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset; } - } - if ((nullBits & 2) != 0) { + if ((nullBits & 1) != 0) { + fieldOffset0 = buf.getIntLE(offset + 42); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 58) { + throw ProtocolException.invalidOffset("Name", fieldOffset0, maxEnd); + } + + pos0 = offset + 58 + fieldOffset0; + pos0 += FormattedMessage.computeBytesConsumed(buf, pos0); + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + fieldOffset0 = buf.getIntLE(offset + 46); - pos0 = offset + 62 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; + if (fieldOffset0 >= 0 && fieldOffset0 <= buf.writerIndex() - offset - 58) { + pos0 = offset + 58 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl) + sl; + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + + if ((nullBits & 2) != 0) { + fieldOffset0 = buf.getIntLE(offset + 50); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 58) { + throw ProtocolException.invalidOffset("ContextMenuItems", fieldOffset0, maxEnd); + } + + pos0 = offset + 58 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl); + + for (int i = 0; i < sl; i++) { + pos0 += ContextMenuItem.computeBytesConsumed(buf, pos0); + } + + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + if ((nullBits & 4) != 0) { + fieldOffset0 = buf.getIntLE(offset + 54); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 58) { + throw ProtocolException.invalidOffset("Components", fieldOffset0, maxEnd); + } + + pos0 = offset + 58 + fieldOffset0; + sl = VarInt.peek(buf, pos0); + pos0 += VarInt.size(sl); + + for (int i = 0; i < sl; i++) { + pos0 += MapMarkerComponent.computeBytesConsumed(buf, pos0); + } + + if (pos0 - offset > maxEnd) { + maxEnd = pos0 - offset; + } + } + + return maxEnd; + } else { + throw ProtocolException.invalidOffset("MarkerImage", fieldOffset0, maxEnd); } + } else { + throw ProtocolException.invalidOffset("Id", fieldOffset0, maxEnd); } - - fieldOffset0 = buf.getIntLE(offset + 50); - pos0 = offset + 62 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0) + sl; - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - - if ((nullBits & 4) != 0) { - fieldOffset0 = buf.getIntLE(offset + 54); - pos0 = offset + 62 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); - - for (int i = 0; i < sl; i++) { - pos0 += ContextMenuItem.computeBytesConsumed(buf, pos0); - } - - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - } - - if ((nullBits & 8) != 0) { - fieldOffset0 = buf.getIntLE(offset + 58); - pos0 = offset + 62 + fieldOffset0; - sl = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); - - for (int i = 0; i < sl; i++) { - pos0 += MapMarkerComponent.computeBytesConsumed(buf, pos0); - } - - if (pos0 - offset > maxEnd) { - maxEnd = pos0 - offset; - } - } - - return maxEnd; } public void serialize(@Nonnull ByteBuf buf) { @@ -237,16 +267,12 @@ public class MapMarker { nullBits = (byte)(nullBits | 1); } - if (this.customName != null) { + if (this.contextMenuItems != null) { nullBits = (byte)(nullBits | 2); } - if (this.contextMenuItems != null) { - nullBits = (byte)(nullBits | 4); - } - if (this.components != null) { - nullBits = (byte)(nullBits | 8); + nullBits = (byte)(nullBits | 4); } buf.writeByte(nullBits); @@ -255,8 +281,6 @@ public class MapMarker { buf.writeIntLE(0); int nameOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); - int customNameOffsetSlot = buf.writerIndex(); - buf.writeIntLE(0); int markerImageOffsetSlot = buf.writerIndex(); buf.writeIntLE(0); int contextMenuItemsOffsetSlot = buf.writerIndex(); @@ -273,13 +297,6 @@ public class MapMarker { buf.setIntLE(nameOffsetSlot, -1); } - if (this.customName != null) { - buf.setIntLE(customNameOffsetSlot, buf.writerIndex() - varBlockStart); - PacketIO.writeVarString(buf, this.customName, 4096000); - } else { - buf.setIntLE(customNameOffsetSlot, -1); - } - buf.setIntLE(markerImageOffsetSlot, buf.writerIndex() - varBlockStart); PacketIO.writeVarString(buf, this.markerImage, 4096000); if (this.contextMenuItems != null) { @@ -314,16 +331,12 @@ public class MapMarker { } public int computeSize() { - int size = 62; + int size = 58; size += PacketIO.stringSize(this.id); if (this.name != null) { size += this.name.computeSize(); } - if (this.customName != null) { - size += PacketIO.stringSize(this.customName); - } - size += PacketIO.stringSize(this.markerImage); if (this.contextMenuItems != null) { int contextMenuItemsSize = 0; @@ -349,166 +362,119 @@ public class MapMarker { } public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { - if (buffer.readableBytes() - offset < 62) { - return ValidationResult.error("Buffer too small: expected at least 62 bytes"); + if (buffer.readableBytes() - offset < 58) { + return ValidationResult.error("Buffer too small: expected at least 58 bytes"); } else { byte nullBits = buffer.getByte(offset); int idOffset = buffer.getIntLE(offset + 38); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Id"); - } else { - int pos = offset + 62 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Id"); + if (idOffset >= 0 && idOffset <= buffer.writerIndex() - offset - 58) { + int pos = offset + 58 + idOffset; + int idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for Id"); + } else if (idLen > 4096000) { + return ValidationResult.error("Id exceeds max length 4096000"); } else { - int idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for Id"); - } else if (idLen > 4096000) { - return ValidationResult.error("Id exceeds max length 4096000"); + pos += VarInt.size(idLen); + pos += idLen; + if (pos > buffer.writerIndex()) { + return ValidationResult.error("Buffer overflow reading Id"); } else { - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading Id"); - } else { - if ((nullBits & 1) != 0) { - idOffset = buffer.getIntLE(offset + 42); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Name"); - } - - pos = offset + 62 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Name"); - } - - ValidationResult nameResult = FormattedMessage.validateStructure(buffer, pos); - if (!nameResult.isValid()) { - return ValidationResult.error("Invalid Name: " + nameResult.error()); - } - - pos += FormattedMessage.computeBytesConsumed(buffer, pos); + if ((nullBits & 1) != 0) { + idOffset = buffer.getIntLE(offset + 42); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 58) { + return ValidationResult.error("Invalid offset for Name"); } - if ((nullBits & 2) != 0) { - idOffset = buffer.getIntLE(offset + 46); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for CustomName"); - } + pos = offset + 58 + idOffset; + ValidationResult nameResult = FormattedMessage.validateStructure(buffer, pos); + if (!nameResult.isValid()) { + return ValidationResult.error("Invalid Name: " + nameResult.error()); + } - pos = offset + 62 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for CustomName"); - } + pos += FormattedMessage.computeBytesConsumed(buffer, pos); + } - idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for CustomName"); - } - - if (idLen > 4096000) { - return ValidationResult.error("CustomName exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); + idOffset = buffer.getIntLE(offset + 46); + if (idOffset >= 0 && idOffset <= buffer.writerIndex() - offset - 58) { + pos = offset + 58 + idOffset; + idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid string length for MarkerImage"); + } else if (idLen > 4096000) { + return ValidationResult.error("MarkerImage exceeds max length 4096000"); + } else { + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading CustomName"); - } - } - - idOffset = buffer.getIntLE(offset + 50); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for MarkerImage"); - } else { - pos = offset + 62 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for MarkerImage"); + return ValidationResult.error("Buffer overflow reading MarkerImage"); } else { - idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid string length for MarkerImage"); - } else if (idLen > 4096000) { - return ValidationResult.error("MarkerImage exceeds max length 4096000"); - } else { - pos += VarInt.length(buffer, pos); - pos += idLen; - if (pos > buffer.writerIndex()) { - return ValidationResult.error("Buffer overflow reading MarkerImage"); - } else { - if ((nullBits & 4) != 0) { - idOffset = buffer.getIntLE(offset + 54); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for ContextMenuItems"); - } + if ((nullBits & 2) != 0) { + idOffset = buffer.getIntLE(offset + 50); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 58) { + return ValidationResult.error("Invalid offset for ContextMenuItems"); + } - pos = offset + 62 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for ContextMenuItems"); - } + pos = offset + 58 + idOffset; + idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid array count for ContextMenuItems"); + } - idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid array count for ContextMenuItems"); - } + if (idLen > 4096000) { + return ValidationResult.error("ContextMenuItems exceeds max length 4096000"); + } - if (idLen > 4096000) { - return ValidationResult.error("ContextMenuItems exceeds max length 4096000"); - } + pos += VarInt.size(idLen); - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < idLen; i++) { - ValidationResult structResult = ContextMenuItem.validateStructure(buffer, pos); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid ContextMenuItem in ContextMenuItems[" + i + "]: " + structResult.error()); - } - - pos += ContextMenuItem.computeBytesConsumed(buffer, pos); - } + for (int i = 0; i < idLen; i++) { + ValidationResult structResult = ContextMenuItem.validateStructure(buffer, pos); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid ContextMenuItem in ContextMenuItems[" + i + "]: " + structResult.error()); } - if ((nullBits & 8) != 0) { - idOffset = buffer.getIntLE(offset + 58); - if (idOffset < 0) { - return ValidationResult.error("Invalid offset for Components"); - } - - pos = offset + 62 + idOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Components"); - } - - idLen = VarInt.peek(buffer, pos); - if (idLen < 0) { - return ValidationResult.error("Invalid array count for Components"); - } - - if (idLen > 4096000) { - return ValidationResult.error("Components exceeds max length 4096000"); - } - - pos += VarInt.length(buffer, pos); - - for (int i = 0; i < idLen; i++) { - ValidationResult structResult = MapMarkerComponent.validateStructure(buffer, pos); - if (!structResult.isValid()) { - return ValidationResult.error("Invalid MapMarkerComponent in Components[" + i + "]: " + structResult.error()); - } - - pos += MapMarkerComponent.computeBytesConsumed(buffer, pos); - } - } - - return ValidationResult.OK; + pos += ContextMenuItem.computeBytesConsumed(buffer, pos); } } + + if ((nullBits & 4) != 0) { + idOffset = buffer.getIntLE(offset + 54); + if (idOffset < 0 || idOffset > buffer.writerIndex() - offset - 58) { + return ValidationResult.error("Invalid offset for Components"); + } + + pos = offset + 58 + idOffset; + idLen = VarInt.peek(buffer, pos); + if (idLen < 0) { + return ValidationResult.error("Invalid array count for Components"); + } + + if (idLen > 4096000) { + return ValidationResult.error("Components exceeds max length 4096000"); + } + + pos += VarInt.size(idLen); + + for (int i = 0; i < idLen; i++) { + ValidationResult structResult = MapMarkerComponent.validateStructure(buffer, pos); + if (!structResult.isValid()) { + return ValidationResult.error("Invalid MapMarkerComponent in Components[" + i + "]: " + structResult.error()); + } + + pos += MapMarkerComponent.computeBytesConsumed(buffer, pos); + } + } + + return ValidationResult.OK; } } + } else { + return ValidationResult.error("Invalid offset for MarkerImage"); } } } + } else { + return ValidationResult.error("Invalid offset for Id"); } } } @@ -517,7 +483,6 @@ public class MapMarker { MapMarker copy = new MapMarker(); copy.id = this.id; copy.name = this.name != null ? this.name.clone() : null; - copy.customName = this.customName; copy.markerImage = this.markerImage; copy.transform = this.transform.clone(); copy.contextMenuItems = this.contextMenuItems != null ? Arrays.stream(this.contextMenuItems).map(e -> e.clone()).toArray(ContextMenuItem[]::new) : null; @@ -534,7 +499,6 @@ public class MapMarker { ? false : Objects.equals(this.id, other.id) && Objects.equals(this.name, other.name) - && Objects.equals(this.customName, other.customName) && Objects.equals(this.markerImage, other.markerImage) && Objects.equals(this.transform, other.transform) && Arrays.equals((Object[])this.contextMenuItems, (Object[])other.contextMenuItems) @@ -547,7 +511,6 @@ public class MapMarker { int result = 1; result = 31 * result + Objects.hashCode(this.id); result = 31 * result + Objects.hashCode(this.name); - result = 31 * result + Objects.hashCode(this.customName); result = 31 * result + Objects.hashCode(this.markerImage); result = 31 * result + Objects.hashCode(this.transform); result = 31 * result + Arrays.hashCode((Object[])this.contextMenuItems); diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/MapMarkerComponent.java b/src/com/hypixel/hytale/protocol/packets/worldmap/MapMarkerComponent.java index 71b592d5..7c00d3d8 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/MapMarkerComponent.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/MapMarkerComponent.java @@ -12,7 +12,7 @@ public abstract class MapMarkerComponent { @Nonnull public static MapMarkerComponent deserialize(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return (MapMarkerComponent)(switch (typeId) { case 0 -> PlayerMarkerComponent.deserialize(buf, offset + typeIdLen); @@ -25,7 +25,7 @@ public abstract class MapMarkerComponent { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { int typeId = VarInt.peek(buf, offset); - int typeIdLen = VarInt.length(buf, offset); + int typeIdLen = VarInt.size(typeId); return typeIdLen + switch (typeId) { case 0 -> PlayerMarkerComponent.computeBytesConsumed(buf, offset + typeIdLen); @@ -67,7 +67,7 @@ public abstract class MapMarkerComponent { public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { int typeId = VarInt.peek(buffer, offset); - int typeIdLen = VarInt.length(buffer, offset); + int typeIdLen = VarInt.size(typeId); return switch (typeId) { case 0 -> PlayerMarkerComponent.validateStructure(buffer, offset + typeIdLen); diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/PlacedByMarkerComponent.java b/src/com/hypixel/hytale/protocol/packets/worldmap/PlacedByMarkerComponent.java index 0d0ce6b3..75b6395a 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/PlacedByMarkerComponent.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/PlacedByMarkerComponent.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.protocol.packets.worldmap; import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -34,12 +35,16 @@ public class PlacedByMarkerComponent extends MapMarkerComponent { @Nonnull public static PlacedByMarkerComponent deserialize(@Nonnull ByteBuf buf, int offset) { - PlacedByMarkerComponent obj = new PlacedByMarkerComponent(); - obj.playerId = PacketIO.readUUID(buf, offset + 0); - int pos = offset + 16; - obj.name = FormattedMessage.deserialize(buf, pos); - pos += FormattedMessage.computeBytesConsumed(buf, pos); - return obj; + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("PlacedByMarkerComponent", 16, buf.readableBytes() - offset); + } else { + PlacedByMarkerComponent obj = new PlacedByMarkerComponent(); + obj.playerId = PacketIO.readUUID(buf, offset + 0); + int pos = offset + 16; + obj.name = FormattedMessage.deserialize(buf, pos); + pos += FormattedMessage.computeBytesConsumed(buf, pos); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/PlayerMarkerComponent.java b/src/com/hypixel/hytale/protocol/packets/worldmap/PlayerMarkerComponent.java index 0b74b8d6..d9de3f9b 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/PlayerMarkerComponent.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/PlayerMarkerComponent.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.protocol.packets.worldmap; import com.hypixel.hytale.protocol.io.PacketIO; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -29,9 +30,13 @@ public class PlayerMarkerComponent extends MapMarkerComponent { @Nonnull public static PlayerMarkerComponent deserialize(@Nonnull ByteBuf buf, int offset) { - PlayerMarkerComponent obj = new PlayerMarkerComponent(); - obj.playerId = PacketIO.readUUID(buf, offset + 0); - return obj; + if (buf.readableBytes() - offset < 16) { + throw ProtocolException.bufferTooSmall("PlayerMarkerComponent", 16, buf.readableBytes() - offset); + } else { + PlayerMarkerComponent obj = new PlayerMarkerComponent(); + obj.playerId = PacketIO.readUUID(buf, offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/TeleportToWorldMapMarker.java b/src/com/hypixel/hytale/protocol/packets/worldmap/TeleportToWorldMapMarker.java index 9192d9f6..50d60f26 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/TeleportToWorldMapMarker.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/TeleportToWorldMapMarker.java @@ -46,25 +46,33 @@ public class TeleportToWorldMapMarker implements Packet, ToServerPacket { @Nonnull public static TeleportToWorldMapMarker deserialize(@Nonnull ByteBuf buf, int offset) { - TeleportToWorldMapMarker obj = new TeleportToWorldMapMarker(); - byte nullBits = buf.getByte(offset); - int pos = offset + 1; - if ((nullBits & 1) != 0) { - int idLen = VarInt.peek(buf, pos); - if (idLen < 0) { - throw ProtocolException.negativeLength("Id", idLen); + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("TeleportToWorldMapMarker", 1, buf.readableBytes() - offset); + } else { + TeleportToWorldMapMarker obj = new TeleportToWorldMapMarker(); + byte nullBits = buf.getByte(offset); + int pos = offset + 1; + if ((nullBits & 1) != 0) { + int idLen = VarInt.peek(buf, pos); + if (idLen < 0) { + throw ProtocolException.invalidVarInt("Id"); + } + + int idVarLen = VarInt.size(idLen); + if (idLen > 4096000) { + throw ProtocolException.stringTooLong("Id", idLen, 4096000); + } + + if (pos + idVarLen + idLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Id", pos + idVarLen + idLen, buf.readableBytes()); + } + + obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); + pos += idVarLen + idLen; } - if (idLen > 4096000) { - throw ProtocolException.stringTooLong("Id", idLen, 4096000); - } - - int idVarLen = VarInt.length(buf, pos); - obj.id = PacketIO.readVarString(buf, pos, PacketIO.UTF8); - pos += idVarLen + idLen; + return obj; } - - return obj; } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -72,7 +80,7 @@ public class TeleportToWorldMapMarker implements Packet, ToServerPacket { int pos = offset + 1; if ((nullBits & 1) != 0) { int sl = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos) + sl; + pos += VarInt.size(sl) + sl; } return pos - offset; @@ -117,7 +125,7 @@ public class TeleportToWorldMapMarker implements Packet, ToServerPacket { return ValidationResult.error("Id exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(idLen); pos += idLen; if (pos > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading Id"); diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/TeleportToWorldMapPosition.java b/src/com/hypixel/hytale/protocol/packets/worldmap/TeleportToWorldMapPosition.java index 3466bab2..16368011 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/TeleportToWorldMapPosition.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/TeleportToWorldMapPosition.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.worldmap; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -44,10 +45,14 @@ public class TeleportToWorldMapPosition implements Packet, ToServerPacket { @Nonnull public static TeleportToWorldMapPosition deserialize(@Nonnull ByteBuf buf, int offset) { - TeleportToWorldMapPosition obj = new TeleportToWorldMapPosition(); - obj.x = buf.getIntLE(offset + 0); - obj.y = buf.getIntLE(offset + 4); - return obj; + if (buf.readableBytes() - offset < 8) { + throw ProtocolException.bufferTooSmall("TeleportToWorldMapPosition", 8, buf.readableBytes() - offset); + } else { + TeleportToWorldMapPosition obj = new TeleportToWorldMapPosition(); + obj.x = buf.getIntLE(offset + 0); + obj.y = buf.getIntLE(offset + 4); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/TintComponent.java b/src/com/hypixel/hytale/protocol/packets/worldmap/TintComponent.java index d13b3f63..35c41f6e 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/TintComponent.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/TintComponent.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.protocol.packets.worldmap; import com.hypixel.hytale.protocol.Color; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -28,9 +29,13 @@ public class TintComponent extends MapMarkerComponent { @Nonnull public static TintComponent deserialize(@Nonnull ByteBuf buf, int offset) { - TintComponent obj = new TintComponent(); - obj.color = Color.deserialize(buf, offset + 0); - return obj; + if (buf.readableBytes() - offset < 3) { + throw ProtocolException.bufferTooSmall("TintComponent", 3, buf.readableBytes() - offset); + } else { + TintComponent obj = new TintComponent(); + obj.color = Color.deserialize(buf, offset + 0); + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMap.java b/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMap.java index 6874dedf..1be3914a 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMap.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMap.java @@ -54,94 +54,117 @@ public class UpdateWorldMap implements Packet, ToClientPacket { @Nonnull public static UpdateWorldMap deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateWorldMap obj = new UpdateWorldMap(); - byte nullBits = buf.getByte(offset); - if ((nullBits & 1) != 0) { - int varPos0 = offset + 13 + buf.getIntLE(offset + 1); - int chunksCount = VarInt.peek(buf, varPos0); - if (chunksCount < 0) { - throw ProtocolException.negativeLength("Chunks", chunksCount); - } - - if (chunksCount > 4096000) { - throw ProtocolException.arrayTooLong("Chunks", chunksCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos0); - if (varPos0 + varIntLen + chunksCount * 9L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("Chunks", varPos0 + varIntLen + chunksCount * 9, buf.readableBytes()); - } - - obj.chunks = new MapChunk[chunksCount]; - int elemPos = varPos0 + varIntLen; - - for (int i = 0; i < chunksCount; i++) { - obj.chunks[i] = MapChunk.deserialize(buf, elemPos); - elemPos += MapChunk.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 2) != 0) { - int varPos1 = offset + 13 + buf.getIntLE(offset + 5); - int addedMarkersCount = VarInt.peek(buf, varPos1); - if (addedMarkersCount < 0) { - throw ProtocolException.negativeLength("AddedMarkers", addedMarkersCount); - } - - if (addedMarkersCount > 4096000) { - throw ProtocolException.arrayTooLong("AddedMarkers", addedMarkersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos1); - if (varPos1 + varIntLen + addedMarkersCount * 38L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("AddedMarkers", varPos1 + varIntLen + addedMarkersCount * 38, buf.readableBytes()); - } - - obj.addedMarkers = new MapMarker[addedMarkersCount]; - int elemPos = varPos1 + varIntLen; - - for (int i = 0; i < addedMarkersCount; i++) { - obj.addedMarkers[i] = MapMarker.deserialize(buf, elemPos); - elemPos += MapMarker.computeBytesConsumed(buf, elemPos); - } - } - - if ((nullBits & 4) != 0) { - int varPos2 = offset + 13 + buf.getIntLE(offset + 9); - int removedMarkersCount = VarInt.peek(buf, varPos2); - if (removedMarkersCount < 0) { - throw ProtocolException.negativeLength("RemovedMarkers", removedMarkersCount); - } - - if (removedMarkersCount > 4096000) { - throw ProtocolException.arrayTooLong("RemovedMarkers", removedMarkersCount, 4096000); - } - - int varIntLen = VarInt.length(buf, varPos2); - if (varPos2 + varIntLen + removedMarkersCount * 1L > buf.readableBytes()) { - throw ProtocolException.bufferTooSmall("RemovedMarkers", varPos2 + varIntLen + removedMarkersCount * 1, buf.readableBytes()); - } - - obj.removedMarkers = new String[removedMarkersCount]; - int elemPos = varPos2 + varIntLen; - - for (int i = 0; i < removedMarkersCount; i++) { - int strLen = VarInt.peek(buf, elemPos); - if (strLen < 0) { - throw ProtocolException.negativeLength("removedMarkers[" + i + "]", strLen); + if (buf.readableBytes() - offset < 13) { + throw ProtocolException.bufferTooSmall("UpdateWorldMap", 13, buf.readableBytes() - offset); + } else { + UpdateWorldMap obj = new UpdateWorldMap(); + byte nullBits = buf.getByte(offset); + if ((nullBits & 1) != 0) { + int varPosBase0 = buf.getIntLE(offset + 1); + if (varPosBase0 < 0 || varPosBase0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Chunks", varPosBase0, buf.readableBytes()); } - if (strLen > 4096000) { - throw ProtocolException.stringTooLong("removedMarkers[" + i + "]", strLen, 4096000); + int varPos0 = offset + 13 + varPosBase0; + int chunksCount = VarInt.peek(buf, varPos0); + if (chunksCount < 0) { + throw ProtocolException.invalidVarInt("Chunks"); } - int strVarLen = VarInt.length(buf, elemPos); - obj.removedMarkers[i] = PacketIO.readVarString(buf, elemPos); - elemPos += strVarLen + strLen; - } - } + int varIntLen = VarInt.size(chunksCount); + if (chunksCount > 4096000) { + throw ProtocolException.arrayTooLong("Chunks", chunksCount, 4096000); + } - return obj; + if (varPos0 + varIntLen + chunksCount * 9L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("Chunks", varPos0 + varIntLen + chunksCount * 9, buf.readableBytes()); + } + + obj.chunks = new MapChunk[chunksCount]; + int elemPos = varPos0 + varIntLen; + + for (int i = 0; i < chunksCount; i++) { + obj.chunks[i] = MapChunk.deserialize(buf, elemPos); + elemPos += MapChunk.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 2) != 0) { + int varPosBase1 = buf.getIntLE(offset + 5); + if (varPosBase1 < 0 || varPosBase1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("AddedMarkers", varPosBase1, buf.readableBytes()); + } + + int varPos1 = offset + 13 + varPosBase1; + int addedMarkersCount = VarInt.peek(buf, varPos1); + if (addedMarkersCount < 0) { + throw ProtocolException.invalidVarInt("AddedMarkers"); + } + + int varIntLenx = VarInt.size(addedMarkersCount); + if (addedMarkersCount > 4096000) { + throw ProtocolException.arrayTooLong("AddedMarkers", addedMarkersCount, 4096000); + } + + if (varPos1 + varIntLenx + addedMarkersCount * 38L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("AddedMarkers", varPos1 + varIntLenx + addedMarkersCount * 38, buf.readableBytes()); + } + + obj.addedMarkers = new MapMarker[addedMarkersCount]; + int elemPos = varPos1 + varIntLenx; + + for (int i = 0; i < addedMarkersCount; i++) { + obj.addedMarkers[i] = MapMarker.deserialize(buf, elemPos); + elemPos += MapMarker.computeBytesConsumed(buf, elemPos); + } + } + + if ((nullBits & 4) != 0) { + int varPosBase2 = buf.getIntLE(offset + 9); + if (varPosBase2 < 0 || varPosBase2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("RemovedMarkers", varPosBase2, buf.readableBytes()); + } + + int varPos2 = offset + 13 + varPosBase2; + int removedMarkersCount = VarInt.peek(buf, varPos2); + if (removedMarkersCount < 0) { + throw ProtocolException.invalidVarInt("RemovedMarkers"); + } + + int varIntLenxx = VarInt.size(removedMarkersCount); + if (removedMarkersCount > 4096000) { + throw ProtocolException.arrayTooLong("RemovedMarkers", removedMarkersCount, 4096000); + } + + if (varPos2 + varIntLenxx + removedMarkersCount * 1L > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("RemovedMarkers", varPos2 + varIntLenxx + removedMarkersCount * 1, buf.readableBytes()); + } + + obj.removedMarkers = new String[removedMarkersCount]; + int elemPos = varPos2 + varIntLenxx; + + for (int i = 0; i < removedMarkersCount; i++) { + int strLen = VarInt.peek(buf, elemPos); + if (strLen < 0) { + throw ProtocolException.invalidVarInt("removedMarkers[" + i + "]"); + } + + int strVarLen = VarInt.size(strLen); + if (strLen > 4096000) { + throw ProtocolException.stringTooLong("removedMarkers[" + i + "]", strLen, 4096000); + } + + if (elemPos + strVarLen + strLen > buf.readableBytes()) { + throw ProtocolException.bufferTooSmall("removedMarkers[" + i + "]", elemPos + strVarLen + strLen, buf.readableBytes()); + } + + obj.removedMarkers[i] = PacketIO.readVarString(buf, elemPos); + elemPos += strVarLen + strLen; + } + } + + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -149,9 +172,13 @@ public class UpdateWorldMap implements Packet, ToClientPacket { int maxEnd = 13; if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 1); + if (fieldOffset0 < 0 || fieldOffset0 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("Chunks", fieldOffset0, maxEnd); + } + int pos0 = offset + 13 + fieldOffset0; int arrLen = VarInt.peek(buf, pos0); - pos0 += VarInt.length(buf, pos0); + pos0 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos0 += MapChunk.computeBytesConsumed(buf, pos0); @@ -164,9 +191,13 @@ public class UpdateWorldMap implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int fieldOffset1 = buf.getIntLE(offset + 5); + if (fieldOffset1 < 0 || fieldOffset1 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("AddedMarkers", fieldOffset1, maxEnd); + } + int pos1 = offset + 13 + fieldOffset1; int arrLen = VarInt.peek(buf, pos1); - pos1 += VarInt.length(buf, pos1); + pos1 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { pos1 += MapMarker.computeBytesConsumed(buf, pos1); @@ -179,13 +210,17 @@ public class UpdateWorldMap implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int fieldOffset2 = buf.getIntLE(offset + 9); + if (fieldOffset2 < 0 || fieldOffset2 > buf.writerIndex() - offset - 13) { + throw ProtocolException.invalidOffset("RemovedMarkers", fieldOffset2, maxEnd); + } + int pos2 = offset + 13 + fieldOffset2; int arrLen = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2); + pos2 += VarInt.size(arrLen); for (int i = 0; i < arrLen; i++) { int sl = VarInt.peek(buf, pos2); - pos2 += VarInt.length(buf, pos2) + sl; + pos2 += VarInt.size(sl) + sl; } if (pos2 - offset > maxEnd) { @@ -309,15 +344,11 @@ public class UpdateWorldMap implements Packet, ToClientPacket { byte nullBits = buffer.getByte(offset); if ((nullBits & 1) != 0) { int chunksOffset = buffer.getIntLE(offset + 1); - if (chunksOffset < 0) { + if (chunksOffset < 0 || chunksOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for Chunks"); } int pos = offset + 13 + chunksOffset; - if (pos >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for Chunks"); - } - int chunksCount = VarInt.peek(buffer, pos); if (chunksCount < 0) { return ValidationResult.error("Invalid array count for Chunks"); @@ -327,7 +358,7 @@ public class UpdateWorldMap implements Packet, ToClientPacket { return ValidationResult.error("Chunks exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(chunksCount); for (int i = 0; i < chunksCount; i++) { ValidationResult structResult = MapChunk.validateStructure(buffer, pos); @@ -341,15 +372,11 @@ public class UpdateWorldMap implements Packet, ToClientPacket { if ((nullBits & 2) != 0) { int addedMarkersOffset = buffer.getIntLE(offset + 5); - if (addedMarkersOffset < 0) { + if (addedMarkersOffset < 0 || addedMarkersOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for AddedMarkers"); } int posx = offset + 13 + addedMarkersOffset; - if (posx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for AddedMarkers"); - } - int addedMarkersCount = VarInt.peek(buffer, posx); if (addedMarkersCount < 0) { return ValidationResult.error("Invalid array count for AddedMarkers"); @@ -359,7 +386,7 @@ public class UpdateWorldMap implements Packet, ToClientPacket { return ValidationResult.error("AddedMarkers exceeds max length 4096000"); } - posx += VarInt.length(buffer, posx); + posx += VarInt.size(addedMarkersCount); for (int i = 0; i < addedMarkersCount; i++) { ValidationResult structResult = MapMarker.validateStructure(buffer, posx); @@ -373,15 +400,11 @@ public class UpdateWorldMap implements Packet, ToClientPacket { if ((nullBits & 4) != 0) { int removedMarkersOffset = buffer.getIntLE(offset + 9); - if (removedMarkersOffset < 0) { + if (removedMarkersOffset < 0 || removedMarkersOffset > buffer.writerIndex() - offset - 13) { return ValidationResult.error("Invalid offset for RemovedMarkers"); } int posxx = offset + 13 + removedMarkersOffset; - if (posxx >= buffer.writerIndex()) { - return ValidationResult.error("Offset out of bounds for RemovedMarkers"); - } - int removedMarkersCount = VarInt.peek(buffer, posxx); if (removedMarkersCount < 0) { return ValidationResult.error("Invalid array count for RemovedMarkers"); @@ -391,7 +414,7 @@ public class UpdateWorldMap implements Packet, ToClientPacket { return ValidationResult.error("RemovedMarkers exceeds max length 4096000"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(removedMarkersCount); for (int i = 0; i < removedMarkersCount; i++) { int strLen = VarInt.peek(buffer, posxx); @@ -399,7 +422,7 @@ public class UpdateWorldMap implements Packet, ToClientPacket { return ValidationResult.error("Invalid string length in RemovedMarkers"); } - posxx += VarInt.length(buffer, posxx); + posxx += VarInt.size(strLen); posxx += strLen; if (posxx > buffer.writerIndex()) { return ValidationResult.error("Buffer overflow reading string in RemovedMarkers"); diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMapSettings.java b/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMapSettings.java index 7ad5b745..fda05ec9 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMapSettings.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMapSettings.java @@ -90,44 +90,49 @@ public class UpdateWorldMapSettings implements Packet, ToClientPacket { @Nonnull public static UpdateWorldMapSettings deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateWorldMapSettings obj = new UpdateWorldMapSettings(); - byte nullBits = buf.getByte(offset); - obj.enabled = buf.getByte(offset + 1) != 0; - obj.allowTeleportToCoordinates = buf.getByte(offset + 2) != 0; - obj.allowTeleportToMarkers = buf.getByte(offset + 3) != 0; - obj.allowShowOnMapToggle = buf.getByte(offset + 4) != 0; - obj.allowCompassTrackingToggle = buf.getByte(offset + 5) != 0; - obj.allowCreatingMapMarkers = buf.getByte(offset + 6) != 0; - obj.allowRemovingOtherPlayersMarkers = buf.getByte(offset + 7) != 0; - obj.defaultScale = buf.getFloatLE(offset + 8); - obj.minScale = buf.getFloatLE(offset + 12); - obj.maxScale = buf.getFloatLE(offset + 16); - int pos = offset + 20; - if ((nullBits & 1) != 0) { - int biomeDataMapCount = VarInt.peek(buf, pos); - if (biomeDataMapCount < 0) { - throw ProtocolException.negativeLength("BiomeDataMap", biomeDataMapCount); - } + if (buf.readableBytes() - offset < 20) { + throw ProtocolException.bufferTooSmall("UpdateWorldMapSettings", 20, buf.readableBytes() - offset); + } else { + UpdateWorldMapSettings obj = new UpdateWorldMapSettings(); + byte nullBits = buf.getByte(offset); + obj.enabled = buf.getByte(offset + 1) != 0; + obj.allowTeleportToCoordinates = buf.getByte(offset + 2) != 0; + obj.allowTeleportToMarkers = buf.getByte(offset + 3) != 0; + obj.allowShowOnMapToggle = buf.getByte(offset + 4) != 0; + obj.allowCompassTrackingToggle = buf.getByte(offset + 5) != 0; + obj.allowCreatingMapMarkers = buf.getByte(offset + 6) != 0; + obj.allowRemovingOtherPlayersMarkers = buf.getByte(offset + 7) != 0; + obj.defaultScale = buf.getFloatLE(offset + 8); + obj.minScale = buf.getFloatLE(offset + 12); + obj.maxScale = buf.getFloatLE(offset + 16); + int pos = offset + 20; + if ((nullBits & 1) != 0) { + int biomeDataMapCount = VarInt.peek(buf, pos); + if (biomeDataMapCount < 0) { + throw ProtocolException.invalidVarInt("BiomeDataMap"); + } - if (biomeDataMapCount > 4096000) { - throw ProtocolException.dictionaryTooLarge("BiomeDataMap", biomeDataMapCount, 4096000); - } + int biomeDataMapVarLen = VarInt.size(biomeDataMapCount); + if (biomeDataMapCount > 4096000) { + throw ProtocolException.dictionaryTooLarge("BiomeDataMap", biomeDataMapCount, 4096000); + } - pos += VarInt.size(biomeDataMapCount); - obj.biomeDataMap = new HashMap<>(biomeDataMapCount); + pos += biomeDataMapVarLen; + obj.biomeDataMap = new HashMap<>(biomeDataMapCount); - for (int i = 0; i < biomeDataMapCount; i++) { - short key = buf.getShortLE(pos); - pos += 2; - BiomeData val = BiomeData.deserialize(buf, pos); - pos += BiomeData.computeBytesConsumed(buf, pos); - if (obj.biomeDataMap.put(key, val) != null) { - throw ProtocolException.duplicateKey("biomeDataMap", key); + for (int i = 0; i < biomeDataMapCount; i++) { + short key = buf.getShortLE(pos); + pos += 2; + BiomeData val = BiomeData.deserialize(buf, pos); + pos += BiomeData.computeBytesConsumed(buf, pos); + if (obj.biomeDataMap.put(key, val) != null) { + throw ProtocolException.duplicateKey("biomeDataMap", key); + } } } - } - return obj; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { @@ -135,7 +140,7 @@ public class UpdateWorldMapSettings implements Packet, ToClientPacket { int pos = offset + 20; if ((nullBits & 1) != 0) { int dictLen = VarInt.peek(buf, pos); - pos += VarInt.length(buf, pos); + pos += VarInt.size(dictLen); for (int i = 0; i < dictLen; i++) { pos += 2; @@ -210,7 +215,7 @@ public class UpdateWorldMapSettings implements Packet, ToClientPacket { return ValidationResult.error("BiomeDataMap exceeds max length 4096000"); } - pos += VarInt.length(buffer, pos); + pos += VarInt.size(biomeDataMapCount); for (int i = 0; i < biomeDataMapCount; i++) { pos += 2; diff --git a/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMapVisible.java b/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMapVisible.java index 6e916851..dea4e298 100644 --- a/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMapVisible.java +++ b/src/com/hypixel/hytale/protocol/packets/worldmap/UpdateWorldMapVisible.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.protocol.packets.worldmap; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToServerPacket; +import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ValidationResult; import io.netty.buffer.ByteBuf; import java.util.Objects; @@ -41,9 +42,13 @@ public class UpdateWorldMapVisible implements Packet, ToServerPacket { @Nonnull public static UpdateWorldMapVisible deserialize(@Nonnull ByteBuf buf, int offset) { - UpdateWorldMapVisible obj = new UpdateWorldMapVisible(); - obj.visible = buf.getByte(offset + 0) != 0; - return obj; + if (buf.readableBytes() - offset < 1) { + throw ProtocolException.bufferTooSmall("UpdateWorldMapVisible", 1, buf.readableBytes() - offset); + } else { + UpdateWorldMapVisible obj = new UpdateWorldMapVisible(); + obj.visible = buf.getByte(offset + 0) != 0; + return obj; + } } public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { diff --git a/src/com/hypixel/hytale/server/core/Constants.java b/src/com/hypixel/hytale/server/core/Constants.java index 9fe67f00..1647206e 100644 --- a/src/com/hypixel/hytale/server/core/Constants.java +++ b/src/com/hypixel/hytale/server/core/Constants.java @@ -7,6 +7,7 @@ import com.hypixel.hytale.server.core.blocktype.BlockTypeModule; import com.hypixel.hytale.server.core.console.ConsoleModule; import com.hypixel.hytale.server.core.cosmetics.CosmeticsModule; import com.hypixel.hytale.server.core.io.ServerManager; +import com.hypixel.hytale.server.core.liveconfig.LiveConfigModule; import com.hypixel.hytale.server.core.modules.LegacyModule; import com.hypixel.hytale.server.core.modules.accesscontrol.AccessControlModule; import com.hypixel.hytale.server.core.modules.anchoraction.AnchorActionModule; @@ -31,10 +32,10 @@ import com.hypixel.hytale.server.core.modules.serverplayerlist.ServerPlayerListM import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule; import com.hypixel.hytale.server.core.modules.splitvelocity.SplitVelocity; import com.hypixel.hytale.server.core.modules.time.TimeModule; +import com.hypixel.hytale.server.core.modules.voice.VoiceModule; import com.hypixel.hytale.server.core.permissions.PermissionsModule; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.connectedblocks.ConnectedBlocksModule; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.update.UpdateModule; import java.nio.file.Files; import java.nio.file.Path; @@ -54,6 +55,7 @@ public final class Constants { ConsoleModule.MANIFEST, PermissionsModule.MANIFEST, UpdateModule.MANIFEST, + LiveConfigModule.MANIFEST, FlyCameraModule.MANIFEST, AssetModule.MANIFEST, CommonAssetModule.MANIFEST, @@ -64,7 +66,6 @@ public final class Constants { BlockTypeModule.MANIFEST, LegacyModule.MANIFEST, BlockModule.MANIFEST, - BlockStateModule.MANIFEST, CollisionModule.MANIFEST, BlockSetModule.MANIFEST, MigrationModule.MANIFEST, @@ -82,6 +83,7 @@ public final class Constants { DebugPlugin.MANIFEST, ProjectileModule.MANIFEST, ServerPlayerListModule.MANIFEST, + VoiceModule.MANIFEST, AccessControlModule.MANIFEST, SingleplayerModule.MANIFEST, Universe.MANIFEST, @@ -106,8 +108,4 @@ public final class Constants { private static Path getUniversePath() { return OPTION_SET.has(Options.UNIVERSE) ? OPTION_SET.valueOf(Options.UNIVERSE) : Path.of("universe"); } - - public static boolean shouldSkipModValidation() { - return OPTION_SET.has(Options.SKIP_MOD_VALIDATION) || HytaleServer.get().getConfig().shouldSkipModValidation(); - } } diff --git a/src/com/hypixel/hytale/server/core/HytaleServer.java b/src/com/hypixel/hytale/server/core/HytaleServer.java index 2a60afc3..526ed4d0 100644 --- a/src/com/hypixel/hytale/server/core/HytaleServer.java +++ b/src/com/hypixel/hytale/server/core/HytaleServer.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.server.core; import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.EmptyExtraInfo; import com.hypixel.hytale.common.plugin.PluginManifest; import com.hypixel.hytale.common.thread.HytaleForkJoinThreadFactory; import com.hypixel.hytale.common.util.FormatUtil; @@ -35,10 +36,12 @@ import com.hypixel.hytale.server.core.plugin.PluginBase; import com.hypixel.hytale.server.core.plugin.PluginClassLoader; import com.hypixel.hytale.server.core.plugin.PluginManager; import com.hypixel.hytale.server.core.plugin.PluginState; +import com.hypixel.hytale.server.core.schema.SchemaGenerator; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.datastore.DataStoreProvider; import com.hypixel.hytale.server.core.universe.datastore.DiskDataStoreProvider; import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.WorldConfig; import com.hypixel.hytale.server.core.update.UpdateModule; import com.hypixel.hytale.server.core.util.concurrent.ThreadUtil; import io.netty.handler.codec.quic.Quic; @@ -264,22 +267,33 @@ public class HytaleServer { } }, "ShutdownHook"); shutdownHook.setDaemon(false); - Runtime.getRuntime().addShutdownHook(shutdownHook); - AssetRegistryLoader.init(); - - for (PluginManifest manifest : Constants.CORE_PLUGINS) { - this.pluginManager.registerCorePlugin(manifest); - } - - GCUtil.register(info -> { - Universe universe = Universe.get(); - if (universe != null) { - for (World world : universe.getWorlds().values()) { - world.markGCHasRun(); + if (!this.isShuttingDown()) { + try { + Runtime.getRuntime().addShutdownHook(shutdownHook); + } catch (IllegalStateException var9) { + if (!this.isShuttingDown()) { + throw var9; } + + return; } - }); - this.boot(); + + AssetRegistryLoader.init(); + + for (PluginManifest manifest : Constants.CORE_PLUGINS) { + this.pluginManager.registerCorePlugin(manifest); + } + + GCUtil.register(info -> { + Universe universe = Universe.get(); + if (universe != null) { + for (World world : universe.getWorlds().values()) { + world.markGCHasRun(); + } + } + }); + this.boot(); + } } @Nonnull @@ -335,7 +349,8 @@ public class HytaleServer { List reasons = loadAssetEvent.getReasons(); String join = String.join("\n", reasons); LOGGER.at(Level.SEVERE).log("Asset validation FAILED with %d reason(s):\n%s", reasons.size(), join); - this.shutdownServer(ShutdownReason.VALIDATE_ERROR.withMessage(join)); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.validateError.detail").param("detail", join); + this.shutdownServer(ShutdownReason.VALIDATE_ERROR.withMessage(reasonMessage)); return; } @@ -345,6 +360,20 @@ public class HytaleServer { return; } + SchemaGenerator.registerConfig("HytaleServerConfig", HytaleServerConfig.CODEC, "Config", List.of("/config.json")); + SchemaGenerator.registerConfig("WorldConfig", WorldConfig.CODEC, "Config", List.of("/worlds/*/config.json")); + boolean generateAssets = Options.getOptionSet().has(Options.GENERATE_ASSET_SCHEMA); + boolean generateConfigs = Options.getOptionSet().has(Options.GENERATE_CONFIG_SCHEMA); + if (generateAssets || generateConfigs) { + SchemaGenerator.generate( + generateAssets ? Options.getOptionSet().valueOf(Options.GENERATE_ASSET_SCHEMA) : null, + generateConfigs ? Options.getOptionSet().valueOf(Options.GENERATE_CONFIG_SCHEMA) : null + ); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.shutdown.schemaGenerated"); + this.shutdownServer(ShutdownReason.SHUTDOWN.withMessage(reasonMessage)); + return; + } + this.pluginsProgress = 0; this.sendSingleplayerProgress(); if (this.isShuttingDown()) { @@ -358,7 +387,7 @@ public class HytaleServer { return; } - this.sendSingleplayerSignal("-=|Enabled|0"); + this.reportSingleplayerStatus(Message.translation("client.gameLoadingView.status.enabled")); } catch (Throwable var6) { LOGGER.at(Level.SEVERE).withCause(var6).log("Failed to boot HytaleServer!"); Throwable t = var6; @@ -367,65 +396,69 @@ public class HytaleServer { t = t.getCause(); } - this.shutdownServer(ShutdownReason.CRASH.withMessage("Failed to start server! " + t.getMessage())); + this.shutdownServer( + ShutdownReason.CRASH.withMessage(Message.translation("client.disconnection.shutdownReason.crash.startFailed").param("detail", t.getMessage())) + ); } if (this.hytaleServerConfig.consumeHasChanged()) { HytaleServerConfig.save(this.hytaleServerConfig).join(); } - SCHEDULED_EXECUTOR.scheduleWithFixedDelay(() -> { - try { - if (this.hytaleServerConfig.consumeHasChanged()) { - HytaleServerConfig.save(this.hytaleServerConfig).join(); + if (!this.isShuttingDown()) { + SCHEDULED_EXECUTOR.scheduleWithFixedDelay(() -> { + try { + if (this.hytaleServerConfig.consumeHasChanged()) { + HytaleServerConfig.save(this.hytaleServerConfig).join(); + } + } catch (Exception var2x) { + LOGGER.at(Level.SEVERE).withCause(var2x).log("Failed to save server config!"); } - } catch (Exception var2x) { - LOGGER.at(Level.SEVERE).withCause(var2x).log("Failed to save server config!"); + }, 1L, 1L, TimeUnit.MINUTES); + LOGGER.at(Level.INFO).log("Getting Hytale Universe ready..."); + Universe.get().getUniverseReady().join(); + LOGGER.at(Level.INFO).log("Universe ready!"); + List tags = new ObjectArrayList<>(); + if (Constants.SINGLEPLAYER) { + tags.add("Singleplayer"); + } else { + tags.add("Multiplayer"); } - }, 1L, 1L, TimeUnit.MINUTES); - LOGGER.at(Level.INFO).log("Getting Hytale Universe ready..."); - Universe.get().getUniverseReady().join(); - LOGGER.at(Level.INFO).log("Universe ready!"); - List tags = new ObjectArrayList<>(); - if (Constants.SINGLEPLAYER) { - tags.add("Singleplayer"); - } else { - tags.add("Multiplayer"); - } - if (Constants.FRESH_UNIVERSE) { - tags.add("Fresh Universe"); - } + if (Constants.FRESH_UNIVERSE) { + tags.add("Fresh Universe"); + } - this.booted.set(true); - ServerManager.get().waitForBindComplete(); - this.eventBus.dispatch(BootEvent.class); - List bootCommands = Options.getOptionSet().valuesOf(Options.BOOT_COMMAND); - if (!bootCommands.isEmpty()) { - CommandManager.get().handleCommands(ConsoleSender.INSTANCE, new ArrayDeque<>(bootCommands)).join(); - } + this.booted.set(true); + ServerManager.get().waitForBindComplete(); + this.eventBus.dispatch(BootEvent.class); + List bootCommands = Options.getOptionSet().valuesOf(Options.BOOT_COMMAND); + if (!bootCommands.isEmpty()) { + CommandManager.get().handleCommands(ConsoleSender.INSTANCE, new ArrayDeque<>(bootCommands)).join(); + } - String border = "\u001b[0;32m==============================================================================================="; - LOGGER.at(Level.INFO).log("\u001b[0;32m==============================================================================================="); - LOGGER.at(Level.INFO) - .log( - "%s Hytale Server Booted! [%s] took %s", - "\u001b[0;32m", - String.join(", ", tags), - FormatUtil.nanosToString(System.nanoTime() - this.bootStart) - ); - LOGGER.at(Level.INFO).log("\u001b[0;32m==============================================================================================="); - UpdateModule updateModule = UpdateModule.get(); - if (updateModule != null) { - updateModule.onServerReady(); - } + String border = "\u001b[0;32m==============================================================================================="; + LOGGER.at(Level.INFO).log("\u001b[0;32m==============================================================================================="); + LOGGER.at(Level.INFO) + .log( + "%s Hytale Server Booted! [%s] took %s", + "\u001b[0;32m", + String.join(", ", tags), + FormatUtil.nanosToString(System.nanoTime() - this.bootStart) + ); + LOGGER.at(Level.INFO).log("\u001b[0;32m==============================================================================================="); + UpdateModule updateModule = UpdateModule.get(); + if (updateModule != null) { + updateModule.onServerReady(); + } - ServerAuthManager authManager = ServerAuthManager.getInstance(); - if (!authManager.isSingleplayer() && authManager.getAuthMode() == ServerAuthManager.AuthMode.NONE) { - LOGGER.at(Level.WARNING).log("%sNo server tokens configured. Use /auth login to authenticate.", "\u001b[0;31m"); - } + ServerAuthManager authManager = ServerAuthManager.getInstance(); + if (!authManager.isSingleplayer() && authManager.getAuthMode() == ServerAuthManager.AuthMode.NONE) { + LOGGER.at(Level.WARNING).log("%sNo server tokens configured. Use /auth login to authenticate.", "\u001b[0;31m"); + } - this.sendSingleplayerSignal(">> Singleplayer Ready <<"); + this.sendSingleplayerSignal(">> Singleplayer Ready <<"); + } } } @@ -436,8 +469,9 @@ public class HytaleServer { public void shutdownServer(@Nonnull ShutdownReason reason) { Objects.requireNonNull(reason, "Server shutdown reason can't be null!"); if (this.shutdown.getAndSet(reason) == null) { - if (reason.getMessage() != null) { - this.sendSingleplayerSignal("-=|Shutdown|" + reason.getMessage().replace("\n", "\\n")); + if (reason.getFormattedMessage() != null) { + String json = Message.CODEC.encode(new Message(reason.getFormattedMessage()), EmptyExtraInfo.EMPTY).toString(); + this.sendSingleplayerSignal("-=|Shutdown|" + json); } Thread shutdownThread = new Thread(() -> this.shutdown0(reason), "ShutdownThread"); @@ -496,11 +530,18 @@ public class HytaleServer { public void sendSingleplayerProgress() { List plugins = this.pluginManager.getPlugins(); if (this.shutdown.get() != null) { - this.sendSingleplayerSignal("-=|Shutdown Modules|" + MathUtil.round((double)(plugins.size() - this.pluginsProgress) / plugins.size(), 2) * 100.0); + this.reportSingleplayerStatus( + Message.translation("client.gameLoadingView.status.shuttingDownModules"), + plugins.isEmpty() ? 100.0 : MathUtil.round((double)(plugins.size() - this.pluginsProgress) / plugins.size(), 2) * 100.0 + ); } else if (this.pluginManager.getState() == PluginState.SETUP) { - this.sendSingleplayerSignal("-=|Setup|" + MathUtil.round((double)this.pluginsProgress / plugins.size(), 2) * 100.0); + this.reportSingleplayerStatus( + Message.translation("client.gameLoadingView.status.settingUp"), MathUtil.round((double)this.pluginsProgress / plugins.size(), 2) * 100.0 + ); } else if (this.pluginManager.getState() == PluginState.START) { - this.sendSingleplayerSignal("-=|Starting|" + MathUtil.round((double)this.pluginsProgress / plugins.size(), 2) * 100.0); + this.reportSingleplayerStatus( + Message.translation("client.gameLoadingView.status.starting"), MathUtil.round((double)this.pluginsProgress / plugins.size(), 2) * 100.0 + ); } } @@ -540,15 +581,14 @@ public class HytaleServer { } } - public void reportSingleplayerStatus(String message) { - if (Constants.SINGLEPLAYER) { - HytaleLoggerBackend.rawLog("-=|" + message + "|0"); - } + public void reportSingleplayerStatus(@Nonnull Message message) { + this.reportSingleplayerStatus(message, 0.0); } - public void reportSingleplayerStatus(String message, double progress) { + public void reportSingleplayerStatus(@Nonnull Message message, double progress) { if (Constants.SINGLEPLAYER) { - HytaleLoggerBackend.rawLog("-=|" + message + "|" + progress); + String json = Message.CODEC.encode(message, EmptyExtraInfo.EMPTY).toString(); + HytaleLoggerBackend.rawLog("-=|" + json + "|" + progress); } } @@ -556,7 +596,7 @@ public class HytaleServer { if (this.isShuttingDown()) { double progress = MathUtil.round((double)saved / total, 2) * 100.0; if (Constants.SINGLEPLAYER) { - this.sendSingleplayerSignal("-=|Saving world " + world.getName() + " chunks|" + progress); + this.reportSingleplayerStatus(Message.translation("client.gameLoadingView.status.savingChunks").param("name", world.getName()), progress); } else if (total < 10 || saved % (total / 10) == 0) { world.getLogger().at(Level.INFO).log("Saving chunks: %.0f%%", progress); } diff --git a/src/com/hypixel/hytale/server/core/HytaleServerConfig.java b/src/com/hypixel/hytale/server/core/HytaleServerConfig.java index 83918038..c5c12f0c 100644 --- a/src/com/hypixel/hytale/server/core/HytaleServerConfig.java +++ b/src/com/hypixel/hytale/server/core/HytaleServerConfig.java @@ -5,19 +5,23 @@ import com.hypixel.hytale.codec.DocumentContainingCodec; import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.codecs.map.MapCodec; import com.hypixel.hytale.codec.codecs.map.ObjectMapCodec; +import com.hypixel.hytale.codec.function.FunctionCodec; import com.hypixel.hytale.codec.lookup.Priority; import com.hypixel.hytale.codec.util.RawJsonReader; +import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.common.plugin.PluginIdentifier; -import com.hypixel.hytale.common.util.java.ManifestUtil; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.protocol.GameMode; +import com.hypixel.hytale.protocol.HostAddress; import com.hypixel.hytale.server.core.auth.AuthCredentialStoreProvider; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.config.BackupConfig; import com.hypixel.hytale.server.core.config.ModConfig; import com.hypixel.hytale.server.core.config.RateLimitConfig; +import com.hypixel.hytale.server.core.config.ServerWorldMapConfig; import com.hypixel.hytale.server.core.config.UpdateConfig; import com.hypixel.hytale.server.core.universe.playerdata.DefaultPlayerStorageProvider; import com.hypixel.hytale.server.core.universe.playerdata.DiskPlayerStorageProvider; @@ -44,6 +48,7 @@ public class HytaleServerConfig { public static final int DEFAULT_MAX_VIEW_RADIUS = 32; @Nonnull public static final Path PATH = Path.of("config.json"); + public static final PluginIdentifier[] PLUGIN_IDENTIFIERS = new PluginIdentifier[0]; @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder(HytaleServerConfig.class, HytaleServerConfig::new) .versioned() @@ -91,6 +96,15 @@ public class HytaleServerConfig { o -> o.modConfig ) .add() + .append( + new KeyedCodec<>( + "ModLoadOrder", + new ArrayCodec<>(new FunctionCodec<>(Codec.STRING, PluginIdentifier::fromString, PluginIdentifier::toString), PluginIdentifier[]::new) + ), + (o, i) -> o.modLoadOrder = i, + o -> o.modLoadOrder.length == 0 ? null : o.modLoadOrder + ) + .add() .append(new KeyedCodec<>("DefaultModsEnabled", Codec.BOOLEAN), (o, v) -> o.defaultModsEnabled = v, o -> o.defaultModsEnabled) .setVersionRange(4, Integer.MAX_VALUE) .add() @@ -106,16 +120,37 @@ public class HytaleServerConfig { .add() .append(new KeyedCodec<>("Update", UpdateConfig.CODEC), (o, value) -> o.updateConfig = value, o -> o.updateConfig) .add() - .append(new KeyedCodec<>("SkipModValidationForVersion", Codec.STRING), (o, v) -> o.skipModValidationForVersion = v, o -> o.skipModValidationForVersion) - .add() .append(new KeyedCodec<>("Backup", BackupConfig.CODEC), (o, value) -> o.backupConfig = value, o -> o.backupConfig) .add() + .append( + new KeyedCodec<>("WorldMap", ServerWorldMapConfig.CODEC), + (o, value) -> o.worldMapConfig = value != null ? value : new ServerWorldMapConfig(o), + o -> o.worldMapConfig + ) + .add() + .append( + new KeyedCodec<>( + "FallbackServer", + BuilderCodec.builder(HostAddress.class, HostAddress::new) + .append(new KeyedCodec<>("Host", Codec.STRING), (o, i) -> o.host = i, o -> o.host) + .addValidator(Validators.nonNull()) + .add() + .append(new KeyedCodec<>("Port", Codec.SHORT), (o, i) -> o.port = i, o -> o.port) + .addValidator(Validators.nonNull()) + .add() + .build() + ), + (o, i) -> o.fallbackServer = i, + o -> o.fallbackServer + ) + .add() .afterDecode((config, extraInfo) -> { config.defaults.hytaleServerConfig = config; config.connectionTimeouts.setHytaleServerConfig(config); config.rateLimitConfig.setHytaleServerConfig(config); config.updateConfig.setHytaleServerConfig(config); config.backupConfig.setHytaleServerConfig(config); + config.worldMapConfig.setHytaleServerConfig(config); config.modules.values().forEach(m -> m.setHytaleServerConfig(config)); if (config.legacyPluginConfig != null && !config.legacyPluginConfig.isEmpty()) { for (Entry entry : config.legacyPluginConfig.entrySet()) { @@ -156,6 +191,7 @@ public class HytaleServerConfig { private transient Map legacyPluginConfig; @Nonnull private Map modConfig = new ConcurrentHashMap<>(); + private PluginIdentifier[] modLoadOrder = PLUGIN_IDENTIFIERS; @Nullable private Boolean defaultModsEnabled; @Nonnull @@ -173,8 +209,10 @@ public class HytaleServerConfig { private UpdateConfig updateConfig = new UpdateConfig(this); @Nonnull private BackupConfig backupConfig = new BackupConfig(this); + @Nonnull + private ServerWorldMapConfig worldMapConfig = new ServerWorldMapConfig(this); @Nullable - private String skipModValidationForVersion; + private HostAddress fallbackServer; public static void setBoot(@Nonnull HytaleServerConfig serverConfig, @Nonnull PluginIdentifier identifier, boolean enabled) { serverConfig.modConfig.computeIfAbsent(identifier, id -> new ModConfig()).setEnabled(enabled); @@ -302,6 +340,10 @@ public class HytaleServerConfig { return this.defaultModsEnabled != null ? this.defaultModsEnabled : !Constants.SINGLEPLAYER; } + public PluginIdentifier[] getModLoadOrder() { + return this.modLoadOrder; + } + @Nonnull public PlayerStorageProvider getPlayerStorageProvider() { return this.playerStorageProvider; @@ -353,8 +395,24 @@ public class HytaleServerConfig { this.markChanged(); } - public boolean shouldSkipModValidation() { - return this.skipModValidationForVersion != null && this.skipModValidationForVersion.equals(ManifestUtil.getImplementationRevisionId()); + @Nullable + public HostAddress getFallbackServer() { + return this.fallbackServer; + } + + public void setFallbackServer(@Nullable HostAddress fallbackServer) { + this.fallbackServer = fallbackServer; + this.markChanged(); + } + + @Nonnull + public ServerWorldMapConfig getWorldMapConfig() { + return this.worldMapConfig; + } + + public void setWorldMapConfig(@Nonnull ServerWorldMapConfig worldMapConfig) { + this.worldMapConfig = worldMapConfig; + this.markChanged(); } public void removeModule(@Nonnull String module) { diff --git a/src/com/hypixel/hytale/server/core/Options.java b/src/com/hypixel/hytale/server/core/Options.java index f1d59262..29cad17e 100644 --- a/src/com/hypixel/hytale/server/core/Options.java +++ b/src/com/hypixel/hytale/server/core/Options.java @@ -81,9 +81,16 @@ public class Options { public static final OptionSpec SHUTDOWN_AFTER_VALIDATE = PARSER.accepts( "shutdown-after-validate", "Automatically shutdown the server after asset and/or prefab validation." ); - public static final OptionSpec GENERATE_SCHEMA = PARSER.accepts( - "generate-schema", "Causes the server generate schema, save it into the assets directory and then exit" - ); + public static final OptionSpec GENERATE_ASSET_SCHEMA = PARSER.accepts( + "generate-asset-schema", "Generate asset JSON schemas to the specified directory and exit" + ) + .withRequiredArg() + .withValuesConvertedBy(new Options.PathConverter(Options.PathConverter.PathType.ANY)); + public static final OptionSpec GENERATE_CONFIG_SCHEMA = PARSER.accepts( + "generate-config-schema", "Generate config JSON schemas to the specified directory and exit" + ) + .withRequiredArg() + .withValuesConvertedBy(new Options.PathConverter(Options.PathConverter.PathType.ANY)); public static final OptionSpec WORLD_GEN_DIRECTORY = PARSER.accepts("world-gen", "World gen directory") .withRequiredArg() .withValuesConvertedBy(new Options.PathConverter(Options.PathConverter.PathType.DIR)); @@ -122,8 +129,8 @@ public class Options { ) .withRequiredArg() .withValuesSeparatedBy(','); - public static final OptionSpec SKIP_MOD_VALIDATION = PARSER.accepts( - "skip-mod-validation", "Skips mod validation, attempting to allow the server to boot even if one fails to load" + public static final OptionSpec IGNORE_BROKEN_MODS = PARSER.accepts( + "ignore-broken-mods", "Ignores broken mods, attempting to allow the server to boot even if one fails to load" ); public static final String ALLOW_SELF_OP_COMMAND_STRING = "allow-op"; public static final OptionSpec ALLOW_SELF_OP_COMMAND = PARSER.accepts("allow-op"); @@ -135,6 +142,13 @@ public class Options { .withRequiredArg() .ofType(String.class); public static final OptionSpec IDENTITY_TOKEN = PARSER.accepts("identity-token", "Identity token (JWT)").withRequiredArg().ofType(String.class); + public static final OptionSpec VERIFY_WORLDS = PARSER.accepts("verify-worlds", "Verify all worlds and then exits"); + public static final OptionSpec RECOVERY_MODE = PARSER.accepts( + "recovery-mode", "How to handle broken chunks when encountered during recovery" + ) + .availableIf(VERIFY_WORLDS) + .withRequiredArg() + .ofType(Options.RecoveryMode.class); private static OptionSet optionSet; public static OptionSet getOptionSet() { @@ -307,6 +321,11 @@ public class Options { } } + public static enum RecoveryMode { + FROM_BACKUP_OR_REGENERATE, + REGENERATE; + } + public static class SocketAddressValueConverter implements ValueConverter { @Nonnull public InetSocketAddress convert(@Nonnull String value) { diff --git a/src/com/hypixel/hytale/server/core/ShutdownReason.java b/src/com/hypixel/hytale/server/core/ShutdownReason.java index 218c518a..686fa7c0 100644 --- a/src/com/hypixel/hytale/server/core/ShutdownReason.java +++ b/src/com/hypixel/hytale/server/core/ShutdownReason.java @@ -1,6 +1,9 @@ package com.hypixel.hytale.server.core; +import com.hypixel.hytale.protocol.FormattedMessage; +import com.hypixel.hytale.server.core.util.MessageUtil; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ShutdownReason { public static final ShutdownReason SIGINT = new ShutdownReason(130); @@ -14,14 +17,16 @@ public class ShutdownReason { public static final ShutdownReason MISSING_ASSETS = new ShutdownReason(7); public static final ShutdownReason UPDATE = new ShutdownReason(8); public static final ShutdownReason MOD_ERROR = new ShutdownReason(9); + public static final ShutdownReason VERIFY_ERROR = new ShutdownReason(10); private final int exitCode; - private final String message; + @Nullable + private final FormattedMessage message; public ShutdownReason(int exitCode) { this(exitCode, null); } - public ShutdownReason(int exitCode, String message) { + public ShutdownReason(int exitCode, @Nullable FormattedMessage message) { this.exitCode = exitCode; this.message = message; } @@ -30,18 +35,24 @@ public class ShutdownReason { return this.exitCode; } + @Nullable public String getMessage() { + return this.message != null ? MessageUtil.formatMessageToPlainString(this.message) : null; + } + + @Nullable + public FormattedMessage getFormattedMessage() { return this.message; } @Nonnull - public ShutdownReason withMessage(String message) { - return new ShutdownReason(this.exitCode, message); + public ShutdownReason withMessage(@Nonnull Message message) { + return new ShutdownReason(this.exitCode, message.getFormattedMessage()); } @Nonnull @Override public String toString() { - return "ShutdownReason{exitCode=" + this.exitCode + ", message='" + this.message + "'}"; + return "ShutdownReason{exitCode=" + this.exitCode + ", message='" + this.getMessage() + "'}"; } } diff --git a/src/com/hypixel/hytale/server/core/asset/AssetModule.java b/src/com/hypixel/hytale/server/core/asset/AssetModule.java index 1af4a947..72c052c0 100644 --- a/src/com/hypixel/hytale/server/core/asset/AssetModule.java +++ b/src/com/hypixel/hytale/server/core/asset/AssetModule.java @@ -10,14 +10,15 @@ import com.hypixel.hytale.assetstore.event.RemoveAssetStoreEvent; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.codec.util.RawJsonReader; +import com.hypixel.hytale.common.plugin.Mod; +import com.hypixel.hytale.common.plugin.ModLoadOrderException; import com.hypixel.hytale.common.plugin.PluginIdentifier; import com.hypixel.hytale.common.plugin.PluginManifest; +import com.hypixel.hytale.common.semver.SemverRange; import com.hypixel.hytale.common.util.FormatUtil; import com.hypixel.hytale.common.util.PathUtil; import com.hypixel.hytale.common.util.java.ManifestUtil; -import com.hypixel.hytale.event.EventPriority; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.server.core.Constants; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.HytaleServerConfig; import com.hypixel.hytale.server.core.Message; @@ -29,7 +30,6 @@ import com.hypixel.hytale.server.core.asset.type.gameplay.respawn.RespawnControl import com.hypixel.hytale.server.core.asset.type.gameplay.respawn.WorldSpawnPoint; import com.hypixel.hytale.server.core.asset.type.item.DroplistCommand; import com.hypixel.hytale.server.core.config.ModConfig; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.BootEvent; import com.hypixel.hytale.server.core.event.events.player.AddPlayerToWorldEvent; import com.hypixel.hytale.server.core.plugin.JavaPlugin; @@ -54,10 +54,10 @@ import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; @@ -70,7 +70,7 @@ public class AssetModule extends JavaPlugin { @Nullable private AssetMonitor assetMonitor; @Nonnull - private final List assetPacks = new CopyOnWriteArrayList<>(); + private List assetPacks = new CopyOnWriteArrayList<>(); private final List> pendingAssetPacks = new ArrayList<>(); private boolean hasSetup = false; private boolean hasLoaded = false; @@ -126,9 +126,10 @@ public class AssetModule extends JavaPlugin { } if (this.assetPacks.isEmpty()) { - HytaleServer.get().shutdownServer(ShutdownReason.MISSING_ASSETS.withMessage("Failed to load any asset packs")); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.missingAssets.failedToLoad"); + HytaleServer.get().shutdownServer(ShutdownReason.MISSING_ASSETS.withMessage(reasonMessage)); } else { - boolean hasOutdatedPacks = false; + ArrayList outdatedPacks = new ArrayList<>(); String serverVersion = ManifestUtil.getVersion(); for (AssetPack pack : this.assetPacks) { @@ -136,7 +137,7 @@ public class AssetModule extends JavaPlugin { PluginManifest manifest = pack.getManifest(); String targetServerVersion = manifest.getServerVersion(); if (targetServerVersion == null || !targetServerVersion.equals(serverVersion)) { - hasOutdatedPacks = true; + outdatedPacks.add(pack.getName()); if (targetServerVersion != null && !"*".equals(targetServerVersion)) { this.getLogger() .at(Level.WARNING) @@ -157,48 +158,35 @@ public class AssetModule extends JavaPlugin { } } - if (hasOutdatedPacks && System.getProperty("hytale.allow_outdated_mods") == null) { + if (!outdatedPacks.isEmpty() && System.getProperty("hytale.allow_outdated_mods") == null) { this.getLogger() .at(Level.SEVERE) .log("One or more asset packs are targeting an older server version. It is recommended to update these plugins to ensure compatibility."); + HytaleServer.get() + .getEventBus() + .registerGlobal( + AddPlayerToWorldEvent.class, + event -> { + PlayerRef playerRef = event.getHolder().getComponent(PlayerRef.getComponentType()); + if (playerRef != null && playerRef.hasPermission("hytale.mods.outdated.notify")) { + StringBuilder modsList = new StringBuilder(); - try { - if (!Constants.SINGLEPLAYER) { - Thread.sleep(Duration.ofSeconds(2L)); - } - } catch (InterruptedException var9) { - throw new RuntimeException(var9); - } + for (String packx : outdatedPacks) { + modsList.append("\n - ").append(packx); + } - HytaleServer.get().getEventBus().registerGlobal(AddPlayerToWorldEvent.class, event -> { - PlayerRef playerRef = event.getHolder().getComponent(PlayerRef.getComponentType()); - Player player = event.getHolder().getComponent(Player.getComponentType()); - if (playerRef != null && player != null) { - if (player.hasPermission("hytale.mods.outdated.notify")) { - playerRef.sendMessage(Message.translation("server.assetModule.outOfDatePacks").color(Color.RED)); + playerRef.sendMessage( + Message.translation("server.assetModule.outOfDatePacks") + .param("count", outdatedPacks.size()) + .param("mods", modsList.toString()) + .color(Color.RED) + ); + } } - } - }); + ); } - this.getEventRegistry().register((short)-16, LoadAssetEvent.class, event -> { - if (this.hasLoaded) { - throw new IllegalStateException("LoadAssetEvent has already been dispatched"); - } else { - AssetRegistry.ASSET_LOCK.writeLock().lock(); - - try { - this.hasLoaded = true; - AssetRegistryLoader.preLoadAssets(event); - - for (AssetPack packx : this.assetPacks) { - AssetRegistryLoader.loadAssets(event, packx); - } - } finally { - AssetRegistry.ASSET_LOCK.writeLock().unlock(); - } - } - }); + this.getEventRegistry().register((short)-16, LoadAssetEvent.class, this::loadAllAssetPacks); this.getEventRegistry().register((short)-16, AssetPackRegisterEvent.class, event -> AssetRegistryLoader.loadAssets(null, event.getAssetPack())); this.getEventRegistry().register(AssetPackUnregisterEvent.class, event -> { for (AssetStore assetStore : AssetRegistry.getStoreMap().values()) { @@ -206,7 +194,6 @@ public class AssetModule extends JavaPlugin { } }); this.getEventRegistry().register(LoadAssetEvent.class, AssetModule::validateWorldGen); - this.getEventRegistry().register(EventPriority.FIRST, LoadAssetEvent.class, SneakyThrow.sneakyConsumer(AssetRegistryLoader::writeSchemas)); this.getEventRegistry().register(RegisterAssetStoreEvent.class, this::onNewStore); this.getEventRegistry().register(RemoveAssetStoreEvent.class, this::onRemoveStore); this.getEventRegistry().registerGlobal(BootEvent.class, event -> { @@ -452,6 +439,70 @@ public class AssetModule extends JavaPlugin { } } + private void loadAllAssetPacks(LoadAssetEvent event) { + if (this.hasLoaded) { + throw new IllegalStateException("LoadAssetEvent has already been dispatched"); + } else { + AssetRegistry.ASSET_LOCK.writeLock().lock(); + + try { + this.hasLoaded = true; + AssetRegistryLoader.preLoadAssets(event); + HashMap pendingPacks = new HashMap<>(); + + for (AssetPack assetPack : this.assetPacks) { + pendingPacks.put(PluginIdentifier.fromString(assetPack.getName()), assetPack); + } + + List modOrder = List.of(HytaleServer.get().getConfig().getModLoadOrder()); + + try { + this.assetPacks = new CopyOnWriteArrayList<>( + Mod.calculateLoadOrder(pendingPacks, pluginIdentifier -> PluginManager.get().hasPlugin(pluginIdentifier, SemverRange.WILDCARD), modOrder) + ); + } catch (ModLoadOrderException var9) { + throw new IllegalStateException("Failed to calculate asset pack load order", var9); + } + + for (AssetPack pack : this.assetPacks) { + AssetRegistryLoader.loadAssets(event, pack); + } + } finally { + AssetRegistry.ASSET_LOCK.writeLock().unlock(); + } + } + } + + public boolean validatePackExistsOnDisk(@Nonnull AssetPack pack) { + if (pack.getFileSystem() != null) { + return true; + } else { + Path root = pack.getRoot(); + if (Files.isDirectory(root) && Files.exists(root.resolve("manifest.json"))) { + return true; + } else { + this.getLogger().at(Level.WARNING).log("Asset pack '%s' no longer exists on disk, unregistering", pack.getName()); + this.assetPacks.remove(pack); + HytaleServer.SCHEDULED_EXECUTOR + .execute( + () -> { + AssetRegistry.ASSET_LOCK.writeLock().lock(); + + try { + HytaleServer.get() + .getEventBus() + .dispatchFor(AssetPackUnregisterEvent.class) + .dispatch(new AssetPackUnregisterEvent(pack)); + } finally { + AssetRegistry.ASSET_LOCK.writeLock().unlock(); + } + } + ); + return false; + } + } + } + public AssetPack getAssetPack(@Nonnull String name) { for (AssetPack pack : this.assetPacks) { if (name.equals(pack.getName())) { diff --git a/src/com/hypixel/hytale/server/core/asset/AssetRegistryLoader.java b/src/com/hypixel/hytale/server/core/asset/AssetRegistryLoader.java index 21836d2e..64b7cee0 100644 --- a/src/com/hypixel/hytale/server/core/asset/AssetRegistryLoader.java +++ b/src/com/hypixel/hytale/server/core/asset/AssetRegistryLoader.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.assetstore.AssetMap; import com.hypixel.hytale.assetstore.AssetPack; import com.hypixel.hytale.assetstore.AssetRegistry; import com.hypixel.hytale.assetstore.AssetStore; -import com.hypixel.hytale.assetstore.codec.AssetCodec; -import com.hypixel.hytale.assetstore.codec.AssetCodecMapCodec; import com.hypixel.hytale.assetstore.iterator.AssetStoreIterator; import com.hypixel.hytale.assetstore.iterator.CircularDependencyException; import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; @@ -14,16 +12,11 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.IndexedAssetMap; import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; -import com.hypixel.hytale.codec.EmptyExtraInfo; -import com.hypixel.hytale.codec.schema.SchemaContext; -import com.hypixel.hytale.codec.schema.config.Schema; import com.hypixel.hytale.common.util.FormatUtil; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.protocol.ToClientPacket; -import com.hypixel.hytale.server.core.Constants; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Options; -import com.hypixel.hytale.server.core.ShutdownReason; import com.hypixel.hytale.server.core.asset.type.ambiencefx.AmbienceFXPacketGenerator; import com.hypixel.hytale.server.core.asset.type.ambiencefx.config.AmbienceFX; import com.hypixel.hytale.server.core.asset.type.audiocategory.AudioCategoryPacketGenerator; @@ -82,6 +75,8 @@ import com.hypixel.hytale.server.core.asset.type.particle.ParticleSpawnerPacketG import com.hypixel.hytale.server.core.asset.type.particle.ParticleSystemPacketGenerator; import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSpawner; import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSystem; +import com.hypixel.hytale.server.core.asset.type.physicalmaterial.PhysicalMaterialPacketGenerator; +import com.hypixel.hytale.server.core.asset.type.physicalmaterial.config.PhysicalMaterial; import com.hypixel.hytale.server.core.asset.type.portalworld.PortalType; import com.hypixel.hytale.server.core.asset.type.projectile.config.Projectile; import com.hypixel.hytale.server.core.asset.type.responsecurve.config.ExponentialResponseCurve; @@ -118,7 +113,6 @@ import com.hypixel.hytale.server.core.modules.item.ItemReticleConfigPacketGenera import com.hypixel.hytale.server.core.modules.projectile.config.ProjectileConfig; import com.hypixel.hytale.server.core.modules.projectile.config.ProjectileConfigPacketGenerator; import com.hypixel.hytale.server.core.universe.world.connectedblocks.CustomConnectedBlockTemplateAsset; -import com.hypixel.hytale.server.core.util.BsonUtil; import com.hypixel.hytale.sneakythrow.SneakyThrow; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; import java.nio.file.Files; @@ -126,21 +120,11 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.function.Consumer; import java.util.logging.Level; -import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.bson.BsonArray; -import org.bson.BsonDocument; -import org.bson.BsonInt32; -import org.bson.BsonString; -import org.bson.BsonValue; public class AssetRegistryLoader { public static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @@ -157,21 +141,25 @@ public class AssetRegistryLoader { } } - public static void loadAssets(@Nullable LoadAssetEvent event, @Nonnull AssetPack assetPack) { + public static boolean loadAssets(@Nullable LoadAssetEvent event, @Nonnull AssetPack assetPack) { AssetRegistry.ASSET_LOCK.writeLock().lock(); + boolean var3; try { - loadAssets0(event, assetPack); + boolean failed = loadAssets0(event, assetPack); AssetRegistry.HAS_INIT = true; - } catch (Throwable var6) { + var3 = failed; + } catch (Throwable var7) { if (event != null) { event.failed(true, "failed to validate assets"); } - throw SneakyThrow.sneakyThrow(var6); + throw SneakyThrow.sneakyThrow(var7); } finally { AssetRegistry.ASSET_LOCK.writeLock().unlock(); } + + return var3; } private static void preLoadAssets0(@Nonnull LoadAssetEvent event) { @@ -243,12 +231,13 @@ public class AssetRegistryLoader { iterator.close(); } - private static void loadAssets0(@Nullable LoadAssetEvent event, @Nonnull AssetPack assetPack) { + private static boolean loadAssets0(@Nullable LoadAssetEvent event, @Nonnull AssetPack assetPack) { AssetStore.DISABLE_DYNAMIC_DEPENDENCIES = true; Path serverAssetDirectory = assetPack.getRoot().resolve("Server"); HytaleLogger.getLogger().at(Level.INFO).log("Loading assets from: %s", serverAssetDirectory); long startAll = System.nanoTime(); - boolean shouldFail = Options.getOptionSet().has(Options.VALIDATE_ASSETS) || !Constants.shouldSkipModValidation(); + boolean shouldFail = Options.getOptionSet().has(Options.VALIDATE_ASSETS) + || assetPack.isImmutable() && !Options.getOptionSet().has(Options.IGNORE_BROKEN_MODS); boolean failedToLoadAsset = false; LOGGER.at(Level.INFO).log("Loading assets from %s", serverAssetDirectory); Collection> values = AssetRegistry.getStoreMap().values(); @@ -257,7 +246,7 @@ public class AssetRegistryLoader { while (iterator.hasNext()) { if (HytaleServer.get().isShuttingDown()) { LOGGER.at(Level.INFO).log("Aborted asset loading due to server shutdown!"); - return; + return failedToLoadAsset; } AssetStore>, ? extends AssetMap>> assetStore = (AssetStore>, ? extends AssetMap>>)iterator.next(); @@ -325,6 +314,8 @@ public class AssetRegistryLoader { event.failed(shouldFail, "Mod " + assetPack.getName() + " failed to load. Check for mod updates or contact the mod author."); } } + + return failedToLoadAsset; } public static void sendAssets(@Nonnull PacketHandler packetHandler) { @@ -341,127 +332,6 @@ public class AssetRegistryLoader { } } - @Nonnull - public static Map generateSchemas(@Nonnull SchemaContext context, @Nonnull BsonDocument vsCodeConfig) { - AssetStore[] values = AssetRegistry.getStoreMap().values().toArray(AssetStore[]::new); - Arrays.sort(values, Comparator.comparing(storex -> storex.getAssetClass().getSimpleName())); - BsonArray vsCodeSchemas = new BsonArray(); - BsonDocument vsCodeFiles = new BsonDocument(); - vsCodeConfig.put("json.schemas", vsCodeSchemas); - vsCodeConfig.put("files.associations", vsCodeFiles); - vsCodeConfig.put("editor.tabSize", new BsonInt32(2)); - - for (AssetStore store : values) { - Class assetClass = store.getAssetClass(); - String name = assetClass.getSimpleName(); - AssetCodec codec = store.getCodec(); - context.addFileReference(name + ".json", codec); - } - - HashMap schemas = new HashMap<>(); - - for (AssetStore store : values) { - Class assetClass = store.getAssetClass(); - String path = store.getPath(); - String name = assetClass.getSimpleName(); - AssetCodec codec = store.getCodec(); - Schema schema = codec.toSchema(context); - if (codec instanceof AssetCodecMapCodec) { - schema.setTitle(name); - } - - schema.setId(name + ".json"); - Schema.HytaleMetadata hytale = schema.getHytale(); - hytale.setPath(path); - hytale.setExtension(store.getExtension()); - Class idProvider = store.getIdProvider(); - if (idProvider != null) { - hytale.setIdProvider(idProvider.getSimpleName()); - } - - List preload = store.getPreAddedAssets(); - if (preload != null && !preload.isEmpty()) { - String[] internal = new String[preload.size()]; - - for (int i = 0; i < preload.size(); i++) { - Object p = preload.get(i); - Object k = store.getKeyFunction().apply(p); - internal[i] = k.toString(); - } - - hytale.setInternalKeys(internal); - } - - BsonDocument config = new BsonDocument(); - config.put( - "fileMatch", - new BsonArray( - List.of(new BsonString("/Server/" + path + "/*" + store.getExtension()), new BsonString("/Server/" + path + "/**/*" + store.getExtension())) - ) - ); - config.put("url", new BsonString("./Schema/" + name + ".json")); - vsCodeSchemas.add((BsonValue)config); - if (!store.getExtension().equals(".json")) { - vsCodeFiles.put("*" + store.getExtension(), new BsonString("json")); - } - - schemas.put(name + ".json", schema); - } - - HytaleServer.get() - .getEventBus() - .dispatchFor(GenerateSchemaEvent.class) - .dispatch(new GenerateSchemaEvent(schemas, context, vsCodeConfig)); - Schema definitions = new Schema(); - definitions.setDefinitions(context.getDefinitions()); - definitions.setId("common.json"); - schemas.put("common.json", definitions); - Schema otherDefinitions = new Schema(); - otherDefinitions.setDefinitions(context.getOtherDefinitions()); - otherDefinitions.setId("other.json"); - schemas.put("other.json", otherDefinitions); - return schemas; - } - - public static void writeSchemas(LoadAssetEvent event) { - if (Options.getOptionSet().has(Options.GENERATE_SCHEMA)) { - try { - AssetPack pack = AssetModule.get().getBaseAssetPack(); - if (pack.isImmutable()) { - LOGGER.at(Level.SEVERE).log("Not generating schema due launcher assets"); - HytaleServer.get().shutdownServer(ShutdownReason.VALIDATE_ERROR.withMessage("Not generating scheme due launcher assets")); - return; - } - - BsonDocument vsCodeConfig = new BsonDocument(); - Path assetDirectory = pack.getRoot(); - Path schemaDir = assetDirectory.resolve("Schema"); - Files.createDirectories(schemaDir); - - try (Stream stream = Files.walk(schemaDir, 1)) { - stream.filter(v -> v.toString().endsWith(".json")).forEach(SneakyThrow.sneakyConsumer(Files::delete)); - } - - SchemaContext context = new SchemaContext(); - Map schemas = generateSchemas(context, vsCodeConfig); - - for (Entry schema : schemas.entrySet()) { - BsonUtil.writeDocument(schemaDir.resolve(schema.getKey()), Schema.CODEC.encode(schema.getValue(), EmptyExtraInfo.EMPTY).asDocument(), false) - .join(); - } - - Files.createDirectories(assetDirectory.resolve(".vscode")); - BsonUtil.writeDocument(assetDirectory.resolve(".vscode/settings.json"), vsCodeConfig, false).join(); - } catch (Throwable var11) { - LOGGER.at(Level.SEVERE).withCause(var11).log("Schema generation failed"); - HytaleServer.get().shutdownServer(ShutdownReason.CRASH.withMessage("Schema generation failed")); - return; - } - - HytaleServer.get().shutdownServer(ShutdownReason.SHUTDOWN.withMessage("Schema generated")); - } - } - static { AssetRegistry.register( ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( @@ -481,7 +351,8 @@ public class AssetRegistryLoader { TagPattern.class, AudioCategory.class, ReverbEffect.class, - EqualizerEffect.class + EqualizerEffect.class, + PhysicalMaterial.class )) .preLoadAssets(Collections.singletonList(AmbienceFX.EMPTY))) .build() @@ -523,6 +394,18 @@ public class AssetRegistryLoader { .preLoadAssets(Collections.singletonList(BlockSoundSet.EMPTY_BLOCK_SOUND_SET))) .build() ); + AssetRegistry.register( + ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( + PhysicalMaterial.class, new IndexedLookupTableAssetMap<>(PhysicalMaterial[]::new) + ) + .setPath("Item/Block/PhysicalMaterials")) + .setCodec(PhysicalMaterial.CODEC)) + .setKeyFunction(PhysicalMaterial::getId)) + .setReplaceOnRemove(PhysicalMaterial::new)) + .setPacketGenerator(new PhysicalMaterialPacketGenerator()) + .preLoadAssets(Collections.singletonList(PhysicalMaterial.EMPTY_PHYSICAL_MATERIAL))) + .build() + ); AssetRegistry.register( ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( ItemSoundSet.class, new IndexedLookupTableAssetMap<>(ItemSoundSet[]::new) @@ -577,7 +460,9 @@ public class AssetRegistryLoader { .loadsAfter( BlockBoundingBoxes.class, BlockSoundSet.class, + PhysicalMaterial.class, SoundEvent.class, + AmbienceFX.class, BlockParticleSet.class, BlockBreakingDecal.class, CustomConnectedBlockTemplateAsset.class, diff --git a/src/com/hypixel/hytale/server/core/asset/GenerateSchemaEvent.java b/src/com/hypixel/hytale/server/core/asset/GenerateSchemaEvent.java deleted file mode 100644 index ecf1c7a4..00000000 --- a/src/com/hypixel/hytale/server/core/asset/GenerateSchemaEvent.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.hypixel.hytale.server.core.asset; - -import com.hypixel.hytale.codec.schema.SchemaContext; -import com.hypixel.hytale.codec.schema.config.Schema; -import com.hypixel.hytale.event.IEvent; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.bson.BsonArray; -import org.bson.BsonDocument; -import org.bson.BsonString; -import org.bson.BsonValue; - -public class GenerateSchemaEvent implements IEvent { - protected final Map schemas; - protected final SchemaContext context; - protected final BsonDocument vsCodeConfig; - - public GenerateSchemaEvent(Map schemas, SchemaContext context, BsonDocument vsCodeConfig) { - this.schemas = schemas; - this.context = context; - this.vsCodeConfig = vsCodeConfig; - } - - public SchemaContext getContext() { - return this.context; - } - - public BsonDocument getVsCodeConfig() { - return this.vsCodeConfig; - } - - public void addSchemaLink(String name, @Nonnull List paths, @Nullable String extension) { - BsonDocument config = new BsonDocument(); - config.put("fileMatch", new BsonArray(paths.stream().map(v -> new BsonString("/Server/" + v)).collect(Collectors.toList()))); - config.put("url", new BsonString("./Schema/" + name + ".json")); - this.vsCodeConfig.getArray("json.schemas").add((BsonValue)config); - if (extension != null && !extension.equals(".json")) { - this.vsCodeConfig.getDocument("files.associations").put("*" + extension, new BsonString("json")); - } - } - - public void addSchema(String fileName, Schema schema) { - this.schemas.put(fileName, schema); - } -} diff --git a/src/com/hypixel/hytale/server/core/asset/HytaleAssetStore.java b/src/com/hypixel/hytale/server/core/asset/HytaleAssetStore.java index 66028787..f62e2d6c 100644 --- a/src/com/hypixel/hytale/server/core/asset/HytaleAssetStore.java +++ b/src/com/hypixel/hytale/server/core/asset/HytaleAssetStore.java @@ -192,6 +192,8 @@ public class HytaleAssetStore, M extends Ass private final String packKey; public AssetStoreMonitorHandler(String packKey) { + Objects.requireNonNull(HytaleAssetStore.this); + super(); this.packKey = packKey; } diff --git a/src/com/hypixel/hytale/server/core/asset/common/CommonAssetModule.java b/src/com/hypixel/hytale/server/core/asset/common/CommonAssetModule.java index 7261563e..5add3523 100644 --- a/src/com/hypixel/hytale/server/core/asset/common/CommonAssetModule.java +++ b/src/com/hypixel/hytale/server/core/asset/common/CommonAssetModule.java @@ -392,6 +392,10 @@ public class CommonAssetModule extends JavaPlugin { long walkFileTreeStart = System.nanoTime(); final ObjectArrayList> futures = new ObjectArrayList<>(); Files.walkFileTree(commonPath, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor() { + { + Objects.requireNonNull(CommonAssetModule.this); + } + @Nonnull public FileVisitResult visitFile(@Nonnull Path path, @Nonnull BasicFileAttributes attrs) throws IOException { if (!attrs.isRegularFile()) { @@ -598,6 +602,8 @@ public class CommonAssetModule extends JavaPlugin { private final Path commonPath; public CommonAssetMonitorHandler(AssetPack pack, Path commonPath) { + Objects.requireNonNull(CommonAssetModule.this); + super(); this.pack = pack; this.commonPath = commonPath; } diff --git a/src/com/hypixel/hytale/server/core/asset/common/CommonAssetValidator.java b/src/com/hypixel/hytale/server/core/asset/common/CommonAssetValidator.java index fe93928e..bc472ce7 100644 --- a/src/com/hypixel/hytale/server/core/asset/common/CommonAssetValidator.java +++ b/src/com/hypixel/hytale/server/core/asset/common/CommonAssetValidator.java @@ -27,6 +27,7 @@ public class CommonAssetValidator implements Validator { public static final CommonAssetValidator ICON_CRAFTING = new CommonAssetValidator("png", "Icons/CraftingCategories"); public static final CommonAssetValidator ICON_ENTITY_STAT = new CommonAssetValidator("png", "Icons/EntityStats"); public static final CommonAssetValidator ICON_MODEL = new CommonAssetValidator("png", "Icons/ModelsGenerated", "Icons/Models"); + public static final CommonAssetValidator ICON_EMOTE = new CommonAssetValidator("png", "Icons/Emotes"); public static final CommonAssetValidator UI_RETICLE_PART = new CommonAssetValidator("png", "UI/Reticles"); public static final ArrayValidator UI_RETICLE_PARTS_ARRAY = new ArrayValidator<>(UI_RETICLE_PART); public static final CommonAssetValidator UI_SCREEN_EFFECT = new CommonAssetValidator("png", "ScreenEffects"); @@ -43,6 +44,7 @@ public class CommonAssetValidator implements Validator { "blockyanim", "Blocks", "Items", "Resources", "NPC", "VFX", "Consumable" ); public static final CommonAssetValidator ANIMATION_CHARACTER = new CommonAssetValidator("blockyanim", "Characters", "NPC", "Equipment", "VFX", "Items"); + public static final CommonAssetValidator ANIMATION_EMOTE = new CommonAssetValidator("blockyanim", "Characters"); public static final CommonAssetValidator MUSIC = new CommonAssetValidator("ogg", "Music"); public static final CommonAssetValidator SOUNDS = new CommonAssetValidator("ogg", "Sounds"); @Nullable diff --git a/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXConditions.java b/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXConditions.java index d9d23638..708251b3 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXConditions.java +++ b/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXConditions.java @@ -3,11 +3,17 @@ package com.hypixel.hytale.server.core.asset.type.ambiencefx.config; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.common.util.ArrayUtil; import com.hypixel.hytale.protocol.Range; import com.hypixel.hytale.protocol.Rangeb; import com.hypixel.hytale.protocol.Rangef; +import com.hypixel.hytale.protocol.RoofState; +import com.hypixel.hytale.protocol.ShelterType; +import com.hypixel.hytale.protocol.SpaceSize; +import com.hypixel.hytale.protocol.SurfaceType; import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.core.asset.type.fluidfx.config.FluidFX; import com.hypixel.hytale.server.core.asset.type.tagpattern.config.TagPattern; @@ -90,12 +96,14 @@ public class AmbienceFXConditions implements NetworkSerializable ambienceFXConditions.walls = parent.walls ) .add() - .appendInherited( + .appendInherited( new KeyedCodec<>("Roof", Codec.BOOLEAN), (ambienceFXConditions, aBoolean) -> ambienceFXConditions.roof = aBoolean, ambienceFXConditions -> ambienceFXConditions.roof, (ambienceFXConditions, parent) -> ambienceFXConditions.roof = parent.roof ) + .documentation("Deprecated: Use RoofState instead.") + .addValidator(Validators.deprecated()) .add() .appendInherited( new KeyedCodec<>("RoofMaterialTagPattern", TagPattern.CHILD_ASSET_CODEC), @@ -141,6 +149,118 @@ public class AmbienceFXConditions implements NetworkSerializable ambienceFXConditions.dayTime = parent.dayTime ) .add() + .appendInherited( + new KeyedCodec<>("Space", new ArrayCodec<>(new EnumCodec<>(SpaceSize.class), SpaceSize[]::new)), + (c, v) -> c.space = v, + c -> c.space, + (c, parent) -> c.space = parent.space + ) + .documentation("The rough scale of the space to match.") + .add() + .appendInherited( + new KeyedCodec<>("Shelter", new ArrayCodec<>(new EnumCodec<>(ShelterType.class), ShelterType[]::new)), + (c, v) -> c.shelter = v, + c -> c.shelter, + (c, parent) -> c.shelter = parent.shelter + ) + .documentation("The rough amount of shelter from exterior elements to match.") + .add() + .appendInherited( + new KeyedCodec<>("Surfaces", new ArrayCodec<>(new EnumCodec<>(SurfaceType.class), SurfaceType[]::new)), + (c, v) -> c.surfaces = v, + c -> c.surfaces, + (c, parent) -> c.surfaces = parent.surfaces + ) + .documentation("Reflectivity of surfaces within the space to match.") + .add() + .appendInherited( + new KeyedCodec<>("RoofState", new EnumCodec<>(RoofState.class)), + (c, v) -> c.roofState = v, + c -> c.roofState, + (c, parent) -> c.roofState = parent.roofState + ) + .documentation("Whether or not a roof must be present to match (or can be set to not care).") + .add() + .appendInherited( + new KeyedCodec<>("SpaceScaleRange", ProtocolCodecs.RANGEF), + (c, v) -> c.spaceScaleRange = v, + c -> c.spaceScaleRange, + (c, parent) -> c.spaceScaleRange = parent.spaceScaleRange + ) + .add() + .appendInherited( + new KeyedCodec<>("SpaceScaleMinRange", ProtocolCodecs.RANGEF), + (c, v) -> c.spaceScaleMinRange = v, + c -> c.spaceScaleMinRange, + (c, parent) -> c.spaceScaleMinRange = parent.spaceScaleMinRange + ) + .add() + .appendInherited( + new KeyedCodec<>("SpaceScaleMaxRange", ProtocolCodecs.RANGEF), + (c, v) -> c.spaceScaleMaxRange = v, + c -> c.spaceScaleMaxRange, + (c, parent) -> c.spaceScaleMaxRange = parent.spaceScaleMaxRange + ) + .add() + .appendInherited( + new KeyedCodec<>("EscapedRayPercentRange", ProtocolCodecs.RANGEF), + (c, v) -> c.escapedRayPercentRange = v, + c -> c.escapedRayPercentRange, + (c, parent) -> c.escapedRayPercentRange = parent.escapedRayPercentRange + ) + .add() + .appendInherited( + new KeyedCodec<>("ReflectionCoeffRange", ProtocolCodecs.RANGEF), + (c, v) -> c.reflectionCoeffRange = v, + c -> c.reflectionCoeffRange, + (c, parent) -> c.reflectionCoeffRange = parent.reflectionCoeffRange + ) + .add() + .appendInherited( + new KeyedCodec<>("AbsorptionCoeffRange", ProtocolCodecs.RANGEF), + (c, v) -> c.absorptionCoeffRange = v, + c -> c.absorptionCoeffRange, + (c, parent) -> c.absorptionCoeffRange = parent.absorptionCoeffRange + ) + .add() + .appendInherited( + new KeyedCodec<>("RoofDistanceRange", ProtocolCodecs.RANGEF), + (c, v) -> c.roofDistanceRange = v, + c -> c.roofDistanceRange, + (c, parent) -> c.roofDistanceRange = parent.roofDistanceRange + ) + .add() + .appendInherited( + new KeyedCodec<>("SurfacePhysicalMaterials", new ArrayCodec<>(AmbienceFXPhysicalMaterial.CODEC, AmbienceFXPhysicalMaterial[]::new)), + (c, v) -> c.surfacePhysicalMaterials = v, + c -> c.surfacePhysicalMaterials, + (c, parent) -> c.surfacePhysicalMaterials = parent.surfacePhysicalMaterials + ) + .add() + .appendInherited( + new KeyedCodec<>("SurfacePhysicalMaterialsMatchAny", Codec.BOOLEAN), + (c, v) -> c.surfacePhysicalMaterialsMatchAny = v, + c -> c.surfacePhysicalMaterialsMatchAny, + (c, parent) -> c.surfacePhysicalMaterialsMatchAny = parent.surfacePhysicalMaterialsMatchAny + ) + .documentation("When true, the SurfacePhysicalMaterials condition matches if any entry matches (OR semantics). By default all must much.") + .add() + .appendInherited( + new KeyedCodec<>("ExteriorRoofPhysicalMaterials", new ArrayCodec<>(AmbienceFXPhysicalMaterial.CODEC, AmbienceFXPhysicalMaterial[]::new)), + (c, v) -> c.exteriorRoofPhysicalMaterials = v, + c -> c.exteriorRoofPhysicalMaterials, + (c, parent) -> c.exteriorRoofPhysicalMaterials = parent.exteriorRoofPhysicalMaterials + ) + .documentation("The percentage ratios of the last physical material encountered before a probe ray escapes, i.e. the inferred \"roof\" materials.") + .add() + .appendInherited( + new KeyedCodec<>("ExteriorRoofPhysicalMaterialsMatchAny", Codec.BOOLEAN), + (c, v) -> c.exteriorRoofPhysicalMaterialsMatchAny = v, + c -> c.exteriorRoofPhysicalMaterialsMatchAny, + (c, parent) -> c.exteriorRoofPhysicalMaterialsMatchAny = parent.exteriorRoofPhysicalMaterialsMatchAny + ) + .documentation("When true, the ExteriorRoofPhysicalMaterials condition matches if any entry matches (OR semantics). By default all must match.") + .add() .afterDecode(AmbienceFXConditions::processConfig) .build(); public static final Range DEFAULT_ALTITUDE = new Range(0, 512); @@ -159,6 +279,7 @@ public class AmbienceFXConditions implements NetworkSerializable 0) { + packet.surfacePhysicalMaterials = ArrayUtil.copyAndMutate( + this.surfacePhysicalMaterials, AmbienceFXPhysicalMaterial::toPacket, com.hypixel.hytale.protocol.AmbienceFXPhysicalMaterial[]::new + ); + } + + packet.surfacePhysicalMaterialsMatchAny = this.surfacePhysicalMaterialsMatchAny; + if (this.exteriorRoofPhysicalMaterials != null && this.exteriorRoofPhysicalMaterials.length > 0) { + packet.exteriorRoofPhysicalMaterials = ArrayUtil.copyAndMutate( + this.exteriorRoofPhysicalMaterials, AmbienceFXPhysicalMaterial::toPacket, com.hypixel.hytale.protocol.AmbienceFXPhysicalMaterial[]::new + ); + } + + packet.exteriorRoofPhysicalMaterialsMatchAny = this.exteriorRoofPhysicalMaterialsMatchAny; return packet; } diff --git a/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXPhysicalMaterial.java b/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXPhysicalMaterial.java new file mode 100644 index 00000000..ad64d06b --- /dev/null +++ b/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXPhysicalMaterial.java @@ -0,0 +1,76 @@ +package com.hypixel.hytale.server.core.asset.type.ambiencefx.config; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.protocol.Rangef; +import com.hypixel.hytale.server.core.asset.type.physicalmaterial.config.PhysicalMaterial; +import com.hypixel.hytale.server.core.codec.ProtocolCodecs; +import com.hypixel.hytale.server.core.io.NetworkSerializable; +import javax.annotation.Nonnull; + +public class AmbienceFXPhysicalMaterial implements NetworkSerializable { + public static final BuilderCodec CODEC = BuilderCodec.builder(AmbienceFXPhysicalMaterial.class, AmbienceFXPhysicalMaterial::new) + .append( + new KeyedCodec<>("PhysicalMaterialId", Codec.STRING), + (ambienceFXPhysicalMaterial, s) -> ambienceFXPhysicalMaterial.physicalMaterialId = s, + ambienceFXPhysicalMaterial -> ambienceFXPhysicalMaterial.physicalMaterialId + ) + .addValidator(Validators.nonNull()) + .addValidator(PhysicalMaterial.VALIDATOR_CACHE.getValidator()) + .add() + .append( + new KeyedCodec<>("Percent", ProtocolCodecs.RANGEF), + (ambienceFXPhysicalMaterial, o) -> ambienceFXPhysicalMaterial.percent = o, + ambienceFXPhysicalMaterial -> ambienceFXPhysicalMaterial.percent + ) + .addValidator(Validators.nonNull()) + .add() + .afterDecode(AmbienceFXPhysicalMaterial::processConfig) + .build(); + public static final Rangef DEFAULT_PERCENT = new Rangef(0.0F, 0.0F); + protected String physicalMaterialId; + protected transient int physicalMaterialIndex; + protected Rangef percent = DEFAULT_PERCENT; + + public AmbienceFXPhysicalMaterial(String physicalMaterialId, Rangef percent) { + this.physicalMaterialId = physicalMaterialId; + this.percent = percent; + } + + protected AmbienceFXPhysicalMaterial() { + } + + @Nonnull + public com.hypixel.hytale.protocol.AmbienceFXPhysicalMaterial toPacket() { + com.hypixel.hytale.protocol.AmbienceFXPhysicalMaterial packet = new com.hypixel.hytale.protocol.AmbienceFXPhysicalMaterial(); + packet.physicalMaterialIndex = this.physicalMaterialIndex; + packet.percent = this.percent; + return packet; + } + + public String getPhysicalMaterialId() { + return this.physicalMaterialId; + } + + public Rangef getPercent() { + return this.percent; + } + + protected void processConfig() { + this.physicalMaterialIndex = PhysicalMaterial.getAssetMap().getIndex(this.physicalMaterialId); + } + + @Nonnull + @Override + public String toString() { + return "AmbienceFXPhysicalMaterial{physicalMaterialId='" + + this.physicalMaterialId + + "', physicalMaterialIndex=" + + this.physicalMaterialIndex + + ", percent=" + + this.percent + + "}"; + } +} diff --git a/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXSound.java b/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXSound.java index 4c90672e..8f00b171 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXSound.java +++ b/src/com/hypixel/hytale/server/core/asset/type/ambiencefx/config/AmbienceFXSound.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.protocol.AmbienceFXAltitude; import com.hypixel.hytale.protocol.AmbienceFXSoundPlay3D; import com.hypixel.hytale.protocol.Range; +import com.hypixel.hytale.protocol.Rangeb; import com.hypixel.hytale.protocol.Rangef; import com.hypixel.hytale.server.core.asset.type.blocksound.config.BlockSoundSet; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; @@ -48,6 +49,22 @@ public class AmbienceFXSound implements NetworkSerializable ambienceFXSound.frequency ) .addField(new KeyedCodec<>("Radius", ProtocolCodecs.RANGE), (ambienceFXSound, o) -> ambienceFXSound.radius = o, ambienceFXSound -> ambienceFXSound.radius) + .append( + new KeyedCodec<>("MaxBodiesPerEmitter", Codec.INTEGER), + (ambienceFXSound, o) -> ambienceFXSound.maxBodiesPerEmitter = o, + ambienceFXSound -> ambienceFXSound.maxBodiesPerEmitter + ) + .addValidator(Validators.greaterThan(0)) + .add() + .documentation( + "Random radius within which to play the sound. When used in conjunction with LocationNameRandom for Play3D, acts as a distance bound on position selection. Ignored when Play3D is set to LocationName." + ) + .addField( + new KeyedCodec<>("SunlightRange", ProtocolCodecs.RANGEB), + (ambienceFXSound, o) -> ambienceFXSound.sunlightRange = o, + ambienceFXSound -> ambienceFXSound.sunlightRange + ) + .documentation("Required sunlight range for finding positions at which to play the sound. High values can be considered to be \"exterior\".") .afterDecode(AmbienceFXSound::processConfig) .build(); public static final Rangef DEFAULT_FREQUENCY = new Rangef(1.0F, 10.0F); @@ -60,9 +77,17 @@ public class AmbienceFXSound implements NetworkSerializable>, @@ -54,7 +55,7 @@ public class BlockParticleSet .documentation("The scale of the particle system.") .add() .appendInherited( - new KeyedCodec<>("PositionOffset", ProtocolCodecs.VECTOR3F), + new KeyedCodec<>("PositionOffset", Vector3fUtil.CODEC), (blockParticleSet, s) -> blockParticleSet.positionOffset = s, blockParticleSet -> blockParticleSet.positionOffset, (blockParticleSet, parent) -> blockParticleSet.positionOffset = parent.positionOffset diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocksound/config/BlockSoundSet.java b/src/com/hypixel/hytale/server/core/asset/type/blocksound/config/BlockSoundSet.java index 65ffbefc..c41997b4 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocksound/config/BlockSoundSet.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocksound/config/BlockSoundSet.java @@ -13,6 +13,7 @@ import com.hypixel.hytale.codec.codecs.map.EnumMapCodec; import com.hypixel.hytale.codec.schema.metadata.ui.UIDefaultCollapsedState; import com.hypixel.hytale.codec.validation.ValidatorCache; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.common.util.MapUtil; import com.hypixel.hytale.math.range.FloatRange; import com.hypixel.hytale.protocol.BlockSoundEvent; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; @@ -23,6 +24,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMaps; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.lang.ref.SoftReference; import java.util.Collections; +import java.util.EnumMap; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; @@ -45,9 +47,9 @@ public class BlockSoundSet ) .appendInherited( new KeyedCodec<>("SoundEvents", new EnumMapCodec<>(BlockSoundEvent.class, Codec.STRING)), - (blockParticleSet, s) -> blockParticleSet.soundEventIds = s, - blockParticleSet -> blockParticleSet.soundEventIds, - (blockParticleSet, parent) -> blockParticleSet.soundEventIds = parent.soundEventIds + (blockSounds, s) -> blockSounds.soundEventIds = MapUtil.combineUnmodifiable(blockSounds.soundEventIds, s, () -> new EnumMap<>(BlockSoundEvent.class)), + blockSounds -> blockSounds.soundEventIds, + (blockSounds, parent) -> blockSounds.soundEventIds = parent.soundEventIds ) .addValidator(Validators.nonNull()) .addValidator(SoundEvent.VALIDATOR_CACHE.getMapValueValidator()) diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFace.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFace.java index 0d490199..19649481 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFace.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFace.java @@ -1,20 +1,22 @@ package com.hypixel.hytale.server.core.asset.type.blocktype.config; import com.hypixel.hytale.codec.codecs.EnumCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.BlockNeighbor; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; +import org.joml.Vector3ic; public enum BlockFace { - UP(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.Up, Vector3i.UP), - DOWN(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.Down, Vector3i.DOWN), - NORTH(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.North, Vector3i.NORTH), - EAST(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.East, Vector3i.EAST), - SOUTH(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.South, Vector3i.SOUTH), - WEST(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.West, Vector3i.WEST), + UP(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.Up, Vector3iUtil.UP), + DOWN(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.Down, Vector3iUtil.DOWN), + NORTH(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.North, Vector3iUtil.NORTH), + EAST(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.East, Vector3iUtil.EAST), + SOUTH(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.South, Vector3iUtil.SOUTH), + WEST(BlockFace.FaceConnectionType.FLIP, BlockNeighbor.West, Vector3iUtil.WEST), UP_NORTH(BlockFace.FaceConnectionType.ROTATE_X, BlockNeighbor.UpNorth, UP, NORTH), UP_SOUTH(BlockFace.FaceConnectionType.ROTATE_X, BlockNeighbor.UpSouth, UP, SOUTH), UP_EAST(BlockFace.FaceConnectionType.ROTATE_Z, BlockNeighbor.UpEast, UP, EAST), @@ -39,16 +41,16 @@ public enum BlockFace { public static final EnumCodec CODEC = new EnumCodec<>(BlockFace.class); public static final BlockFace[] VALUES = values(); @Nonnull - private static final Map DIRECTION_MAP = new Object2ObjectOpenHashMap<>(); + private static final Map DIRECTION_MAP = new Object2ObjectOpenHashMap<>(); private final BlockFace.FaceConnectionType faceConnectionType; @Nonnull private final BlockFace[] components; - private final Vector3i direction; + private final Vector3ic direction; private final BlockNeighbor blockNeighbor; private BlockFace[] connectingFaces; - private Vector3i[] connectingFaceOffsets; + private Vector3ic[] connectingFaceOffsets; - private BlockFace(BlockFace.FaceConnectionType faceConnectionType, BlockNeighbor blockNeighbor, Vector3i direction) { + private BlockFace(BlockFace.FaceConnectionType faceConnectionType, BlockNeighbor blockNeighbor, Vector3ic direction) { this.faceConnectionType = faceConnectionType; this.direction = direction; this.blockNeighbor = blockNeighbor; @@ -65,12 +67,13 @@ public enum BlockFace { } } - this.direction = new Vector3i(); + Vector3i direction = new Vector3i(); for (BlockFace componentx : components) { - this.direction.add(componentx.direction); + direction.add(componentx.direction); } + this.direction = direction; this.blockNeighbor = blockNeighbor; } @@ -83,7 +86,7 @@ public enum BlockFace { return this.components; } - public Vector3i getDirection() { + public Vector3ic getDirection() { return this.direction; } @@ -91,7 +94,7 @@ public enum BlockFace { return this.connectingFaces; } - public Vector3i[] getConnectingFaceOffsets() { + public Vector3ic[] getConnectingFaceOffsets() { return this.connectingFaceOffsets; } @@ -139,16 +142,16 @@ public enum BlockFace { @Nonnull private Vector3i directionTo(@Nonnull BlockFace connectingFace) { Vector3i vector3i = new Vector3i(); - if (this.direction.getX() == -connectingFace.direction.getX()) { - vector3i.setX(this.direction.getX()); + if (this.direction.x() == -connectingFace.direction.x()) { + vector3i.x = this.direction.x(); } - if (this.direction.getY() == -connectingFace.direction.getY()) { - vector3i.setY(this.direction.getY()); + if (this.direction.y() == -connectingFace.direction.y()) { + vector3i.y = this.direction.y(); } - if (this.direction.getZ() == -connectingFace.direction.getZ()) { - vector3i.setZ(this.direction.getZ()); + if (this.direction.z() == -connectingFace.direction.z()) { + vector3i.z = this.direction.z(); } return vector3i; @@ -169,7 +172,7 @@ public enum BlockFace { } public static BlockFace flip(@Nonnull BlockFace blockFace) { - Vector3i flipped = blockFace.direction.clone().scale(-1); + Vector3i flipped = new Vector3i(blockFace.direction).mul(-1); return lookup(flipped); } diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFaceSupport.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFaceSupport.java index a4f340a3..68e4d712 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFaceSupport.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFaceSupport.java @@ -5,10 +5,11 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.common.util.ArrayUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.io.NetworkSerializable; import java.util.Arrays; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BlockFaceSupport implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(BlockFaceSupport.class, BlockFaceSupport::new) @@ -16,7 +17,7 @@ public class BlockFaceSupport implements NetworkSerializable("Filler", new ArrayCodec<>(Vector3i.CODEC, Vector3i[]::new)), + new KeyedCodec<>("Filler", new ArrayCodec<>(Vector3iUtil.CODEC, Vector3i[]::new)), (blockFaceSupport, o) -> blockFaceSupport.filler = o, blockFaceSupport -> blockFaceSupport.filler ) diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFlipType.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFlipType.java index 8a6253e4..4684ad5b 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFlipType.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockFlipType.java @@ -5,27 +5,49 @@ import javax.annotation.Nonnull; public enum BlockFlipType { ORTHOGONAL, + ORTHOGONAL_INVERSE, SYMMETRIC; public Rotation flipYaw(@Nonnull Rotation rotation, Axis axis) { - if (axis == Axis.Y) { - return rotation; - } else { - switch (this) { - case ORTHOGONAL: - int multiplier = axis == rotation.getAxisOfAlignment() ? -1 : 1; - int index = rotation.ordinal() + multiplier + Rotation.VALUES.length; - index %= Rotation.VALUES.length; - return Rotation.VALUES[index]; - case SYMMETRIC: - if (rotation.getAxisOfAlignment() == axis) { - return rotation.add(Rotation.OneEighty); - } + return this.flipComponent(rotation, axis, Axis.Y, Axis.Z, rotation.getAxisOfAlignment()); + } + private Rotation flipComponent(@Nonnull Rotation rotation, Axis axis, Axis ownAxis, Axis negateAxis, Axis alignment) { + switch (this) { + case ORTHOGONAL: + int multiplier = rotation.getDegrees() % 180 == 90 ? 1 : -1; + if (axis == negateAxis) { + multiplier = -multiplier; + } + + int index = rotation.ordinal() + multiplier + Rotation.VALUES.length; + if (axis == ownAxis && rotation.getDegrees() % 180 == 90) { + index += 2; + } + + index %= Rotation.VALUES.length; + return Rotation.VALUES[index]; + case ORTHOGONAL_INVERSE: + int multiplierInv = rotation.getDegrees() % 180 == 90 ? -1 : 1; + if (axis == negateAxis) { + multiplierInv = -multiplierInv; + } + + int indexInv = rotation.ordinal() + multiplierInv + Rotation.VALUES.length; + if (axis == ownAxis && rotation.getDegrees() % 180 == 90) { + indexInv += 2; + } + + indexInv %= Rotation.VALUES.length; + return Rotation.VALUES[indexInv]; + case SYMMETRIC: + if (axis != ownAxis && alignment != axis) { return rotation; - default: - throw new UnsupportedOperationException(); - } + } + + return rotation.add(Rotation.OneEighty); + default: + throw new UnsupportedOperationException(); } } } diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.java index a0f8522f..199b1681 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.protocol.BlockPlacementRotationMode; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; @@ -71,155 +72,47 @@ public class BlockPlacementSettings implements NetworkSerializable ()V - // 007: astore 1 - // 008: aload 1 - // 009: aload 0 - // 00a: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.allowRotationKey Z - // 00d: putfield com/hypixel/hytale/protocol/BlockPlacementSettings.allowRotationKey Z - // 010: aload 1 - // 011: aload 0 - // 012: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.placeInEmptyBlocks Z - // 015: putfield com/hypixel/hytale/protocol/BlockPlacementSettings.placeInEmptyBlocks Z - // 018: aload 1 - // 019: aload 0 - // 01a: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.allowBreakReplace Z - // 01d: putfield com/hypixel/hytale/protocol/BlockPlacementSettings.allowBreakReplace Z - // 020: aload 1 - // 021: aload 0 - // 022: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.previewVisibility Lcom/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings$BlockPreviewVisibility; - // 025: astore 2 - // 026: bipush 0 - // 027: istore 3 - // 028: aload 2 - // 029: iload 3 - // 02a: invokedynamic typeSwitch (Ljava/lang/Object;I)I bsm=java/lang/runtime/SwitchBootstraps.typeSwitch (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc; ] - // 02f: tableswitch 29 -1 2 39 45 51 57 - // 04c: new java/lang/MatchException - // 04f: dup - // 050: aconst_null - // 051: aconst_null - // 052: invokespecial java/lang/MatchException. (Ljava/lang/String;Ljava/lang/Throwable;)V - // 055: athrow - // 056: getstatic com/hypixel/hytale/protocol/BlockPreviewVisibility.Default Lcom/hypixel/hytale/protocol/BlockPreviewVisibility; - // 059: goto 06b - // 05c: getstatic com/hypixel/hytale/protocol/BlockPreviewVisibility.Default Lcom/hypixel/hytale/protocol/BlockPreviewVisibility; - // 05f: goto 06b - // 062: getstatic com/hypixel/hytale/protocol/BlockPreviewVisibility.AlwaysHidden Lcom/hypixel/hytale/protocol/BlockPreviewVisibility; - // 065: goto 06b - // 068: getstatic com/hypixel/hytale/protocol/BlockPreviewVisibility.AlwaysVisible Lcom/hypixel/hytale/protocol/BlockPreviewVisibility; - // 06b: putfield com/hypixel/hytale/protocol/BlockPlacementSettings.previewVisibility Lcom/hypixel/hytale/protocol/BlockPreviewVisibility; - // 06e: aload 1 - // 06f: aload 0 - // 070: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.rotationMode Lcom/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings$RotationMode; - // 073: astore 2 - // 074: bipush 0 - // 075: istore 3 - // 076: aload 2 - // 077: iload 3 - // 078: invokedynamic typeSwitch (Ljava/lang/Object;I)I bsm=java/lang/runtime/SwitchBootstraps.typeSwitch (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc; ] - // 07d: tableswitch 35 -1 3 45 51 57 63 69 - // 0a0: new java/lang/MatchException - // 0a3: dup - // 0a4: aconst_null - // 0a5: aconst_null - // 0a6: invokespecial java/lang/MatchException. (Ljava/lang/String;Ljava/lang/Throwable;)V - // 0a9: athrow - // 0aa: getstatic com/hypixel/hytale/protocol/BlockPlacementRotationMode.Default Lcom/hypixel/hytale/protocol/BlockPlacementRotationMode; - // 0ad: goto 0c5 - // 0b0: getstatic com/hypixel/hytale/protocol/BlockPlacementRotationMode.Default Lcom/hypixel/hytale/protocol/BlockPlacementRotationMode; - // 0b3: goto 0c5 - // 0b6: getstatic com/hypixel/hytale/protocol/BlockPlacementRotationMode.FacingPlayer Lcom/hypixel/hytale/protocol/BlockPlacementRotationMode; - // 0b9: goto 0c5 - // 0bc: getstatic com/hypixel/hytale/protocol/BlockPlacementRotationMode.StairFacingPlayer Lcom/hypixel/hytale/protocol/BlockPlacementRotationMode; - // 0bf: goto 0c5 - // 0c2: getstatic com/hypixel/hytale/protocol/BlockPlacementRotationMode.BlockNormal Lcom/hypixel/hytale/protocol/BlockPlacementRotationMode; - // 0c5: putfield com/hypixel/hytale/protocol/BlockPlacementSettings.rotationMode Lcom/hypixel/hytale/protocol/BlockPlacementRotationMode; - // 0c8: aload 1 - // 0c9: aload 0 - // 0ca: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.wallPlacementOverrideBlockId Ljava/lang/String; - // 0cd: ifnonnull 0d4 - // 0d0: bipush -1 - // 0d1: goto 0de - // 0d4: invokestatic com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType.getAssetMap ()Lcom/hypixel/hytale/assetstore/map/BlockTypeAssetMap; - // 0d7: aload 0 - // 0d8: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.wallPlacementOverrideBlockId Ljava/lang/String; - // 0db: invokevirtual com/hypixel/hytale/assetstore/map/BlockTypeAssetMap.getIndex (Ljava/lang/Object;)I - // 0de: putfield com/hypixel/hytale/protocol/BlockPlacementSettings.wallPlacementOverrideBlockId I - // 0e1: aload 1 - // 0e2: getfield com/hypixel/hytale/protocol/BlockPlacementSettings.wallPlacementOverrideBlockId I - // 0e5: ldc -2147483648 - // 0e7: if_icmpne 0fb - // 0ea: new java/lang/IllegalArgumentException - // 0ed: dup - // 0ee: aload 0 - // 0ef: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.wallPlacementOverrideBlockId Ljava/lang/String; - // 0f2: invokedynamic makeConcatWithConstants (Ljava/lang/String;)Ljava/lang/String; bsm=java/lang/invoke/StringConcatFactory.makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ "Unknown key! \u0001" ] - // 0f7: invokespecial java/lang/IllegalArgumentException. (Ljava/lang/String;)V - // 0fa: athrow - // 0fb: aload 1 - // 0fc: aload 0 - // 0fd: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.floorPlacementOverrideBlockId Ljava/lang/String; - // 100: ifnonnull 107 - // 103: bipush -1 - // 104: goto 111 - // 107: invokestatic com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType.getAssetMap ()Lcom/hypixel/hytale/assetstore/map/BlockTypeAssetMap; - // 10a: aload 0 - // 10b: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.floorPlacementOverrideBlockId Ljava/lang/String; - // 10e: invokevirtual com/hypixel/hytale/assetstore/map/BlockTypeAssetMap.getIndex (Ljava/lang/Object;)I - // 111: putfield com/hypixel/hytale/protocol/BlockPlacementSettings.floorPlacementOverrideBlockId I - // 114: aload 1 - // 115: getfield com/hypixel/hytale/protocol/BlockPlacementSettings.floorPlacementOverrideBlockId I - // 118: ldc -2147483648 - // 11a: if_icmpne 12e - // 11d: new java/lang/IllegalArgumentException - // 120: dup - // 121: aload 0 - // 122: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.floorPlacementOverrideBlockId Ljava/lang/String; - // 125: invokedynamic makeConcatWithConstants (Ljava/lang/String;)Ljava/lang/String; bsm=java/lang/invoke/StringConcatFactory.makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ "Unknown key! \u0001" ] - // 12a: invokespecial java/lang/IllegalArgumentException. (Ljava/lang/String;)V - // 12d: athrow - // 12e: aload 1 - // 12f: aload 0 - // 130: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.ceilingPlacementOverrideBlockId Ljava/lang/String; - // 133: ifnonnull 13a - // 136: bipush -1 - // 137: goto 144 - // 13a: invokestatic com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType.getAssetMap ()Lcom/hypixel/hytale/assetstore/map/BlockTypeAssetMap; - // 13d: aload 0 - // 13e: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.ceilingPlacementOverrideBlockId Ljava/lang/String; - // 141: invokevirtual com/hypixel/hytale/assetstore/map/BlockTypeAssetMap.getIndex (Ljava/lang/Object;)I - // 144: putfield com/hypixel/hytale/protocol/BlockPlacementSettings.ceilingPlacementOverrideBlockId I - // 147: aload 1 - // 148: getfield com/hypixel/hytale/protocol/BlockPlacementSettings.ceilingPlacementOverrideBlockId I - // 14b: ldc -2147483648 - // 14d: if_icmpne 161 - // 150: new java/lang/IllegalArgumentException - // 153: dup - // 154: aload 0 - // 155: getfield com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockPlacementSettings.ceilingPlacementOverrideBlockId Ljava/lang/String; - // 158: invokedynamic makeConcatWithConstants (Ljava/lang/String;)Ljava/lang/String; bsm=java/lang/invoke/StringConcatFactory.makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ "Unknown key! \u0001" ] - // 15d: invokespecial java/lang/IllegalArgumentException. (Ljava/lang/String;)V - // 160: athrow - // 161: aload 1 - // 162: areturn + com.hypixel.hytale.protocol.BlockPlacementSettings packet = new com.hypixel.hytale.protocol.BlockPlacementSettings(); + packet.allowRotationKey = this.allowRotationKey; + packet.placeInEmptyBlocks = this.placeInEmptyBlocks; + packet.allowBreakReplace = this.allowBreakReplace; + + packet.previewVisibility = switch (this.previewVisibility) { + case null -> com.hypixel.hytale.protocol.BlockPreviewVisibility.Default; + case DEFAULT -> com.hypixel.hytale.protocol.BlockPreviewVisibility.Default; + case ALWAYS_HIDDEN -> com.hypixel.hytale.protocol.BlockPreviewVisibility.AlwaysHidden; + case ALWAYS_VISIBLE -> com.hypixel.hytale.protocol.BlockPreviewVisibility.AlwaysVisible; + }; + + packet.rotationMode = switch (this.rotationMode) { + case null -> BlockPlacementRotationMode.Default; + case DEFAULT -> BlockPlacementRotationMode.Default; + case FACING_PLAYER -> BlockPlacementRotationMode.FacingPlayer; + case STAIR_FACING_PLAYER -> BlockPlacementRotationMode.StairFacingPlayer; + case BLOCK_NORMAL -> BlockPlacementRotationMode.BlockNormal; + }; + packet.wallPlacementOverrideBlockId = this.wallPlacementOverrideBlockId == null + ? -1 + : BlockType.getAssetMap().getIndex(this.wallPlacementOverrideBlockId); + if (packet.wallPlacementOverrideBlockId == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! " + this.wallPlacementOverrideBlockId); + } else { + packet.floorPlacementOverrideBlockId = this.floorPlacementOverrideBlockId == null + ? -1 + : BlockType.getAssetMap().getIndex(this.floorPlacementOverrideBlockId); + if (packet.floorPlacementOverrideBlockId == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! " + this.floorPlacementOverrideBlockId); + } else { + packet.ceilingPlacementOverrideBlockId = this.ceilingPlacementOverrideBlockId == null + ? -1 + : BlockType.getAssetMap().getIndex(this.ceilingPlacementOverrideBlockId); + if (packet.ceilingPlacementOverrideBlockId == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! " + this.ceilingPlacementOverrideBlockId); + } else { + return packet; + } + } + } } public String getWallPlacementOverrideBlockId() { diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType.java index 6cea1d0f..1602592e 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType.java @@ -28,9 +28,6 @@ import com.hypixel.hytale.common.util.MapUtil; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.protocol.BenchType; import com.hypixel.hytale.protocol.BlockFlags; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.BlockNeighbor; @@ -61,6 +58,7 @@ import com.hypixel.hytale.server.core.asset.type.buildertool.config.BlockTypeLis import com.hypixel.hytale.server.core.asset.type.buildertool.config.PrefabListAsset; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle; +import com.hypixel.hytale.server.core.asset.type.physicalmaterial.config.PhysicalMaterial; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.asset.type.soundevent.validator.SoundEventValidators; import com.hypixel.hytale.server.core.asset.util.ColorParseUtil; @@ -68,7 +66,7 @@ import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.io.NetworkSerializable; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.InteractionTypeUtils; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; -import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.ISectionPalette; +import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.AbstractSectionPalette; import com.hypixel.hytale.server.core.universe.world.connectedblocks.ConnectedBlockRuleSet; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.util.io.ByteBufUtil; @@ -88,6 +86,8 @@ import java.util.function.ToIntFunction; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class BlockType implements JsonAssetWithMap>, NetworkSerializable { public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( @@ -500,6 +500,15 @@ public class BlockType implements JsonAssetWithMapappendInherited( + new KeyedCodec<>("PhysicalMaterialId", Codec.STRING), + (blockType, o) -> blockType.physicalMaterialId = o, + blockType -> blockType.physicalMaterialId, + (blockType, parent) -> blockType.physicalMaterialId = parent.physicalMaterialId + ) + .documentation("Sets the PhysicalMaterial of the block. Currently used primarily for acoustic properties.") + .addValidator(PhysicalMaterial.VALIDATOR_CACHE.getValidator()) + .add() .appendInherited( new KeyedCodec<>("AmbientSoundEventId", Codec.STRING), (blockType, s) -> blockType.ambientSoundEventId = s, @@ -511,6 +520,16 @@ public class BlockType implements JsonAssetWithMapappendInherited( + new KeyedCodec<>("ConditionalSounds", new ArrayCodec<>(ConditionalBlockSound.CODEC, ConditionalBlockSound[]::new)), + (blockType, o) -> blockType.conditionalSounds = o, + blockType -> blockType.conditionalSounds, + (blockType, parent) -> blockType.conditionalSounds = parent.conditionalSounds + ) + .documentation( + "An array of conditional ambient sounds. Each entry references a looping sound event and an AmbienceFX whose conditions determine when the sound plays from this block." + ) + .add() .appendInherited( new KeyedCodec<>("InteractionSoundEventId", Codec.STRING), (blockType, s) -> blockType.interactionSoundEventId = s, @@ -777,7 +796,7 @@ public class BlockType implements JsonAssetWithMap { + public static final AbstractSectionPalette.KeySerializer KEY_SERIALIZER = (buf, id) -> { String key = getAssetMap().getAssetOrDefault(id, BlockType.UNKNOWN).getId(); ByteBufUtil.writeUTF(buf, key); }; @@ -850,6 +869,8 @@ public class BlockType implements JsonAssetWithMap 0) { + packet.conditionalSounds = new com.hypixel.hytale.protocol.ConditionalBlockSound[this.conditionalSounds.length]; + + for (int i = 0; i < this.conditionalSounds.length; i++) { + packet.conditionalSounds[i] = this.conditionalSounds[i].toPacket(); + } + } + if (this.particles != null && this.particles.length > 0) { packet.particles = new com.hypixel.hytale.protocol.ModelParticle[this.particles.length]; @@ -1333,12 +1368,17 @@ public class BlockType implements JsonAssetWithMap 0 && this.interactionHint == null) { - this.interactionHint = "server.interactionHints.sit"; - } - } else { + } else if (this.isDoor) { this.flags.isUsable = true; if (this.interactionHint == null) { this.interactionHint = "server.interactionHints.open"; } + } else if (this.gathering != null && this.gathering.isHarvestable()) { + this.flags.isUsable = true; + if (this.interactionHint == null) { + this.interactionHint = "server.interactionHints.gather"; + } + } else if (this.seats != null && this.seats.size() > 0 && this.interactionHint == null) { + this.interactionHint = "server.interactionHints.sit"; } if (this.interactions.containsKey(InteractionType.Use)) { @@ -1856,6 +1885,12 @@ public class BlockType implements JsonAssetWithMap { public static final BuilderCodec CODEC = BuilderCodec.builder(RequiredBlockFaceSupport.class, RequiredBlockFaceSupport::new) @@ -57,7 +58,7 @@ public class RequiredBlockFaceSupport implements NetworkSerializable requiredBlockFaceSupport.rotate ) .addField( - new KeyedCodec<>("Filler", new ArrayCodec<>(Vector3i.CODEC, Vector3i[]::new)), + new KeyedCodec<>("Filler", new ArrayCodec<>(Vector3iUtil.CODEC, Vector3i[]::new)), (blockFaceSupport, o) -> blockFaceSupport.filler = o, blockFaceSupport -> blockFaceSupport.filler ) diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/Rotation.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/Rotation.java index 8cdacac6..7f86bbee 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/Rotation.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/Rotation.java @@ -4,19 +4,25 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.io.NetworkSerializable; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Quaterniond; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3f; +import org.joml.Vector3fc; +import org.joml.Vector3i; +import org.joml.Vector3ic; public enum Rotation implements NetworkSerializable { - None(0, com.hypixel.hytale.protocol.Rotation.None, Axis.Z, Vector3i.NEG_Z), - Ninety(90, com.hypixel.hytale.protocol.Rotation.Ninety, Axis.X, Vector3i.NEG_X), - OneEighty(180, com.hypixel.hytale.protocol.Rotation.OneEighty, Axis.Z, Vector3i.POS_Z), - TwoSeventy(270, com.hypixel.hytale.protocol.Rotation.TwoSeventy, Axis.X, Vector3i.POS_X); + None(0, com.hypixel.hytale.protocol.Rotation.None, Axis.Z, Vector3iUtil.NEG_Z), + Ninety(90, com.hypixel.hytale.protocol.Rotation.Ninety, Axis.X, Vector3iUtil.NEG_X), + OneEighty(180, com.hypixel.hytale.protocol.Rotation.OneEighty, Axis.Z, Vector3iUtil.POS_Z), + TwoSeventy(270, com.hypixel.hytale.protocol.Rotation.TwoSeventy, Axis.X, Vector3iUtil.POS_X); public static final Rotation[] VALUES = values(); public static final Rotation[] NORMAL = new Rotation[]{None, Ninety, OneEighty, TwoSeventy}; @@ -24,9 +30,9 @@ public enum Rotation implements NetworkSerializable out.assign(in); - case Ninety -> out.assign(in.x, -in.z, in.y); - case OneEighty -> out.assign(in.x, -in.y, -in.z); - case TwoSeventy -> out.assign(in.x, in.z, -in.y); + case None -> out.set(in); + case Ninety -> out.set(in.x, -in.z, in.y); + case OneEighty -> out.set(in.x, -in.y, -in.z); + case TwoSeventy -> out.set(in.x, in.z, -in.y); }; } @Nonnull public Vector3f rotateX(@Nonnull Vector3f in, @Nonnull Vector3f out) { return switch (this) { - case None -> out.assign(in); - case Ninety -> out.assign(in.x, -in.z, in.y); - case OneEighty -> out.assign(in.x, -in.y, -in.z); - case TwoSeventy -> out.assign(in.x, in.z, -in.y); + case None -> out.set(in); + case Ninety -> out.set(in.x, -in.z, in.y); + case OneEighty -> out.set(in.x, -in.y, -in.z); + case TwoSeventy -> out.set(in.x, in.z, -in.y); }; } @Nonnull public Vector3d rotateX(@Nonnull Vector3d in, @Nonnull Vector3d out) { return switch (this) { - case None -> out.assign(in); - case Ninety -> out.assign(in.x, -in.z, in.y); - case OneEighty -> out.assign(in.x, -in.y, -in.z); - case TwoSeventy -> out.assign(in.x, in.z, -in.y); + case None -> out.set(in); + case Ninety -> out.set(in.x, -in.z, in.y); + case OneEighty -> out.set(in.x, -in.y, -in.z); + case TwoSeventy -> out.set(in.x, in.z, -in.y); }; } @@ -148,30 +154,30 @@ public enum Rotation implements NetworkSerializable out.assign(in); - case Ninety -> out.assign(in.z, in.y, -in.x); - case OneEighty -> out.assign(-in.x, in.y, -in.z); - case TwoSeventy -> out.assign(-in.z, in.y, in.x); + case None -> out.set(in); + case Ninety -> out.set(in.z, in.y, -in.x); + case OneEighty -> out.set(-in.x, in.y, -in.z); + case TwoSeventy -> out.set(-in.z, in.y, in.x); }; } @Nonnull public Vector3f rotateY(@Nonnull Vector3f in, @Nonnull Vector3f out) { return switch (this) { - case None -> out.assign(in); - case Ninety -> out.assign(in.z, in.y, -in.x); - case OneEighty -> out.assign(-in.x, in.y, -in.z); - case TwoSeventy -> out.assign(-in.z, in.y, in.x); + case None -> out.set(in); + case Ninety -> out.set(in.z, in.y, -in.x); + case OneEighty -> out.set(-in.x, in.y, -in.z); + case TwoSeventy -> out.set(-in.z, in.y, in.x); }; } @Nonnull public Vector3d rotateY(@Nonnull Vector3d in, @Nonnull Vector3d out) { return switch (this) { - case None -> out.assign(in); - case Ninety -> out.assign(in.z, in.y, -in.x); - case OneEighty -> out.assign(-in.x, in.y, -in.z); - case TwoSeventy -> out.assign(-in.z, in.y, in.x); + case None -> out.set(in); + case Ninety -> out.set(in.z, in.y, -in.x); + case OneEighty -> out.set(-in.x, in.y, -in.z); + case TwoSeventy -> out.set(-in.z, in.y, in.x); }; } @@ -197,30 +203,30 @@ public enum Rotation implements NetworkSerializable out.assign(in); - case Ninety -> out.assign(-in.y, in.x, in.z); - case OneEighty -> out.assign(-in.x, -in.y, in.z); - case TwoSeventy -> out.assign(in.y, -in.x, in.z); + case None -> out.set(in); + case Ninety -> out.set(-in.y, in.x, in.z); + case OneEighty -> out.set(-in.x, -in.y, in.z); + case TwoSeventy -> out.set(in.y, -in.x, in.z); }; } @Nonnull public Vector3f rotateZ(@Nonnull Vector3f in, @Nonnull Vector3f out) { return switch (this) { - case None -> out.assign(in); - case Ninety -> out.assign(-in.y, in.x, in.z); - case OneEighty -> out.assign(-in.x, -in.y, in.z); - case TwoSeventy -> out.assign(in.y, -in.x, in.z); + case None -> out.set(in); + case Ninety -> out.set(-in.y, in.x, in.z); + case OneEighty -> out.set(-in.x, -in.y, in.z); + case TwoSeventy -> out.set(in.y, -in.x, in.z); }; } @Nonnull public Vector3d rotateZ(@Nonnull Vector3d in, @Nonnull Vector3d out) { return switch (this) { - case None -> out.assign(in); - case Ninety -> out.assign(-in.y, in.x, in.z); - case OneEighty -> out.assign(-in.x, -in.y, in.z); - case TwoSeventy -> out.assign(in.y, -in.x, in.z); + case None -> out.set(in); + case Ninety -> out.set(-in.y, in.x, in.z); + case OneEighty -> out.set(-in.x, -in.y, in.z); + case TwoSeventy -> out.set(in.y, -in.x, in.z); }; } @@ -256,35 +262,88 @@ public enum Rotation implements NetworkSerializable 0; + case Y -> 1; + case Z -> 2; + }; + + for (int i = 0; i < 3; i++) { + matrix[flipRow][i] = -matrix[flipRow][i]; + } + + int[][] correction = flipCorrections[flipType.ordinal()]; + int[][] result = multiply3x3(matrix, correction); + return matrixToRotationTuple(result); + } + @Nonnull - public Vector3d rotate(@Nonnull Vector3d vector) { + public RotationTuple composeOnAxis(@Nonnull Axis axis, @Nonnull Rotation rotation) { + int[][] current = eulerToMatrix(this.yaw, this.pitch, this.roll); + int[][] axisRot = axisRotationMatrix(axis, rotation); + int[][] result = multiply3x3(axisRot, current); + return matrixToRotationTuple(result); + } + + private static int[][] eulerToMatrix(@Nonnull Rotation yaw, @Nonnull Rotation pitch, @Nonnull Rotation roll) { + int cy = cos90(yaw); + int sy = sin90(yaw); + int cp = cos90(pitch); + int sp = sin90(pitch); + int cr = cos90(roll); + int sr = sin90(roll); + return new int[][]{ + {cy * cr + sy * sp * sr, -cy * sr + sy * sp * cr, sy * cp}, {cp * sr, cp * cr, -sp}, {-sy * cr + cy * sp * sr, sy * sr + cy * sp * cr, cy * cp} + }; + } + + private static int[][] axisRotationMatrix(@Nonnull Axis axis, @Nonnull Rotation rotation) { + int c = cos90(rotation); + int s = sin90(rotation); + + return switch (axis) { + case X -> new int[][]{{1, 0, 0}, {0, c, -s}, {0, s, c}}; + case Y -> new int[][]{{c, 0, s}, {0, 1, 0}, {-s, 0, c}}; + case Z -> new int[][]{{c, -s, 0}, {s, c, 0}, {0, 0, 1}}; + }; + } + + private static int[][] multiply3x3(int[][] a, int[][] b) { + int[][] r = new int[3][3]; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + r[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j]; + } + } + + return r; + } + + private static RotationTuple matrixToRotationTuple(int[][] m) { + int sp = -m[1][2]; + Rotation newPitch = sinToRotation(sp); + Rotation newYaw; + Rotation newRoll; + if (sp != 1 && sp != -1) { + newYaw = atan2_90(m[0][2], m[2][2]); + newRoll = atan2_90(m[1][0], m[1][1]); + } else { + newYaw = atan2_90(-m[2][0], m[0][0]); + newRoll = Rotation.None; + } + + return of(newYaw, newPitch, newRoll); + } + + private static int cos90(@Nonnull Rotation r) { + return switch (r) { + case None -> 1; + case Ninety -> 0; + case OneEighty -> -1; + case TwoSeventy -> 0; + }; + } + + private static int sin90(@Nonnull Rotation r) { + return switch (r) { + case None -> 0; + case Ninety -> 1; + case OneEighty -> 0; + case TwoSeventy -> -1; + }; + } + + private static Rotation sinToRotation(int s) { + return switch (s) { + case -1 -> Rotation.TwoSeventy; + case 0 -> Rotation.None; + case 1 -> Rotation.Ninety; + default -> throw new IllegalArgumentException("Invalid sin value for 90-degree rotation: " + s); + }; + } + + private static Rotation atan2_90(int sinVal, int cosVal) { + if (sinVal == 0 && cosVal == 1) { + return Rotation.None; + } else if (sinVal == 1 && cosVal == 0) { + return Rotation.Ninety; + } else if (sinVal == 0 && cosVal == -1) { + return Rotation.OneEighty; + } else if (sinVal == -1 && cosVal == 0) { + return Rotation.TwoSeventy; + } else { + throw new IllegalArgumentException("Invalid atan2 values for 90-degree rotation: sin=" + sinVal + " cos=" + cosVal); + } + } + + @Nonnull + public Vector3d rotatedVector(@Nonnull Vector3d vector) { return Rotation.rotate(vector, this.yaw, this.pitch, this.roll); } + public void applyRotationTo(@Nonnull Vector3i vector) { + Rotation.applyRotationTo(vector, this.yaw, this.pitch, this.roll); + } + + public void applyRotationTo(@Nonnull Vector3f vector) { + Rotation.applyRotationTo(vector, this.yaw, this.pitch, this.roll); + } + + public void applyRotationTo(@Nonnull Rotation3f rotation) { + Rotation.applyRotationTo(rotation, this.yaw, this.pitch, this.roll); + } + + public void applyRotationTo(@Nonnull Vector3d vector) { + Rotation.applyRotationTo(vector, this.yaw, this.pitch, this.roll); + } + + public void undoRotationTo(@Nonnull Vector3i vector) { + Rotation.undoRotationTo(vector, this.yaw, this.pitch, this.roll); + } + + public void undoRotationTo(@Nonnull Vector3f vector) { + Rotation.undoRotationTo(vector, this.yaw, this.pitch, this.roll); + } + + public void undoRotationTo(@Nonnull Vector3d vector) { + Rotation.undoRotationTo(vector, this.yaw, this.pitch, this.roll); + } + static { RotationTuple[] arr = new RotationTuple[Rotation.VALUES.length * Rotation.VALUES.length * Rotation.VALUES.length]; arr[0] = NONE; diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/StateData.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/StateData.java index f3fc00fa..5ff4b42f 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/StateData.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/StateData.java @@ -2,12 +2,9 @@ package com.hypixel.hytale.server.core.asset.type.blocktype.config; import com.hypixel.hytale.assetstore.AssetExtraInfo; import com.hypixel.hytale.assetstore.codec.ContainedAssetCodec; -import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.map.MapCodec; -import com.hypixel.hytale.codec.lookup.CodecMapCodec; -import com.hypixel.hytale.codec.lookup.Priority; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Collections; @@ -19,9 +16,7 @@ import javax.annotation.Nullable; public class StateData { public static final String NULL_STATE_ID = "default"; - public static final BuilderCodec.Builder DEFAULT_CODEC_BUILDER = BuilderCodec.builder(StateData.class, StateData::new) - .appendInherited(new KeyedCodec<>("Id", Codec.STRING), (stateData, s) -> stateData.id = s, stateData -> stateData.id, (o, p) -> o.id = p.id) - .add() + public static final BuilderCodec.Builder CODEC_BUILDER = BuilderCodec.builder(StateData.class, StateData::new) .afterDecode((stateData, extraInfo) -> { if (stateData.stateToBlock != null) { Map map = new Object2ObjectOpenHashMap<>(); @@ -33,25 +28,13 @@ public class StateData { stateData.blockToState = Collections.unmodifiableMap(map); } }); - public static final BuilderCodec DEFAULT_CODEC = DEFAULT_CODEC_BUILDER.build(); - public static final CodecMapCodec CODEC = new CodecMapCodec(true) - .register(Priority.DEFAULT, "StateData", StateData.class, DEFAULT_CODEC); - private String id; + public static final BuilderCodec CODEC = CODEC_BUILDER.build(); private Map stateToBlock; private Map blockToState; protected StateData() { } - public StateData(String id) { - this.id = id; - } - - @Nullable - public String getId() { - return this.id; - } - @Nullable public String getBlockForState(String state) { return this.stateToBlock == null ? null : this.stateToBlock.get(state); @@ -84,7 +67,7 @@ public class StateData { @Nonnull @Override public String toString() { - return "StateData{id='" + this.id + "', stateToBlock='" + this.stateToBlock + "'}"; + return "StateData{, stateToBlock='" + this.stateToBlock + "'}"; } public void copyFrom(@Nullable StateData state) { @@ -95,7 +78,7 @@ public class StateData { } static void addDefinitions() { - DEFAULT_CODEC_BUILDER.addField( + CODEC_BUILDER.addField( new KeyedCodec<>( "Definitions", new MapCodec( diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/farming/FarmingStageData.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/farming/FarmingStageData.java index 91fb45bc..c6d24915 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/farming/FarmingStageData.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/farming/FarmingStageData.java @@ -10,14 +10,17 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.protocol.Rangef; import com.hypixel.hytale.protocol.SoundCategory; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.asset.type.soundevent.validator.SoundEventValidators; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.core.util.FillerBlockUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -74,6 +77,36 @@ public abstract class FarmingStageData { return false; } + public boolean canApply( + @Nonnull ComponentAccessor commandBuffer, @Nonnull Ref sectionRef, @Nonnull Ref blockRef, int x, int y, int z + ) { + return true; + } + + protected static boolean testFillerPositions(@Nonnull WorldChunk worldChunk, @Nonnull BlockType blockType, int x, int y, int z) { + int rotationIndex = worldChunk.getRotationIndex(x, y, z); + int originWorldX = ChunkUtil.worldCoordFromLocalCoord(worldChunk.getX(), x); + int originWorldZ = ChunkUtil.worldCoordFromLocalCoord(worldChunk.getZ(), z); + return worldChunk.testPlaceBlock( + x, + y, + z, + blockType, + rotationIndex, + (bx, by, bz, bt, br, bf) -> { + if (bx == originWorldX && by == y && bz == originWorldZ) { + return true; + } else { + return bf == 0 + ? false + : bx - FillerBlockUtil.unpackX(bf) == originWorldX + && by - FillerBlockUtil.unpackY(bf) == y + && bz - FillerBlockUtil.unpackZ(bf) == originWorldZ; + } + } + ); + } + public void apply( @Nonnull ComponentAccessor commandBuffer, @Nonnull Ref sectionRef, diff --git a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/mountpoints/BlockMountPoint.java b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/mountpoints/BlockMountPoint.java index c0eafc89..7b980351 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/mountpoints/BlockMountPoint.java +++ b/src/com/hypixel/hytale/server/core/asset/type/blocktype/config/mountpoints/BlockMountPoint.java @@ -3,15 +3,21 @@ package com.hypixel.hytale.server.core.asset.type.blocktype.config.mountpoints; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3i; public class BlockMountPoint { public static final BuilderCodec CODEC = BuilderCodec.builder(BlockMountPoint.class, BlockMountPoint::new) - .appendInherited(new KeyedCodec<>("Offset", Vector3f.CODEC), (seat, i) -> seat.offset = i, seat -> seat.offset, (seat, p) -> seat.offset = p.offset) + .appendInherited( + new KeyedCodec<>("Offset", Vector3dUtil.CODEC), (seat, i) -> seat.offset.set(i), seat -> seat.offset, (seat, p) -> seat.offset.set(p.offset) + ) .documentation("Relative offset from the block center (the point at .5,.5,.5 in world). Forward on a chair is 0,0,0.3") .add() .appendInherited( @@ -24,19 +30,19 @@ public class BlockMountPoint { .add() .build(); public static final BlockMountPoint[] EMPTY_ARRAY = new BlockMountPoint[0]; - private Vector3f offset; + private final Vector3d offset = new Vector3d(); private float yawOffSetDegrees; public BlockMountPoint() { - this(new Vector3f(), 0.0F); + this(new Vector3d(), 0.0F); } - public BlockMountPoint(Vector3f offset, float yawOffSetDegrees) { - this.offset = offset; + public BlockMountPoint(Vector3dc offset, float yawOffSetDegrees) { + this.offset.set(offset); this.yawOffSetDegrees = yawOffSetDegrees; } - public Vector3f getOffset() { + public Vector3dc getOffset() { return this.offset; } @@ -46,19 +52,19 @@ public class BlockMountPoint { @Nonnull public BlockMountPoint rotate(@Nonnull Rotation yaw, @Nonnull Rotation pitch, @Nonnull Rotation roll) { - Vector3f rotatedOffset = Rotation.rotate(this.offset, yaw, pitch, roll); + Vector3d rotatedOffset = Rotation.rotate(this.offset, yaw, pitch, roll); return new BlockMountPoint(rotatedOffset, this.yawOffSetDegrees); } @Nonnull - public Vector3f computeWorldSpacePosition(@Nonnull Vector3i blockLoc) { - return blockLoc.toVector3f().add(0.5F, 0.5F, 0.5F).add(this.offset.x, this.offset.y, this.offset.z); + public Vector3d computeWorldSpacePosition(@Nonnull Vector3i blockLoc) { + return Vector3iUtil.toVector3d(blockLoc).add(0.5, 0.5, 0.5).add(this.offset.x, this.offset.y, this.offset.z); } @Nonnull - public Vector3f computeRotationEuler(@Nonnull int rotationIndex) { + public Rotation3f computeRotationEuler(@Nonnull int rotationIndex) { RotationTuple rotationTuple = RotationTuple.get(rotationIndex); - Vector3f rotation = new Vector3f( + Rotation3f rotation = new Rotation3f( (float)rotationTuple.pitch().getRadians(), (float)rotationTuple.yaw().getRadians(), (float)rotationTuple.roll().getRadians() ); rotation.addYaw((float) Math.PI); diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BrushData.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BrushData.java deleted file mode 100644 index f71bede7..00000000 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BrushData.java +++ /dev/null @@ -1,800 +0,0 @@ -package com.hypixel.hytale.server.core.asset.type.buildertool.config; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.codecs.array.ArrayCodec; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.common.util.ArrayUtil; -import com.hypixel.hytale.protocol.Rotation; -import com.hypixel.hytale.protocol.packets.buildertools.BrushAxis; -import com.hypixel.hytale.protocol.packets.buildertools.BrushOrigin; -import com.hypixel.hytale.protocol.packets.buildertools.BrushShape; -import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolBlockArg; -import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolBrushData; -import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolStringArg; -import com.hypixel.hytale.server.core.Message; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BlockArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BoolArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BrushAxisArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BrushOriginArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BrushRotationArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BrushShapeArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.IntArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.MaskArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.StringArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.ToolArg; -import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.ToolArgException; -import com.hypixel.hytale.server.core.io.NetworkSerializable; -import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask; -import com.hypixel.hytale.server.core.prefab.selection.mask.BlockPattern; -import java.util.Arrays; -import java.util.Objects; -import java.util.regex.Pattern; -import javax.annotation.Nonnull; - -public class BrushData implements NetworkSerializable { - public static final String WIDTH_KEY = "Width"; - public static final String HEIGHT_KEY = "Height"; - public static final String SHAPE_KEY = "Shape"; - public static final String THICKNESS_KEY = "Thickness"; - public static final String CAPPED_KEY = "Capped"; - public static final String ORIGIN_KEY = "Origin"; - public static final String ORIGIN_ROTATION_KEY = "OriginRotation"; - public static final String ROTATION_AXIS_KEY = "RotationAxis"; - public static final String ROTATION_ANGLE_KEY = "RotationAngle"; - public static final String MIRROR_AXIS_KEY = "MirrorAxis"; - public static final String MATERIAL_KEY = "Material"; - public static final String FAVORITE_MATERIALS_KEY = "FavoriteMaterials"; - public static final String MASK_KEY = "Mask"; - public static final String MASK_ABOVE_KEY = "MaskAbove"; - public static final String MASK_NOT_KEY = "MaskNot"; - public static final String MASK_BELOW_KEY = "MaskBelow"; - public static final String MASK_ADJACENT_KEY = "MaskAdjacent"; - public static final String MASK_NEIGHBOR_KEY = "MaskNeighbor"; - public static final String MASK_COMMANDS_KEY = "MaskCommands"; - public static final String USE_MASK_COMMANDS_KEY = "UseMaskCommands"; - public static final String INVERT_MASK_KEY = "InvertMask"; - private static final String WIDTH_DOC = "The width of the brush shape"; - private static final String HEIGHT_DOC = "The height of the brush shape"; - private static final String THICKNESS_DOC = "The number of blocks thick the walls of the brush shape should be"; - private static final String CAPPED_DOC = "Controls whether the end(s) of hollow brush shapes are closed or open"; - private static final String SHAPE_DOC = "The brush shape"; - private static final String ORIGIN_DOC = "The origin of the brush shape"; - private static final String ORIGIN_ROTATION_DOC = "Toggles the vertical offset for shapes rotated about the x/z axis"; - private static final String ROTATION_AXIS_DOC = "The axis that the brush shape should rotate around"; - private static final String ROTATION_ANGLE_DOC = "The angle that the brush shape should be rotated by"; - private static final String MIRROR_AXIS_DOC = "The axis that the brush shape should mirror in"; - private static final String MATERIAL_DOC = "The material to apply when the brush is used"; - private static final String FAVORITE_MATERIALS_DOC = "Materials available for quick selection.\n\nWhen a material is selected from here, it is set on the Material key."; - private static final String MASK_DOC = "Limits the selection to blocks matching materials in this mask"; - private static final String MASK_ABOVE_DOC = "Limits the selection to blocks above ones matching materials in this mask"; - private static final String MASK_NOT_DOC = "Limits the selection to any blocks except ones matching materials in this mask"; - private static final String MASK_BELOW_DOC = "Limits the selection to blocks below ones matching materials in this mask"; - private static final String MASK_ADJACENT_DOC = "Limits the selection to blocks horizontally adjacent to ones matching materials in this mask"; - private static final String MASK_NEIGHBOR_DOC = "Limits the selection to blocks neighboring (in any direction) ones matching materials in this mask"; - private static final String MASK_COMMANDS_DOC = "Custom mask commands to apply to the brush, based on /gmask syntax"; - private static final String USE_MASK_COMMANDS_DOC = "Specifies whether to use the block selector mask values or custom mask commands"; - private static final String INVERT_MASK_DOC = "When enabled, inverts the entire combined mask result"; - public static final int DEFAULT_WIDTH = 5; - public static final int DEFAULT_HEIGHT = 5; - public static final BrushData DEFAULT = new BrushData(); - public static final int DEFAULT_FAVORITE_MATERIALS_CAPACITY = 5; - private static final Pattern NEWLINES_PATTERN = Pattern.compile("\\r?\\n"); - public static final BuilderCodec CODEC = BuilderCodec.builder(BrushData.class, BrushData::new) - .append(new KeyedCodec<>("Width", IntArg.CODEC), (brushData, o) -> brushData.width = o, brushData -> brushData.width) - .addValidator(Validators.nonNull()) - .documentation("The width of the brush shape") - .add() - .append(new KeyedCodec<>("Height", IntArg.CODEC), (brushData, o) -> brushData.height = o, brushData -> brushData.height) - .addValidator(Validators.nonNull()) - .documentation("The height of the brush shape") - .add() - .append(new KeyedCodec<>("Thickness", IntArg.CODEC), (data, o) -> data.thickness = o, data -> data.thickness) - .addValidator(Validators.nonNull()) - .documentation("The number of blocks thick the walls of the brush shape should be") - .add() - .append(new KeyedCodec<>("Capped", BoolArg.CODEC), (data, o) -> data.capped = o, data -> data.capped) - .addValidator(Validators.nonNull()) - .documentation("Controls whether the end(s) of hollow brush shapes are closed or open") - .add() - .append(new KeyedCodec<>("Shape", BrushShapeArg.CODEC), (brushData, o) -> brushData.shape = o, brushData -> brushData.shape) - .addValidator(Validators.nonNull()) - .documentation("The brush shape") - .add() - .append(new KeyedCodec<>("Origin", BrushOriginArg.CODEC), (brushData, o) -> brushData.origin = o, brushData -> brushData.origin) - .addValidator(Validators.nonNull()) - .documentation("The origin of the brush shape") - .add() - .append(new KeyedCodec<>("OriginRotation", BoolArg.CODEC), (data, o) -> data.originRotation = o, data -> data.originRotation) - .addValidator(Validators.nonNull()) - .documentation("Toggles the vertical offset for shapes rotated about the x/z axis") - .add() - .append(new KeyedCodec<>("RotationAxis", BrushAxisArg.CODEC), (data, o) -> data.rotationAxis = o, data -> data.rotationAxis) - .addValidator(Validators.nonNull()) - .documentation("The axis that the brush shape should rotate around") - .add() - .append(new KeyedCodec<>("RotationAngle", BrushRotationArg.CODEC), (data, o) -> data.rotationAngle = o, data -> data.rotationAngle) - .addValidator(Validators.nonNull()) - .documentation("The angle that the brush shape should be rotated by") - .add() - .append(new KeyedCodec<>("MirrorAxis", BrushAxisArg.CODEC), (data, o) -> data.mirrorAxis = o, data -> data.mirrorAxis) - .addValidator(Validators.nonNull()) - .documentation("The axis that the brush shape should mirror in") - .add() - .append(new KeyedCodec<>("Material", BlockArg.CODEC), (brushData, o) -> brushData.material = o, brushData -> brushData.material) - .addValidator(Validators.nonNull()) - .documentation("The material to apply when the brush is used") - .add() - .append( - new KeyedCodec<>("FavoriteMaterials", new ArrayCodec<>(BlockArg.CODEC, BlockArg[]::new)), - (brushData, o) -> brushData.favoriteMaterials = o, - brushData -> brushData.favoriteMaterials - ) - .documentation("Materials available for quick selection.\n\nWhen a material is selected from here, it is set on the Material key.") - .add() - .append(new KeyedCodec<>("Mask", MaskArg.CODEC), (brushData, o) -> brushData.mask = o, brushData -> brushData.mask) - .documentation("Limits the selection to blocks matching materials in this mask") - .add() - .append(new KeyedCodec<>("MaskAbove", MaskArg.CODEC), (brushData, o) -> brushData.maskAbove = o, brushData -> brushData.maskAbove) - .documentation("Limits the selection to blocks above ones matching materials in this mask") - .add() - .append(new KeyedCodec<>("MaskNot", MaskArg.CODEC), (brushData, o) -> brushData.maskNot = o, brushData -> brushData.maskNot) - .documentation("Limits the selection to any blocks except ones matching materials in this mask") - .add() - .append(new KeyedCodec<>("MaskBelow", MaskArg.CODEC), (brushData, o) -> brushData.maskBelow = o, brushData -> brushData.maskBelow) - .documentation("Limits the selection to blocks below ones matching materials in this mask") - .add() - .append(new KeyedCodec<>("MaskAdjacent", MaskArg.CODEC), (brushData, o) -> brushData.maskAdjacent = o, brushData -> brushData.maskAdjacent) - .documentation("Limits the selection to blocks horizontally adjacent to ones matching materials in this mask") - .add() - .append(new KeyedCodec<>("MaskNeighbor", MaskArg.CODEC), (brushData, o) -> brushData.maskNeighbor = o, brushData -> brushData.maskNeighbor) - .documentation("Limits the selection to blocks neighboring (in any direction) ones matching materials in this mask") - .add() - .append( - new KeyedCodec<>("MaskCommands", new ArrayCodec<>(StringArg.CODEC, StringArg[]::new)), - (brushData, o) -> brushData.maskCommands = o, - brushData -> brushData.maskCommands - ) - .documentation("Custom mask commands to apply to the brush, based on /gmask syntax") - .add() - .append( - new KeyedCodec<>("UseMaskCommands", BoolArg.CODEC), (brushData, o) -> brushData.useMaskCommands = o, brushData -> brushData.useMaskCommands - ) - .documentation("Specifies whether to use the block selector mask values or custom mask commands") - .add() - .append(new KeyedCodec<>("InvertMask", BoolArg.CODEC), (brushData, o) -> brushData.invertMask = o, brushData -> brushData.invertMask) - .documentation("When enabled, inverts the entire combined mask result") - .add() - .build(); - protected IntArg width = new IntArg(5, 1, 100); - protected IntArg height = new IntArg(5, 1, 100); - protected IntArg thickness = new IntArg(0, 0, 100); - protected BoolArg capped = new BoolArg(false); - protected BrushShapeArg shape = new BrushShapeArg(BrushShape.Sphere); - protected BrushOriginArg origin = new BrushOriginArg(BrushOrigin.Center); - protected BoolArg originRotation = new BoolArg(false); - protected BrushAxisArg rotationAxis = new BrushAxisArg(BrushAxis.None); - protected BrushRotationArg rotationAngle = new BrushRotationArg(Rotation.None); - protected BrushAxisArg mirrorAxis = new BrushAxisArg(BrushAxis.None); - protected BlockArg material = new BlockArg(BlockPattern.EMPTY, true); - protected BlockArg[] favoriteMaterials = BlockArg.EMPTY_ARRAY; - protected MaskArg mask = MaskArg.EMPTY; - protected MaskArg maskAbove = MaskArg.EMPTY; - protected MaskArg maskNot = MaskArg.EMPTY; - protected MaskArg maskBelow = MaskArg.EMPTY; - protected MaskArg maskAdjacent = MaskArg.EMPTY; - protected MaskArg maskNeighbor = MaskArg.EMPTY; - protected StringArg[] maskCommands = StringArg.EMPTY_ARRAY; - protected BoolArg useMaskCommands = new BoolArg(false); - protected BoolArg invertMask = new BoolArg(false); - - protected BrushData() { - } - - public BrushData( - IntArg width, - IntArg height, - IntArg thickness, - BoolArg capped, - BrushShapeArg shape, - BrushOriginArg origin, - BoolArg originRotation, - BrushAxisArg rotationAxis, - BrushRotationArg rotationAngle, - BrushAxisArg mirrorAxis, - BlockArg material, - BlockArg[] favoriteMaterials, - MaskArg mask, - MaskArg maskAbove, - MaskArg maskNot, - MaskArg maskBelow, - MaskArg maskAdjacent, - MaskArg maskNeighbor, - StringArg[] maskCommands, - BoolArg useMaskCommands - ) { - this.width = width; - this.height = height; - this.thickness = thickness; - this.capped = capped; - this.shape = shape; - this.origin = origin; - this.originRotation = originRotation; - this.rotationAxis = rotationAxis; - this.rotationAngle = rotationAngle; - this.mirrorAxis = mirrorAxis; - this.material = material; - this.favoriteMaterials = favoriteMaterials; - this.mask = mask; - this.maskAbove = maskAbove; - this.maskNot = maskNot; - this.maskBelow = maskBelow; - this.maskAdjacent = maskAdjacent; - this.maskNeighbor = maskNeighbor; - this.maskCommands = maskCommands; - this.useMaskCommands = useMaskCommands; - } - - public IntArg getWidth() { - return this.width; - } - - public IntArg getHeight() { - return this.height; - } - - public IntArg getThickness() { - return this.thickness; - } - - public BoolArg getCapped() { - return this.capped; - } - - public BrushShapeArg getShape() { - return this.shape; - } - - public BrushOriginArg getOrigin() { - return this.origin; - } - - public BoolArg getOriginRotation() { - return this.originRotation; - } - - public BrushAxisArg getRotationAxis() { - return this.rotationAxis; - } - - public BrushRotationArg getRotationAngle() { - return this.rotationAngle; - } - - public BrushAxisArg getMirrorAxis() { - return this.mirrorAxis; - } - - public BlockArg getMaterial() { - return this.material; - } - - public BlockArg[] getFavoriteMaterials() { - return this.favoriteMaterials; - } - - public MaskArg getMask() { - return this.mask; - } - - public MaskArg getMaskAbove() { - return this.maskAbove; - } - - public MaskArg getMaskNot() { - return this.maskNot; - } - - public MaskArg getMaskBelow() { - return this.maskBelow; - } - - public MaskArg getMaskAdjacent() { - return this.maskAdjacent; - } - - public MaskArg getMaskNeighbor() { - return this.maskNeighbor; - } - - public StringArg[] getMaskCommands() { - return this.maskCommands; - } - - public BoolArg getUseMaskCommands() { - return this.useMaskCommands; - } - - public BoolArg getInvertMask() { - return this.invertMask; - } - - public void updateArgValue(@Nonnull BrushData.Values brush, @Nonnull String id, @Nonnull String value) throws ToolArgException { - switch (id) { - case "Height": - brush.height = this.height.fromString(value); - break; - case "Width": - brush.width = this.width.fromString(value); - break; - case "Thickness": - brush.thickness = this.thickness.fromString(value); - break; - case "Capped": - brush.capped = this.capped.fromString(value); - break; - case "Shape": - brush.shape = this.shape.fromString(value); - break; - case "Origin": - brush.origin = this.origin.fromString(value); - break; - case "OriginRotation": - brush.originRotation = this.originRotation.fromString(value); - break; - case "RotationAxis": - brush.rotationAxis = this.rotationAxis.fromString(value); - break; - case "RotationAngle": - brush.rotationAngle = this.rotationAngle.fromString(value); - break; - case "MirrorAxis": - brush.mirrorAxis = this.mirrorAxis.fromString(value); - break; - case "Material": - brush.material = this.material.fromString(value); - break; - case "FavoriteMaterials": - brush.favoriteMaterials = value.isEmpty() - ? BlockPattern.EMPTY_ARRAY - : Arrays.stream(value.split(",")).limit(5L).map(BlockPattern::parse).toArray(BlockPattern[]::new); - break; - case "Mask": - brush.mask = this.mask.fromString(value); - break; - case "MaskAbove": - brush.maskAbove = this.maskAbove.fromString(value); - break; - case "MaskNot": - brush.maskNot = this.maskNot.fromString(value); - break; - case "MaskBelow": - brush.maskBelow = this.maskBelow.fromString(value); - break; - case "MaskAdjacent": - brush.maskAdjacent = this.maskAdjacent.fromString(value); - break; - case "MaskNeighbor": - brush.maskNeighbor = this.maskNeighbor.fromString(value); - break; - case "MaskCommands": - brush.maskCommands = value.isEmpty() ? ArrayUtil.EMPTY_STRING_ARRAY : NEWLINES_PATTERN.split(value); - break; - case "UseMaskCommands": - brush.useMaskCommands = this.useMaskCommands.fromString(value); - break; - case "InvertMask": - brush.invertMask = this.invertMask.fromString(value); - break; - default: - throw new ToolArgException(Message.translation("server.builderTools.toolUnknownArg").param("arg", id)); - } - } - - @Nonnull - public BuilderToolBrushData toPacket() { - BuilderToolBrushData packet = new BuilderToolBrushData(); - packet.width = this.width.toIntArgPacket(); - packet.height = this.height.toIntArgPacket(); - packet.thickness = this.thickness.toIntArgPacket(); - packet.capped = this.capped.toBoolArgPacket(); - packet.shape = this.shape.toBrushShapeArgPacket(); - packet.origin = this.origin.toBrushOriginArgPacket(); - packet.originRotation = this.originRotation.toBoolArgPacket(); - packet.rotationAxis = this.rotationAxis.toBrushAxisArgPacket(); - packet.rotationAngle = this.rotationAngle.toRotationArgPacket(); - packet.mirrorAxis = this.mirrorAxis.toBrushAxisArgPacket(); - packet.material = this.material.toBlockArgPacket(); - packet.favoriteMaterials = Arrays.stream(this.favoriteMaterials) - .filter(Objects::nonNull) - .map(BlockArg::toBlockArgPacket) - .toArray(BuilderToolBlockArg[]::new); - packet.mask = this.mask.toMaskArgPacket(); - packet.maskAbove = this.maskAbove.toMaskArgPacket(); - packet.maskNot = this.maskNot.toMaskArgPacket(); - packet.maskBelow = this.maskBelow.toMaskArgPacket(); - packet.maskAdjacent = this.maskAdjacent.toMaskArgPacket(); - packet.maskNeighbor = this.maskNeighbor.toMaskArgPacket(); - packet.maskCommands = Arrays.stream(this.maskCommands).filter(Objects::nonNull).map(StringArg::toStringArgPacket).toArray(BuilderToolStringArg[]::new); - packet.useMaskCommands = this.useMaskCommands.toBoolArgPacket(); - packet.invertMask = this.invertMask.toBoolArgPacket(); - return packet; - } - - @Nonnull - @Override - public String toString() { - return "BrushData{width=" - + this.width - + ", height=" - + this.height - + ", thickness=" - + this.thickness - + ", capped=" - + this.capped - + ", shape=" - + this.shape - + ", origin=" - + this.origin - + ", originRotation=" - + this.originRotation - + ", rotationAxis=" - + this.rotationAxis - + ", rotationAngle=" - + this.rotationAngle - + ", mirrorAxis=" - + this.mirrorAxis - + ", material=" - + this.material - + ", favoriteMaterials=" - + Arrays.toString((Object[])this.favoriteMaterials) - + ", mask=" - + this.mask - + ", maskAbove=" - + this.maskAbove - + ", maskNot=" - + this.maskNot - + ", maskBelow=" - + this.maskBelow - + ", maskAdjacent=" - + this.maskAdjacent - + ", maskNeighbor=" - + this.maskNeighbor - + ", maskCommands=" - + Arrays.toString((Object[])this.maskCommands) - + ", useMaskCommands=" - + this.useMaskCommands - + ", invertMask=" - + this.invertMask - + "}"; - } - - public static class Values { - public static final Codec CODEC = BuilderCodec.builder(BrushData.Values.class, BrushData.Values::new) - .append(new KeyedCodec<>("Width", Codec.INTEGER), (brushData, o) -> brushData.width = o, brushData -> brushData.width) - .addValidator(Validators.greaterThan(0)) - .documentation("The width of the brush shape") - .add() - .append(new KeyedCodec<>("Height", Codec.INTEGER), (brushData, o) -> brushData.height = o, brushData -> brushData.height) - .addValidator(Validators.greaterThan(0)) - .documentation("The height of the brush shape") - .add() - .append(new KeyedCodec<>("Thickness", Codec.INTEGER), (data, o) -> data.thickness = o, data -> data.thickness) - .addValidator(Validators.range(0, 100)) - .documentation("The number of blocks thick the walls of the brush shape should be") - .add() - .append(new KeyedCodec<>("Capped", Codec.BOOLEAN), (data, o) -> data.capped = o, data -> data.capped) - .addValidator(Validators.nonNull()) - .documentation("Controls whether the end(s) of hollow brush shapes are closed or open") - .add() - .append(new KeyedCodec<>("Shape", BrushShapeArg.BRUSH_SHAPE_CODEC), (brushData, o) -> brushData.shape = o, brushData -> brushData.shape) - .addValidator(Validators.nonNull()) - .documentation("The brush shape") - .add() - .append( - new KeyedCodec<>("Origin", BrushOriginArg.BRUSH_ORIGIN_CODEC), (brushData, o) -> brushData.origin = o, brushData -> brushData.origin - ) - .addValidator(Validators.nonNull()) - .documentation("The origin of the brush shape") - .add() - .append(new KeyedCodec<>("OriginRotation", Codec.BOOLEAN), (data, o) -> data.originRotation = o, data -> data.originRotation) - .addValidator(Validators.nonNull()) - .documentation("Toggles the vertical offset for shapes rotated about the x/z axis") - .add() - .append(new KeyedCodec<>("RotationAxis", BrushAxisArg.BRUSH_AXIS_CODEC), (data, o) -> data.rotationAxis = o, data -> data.rotationAxis) - .addValidator(Validators.nonNull()) - .documentation("The axis that the brush shape should rotate around") - .add() - .append(new KeyedCodec<>("RotationAngle", BrushRotationArg.ROTATION_CODEC), (data, o) -> data.rotationAngle = o, data -> data.rotationAngle) - .addValidator(Validators.nonNull()) - .documentation("The angle that the brush shape should be rotated by") - .add() - .append(new KeyedCodec<>("MirrorAxis", BrushAxisArg.BRUSH_AXIS_CODEC), (data, o) -> data.mirrorAxis = o, data -> data.mirrorAxis) - .addValidator(Validators.nonNull()) - .documentation("The axis that the brush shape should mirror in") - .add() - .append(new KeyedCodec<>("Material", BlockPattern.CODEC), (brushData, o) -> brushData.material = o, brushData -> brushData.material) - .addValidator(Validators.nonNull()) - .documentation("The material to apply when the brush is used") - .add() - .append( - new KeyedCodec<>("FavoriteMaterials", new ArrayCodec<>(BlockPattern.CODEC, BlockPattern[]::new)), - (brushData, o) -> brushData.favoriteMaterials = o, - brushData -> brushData.favoriteMaterials - ) - .addValidator(Validators.arraySizeRange(0, 5)) - .documentation("Materials available for quick selection.\n\nWhen a material is selected from here, it is set on the Material key.") - .add() - .append(new KeyedCodec<>("Mask", BlockMask.CODEC), (brushData, o) -> brushData.mask = o, brushData -> brushData.mask) - .documentation("Limits the selection to blocks matching materials in this mask") - .add() - .append(new KeyedCodec<>("MaskAbove", BlockMask.CODEC), (brushData, o) -> brushData.maskAbove = o, brushData -> brushData.maskAbove) - .documentation("Limits the selection to blocks above ones matching materials in this mask") - .add() - .append(new KeyedCodec<>("MaskNot", BlockMask.CODEC), (brushData, o) -> brushData.maskNot = o, brushData -> brushData.maskNot) - .documentation("Limits the selection to any blocks except ones matching materials in this mask") - .add() - .append(new KeyedCodec<>("MaskBelow", BlockMask.CODEC), (brushData, o) -> brushData.maskBelow = o, brushData -> brushData.maskBelow) - .documentation("Limits the selection to blocks below ones matching materials in this mask") - .add() - .append( - new KeyedCodec<>("MaskAdjacent", BlockMask.CODEC), (brushData, o) -> brushData.maskAdjacent = o, brushData -> brushData.maskAdjacent - ) - .documentation("Limits the selection to blocks horizontally adjacent to ones matching materials in this mask") - .add() - .append( - new KeyedCodec<>("MaskNeighbor", BlockMask.CODEC), (brushData, o) -> brushData.maskNeighbor = o, brushData -> brushData.maskNeighbor - ) - .documentation("Limits the selection to blocks neighboring (in any direction) ones matching materials in this mask") - .add() - .append( - new KeyedCodec<>("MaskCommands", Codec.STRING_ARRAY), (brushData, o) -> brushData.maskCommands = o, brushData -> brushData.maskCommands - ) - .documentation("Custom mask commands to apply to the brush, based on /gmask syntax") - .add() - .append( - new KeyedCodec<>("UseMaskCommands", Codec.BOOLEAN), (brushData, o) -> brushData.useMaskCommands = o, brushData -> brushData.useMaskCommands - ) - .documentation("Specifies whether to use the block selector mask values or custom mask commands") - .add() - .append(new KeyedCodec<>("InvertMask", Codec.BOOLEAN), (brushData, o) -> brushData.invertMask = o, brushData -> brushData.invertMask) - .documentation("When enabled, inverts the entire combined mask result") - .add() - .build(); - private int width; - private int height; - private int thickness; - private boolean capped; - private BrushShape shape; - private BrushOrigin origin; - private boolean originRotation; - private BrushAxis rotationAxis; - private Rotation rotationAngle; - private BrushAxis mirrorAxis; - private BlockPattern material; - private BlockPattern[] favoriteMaterials; - private BlockMask mask; - private BlockMask maskAbove; - private BlockMask maskNot; - private BlockMask maskBelow; - private BlockMask maskAdjacent; - private BlockMask maskNeighbor; - private String[] maskCommands; - private boolean useMaskCommands; - private boolean invertMask; - - protected Values() { - this(BrushData.DEFAULT); - } - - public Values(@Nonnull BrushData brushData) { - this.width = brushData.width.getValue(); - this.height = brushData.height.getValue(); - this.thickness = brushData.thickness.getValue(); - this.capped = brushData.capped.getValue(); - this.shape = brushData.shape.getValue(); - this.origin = brushData.origin.getValue(); - this.originRotation = brushData.originRotation.getValue(); - this.rotationAxis = brushData.rotationAxis.getValue(); - this.rotationAngle = brushData.rotationAngle.getValue(); - this.mirrorAxis = brushData.mirrorAxis.getValue(); - this.material = brushData.material.getValue(); - this.favoriteMaterials = brushData.favoriteMaterials.length == 0 - ? BlockPattern.EMPTY_ARRAY - : Arrays.stream(brushData.favoriteMaterials).limit(5L).map(ToolArg::getValue).toArray(BlockPattern[]::new); - this.mask = brushData.mask.getValue(); - this.maskAbove = brushData.maskAbove.getValue(); - this.maskNot = brushData.maskNot.getValue(); - this.maskBelow = brushData.maskBelow.getValue(); - this.maskAdjacent = brushData.maskAdjacent.getValue(); - this.maskNeighbor = brushData.maskNeighbor.getValue(); - this.maskCommands = brushData.maskCommands.length == 0 - ? ArrayUtil.EMPTY_STRING_ARRAY - : Arrays.stream(brushData.maskCommands).map(ToolArg::getValue).toArray(String[]::new); - this.useMaskCommands = brushData.useMaskCommands.getValue(); - this.invertMask = brushData.invertMask.getValue(); - } - - public Values( - int width, - int height, - int thickness, - boolean capped, - BrushShape shape, - BrushOrigin origin, - boolean originRotation, - BrushAxis rotationAxis, - Rotation rotationAngle, - BrushAxis mirrorAxis, - BlockPattern material, - BlockPattern[] favoriteMaterials, - BlockMask mask, - BlockMask maskAbove, - BlockMask maskNot, - BlockMask maskBelow, - BlockMask maskAdjacent, - BlockMask maskNeighbor, - String[] maskCommands, - boolean useMaskCommands - ) { - this.width = width; - this.height = height; - this.thickness = thickness; - this.capped = capped; - this.shape = shape; - this.origin = origin; - this.originRotation = originRotation; - this.rotationAxis = rotationAxis; - this.rotationAngle = rotationAngle; - this.mirrorAxis = mirrorAxis; - this.material = material; - this.favoriteMaterials = favoriteMaterials; - this.mask = mask; - this.maskAbove = maskAbove; - this.maskNot = maskNot; - this.maskBelow = maskBelow; - this.maskAdjacent = maskAdjacent; - this.maskNeighbor = maskNeighbor; - this.maskCommands = maskCommands; - this.useMaskCommands = useMaskCommands; - } - - public int getWidth() { - return this.width; - } - - public int getHeight() { - return this.height; - } - - public int getThickness() { - return this.thickness; - } - - public boolean isCapped() { - return this.capped; - } - - public BrushShape getShape() { - return this.shape; - } - - public BrushOrigin getOrigin() { - return this.origin; - } - - public boolean getOriginRotation() { - return this.originRotation; - } - - public BrushAxis getRotationAxis() { - return this.rotationAxis; - } - - public Rotation getRotationAngle() { - return this.rotationAngle; - } - - public BrushAxis getMirrorAxis() { - return this.mirrorAxis; - } - - public BlockPattern getMaterial() { - return this.material; - } - - public BlockPattern[] getFavoriteMaterials() { - return this.favoriteMaterials; - } - - public BlockMask getMask() { - return this.mask; - } - - public BlockMask getMaskAbove() { - return this.maskAbove; - } - - public BlockMask getMaskNot() { - return this.maskNot; - } - - public BlockMask getMaskBelow() { - return this.maskBelow; - } - - public BlockMask getMaskAdjacent() { - return this.maskAdjacent; - } - - public BlockMask getMaskNeighbor() { - return this.maskNeighbor; - } - - public String[] getMaskCommands() { - return this.maskCommands; - } - - @Nonnull - public BlockMask[] getParsedMaskCommands() { - return Arrays.stream(this.getMaskCommands()).map(m -> m.split(" ")).map(BlockMask::parse).toArray(BlockMask[]::new); - } - - public boolean shouldUseMaskCommands() { - return this.useMaskCommands; - } - - public boolean shouldInvertMask() { - return this.invertMask; - } - - @Nonnull - @Override - public String toString() { - return "Values{width=" - + this.width - + ", height=" - + this.height - + ", thickness=" - + this.thickness - + ", capped=" - + this.capped - + ", shape=" - + this.shape - + ", origin=" - + this.origin - + ", originRotation=" - + this.originRotation - + ", rotationAxis=" - + this.rotationAxis - + ", rotationAngle=" - + this.rotationAngle - + ", mirrorAxis=" - + this.mirrorAxis - + ", material=" - + this.material - + ", favoriteMaterials=" - + Arrays.toString((Object[])this.favoriteMaterials) - + ", mask=" - + this.mask - + ", maskAbove=" - + this.maskAbove - + ", maskNot=" - + this.maskNot - + ", maskBelow=" - + this.maskBelow - + ", maskAdjacent=" - + this.maskAdjacent - + ", maskNeighbor=" - + this.maskNeighbor - + ", maskCommands=" - + Arrays.toString((Object[])this.maskCommands) - + ", useMaskCommands=" - + this.useMaskCommands - + ", invertMask=" - + this.invertMask - + "}"; - } - } -} diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BuilderTool.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BuilderTool.java index db71601e..e2fa3152 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BuilderTool.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BuilderTool.java @@ -7,22 +7,28 @@ import com.hypixel.hytale.assetstore.map.DefaultAssetMap; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.codecs.map.MapCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.lookup.MapProvidedMapCodec; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolArg; -import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolArgGroup; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolState; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.BoolArg; +import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.MaskArg; +import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.StringArg; import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.ToolArg; import com.hypixel.hytale.server.core.asset.type.buildertool.config.args.ToolArgException; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.io.NetworkSerializable; +import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.lang.ref.SoftReference; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; @@ -31,26 +37,72 @@ import org.bson.BsonDocument; public class BuilderTool implements JsonAssetWithMap>, NetworkSerializable { public static final String TOOL_DATA_KEY = "ToolData"; - public static final KeyedCodec BRUSH_DATA_KEY_CODEC = new KeyedCodec<>("BrushData", BrushData.Values.CODEC); + public static final String MATERIAL_KEY = "builtin_Material"; + public static final String FAVORITE_MATERIALS_KEY = "builtin_FavoriteMaterials"; + public static final String WIDTH_KEY = "builtin_Width"; + public static final String HEIGHT_KEY = "builtin_Height"; + public static final String THICKNESS_KEY = "builtin_Thickness"; + public static final String CAPPED_KEY = "builtin_Capped"; + public static final String SHAPE_KEY = "builtin_Shape"; + public static final String ORIGIN_KEY = "builtin_Origin"; + public static final String ORIGIN_ROTATION_KEY = "builtin_OriginRotation"; + public static final String ROTATION_AXIS_KEY = "builtin_RotationAxis"; + public static final String ROTATION_ANGLE_KEY = "builtin_RotationAngle"; + public static final String MIRROR_AXIS_KEY = "builtin_MirrorAxis"; + public static final String ROTATION_FACE_KEY = "builtin_RotationFace"; + public static final String DENSITY_KEY = "builtin_Density"; + public static final String SPACING_KEY = "builtin_Spacing"; + public static final String MASK_KEY = "builtin_Mask"; + public static final String MASK_ABOVE_KEY = "builtin_MaskAbove"; + public static final String MASK_NOT_KEY = "builtin_MaskNot"; + public static final String MASK_BELOW_KEY = "builtin_MaskBelow"; + public static final String MASK_ADJACENT_KEY = "builtin_MaskAdjacent"; + public static final String MASK_NEIGHBOR_KEY = "builtin_MaskNeighbor"; + public static final String MASK_COMMANDS_KEY = "builtin_MaskCommands"; + public static final String USE_MASK_COMMANDS_KEY = "builtin_UseMaskCommands"; + public static final String INVERT_MASK_KEY = "builtin_InvertMask"; + public static HashSet MASK_ARGS = setMandatoryToolArgs(); public static final BuilderTool DEFAULT = new BuilderTool(); public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( BuilderTool.class, BuilderTool::new, Codec.STRING, (t, k) -> t.id = k, t -> t.id, (asset, data) -> asset.data = data, asset -> asset.data ) - .addField(new KeyedCodec<>("Id", Codec.STRING), (builderTool, o) -> builderTool.id = o, builderTool -> builderTool.id) - .addField(new KeyedCodec<>("IsBrush", Codec.BOOLEAN), (builderTool, o) -> builderTool.isBrush = o, builderTool -> builderTool.isBrush) - .addField( + .append(new KeyedCodec<>("Id", Codec.STRING), (builderTool, o) -> builderTool.id = o, builderTool -> builderTool.id) + .add() + .append(new KeyedCodec<>("IsBrush", Codec.BOOLEAN), (builderTool, o) -> builderTool.isBrush = o, builderTool -> builderTool.isBrush) + .add() + .append( new KeyedCodec<>("BrushConfigurationCommand", Codec.STRING), (builderTool, o) -> builderTool.brushConfigurationCommand = o, builderTool -> builderTool.brushConfigurationCommand ) - .addField( - new KeyedCodec<>("Args", new MapCodec<>(ToolArg.CODEC, HashMap::new)), (builderTool, s) -> builderTool.args = s, builderTool -> builderTool.args - ) - .addField(new KeyedCodec<>("BrushData", BrushData.CODEC), (builderTool, o) -> builderTool.brushData = o, builderTool -> builderTool.brushData) + .add() + .append(new KeyedCodec<>("Args", new ArrayCodec<>(ToolArg.CODEC, ToolArg[]::new)), (builderTool, s) -> { + builderTool.args = new LinkedHashMap<>(); + Arrays.stream(s).forEach(arg -> builderTool.args.put(arg.getId(), arg)); + }, builderTool -> builderTool.args.values().toArray(new ToolArg[builderTool.args.size()])) + .add() .afterDecode(builderTool -> { - if (!builderTool.args.isEmpty()) { - builderTool.argsCodec = new MapProvidedMapCodec<>(builderTool.args, ToolArg::getCodec, HashMap::new); + Map allArgs = new LinkedHashMap<>(builderTool.args); + + for (String maskKey : MASK_ARGS) { + if (!allArgs.containsKey(maskKey)) { + allArgs.put(maskKey, MaskArg.EMPTY); + } } + + if (!allArgs.containsKey("builtin_InvertMask")) { + allArgs.put("builtin_InvertMask", new BoolArg(false)); + } + + if (!allArgs.containsKey("builtin_UseMaskCommands")) { + allArgs.put("builtin_UseMaskCommands", new BoolArg(false)); + } + + if (!allArgs.containsKey("builtin_MaskCommands")) { + allArgs.put("builtin_MaskCommands", new StringArg("")); + } + + builderTool.argsCodec = new MapProvidedMapCodec<>(allArgs, ToolArg::getCodec, HashMap::new); }) .build(); private static DefaultAssetMap ASSET_MAP; @@ -58,7 +110,6 @@ public class BuilderTool implements JsonAssetWithMap args = Collections.emptyMap(); protected Map defaultToolArgs; private MapProvidedMapCodec argsCodec; @@ -72,6 +123,17 @@ public class BuilderTool implements JsonAssetWithMap setMandatoryToolArgs() { + HashSet argKeys = new HashSet<>(); + argKeys.add("builtin_Mask"); + argKeys.add("builtin_MaskAbove"); + argKeys.add("builtin_MaskNot"); + argKeys.add("builtin_MaskBelow"); + argKeys.add("builtin_MaskAdjacent"); + argKeys.add("builtin_MaskNeighbor"); + return argKeys; + } + @Nullable public static BuilderTool getActiveBuilderTool(@Nonnull Player player) { ItemStack activeItemStack = player.getInventory().getItemInHand(); @@ -79,8 +141,8 @@ public class BuilderTool implements JsonAssetWithMap getArgs() { return this.args; } @@ -110,7 +168,7 @@ public class BuilderTool implements JsonAssetWithMap getDefaultToolArgs(@Nonnull ItemStack itemStack) { - BuilderTool builderToolAsset = itemStack.getItem().getBuilderToolData().getTools()[0]; + BuilderTool builderToolAsset = itemStack.getItem().getBuilderTool(); Map map = new Object2ObjectOpenHashMap<>(builderToolAsset.args.size()); for (Entry entry : builderToolAsset.args.entrySet()) { @@ -120,27 +178,19 @@ public class BuilderTool implements JsonAssetWithMap toolArgs = null; if (!this.args.isEmpty()) { - Map toolData = itemStack.getFromMetadataOrNull("ToolData", this.argsCodec); - toolArgs = toolData == null ? this.getDefaultToolArgs(itemStack) : toolData; + try { + Map toolData = itemStack.getFromMetadataOrNull("ToolData", this.argsCodec); + toolArgs = toolData == null ? this.getDefaultToolArgs(itemStack) : toolData; + } catch (Exception var4) { + toolArgs = this.getDefaultToolArgs(itemStack); + } } - BrushData.Values brushArgs = null; - if (this.isBrush) { - BrushData.Values brushData = itemStack.getFromMetadataOrNull(BRUSH_DATA_KEY_CODEC); - brushArgs = brushData == null ? this.getDefaultBrushArgs(itemStack) : brushData; - } - - return new BuilderTool.ArgData(toolArgs, brushArgs); + return new BuilderTool.ArgData(toolArgs); } @Nonnull @@ -150,19 +200,13 @@ public class BuilderTool implements JsonAssetWithMap map = new Object2ObjectOpenHashMap<>(this.args.size()); + Map map = new LinkedHashMap<>(this.args.size()); for (Entry entry : this.args.entrySet()) { map.put(entry.getKey(), entry.getValue().toPacket()); } - packet.args = map; + packet.args = map.values().toArray(new BuilderToolArg[map.values().size()]); this.cachedPacket = new SoftReference<>(packet); return packet; } @@ -211,10 +262,10 @@ public class BuilderTool implements JsonAssetWithMap tool, @Nullable BrushData.Values brush) { + public record ArgData(@Nullable Map tool) { @Nonnull public static BuilderTool.ArgData setToolArg(@Nonnull BuilderTool.ArgData argData, String argId, Object value) { Map tool = argData.tool(); @@ -223,7 +274,7 @@ public class BuilderTool implements JsonAssetWithMap newToolArgs = new Object2ObjectOpenHashMap<>(tool); newToolArgs.put(argId, value); - return new BuilderTool.ArgData(newToolArgs, argData.brush()); + return new BuilderTool.ArgData(newToolArgs); } } @@ -235,14 +286,14 @@ public class BuilderTool implements JsonAssetWithMap newToolArgs = new Object2ObjectOpenHashMap<>(tool); newToolArgs.remove(argId); - return new BuilderTool.ArgData(newToolArgs, argData.brush()); + return new BuilderTool.ArgData(newToolArgs); } } @Nonnull @Override public String toString() { - return "ArgData{tool=" + this.tool + ", brush=" + this.brush + "}"; + return "ArgData{tool=" + this.tool + "}"; } } } diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BuilderToolData.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BuilderToolData.java deleted file mode 100644 index b10aefa4..00000000 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/BuilderToolData.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.hypixel.hytale.server.core.asset.type.buildertool.config; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.codecs.array.ArrayCodec; -import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.protocol.ItemBuilderToolData; -import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolState; -import com.hypixel.hytale.server.core.io.NetworkSerializable; -import java.util.Arrays; -import javax.annotation.Nonnull; - -public class BuilderToolData implements NetworkSerializable { - public static final BuilderToolData DEFAULT = new BuilderToolData(); - public static final BuilderCodec CODEC = BuilderCodec.builder(BuilderToolData.class, BuilderToolData::new) - .addField( - new KeyedCodec<>("UI", new ArrayCodec<>(Codec.STRING, String[]::new)), - (builderToolData, o) -> builderToolData.ui = o, - builderToolData -> builderToolData.ui - ) - .append( - new KeyedCodec<>("Tools", new ArrayCodec<>(BuilderTool.CODEC, BuilderTool[]::new)), - (builderToolData, o) -> builderToolData.tools = o, - builderToolData -> builderToolData.tools - ) - .addValidator(Validators.nonEmptyArray()) - .add() - .build(); - protected String[] ui; - protected BuilderTool[] tools; - - public BuilderToolData() { - } - - public BuilderToolData(String[] ui, BuilderTool[] tools) { - this.ui = ui; - this.tools = tools; - } - - public String[] getUi() { - return this.ui; - } - - public BuilderTool[] getTools() { - return this.tools; - } - - @Nonnull - public ItemBuilderToolData toPacket() { - ItemBuilderToolData packet = new ItemBuilderToolData(); - packet.ui = this.ui; - packet.tools = new BuilderToolState[this.tools.length]; - - for (int i = 0; i < this.tools.length; i++) { - packet.tools[i] = this.tools[i].toPacket(); - } - - return packet; - } - - @Nonnull - @Override - public String toString() { - return "BuilderToolData{ui=" + Arrays.toString((Object[])this.ui) + ", tools=" + Arrays.toString((Object[])this.tools) + "}"; - } -} diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BlockArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BlockArg.java index 6664afbf..1c1c3b09 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BlockArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BlockArg.java @@ -12,8 +12,10 @@ import javax.annotation.Nonnull; public class BlockArg extends ToolArg { public static final BlockArg[] EMPTY_ARRAY = new BlockArg[0]; public static final BuilderCodec CODEC = BuilderCodec.builder(BlockArg.class, BlockArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", BlockPattern.CODEC), (blockArg, d) -> blockArg.value = d, blockArg -> blockArg.value) - .addField(new KeyedCodec<>("AllowPattern", Codec.BOOLEAN), (blockArg, d) -> blockArg.allowPattern = d, blockArg -> blockArg.allowPattern) + .append(new KeyedCodec<>("Default", BlockPattern.CODEC), (blockArg, d) -> blockArg.value = d, blockArg -> blockArg.value) + .add() + .append(new KeyedCodec<>("AllowPattern", Codec.BOOLEAN), (blockArg, d) -> blockArg.allowPattern = d, blockArg -> blockArg.allowPattern) + .add() .build(); protected boolean allowPattern; diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BoolArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BoolArg.java index 6fb01d1d..fbcd0084 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BoolArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BoolArg.java @@ -10,7 +10,8 @@ import javax.annotation.Nonnull; public class BoolArg extends ToolArg { public static final BuilderCodec CODEC = BuilderCodec.builder(BoolArg.class, BoolArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", Codec.BOOLEAN), (boolArg, d) -> boolArg.value = d, boolArg -> boolArg.value) + .append(new KeyedCodec<>("Default", Codec.BOOLEAN), (boolArg, d) -> boolArg.value = d, boolArg -> boolArg.value) + .add() .build(); public BoolArg() { diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BrushOriginArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BrushOriginArg.java index 195d3539..11535f29 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BrushOriginArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BrushOriginArg.java @@ -13,7 +13,8 @@ import javax.annotation.Nonnull; public class BrushOriginArg extends ToolArg { public static final EnumCodec BRUSH_ORIGIN_CODEC = new EnumCodec<>(BrushOrigin.class); public static final BuilderCodec CODEC = BuilderCodec.builder(BrushOriginArg.class, BrushOriginArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", BRUSH_ORIGIN_CODEC), (originArg, o) -> originArg.value = o, originArg -> originArg.value) + .append(new KeyedCodec<>("Default", BRUSH_ORIGIN_CODEC), (originArg, o) -> originArg.value = o, originArg -> originArg.value) + .add() .build(); public BrushOriginArg() { diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BrushShapeArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BrushShapeArg.java index 9e090e2a..fca1f629 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BrushShapeArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/BrushShapeArg.java @@ -13,7 +13,8 @@ import javax.annotation.Nonnull; public class BrushShapeArg extends ToolArg { public static final EnumCodec BRUSH_SHAPE_CODEC = new EnumCodec<>(BrushShape.class); public static final BuilderCodec CODEC = BuilderCodec.builder(BrushShapeArg.class, BrushShapeArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", BRUSH_SHAPE_CODEC), (shapeArg, o) -> shapeArg.value = o, shapeArg -> shapeArg.value) + .append(new KeyedCodec<>("Default", BRUSH_SHAPE_CODEC), (shapeArg, o) -> shapeArg.value = o, shapeArg -> shapeArg.value) + .add() .build(); public BrushShapeArg() { diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/FloatArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/FloatArg.java index 948ea051..a32fd0c0 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/FloatArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/FloatArg.java @@ -11,9 +11,12 @@ import javax.annotation.Nonnull; public class FloatArg extends ToolArg { public static final BuilderCodec CODEC = BuilderCodec.builder(FloatArg.class, FloatArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", Codec.DOUBLE), (floatArg, o) -> floatArg.value = o.floatValue(), floatArg -> (double)floatArg.value.floatValue()) - .addField(new KeyedCodec<>("Min", Codec.DOUBLE), (floatArg, o) -> floatArg.min = o.floatValue(), floatArg -> (double)floatArg.min) - .addField(new KeyedCodec<>("Max", Codec.DOUBLE), (floatArg, o) -> floatArg.max = o.floatValue(), floatArg -> (double)floatArg.max) + .append(new KeyedCodec<>("Default", Codec.DOUBLE), (floatArg, o) -> floatArg.value = o.floatValue(), floatArg -> (double)floatArg.value.floatValue()) + .add() + .append(new KeyedCodec<>("Min", Codec.DOUBLE), (floatArg, o) -> floatArg.min = o.floatValue(), floatArg -> (double)floatArg.min) + .add() + .append(new KeyedCodec<>("Max", Codec.DOUBLE), (floatArg, o) -> floatArg.max = o.floatValue(), floatArg -> (double)floatArg.max) + .add() .build(); protected float min; protected float max; diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/IntArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/IntArg.java index 95873cc3..eaf24ecc 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/IntArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/IntArg.java @@ -11,9 +11,12 @@ import javax.annotation.Nonnull; public class IntArg extends ToolArg { public static final BuilderCodec CODEC = BuilderCodec.builder(IntArg.class, IntArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", Codec.INTEGER), (intArg, d) -> intArg.value = d, intArg -> intArg.value) - .addField(new KeyedCodec<>("Min", Codec.INTEGER), (intArg, d) -> intArg.min = d, intArg -> intArg.min) - .addField(new KeyedCodec<>("Max", Codec.INTEGER), (intArg, d) -> intArg.max = d, intArg -> intArg.max) + .append(new KeyedCodec<>("Default", Codec.INTEGER), (intArg, d) -> intArg.value = d, intArg -> intArg.value) + .add() + .append(new KeyedCodec<>("Min", Codec.INTEGER), (intArg, d) -> intArg.min = d, intArg -> intArg.min) + .add() + .append(new KeyedCodec<>("Max", Codec.INTEGER), (intArg, d) -> intArg.max = d, intArg -> intArg.max) + .add() .build(); protected int min; protected int max; diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/MaskArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/MaskArg.java index c6311ea5..01c969a2 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/MaskArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/MaskArg.java @@ -12,7 +12,8 @@ import javax.annotation.Nonnull; public class MaskArg extends ToolArg { public static final MaskArg EMPTY = new MaskArg(BlockMask.EMPTY, false); public static final BuilderCodec CODEC = BuilderCodec.builder(MaskArg.class, MaskArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", BlockMask.CODEC), (maskArg, d) -> maskArg.value = d, maskArg -> maskArg.value) + .append(new KeyedCodec<>("Default", BlockMask.CODEC), (maskArg, d) -> maskArg.value = d, maskArg -> maskArg.value) + .add() .build(); public MaskArg() { diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/OptionArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/OptionArg.java index ee01ce4f..4b63f93b 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/OptionArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/OptionArg.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.server.core.asset.type.buildertool.config.args; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolArg; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolArgType; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolOptionArg; @@ -11,8 +12,11 @@ import javax.annotation.Nonnull; public class OptionArg extends ToolArg { public static final BuilderCodec CODEC = BuilderCodec.builder(OptionArg.class, OptionArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", Codec.STRING), (optionArg, o) -> optionArg.value = o, optionArg -> optionArg.value) - .addField(new KeyedCodec<>("Options", Codec.STRING_ARRAY), (optionArg, o) -> optionArg.options = o, optionArg -> optionArg.options) + .append(new KeyedCodec<>("Default", Codec.STRING), (optionArg, o) -> optionArg.value = o, optionArg -> optionArg.value) + .add() + .append(new KeyedCodec<>("Options", Codec.STRING_ARRAY, true), (optionArg, o) -> optionArg.options = o, optionArg -> optionArg.options) + .addValidator(Validators.nonNull()) + .add() .build(); protected String[] options; diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/StringArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/StringArg.java index 5f99e387..4111f435 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/StringArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/StringArg.java @@ -11,7 +11,8 @@ import javax.annotation.Nonnull; public class StringArg extends ToolArg { public static final StringArg[] EMPTY_ARRAY = new StringArg[0]; public static final BuilderCodec CODEC = BuilderCodec.builder(StringArg.class, StringArg::new, ToolArg.DEFAULT_CODEC) - .addField(new KeyedCodec<>("Default", Codec.STRING), (stringArg, d) -> stringArg.value = d, stringArg -> stringArg.value) + .append(new KeyedCodec<>("Default", Codec.STRING), (stringArg, d) -> stringArg.value = d, stringArg -> stringArg.value) + .add() .build(); public StringArg() { diff --git a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/ToolArg.java b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/ToolArg.java index 49d70078..bd2c3583 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/ToolArg.java +++ b/src/com/hypixel/hytale/server/core/asset/type/buildertool/config/args/ToolArg.java @@ -4,6 +4,7 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.lookup.CodecMapCodec; +import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolArg; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; @@ -11,11 +12,20 @@ import javax.annotation.Nonnull; public abstract class ToolArg implements NetworkSerializable { public static final CodecMapCodec CODEC = new CodecMapCodec<>("Type"); public static final BuilderCodec DEFAULT_CODEC = BuilderCodec.abstractBuilder(ToolArg.class) - .addField(new KeyedCodec<>("Required", Codec.BOOLEAN), (shapeArg, o) -> shapeArg.required = o, shapeArg -> shapeArg.required) + .append(new KeyedCodec<>("Required", Codec.BOOLEAN), (shapeArg, o) -> shapeArg.required = o, shapeArg -> shapeArg.required) + .add() + .append(new KeyedCodec<>("Id", CodecMapCodec.STRING), (arg, o) -> arg.id = o, arg -> arg.id) + .addValidator(Validators.nonNull()) + .add() .build(); + protected String id; protected boolean required = true; protected T value; + public String getId() { + return this.id; + } + public T getValue() { return this.value; } @@ -35,6 +45,7 @@ public abstract class ToolArg implements NetworkSerializable public BuilderToolArg toPacket() { BuilderToolArg packet = new BuilderToolArg(); packet.required = this.required; + packet.id = this.id; this.setupPacket(packet); return packet; } @@ -42,6 +53,6 @@ public abstract class ToolArg implements NetworkSerializable @Nonnull @Override public String toString() { - return "ToolArg{required=" + this.required + ", value=" + this.value + "}"; + return "ToolArg{required=" + this.required + "id=" + this.id + ", value=" + this.value + "}"; } } diff --git a/src/com/hypixel/hytale/server/core/asset/type/entityeffect/config/EntityEffect.java b/src/com/hypixel/hytale/server/core/asset/type/entityeffect/config/EntityEffect.java index 6442253d..b9e33aeb 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/entityeffect/config/EntityEffect.java +++ b/src/com/hypixel/hytale/server/core/asset/type/entityeffect/config/EntityEffect.java @@ -21,6 +21,7 @@ import com.hypixel.hytale.server.core.asset.type.item.config.ItemArmor; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.io.NetworkSerializable; +import com.hypixel.hytale.server.core.modules.entity.condition.Condition; import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; @@ -234,6 +235,14 @@ public class EntityEffect ) .documentation("Localization key used on the death screen when this EntityEffect kills a player.") .add() + .appendInherited( + new KeyedCodec<>("ApplyConditions", new ArrayCodec<>(Condition.CODEC, Condition[]::new)), + (entityEffect, conditions) -> entityEffect.applyConditions = conditions, + entityEffect -> entityEffect.applyConditions, + (entityEffect, parent) -> entityEffect.applyConditions = parent.applyConditions + ) + .documentation("Conditions that must ALL be true for this effect to remain active. If any condition fails, the effect is removed.") + .add() .afterDecode(entityEffect -> { entityEffect.entityStats = EntityStatsModule.resolveEntityStats(entityEffect.unknownEntityStats); entityEffect.statModifiers = EntityStatsModule.resolveEntityStats(entityEffect.rawStatModifiers); @@ -297,6 +306,8 @@ public class EntityEffect protected boolean invulnerable = false; protected String deathMessageKey; @Nullable + protected Condition[] applyConditions; + @Nullable protected Map rawStatModifiers; @Nullable protected Int2ObjectMap statModifiers; @@ -431,6 +442,11 @@ public class EntityEffect return this.deathMessageKey; } + @Nullable + public Condition[] getApplyConditions() { + return this.applyConditions; + } + @Nonnull public com.hypixel.hytale.protocol.EntityEffect toPacket() { com.hypixel.hytale.protocol.EntityEffect cached = this.cachedPacket == null ? null : this.cachedPacket.get(); diff --git a/src/com/hypixel/hytale/server/core/asset/type/environment/config/Environment.java b/src/com/hypixel/hytale/server/core/asset/type/environment/config/Environment.java index 4a6a32a6..6c54e34e 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/environment/config/Environment.java +++ b/src/com/hypixel/hytale/server/core/asset/type/environment/config/Environment.java @@ -60,6 +60,7 @@ public class Environment implements JsonAssetWithMap> weatherForecasts; protected double spawnDensity; protected boolean blockModificationAllowed = true; + private String weatherSeedKey; private SoftReference cachedPacket; public static AssetStore> getAssetStore() { @@ -78,6 +79,7 @@ public class Environment implements JsonAssetWithMap fluidParticles, Int2ObjectMap> weatherForecasts, double spawnDensity ) { this.id = id; + this.weatherSeedKey = id; this.waterTint = waterTint; this.fluidParticles = fluidParticles; this.weatherForecasts = weatherForecasts; @@ -111,6 +113,10 @@ public class Environment implements JsonAssetWithMap("WeatherForecastSeed", Codec.STRING, true), + (environment, seed) -> environment.weatherSeedKey = seed, + environment -> environment.weatherSeedKey, + (environment, parent) -> environment.weatherSeedKey = parent.getWeatherSeedKey() + ) + .add() .build(); VALIDATOR_CACHE = new ValidatorCache<>(new AssetKeyValidator<>(Environment::getAssetStore)); UNKNOWN = getUnknownFor("Unknown"); diff --git a/src/com/hypixel/hytale/server/core/asset/type/fluid/DefaultFluidTicker.java b/src/com/hypixel/hytale/server/core/asset/type/fluid/DefaultFluidTicker.java index 299155bf..fa8fd945 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/fluid/DefaultFluidTicker.java +++ b/src/com/hypixel/hytale/server/core/asset/type/fluid/DefaultFluidTicker.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.map.MapCodec; import com.hypixel.hytale.common.util.MapUtil; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -25,6 +24,7 @@ import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; public class DefaultFluidTicker extends FluidTicker { public static final BuilderCodec CODEC = BuilderCodec.builder(DefaultFluidTicker.class, DefaultFluidTicker::new, BASE_CODEC) diff --git a/src/com/hypixel/hytale/server/core/asset/type/fluid/FiniteFluidTicker.java b/src/com/hypixel/hytale/server/core/asset/type/fluid/FiniteFluidTicker.java index 1e2e038a..7fe796fe 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/fluid/FiniteFluidTicker.java +++ b/src/com/hypixel/hytale/server/core/asset/type/fluid/FiniteFluidTicker.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.universe.world.World; @@ -17,6 +16,7 @@ import java.util.List; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; public class FiniteFluidTicker extends FluidTicker { @Nonnull @@ -212,8 +212,8 @@ public class FiniteFluidTicker extends FluidTicker { if (!isOffsetConnected(accessor, blockSection, offset, worldX, worldY, worldZ)) { return null; } else { - int x = offset.getX(); - int z = offset.getY(); + int x = offset.x(); + int z = offset.y(); int blockX = worldX + x; int blockZ = worldZ + z; boolean isDifferentSection = !ChunkUtil.isSameChunkSection(worldX, worldY, worldZ, blockX, worldY, blockZ); @@ -332,8 +332,8 @@ public class FiniteFluidTicker extends FluidTicker { private static boolean isOffsetConnected( @Nonnull FluidTicker.Accessor accessor, BlockSection blockSection, @Nonnull Vector2i offset, int worldX, int worldY, int worldZ ) { - int x = offset.getX(); - int z = offset.getY(); + int x = offset.x(); + int z = offset.y(); if (x != 0 && z != 0) { BlockSection section1 = ChunkUtil.isSameChunkSection(worldX, worldY, worldZ, worldX + x, worldY, worldZ) ? blockSection diff --git a/src/com/hypixel/hytale/server/core/asset/type/fluid/FireFluidTicker.java b/src/com/hypixel/hytale/server/core/asset/type/fluid/FireFluidTicker.java index b9f4046a..865cba45 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/fluid/FireFluidTicker.java +++ b/src/com/hypixel/hytale/server/core/asset/type/fluid/FireFluidTicker.java @@ -7,7 +7,6 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.asset.type.tagpattern.config.TagPattern; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -26,6 +26,7 @@ import java.util.List; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class FireFluidTicker extends FluidTicker { public static final BuilderCodec CODEC = BuilderCodec.builder(FireFluidTicker.class, FireFluidTicker::new, BASE_CODEC) @@ -182,10 +183,34 @@ public class FireFluidTicker extends FluidTicker { int blockZ ) { int resultingBlockIndex = config.getResultingBlockIndex(); - if (resultingBlockIndex != Integer.MIN_VALUE) { - int originalRotation = blockSection.getRotationIndex(blockX, blockY, blockZ); - int originalFiller = blockSection.getFiller(blockX, blockY, blockZ); - blockSection.set(blockX, blockY, blockZ, resultingBlockIndex, originalRotation, originalFiller); + String resultingBlockState = config.getResultingState(); + if (resultingBlockIndex != Integer.MIN_VALUE || resultingBlockState != null) { + world.execute(() -> { + WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockX, blockZ)); + if (chunk != null) { + int originalRotation = blockSection.getRotationIndex(blockX, blockY, blockZ); + int originalFiller = blockSection.getFiller(blockX, blockY, blockZ); + if (resultingBlockIndex != Integer.MIN_VALUE && (resultingBlockState == null || resultingBlockIndex != 0)) { + BlockType resultingBlockType = BlockType.getAssetMap().getAsset(resultingBlockIndex); + chunk.setBlock(blockX, blockY, blockZ, resultingBlockIndex, resultingBlockType, originalRotation, originalFiller, 0); + } + + if (resultingBlockState != null) { + BlockType existingBlock = chunk.getBlockType(blockX, blockY, blockZ); + if (existingBlock == null) { + return; + } + + BlockType newBlockType = existingBlock.getBlockForState(resultingBlockState); + if (newBlockType == null) { + return; + } + + int newBlockIndex = BlockType.getAssetMap().getIndex(newBlockType.getId()); + chunk.setBlock(blockX, blockY, blockZ, newBlockIndex, newBlockType, originalRotation, originalFiller, 0); + } + } + }); setTickingSurrounding(accessor, blockSection, blockX, blockY, blockZ); } @@ -262,6 +287,14 @@ public class FireFluidTicker extends FluidTicker { ) .documentation("The block to place after burning, if any") .add() + .appendInherited( + new KeyedCodec<>("ResultingState", Codec.STRING), + (o, v) -> o.resultingState = v, + o -> o.resultingState, + (o, p) -> o.resultingState = p.resultingState + ) + .documentation("The block state to attempt to change to after burning, if any") + .add() .appendInherited( new KeyedCodec<>("SoundEvent", Codec.STRING), (o, v) -> o.soundEvent = v, o -> o.soundEvent, (o, p) -> o.soundEvent = p.soundEvent ) @@ -275,6 +308,8 @@ public class FireFluidTicker extends FluidTicker { private byte burnLevel = 1; private float burnChance = 0.1F; private String resultingBlock = "Empty"; + @Nullable + private String resultingState; private int resultingBlockIndex = Integer.MIN_VALUE; private String soundEvent; private int soundEventIndex = Integer.MIN_VALUE; @@ -308,6 +343,11 @@ public class FireFluidTicker extends FluidTicker { return this.resultingBlockIndex; } + @Nullable + public String getResultingState() { + return this.resultingState; + } + public int getSoundEventIndex() { if (this.soundEventIndex == Integer.MIN_VALUE && this.soundEvent != null) { this.soundEventIndex = SoundEvent.getAssetMap().getIndex(this.soundEvent); diff --git a/src/com/hypixel/hytale/server/core/asset/type/fluid/Fluid.java b/src/com/hypixel/hytale/server/core/asset/type/fluid/Fluid.java index 70dc07ad..36fb3442 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/fluid/Fluid.java +++ b/src/com/hypixel/hytale/server/core/asset/type/fluid/Fluid.java @@ -35,7 +35,7 @@ import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.io.NetworkSerializable; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.InteractionTypeUtils; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; -import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.ISectionPalette; +import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.AbstractSectionPalette; import com.hypixel.hytale.server.core.util.io.ByteBufUtil; import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.IntSet; @@ -191,7 +191,7 @@ public class Fluid implements JsonAssetWithMap { + public static final AbstractSectionPalette.KeySerializer KEY_SERIALIZER = (buf, id) -> { String key = getAssetMap().getAssetOrDefault(id, Fluid.UNKNOWN).getId(); ByteBufUtil.writeUTF(buf, key); }; diff --git a/src/com/hypixel/hytale/server/core/asset/type/fluid/FluidTicker.java b/src/com/hypixel/hytale/server/core/asset/type/fluid/FluidTicker.java index 4f7280b5..2193e98b 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/fluid/FluidTicker.java +++ b/src/com/hypixel/hytale/server/core/asset/type/fluid/FluidTicker.java @@ -11,7 +11,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.DrawType; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; @@ -26,6 +25,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; public abstract class FluidTicker { public static final BuilderCodec BASE_CODEC = BuilderCodec.abstractBuilder(FluidTicker.class) diff --git a/src/com/hypixel/hytale/server/core/asset/type/gameplay/GameplayConfig.java b/src/com/hypixel/hytale/server/core/asset/type/gameplay/GameplayConfig.java index 25cb8b7b..ebc18b57 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/gameplay/GameplayConfig.java +++ b/src/com/hypixel/hytale/server/core/asset/type/gameplay/GameplayConfig.java @@ -26,123 +26,128 @@ public class GameplayConfig implements JsonAssetWithMap gameplayConfig.id = s, + (config, s) -> config.id = s, GameplayConfig::getId, (asset, data) -> asset.data = data, asset -> asset.data ) .appendInherited( new KeyedCodec<>("Gathering", GatheringConfig.CODEC), - (gameplayConfig, o) -> gameplayConfig.gatheringConfig = o, - gameplayConfig -> gameplayConfig.gatheringConfig, - (gameplayConfig, parent) -> gameplayConfig.gatheringConfig = parent.gatheringConfig + (config, o) -> config.gatheringConfig = o, + config -> config.gatheringConfig, + (config, parent) -> config.gatheringConfig = parent.gatheringConfig ) .add() .appendInherited( new KeyedCodec<>("World", WorldConfig.CODEC), - (gameplayConfig, o) -> gameplayConfig.worldConfig = o, - gameplayConfig -> gameplayConfig.worldConfig, - (gameplayConfig, parent) -> gameplayConfig.worldConfig = parent.worldConfig + (config, o) -> config.worldConfig = o, + config -> config.worldConfig, + (config, parent) -> config.worldConfig = parent.worldConfig ) .add() .appendInherited( new KeyedCodec<>("WorldMap", WorldMapConfig.CODEC), - (gameplayConfig, o) -> gameplayConfig.worldMapConfig = o, - gameplayConfig -> gameplayConfig.worldMapConfig, - (gameplayConfig, parent) -> gameplayConfig.worldMapConfig = parent.worldMapConfig + (config, o) -> config.worldMapConfig = o, + config -> config.worldMapConfig, + (config, parent) -> config.worldMapConfig = parent.worldMapConfig ) .add() .appendInherited( new KeyedCodec<>("Death", DeathConfig.CODEC), - (gameplayConfig, o) -> gameplayConfig.deathConfig = o, - gameplayConfig -> gameplayConfig.deathConfig, - (gameplayConfig, parent) -> gameplayConfig.deathConfig = parent.deathConfig + (config, o) -> config.deathConfig = o, + config -> config.deathConfig, + (config, parent) -> config.deathConfig = parent.deathConfig ) .add() .appendInherited( new KeyedCodec<>("Respawn", RespawnConfig.CODEC), - (gameplayConfig, o) -> gameplayConfig.respawnConfig = o, - gameplayConfig -> gameplayConfig.respawnConfig, - (gameplayConfig, parent) -> gameplayConfig.respawnConfig = parent.respawnConfig + (config, o) -> config.respawnConfig = o, + config -> config.respawnConfig, + (config, parent) -> config.respawnConfig = parent.respawnConfig ) .add() .appendInherited( new KeyedCodec<>("ShowItemPickupNotifications", Codec.BOOLEAN), - (gameplayConfig, showItemPickupNotifications) -> gameplayConfig.showItemPickupNotifications = showItemPickupNotifications, - gameplayConfig -> gameplayConfig.showItemPickupNotifications, - (gameplayConfig, parent) -> gameplayConfig.showItemPickupNotifications = parent.showItemPickupNotifications + (config, showItemPickupNotifications) -> config.showItemPickupNotifications = showItemPickupNotifications, + config -> config.showItemPickupNotifications, + (config, parent) -> config.showItemPickupNotifications = parent.showItemPickupNotifications ) .add() .appendInherited( new KeyedCodec<>("ItemDurability", ItemDurabilityConfig.CODEC), - (gameplayConfig, o) -> gameplayConfig.itemDurabilityConfig = o, - gameplayConfig -> gameplayConfig.itemDurabilityConfig, - (gameplayConfig, parent) -> gameplayConfig.itemDurabilityConfig = parent.itemDurabilityConfig + (config, o) -> config.itemDurabilityConfig = o, + config -> config.itemDurabilityConfig, + (config, parent) -> config.itemDurabilityConfig = parent.itemDurabilityConfig ) .add() .appendInherited( new KeyedCodec<>("ItemEntity", ItemEntityConfig.CODEC), - (gameplayConfig, itemEntityConfig) -> gameplayConfig.itemEntityConfig = itemEntityConfig, - gameplayConfig -> gameplayConfig.itemEntityConfig, - (gameplayConfig, parent) -> gameplayConfig.itemEntityConfig = parent.itemEntityConfig + (config, itemEntityConfig) -> config.itemEntityConfig = itemEntityConfig, + config -> config.itemEntityConfig, + (config, parent) -> config.itemEntityConfig = parent.itemEntityConfig ) .add() .appendInherited( new KeyedCodec<>("Combat", CombatConfig.CODEC), - (gameplayConfig, combatConfig) -> gameplayConfig.combatConfig = combatConfig, - gameplayConfig -> gameplayConfig.combatConfig, - (gameplayConfig, parent) -> gameplayConfig.combatConfig = parent.combatConfig + (config, combatConfig) -> config.combatConfig = combatConfig, + config -> config.combatConfig, + (config, parent) -> config.combatConfig = parent.combatConfig ) .add() - .>appendInherited(new KeyedCodec<>("Plugin", PLUGIN_CODEC), (o, i) -> { - if (o.pluginConfig.isEmpty()) { - o.pluginConfig = i; + .>appendInherited(new KeyedCodec<>("Plugin", PLUGIN_CODEC), (config, i) -> { + if (config.pluginConfig.isEmpty()) { + config.pluginConfig = i; } else { - MapKeyMapCodec.TypeMap temp = o.pluginConfig; - o.pluginConfig = new MapKeyMapCodec.TypeMap<>(PLUGIN_CODEC); - o.pluginConfig.putAll(temp); - o.pluginConfig.putAll(i); + MapKeyMapCodec.TypeMap temp = config.pluginConfig; + config.pluginConfig = new MapKeyMapCodec.TypeMap<>(PLUGIN_CODEC); + config.pluginConfig.putAll(temp); + config.pluginConfig.putAll(i); } - }, o -> o.pluginConfig, (o, p) -> o.pluginConfig = p.pluginConfig) + }, config -> config.pluginConfig, (config, p) -> config.pluginConfig = p.pluginConfig) .addValidator(Validators.nonNull()) .add() .appendInherited( new KeyedCodec<>("Player", PlayerConfig.CODEC), - (gameplayConfig, playerConfig) -> gameplayConfig.playerConfig = playerConfig, - gameplayConfig -> gameplayConfig.playerConfig, - (gameplayConfig, parent) -> gameplayConfig.playerConfig = parent.playerConfig + (config, playerConfig) -> config.playerConfig = playerConfig, + config -> config.playerConfig, + (config, parent) -> config.playerConfig = parent.playerConfig ) .add() .appendInherited( new KeyedCodec<>("CameraEffects", CameraEffectsConfig.CODEC), - (o, i) -> o.cameraEffectsConfig = i, - o -> o.cameraEffectsConfig, - (o, p) -> o.cameraEffectsConfig = p.cameraEffectsConfig + (config, i) -> config.cameraEffectsConfig = i, + config -> config.cameraEffectsConfig, + (config, p) -> config.cameraEffectsConfig = p.cameraEffectsConfig ) .addValidator(Validators.nonNull()) .add() .appendInherited( new KeyedCodec<>("CreativePlaySoundSet", SoundSet.CHILD_ASSET_CODEC), - (o, i) -> o.creativePlaySoundSet = i, - o -> o.creativePlaySoundSet, - (o, p) -> o.creativePlaySoundSet = p.creativePlaySoundSet + (config, i) -> config.creativePlaySoundSet = i, + config -> config.creativePlaySoundSet, + (config, p) -> config.creativePlaySoundSet = p.creativePlaySoundSet ) .addValidator(SoundSet.VALIDATOR_CACHE.getValidator()) .add() .appendInherited( new KeyedCodec<>("Crafting", CraftingConfig.CODEC), - (gameplayConfig, o) -> gameplayConfig.craftingConfig = o, - gameplayConfig -> gameplayConfig.craftingConfig, - (gameplayConfig, parent) -> gameplayConfig.craftingConfig = parent.craftingConfig + (config, o) -> config.craftingConfig = o, + config -> config.craftingConfig, + (config, parent) -> config.craftingConfig = parent.craftingConfig ) .add() - .appendInherited(new KeyedCodec<>("Spawn", SpawnConfig.CODEC), (o, v) -> o.spawnConfig = v, o -> o.spawnConfig, (o, p) -> o.spawnConfig = p.spawnConfig) + .appendInherited( + new KeyedCodec<>("Spawn", SpawnConfig.CODEC), + (config, v) -> config.spawnConfig = v, + config -> config.spawnConfig, + (config, p) -> config.spawnConfig = p.spawnConfig + ) .add() .appendInherited( new KeyedCodec<>("MaxEnvironmentalNPCSpawns", Codec.INTEGER), - (o, v) -> o.maxEnvironmentalNPCSpawns = v, - o -> o.maxEnvironmentalNPCSpawns, - (o, p) -> o.maxEnvironmentalNPCSpawns = p.maxEnvironmentalNPCSpawns + (config, v) -> config.maxEnvironmentalNPCSpawns = v, + config -> config.maxEnvironmentalNPCSpawns, + (config, p) -> config.maxEnvironmentalNPCSpawns = p.maxEnvironmentalNPCSpawns ) .documentation("The absolute maximum number of environmental NPC spawns. < 0 for infinite.") .add() @@ -151,8 +156,8 @@ public class GameplayConfig implements JsonAssetWithMap> ASSET_STORE; @Nonnull public static final ValidatorCache VALIDATOR_CACHE = new ValidatorCache<>(new AssetKeyValidator<>(GameplayConfig::getAssetStore)); - protected AssetExtraInfo.Data data; protected String id; + protected AssetExtraInfo.Data data; protected GatheringConfig gatheringConfig = new GatheringConfig(); protected WorldConfig worldConfig = new WorldConfig(); protected WorldMapConfig worldMapConfig = new WorldMapConfig(); diff --git a/src/com/hypixel/hytale/server/core/asset/type/item/config/AssetIconProperties.java b/src/com/hypixel/hytale/server/core/asset/type/item/config/AssetIconProperties.java index 161275c8..c4732c98 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/item/config/AssetIconProperties.java +++ b/src/com/hypixel/hytale/server/core/asset/type/item/config/AssetIconProperties.java @@ -3,25 +3,27 @@ package com.hypixel.hytale.server.core.asset.type.item.config; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.protocol.Vector2f; -import com.hypixel.hytale.protocol.Vector3f; +import com.hypixel.hytale.math.vector.Vector2dUtil; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector2f; +import org.joml.Vector3d; +import org.joml.Vector3f; public class AssetIconProperties implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(AssetIconProperties.class, AssetIconProperties::new) .addField(new KeyedCodec<>("Scale", Codec.DOUBLE), (props, scale) -> props.scale = scale.floatValue(), props -> (double)props.scale) .addField( - new KeyedCodec<>("Translation", Vector2d.AS_ARRAY_CODEC), - (props, translation) -> props.translation = translation == null ? null : new Vector2f((float)translation.getX(), (float)translation.getY()), + new KeyedCodec<>("Translation", Vector2dUtil.AS_ARRAY_CODEC), + (props, translation) -> props.translation = translation == null ? null : new Vector2f((float)translation.x(), (float)translation.y()), props -> props.translation == null ? null : new Vector2d(props.translation.x, props.translation.y) ) .addField( - new KeyedCodec<>("Rotation", Vector3d.AS_ARRAY_CODEC), - (props, rot) -> props.rotation = rot == null ? null : new Vector3f((float)rot.getX(), (float)rot.getY(), (float)rot.getZ()), + new KeyedCodec<>("Rotation", Vector3dUtil.AS_ARRAY_CODEC), + (props, rot) -> props.rotation = rot == null ? null : new Vector3f((float)rot.x(), (float)rot.y(), (float)rot.z()), props -> props.rotation == null ? null : new Vector3d(props.rotation.x, props.rotation.y, props.rotation.z) ) .build(); diff --git a/src/com/hypixel/hytale/server/core/asset/type/item/config/CraftingRecipe.java b/src/com/hypixel/hytale/server/core/asset/type/item/config/CraftingRecipe.java index 170c42e2..22ae70aa 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/item/config/CraftingRecipe.java +++ b/src/com/hypixel/hytale/server/core/asset/type/item/config/CraftingRecipe.java @@ -18,6 +18,7 @@ import com.hypixel.hytale.protocol.BenchType; import com.hypixel.hytale.server.core.inventory.MaterialQuantity; import java.util.Arrays; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class CraftingRecipe implements JsonAssetWithMap> { public static final String FIELDCRAFT_REQUIREMENT = "Fieldcraft"; @@ -36,6 +37,7 @@ public class CraftingRecipe implements JsonAssetWithMap craftingRecipe.input ) .addValidator(Validators.nonNull()) + .addValidator(Validators.nonEmptyArray()) .add() .append( new KeyedCodec<>("Output", new ArrayCodec<>(MaterialQuantity.CODEC, MaterialQuantity[]::new)), @@ -134,12 +136,13 @@ public class CraftingRecipe implements JsonAssetWithMap> ASSET_STORE; private AssetExtraInfo.Data data; protected String id; protected MaterialQuantity[] input; protected MaterialQuantity[] outputs = EMPTY_OUTPUT; + @Nullable protected MaterialQuantity primaryOutput; protected int primaryOutputQuantity = 1; protected BenchRequirement[] benchRequirement; @@ -161,7 +164,7 @@ public class CraftingRecipe implements JsonAssetWithMap 0) { packet.outputs = ArrayUtil.copyAndMutate(this.outputs, MaterialQuantity::toPacket, com.hypixel.hytale.protocol.MaterialQuantity[]::new); } @@ -250,6 +253,7 @@ public class CraftingRecipe implements JsonAssetWithMap ItemCategory.VALIDATOR_CACHE.getArrayValidator().late()) .documentation("A list of categories this item will be shown in on the creative library menu.") .add() + .appendInherited( + new KeyedCodec<>("SubCategory", Codec.STRING), + (item, s) -> item.subCategory = s, + item -> item.subCategory, + (item, parent) -> item.subCategory = parent.subCategory + ) + .documentation("Optional sub-category for grouping items with a label header in the creative library menu.") + .add() .appendInherited( new KeyedCodec<>("IconProperties", AssetIconProperties.CODEC), (item, s) -> item.iconProperties = s, @@ -309,10 +317,10 @@ public class Item implements JsonAssetWithMap("BuilderTool", BuilderToolData.CODEC), - (item, s) -> item.builderToolData = s, - item -> item.builderToolData, - (item, parent) -> item.builderToolData = parent.builderToolData + new KeyedCodec<>("BuilderTool", BuilderTool.CODEC), + (item, s) -> item.builderTool = s, + item -> item.builderTool, + (item, parent) -> item.builderTool = parent.builderTool ) .add() .appendInherited( @@ -389,6 +397,16 @@ public class Item implements JsonAssetWithMapappendInherited( + new KeyedCodec<>("Repairable", Codec.BOOLEAN), + (item, s) -> item.repairable = s, + item -> item.repairable, + (item, parent) -> item.repairable = parent.repairable + ) + .documentation( + "Whether this item can be repaired with a repair kit. Defaults to true. Set to false for items that use durability as a consumable resource (e.g. watering cans, fertilizer)." + ) + .add() .appendInherited( new KeyedCodec<>("BlockType", new ContainedAssetCodec<>(BlockType.class, BlockType.CODEC, ContainedAssetCodec.Mode.INHERIT_ID_AND_PARENT)), (item, s) -> item.hasBlockType = true, @@ -467,6 +485,7 @@ public class Item implements JsonAssetWithMap item.renderDeployablePreview = parent.renderDeployablePreview ) .add() + .addField(new KeyedCodec<>("HudUI", new ArrayCodec<>(ItemHudUI.CODEC, ItemHudUI[]::new)), (item, s) -> item.hudUI = s, item -> item.hudUI) .appendInherited( new KeyedCodec<>("DropOnDeath", Codec.BOOLEAN), (item, aBoolean) -> item.dropOnDeath = aBoolean, @@ -508,7 +527,7 @@ public class Item implements JsonAssetWithMap cachedPacket; public static AssetStore> getAssetStore() { @@ -593,12 +615,13 @@ public class Item implements JsonAssetWithMap 0) { + packet.hudUI = new com.hypixel.hytale.protocol.ItemHudUI[this.hudUI.length]; + + for (int i = 0; i < this.hudUI.length; i++) { + packet.hudUI[i] = this.hudUI[i].toPacket(); + } + } + this.cachedPacket = new SoftReference<>(packet); return packet; } @@ -920,8 +956,8 @@ public class Item implements JsonAssetWithMap asset.data = data, asset -> asset.data ) - .addField(new KeyedCodec<>("Id", Codec.STRING), (itemCategory, s) -> itemCategory.id = s, itemCategory -> itemCategory.id) - .addField(new KeyedCodec<>("Name", Codec.STRING), (itemCategory, s) -> itemCategory.name = s, itemCategory -> itemCategory.name) + .append(new KeyedCodec<>("Id", Codec.STRING), (itemCategory, s) -> itemCategory.id = s, itemCategory -> itemCategory.id) + .add() + .append(new KeyedCodec<>("Name", Codec.STRING), (itemCategory, s) -> itemCategory.name = s, itemCategory -> itemCategory.name) + .add() .append(new KeyedCodec<>("Icon", Codec.STRING), (itemCategory, s) -> itemCategory.icon = s, itemCategory -> itemCategory.icon) .addValidator(CommonAssetValidator.ICON_ITEM_CATEGORIES) .add() @@ -46,7 +49,14 @@ public class ItemCategory ) .addValidator(Validators.nonNull()) .add() - .addField(new KeyedCodec<>("Order", Codec.INTEGER), (itemCategory, s) -> itemCategory.order = s, itemCategory -> itemCategory.order) + .append(new KeyedCodec<>("Order", Codec.INTEGER), (itemCategory, s) -> itemCategory.order = s, itemCategory -> itemCategory.order) + .add() + .append( + new KeyedCodec<>("SubCategories", new ArrayCodec<>(ItemCategory.SubCategoryDefinition.CODEC, ItemCategory.SubCategoryDefinition[]::new)), + (itemCategory, l) -> itemCategory.subCategories = l, + itemCategory -> itemCategory.subCategories + ) + .add() .afterDecode(itemCategory -> { if (itemCategory.children != null) { Arrays.sort(itemCategory.children, Comparator.comparingInt(value -> value.order)); @@ -63,6 +73,7 @@ public class ItemCategory @Nonnull protected ItemGridInfoDisplayMode infoDisplayMode = ItemGridInfoDisplayMode.Tooltip; protected ItemCategory[] children; + protected ItemCategory.SubCategoryDefinition[] subCategories; private SoftReference cachedPacket; public static AssetStore> getAssetStore() { @@ -77,12 +88,15 @@ public class ItemCategory return (DefaultAssetMap)getAssetStore().getAssetMap(); } - public ItemCategory(String id, String name, String icon, ItemGridInfoDisplayMode infoDisplayMode, ItemCategory[] children) { + public ItemCategory( + String id, String name, String icon, ItemGridInfoDisplayMode infoDisplayMode, ItemCategory[] children, ItemCategory.SubCategoryDefinition[] subCategories + ) { this.id = id; this.name = name; this.icon = icon; this.infoDisplayMode = infoDisplayMode; this.children = children; + this.subCategories = subCategories; } protected ItemCategory() { @@ -104,6 +118,19 @@ public class ItemCategory packet.children = ArrayUtil.copyAndMutate(this.children, ItemCategory::toPacket, com.hypixel.hytale.protocol.ItemCategory[]::new); } + if (this.subCategories != null && this.subCategories.length > 0) { + packet.subCategories = new com.hypixel.hytale.protocol.SubCategoryDefinition[this.subCategories.length]; + + for (int i = 0; i < this.subCategories.length; i++) { + com.hypixel.hytale.protocol.SubCategoryDefinition def = new com.hypixel.hytale.protocol.SubCategoryDefinition(); + def.id = this.subCategories[i].id; + def.name = this.subCategories[i].name; + def.description = this.subCategories[i].description; + def.order = this.subCategories[i].order; + packet.subCategories[i] = def; + } + } + this.cachedPacket = new SoftReference<>(packet); return packet; } @@ -133,6 +160,10 @@ public class ItemCategory return this.children; } + public ItemCategory.SubCategoryDefinition[] getSubCategories() { + return this.subCategories; + } + @Nonnull @Override public String toString() { @@ -148,6 +179,8 @@ public class ItemCategory + this.infoDisplayMode + "', children=" + Arrays.toString((Object[])this.children) + + ", subCategories" + + Arrays.toString((Object[])this.subCategories) + "}"; } @@ -158,4 +191,23 @@ public class ItemCategory itemCategory -> itemCategory.children ); } + + public static class SubCategoryDefinition { + public static final Codec CODEC = BuilderCodec.builder( + ItemCategory.SubCategoryDefinition.class, ItemCategory.SubCategoryDefinition::new + ) + .append(new KeyedCodec<>("Id", Codec.STRING), (s, v) -> s.id = v, s -> s.id) + .add() + .append(new KeyedCodec<>("Name", Codec.STRING), (s, v) -> s.name = v, s -> s.name) + .add() + .append(new KeyedCodec<>("Description", Codec.STRING), (s, v) -> s.description = v, s -> s.description) + .add() + .append(new KeyedCodec<>("Order", Codec.INTEGER), (s, v) -> s.order = v, s -> s.order) + .add() + .build(); + protected String id; + protected String name; + protected String description; + protected int order; + } } diff --git a/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemHudUI.java b/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemHudUI.java new file mode 100644 index 00000000..14905395 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemHudUI.java @@ -0,0 +1,40 @@ +package com.hypixel.hytale.server.core.asset.type.item.config; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.protocol.ItemHudUIType; +import com.hypixel.hytale.server.core.io.NetworkSerializable; +import javax.annotation.Nonnull; + +public class ItemHudUI implements NetworkSerializable { + public static final BuilderCodec CODEC = BuilderCodec.builder(ItemHudUI.class, ItemHudUI::new) + .addField(new KeyedCodec<>("Path", Codec.STRING), (entry, s) -> entry.path = s, entry -> entry.path) + .addField(new KeyedCodec<>("Type", new EnumCodec<>(ItemHudUIType.class)), (entry, t) -> entry.type = t, entry -> entry.type) + .build(); + protected String path; + protected ItemHudUIType type = ItemHudUIType.Hud; + + public String getPath() { + return this.path; + } + + public ItemHudUIType getType() { + return this.type; + } + + @Nonnull + public com.hypixel.hytale.protocol.ItemHudUI toPacket() { + com.hypixel.hytale.protocol.ItemHudUI packet = new com.hypixel.hytale.protocol.ItemHudUI(); + packet.path = this.path; + packet.type = this.type; + return packet; + } + + @Nonnull + @Override + public String toString() { + return "ItemHudUI{path='" + this.path + "', type=" + this.type + "}"; + } +} diff --git a/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemPullbackConfig.java b/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemPullbackConfig.java index 32d55c34..46f5b5da 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemPullbackConfig.java +++ b/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemPullbackConfig.java @@ -2,50 +2,51 @@ package com.hypixel.hytale.server.core.asset.type.item.config; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.ItemPullbackConfiguration; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class ItemPullbackConfig implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(ItemPullbackConfig.class, ItemPullbackConfig::new) .append( - new KeyedCodec<>("LeftOffsetOverride", Vector3d.AS_ARRAY_CODEC), + new KeyedCodec<>("LeftOffsetOverride", Vector3dUtil.AS_ARRAY_CODEC), (pullbackConfig, offOverride) -> pullbackConfig.leftOffsetOverride = offOverride == null ? null - : new Vector3f((float)offOverride.getX(), (float)offOverride.getY(), (float)offOverride.getZ()), + : new Vector3f((float)offOverride.x(), (float)offOverride.y(), (float)offOverride.z()), pullbackConfig -> pullbackConfig.leftOffsetOverride == null ? null : new Vector3d(pullbackConfig.leftOffsetOverride.x, pullbackConfig.leftOffsetOverride.y, pullbackConfig.leftOffsetOverride.z) ) .add() .append( - new KeyedCodec<>("LeftRotationOverride", Vector3d.AS_ARRAY_CODEC), + new KeyedCodec<>("LeftRotationOverride", Vector3dUtil.AS_ARRAY_CODEC), (pullbackConfig, rotOverride) -> pullbackConfig.leftRotationOverride = rotOverride == null ? null - : new Vector3f((float)rotOverride.getX(), (float)rotOverride.getY(), (float)rotOverride.getZ()), + : new Vector3f((float)rotOverride.x(), (float)rotOverride.y(), (float)rotOverride.z()), pullbackConfig -> pullbackConfig.leftRotationOverride == null ? null : new Vector3d(pullbackConfig.leftRotationOverride.x, pullbackConfig.leftRotationOverride.y, pullbackConfig.leftRotationOverride.z) ) .add() .append( - new KeyedCodec<>("RightOffsetOverride", Vector3d.AS_ARRAY_CODEC), + new KeyedCodec<>("RightOffsetOverride", Vector3dUtil.AS_ARRAY_CODEC), (pullbackConfig, offOverride) -> pullbackConfig.rightOffsetOverride = offOverride == null ? null - : new Vector3f((float)offOverride.getX(), (float)offOverride.getY(), (float)offOverride.getZ()), + : new Vector3f((float)offOverride.x(), (float)offOverride.y(), (float)offOverride.z()), pullbackConfig -> pullbackConfig.rightOffsetOverride == null ? null : new Vector3d(pullbackConfig.rightOffsetOverride.x, pullbackConfig.rightOffsetOverride.y, pullbackConfig.rightOffsetOverride.z) ) .add() .append( - new KeyedCodec<>("RightRotationOverride", Vector3d.AS_ARRAY_CODEC), + new KeyedCodec<>("RightRotationOverride", Vector3dUtil.AS_ARRAY_CODEC), (pullbackConfig, rotOverride) -> pullbackConfig.rightRotationOverride = rotOverride == null ? null - : new Vector3f((float)rotOverride.getX(), (float)rotOverride.getY(), (float)rotOverride.getZ()), + : new Vector3f((float)rotOverride.x(), (float)rotOverride.y(), (float)rotOverride.z()), pullbackConfig -> pullbackConfig.rightRotationOverride == null ? null : new Vector3d(pullbackConfig.rightRotationOverride.x, pullbackConfig.rightRotationOverride.y, pullbackConfig.rightRotationOverride.z) diff --git a/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemTranslationProperties.java b/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemTranslationProperties.java index 81bddb05..48a89acb 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemTranslationProperties.java +++ b/src/com/hypixel/hytale/server/core/asset/type/item/config/ItemTranslationProperties.java @@ -3,20 +3,43 @@ package com.hypixel.hytale.server.core.asset.type.item.config; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.Schema; import com.hypixel.hytale.codec.schema.metadata.ui.UIEditor; +import com.hypixel.hytale.codec.validation.ValidationResults; +import com.hypixel.hytale.codec.validation.Validator; import com.hypixel.hytale.server.core.io.NetworkSerializable; +import com.hypixel.hytale.server.core.modules.i18n.I18nModule; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class ItemTranslationProperties implements NetworkSerializable { + private static final Validator SERVER_LANG_KEY_VALIDATOR = new Validator() { + public void accept(@Nullable String key, @Nonnull ValidationResults results) { + if (key != null && key.startsWith("server.")) { + I18nModule i18n = I18nModule.get(); + if (i18n != null) { + if (!i18n.getMessages("en-US").containsKey(key)) { + results.warn("[LOC] Key '" + key + "' does not exist in server.lang!"); + } + } + } + } + + @Override + public void updateSchema(SchemaContext context, Schema target) { + } + }; public static final BuilderCodec CODEC = BuilderCodec.builder(ItemTranslationProperties.class, ItemTranslationProperties::new) .appendInherited(new KeyedCodec<>("Name", Codec.STRING), (data, s) -> data.name = s, data -> data.name, (o, p) -> o.name = p.name) + .addValidator(SERVER_LANG_KEY_VALIDATOR) .documentation("The translation key for the name of this item.") .metadata(new UIEditor(new UIEditor.LocalizationKeyField("server.items.{assetId}.name", true))) .add() .appendInherited( new KeyedCodec<>("Description", Codec.STRING), (data, s) -> data.description = s, data -> data.description, (o, p) -> o.description = p.description ) + .addValidator(SERVER_LANG_KEY_VALIDATOR) .documentation("The translation key for the description of this item.") .metadata(new UIEditor(new UIEditor.LocalizationKeyField("server.items.{assetId}.description"))) .add() diff --git a/src/com/hypixel/hytale/server/core/asset/type/model/config/DetailBox.java b/src/com/hypixel/hytale/server/core/asset/type/model/config/DetailBox.java index 4c1f035b..a69a41b3 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/model/config/DetailBox.java +++ b/src/com/hypixel/hytale/server/core/asset/type/model/config/DetailBox.java @@ -4,34 +4,36 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.protocol.Vector3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.io.NetworkSerializable; import com.hypixel.hytale.server.core.io.NetworkSerializers; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; +import org.joml.Vector3f; public class DetailBox implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(DetailBox.class, DetailBox::new) - .appendInherited(new KeyedCodec<>("Offset", Vector3d.CODEC), (o, i) -> o.offset = i, o -> o.offset, (o, p) -> o.offset = p.offset) + .appendInherited(new KeyedCodec<>("Offset", Vector3dUtil.CODEC), (o, i) -> o.offset.set(i), o -> o.offset, (o, p) -> o.offset.set(p.offset)) .addValidator(Validators.nonNull()) .add() .appendInherited(new KeyedCodec<>("Box", Box.CODEC), (o, i) -> o.box = i, o -> o.box, (o, p) -> o.box = p.box) .addValidator(Validators.nonNull()) .add() .build(); - protected Vector3d offset = Vector3d.ZERO; + protected final Vector3d offset = new Vector3d(); protected Box box = Box.UNIT; public DetailBox() { } - public DetailBox(Vector3d offset, Box box) { - this.offset = offset; + public DetailBox(Vector3dc offset, Box box) { + this.offset.set(offset); this.box = box; } public DetailBox(DetailBox other) { - this.offset.assign(other.offset); + this.offset.set(other.offset); this.box.assign(other.box); } @@ -44,7 +46,7 @@ public class DetailBox implements NetworkSerializable { public static final String UNKNOWN_TEXTURE = "textures/Unknown.png"; @@ -107,9 +108,11 @@ public class Model implements NetworkSerializable> { public static final BuilderCodec MODEL_TRAIL_CODEC = BuilderCodec.builder(ModelTrail.class, ModelTrail::new) @@ -69,7 +71,11 @@ public class ModelAsset implements JsonAssetWithMap("TargetEntityPart", new EnumCodec<>(EntityPart.class)), (trail, s) -> trail.targetEntityPart = s, trail -> trail.targetEntityPart ) .addField(new KeyedCodec<>("TargetNodeName", Codec.STRING), (trail, s) -> trail.targetNodeName = s, trail -> trail.targetNodeName) - .addField(new KeyedCodec<>("PositionOffset", ProtocolCodecs.VECTOR3F), (trail, s) -> trail.positionOffset = s, trail -> trail.positionOffset) + .addField( + new KeyedCodec<>("PositionOffset", Vector3fUtil.CODEC), + (trail, s) -> trail.positionOffset = s, + trail -> trail.positionOffset != null ? new Vector3f(trail.positionOffset.x(), trail.positionOffset.y(), trail.positionOffset.z()) : null + ) .addField(new KeyedCodec<>("RotationOffset", ProtocolCodecs.DIRECTION), (trail, s) -> trail.rotationOffset = s, trail -> trail.rotationOffset) .addField(new KeyedCodec<>("FixedRotation", Codec.BOOLEAN), (trail, s) -> trail.fixedRotation = s, trail -> trail.fixedRotation) .build(); diff --git a/src/com/hypixel/hytale/server/core/asset/type/model/config/ModelParticle.java b/src/com/hypixel/hytale/server/core/asset/type/model/config/ModelParticle.java index 1c1d5ac1..7efd5f62 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/model/config/ModelParticle.java +++ b/src/com/hypixel/hytale/server/core/asset/type/model/config/ModelParticle.java @@ -6,14 +6,15 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.protocol.Color; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.EntityPart; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSystem; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; +import org.joml.Vector3f; public class ModelParticle implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(ModelParticle.class, ModelParticle::new) @@ -35,7 +36,7 @@ public class ModelParticle implements NetworkSerializableappend(new KeyedCodec<>("Scale", Codec.DOUBLE), (particle, o) -> particle.scale = o.floatValue(), particle -> (double)particle.scale) .addValidator(Validators.greaterThan(0.0)) .add() - .append(new KeyedCodec<>("PositionOffset", ProtocolCodecs.VECTOR3F), (particle, s) -> particle.positionOffset = s, particle -> particle.positionOffset) + .append(new KeyedCodec<>("PositionOffset", Vector3fUtil.CODEC), (particle, s) -> particle.positionOffset = s, particle -> particle.positionOffset) .add() .append(new KeyedCodec<>("RotationOffset", ProtocolCodecs.DIRECTION), (particle, s) -> particle.rotationOffset = s, particle -> particle.rotationOffset) .add() diff --git a/src/com/hypixel/hytale/server/core/asset/type/model/config/camera/CameraSettings.java b/src/com/hypixel/hytale/server/core/asset/type/model/config/camera/CameraSettings.java index 2e41c56a..cb1aefd8 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/model/config/camera/CameraSettings.java +++ b/src/com/hypixel/hytale/server/core/asset/type/model/config/camera/CameraSettings.java @@ -2,16 +2,16 @@ package com.hypixel.hytale.server.core.asset.type.model.config.camera; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.protocol.Vector3f; -import com.hypixel.hytale.server.core.codec.ProtocolCodecs; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3f; public class CameraSettings implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(CameraSettings.class, CameraSettings::new) .addField( - new KeyedCodec<>("PositionOffset", ProtocolCodecs.VECTOR3F), + new KeyedCodec<>("PositionOffset", Vector3fUtil.CODEC), (cameraSettings, s) -> cameraSettings.positionOffset = s, cameraSettings -> cameraSettings.positionOffset ) diff --git a/src/com/hypixel/hytale/server/core/asset/type/modelvfx/config/ModelVFX.java b/src/com/hypixel/hytale/server/core/asset/type/modelvfx/config/ModelVFX.java index 51e44e0e..b602f342 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/modelvfx/config/ModelVFX.java +++ b/src/com/hypixel/hytale/server/core/asset/type/modelvfx/config/ModelVFX.java @@ -14,15 +14,16 @@ import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.ValidatorCache; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.vector.Vector2fUtil; import com.hypixel.hytale.protocol.Color; import com.hypixel.hytale.protocol.CurveType; import com.hypixel.hytale.protocol.EffectDirection; import com.hypixel.hytale.protocol.LoopOption; import com.hypixel.hytale.protocol.SwitchTo; -import com.hypixel.hytale.protocol.Vector2f; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; +import org.joml.Vector2f; public class ModelVFX implements JsonAssetWithMap>, @@ -47,7 +48,7 @@ public class ModelVFX .addValidator(Validators.nonNull()) .add() .addField(new KeyedCodec<>("AnimationDuration", Codec.FLOAT), (modelVFX, d) -> modelVFX.animationDuration = d, modelVFX -> modelVFX.animationDuration) - .addField(new KeyedCodec<>("AnimationRange", ProtocolCodecs.VECTOR2F), (modelVFX, d) -> modelVFX.animationRange = d, modelVFX -> modelVFX.animationRange) + .addField(new KeyedCodec<>("AnimationRange", Vector2fUtil.CODEC), (modelVFX, d) -> modelVFX.animationRange = d, modelVFX -> modelVFX.animationRange) .append( new KeyedCodec<>("LoopOption", new EnumCodec<>(LoopOption.class)), (modelVFX, s) -> modelVFX.loopOption = s, modelVFX -> modelVFX.loopOption ) @@ -68,10 +69,8 @@ public class ModelVFX (modelVFX, b) -> modelVFX.useProgressiveHighlight = b, modelVFX -> modelVFX.useProgressiveHighlight ) - .addField(new KeyedCodec<>("NoiseScale", ProtocolCodecs.VECTOR2F), (modelVFX, d) -> modelVFX.noiseScale = d, modelVFX -> modelVFX.noiseScale) - .addField( - new KeyedCodec<>("NoiseScrollSpeed", ProtocolCodecs.VECTOR2F), (modelVFX, d) -> modelVFX.noiseScrollSpeed = d, modelVFX -> modelVFX.noiseScrollSpeed - ) + .addField(new KeyedCodec<>("NoiseScale", Vector2fUtil.CODEC), (modelVFX, d) -> modelVFX.noiseScale = d, modelVFX -> modelVFX.noiseScale) + .addField(new KeyedCodec<>("NoiseScrollSpeed", Vector2fUtil.CODEC), (modelVFX, d) -> modelVFX.noiseScrollSpeed = d, modelVFX -> modelVFX.noiseScrollSpeed) .addField(new KeyedCodec<>("PostColor", ProtocolCodecs.COLOR), (modelVFX, o) -> modelVFX.postColor = o, modelVFX -> modelVFX.postColor) .append(new KeyedCodec<>("PostColorOpacity", Codec.FLOAT), (modelVFX, d) -> modelVFX.postColorOpacity = d, modelVFX -> modelVFX.postColorOpacity) .addValidator(Validators.range(0.0F, 1.0F)) diff --git a/src/com/hypixel/hytale/server/core/asset/type/particle/commands/ParticleSpawnCommand.java b/src/com/hypixel/hytale/server/core/asset/type/particle/commands/ParticleSpawnCommand.java index 563d359c..4bfec7d6 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/particle/commands/ParticleSpawnCommand.java +++ b/src/com/hypixel/hytale/server/core/asset/type/particle/commands/ParticleSpawnCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.asset.type.particle.commands; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSystem; import com.hypixel.hytale.server.core.asset.type.particle.pages.ParticleSpawnPage; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -17,9 +16,10 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.ParticleUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ParticleSpawnCommand extends AbstractTargetPlayerCommand { @Nonnull @@ -48,7 +48,7 @@ public class ParticleSpawnCommand extends AbstractTargetPlayerCommand { Vector3d position = transformComponent.getPosition(); SpatialResource, EntityStore> playerSpatialResource = store.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, 75.0, results); ParticleUtil.spawnParticleEffect(particleSystem.getId(), position, transformComponent.getRotation(), results, store); } diff --git a/src/com/hypixel/hytale/server/core/asset/type/particle/config/ParticleAttractor.java b/src/com/hypixel/hytale/server/core/asset/type/particle/config/ParticleAttractor.java index 9d2f6605..7ba6275e 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/particle/config/ParticleAttractor.java +++ b/src/com/hypixel/hytale/server/core/asset/type/particle/config/ParticleAttractor.java @@ -4,20 +4,20 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.protocol.Vector3f; -import com.hypixel.hytale.server.core.codec.ProtocolCodecs; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; +import org.joml.Vector3f; public class ParticleAttractor implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(ParticleAttractor.class, ParticleAttractor::new) .addField( - new KeyedCodec<>("Position", ProtocolCodecs.VECTOR3F), + new KeyedCodec<>("Position", Vector3fUtil.CODEC), (particleAttractor, o) -> particleAttractor.position = o, particleAttractor -> particleAttractor.position ) .addField( - new KeyedCodec<>("RadialAxis", ProtocolCodecs.VECTOR3F), + new KeyedCodec<>("RadialAxis", Vector3fUtil.CODEC), (particleAttractor, o) -> particleAttractor.radialAxis = o, particleAttractor -> particleAttractor.radialAxis ) @@ -40,7 +40,7 @@ public class ParticleAttractor implements NetworkSerializable particleAttractor.radialTangentAcceleration ) .addField( - new KeyedCodec<>("LinearAcceleration", ProtocolCodecs.VECTOR3F), + new KeyedCodec<>("LinearAcceleration", Vector3fUtil.CODEC), (particleAttractor, o) -> particleAttractor.linearAcceleration = o, particleAttractor -> particleAttractor.linearAcceleration ) @@ -55,12 +55,12 @@ public class ParticleAttractor implements NetworkSerializable particleAttractor.radialTangentImpulse ) .addField( - new KeyedCodec<>("LinearImpulse", ProtocolCodecs.VECTOR3F), + new KeyedCodec<>("LinearImpulse", Vector3fUtil.CODEC), (particleAttractor, o) -> particleAttractor.linearImpulse = o, particleAttractor -> particleAttractor.linearImpulse ) .addField( - new KeyedCodec<>("DampingMultiplier", ProtocolCodecs.VECTOR3F), + new KeyedCodec<>("DampingMultiplier", Vector3fUtil.CODEC), (particleAttractor, o) -> particleAttractor.dampingMultiplier = o, particleAttractor -> particleAttractor.dampingMultiplier ) diff --git a/src/com/hypixel/hytale/server/core/asset/type/particle/config/ParticleSpawnerGroup.java b/src/com/hypixel/hytale/server/core/asset/type/particle/config/ParticleSpawnerGroup.java index 04a7a8f7..b50e0790 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/particle/config/ParticleSpawnerGroup.java +++ b/src/com/hypixel/hytale/server/core/asset/type/particle/config/ParticleSpawnerGroup.java @@ -5,15 +5,16 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.common.util.ArrayUtil; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.InitialVelocity; import com.hypixel.hytale.protocol.RangeVector3f; import com.hypixel.hytale.protocol.Rangef; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.io.NetworkSerializable; import java.util.Arrays; import javax.annotation.Nonnull; +import org.joml.Vector3f; public class ParticleSpawnerGroup implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(ParticleSpawnerGroup.class, ParticleSpawnerGroup::new) @@ -25,7 +26,7 @@ public class ParticleSpawnerGroup implements NetworkSerializable("PositionOffset", ProtocolCodecs.VECTOR3F), + new KeyedCodec<>("PositionOffset", Vector3fUtil.CODEC), (particleSpawnerGroup, o) -> particleSpawnerGroup.positionOffset = o, particleSpawnerGroup -> particleSpawnerGroup.positionOffset ) diff --git a/src/com/hypixel/hytale/server/core/asset/type/particle/config/WorldParticle.java b/src/com/hypixel/hytale/server/core/asset/type/particle/config/WorldParticle.java index 88baa9fa..2377258f 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/particle/config/WorldParticle.java +++ b/src/com/hypixel/hytale/server/core/asset/type/particle/config/WorldParticle.java @@ -5,12 +5,13 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.protocol.Color; import com.hypixel.hytale.protocol.Direction; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.io.NetworkSerializable; import javax.annotation.Nonnull; +import org.joml.Vector3f; public class WorldParticle implements NetworkSerializable { public static final String SYSTEM_ID_DOC = "The id of the particle system."; @@ -32,7 +33,7 @@ public class WorldParticle implements NetworkSerializableappend( - new KeyedCodec<>("PositionOffset", ProtocolCodecs.VECTOR3F), (particle, s) -> particle.positionOffset = s, particle -> particle.positionOffset + new KeyedCodec<>("PositionOffset", Vector3fUtil.CODEC), (particle, s) -> particle.positionOffset = s, particle -> particle.positionOffset ) .documentation("The position offset from the spawn position.") .add() diff --git a/src/com/hypixel/hytale/server/core/asset/type/particle/pages/ParticleSpawnPage.java b/src/com/hypixel/hytale/server/core/asset/type/particle/pages/ParticleSpawnPage.java index 6639f84c..aad3b2b0 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/particle/pages/ParticleSpawnPage.java +++ b/src/com/hypixel/hytale/server/core/asset/type/particle/pages/ParticleSpawnPage.java @@ -11,9 +11,8 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.Rangef; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; @@ -34,7 +33,6 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.Comparator; import java.util.List; import java.util.Locale; @@ -42,6 +40,7 @@ import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ParticleSpawnPage extends InteractiveCustomUIPage { private static final String COMMON_TEXT_BUTTON_DOCUMENT = "Common/TextButton.ui"; @@ -55,7 +54,7 @@ public class ParticleSpawnPage extends InteractiveCustomUIPage particleSystemPreview; private Vector3d position; - private Vector3f rotation; + private Rotation3f rotation; public ParticleSpawnPage(@Nonnull PlayerRef playerRef) { super(playerRef, CustomPageLifetime.CanDismiss, ParticleSpawnPage.ParticleSpawnPageEventData.CODEC); @@ -97,10 +96,10 @@ public class ParticleSpawnPage extends InteractiveCustomUIPage, EntityStore> playerSpatialResource = store.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(this.position, 75.0, results); ParticleUtil.spawnParticleEffect(this.selectedParticleSystemId, this.position, this.rotation, results, store); } @@ -220,10 +219,10 @@ public class ParticleSpawnPage extends InteractiveCustomUIPage holder = store.getRegistry().newHolder(); diff --git a/src/com/hypixel/hytale/server/core/asset/type/physicalmaterial/PhysicalMaterialPacketGenerator.java b/src/com/hypixel/hytale/server/core/asset/type/physicalmaterial/PhysicalMaterialPacketGenerator.java new file mode 100644 index 00000000..0bb9f6e3 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/asset/type/physicalmaterial/PhysicalMaterialPacketGenerator.java @@ -0,0 +1,78 @@ +package com.hypixel.hytale.server.core.asset.type.physicalmaterial; + +import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.UpdateType; +import com.hypixel.hytale.protocol.packets.assets.UpdatePhysicalMaterials; +import com.hypixel.hytale.server.core.asset.packet.SimpleAssetPacketGenerator; +import com.hypixel.hytale.server.core.asset.type.physicalmaterial.config.PhysicalMaterial; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nonnull; + +public class PhysicalMaterialPacketGenerator extends SimpleAssetPacketGenerator> { + @Nonnull + public ToClientPacket generateInitPacket( + @Nonnull IndexedLookupTableAssetMap assetMap, @Nonnull Map assets + ) { + UpdatePhysicalMaterials packet = new UpdatePhysicalMaterials(); + packet.type = UpdateType.Init; + packet.physicalMaterials = new Int2ObjectOpenHashMap<>(); + + for (Entry entry : assets.entrySet()) { + String key = entry.getKey(); + int index = assetMap.getIndex(key); + if (index == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! " + key); + } + + packet.physicalMaterials.put(index, entry.getValue().toPacket()); + } + + packet.maxId = assetMap.getNextIndex(); + return packet; + } + + @Nonnull + public ToClientPacket generateUpdatePacket( + @Nonnull IndexedLookupTableAssetMap assetMap, @Nonnull Map loadedAssets + ) { + UpdatePhysicalMaterials packet = new UpdatePhysicalMaterials(); + packet.type = UpdateType.AddOrUpdate; + packet.physicalMaterials = new Int2ObjectOpenHashMap<>(); + + for (Entry entry : loadedAssets.entrySet()) { + String key = entry.getKey(); + int index = assetMap.getIndex(key); + if (index == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! " + key); + } + + packet.physicalMaterials.put(index, entry.getValue().toPacket()); + } + + packet.maxId = assetMap.getNextIndex(); + return packet; + } + + @Nonnull + public ToClientPacket generateRemovePacket(@Nonnull IndexedLookupTableAssetMap assetMap, @Nonnull Set removed) { + UpdatePhysicalMaterials packet = new UpdatePhysicalMaterials(); + packet.type = UpdateType.Remove; + packet.physicalMaterials = new Int2ObjectOpenHashMap<>(); + + for (String key : removed) { + int index = assetMap.getIndex(key); + if (index == Integer.MIN_VALUE) { + throw new IllegalArgumentException("Unknown key! " + key); + } + + packet.physicalMaterials.put(index, null); + } + + packet.maxId = assetMap.getNextIndex(); + return packet; + } +} diff --git a/src/com/hypixel/hytale/server/core/asset/type/physicalmaterial/config/PhysicalMaterial.java b/src/com/hypixel/hytale/server/core/asset/type/physicalmaterial/config/PhysicalMaterial.java new file mode 100644 index 00000000..9dfb82d9 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/asset/type/physicalmaterial/config/PhysicalMaterial.java @@ -0,0 +1,151 @@ +package com.hypixel.hytale.server.core.asset.type.physicalmaterial.config; + +import com.hypixel.hytale.assetstore.AssetExtraInfo; +import com.hypixel.hytale.assetstore.AssetKeyValidator; +import com.hypixel.hytale.assetstore.AssetRegistry; +import com.hypixel.hytale.assetstore.AssetStore; +import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; +import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; +import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.validation.ValidatorCache; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.server.core.io.NetworkSerializable; +import java.lang.ref.SoftReference; +import javax.annotation.Nonnull; + +public class PhysicalMaterial + implements JsonAssetWithMap>, + NetworkSerializable { + public static final int EMPTY_ID = 0; + public static final String EMPTY = "EMPTY"; + public static final PhysicalMaterial EMPTY_PHYSICAL_MATERIAL = new PhysicalMaterial("EMPTY"); + public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( + PhysicalMaterial.class, + PhysicalMaterial::new, + Codec.STRING, + (mat, s) -> mat.id = s, + mat -> mat.id, + (asset, data) -> asset.data = data, + asset -> asset.data + ) + .appendInherited( + new KeyedCodec<>("ReflectionCoeff", Codec.FLOAT), + (mat, v) -> mat.reflectionCoeff = v, + mat -> mat.reflectionCoeff, + (mat, parent) -> mat.reflectionCoeff = parent.reflectionCoeff + ) + .addValidator(Validators.range(0.0F, 1.0F)) + .add() + .appendInherited( + new KeyedCodec<>("GainPerBlock", Codec.FLOAT), + (mat, v) -> mat.gainPerBlock = v, + mat -> mat.gainPerBlock, + (mat, parent) -> mat.gainPerBlock = parent.gainPerBlock + ) + .addValidator(Validators.min(0.0F)) + .add() + .appendInherited( + new KeyedCodec<>("HFGainPerBlock", Codec.FLOAT), + (mat, v) -> mat.hfGainPerBlock = v, + mat -> mat.hfGainPerBlock, + (mat, parent) -> mat.hfGainPerBlock = parent.hfGainPerBlock + ) + .addValidator(Validators.min(0.0F)) + .add() + .appendInherited( + new KeyedCodec<>("ShelterOpacity", Codec.FLOAT), + (mat, v) -> mat.shelterOpacity = v, + mat -> mat.shelterOpacity, + (mat, parent) -> mat.shelterOpacity = parent.shelterOpacity + ) + .addValidator(Validators.range(0.0F, 1.0F)) + .add() + .afterDecode(PhysicalMaterial::processConfig) + .build(); + public static final ValidatorCache VALIDATOR_CACHE = new ValidatorCache<>(new AssetKeyValidator<>(PhysicalMaterial::getAssetStore)); + private static AssetStore> ASSET_STORE; + protected AssetExtraInfo.Data data; + protected String id; + protected float reflectionCoeff; + protected float gainPerBlock; + protected float hfGainPerBlock; + protected float shelterOpacity; + private SoftReference cachedPacket; + + public static AssetStore> getAssetStore() { + if (ASSET_STORE == null) { + ASSET_STORE = AssetRegistry.getAssetStore(PhysicalMaterial.class); + } + + return ASSET_STORE; + } + + public static IndexedLookupTableAssetMap getAssetMap() { + return (IndexedLookupTableAssetMap)getAssetStore().getAssetMap(); + } + + public PhysicalMaterial(String id) { + this.id = id; + } + + protected PhysicalMaterial() { + } + + @Nonnull + public com.hypixel.hytale.protocol.PhysicalMaterial toPacket() { + com.hypixel.hytale.protocol.PhysicalMaterial cached = this.cachedPacket == null ? null : this.cachedPacket.get(); + if (cached != null) { + return cached; + } else { + com.hypixel.hytale.protocol.PhysicalMaterial packet = new com.hypixel.hytale.protocol.PhysicalMaterial(); + packet.id = this.id; + packet.reflectionCoeff = this.reflectionCoeff; + packet.gainPerBlock = this.gainPerBlock; + packet.hFGainPerBlock = this.hfGainPerBlock; + packet.shelterOpacity = this.shelterOpacity; + this.cachedPacket = new SoftReference<>(packet); + return packet; + } + } + + public String getId() { + return this.id; + } + + public float getReflectionCoeff() { + return this.reflectionCoeff; + } + + public float getGainPerBlock() { + return this.gainPerBlock; + } + + public float getHfGainPerBlock() { + return this.hfGainPerBlock; + } + + public float getShelterOpacity() { + return this.shelterOpacity; + } + + protected void processConfig() { + } + + @Nonnull + @Override + public String toString() { + return "PhysicalMaterial{id='" + + this.id + + "', reflectionCoeff=" + + this.reflectionCoeff + + ", gainPerBlock=" + + this.gainPerBlock + + ", hfGainPerBlock=" + + this.hfGainPerBlock + + ", shelterOpacity=" + + this.shelterOpacity + + "}"; + } +} diff --git a/src/com/hypixel/hytale/server/core/asset/type/portalworld/PortalSpawnConfig.java b/src/com/hypixel/hytale/server/core/asset/type/portalworld/PortalSpawnConfig.java new file mode 100644 index 00000000..f0b88b6f --- /dev/null +++ b/src/com/hypixel/hytale/server/core/asset/type/portalworld/PortalSpawnConfig.java @@ -0,0 +1,49 @@ +package com.hypixel.hytale.server.core.asset.type.portalworld; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider; +import javax.annotation.Nullable; + +public class PortalSpawnConfig { + public static final BuilderCodec CODEC = BuilderCodec.builder(PortalSpawnConfig.class, PortalSpawnConfig::new) + .append(new KeyedCodec<>("SpawnReturnPortal", Codec.BOOLEAN), (config, o) -> config.spawnReturnPortal = o, config -> config.spawnReturnPortal) + .documentation("Whether to spawn the return (usually a portal block) on the spawn location within the fragment instance.") + .add() + .append( + new KeyedCodec<>("SpawnProviderOverride", ISpawnProvider.CODEC), + (config, o) -> config.spawnProviderOverride = o, + config -> config.spawnProviderOverride + ) + .documentation( + "Set a spawn provider which will override the world's spawn provider. The spawn returned from this spawn provider will be the exact spawn location for the fragment." + ) + .add() + .append(new KeyedCodec<>("ReturnBlock", Codec.STRING), (config, o) -> config.returnBlockId = o, config -> config.returnBlockId) + .documentation("Overrides the block to use as a return portal for this portal type.") + .addValidatorLate(() -> BlockType.VALIDATOR_CACHE.getValidator().late()) + .add() + .build(); + private boolean spawnReturnPortal = true; + private ISpawnProvider spawnProviderOverride = null; + private String returnBlockId; + + public boolean isSpawningReturnPortal() { + return this.spawnReturnPortal; + } + + public ISpawnProvider getSpawnProviderOverride() { + return this.spawnProviderOverride; + } + + public String getReturnBlockOverrideId() { + return this.returnBlockId; + } + + @Nullable + public BlockType getReturnBlockOverride() { + return this.returnBlockId == null ? null : BlockType.getAssetMap().getAsset(this.returnBlockId); + } +} diff --git a/src/com/hypixel/hytale/server/core/asset/type/portalworld/PortalType.java b/src/com/hypixel/hytale/server/core/asset/type/portalworld/PortalType.java index f8c39f6a..5fc851b1 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/portalworld/PortalType.java +++ b/src/com/hypixel/hytale/server/core/asset/type/portalworld/PortalType.java @@ -51,6 +51,9 @@ public class PortalType implements JsonAssetWithMapappend(new KeyedCodec<>("GameplayConfig", Codec.STRING), (config, o) -> config.gameplayConfig = o, config -> config.gameplayConfig) .documentation("The ID of the GameplayConfig asset for worlds spawned with this portal type.") .add() + .append(new KeyedCodec<>("Spawn", PortalSpawnConfig.CODEC), (config, o) -> config.spawn = o, config -> config.spawn) + .documentation("Configs related to spawning into the portal fragment's instance.") + .add() .build(); private static AssetStore> ASSET_STORE; public static final ValidatorCache VALIDATOR_CACHE = new ValidatorCache<>(new AssetKeyValidator<>(PortalType::getAssetStore)); @@ -61,6 +64,7 @@ public class PortalType implements JsonAssetWithMap cursedItems = Collections.emptySet(); + private PortalSpawnConfig spawn = new PortalSpawnConfig(); public static AssetStore> getAssetStore() { if (ASSET_STORE == null) { @@ -102,6 +106,10 @@ public class PortalType implements JsonAssetWithMap CODEC = BuilderCodec.builder(SwitchResponseCurve.class, SwitchResponseCurve::new) + .documentation( + "A type of response curve which returns the initial state value before the defined switch point and the final state value after reaching it." + ) + .append(new KeyedCodec<>("InitialState", Codec.DOUBLE), (curve, d) -> curve.initialState = d, curve -> curve.initialState) + .addValidator(Validators.range(0.0, 1.0)) + .documentation("The y value to return before the switch point.") + .add() + .append(new KeyedCodec<>("FinalState", Codec.DOUBLE), (curve, d) -> curve.finalState = d, curve -> curve.finalState) + .addValidator(Validators.range(0.0, 1.0)) + .documentation("The y value to return at and beyond the switch point.") + .add() + .append(new KeyedCodec<>("SwitchPoint", Codec.DOUBLE), (curve, d) -> curve.switchPoint = d, curve -> curve.switchPoint) + .addValidator(Validators.range(0.0, 1.0)) + .documentation("The value at which to switch from the initial state to the final state.") + .add() + .build(); + protected double initialState = 0.0; + protected double finalState = 1.0; + protected double switchPoint; + + protected SwitchResponseCurve() { + } + + @Override + public double computeY(double x) { + return x < this.switchPoint ? this.initialState : this.finalState; + } + + @Nonnull + @Override + public String toString() { + return "SwitchResponseCurve{initialState=" + + this.initialState + + ", finalState=" + + this.finalState + + ", switchPoint=" + + this.switchPoint + + "}" + + super.toString(); + } +} diff --git a/src/com/hypixel/hytale/server/core/asset/type/soundevent/config/SoundEvent.java b/src/com/hypixel/hytale/server/core/asset/type/soundevent/config/SoundEvent.java index d681965f..fcdb61a4 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/soundevent/config/SoundEvent.java +++ b/src/com/hypixel/hytale/server/core/asset/type/soundevent/config/SoundEvent.java @@ -27,6 +27,7 @@ public class SoundEvent public static final int EMPTY_ID = 0; public static final String EMPTY = "EMPTY"; public static final SoundEvent EMPTY_SOUND_EVENT = new SoundEvent("EMPTY"); + private static final int MAX_SOUND_EVENT_LAYERS = 8; public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( SoundEvent.class, SoundEvent::new, Codec.STRING, (t, k) -> t.id = k, t -> t.id, (asset, data) -> asset.data = data, asset -> asset.data ) @@ -83,6 +84,17 @@ public class SoundEvent ) .documentation("Maximum distance at which this sound event can be heard in blocks (i.e. the distance at which it's attenuated to zero).") .add() + .appendInherited( + new KeyedCodec<>("SpatialBlend", Codec.FLOAT), + (soundEvent, f) -> soundEvent.spatialBlend = f, + soundEvent -> soundEvent.spatialBlend, + (soundEvent, parent) -> soundEvent.spatialBlend = parent.spatialBlend + ) + .addValidator(Validators.range(0.0F, 1.0F)) + .documentation( + "Controls spatial blending. At 1.0 the source is fully 3D (i.e. a point source). At 0.0 the source is fully diffuse (i.e. centered on the player). Only applies to Stereo Headphone mode." + ) + .add() .appendInherited( new KeyedCodec<>("MaxInstance", Codec.INTEGER), (soundEvent, i) -> soundEvent.maxInstance = i, @@ -106,7 +118,8 @@ public class SoundEvent soundEvent -> soundEvent.layers, (soundEvent, parent) -> soundEvent.layers = parent.layers ) - .addValidator(Validators.nonEmptyArray()) + .addValidator(Validators.nonNull()) + .addValidator(Validators.arraySizeRange(1, 8)) .documentation("The layered sounds that make up this sound event.") .add() .appendInherited( @@ -130,6 +143,7 @@ public class SoundEvent protected transient float ambientDuckingVolume = 1.0F; protected float startAttenuationDistance = 2.0F; protected float maxDistance = 16.0F; + protected float spatialBlend = 0.6F; protected int maxInstance = 50; protected boolean preventSoundInterruption = false; protected SoundEventLayer[] layers; @@ -224,6 +238,10 @@ public class SoundEvent return this.maxDistance; } + public float getSpatialBlend() { + return this.spatialBlend; + } + public int getMaxInstance() { return this.maxInstance; } @@ -266,6 +284,8 @@ public class SoundEvent + this.startAttenuationDistance + ", maxDistance=" + this.maxDistance + + ", spatialBlend=" + + this.spatialBlend + ", maxInstance=" + this.maxInstance + ", preventSoundInterruption=" @@ -295,6 +315,7 @@ public class SoundEvent packet.ambientDuckingVolume = this.ambientDuckingVolume; packet.startAttenuationDistance = this.startAttenuationDistance; packet.maxDistance = this.maxDistance; + packet.spatialBlend = this.spatialBlend; packet.maxInstance = this.maxInstance; packet.preventSoundInterruption = this.preventSoundInterruption; packet.audioCategory = this.audioCategoryIndex; diff --git a/src/com/hypixel/hytale/server/core/asset/type/trail/config/Animation.java b/src/com/hypixel/hytale/server/core/asset/type/trail/config/Animation.java index 9259ef08..95367154 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/trail/config/Animation.java +++ b/src/com/hypixel/hytale/server/core/asset/type/trail/config/Animation.java @@ -3,15 +3,16 @@ package com.hypixel.hytale.server.core.asset.type.trail.config; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector2i; +import com.hypixel.hytale.math.vector.Vector2iUtil; import com.hypixel.hytale.protocol.Range; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class Animation { public static final BuilderCodec CODEC = BuilderCodec.builder(Animation.class, Animation::new) .appendInherited( - new KeyedCodec<>("FrameSize", Vector2i.CODEC), + new KeyedCodec<>("FrameSize", Vector2iUtil.CODEC), (animation, b) -> animation.frameSize = b, animation -> animation.frameSize, (animation, parent) -> animation.frameSize = parent.frameSize diff --git a/src/com/hypixel/hytale/server/core/asset/type/trail/config/Trail.java b/src/com/hypixel/hytale/server/core/asset/type/trail/config/Trail.java index 109999b6..5b48c03c 100644 --- a/src/com/hypixel/hytale/server/core/asset/type/trail/config/Trail.java +++ b/src/com/hypixel/hytale/server/core/asset/type/trail/config/Trail.java @@ -13,7 +13,6 @@ import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.codec.schema.metadata.ui.UIDefaultCollapsedState; import com.hypixel.hytale.codec.validation.ValidatorCache; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.protocol.FXRenderMode; import com.hypixel.hytale.protocol.IntersectionHighlight; import com.hypixel.hytale.server.core.asset.common.CommonAssetValidator; @@ -21,6 +20,7 @@ import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.io.NetworkSerializable; import java.lang.ref.SoftReference; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class Trail implements JsonAssetWithMap>, NetworkSerializable { public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( @@ -176,7 +176,7 @@ public class Trail implements JsonAssetWithMap= 400 && statusCode < 500 && statusCode != 429; + } + private AuthConfig() { } } diff --git a/src/com/hypixel/hytale/server/core/auth/HttpResponseException.java b/src/com/hypixel/hytale/server/core/auth/HttpResponseException.java new file mode 100644 index 00000000..5ae270c1 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/auth/HttpResponseException.java @@ -0,0 +1,26 @@ +package com.hypixel.hytale.server.core.auth; + +import java.io.IOException; + +public class HttpResponseException extends IOException { + private final int statusCode; + private final String responseBody; + + public HttpResponseException(int statusCode, String responseBody) { + super("HTTP " + statusCode + ": " + truncateBody(responseBody)); + this.statusCode = statusCode; + this.responseBody = responseBody; + } + + public int getStatusCode() { + return this.statusCode; + } + + public String getResponseBody() { + return this.responseBody; + } + + private static String truncateBody(String body) { + return body != null && body.length() > 200 ? body.substring(0, 200) + "..." : body; + } +} diff --git a/src/com/hypixel/hytale/server/core/auth/JWTValidator.java b/src/com/hypixel/hytale/server/core/auth/JWTValidator.java index 8398898f..4496135e 100644 --- a/src/com/hypixel/hytale/server/core/auth/JWTValidator.java +++ b/src/com/hypixel/hytale/server/core/auth/JWTValidator.java @@ -8,6 +8,11 @@ import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.OctetKeyPair; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.cert.X509Certificate; import java.text.ParseException; import java.time.Duration; @@ -29,6 +34,8 @@ public class JWTValidator { private static final int MIN_SIGNATURE_LENGTH = 80; private static final int MAX_SIGNATURE_LENGTH = 90; private static final Duration JWKS_REFRESH_MIN_INTERVAL = Duration.ofMinutes(5L); + private static final String JWKS_BUNDLED_RESOURCE = "/jwks_bundled.json"; + private static final String JWKS_CACHE_FILE = ".jwks.json"; private final SessionServiceClient sessionServiceClient; private final String expectedIssuer; private final String expectedAudience; @@ -41,6 +48,7 @@ public class JWTValidator { this.sessionServiceClient = sessionServiceClient; this.expectedIssuer = expectedIssuer; this.expectedAudience = expectedAudience; + this.preSeedJwksCache(); } @Nullable @@ -228,6 +236,7 @@ public class JWTValidator { JWKSet newSet = new JWKSet(jwkList); this.cachedJwkSet = newSet; this.lastJwksRefresh = Instant.now(); + this.saveJwksCacheToDisk(newSet); LOGGER.at(Level.INFO).log("JWKS loaded with %d keys (cached permanently)", jwkList.size()); return newSet; } @@ -278,6 +287,83 @@ public class JWTValidator { } } + private void preSeedJwksCache() { + JWKSet diskCache = this.loadJwksCacheFromDisk(); + if (diskCache != null) { + this.cachedJwkSet = diskCache; + LOGGER.at(Level.INFO).log("JWKS pre-seeded from disk cache (%d keys)", diskCache.getKeys().size()); + } else { + JWKSet embedded = this.loadEmbeddedJwks(); + if (embedded != null) { + this.cachedJwkSet = embedded; + LOGGER.at(Level.INFO).log("JWKS pre-seeded from embedded resource (%d keys)", embedded.getKeys().size()); + } else { + LOGGER.at(Level.INFO).log("No pre-seeded JWKS available, will fetch from network on first validation"); + } + } + } + + @Nullable + private JWKSet loadEmbeddedJwks() { + try { + JWKSet var3; + try (InputStream is = this.getClass().getResourceAsStream("/jwks_bundled.json")) { + if (is == null) { + LOGGER.at(Level.FINE).log("No embedded JWKS resource found"); + return null; + } + + String json = new String(is.readAllBytes(), StandardCharsets.UTF_8); + var3 = JWKSet.parse(json); + } + + return var3; + } catch (ParseException | IOException var6) { + LOGGER.at(Level.WARNING).withCause(var6).log("Failed to load embedded JWKS"); + return null; + } + } + + @Nullable + private JWKSet loadJwksCacheFromDisk() { + Path cacheFile = Path.of(".jwks.json"); + if (!Files.exists(cacheFile)) { + return null; + } else { + try { + String json = Files.readString(cacheFile, StandardCharsets.UTF_8); + JWKSet jwkSet = JWKSet.parse(json); + LOGGER.at(Level.FINE).log("Loaded JWKS cache from disk: %s", cacheFile); + return jwkSet; + } catch (ParseException | IOException var4) { + LOGGER.at(Level.WARNING).withCause(var4).log("Failed to load JWKS cache from disk"); + return null; + } + } + } + + private void saveJwksCacheToDisk(@Nonnull JWKSet jwkSet) { + try { + Files.writeString(Path.of(".jwks.json"), jwkSet.toString(), StandardCharsets.UTF_8); + LOGGER.at(Level.FINE).log("Saved JWKS cache to disk"); + } catch (IOException var3) { + LOGGER.at(Level.WARNING).withCause(var3).log("Failed to save JWKS cache to disk"); + } + } + + @Nullable + public JWTValidator.IdentityTokenClaims validateOfflineToken(@Nonnull String offlineToken) { + JWTValidator.IdentityTokenClaims claims = this.validateIdentityToken(offlineToken); + if (claims == null) { + return null; + } else if (!claims.hasScope("hytale:offline")) { + LOGGER.at(Level.WARNING).log("Offline token missing required scope: expected %s, got %s", "hytale:offline", claims.scope); + return null; + } else { + return claims; + } + } + public void invalidateJwksCache() { this.jwksFetchLock.lock(); diff --git a/src/com/hypixel/hytale/server/core/auth/ServerAuthManager.java b/src/com/hypixel/hytale/server/core/auth/ServerAuthManager.java index 99e0d5b5..28831355 100644 --- a/src/com/hypixel/hytale/server/core/auth/ServerAuthManager.java +++ b/src/com/hypixel/hytale/server/core/auth/ServerAuthManager.java @@ -5,11 +5,13 @@ import com.google.gson.JsonParser; import com.hypixel.hytale.codec.lookup.Priority; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.Options; import com.hypixel.hytale.server.core.ShutdownReason; import com.hypixel.hytale.server.core.auth.oauth.OAuthBrowserFlow; import com.hypixel.hytale.server.core.auth.oauth.OAuthClient; import com.hypixel.hytale.server.core.auth.oauth.OAuthDeviceFlow; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.cert.X509Certificate; import java.time.Instant; @@ -17,6 +19,7 @@ import java.util.Base64; import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -31,6 +34,8 @@ import joptsimple.OptionSet; public class ServerAuthManager { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); private static final int REFRESH_BUFFER_SECONDS = 300; + private static final int REFRESH_MAX_RETRIES = 3; + private static final int REFRESH_RETRY_BASE_DELAY_SECONDS = 30; private static volatile ServerAuthManager instance; private volatile ServerAuthManager.AuthMode authMode = ServerAuthManager.AuthMode.NONE; private volatile Instant tokenExpiry; @@ -52,7 +57,7 @@ public class ServerAuthManager { }); private ScheduledFuture refreshTask; private Runnable cancelActiveFlow; - private volatile String pendingFatalError; + private volatile Message pendingFatalError; private ServerAuthManager() { } @@ -97,66 +102,83 @@ public class ServerAuthManager { LOGGER.at(Level.INFO).log("Singleplayer mode, owner: %s (%s)", ownerProfile.username, ownerProfile.uuid); } - boolean hasCliTokens = false; - String sessionTokenValue = null; - String identityTokenValue = null; - if (optionSet.has(Options.SESSION_TOKEN)) { - sessionTokenValue = optionSet.valueOf(Options.SESSION_TOKEN); - LOGGER.at(Level.INFO).log("Session token loaded from CLI"); - } else { - String envToken = System.getenv("HYTALE_SERVER_SESSION_TOKEN"); - if (envToken != null && !envToken.isEmpty()) { - sessionTokenValue = envToken; - LOGGER.at(Level.INFO).log("Session token loaded from environment"); - } - } - - if (optionSet.has(Options.IDENTITY_TOKEN)) { - identityTokenValue = optionSet.valueOf(Options.IDENTITY_TOKEN); - LOGGER.at(Level.INFO).log("Identity token loaded from CLI"); - } else { - String envTokenx = System.getenv("HYTALE_SERVER_IDENTITY_TOKEN"); - if (envTokenx != null && !envTokenx.isEmpty()) { - identityTokenValue = envTokenx; - LOGGER.at(Level.INFO).log("Identity token loaded from environment"); - } - } - - if (sessionTokenValue != null || identityTokenValue != null) { - if (this.validateInitialTokens(sessionTokenValue, identityTokenValue)) { - SessionServiceClient.GameSessionResponse session = new SessionServiceClient.GameSessionResponse(); - session.sessionToken = sessionTokenValue; - session.identityToken = identityTokenValue; - this.gameSession.set(session); - hasCliTokens = true; + if (this.isSingleplayer && optionSet.valueOf(Options.AUTH_MODE) == Options.AuthMode.OFFLINE) { + String offlineTokenValue = System.getenv("HYTALE_SERVER_OFFLINE_TOKEN"); + if (offlineTokenValue != null && !offlineTokenValue.isEmpty()) { + LOGGER.at(Level.INFO).log("Offline token loaded from environment"); + if (this.validateOfflineToken(offlineTokenValue)) { + LOGGER.at(Level.INFO).log("Offline token validated, singleplayer offline mode"); + LOGGER.at(Level.INFO).log("Server session ID: %s", this.serverSessionId); + } else { + this.pendingFatalError = Message.translation("client.disconnection.shutdownReason.authFailed.offlineTokenValidationFailed"); + LOGGER.at(Level.SEVERE).log(this.pendingFatalError.getAnsiMessage()); + } } else { - this.pendingFatalError = "Token validation failed. Provided tokens may be expired, tampered, or malformed. Remove invalid tokens or provide valid ones."; - LOGGER.at(Level.SEVERE).log(this.pendingFatalError); + this.pendingFatalError = Message.translation("client.disconnection.shutdownReason.authFailed.needOfficialLauncher"); + LOGGER.at(Level.SEVERE).log(this.pendingFatalError.getAnsiMessage()); } - } - - if (hasCliTokens) { - if (this.isSingleplayer) { - this.authMode = ServerAuthManager.AuthMode.SINGLEPLAYER; - LOGGER.at(Level.INFO).log("Auth mode: SINGLEPLAYER"); - } else { - this.authMode = ServerAuthManager.AuthMode.EXTERNAL_SESSION; - LOGGER.at(Level.INFO).log("Auth mode: EXTERNAL_SESSION"); - } - - this.parseAndScheduleRefresh(); } else { - LOGGER.at(Level.INFO).log("No server tokens configured. Use /auth login to authenticate, or provide tokens via CLI/environment."); - } + boolean hasCliTokens = false; + String sessionTokenValue = null; + String identityTokenValue = null; + if (optionSet.has(Options.SESSION_TOKEN)) { + sessionTokenValue = optionSet.valueOf(Options.SESSION_TOKEN); + LOGGER.at(Level.INFO).log("Session token loaded from CLI"); + } else { + String envToken = System.getenv("HYTALE_SERVER_SESSION_TOKEN"); + if (envToken != null && !envToken.isEmpty()) { + sessionTokenValue = envToken; + LOGGER.at(Level.INFO).log("Session token loaded from environment"); + } + } - LOGGER.at(Level.INFO).log("Server session ID: %s", this.serverSessionId); - LOGGER.at(Level.FINE) - .log( - "ServerAuthManager initialized - session token: %s, identity token: %s, auth mode: %s", - this.hasSessionToken() ? "present" : "missing", - this.hasIdentityToken() ? "present" : "missing", - this.authMode - ); + if (optionSet.has(Options.IDENTITY_TOKEN)) { + identityTokenValue = optionSet.valueOf(Options.IDENTITY_TOKEN); + LOGGER.at(Level.INFO).log("Identity token loaded from CLI"); + } else { + String envTokenx = System.getenv("HYTALE_SERVER_IDENTITY_TOKEN"); + if (envTokenx != null && !envTokenx.isEmpty()) { + identityTokenValue = envTokenx; + LOGGER.at(Level.INFO).log("Identity token loaded from environment"); + } + } + + if (sessionTokenValue != null || identityTokenValue != null) { + if (this.validateInitialTokens(sessionTokenValue, identityTokenValue)) { + SessionServiceClient.GameSessionResponse session = new SessionServiceClient.GameSessionResponse(); + session.sessionToken = sessionTokenValue; + session.identityToken = identityTokenValue; + this.gameSession.set(session); + hasCliTokens = true; + } else { + this.pendingFatalError = Message.translation("client.disconnection.shutdownReason.authFailed.tokenValidationFailed"); + LOGGER.at(Level.SEVERE).log(this.pendingFatalError.getAnsiMessage()); + } + } + + if (hasCliTokens) { + if (this.isSingleplayer) { + this.authMode = ServerAuthManager.AuthMode.SINGLEPLAYER; + LOGGER.at(Level.INFO).log("Auth mode: SINGLEPLAYER"); + } else { + this.authMode = ServerAuthManager.AuthMode.EXTERNAL_SESSION; + LOGGER.at(Level.INFO).log("Auth mode: EXTERNAL_SESSION"); + } + + this.parseAndScheduleRefresh(); + } else { + LOGGER.at(Level.INFO).log("No server tokens configured. Use /auth login to authenticate, or provide tokens via CLI/environment."); + } + + LOGGER.at(Level.INFO).log("Server session ID: %s", this.serverSessionId); + LOGGER.at(Level.FINE) + .log( + "ServerAuthManager initialized - session token: %s, identity token: %s, auth mode: %s", + this.hasSessionToken() ? "present" : "missing", + this.hasIdentityToken() ? "present" : "missing", + this.authMode + ); + } } } @@ -188,11 +210,7 @@ public class ServerAuthManager { public void shutdown() { this.cancelActiveFlow(); - if (this.refreshTask != null) { - this.refreshTask.cancel(false); - } - - this.refreshScheduler.shutdown(); + this.refreshScheduler.shutdownNow(); if (this.isSingleplayer()) { String currentSessionToken = this.getSessionToken(); if (currentSessionToken != null && !currentSessionToken.isEmpty()) { @@ -477,6 +495,36 @@ public class ServerAuthManager { this.pendingAuthMode = null; } + private boolean validateOfflineToken(@Nonnull String offlineToken) { + if (this.sessionServiceClient == null) { + this.sessionServiceClient = new SessionServiceClient("https://sessions.hytale.com"); + } + + JWTValidator validator = new JWTValidator(this.sessionServiceClient, "https://sessions.hytale.com", ""); + JWTValidator.IdentityTokenClaims claims = validator.validateOfflineToken(offlineToken); + if (claims == null) { + LOGGER.at(Level.WARNING).log("Offline token validation failed"); + return false; + } else { + OptionSet optionSet = Options.getOptionSet(); + UUID expectedOwnerUuid = optionSet != null && optionSet.has(Options.OWNER_UUID) ? optionSet.valueOf(Options.OWNER_UUID) : null; + String expectedOwnerName = optionSet != null && optionSet.has(Options.OWNER_NAME) ? optionSet.valueOf(Options.OWNER_NAME) : null; + UUID tokenUuid = claims.getSubjectAsUUID(); + if (expectedOwnerUuid == null || tokenUuid != null && tokenUuid.equals(expectedOwnerUuid)) { + if (expectedOwnerName != null && claims.username != null && !claims.username.equals(expectedOwnerName)) { + LOGGER.at(Level.WARNING).log("Offline token username mismatch: token has '%s', expected '%s'", claims.username, expectedOwnerName); + return false; + } else { + LOGGER.at(Level.INFO).log("Offline token validated for %s (%s)", claims.username, claims.subject); + return true; + } + } else { + LOGGER.at(Level.WARNING).log("Offline token UUID mismatch: token has %s, expected %s", claims.subject, expectedOwnerUuid); + return false; + } + } + } + private boolean validateInitialTokens(@Nullable String sessionToken, @Nullable String identityToken) { if (sessionToken == null && identityToken == null) { return false; @@ -604,17 +652,49 @@ public class ServerAuthManager { LOGGER.at(Level.WARNING).log("No refresh token present to refresh OAuth tokens"); return false; } else { - LOGGER.at(Level.INFO).log("Refreshing OAuth tokens..."); - OAuthClient.TokenResponse newTokens = this.oauthClient.refreshTokens(refreshToken); - if (newTokens != null && newTokens.isSuccess()) { - store.setTokens( - new IAuthCredentialStore.OAuthTokens(newTokens.accessToken(), newTokens.refreshToken(), Instant.now().plusSeconds(newTokens.expiresIn())) - ); - return true; - } else { - LOGGER.at(Level.WARNING).log("OAuth token refresh failed"); - return false; + for (int attempt = 1; attempt <= 3; attempt++) { + if (attempt > 1) { + LOGGER.at(Level.INFO).log("Refreshing OAuth tokens (attempt %d/%d)...", attempt, 3); + } else { + LOGGER.at(Level.INFO).log("Refreshing OAuth tokens..."); + } + + try { + OAuthClient.TokenResponse newTokens = this.oauthClient.refreshTokens(refreshToken); + if (newTokens != null && newTokens.isSuccess()) { + store.setTokens( + new IAuthCredentialStore.OAuthTokens( + newTokens.accessToken(), newTokens.refreshToken(), Instant.now().plusSeconds(newTokens.expiresIn()) + ) + ); + return true; + } + + LOGGER.at(Level.WARNING).log("OAuth token refresh rejected by server"); + return false; + } catch (InterruptedException var12) { + Thread.currentThread().interrupt(); + LOGGER.at(Level.WARNING).log("OAuth token refresh interrupted"); + return false; + } catch (IOException var13) { + if (attempt < 3) { + long delay = 30L * (1L << attempt - 1); + LOGGER.at(Level.WARNING).log("OAuth token refresh IO error (attempt %d/%d), retrying in %d seconds...", attempt, 3, delay); + + try { + Thread.sleep(delay * 1000L); + } catch (InterruptedException var11) { + Thread.currentThread().interrupt(); + LOGGER.at(Level.WARNING).log("OAuth token refresh retry interrupted"); + return false; + } + } else { + LOGGER.at(Level.WARNING).log("OAuth token refresh failed after %d attempts due to IO errors", 3); + } + } } + + return false; } } } @@ -758,43 +838,66 @@ public class ServerAuthManager { if (secondsUntilExpiry > 300L) { long refreshDelay = Math.max(secondsUntilExpiry - 300L, 60L); LOGGER.at(Level.INFO).log("Token refresh scheduled in %d seconds", refreshDelay); - this.refreshTask = this.refreshScheduler.schedule(this::doRefresh, refreshDelay, TimeUnit.SECONDS); + this.refreshTask = this.refreshScheduler.schedule(() -> this.attemptSessionRefresh(1), refreshDelay, TimeUnit.SECONDS); } } - private void doRefresh() { + private void attemptSessionRefresh(int attempt) { String currentSessionToken = this.getSessionToken(); - if (currentSessionToken == null || !this.refreshGameSession(currentSessionToken)) { - LOGGER.at(Level.INFO).log("Game session refresh failed, attempting OAuth refresh..."); - if (!this.refreshGameSessionViaOAuth()) { - LOGGER.at(Level.WARNING).log("All refresh attempts failed. Server may lose authentication."); + if (currentSessionToken != null) { + if (attempt > 1) { + LOGGER.at(Level.INFO).log("Refreshing game session with Session Service (attempt %d/%d)...", attempt, 3); + } else { + LOGGER.at(Level.INFO).log("Refreshing game session with Session Service..."); } + + try { + if (this.refreshGameSession(currentSessionToken)) { + return; + } + } catch (CompletionException var6) { + if (var6.getCause() instanceof IOException && attempt < 3) { + long delay = 30L * (1L << attempt - 1); + LOGGER.at(Level.WARNING).log("Game session refresh IO error (attempt %d/%d), retrying in %d seconds...", attempt, 3, delay); + this.refreshTask = this.refreshScheduler.schedule(() -> this.attemptSessionRefresh(attempt + 1), delay, TimeUnit.SECONDS); + return; + } + + if (var6.getCause() instanceof IOException) { + LOGGER.at(Level.WARNING).log("Game session refresh failed after %d attempts due to IO errors", 3); + } else { + LOGGER.at(Level.WARNING).log("Session Service refresh failed: %s", var6.getMessage()); + } + } catch (Exception var7) { + LOGGER.at(Level.WARNING).log("Session Service refresh failed: %s", var7.getMessage()); + } + } + + LOGGER.at(Level.INFO).log("Game session refresh failed, attempting OAuth refresh..."); + if (!this.refreshGameSessionViaOAuth()) { + LOGGER.at(Level.WARNING).log("All refresh attempts failed. Server may lose authentication."); } } private boolean refreshGameSession(String currentSessionToken) { - LOGGER.at(Level.INFO).log("Refreshing game session with Session Service..."); if (this.sessionServiceClient == null) { this.sessionServiceClient = new SessionServiceClient("https://sessions.hytale.com"); } - try { - SessionServiceClient.GameSessionResponse response = this.sessionServiceClient.refreshSessionAsync(currentSessionToken).join(); - if (response != null) { - this.gameSession.set(response); - Instant effectiveExpiry = this.getEffectiveExpiry(response); - if (effectiveExpiry != null) { - this.setExpiryAndScheduleRefresh(effectiveExpiry); - } - - LOGGER.at(Level.INFO).log("Game session refresh successful"); - return true; + SessionServiceClient.GameSessionResponse response = this.sessionServiceClient.refreshSessionAsync(currentSessionToken).join(); + if (response == null) { + LOGGER.at(Level.WARNING).log("Game session refresh rejected by server"); + return false; + } else { + this.gameSession.set(response); + Instant effectiveExpiry = this.getEffectiveExpiry(response); + if (effectiveExpiry != null) { + this.setExpiryAndScheduleRefresh(effectiveExpiry); } - } catch (Exception var4) { - LOGGER.at(Level.WARNING).log("Session Service refresh failed: %s", var4.getMessage()); - } - return false; + LOGGER.at(Level.INFO).log("Game session refresh successful"); + return true; + } } private boolean refreshGameSessionViaOAuth() { @@ -830,8 +933,9 @@ public class ServerAuthManager { } static { - AuthCredentialStoreProvider.CODEC.register(Priority.DEFAULT, "Memory", MemoryAuthCredentialStoreProvider.class, MemoryAuthCredentialStoreProvider.CODEC); - AuthCredentialStoreProvider.CODEC.register("Encrypted", EncryptedAuthCredentialStoreProvider.class, EncryptedAuthCredentialStoreProvider.CODEC); + AuthCredentialStoreProvider.CODEC.register("Memory", MemoryAuthCredentialStoreProvider.class, MemoryAuthCredentialStoreProvider.CODEC); + AuthCredentialStoreProvider.CODEC + .register(Priority.DEFAULT, "Encrypted", EncryptedAuthCredentialStoreProvider.class, EncryptedAuthCredentialStoreProvider.CODEC); } public static enum AuthMode { diff --git a/src/com/hypixel/hytale/server/core/auth/SessionServiceClient.java b/src/com/hypixel/hytale/server/core/auth/SessionServiceClient.java index b79298f1..52d42410 100644 --- a/src/com/hypixel/hytale/server/core/auth/SessionServiceClient.java +++ b/src/com/hypixel/hytale/server/core/auth/SessionServiceClient.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.util.RawJsonReader; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.server.core.util.ServiceHttpClientFactory; +import com.hypixel.hytale.sneakythrow.SneakyThrow; import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -217,6 +218,7 @@ public class SessionServiceClient { } } + @Nullable public SessionServiceClient.GameSessionResponse createGameSession(@Nonnull String oauthAccessToken, @Nonnull UUID profileUuid) { try { String body = String.format("{\"uuid\":\"%s\"}", profileUuid.toString()); @@ -271,9 +273,14 @@ public class SessionServiceClient { .build(); LOGGER.at(Level.INFO).log("Refreshing game session..."); HttpResponse response = this.httpClient.send(request, BodyHandlers.ofString()); - if (response.statusCode() != 200) { - LOGGER.at(Level.WARNING).log("Failed to refresh session: HTTP %d - %s", response.statusCode(), response.body()); - return null; + int statusCode = response.statusCode(); + if (statusCode != 200) { + LOGGER.at(Level.WARNING).log("Failed to refresh session: HTTP %d - %s", statusCode, response.body()); + if (!AuthConfig.isRejectedStatusCode(statusCode)) { + throw new HttpResponseException(statusCode, response.body()); + } else { + return null; + } } else { SessionServiceClient.GameSessionResponse sessionResponse = SessionServiceClient.GameSessionResponse.CODEC .decodeJson(new RawJsonReader(response.body().toCharArray()), EmptyExtraInfo.EMPTY); @@ -285,15 +292,15 @@ public class SessionServiceClient { return null; } } - } catch (IOException var5) { - LOGGER.at(Level.WARNING).log("IO error while refreshing session: %s", var5.getMessage()); - return null; - } catch (InterruptedException var6) { + } catch (IOException var6) { + LOGGER.at(Level.WARNING).log("IO error while refreshing session: %s", var6.getMessage()); + throw SneakyThrow.sneakyThrow(var6); + } catch (InterruptedException var7) { LOGGER.at(Level.WARNING).log("Request interrupted while refreshing session"); Thread.currentThread().interrupt(); return null; - } catch (Exception var7) { - LOGGER.at(Level.WARNING).log("Unexpected error refreshing session: %s", var7.getMessage()); + } catch (Exception var8) { + LOGGER.at(Level.WARNING).log("Unexpected error refreshing session: %s", var8.getMessage()); return null; } }, @@ -385,6 +392,7 @@ public class SessionServiceClient { .add() .build(); + @Nullable public Instant getExpiresAtInstant() { if (this.expiresAt == null) { return null; diff --git a/src/com/hypixel/hytale/server/core/auth/oauth/OAuthClient.java b/src/com/hypixel/hytale/server/core/auth/oauth/OAuthClient.java index 58b144cf..5da984e0 100644 --- a/src/com/hypixel/hytale/server/core/auth/oauth/OAuthClient.java +++ b/src/com/hypixel/hytale/server/core/auth/oauth/OAuthClient.java @@ -6,8 +6,10 @@ import com.google.gson.JsonParser; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.auth.AuthConfig; +import com.hypixel.hytale.server.core.auth.HttpResponseException; import com.hypixel.hytale.server.core.util.ServiceHttpClientFactory; import com.sun.net.httpserver.HttpServer; +import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.URI; @@ -190,7 +192,7 @@ public class OAuthClient { } @Nullable - public OAuthClient.TokenResponse refreshTokens(@Nonnull String refreshToken) { + public OAuthClient.TokenResponse refreshTokens(@Nonnull String refreshToken) throws IOException, InterruptedException { try { String body = "grant_type=refresh_token&client_id=" + URLEncoder.encode("hytale-server", StandardCharsets.UTF_8) @@ -203,14 +205,25 @@ public class OAuthClient { .POST(BodyPublishers.ofString(body)) .build(); HttpResponse response = this.httpClient.send(request, BodyHandlers.ofString()); - if (response.statusCode() != 200) { - LOGGER.at(Level.WARNING).log("Token refresh failed: HTTP %d - %s", response.statusCode(), response.body()); - return null; + int statusCode = response.statusCode(); + if (statusCode != 200) { + LOGGER.at(Level.WARNING).log("Token refresh failed: HTTP %d - %s", statusCode, response.body()); + if (!AuthConfig.isRejectedStatusCode(statusCode)) { + throw new HttpResponseException(statusCode, response.body()); + } else { + return null; + } } else { return this.parseTokenResponse(response.body()); } - } catch (Exception var5) { - LOGGER.at(Level.WARNING).withCause(var5).log("Token refresh failed"); + } catch (IOException var6) { + LOGGER.at(Level.WARNING).withCause(var6).log("Token refresh failed (IO error)"); + throw var6; + } catch (InterruptedException var7) { + Thread.currentThread().interrupt(); + throw var7; + } catch (Exception var8) { + LOGGER.at(Level.WARNING).withCause(var8).log("Token refresh failed"); return null; } } diff --git a/src/com/hypixel/hytale/server/core/blocktype/BlockTypeModule.java b/src/com/hypixel/hytale/server/core/blocktype/BlockTypeModule.java index 914477ef..e06e147d 100644 --- a/src/com/hypixel/hytale/server/core/blocktype/BlockTypeModule.java +++ b/src/com/hypixel/hytale/server/core/blocktype/BlockTypeModule.java @@ -11,19 +11,13 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.component.dependency.Dependency; -import com.hypixel.hytale.component.dependency.Order; -import com.hypixel.hytale.component.dependency.RootDependency; -import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BenchType; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Bench; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.CraftingBench; import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.DiagramCraftingBench; @@ -32,7 +26,6 @@ import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Structur import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; import com.hypixel.hytale.server.core.modules.LegacyModule; import com.hypixel.hytale.server.core.modules.item.ItemModule; -import com.hypixel.hytale.server.core.modules.migrations.ChunkColumnMigrationSystem; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.world.World; @@ -44,14 +37,10 @@ import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkFlag; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; -import com.hypixel.hytale.server.core.universe.world.chunk.systems.ChunkSystems; import com.hypixel.hytale.server.core.universe.world.events.ChunkPreLoadProcessEvent; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import java.util.Arrays; -import java.util.Set; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -88,59 +77,15 @@ public class BlockTypeModule extends JavaPlugin { Bench.CODEC.register(BenchType.DiagramCrafting, DiagramCraftingBench.class, DiagramCraftingBench.CODEC); Bench.CODEC.register(BenchType.StructuralCrafting, StructuralCraftingBench.class, StructuralCraftingBench.CODEC); this.blockPhysicsComponentType = this.getChunkStoreRegistry().registerComponent(BlockPhysics.class, "BlockPhysics", BlockPhysics.CODEC); - this.getChunkStoreRegistry().registerSystem(new BlockTypeModule.MigrateLegacySections()); } public ComponentType getBlockPhysicsComponentType() { return this.blockPhysicsComponentType; } - @Deprecated - private static void onChunkPreLoadProcessEnsureBlockState(@Nonnull ChunkPreLoadProcessEvent event) { - if (event.isNewlyGenerated()) { - BlockTypeAssetMap blockTypeAssetMap = BlockType.getAssetMap(); - Holder holder = event.getHolder(); - WorldChunk chunk = event.getChunk(); - ChunkColumn column = holder.getComponent(ChunkColumn.getComponentType()); - if (column != null) { - Holder[] sections = column.getSectionHolders(); - if (sections != null) { - for (int sectionIndex = 0; sectionIndex < 10; sectionIndex++) { - BlockSection section = sections[sectionIndex].ensureAndGetComponent(BlockSection.getComponentType()); - if (!section.isSolidAir()) { - int sectionYBlock = sectionIndex << 5; - - for (int sectionY = 0; sectionY < 32; sectionY++) { - int y = sectionYBlock | sectionY; - - for (int x = 0; x < 32; x++) { - for (int z = 0; z < 32; z++) { - int blockId = section.get(x, y, z); - BlockType blockType = blockTypeAssetMap.getAsset(blockId); - if (blockType != null && !blockType.isUnknown() && section.getFiller(x, y, z) == 0) { - StateData state = blockType.getState(); - if (state != null && state.getId() != null && chunk.getState(x, y, z) == null) { - Vector3i position = new Vector3i(x, y, z); - BlockState blockState = BlockStateModule.get().createBlockState(state.getId(), chunk, position, blockType); - if (blockState != null) { - chunk.setState(x, y, z, blockState); - } - } - } - } - } - } - } - } - } - } - } - } - private static void onChunkPreLoadProcess(@Nonnull ChunkPreLoadProcessEvent event) { if (event.isNewlyGenerated()) { WorldChunk chunk = event.getChunk(); - BlockChunk blockChunk = chunk.getBlockChunk(); Holder holder = event.getHolder(); ChunkColumn column = holder.getComponent(ChunkColumn.getComponentType()); if (column != null) { @@ -459,57 +404,4 @@ public class BlockTypeModule extends JavaPlugin { ) { } } - - @Deprecated(forRemoval = true) - private static class MigrateLegacySections extends ChunkColumnMigrationSystem { - private final Query QUERY = Query.and(ChunkColumn.getComponentType(), BlockChunk.getComponentType()); - private final Set> DEPENDENCIES = Set.of( - new SystemDependency<>(Order.BEFORE, LegacyModule.MigrateLegacySections.class), - new SystemDependency<>(Order.AFTER, ChunkSystems.OnNewChunk.class), - RootDependency.first() - ); - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - ChunkColumn column = holder.getComponent(ChunkColumn.getComponentType()); - - assert column != null; - - BlockChunk blockChunk = holder.getComponent(BlockChunk.getComponentType()); - - assert blockChunk != null; - - Holder[] sections = column.getSectionHolders(); - BlockSection[] legacySections = blockChunk.getMigratedSections(); - if (legacySections != null) { - for (int i = 0; i < sections.length; i++) { - Holder section = sections[i]; - BlockSection paletteSection = legacySections[i]; - if (section != null && paletteSection != null) { - BlockPhysics phys = paletteSection.takeMigratedDecoBlocks(); - if (phys != null) { - section.putComponent(BlockPhysics.getComponentType(), phys); - blockChunk.markNeedsSaving(); - } - } - } - } - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return this.QUERY; - } - - @Nonnull - @Override - public Set> getDependencies() { - return this.DEPENDENCIES; - } - } } diff --git a/src/com/hypixel/hytale/server/core/blocktype/component/BlockPhysics.java b/src/com/hypixel/hytale/server/core/blocktype/component/BlockPhysics.java index 1520f729..4adfac4f 100644 --- a/src/com/hypixel/hytale/server/core/blocktype/component/BlockPhysics.java +++ b/src/com/hypixel/hytale/server/core/blocktype/component/BlockPhysics.java @@ -161,8 +161,8 @@ public class BlockPhysics implements Component { } } - public static void clear(@Nonnull Store store, @Nonnull Ref section, int x, int y, int z) { - BlockPhysics blockPhysics = store.getComponent(section, getComponentType()); + public static void clear(@Nonnull ComponentAccessor accessor, @Nonnull Ref section, int x, int y, int z) { + BlockPhysics blockPhysics = accessor.getComponent(section, getComponentType()); if (blockPhysics != null) { blockPhysics.set(ChunkUtil.indexBlock(x, y, z), 0); } @@ -175,10 +175,10 @@ public class BlockPhysics implements Component { } } - public static void reset(@Nonnull Store store, @Nonnull Ref section, int x, int y, int z) { - BlockPhysics blockPhysics = store.getComponent(section, getComponentType()); + public static void reset(@Nonnull ComponentAccessor accessor, @Nonnull Ref section, int x, int y, int z) { + BlockPhysics blockPhysics = accessor.getComponent(section, getComponentType()); if (blockPhysics == null) { - blockPhysics = store.ensureAndGetComponent(section, getComponentType()); + blockPhysics = accessor.ensureAndGetComponent(section, getComponentType()); } blockPhysics.set(ChunkUtil.indexBlock(x, y, z), 0); diff --git a/src/com/hypixel/hytale/server/core/codec/ProtocolCodecs.java b/src/com/hypixel/hytale/server/core/codec/ProtocolCodecs.java index 519d6605..9e14e39d 100644 --- a/src/com/hypixel/hytale/server/core/codec/ProtocolCodecs.java +++ b/src/com/hypixel/hytale/server/core/codec/ProtocolCodecs.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.schema.metadata.HytaleType; import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.protocol.AccumulationMode; import com.hypixel.hytale.protocol.ChangeStatBehaviour; import com.hypixel.hytale.protocol.ChangeVelocityType; @@ -30,13 +31,12 @@ import com.hypixel.hytale.protocol.SavedMovementStates; import com.hypixel.hytale.protocol.Size; import com.hypixel.hytale.protocol.UVMotion; import com.hypixel.hytale.protocol.UVMotionCurveType; -import com.hypixel.hytale.protocol.Vector2f; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.asset.common.BlockyAnimationCache; import com.hypixel.hytale.server.core.asset.common.CommonAssetValidator; import com.hypixel.hytale.server.core.asset.util.ColorParseUtil; import com.hypixel.hytale.server.core.codec.protocol.ColorAlphaCodec; import com.hypixel.hytale.server.core.codec.protocol.ColorCodec; +import org.joml.Vector3f; public final class ProtocolCodecs { public static final BuilderCodec DIRECTION = BuilderCodec.builder(Direction.class, Direction::new) @@ -48,22 +48,6 @@ public final class ProtocolCodecs { .appendInherited(new KeyedCodec<>("Roll", Codec.FLOAT), (o, i) -> o.roll = i, o -> o.roll, (o, p) -> o.roll = p.roll) .add() .build(); - public static final BuilderCodec VECTOR2F = BuilderCodec.builder(Vector2f.class, Vector2f::new) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.FLOAT), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.FLOAT), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) - .add() - .build(); - public static final BuilderCodec VECTOR3F = BuilderCodec.builder(Vector3f.class, Vector3f::new) - .metadata(UIDisplayMode.COMPACT) - .appendInherited(new KeyedCodec<>("X", Codec.FLOAT), (o, i) -> o.x = i, o -> o.x, (o, p) -> o.x = p.x) - .add() - .appendInherited(new KeyedCodec<>("Y", Codec.FLOAT), (o, i) -> o.y = i, o -> o.y, (o, p) -> o.y = p.y) - .add() - .appendInherited(new KeyedCodec<>("Z", Codec.FLOAT), (o, i) -> o.z = i, o -> o.z, (o, p) -> o.z = p.z) - .add() - .build(); public static final BuilderCodec COLOR_LIGHT = BuilderCodec.builder(ColorLight.class, ColorLight::new) .appendInherited(new KeyedCodec<>("Color", Codec.STRING), ColorParseUtil::hexStringToColorLightDirect, ColorParseUtil::colorLightToHexString, (o, p) -> { o.red = p.red; @@ -229,19 +213,27 @@ public final class ProtocolCodecs { .documentKey(ChangeVelocityType.Add, "Adds the velocity to any existing velocity") .documentKey(ChangeVelocityType.Set, "Changes the velocity to the given value. Overriding existing values."); public static final BuilderCodec RAIL_POINT_CODEC = BuilderCodec.builder(RailPoint.class, RailPoint::new) - .appendInherited(new KeyedCodec<>("Point", VECTOR3F), (o, v) -> o.point = v, o -> o.point, (o, p) -> o.point = p.point) + .appendInherited( + new KeyedCodec<>("Point", Vector3fUtil.CODEC), + (o, v) -> o.point = v, + o -> new Vector3f(o.point.x(), o.point.y(), o.point.z()), + (o, p) -> o.point = p.point + ) .addValidator(Validators.nonNull()) .add() - .appendInherited(new KeyedCodec<>("Normal", VECTOR3F), (o, v) -> o.normal = v, o -> o.normal, (o, p) -> o.normal = p.normal) + .appendInherited( + new KeyedCodec<>("Normal", Vector3fUtil.CODEC), + (o, v) -> o.normal = v, + o -> new Vector3f(o.normal.x(), o.normal.y(), o.normal.z()), + (o, p) -> o.normal = p.normal + ) .addValidator(Validators.nonNull()) .add() .afterDecode(o -> { if (o.normal != null) { - com.hypixel.hytale.math.vector.Vector3f v = new com.hypixel.hytale.math.vector.Vector3f(o.normal.x, o.normal.y, o.normal.z); - v = v.normalize(); - o.normal.x = v.x; - o.normal.y = v.y; - o.normal.z = v.z; + Vector3f v = new Vector3f(o.normal.x(), o.normal.y(), o.normal.z()); + v.normalize(); + o.normal = v; } }) .build(); diff --git a/src/com/hypixel/hytale/server/core/codec/ShapeCodecs.java b/src/com/hypixel/hytale/server/core/codec/ShapeCodecs.java index 2153f86b..53242bb7 100644 --- a/src/com/hypixel/hytale/server/core/codec/ShapeCodecs.java +++ b/src/com/hypixel/hytale/server/core/codec/ShapeCodecs.java @@ -9,13 +9,13 @@ import com.hypixel.hytale.math.shape.Cylinder; import com.hypixel.hytale.math.shape.Ellipsoid; import com.hypixel.hytale.math.shape.OriginShape; import com.hypixel.hytale.math.shape.Shape; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; public class ShapeCodecs { public static final CodecMapCodec SHAPE = new CodecMapCodec<>(); public static final BuilderCodec BOX = BuilderCodec.builder(Box.class, Box::new) - .addField(new KeyedCodec<>("Min", Vector3d.CODEC), (shape, min) -> shape.min.assign(min), shape -> shape.min) - .addField(new KeyedCodec<>("Max", Vector3d.CODEC), (shape, max) -> shape.max.assign(max), shape -> shape.max) + .addField(new KeyedCodec<>("Min", Vector3dUtil.CODEC), (shape, min) -> shape.min.set(min), shape -> shape.min) + .addField(new KeyedCodec<>("Max", Vector3dUtil.CODEC), (shape, max) -> shape.max.set(max), shape -> shape.max) .build(); public static final BuilderCodec ELLIPSOID = BuilderCodec.builder(Ellipsoid.class, Ellipsoid::new) .addField(new KeyedCodec<>("RadiusX", Codec.DOUBLE), (shape, radius) -> shape.radiusX = radius, shape -> shape.radiusX) @@ -30,7 +30,7 @@ public class ShapeCodecs { .addField(new KeyedCodec<>("Radius", Codec.DOUBLE), Cylinder::assign, shape -> null) .build(); public static final BuilderCodec> ORIGIN_SHAPE = BuilderCodec.builder(OriginShape.class, OriginShape::new) - .addField(new KeyedCodec<>("Origin", Vector3d.CODEC), (shape, origin) -> shape.origin.assign(origin), shape -> shape.origin) + .addField(new KeyedCodec<>("Origin", Vector3dUtil.CODEC), (shape, origin) -> shape.origin.set(origin), shape -> shape.origin) .addField(new KeyedCodec<>("Shape", SHAPE), (shape, childShape) -> shape.shape = (S)childShape, shape -> shape.shape) .build(); diff --git a/src/com/hypixel/hytale/server/core/command/commands/debug/DebugPlayerPositionCommand.java b/src/com/hypixel/hytale/server/core/command/commands/debug/DebugPlayerPositionCommand.java index 9e4b4b0e..faf99c74 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/debug/DebugPlayerPositionCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/debug/DebugPlayerPositionCommand.java @@ -2,9 +2,8 @@ package com.hypixel.hytale.server.core.command.commands.debug; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; @@ -17,6 +16,8 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DebugPlayerPositionCommand extends AbstractPlayerCommand { public DebugPlayerPositionCommand() { @@ -36,7 +37,7 @@ public class DebugPlayerPositionCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); + Rotation3f headRotation = headRotationComponent.getRotation(); Teleport teleport = store.getComponent(ref, Teleport.getComponentType()); PendingTeleport pendingTeleport = store.getComponent(ref, PendingTeleport.getComponentType()); String teleportFmt = teleport == null ? "none" : fmtPos(teleport.getPosition()); @@ -55,11 +56,11 @@ public class DebugPlayerPositionCommand extends AbstractPlayerCommand { private static String fmtPos(@Nonnull Vector3d vector) { String fmt = "%.1f"; - return String.format("%.1f", vector.getX()) + ", " + String.format("%.1f", vector.getY()) + ", " + String.format("%.1f", vector.getZ()); + return String.format("%.1f", vector.x()) + ", " + String.format("%.1f", vector.y()) + ", " + String.format("%.1f", vector.z()); } - private static String fmtRot(@Nonnull Vector3f vector) { - return "Pitch=" + fmtDegrees(vector.getPitch()) + ", Yaw=" + fmtDegrees(vector.getYaw()) + ", Roll=" + fmtDegrees(vector.getRoll()); + private static String fmtRot(@Nonnull Rotation3f vector) { + return "Pitch=" + fmtDegrees(vector.pitch()) + ", Yaw=" + fmtDegrees(vector.yaw()) + ", Roll=" + fmtDegrees(vector.roll()); } private static String fmtDegrees(float radians) { diff --git a/src/com/hypixel/hytale/server/core/command/commands/debug/packs/PacksListCommand.java b/src/com/hypixel/hytale/server/core/command/commands/debug/packs/PacksListCommand.java index 2623dca8..291280aa 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/debug/packs/PacksListCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/debug/packs/PacksListCommand.java @@ -7,7 +7,6 @@ import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase; import com.hypixel.hytale.server.core.util.message.MessageFormat; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.Comparator; import java.util.List; import javax.annotation.Nonnull; @@ -33,10 +32,7 @@ public class PacksListCommand extends CommandBase { context.sendMessage(MESSAGE_PACKS_NONE_LOADED); } else { ObjectArrayList packs = new ObjectArrayList<>(); - assetPacks.stream() - .sorted(Comparator.comparing(AssetPack::getName, String.CASE_INSENSITIVE_ORDER)) - .map(PacksListCommand::formatPack) - .forEach(packs::add); + assetPacks.stream().map(PacksListCommand::formatPack).forEach(packs::add); context.sendMessage(MessageFormat.list(Message.translation("server.commands.packs.listHeader"), packs)); } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/Bot.java b/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/Bot.java index 5c7a7a5f..b7113083 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/Bot.java +++ b/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/Bot.java @@ -1,8 +1,7 @@ package com.hypixel.hytale.server.core.command.commands.debug.stresstest; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.Asset; import com.hypixel.hytale.protocol.ComponentUpdate; import com.hypixel.hytale.protocol.Direction; @@ -16,13 +15,15 @@ import com.hypixel.hytale.protocol.TeleportAck; import com.hypixel.hytale.protocol.TransformUpdate; import com.hypixel.hytale.protocol.io.netty.PacketDecoder; import com.hypixel.hytale.protocol.io.netty.PacketEncoder; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnect; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnectReason; import com.hypixel.hytale.protocol.packets.connection.ClientType; import com.hypixel.hytale.protocol.packets.connection.Connect; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; import com.hypixel.hytale.protocol.packets.connection.DisconnectType; import com.hypixel.hytale.protocol.packets.connection.Ping; import com.hypixel.hytale.protocol.packets.connection.Pong; import com.hypixel.hytale.protocol.packets.connection.PongType; +import com.hypixel.hytale.protocol.packets.connection.ServerDisconnect; import com.hypixel.hytale.protocol.packets.entities.EntityUpdates; import com.hypixel.hytale.protocol.packets.player.ClientMovement; import com.hypixel.hytale.protocol.packets.player.ClientReady; @@ -35,6 +36,7 @@ import com.hypixel.hytale.server.core.cosmetics.CosmeticsModule; import com.hypixel.hytale.server.core.io.ServerManager; import com.hypixel.hytale.server.core.io.netty.NettyUtil; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; +import com.hypixel.hytale.server.core.util.MessageUtil; import com.hypixel.hytale.server.core.util.PositionUtil; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFutureListener; @@ -54,6 +56,7 @@ import java.net.InetSocketAddress; import java.net.SocketException; import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -63,6 +66,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Bot extends SimpleChannelInboundHandler { private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(8); @@ -81,17 +85,17 @@ public class Bot extends SimpleChannelInboundHandler { private SocketChannel channel; private int id = -1; private Vector3d pos; - private final Vector3f rotation = new Vector3f(); + private final Rotation3f rotation = new Rotation3f(); private final Vector3d destination = new Vector3d(); private final Vector3d temp = new Vector3d(); - private final Vector3f targetRotation = new Vector3f(); + private final Rotation3f targetRotation = new Rotation3f(); public Bot(String name, @Nonnull BotConfig config, int tickStepNanos) throws InterruptedException, SocketException { this.logger = HytaleLogger.get(name); this.name = name; this.config = config; - this.destination.assign(config.spawn.getPosition()); - this.destination.y = ThreadLocalRandom.current().nextDouble(config.flyYHeight.getX(), config.flyYHeight.getY()); + this.destination.set(config.spawn.getPosition()); + this.destination.y = ThreadLocalRandom.current().nextDouble(config.flyYHeight.x(), config.flyYHeight.y()); InetSocketAddress address = ServerManager.get().getLocalOrPublicAddress(); this.logger.at(Level.INFO).log("Booting Bot! Connecting to %s", address); new Bootstrap() @@ -99,6 +103,10 @@ public class Bot extends SimpleChannelInboundHandler { .channel(Epoll.isAvailable() ? EpollSocketChannel.class : (KQueue.isAvailable() ? KQueueSocketChannel.class : NioSocketChannel.class)) .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE) .handler(new ChannelInitializer() { + { + Objects.requireNonNull(Bot.this); + } + protected void initChannel(@Nonnull SocketChannel channel) { Bot.this.channel = channel; channel.pipeline().addLast("packetDecoder", new PacketDecoder()); @@ -146,21 +154,21 @@ public class Bot extends SimpleChannelInboundHandler { this.channel.flush(); } else { double movementDistance = this.config.flySpeed * dt; - if (this.pos.distanceSquaredTo(this.destination) <= movementDistance * movementDistance) { + if (this.pos.distanceSquared(this.destination) <= movementDistance * movementDistance) { ThreadLocalRandom random = ThreadLocalRandom.current(); double randX = random.nextDouble(-this.config.radius, this.config.radius); - double randY = random.nextDouble(this.config.flyYHeight.getX(), this.config.flyYHeight.getY()); + double randY = random.nextDouble(this.config.flyYHeight.x(), this.config.flyYHeight.y()); double randZ = random.nextDouble(-this.config.radius, this.config.radius); - this.destination.assign(this.config.spawn.getPosition()); + this.destination.set(this.config.spawn.getPosition()); this.destination.y = randY; this.destination.add(randX, 0.0, randZ); } - this.temp.assign(this.destination).subtract(this.pos); - Vector3f.lookAt(this.temp, this.targetRotation); - Vector3f.lerpAngle(this.rotation, this.targetRotation, 0.3F, this.rotation); + this.temp.set(this.destination).sub(this.pos); + Rotation3f.lookAt(this.temp, this.targetRotation); + Rotation3f.lerpAngle(this.rotation, this.targetRotation, 0.3F, this.rotation); this.temp.normalize(); - this.temp.scale(movementDistance); + this.temp.mul(movementDistance); this.pos.add(this.temp); this.movementStates.flying = true; this.channel.writeAndFlush(this.createMovementPacket()); @@ -170,7 +178,7 @@ public class Bot extends SimpleChannelInboundHandler { @Override public void channelActive(@Nonnull ChannelHandlerContext ctx) { UUID uuid = UUID.nameUUIDFromBytes(("BOT|" + this.name).getBytes(StandardCharsets.UTF_8)); - ctx.writeAndFlush(new Connect(-1356075132, 20, "bot", ClientType.Game, uuid, this.name, null, "en", null, null)); + ctx.writeAndFlush(new Connect(1608127164, 63, "bot", ClientType.Game, uuid, this.name, null, "en", null, null)); this.logger.at(Level.INFO).log("Connected!"); } @@ -185,7 +193,7 @@ public class Bot extends SimpleChannelInboundHandler { public void exceptionCaught(@Nonnull ChannelHandlerContext ctx, @Nonnull Throwable cause) { this.logger.at(Level.WARNING).withCause(cause).log("Got exception from netty pipeline"); if (ctx.channel().isWritable()) { - ctx.channel().writeAndFlush(new Disconnect(cause.getMessage(), DisconnectType.Crash)).addListener(ChannelFutureListener.CLOSE); + ctx.channel().writeAndFlush(new ClientDisconnect(ClientDisconnectReason.Crash, DisconnectType.Crash)).addListener(ChannelFutureListener.CLOSE); } else { ctx.channel().close(); } @@ -196,11 +204,14 @@ public class Bot extends SimpleChannelInboundHandler { public void channelRead0(@Nonnull ChannelHandlerContext ctx, @Nonnull Packet packet) { switch (packet.getId()) { - case 1: - this.logger.at(Level.INFO).log("Disconnected for: %s %s", ((Disconnect)packet).reason, ((Disconnect)packet).type); + case 2: + ServerDisconnect disconnect = (ServerDisconnect)packet; + this.logger + .at(Level.INFO) + .log("Disconnected for: %s %s", disconnect.reason != null ? MessageUtil.formatMessageToPlainString(disconnect.reason) : null, disconnect.type); ctx.close(); break; - case 2: + case 3: Ping ping = (Ping)packet; InstantData instantData = WorldTimeResource.instantToInstantData(Instant.now()); ctx.write(new Pong(ping.id, instantData, PongType.Raw, (short)0)); @@ -254,7 +265,7 @@ public class Bot extends SimpleChannelInboundHandler { this.pos = new Vector3d(); } - this.pos.assign(position.x, position.y, position.z); + this.pos.set(position.x, position.y, position.z); } Direction lookOrientation = modelTransform.lookOrientation; diff --git a/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/BotConfig.java b/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/BotConfig.java index c66b72f9..8604036d 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/BotConfig.java +++ b/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/BotConfig.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.server.core.command.commands.debug.stresstest; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector2d; +import org.joml.Vector2d; public class BotConfig { public final double radius; diff --git a/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/StressTestStartCommand.java b/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/StressTestStartCommand.java index 3ce66668..d08fb584 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/StressTestStartCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/debug/stresstest/StressTestStartCommand.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.event.EventRegistration; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector2d; import com.hypixel.hytale.metrics.MetricsRegistry; import com.hypixel.hytale.metrics.metric.HistoricMetric; import com.hypixel.hytale.server.core.HytaleServer; @@ -47,6 +46,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; public class StressTestStartCommand extends AbstractAsyncWorldCommand { @Nonnull @@ -282,7 +282,8 @@ public class StressTestStartCommand extends AbstractAsyncWorldCommand { } if (shutdown) { - HytaleServer.get().shutdownServer(ShutdownReason.SHUTDOWN.withMessage("Stress test finished!")); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.shutdown.stressTestFinished"); + HytaleServer.get().shutdownServer(ShutdownReason.SHUTDOWN.withMessage(reasonMessage)); } } else { BOTS.add(new Bot("bot-" + counter.getAndIncrement(), config, tickStepNanos)); diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/SudoCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/SudoCommand.java index 8aaaf796..054b0301 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/SudoCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/SudoCommand.java @@ -1,22 +1,18 @@ package com.hypixel.hytale.server.core.command.commands.player; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.NameMatching; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.CommandManager; -import com.hypixel.hytale.server.core.command.system.CommandUtil; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.Universe; -import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.List; +import java.util.Collection; import javax.annotation.Nonnull; public class SudoCommand extends CommandBase { @@ -24,62 +20,48 @@ public class SudoCommand extends CommandBase { private static final Message MESSAGE_COMMANDS_SU_INVALID_USAGE = Message.translation("server.commands.sudo.invalidusage"); @Nonnull private final RequiredArg playerArg = this.withRequiredArg("player", "server.commands.sudo.player.desc", ArgTypes.STRING); + @Nonnull + private final RequiredArg commandArg = this.withRequiredArg("command", "server.commands.sudo.command.desc", ArgTypes.GREEDY_STRING); public SudoCommand() { super("sudo", "server.commands.sudo.desc"); this.addAliases("su"); - this.setAllowsExtraArguments(true); } @Override protected void executeSync(@Nonnull CommandContext context) { String playerName = this.playerArg.get(context); - String inputString = context.getInputString(); - String rawArgs = CommandUtil.stripCommandName(inputString); - int commandIndex = rawArgs.indexOf(32); - if (commandIndex == -1) { + String commandToExecute = this.commandArg.get(context); + if (commandToExecute.isEmpty()) { context.sendMessage(MESSAGE_COMMANDS_SU_INVALID_USAGE); } else { - String commandToExecute = rawArgs.substring(commandIndex + 1).trim(); - if (commandToExecute.isEmpty()) { - context.sendMessage(MESSAGE_COMMANDS_SU_INVALID_USAGE); + if (commandToExecute.charAt(0) == '/') { + commandToExecute = commandToExecute.substring(1); + } + + Collection players; + if (playerName.equals("*")) { + players = Universe.get().getPlayers(); } else { - if (commandToExecute.charAt(0) == '/') { - commandToExecute = commandToExecute.substring(1); - } - - List players; - if (playerName.equals("*")) { - players = Universe.get().getPlayers(); - } else { - PlayerRef player = Universe.get().getPlayer(playerName, NameMatching.DEFAULT); - if (player == null) { - context.sendMessage(Message.translation("server.commands.errors.noSuchPlayer").param("username", playerName)); - return; - } - - players = new ObjectArrayList<>(); - players.add(player); - } - - if (players.isEmpty()) { + PlayerRef player = Universe.get().getPlayer(playerName, NameMatching.DEFAULT); + if (player == null) { context.sendMessage(Message.translation("server.commands.errors.noSuchPlayer").param("username", playerName)); - } else { - String finalCommand = commandToExecute; + return; + } - for (PlayerRef player : players) { - Ref ref = player.getReference(); - if (ref != null && ref.isValid()) { - Store store = ref.getStore(); - World world = store.getExternalData().getWorld(); - world.execute(() -> { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); + players = new ObjectArrayList<>(); + players.add(player); + } - assert playerComponent != null; + if (players.isEmpty()) { + context.sendMessage(Message.translation("server.commands.errors.noSuchPlayer").param("username", playerName)); + } else { + String finalCommand = commandToExecute; - CommandManager.get().handleCommand(playerComponent, finalCommand); - }); - } + for (PlayerRef playerRef : players) { + Ref ref = playerRef.getReference(); + if (ref != null && ref.isValid()) { + CommandManager.get().handleCommand(playerRef, finalCommand); } } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/WhereAmICommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/WhereAmICommand.java index 0b57886f..ebe3ab23 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/WhereAmICommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/WhereAmICommand.java @@ -5,9 +5,7 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -25,6 +23,8 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class WhereAmICommand extends AbstractPlayerCommand { @Nonnull @@ -56,13 +56,13 @@ public class WhereAmICommand extends AbstractPlayerCommand { assert headRotationComponent != null; Vector3d position = transformComponent.getPosition(); - Vector3f headRotation = headRotationComponent.getRotation(); + Rotation3f headRotation = headRotationComponent.getRotation(); Vector3i axisDirection = headRotationComponent.getAxisDirection(); Axis axis = headRotationComponent.getAxis(); Vector3d direction = headRotationComponent.getDirection(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkY = MathUtil.floor(position.getY()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkY = MathUtil.floor(position.y()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; long chunkIndex = ChunkUtil.indexChunk(chunkX, chunkZ); WorldChunk playerChunk = world.getChunkIfInMemory(chunkIndex); String headerKey = targetUsername != null ? "server.commands.whereami.header.other" : "server.commands.whereami.header"; @@ -72,12 +72,12 @@ public class WhereAmICommand extends AbstractPlayerCommand { .param("chunkX", chunkX) .param("chunkY", chunkY) .param("chunkZ", chunkZ) - .param("posX", position.getX()) - .param("posY", position.getY()) - .param("posZ", position.getZ()) - .param("yaw", headRotation.getYaw()) - .param("pitch", headRotation.getPitch()) - .param("roll", headRotation.getRoll()) + .param("posX", position.x()) + .param("posY", position.y()) + .param("posZ", position.z()) + .param("yaw", headRotation.yaw()) + .param("pitch", headRotation.pitch()) + .param("roll", headRotation.roll()) .param("direction", direction.toString()) .param("axisDirection", axisDirection.toString()) .param("axis", axis.toString()); diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/camera/CameraDemo.java b/src/com/hypixel/hytale/server/core/command/commands/player/camera/CameraDemo.java index 973a7a31..638b6fcb 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/camera/CameraDemo.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/camera/CameraDemo.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.math.iterator.BlockIterator; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.ClientCameraView; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.MouseButtonState; @@ -15,7 +14,6 @@ import com.hypixel.hytale.protocol.MovementForceRotationType; import com.hypixel.hytale.protocol.PositionDistanceOffsetType; import com.hypixel.hytale.protocol.RotationType; import com.hypixel.hytale.protocol.ServerCameraSettings; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.protocol.packets.camera.SetServerCamera; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -29,6 +27,8 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nonnull; +import org.joml.Vector3f; +import org.joml.Vector3i; public class CameraDemo { public static final CameraDemo INSTANCE = new CameraDemo(); @@ -92,9 +92,9 @@ public class CameraDemo { return true; }); } else { - int x = targetBlock.getX(); - int z = targetBlock.getZ(); - world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).setBlock(x, targetBlock.getY(), z, blockId); + int x = targetBlock.x(); + int z = targetBlock.z(); + world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).setBlock(x, targetBlock.y(), z, blockId); } } } else if (event.getMouseButton().mouseButtonType == MouseButtonType.Right) { @@ -104,9 +104,9 @@ public class CameraDemo { return true; }); } else { - int x = targetBlock.getX(); - int z = targetBlock.getZ(); - world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).setBlock(x, targetBlock.getY(), z, 0); + int x = targetBlock.x(); + int z = targetBlock.z(); + world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).setBlock(x, targetBlock.y(), z, 0); } } else if (event.getMouseButton().mouseButtonType == MouseButtonType.Left && event.getItemInHand() != null @@ -120,21 +120,21 @@ public class CameraDemo { if (!lastTargetBlock.equals(targetBlock)) { BlockIterator.iterateFromTo( - lastTargetBlock.getX(), - lastTargetBlock.getY() + 1, - lastTargetBlock.getZ(), - targetBlock.getX(), - targetBlock.getY() + 1, - targetBlock.getZ(), + lastTargetBlock.x(), + lastTargetBlock.y() + 1, + lastTargetBlock.z(), + targetBlock.x(), + targetBlock.y() + 1, + targetBlock.z(), (xx, y, zx, px, py, pz, qx, qy, qz) -> { world.getChunk(ChunkUtil.indexChunkFromBlock(xx, zx)).setBlock(xx, y, zx, blockId); return true; } ); } else { - int x = targetBlock.getX(); - int z = targetBlock.getZ(); - world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).setBlock(x, targetBlock.getY() + 1, z, blockIdx); + int x = targetBlock.x(); + int z = targetBlock.z(); + world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).setBlock(x, targetBlock.y() + 1, z, blockIdx); } } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/camera/PlayerCameraSideScrollerCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/camera/PlayerCameraSideScrollerCommand.java index d878fc40..9eb7b743 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/camera/PlayerCameraSideScrollerCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/camera/PlayerCameraSideScrollerCommand.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.protocol.MovementForceRotationType; import com.hypixel.hytale.protocol.PositionDistanceOffsetType; import com.hypixel.hytale.protocol.RotationType; import com.hypixel.hytale.protocol.ServerCameraSettings; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.protocol.packets.camera.SetServerCamera; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3f; public class PlayerCameraSideScrollerCommand extends AbstractTargetPlayerCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/camera/PlayerCameraTopdownCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/camera/PlayerCameraTopdownCommand.java index 4fa5bf2c..14be4ac6 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/camera/PlayerCameraTopdownCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/camera/PlayerCameraTopdownCommand.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.protocol.MovementForceRotationType; import com.hypixel.hytale.protocol.PositionDistanceOffsetType; import com.hypixel.hytale.protocol.RotationType; import com.hypixel.hytale.protocol.ServerCameraSettings; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.protocol.packets.camera.SetServerCamera; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3f; public class PlayerCameraTopdownCommand extends AbstractTargetPlayerCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/GiveArmorCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/GiveArmorCommand.java index 38863acc..17bcb565 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/GiveArmorCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/GiveArmorCommand.java @@ -11,7 +11,7 @@ import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalAr import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncCommand; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; @@ -20,9 +20,10 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceLists; import java.awt.Color; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -52,7 +53,7 @@ public class GiveArmorCommand extends AbstractAsyncCommand { if (this.playerArg.provided(context)) { String playerInput = this.playerArg.get(context); if ("*".equals(playerInput)) { - targets = new ObjectArrayList<>(); + targets = new ReferenceArrayList<>(); for (PlayerRef player : Universe.get().getPlayers()) { targets.add(player.getReference()); @@ -64,7 +65,7 @@ public class GiveArmorCommand extends AbstractAsyncCommand { return CompletableFuture.completedFuture(null); } - targets = Collections.singletonList(player.getReference()); + targets = ReferenceLists.singleton(player.getReference()); } } else { if (!context.isPlayer()) { @@ -72,7 +73,7 @@ public class GiveArmorCommand extends AbstractAsyncCommand { return CompletableFuture.completedFuture(null); } - targets = Collections.singletonList(context.senderAsPlayerRef()); + targets = ReferenceLists.singleton(context.senderAsPlayerRef()); } if (targets.isEmpty()) { @@ -97,7 +98,7 @@ public class GiveArmorCommand extends AbstractAsyncCommand { if (targetRef != null && targetRef.isValid()) { Store store = targetRef.getStore(); World world = store.getExternalData().getWorld(); - playersByWorld.computeIfAbsent(world, k -> new ObjectArrayList<>()).add(targetRef); + playersByWorld.computeIfAbsent(world, k -> new ReferenceArrayList<>()).add(targetRef); } } @@ -111,9 +112,9 @@ public class GiveArmorCommand extends AbstractAsyncCommand { for (Ref playerRef : worldPlayers) { if (playerRef != null && playerRef.isValid()) { Store storex = playerRef.getStore(); - Player targetPlayerComponent = storex.getComponent(playerRef, Player.getComponentType()); - if (targetPlayerComponent != null) { - ItemContainer armorInventory = targetPlayerComponent.getInventory().getArmor(); + InventoryComponent.Armor armorComponent = storex.getComponent(playerRef, InventoryComponent.Armor.getComponentType()); + if (armorComponent != null) { + ItemContainer armorInventory = armorComponent.getInventory(); if (shouldClear) { armorInventory.clear(); } diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryBackpackCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryBackpackCommand.java index cad8c464..45000a0e 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryBackpackCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryBackpackCommand.java @@ -8,8 +8,7 @@ import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalAr import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.entity.ItemUtils; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; @@ -29,27 +28,27 @@ public class InventoryBackpackCommand extends AbstractPlayerCommand { protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); + InventoryComponent.Backpack backpackInventoryComponent = store.getComponent(ref, InventoryComponent.Backpack.getComponentType()); + if (backpackInventoryComponent != null) { + if (!this.sizeArg.provided(context)) { + context.sendMessage( + Message.translation("server.commands.inventory.backpack.size").param("capacity", backpackInventoryComponent.getInventory().getCapacity()) + ); + } else { + short capacity = this.sizeArg.get(context).shortValue(); + ObjectArrayList remainder = new ObjectArrayList<>(); + backpackInventoryComponent.resize(capacity, remainder); - assert playerComponent != null; + for (ItemStack item : remainder) { + ItemUtils.dropItem(ref, item, store); + } - Inventory inventory = playerComponent.getInventory(); - if (!this.sizeArg.provided(context)) { - context.sendMessage(Message.translation("server.commands.inventory.backpack.size").param("capacity", inventory.getBackpack().getCapacity())); - } else { - short capacity = this.sizeArg.get(context).shortValue(); - ObjectArrayList remainder = new ObjectArrayList<>(); - inventory.resizeBackpack(capacity, remainder); - - for (ItemStack item : remainder) { - ItemUtils.dropItem(ref, item, store); + context.sendMessage( + Message.translation("server.commands.inventory.backpack.resized") + .param("capacity", backpackInventoryComponent.getInventory().getCapacity()) + .param("dropped", remainder.size()) + ); } - - context.sendMessage( - Message.translation("server.commands.inventory.backpack.resized") - .param("capacity", inventory.getBackpack().getCapacity()) - .param("dropped", remainder.size()) - ); } } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryClearCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryClearCommand.java index 54771237..54d327b3 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryClearCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryClearCommand.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -25,11 +25,12 @@ public class InventoryClearCommand extends AbstractPlayerCommand { protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); + InventoryComponent.getCombined(store, ref, InventoryComponent.EVERYTHING).clear(); + InventoryComponent.Tool toolComponent = store.getComponent(ref, InventoryComponent.Tool.getComponentType()); + if (toolComponent != null) { + toolComponent.getInventory().clear(); + } - assert playerComponent != null; - - playerComponent.getInventory().clear(); context.sendMessage(MESSAGE_COMMANDS_CLEARINV_SUCCESS); } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryItemCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryItemCommand.java index 858496ac..c327fe23 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryItemCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventoryItemCommand.java @@ -8,7 +8,7 @@ import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerWindow; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemStackItemContainer; @@ -31,22 +31,23 @@ public class InventoryItemCommand extends AbstractPlayerCommand { protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - Inventory inventory = playerComponent.getInventory(); - ItemContainer hotbar = inventory.getHotbar(); - byte activeHotbarSlot = inventory.getActiveHotbarSlot(); - ItemStack activeHotbarItem = inventory.getActiveHotbarItem(); - if (ItemStack.isEmpty(activeHotbarItem)) { - context.sendMessage(MESSAGE_COMMANDS_INVENTORY_ITEM_NO_ITEM_IN_HAND); - } else { - ItemStackItemContainer backpackInventory = ItemStackItemContainer.getContainer(hotbar, activeHotbarSlot); - if (backpackInventory != null && backpackInventory.getCapacity() != 0) { - playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, new ContainerWindow(backpackInventory)); + InventoryComponent.Hotbar hotbarComponent = store.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent != null) { + ItemContainer hotbar = hotbarComponent.getInventory(); + byte activeHotbarSlot = hotbarComponent.getActiveSlot(); + ItemStack activeHotbarItem = hotbarComponent.getActiveItem(); + if (ItemStack.isEmpty(activeHotbarItem)) { + context.sendMessage(MESSAGE_COMMANDS_INVENTORY_ITEM_NO_ITEM_IN_HAND); } else { - context.sendMessage(MESSAGE_COMMANDS_INVENTORY_ITEM_NO_CONTAINER_ON_ITEM); + ItemStackItemContainer backpackInventory = ItemStackItemContainer.getContainer(hotbar, activeHotbarSlot); + if (backpackInventory != null && backpackInventory.getCapacity() != 0) { + Player playerComponent = store.getComponent(ref, Player.getComponentType()); + if (playerComponent != null) { + playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, new ContainerWindow(backpackInventory)); + } + } else { + context.sendMessage(MESSAGE_COMMANDS_INVENTORY_ITEM_NO_CONTAINER_ON_ITEM); + } } } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventorySeeCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventorySeeCommand.java index fc2b7f36..a0c3973e 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventorySeeCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/InventorySeeCommand.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerWindow; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.DelegateItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; @@ -43,11 +44,10 @@ public class InventorySeeCommand extends AbstractPlayerCommand { Store targetStore = targetRef.getStore(); World targetWorld = targetStore.getExternalData().getWorld(); targetWorld.execute(() -> { - Player targetPlayerComponent = targetStore.getComponent(targetRef, Player.getComponentType()); - if (targetPlayerComponent == null) { + if (!targetRef.isValid()) { context.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_IN_WORLD); } else { - CombinedItemContainer targetInventory = targetPlayerComponent.getInventory().getCombinedHotbarFirst(); + CombinedItemContainer targetInventory = InventoryComponent.getCombined(targetStore, targetRef, InventoryComponent.HOTBAR_FIRST); ItemContainer targetItemContainer = targetInventory; if (!context.sender().hasPermission(HytalePermissions.fromCommand("invsee", "modify"))) { DelegateItemContainer delegateItemContainer = new DelegateItemContainer<>(targetInventory); diff --git a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/ItemStateCommand.java b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/ItemStateCommand.java index 5f9735de..fc0c83f0 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/player/inventory/ItemStateCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/player/inventory/ItemStateCommand.java @@ -8,8 +8,7 @@ import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; @@ -32,22 +31,20 @@ public class ItemStateCommand extends AbstractPlayerCommand { protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - Inventory inventory = playerComponent.getInventory(); - byte activeHotbarSlot = inventory.getActiveHotbarSlot(); - if (activeHotbarSlot == -1) { - context.sendMessage(MESSAGE_COMMANDS_ITEMSTATE_NO_ITEM); - } else { - ItemContainer hotbar = inventory.getHotbar(); - ItemStack item = hotbar.getItemStack(activeHotbarSlot); - if (item == null) { + InventoryComponent.Hotbar hotbarComponent = store.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent != null) { + byte activeHotbarSlot = hotbarComponent.getActiveSlot(); + if (activeHotbarSlot == -1) { context.sendMessage(MESSAGE_COMMANDS_ITEMSTATE_NO_ITEM); } else { - String state = this.stateArg.get(context); - hotbar.setItemStackForSlot(activeHotbarSlot, item.withState(state)); + ItemContainer hotbar = hotbarComponent.getInventory(); + ItemStack item = hotbar.getItemStack(activeHotbarSlot); + if (item == null) { + context.sendMessage(MESSAGE_COMMANDS_ITEMSTATE_NO_ITEM); + } else { + String state = this.stateArg.get(context); + hotbar.setItemStackForSlot(activeHotbarSlot, item.withState(state)); + } } } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/server/KickCommand.java b/src/com/hypixel/hytale/server/core/command/commands/server/KickCommand.java index 3715da6c..7134bcd5 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/server/KickCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/server/KickCommand.java @@ -19,7 +19,7 @@ public class KickCommand extends CommandBase { @Override protected void executeSync(@Nonnull CommandContext context) { PlayerRef playerToKick = this.playerArg.get(context); - playerToKick.getPacketHandler().disconnect("You were kicked."); + playerToKick.getPacketHandler().disconnect(Message.translation("server.general.disconnect.kick.reason")); context.sendMessage(Message.translation("server.commands.kick.success").param("username", playerToKick.getUsername())); } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/server/MaxPlayersCommand.java b/src/com/hypixel/hytale/server/core/command/commands/server/MaxPlayersCommand.java index 0478a8e4..a2ade93c 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/server/MaxPlayersCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/server/MaxPlayersCommand.java @@ -21,10 +21,18 @@ public class MaxPlayersCommand extends CommandBase { if (this.amountArg.provided(context)) { int maxPlayers = this.amountArg.get(context); HytaleServer.get().getConfig().setMaxPlayers(maxPlayers); - context.sendMessage(Message.translation("server.commands.maxplayers.set").param("maxPlayers", maxPlayers)); + if (maxPlayers > 0) { + context.sendMessage(Message.translation("server.commands.maxplayers.set").param("maxPlayers", maxPlayers)); + } else { + context.sendMessage(Message.translation("server.commands.maxplayers.setInfinite")); + } } else { int maxPlayers = HytaleServer.get().getConfig().getMaxPlayers(); - context.sendMessage(Message.translation("server.commands.maxplayers.get").param("maxPlayers", maxPlayers)); + if (maxPlayers > 0) { + context.sendMessage(Message.translation("server.commands.maxplayers.get").param("maxPlayers", maxPlayers)); + } else { + context.sendMessage(Message.translation("server.commands.maxplayers.getInfinite")); + } } } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/ConvertPrefabsCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/ConvertPrefabsCommand.java index 2c93c756..c3762fda 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/ConvertPrefabsCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/ConvertPrefabsCommand.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.server.core.command.commands.utility; import com.hypixel.hytale.common.util.PathUtil; +import com.hypixel.hytale.component.data.unknown.UnknownComponents; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -15,6 +16,7 @@ import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.WorldConfig; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.provider.EmptyChunkStorageProvider; import com.hypixel.hytale.server.core.universe.world.storage.resources.EmptyResourceStorageProvider; import com.hypixel.hytale.server.core.universe.world.worldgen.provider.DummyWorldGenProvider; @@ -55,6 +57,7 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { @Nonnull private final FlagArg entitiesFlag = this.withFlagArg("entities", "server.commands.convertprefabs.entities.desc"); private final FlagArg destructiveFlag = this.withFlagArg("destructive", "server.commands.convertprefabs.destructive.desc"); + private final FlagArg onlyUnknownFlag = this.withFlagArg("only-unknown", "server.commands.convertprefabs.onlyUnknown.desc"); @Nonnull private final OptionalArg pathArg = this.withOptionalArg("path", "server.commands.convertprefabs.path.desc", ArgTypes.STRING); @Nonnull @@ -74,6 +77,7 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { boolean relative = this.relativeFlag.get(context); boolean entities = this.entitiesFlag.get(context); boolean destructive = this.destructiveFlag.get(context); + boolean onlyUnknown = this.onlyUnknownFlag.get(context); World defaultWorld = Universe.get().getDefaultWorld(); if (defaultWorld == null) { context.sendMessage(MESSAGE_COMMANDS_CONVERT_PREFABS_DEFAULT_WORLD_NULL); @@ -89,7 +93,7 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { context.sendMessage(Message.translation("server.commands.convertprefabs.invalidPath")); return CompletableFuture.completedFuture(null); } else { - return this.convertPath(assetPath, blocks, filler, relative, entities, destructive, failed, skipped).thenApply(_v -> { + return this.convertPath(assetPath, blocks, filler, relative, entities, destructive, onlyUnknown, failed, skipped).thenApply(_v -> { this.sendCompletionMessages(context, assetPath, failed, skipped); return null; }); @@ -98,33 +102,39 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { return switch (storeOption) { case "server" -> { Path assetPath = PrefabStore.get().getServerPrefabsPath(); - yield this.convertPath(assetPath, blocks, filler, relative, entities, destructive, failed, skipped).thenApply(_v -> { + yield this.convertPath(assetPath, blocks, filler, relative, entities, destructive, onlyUnknown, failed, skipped).thenApply(_v -> { this.sendCompletionMessages(context, assetPath, failed, skipped); return null; }); } case "asset" -> { Path assetPath = PrefabStore.get().getAssetPrefabsPath(); - yield this.convertPath(assetPath, blocks, filler, relative, entities, destructive, failed, skipped).thenApply(_v -> { + yield this.convertPath(assetPath, blocks, filler, relative, entities, destructive, onlyUnknown, failed, skipped).thenApply(_v -> { this.sendCompletionMessages(context, assetPath, failed, skipped); return null; }); } case "worldgen" -> { Path assetPath = PrefabStore.get().getWorldGenPrefabsPath(); - yield this.convertPath(assetPath, blocks, filler, relative, entities, destructive, failed, skipped).thenApply(_v -> { + yield this.convertPath(assetPath, blocks, filler, relative, entities, destructive, onlyUnknown, failed, skipped).thenApply(_v -> { this.sendCompletionMessages(context, assetPath, failed, skipped); return null; }); } case "all" -> { Path assetPath = Path.of(""); - yield this.convertPath(PrefabStore.get().getWorldGenPrefabsPath(), blocks, filler, relative, entities, destructive, failed, skipped) - .thenCompose( - _v -> this.convertPath(PrefabStore.get().getServerPrefabsPath(), blocks, filler, relative, entities, destructive, failed, skipped) + yield this.convertPath( + PrefabStore.get().getWorldGenPrefabsPath(), blocks, filler, relative, entities, destructive, onlyUnknown, failed, skipped ) .thenCompose( - _v -> this.convertPath(PrefabStore.get().getAssetPrefabsPath(), blocks, filler, relative, entities, destructive, failed, skipped) + _v -> this.convertPath( + PrefabStore.get().getServerPrefabsPath(), blocks, filler, relative, entities, destructive, onlyUnknown, failed, skipped + ) + ) + .thenCompose( + _v -> this.convertPath( + PrefabStore.get().getAssetPrefabsPath(), blocks, filler, relative, entities, destructive, onlyUnknown, failed, skipped + ) ) .thenApply(_v -> { this.sendCompletionMessages(context, assetPath, failed, skipped); @@ -161,6 +171,7 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { boolean relative, boolean entities, boolean destructive, + boolean onlySerializeIfUnknown, @Nonnull List failed, @Nonnull List skipped ) { @@ -179,8 +190,8 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { try { conversionWorldFuture = universe.makeWorld("ConvertPrefabs-" + UUID.randomUUID(), Files.createTempDirectory("convertprefab"), config); - } catch (IOException var14) { - throw SneakyThrow.sneakyThrow(var14); + } catch (IOException var15) { + throw SneakyThrow.sneakyThrow(var15); } } @@ -197,7 +208,9 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { return CompletableFuture.completedFuture(null); } - e = this.processPrefabsInBatches(prefabPaths, blocks, filler, relative, entities, destructive, conversionWorldFuture, failed, skipped) + e = this.processPrefabsInBatches( + prefabPaths, blocks, filler, relative, entities, destructive, onlySerializeIfUnknown, conversionWorldFuture, failed, skipped + ) .thenApply(_v -> { if (conversionWorldFuture != null) { conversionWorldFuture.thenAccept(world -> Universe.get().removeWorld(world.getName())); @@ -208,8 +221,8 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { } return e; - } catch (IOException var16) { - throw SneakyThrow.sneakyThrow(var16); + } catch (IOException var17) { + throw SneakyThrow.sneakyThrow(var17); } } } @@ -222,6 +235,7 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { boolean relative, boolean entities, boolean destructive, + boolean onlySerializeIfUnknown, @Nullable CompletableFuture conversionWorldFuture, @Nonnull List failed, @Nonnull List skipped @@ -237,7 +251,9 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { } CompletableFuture[] batchFutures = batch.stream() - .map(path -> this.processPrefab(path, blocks, filler, relative, entities, destructive, conversionWorldFuture, failed, skipped)) + .map( + path -> this.processPrefab(path, blocks, filler, relative, entities, destructive, onlySerializeIfUnknown, conversionWorldFuture, failed, skipped) + ) .toArray(CompletableFuture[]::new); result = result.thenCompose(_v -> CompletableFuture.allOf(batchFutures)); } @@ -253,6 +269,7 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { boolean relative, boolean entities, boolean destructive, + boolean onlySerializeIfUnknown, @Nullable CompletableFuture conversionWorldFuture, @Nonnull List failed, @Nonnull List skipped @@ -270,15 +287,37 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { return prefab; }) - .thenCompose(prefab -> entities && conversionWorldFuture != null ? conversionWorldFuture.thenCompose(world -> CompletableFuture.runAsync(() -> { - try { - prefab.reserializeEntities(world.getEntityStore().getStore(), destructive); - } catch (IOException var4x) { - throw SneakyThrow.sneakyThrow(var4x); + .thenApply(prefab -> { + if (onlySerializeIfUnknown) { + boolean[] hasUnknown = new boolean[1]; + prefab.forEachBlock((x, y, z, block) -> { + if (block.holder() != null) { + UnknownComponents unknown = block.holder().getComponent(ChunkStore.REGISTRY.getUnknownComponentType()); + if (unknown != null && !unknown.getUnknownComponents().isEmpty()) { + hasUnknown[0] = true; + } + } + }); + if (!hasUnknown[0]) { + return null; + } } - }, world)).thenApply(_v -> prefab) : CompletableFuture.completedFuture(prefab)) + + return (BlockSelection)prefab; + }) .thenCompose( - prefab -> blocks && conversionWorldFuture != null + prefab -> prefab != null && entities && conversionWorldFuture != null + ? conversionWorldFuture.thenCompose(world -> CompletableFuture.runAsync(() -> { + try { + prefab.reserializeEntities(world.getEntityStore().getStore(), destructive); + } catch (IOException var4x) { + throw SneakyThrow.sneakyThrow(var4x); + } + }, world)).thenApply(_v -> prefab) + : CompletableFuture.completedFuture(prefab) + ) + .thenCompose( + prefab -> prefab != null && blocks && conversionWorldFuture != null ? conversionWorldFuture.thenCompose( world -> CompletableFuture.runAsync(() -> prefab.reserializeBlockStates(world.getChunkStore(), destructive), world) ) @@ -286,8 +325,12 @@ public class ConvertPrefabsCommand extends AbstractAsyncCommand { : CompletableFuture.completedFuture(prefab) ) .thenCompose(prefab -> { - BsonDocument newDocument = SelectionPrefabSerializer.serialize(prefab); - return BsonUtil.writeDocument(path, newDocument, false); + if (prefab == null) { + return CompletableFuture.completedFuture(null); + } else { + BsonDocument newDocument = SelectionPrefabSerializer.serialize(prefab); + return BsonUtil.writeDocument(path, newDocument, false); + } }) .exceptionally( throwable -> { diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/NotifyCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/NotifyCommand.java index 0b4736e4..87b4f556 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/NotifyCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/NotifyCommand.java @@ -3,67 +3,49 @@ package com.hypixel.hytale.server.core.command.commands.utility; import com.hypixel.hytale.protocol.packets.interface_.NotificationStyle; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; -import com.hypixel.hytale.server.core.command.system.CommandUtil; +import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase; import com.hypixel.hytale.server.core.util.NotificationUtil; import javax.annotation.Nonnull; public class NotifyCommand extends CommandBase { + @Nonnull + private final RequiredArg messageArg = this.withRequiredArg("message", "server.commands.notify.message.desc", ArgTypes.GREEDY_STRING); + public NotifyCommand() { super("notify", "server.commands.notify.desc"); - this.setAllowsExtraArguments(true); } @Override protected void executeSync(@Nonnull CommandContext context) { - String inputString = context.getInputString(); - String rawArgs = CommandUtil.stripCommandName(inputString).trim(); - if (rawArgs.isEmpty()) { - context.sendMessage(Message.translation("server.commands.parsing.error.wrongNumberRequiredParameters").param("expected", 1).param("actual", 0)); - } else { - String[] args = rawArgs.split("\\s+"); - if (args.length == 0) { - context.sendMessage(Message.translation("server.commands.parsing.error.wrongNumberRequiredParameters").param("expected", 1).param("actual", 0)); - } else { - NotificationStyle style = NotificationStyle.Default; - int messageStartIndex = 0; - if (args.length >= 2) { - String firstArg = args[0]; - if (!firstArg.startsWith("{")) { - try { - style = NotificationStyle.valueOf(firstArg.toUpperCase()); - messageStartIndex = 1; - } catch (IllegalArgumentException var12) { - } - } - } + String rawArgs = this.messageArg.get(context); + NotificationStyle style = NotificationStyle.Default; + String messageString = rawArgs; + int firstSpace = rawArgs.indexOf(32); + if (firstSpace > 0 && !rawArgs.startsWith("{")) { + String firstWord = rawArgs.substring(0, firstSpace); - StringBuilder messageBuilder = new StringBuilder(); - - for (int i = messageStartIndex; i < args.length; i++) { - if (i > messageStartIndex) { - messageBuilder.append(' '); - } - - messageBuilder.append(args[i]); - } - - String messageString = messageBuilder.toString(); - Message message; - if (messageString.startsWith("{")) { - try { - message = Message.parse(messageString); - } catch (IllegalArgumentException var11) { - context.sendMessage(Message.raw("Invalid formatted message: " + var11.getMessage())); - return; - } - } else { - message = Message.raw(messageString); - } - - Message senderName = Message.raw(context.sender().getDisplayName()); - NotificationUtil.sendNotificationToUniverse(message, senderName, "announcement", null, style); + try { + style = NotificationStyle.valueOf(firstWord.toUpperCase()); + messageString = rawArgs.substring(firstSpace + 1); + } catch (IllegalArgumentException var9) { } } + + Message message; + if (messageString.startsWith("{")) { + try { + message = Message.parse(messageString); + } catch (IllegalArgumentException var8) { + context.sendMessage(Message.raw("Invalid formatted message: " + var8.getMessage())); + return; + } + } else { + message = Message.raw(messageString); + } + + Message senderName = Message.raw(context.sender().getUsername()); + NotificationUtil.sendNotificationToUniverse(message, senderName, "announcement", null, style); } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/StashCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/StashCommand.java index 6a0ebcda..f180d561 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/StashCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/StashCommand.java @@ -4,25 +4,24 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; +import com.hypixel.hytale.server.core.modules.block.components.ItemContainerBlock; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class StashCommand extends AbstractPlayerCommand { @Nonnull @@ -43,7 +42,7 @@ public class StashCommand extends AbstractPlayerCommand { protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - ItemContainerState itemContainerState = this.getItemContainerState(ref, world, context, store); + ItemContainerBlock itemContainerState = this.getItemContainerState(ref, world, context, store); if (itemContainerState != null) { if (this.setArg.provided(context)) { String dropList = this.setArg.get(context); @@ -61,7 +60,7 @@ public class StashCommand extends AbstractPlayerCommand { } @Nullable - private ItemContainerState getItemContainerState( + private ItemContainerBlock getItemContainerState( @Nonnull Ref ref, @Nonnull World world, @Nonnull CommandContext context, @Nonnull ComponentAccessor componentAccessor ) { Vector3i block = TargetUtil.getTargetBlock(ref, 10.0, componentAccessor); @@ -78,7 +77,7 @@ public class StashCommand extends AbstractPlayerCommand { assert blockChunkComponent != null; - WorldChunk worldChunkComponent = chunkStoreStore.getComponent(chunkRef, WorldChunk.getComponentType()); + BlockComponentChunk worldChunkComponent = chunkStoreStore.getComponent(chunkRef, BlockComponentChunk.getComponentType()); assert worldChunkComponent != null; @@ -90,12 +89,18 @@ public class StashCommand extends AbstractPlayerCommand { block.z = block.z - FillerBlockUtil.unpackZ(filler); } - BlockState state = worldChunkComponent.getState(block.x, block.y, block.z); - if (!(state instanceof ItemContainerState)) { + Ref state = worldChunkComponent.getEntityReference(ChunkUtil.indexBlockInColumn(block.x, block.y, block.z)); + if (state == null) { context.sendMessage(Message.translation("server.general.containerNotFound").param("block", block.toString())); return null; } else { - return (ItemContainerState)state; + ItemContainerBlock blockItemComponent = state.getStore().getComponent(state, ItemContainerBlock.getComponentType()); + if (blockItemComponent == null) { + context.sendMessage(Message.translation("server.general.containerNotFound").param("block", block.toString())); + return null; + } else { + return blockItemComponent; + } } } else { int chunkX = ChunkUtil.chunkCoordinate(block.x); diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/git/GitCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/git/GitCommand.java deleted file mode 100644 index 2a65a8f3..00000000 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/git/GitCommand.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.hypixel.hytale.server.core.command.commands.utility.git; - -import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; - -public class GitCommand extends AbstractCommandCollection { - public GitCommand() { - super("git", "server.commands.git.desc"); - this.addSubCommand(new UpdateAssetsCommand()); - this.addSubCommand(new UpdatePrefabsCommand()); - } -} diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/git/UpdateAssetsCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/git/UpdateAssetsCommand.java deleted file mode 100644 index 0e8d647d..00000000 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/git/UpdateAssetsCommand.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.hypixel.hytale.server.core.command.commands.utility.git; - -import com.hypixel.hytale.common.util.PathUtil; -import com.hypixel.hytale.server.core.Message; -import com.hypixel.hytale.server.core.command.system.CommandContext; -import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncCommand; -import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; -import com.hypixel.hytale.server.core.util.AssetUtil; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - -public class UpdateAssetsCommand extends AbstractCommandCollection { - public UpdateAssetsCommand() { - super("assets", "server.commands.git.assets.desc"); - this.addSubCommand(new UpdateAssetsCommand.UpdateAssetsStatusCommand()); - this.addSubCommand(new UpdateAssetsCommand.UpdateAssetsResetCommand()); - this.addSubCommand(new UpdateAssetsCommand.UpdateAssetsPullCommand()); - } - - private abstract static class UpdateAssetsGitCommand extends AbstractAsyncCommand { - protected UpdateAssetsGitCommand(@Nonnull String name, @Nonnull String description) { - super(name, description); - } - - @Nonnull - protected abstract String[] getCommand(@Nonnull Path var1); - - @Nonnull - @Override - protected CompletableFuture executeAsync(@Nonnull CommandContext context) { - return CompletableFuture.runAsync(() -> { - Path assetPath = AssetUtil.getHytaleAssetsPath(); - Path gitPath = null; - if (Files.exists(assetPath.resolve(".git"))) { - gitPath = assetPath; - } else { - Path parent = PathUtil.getParent(assetPath.toAbsolutePath()); - if (Files.exists(parent.resolve(".git"))) { - gitPath = parent; - } - } - - if (gitPath == null) { - context.sendMessage(Message.translation("server.general.pathNotGitRepo").param("path", assetPath.toString())); - } else { - String[] processCommand = this.getCommand(gitPath); - String commandDisplay = String.join(" ", processCommand); - - try { - context.sendMessage(Message.translation("server.commands.git.running").param("cmd", commandDisplay)); - Process process = new ProcessBuilder(processCommand).directory(gitPath.toFile()).start(); - - try { - process.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)); - - String line; - while ((line = reader.readLine()) != null) { - context.sendMessage(Message.translation("server.commands.git.runningStdOut").param("cmd", commandDisplay).param("line", line)); - } - - reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8)); - - while ((line = reader.readLine()) != null) { - context.sendMessage(Message.translation("server.commands.git.runningStdErr").param("cmd", commandDisplay).param("line", line)); - } - - context.sendMessage(Message.translation("server.commands.git.done").param("cmd", commandDisplay)); - } catch (InterruptedException var9) { - Thread.currentThread().interrupt(); - } - } catch (IOException var10) { - context.sendMessage(Message.translation("server.commands.git.failed").param("cmd", commandDisplay).param("msg", var10.getMessage())); - } - } - }); - } - } - - private static class UpdateAssetsPullCommand extends UpdateAssetsCommand.UpdateAssetsGitCommand { - public UpdateAssetsPullCommand() { - super("pull", "server.commands.git.assets.pull.desc"); - } - - @Nonnull - @Override - protected String[] getCommand(@Nonnull Path gitPath) { - Path script = gitPath.resolve("../../updateAssets.sh"); - if (Files.exists(script)) { - Path relative = gitPath.relativize(script); - return new String[]{"sh", relative.toString()}; - } else { - return new String[]{"git", "pull"}; - } - } - } - - private static class UpdateAssetsResetCommand extends UpdateAssetsCommand.UpdateAssetsGitCommand { - public UpdateAssetsResetCommand() { - super("reset", "server.commands.git.assets.reset.desc"); - } - - @Nonnull - @Override - protected String[] getCommand(@Nonnull Path gitPath) { - return new String[]{"git", "reset", "--hard", "head"}; - } - } - - private static class UpdateAssetsStatusCommand extends UpdateAssetsCommand.UpdateAssetsGitCommand { - public UpdateAssetsStatusCommand() { - super("status", "server.commands.git.assets.status.desc"); - } - - @Nonnull - @Override - protected String[] getCommand(@Nonnull Path gitPath) { - return new String[]{"git", "status"}; - } - } -} diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/git/UpdatePrefabsCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/git/UpdatePrefabsCommand.java deleted file mode 100644 index 972f7a4a..00000000 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/git/UpdatePrefabsCommand.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.hypixel.hytale.server.core.command.commands.utility.git; - -import com.hypixel.hytale.common.util.PathUtil; -import com.hypixel.hytale.server.core.Message; -import com.hypixel.hytale.server.core.command.system.CommandContext; -import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncCommand; -import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; -import com.hypixel.hytale.server.core.prefab.PrefabStore; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Nonnull; - -public class UpdatePrefabsCommand extends AbstractCommandCollection { - public UpdatePrefabsCommand() { - super("prefabs", "server.commands.git.prefabs.desc"); - this.addSubCommand(new UpdatePrefabsCommand.UpdatePrefabsStatusCommand()); - this.addSubCommand(new UpdatePrefabsCommand.UpdatePrefabsCommitCommand()); - this.addSubCommand(new UpdatePrefabsCommand.UpdatePrefabsPullCommand()); - this.addSubCommand(new UpdatePrefabsCommand.UpdatePrefabsPushCommand()); - this.addSubCommand(new UpdatePrefabsCommand.UpdatePrefabsAllCommand()); - } - - private static class UpdatePrefabsAllCommand extends UpdatePrefabsCommand.UpdatePrefabsGitCommand { - public UpdatePrefabsAllCommand() { - super("all", "server.commands.git.prefabs.all.desc"); - } - - @Nonnull - @Override - protected String[][] getCommands(@Nonnull String senderDisplayName) { - return new String[][]{ - {"git", "submodule", "foreach", "git", "add", "--all", "."}, - {"git", "submodule", "foreach", "git", "commit", "-am", "\"Update prefabs by " + senderDisplayName + "\""}, - {"git", "submodule", "foreach", "git", "pull"}, - {"git", "submodule", "foreach", "git", "push"}, - {"git", "add", "--all", "."}, - {"git", "commit", "-am", "Update prefabs by " + senderDisplayName}, - {"git", "pull"}, - {"git", "push"} - }; - } - } - - private static class UpdatePrefabsCommitCommand extends UpdatePrefabsCommand.UpdatePrefabsGitCommand { - public UpdatePrefabsCommitCommand() { - super("commit", "server.commands.git.prefabs.commit.desc"); - } - - @Nonnull - @Override - protected String[][] getCommands(@Nonnull String senderDisplayName) { - return new String[][]{ - {"git", "add", "--all", "."}, - {"git", "commit", "-am", "Update prefabs by " + senderDisplayName}, - {"git", "submodule", "foreach", "git", "add", "--all", "."}, - {"git", "submodule", "foreach", "git", "commit", "-am", "\"Update prefabs by " + senderDisplayName + "\""} - }; - } - } - - private abstract static class UpdatePrefabsGitCommand extends AbstractAsyncCommand { - protected UpdatePrefabsGitCommand(@Nonnull String name, @Nonnull String description) { - super(name, description); - } - - @Nonnull - protected abstract String[][] getCommands(@Nonnull String var1); - - @Nonnull - @Override - protected CompletableFuture executeAsync(@Nonnull CommandContext context) { - return CompletableFuture.runAsync( - () -> { - Path prefabsPath = PrefabStore.get().getServerPrefabsPath(); - Path gitPath = null; - if (Files.isDirectory(prefabsPath.resolve(".git"))) { - gitPath = prefabsPath; - } else { - Path parent = PathUtil.getParent(prefabsPath); - if (Files.isDirectory(parent.resolve(".git"))) { - gitPath = parent; - } - } - - if (gitPath == null) { - context.sendMessage(Message.translation("server.general.pathNotGitRepo").param("path", prefabsPath.toString())); - } else { - String senderDisplayName = context.sender().getDisplayName().replaceAll("[^a-zA-Z0-9 ._-]", ""); - if (senderDisplayName.isEmpty()) { - senderDisplayName = "Unknown"; - } - - String[][] cmds = this.getCommands(senderDisplayName); - - for (String[] processCommand : cmds) { - try { - String commandDisplay = String.join(" ", processCommand); - context.sendMessage(Message.translation("server.commands.git.runningCmd").param("cmd", commandDisplay)); - Process process = new ProcessBuilder(processCommand).directory(gitPath.toFile()).start(); - - try { - process.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)); - - String line; - while ((line = reader.readLine()) != null) { - context.sendMessage(Message.translation("server.commands.git.runningStdOut").param("cmd", commandDisplay).param("line", line)); - } - - reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8)); - - while ((line = reader.readLine()) != null) { - context.sendMessage(Message.translation("server.commands.git.runningStdErr").param("cmd", commandDisplay).param("line", line)); - } - - context.sendMessage(Message.translation("server.commands.git.done").param("cmd", commandDisplay)); - } catch (InterruptedException var15) { - Thread.currentThread().interrupt(); - break; - } - } catch (IOException var16) { - context.sendMessage( - Message.translation("server.commands.git.failed").param("cmd", String.join(" ", processCommand)).param("msg", var16.getMessage()) - ); - break; - } - } - } - } - ); - } - } - - private static class UpdatePrefabsPullCommand extends UpdatePrefabsCommand.UpdatePrefabsGitCommand { - public UpdatePrefabsPullCommand() { - super("pull", "server.commands.git.prefabs.pull.desc"); - } - - @Nonnull - @Override - protected String[][] getCommands(@Nonnull String senderDisplayName) { - return new String[][]{{"git", "pull"}, {"git", "submodule", "foreach", "git", "pull"}}; - } - } - - private static class UpdatePrefabsPushCommand extends UpdatePrefabsCommand.UpdatePrefabsGitCommand { - public UpdatePrefabsPushCommand() { - super("push", "server.commands.git.prefabs.push.desc"); - } - - @Nonnull - @Override - protected String[][] getCommands(@Nonnull String senderDisplayName) { - return new String[][]{{"git", "push", "origin", "master"}, {"git", "submodule", "foreach", "git", "push"}}; - } - } - - private static class UpdatePrefabsStatusCommand extends UpdatePrefabsCommand.UpdatePrefabsGitCommand { - public UpdatePrefabsStatusCommand() { - super("status", "server.commands.git.prefabs.status.desc"); - } - - @Nonnull - @Override - protected String[][] getCommands(@Nonnull String senderDisplayName) { - return new String[][]{{"git", "status"}, {"git", "submodule", "foreach", "git", "status"}}; - } - } -} diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/lighting/LightingGetCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/lighting/LightingGetCommand.java index f9759de3..dbcd5604 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/lighting/LightingGetCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/lighting/LightingGetCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.command.commands.utility.lighting; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkLightDat import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class LightingGetCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/lighting/LightingInvalidateCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/lighting/LightingInvalidateCommand.java index 47c2b0ba..1bfc0896 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/lighting/LightingInvalidateCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/lighting/LightingInvalidateCommand.java @@ -4,8 +4,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -18,6 +16,8 @@ import com.hypixel.hytale.server.core.universe.world.lighting.ChunkLightingManag import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class LightingInvalidateCommand extends AbstractWorldCommand { @Nonnull @@ -53,7 +53,7 @@ public class LightingInvalidateCommand extends AbstractWorldCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - long chunkIndex = ChunkUtil.indexChunkFromBlock((int)position.getX(), (int)position.getZ()); + long chunkIndex = ChunkUtil.indexChunkFromBlock((int)position.x(), (int)position.z()); int chunkX = ChunkUtil.xOfChunkIndex(chunkIndex); int chunkZ = ChunkUtil.zOfChunkIndex(chunkIndex); Ref chunkReference = chunkStore.getChunkReference(chunkIndex); @@ -71,7 +71,7 @@ public class LightingInvalidateCommand extends AbstractWorldCommand { assert blockChunkComponent != null; - int chunkY = MathUtil.floor(position.getY()) >> 5; + int chunkY = MathUtil.floor(position.y()) >> 5; BlockSection section = blockChunkComponent.getSectionAtBlockY(chunkY); section.invalidateLocalLight(); blockChunkComponent.invalidateChunkSection(chunkY); diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/net/NetworkCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/net/NetworkCommand.java index 8a003f08..89574076 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/net/NetworkCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/net/NetworkCommand.java @@ -273,7 +273,7 @@ public class NetworkCommand extends AbstractCommandCollection { ) { NetworkChannel networkChannel = this.channelArg.get(context); int priority = this.priorityArg.get(context); - if (priority >= 0 && priority <= 255) { + if (priority >= 0 && priority <= 127) { if (playerRef.getPacketHandler().getChannel(networkChannel) instanceof QuicStreamChannel quicStreamChannel) { quicStreamChannel.updatePriority(new QuicStreamPriority(priority, true)); context.sendMessage( diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/sound/SoundPlay3DCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/sound/SoundPlay3DCommand.java index 3030cb28..143ab72a 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/sound/SoundPlay3DCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/sound/SoundPlay3DCommand.java @@ -3,8 +3,6 @@ package com.hypixel.hytale.server.core.command.commands.utility.sound; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -21,6 +19,8 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class SoundPlay3DCommand extends AbstractTargetPlayerCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/worldmap/WorldMapCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/worldmap/WorldMapCommand.java index 51692407..b43d42dc 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/worldmap/WorldMapCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/worldmap/WorldMapCommand.java @@ -1,6 +1,11 @@ package com.hypixel.hytale.server.core.command.commands.utility.worldmap; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; +import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase; +import com.hypixel.hytale.server.core.universe.world.worldmap.provider.chunk.ImageBuilder; +import javax.annotation.Nonnull; public class WorldMapCommand extends AbstractCommandCollection { public WorldMapCommand() { @@ -11,5 +16,20 @@ public class WorldMapCommand extends AbstractCommandCollection { this.addSubCommand(new WorldMapUndiscoverCommand()); this.addSubCommand(new WorldMapClearMarkersCommand()); this.addSubCommand(new WorldMapViewRadiusSubCommand()); + this.addSubCommand(new WorldMapCommand.QuantizeCommand()); + } + + private static class QuantizeCommand extends CommandBase { + public QuantizeCommand() { + super("quantize", "server.commands.worldmap.quantize.desc"); + this.addAliases("quant", "q"); + } + + @Override + protected void executeSync(@Nonnull CommandContext context) { + boolean enabled = ImageBuilder.toggleQuantization(); + String key = enabled ? "server.commands.worldmap.quantize.enabled" : "server.commands.worldmap.quantize.disabled"; + context.sendMessage(Message.translation(key)); + } } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/utility/worldmap/WorldMapViewRadiusSetCommand.java b/src/com/hypixel/hytale/server/core/command/commands/utility/worldmap/WorldMapViewRadiusSetCommand.java index d6c56599..ae56690e 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/utility/worldmap/WorldMapViewRadiusSetCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/utility/worldmap/WorldMapViewRadiusSetCommand.java @@ -2,12 +2,14 @@ package com.hypixel.hytale.server.core.command.commands.utility.worldmap; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractTargetPlayerCommand; +import com.hypixel.hytale.server.core.config.ServerWorldMapConfig; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; @@ -43,11 +45,16 @@ public class WorldMapViewRadiusSetCommand extends AbstractTargetPlayerCommand { boolean bypass = this.bypassArg.get(context); if (viewRadius < 0) { context.sendMessage(Message.translation("server.commands.worldmap.viewradius.set.mustBePositive")); - } else if (viewRadius > 512 && !bypass) { - context.sendMessage(Message.translation("server.commands.worldmap.viewradius.set.noHigherThan").param("radius", 512)); } else { - playerComponent.getWorldMapTracker().setViewRadiusOverride(viewRadius); - context.sendMessage(Message.translation("server.commands.worldmap.viewradius.set.success").param("radius", viewRadius)); + ServerWorldMapConfig serverConfig = HytaleServer.get().getConfig().getWorldMapConfig(); + int serverRadiusMax = serverConfig.getViewRadiusMax(); + int effectiveMax = bypass ? serverRadiusMax : world.getWorldMapManager().getWorldMapSettings().getViewRadiusMax(); + if (viewRadius > effectiveMax) { + context.sendMessage(Message.translation("server.commands.worldmap.viewradius.set.noHigherThan").param("radius", effectiveMax)); + } else { + playerComponent.getWorldMapTracker().setViewRadiusOverride(viewRadius); + context.sendMessage(Message.translation("server.commands.worldmap.viewradius.set.success").param("radius", viewRadius)); + } } } } diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/SpawnBlockCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/SpawnBlockCommand.java index ae72410b..ad105a0b 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/SpawnBlockCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/SpawnBlockCommand.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.server.core.command.commands.world; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg; @@ -19,6 +19,7 @@ import com.hypixel.hytale.server.core.modules.time.TimeResource; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpawnBlockCommand extends AbstractWorldCommand { @Nonnull @@ -28,8 +29,8 @@ public class SpawnBlockCommand extends AbstractWorldCommand { "position", "server.commands.spawnblock.arg.position.desc", ArgTypes.RELATIVE_POSITION ); @Nonnull - private final DefaultArg rotationArg = this.withDefaultArg( - "rotation", "server.commands.spawnblock.arg.rotation.desc", ArgTypes.ROTATION, Vector3f.FORWARD, "server.commands.spawnblock.arg.rotation.desc" + private final DefaultArg rotationArg = this.withDefaultArg( + "rotation", "server.commands.spawnblock.arg.rotation.desc", ArgTypes.ROTATION, Rotation3f.IDENTITY, "server.commands.spawnblock.arg.rotation.desc" ); public SpawnBlockCommand() { @@ -40,12 +41,12 @@ public class SpawnBlockCommand extends AbstractWorldCommand { protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store store) { String blockTypeKey = context.get(this.blockArg); Vector3d position = context.get(this.positionArg).getRelativePosition(context, world, store); - Vector3f rotation = this.rotationArg.get(context); + Rotation3fc rotation = this.rotationArg.get(context); TimeResource timeResource = world.getEntityStore().getStore().getResource(TimeResource.getResourceType()); Holder blockEntityHolder = BlockEntity.assembleDefaultBlockEntity(timeResource, blockTypeKey, position); TransformComponent transformComponent = blockEntityHolder.ensureAndGetComponent(TransformComponent.getComponentType()); transformComponent.setPosition(position); - transformComponent.setRotation(rotation); + transformComponent.setRotation(new Rotation3f(rotation)); UUIDComponent uuidComponent = blockEntityHolder.getComponent(UUIDComponent.getComponentType()); String entityIdString = uuidComponent == null ? "None" : uuidComponent.getUuid().toString(); world.getEntityStore().getStore().addEntity(blockEntityHolder, AddReason.SPAWN); diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkFixHeightMapCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkFixHeightMapCommand.java index 6299bc79..9287b1c2 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkFixHeightMapCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkFixHeightMapCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.command.commands.world.chunk; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -20,6 +19,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ChunkFixHeightMapCommand extends AbstractWorldCommand { @Nonnull @@ -70,7 +70,7 @@ public class ChunkFixHeightMapCommand extends AbstractWorldCommand { blockChunkComponent.getSectionAtIndex(chunkSectionY).invalidateLocalLight(); } - chunkLighting.invalidateLightInChunk(worldChunkComponent); + chunkLighting.invalidateLightInChunk(world.getChunkStore(), chunkX, chunkZ); context.sendMessage(MESSAGE_COMMANDS_CHUNK_FIXHEIGHTMAP_DONE); context.sendMessage(Message.translation("server.commands.chunk.fixHeightMap.waitingForLighting").param("x", chunkX).param("z", chunkZ)); int[] count = new int[]{0}; diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkForceTickCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkForceTickCommand.java index f8da9f9f..dd56d3ae 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkForceTickCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkForceTickCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.command.commands.world.chunk; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ChunkForceTickCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkInfoCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkInfoCommand.java index 1af1b9e3..1302a371 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkInfoCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkInfoCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.command.commands.world.chunk; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -20,6 +19,7 @@ import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ChunkInfoCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkLightingCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkLightingCommand.java index 2637848a..e4a02e1c 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkLightingCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkLightingCommand.java @@ -4,8 +4,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -19,6 +17,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector2i; +import org.joml.Vector3i; public class ChunkLightingCommand extends AbstractWorldCommand { @Nonnull @@ -39,7 +39,7 @@ public class ChunkLightingCommand extends AbstractWorldCommand { Vector3i position = this.positionArg.get(context).getBlockPosition(context, store); ChunkStore chunkStore = world.getChunkStore(); Store chunkStoreStore = chunkStore.getStore(); - Vector2i chunkPos = new Vector2i(ChunkUtil.chunkCoordinate(position.getX()), ChunkUtil.chunkCoordinate(position.getZ())); + Vector2i chunkPos = new Vector2i(ChunkUtil.chunkCoordinate(position.x()), ChunkUtil.chunkCoordinate(position.z())); long chunkIndex = ChunkUtil.indexChunk(chunkPos.x, chunkPos.y); Ref chunkRef = chunkStore.getChunkReference(chunkIndex); if (chunkRef != null && chunkRef.isValid()) { diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkLoadCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkLoadCommand.java index f95375c4..6b9e2408 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkLoadCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkLoadCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.command.commands.world.chunk; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ChunkLoadCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkMarkSaveCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkMarkSaveCommand.java index 7572f40e..edbb6e34 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkMarkSaveCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkMarkSaveCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.command.commands.world.chunk; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ChunkMarkSaveCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkRegenerateCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkRegenerateCommand.java index 031f423c..de5613fd 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkRegenerateCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkRegenerateCommand.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.core.command.commands.world.chunk; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -13,6 +12,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ChunkRegenerateCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkResendCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkResendCommand.java index 19cfd472..b758f789 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkResendCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkResendCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.iterator.SpiralIterator; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ChunkResendCommand extends AbstractTargetPlayerCommand { @Nonnull @@ -52,8 +52,8 @@ public class ChunkResendCommand extends AbstractTargetPlayerCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; if (this.clearCacheArg.provided(context)) { ChunkStore chunkStore = world.getChunkStore(); Store chunkStoreStore = chunkStore.getStore(); diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkTintCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkTintCommand.java index 2cfbab4c..0819678c 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkTintCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkTintCommand.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.server.core.Message; @@ -38,6 +37,7 @@ import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.Long2IntMap.Entry; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ChunkTintCommand extends AbstractPlayerCommand { private static final int BLUR_RADIUS = 5; @@ -72,8 +72,8 @@ public class ChunkTintCommand extends AbstractPlayerCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; ChunkStore chunkStore = world.getChunkStore(); Store chunkStoreStore = chunkStore.getStore(); LongOpenHashSet updateChunks = new LongOpenHashSet(); diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkUnloadCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkUnloadCommand.java index 482c963e..5fe9c3f5 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkUnloadCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/chunk/ChunkUnloadCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ChunkUnloadCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityEffectCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityEffectCommand.java index 299467ba..5bce5e05 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityEffectCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityEffectCommand.java @@ -13,7 +13,7 @@ import com.hypixel.hytale.server.core.command.system.basecommands.AbstractTarget import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; public class EntityEffectCommand extends AbstractTargetEntityCommand { @@ -30,9 +30,7 @@ public class EntityEffectCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { EntityEffect entityEffect = this.effectArg.get(context); float duration = this.durationArg.get(context); diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityHideFromAdventurePlayersCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityHideFromAdventurePlayersCommand.java index 27c2cd9d..ebe72ade 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityHideFromAdventurePlayersCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityHideFromAdventurePlayersCommand.java @@ -9,7 +9,7 @@ import com.hypixel.hytale.server.core.command.system.basecommands.AbstractTarget import com.hypixel.hytale.server.core.modules.entity.component.HiddenFromAdventurePlayers; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; public class EntityHideFromAdventurePlayersCommand extends AbstractTargetEntityCommand { @@ -21,9 +21,7 @@ public class EntityHideFromAdventurePlayersCommand extends AbstractTargetEntityC } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { boolean remove = this.removeFlag.provided(context); for (Ref entity : entities) { diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityIntangibleCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityIntangibleCommand.java index 369fe656..71ea802c 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityIntangibleCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityIntangibleCommand.java @@ -9,7 +9,7 @@ import com.hypixel.hytale.server.core.command.system.basecommands.AbstractTarget import com.hypixel.hytale.server.core.modules.entity.component.Intangible; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; public class EntityIntangibleCommand extends AbstractTargetEntityCommand { @@ -21,9 +21,7 @@ public class EntityIntangibleCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { boolean remove = this.removeFlag.provided(context); for (Ref entity : entities) { diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityInvulnerableCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityInvulnerableCommand.java index 2a906f92..3deec996 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityInvulnerableCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityInvulnerableCommand.java @@ -9,7 +9,7 @@ import com.hypixel.hytale.server.core.command.system.basecommands.AbstractTarget import com.hypixel.hytale.server.core.modules.entity.component.Invulnerable; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; public class EntityInvulnerableCommand extends AbstractTargetEntityCommand { @@ -21,9 +21,7 @@ public class EntityInvulnerableCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { boolean remove = this.removeFlag.provided(context); for (Ref entity : entities) { diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityMakeInteractableCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityMakeInteractableCommand.java index 2f23d84e..6a1b76c2 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityMakeInteractableCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/EntityMakeInteractableCommand.java @@ -9,7 +9,7 @@ import com.hypixel.hytale.server.core.command.system.basecommands.AbstractTarget import com.hypixel.hytale.server.core.modules.entity.component.Interactable; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; public class EntityMakeInteractableCommand extends AbstractTargetEntityCommand { @@ -21,9 +21,7 @@ public class EntityMakeInteractableCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { boolean disable = this.disableFlag.provided(context); for (Ref entity : entities) { diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/snapshot/EntitySnapshotHistoryCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/snapshot/EntitySnapshotHistoryCommand.java index 6dbc11ec..f5bbe97a 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/snapshot/EntitySnapshotHistoryCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/snapshot/EntitySnapshotHistoryCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand; import com.hypixel.hytale.server.core.entity.EntitySnapshot; @@ -13,8 +12,9 @@ import com.hypixel.hytale.server.core.modules.entity.component.SnapshotBuffer; import com.hypixel.hytale.server.core.universe.world.ParticleUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EntitySnapshotHistoryCommand extends AbstractWorldCommand { public EntitySnapshotHistoryCommand() { @@ -41,7 +41,7 @@ public class EntitySnapshotHistoryCommand extends AbstractWorldCommand { Vector3d pos = snapshot.getPosition(); SpatialResource, EntityStore> playerSpatialResource = cmdBuffer.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(pos, 75.0, results); ParticleUtil.spawnParticleEffect("Example_Simple", pos.x, pos.y, pos.z, results, cmdBuffer); } diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsAddCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsAddCommand.java index 5dc052a3..434bbd63 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsAddCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsAddCommand.java @@ -14,7 +14,6 @@ import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import javax.annotation.Nonnull; @@ -29,9 +28,7 @@ public class EntityStatsAddCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { int statAmount = this.statAmountArg.get(context); String entityStatName = this.entityStatNameArg.get(context); addEntityStat(context, entities, statAmount, entityStatName, store); diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsDumpCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsDumpCommand.java index 33f048ea..33fea4f7 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsDumpCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsDumpCommand.java @@ -13,7 +13,6 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.message.MessageFormat; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import javax.annotation.Nonnull; @@ -23,9 +22,7 @@ public class EntityStatsDumpCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { dumpEntityStatsData(context, entities, store); } diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsGetCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsGetCommand.java index 486cdcdf..7acf03dc 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsGetCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsGetCommand.java @@ -15,7 +15,6 @@ import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import javax.annotation.Nonnull; @@ -28,9 +27,7 @@ public class EntityStatsGetCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { String entityStat = this.entityStatNameArg.get(context); getEntityStat(context, entities, entityStat, store); } diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsResetCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsResetCommand.java index 1b415e06..7c4a0a3d 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsResetCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsResetCommand.java @@ -14,7 +14,6 @@ import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import javax.annotation.Nonnull; @@ -27,9 +26,7 @@ public class EntityStatsResetCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { String entityStat = this.entityStatNameArg.get(context); resetEntityStat(context, entities, entityStat, store); } diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsSetCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsSetCommand.java index 15c50b6b..d69424c2 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsSetCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsSetCommand.java @@ -14,7 +14,6 @@ import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import javax.annotation.Nonnull; @@ -29,9 +28,7 @@ public class EntityStatsSetCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { int newStatValue = this.statValueArg.get(context); String entityStatName = this.entityStatNameArg.get(context); setEntityStat(context, entities, newStatValue, entityStatName, store); diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsSetToMaxCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsSetToMaxCommand.java index 5fbe52d0..1b5ede9c 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsSetToMaxCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/entity/stats/EntityStatsSetToMaxCommand.java @@ -15,7 +15,6 @@ import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import javax.annotation.Nonnull; @@ -30,9 +29,7 @@ public class EntityStatsSetToMaxCommand extends AbstractTargetEntityCommand { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { String entityStatName = this.entityStatNameArg.get(context); setEntityStatMax(context, entities, entityStatName, store); } diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/worldgen/WorldGenBenchmarkCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/worldgen/WorldGenBenchmarkCommand.java index 1cfd06f4..f853c34e 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/worldgen/WorldGenBenchmarkCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/worldgen/WorldGenBenchmarkCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.command.commands.world.worldgen; import com.hypixel.hytale.common.util.FormatUtil; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -24,6 +23,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class WorldGenBenchmarkCommand extends CommandBase { private static final AtomicBoolean IS_RUNNING = new AtomicBoolean(false); diff --git a/src/com/hypixel/hytale/server/core/command/commands/world/worldgen/WorldGenReloadCommand.java b/src/com/hypixel/hytale/server/core/command/commands/world/worldgen/WorldGenReloadCommand.java index 30656aa9..14359752 100644 --- a/src/com/hypixel/hytale/server/core/command/commands/world/worldgen/WorldGenReloadCommand.java +++ b/src/com/hypixel/hytale/server/core/command/commands/world/worldgen/WorldGenReloadCommand.java @@ -108,8 +108,8 @@ public class WorldGenReloadCommand extends AbstractAsyncWorldCommand { private static CompletableFuture clearChunks(@Nonnull CommandContext context, @Nonnull World world) { ChunkStore chunkComponentStore = world.getChunkStore(); Store componentStore = chunkComponentStore.getStore(); + world.lockSaving(); ChunkSavingSystems.Data data = componentStore.getResource(ChunkStore.SAVE_RESOURCE); - data.isSaving = false; data.clearSaveQueue(); context.sendMessage(MESSAGE_COMMANDS_WORLD_GEN_RELOAD_CHUNK_SAVING_DISABLED); context.sendMessage(MESSAGE_COMMANDS_WORLD_GEN_RELOAD_DELETING_CHUNKS); @@ -174,9 +174,7 @@ public class WorldGenReloadCommand extends AbstractAsyncWorldCommand { return CompletableFuture.allOf(regenerateFutures.toArray(CompletableFuture[]::new)); }, world) .thenRunAsync(() -> { - Store chunkStore = chunkComponentStore.getStore(); - ChunkSavingSystems.Data saveData = chunkStore.getResource(ChunkStore.SAVE_RESOURCE); - saveData.isSaving = true; + world.unlockSaving(); context.sendMessage(MESSAGE_COMMANDS_WORLD_GEN_RELOAD_CHUNK_SAVING_ENABLED); }, world); } diff --git a/src/com/hypixel/hytale/server/core/command/system/AbbreviationMap.java b/src/com/hypixel/hytale/server/core/command/system/AbbreviationMap.java index ebc96f64..35205abb 100644 --- a/src/com/hypixel/hytale/server/core/command/system/AbbreviationMap.java +++ b/src/com/hypixel/hytale/server/core/command/system/AbbreviationMap.java @@ -1,21 +1,59 @@ package com.hypixel.hytale.server.core.command.system; +import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Map.Entry; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class AbbreviationMap { - private final Map abbreviationMap; + private final List> entries; - public AbbreviationMap(@Nonnull Map abbreviationMap) { - this.abbreviationMap = abbreviationMap; + private AbbreviationMap(@Nonnull List> entries) { + this.entries = entries; } - public Value get(@Nonnull String abbreviation) { - return this.abbreviationMap.get(abbreviation.toLowerCase()); + @Nullable + public Value get(@Nonnull String input) { + String lower = input.toLowerCase(); + Value prefixMatch = null; + boolean prefixAmbiguous = false; + Value substringMatch = null; + boolean substringAmbiguous = false; + + for (Pair entry : this.entries) { + String key = entry.left(); + Value value = entry.right(); + if (key.equals(lower)) { + return value; + } + + if (key.startsWith(lower)) { + if (prefixMatch == null) { + prefixMatch = value; + } else if (!prefixMatch.equals(value)) { + prefixAmbiguous = true; + } + } + + if (key.contains(lower)) { + if (substringMatch == null) { + substringMatch = value; + } else if (!substringMatch.equals(value)) { + substringAmbiguous = true; + } + } + } + + if (prefixMatch != null && !prefixAmbiguous) { + return prefixMatch; + } else { + return substringMatch != null && !substringAmbiguous ? substringMatch : null; + } } @Nonnull @@ -37,29 +75,13 @@ public class AbbreviationMap { @Nonnull public AbbreviationMap build() { - Object2ObjectOpenHashMap abbreviationMap = new Object2ObjectOpenHashMap<>(); + List> entries = new ArrayList<>(this.keys.size()); for (Entry entry : this.keys.entrySet()) { - this.appendAbbreviation(entry.getKey(), entry.getValue(), abbreviationMap); + entries.add(Pair.of(entry.getKey(), entry.getValue())); } - abbreviationMap.values().removeIf(Objects::isNull); - abbreviationMap.trim(); - return new AbbreviationMap<>(Collections.unmodifiableMap(abbreviationMap)); - } - - private void appendAbbreviation(@Nonnull String key, @Nonnull Value value, @Nonnull Map map) { - map.put(key, value); - - for (int i = 1; i < key.length(); i++) { - String substring = key.substring(0, key.length() - i); - Value existingAbbreviationValue = map.get(substring); - if (existingAbbreviationValue == null) { - map.put(substring, value); - } else if (!this.keys.containsKey(substring) && !existingAbbreviationValue.equals(value)) { - map.put(substring, null); - } - } + return new AbbreviationMap<>(Collections.unmodifiableList(entries)); } } } diff --git a/src/com/hypixel/hytale/server/core/command/system/AbstractCommand.java b/src/com/hypixel/hytale/server/core/command/system/AbstractCommand.java index 83d15210..20556a30 100644 --- a/src/com/hypixel/hytale/server/core/command/system/AbstractCommand.java +++ b/src/com/hypixel/hytale/server/core/command/system/AbstractCommand.java @@ -25,7 +25,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectBooleanPair; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -86,6 +88,7 @@ public abstract class AbstractCommand { private boolean unavailableInSingleplayer; private boolean allowsExtraArguments; private boolean hasBeenRegistered; + private boolean hasGreedyStringArg; protected AbstractCommand(@Nullable String name, @Nullable String description, boolean requiresConfirmation) { this.name = name == null ? null : name.toLowerCase(); @@ -535,6 +538,12 @@ public abstract class AbstractCommand { int currentReqArgIndex = 0; for (RequiredArg requiredArgument : this.requiredArguments) { + if (requiredArgument.getArgumentType().isGreedyString()) { + String rawTail = this.extractGreedyRawTail(parserContext); + commandContext.appendArgumentData(requiredArgument, new String[]{rawTail}, false, parseResult); + return; + } + if (requiredArgument.getArgumentType().isListArgument() && parserContext.isListToken(currentReqArgIndex)) { ParserContext.PreOptionalListContext preOptionalTokenContext = parserContext.getPreOptionalListToken(currentReqArgIndex); currentReqArgIndex++; @@ -560,6 +569,38 @@ public abstract class AbstractCommand { } } + @Nonnull + private String extractGreedyRawTail(@Nonnull ParserContext parserContext) { + String raw = parserContext.getRawInput(); + int numWordsToStrip = parserContext.getSubCommandIndex(); + + for (RequiredArg reqArg : this.requiredArguments) { + if (reqArg.getArgumentType().isGreedyString()) { + break; + } + + numWordsToStrip += reqArg.getArgumentType().getNumberOfParameters(); + } + + int pos = 0; + + for (int i = 0; i < numWordsToStrip && pos < raw.length(); i++) { + while (pos < raw.length() && raw.charAt(pos) == ' ') { + pos++; + } + + while (pos < raw.length() && raw.charAt(pos) != ' ') { + pos++; + } + } + + while (pos < raw.length() && raw.charAt(pos) == ' ') { + pos++; + } + + return raw.substring(pos); + } + private void processOptionalArguments(@Nonnull ParserContext parserContext, @Nonnull ParseResult parseResult, @Nonnull CommandContext commandContext) { for (java.util.Map.Entry>> optionalArgContext : parserContext.getOptionalArgs()) { AbstractOptionalArg, ?> optionalArg = (AbstractOptionalArg, ?>)this.argumentAbbreviationMap @@ -776,12 +817,19 @@ public abstract class AbstractCommand { private , D> R registerRequiredArg(@Nonnull R requiredArgument) { if (this.hasBeenRegistered) { throw new IllegalStateException("Cannot add new arguments when a command has already completed registration"); - } else if (requiredArgument.getCommandRegisteredTo().equals(this) && !this.requiredArguments.contains(requiredArgument)) { + } else if (!requiredArgument.getCommandRegisteredTo().equals(this) || this.requiredArguments.contains(requiredArgument)) { + throw new IllegalArgumentException("Cannot re-use arguments"); + } else if (this.hasGreedyStringArg) { + throw new IllegalStateException("Cannot register additional required arguments after a greedy string argument"); + } else { + if (requiredArgument.getArgumentType().isGreedyString()) { + this.hasGreedyStringArg = true; + this.allowsExtraArguments = true; + } + this.totalNumRequiredParameters = this.totalNumRequiredParameters + requiredArgument.getArgumentType().getNumberOfParameters(); this.requiredArguments.add(requiredArgument); return requiredArgument; - } else { - throw new IllegalArgumentException("Cannot re-use arguments"); } } @@ -887,7 +935,47 @@ public abstract class AbstractCommand { return this.requiredArguments; } + @Nonnull + public Map> getOptionalArguments() { + return this.optionalArguments; + } + + @Nonnull + public Collection getVariantCommands() { + return this.variantCommands.values(); + } + + @Nullable + public AbstractCommand getVariantByArgCount(int requiredArgCount) { + return this.variantCommands.get(requiredArgCount); + } + + @Nullable + public List getSuggestionOverrides() { + List result = null; + + for (int i = 0; i < this.requiredArguments.size(); i++) { + ArgumentType overrideType = this.requiredArguments.get(i).getSuggestionOverrideType(); + if (overrideType != null) { + if (result == null) { + result = new ArrayList<>(); + } + + result.add(new AbstractCommand.SuggestionOverrideEntry(i, overrideType.getNumberOfParameters(), overrideType)); + } + } + + return result; + } + public boolean hasBeenRegistered() { return this.hasBeenRegistered; } + + public record SuggestionOverrideEntry(int argStart, int argCount, @Nonnull ArgumentType overrideType) { + @Nonnull + public String argTypeId() { + return this.overrideType.getSuggestionTypeId(); + } + } } diff --git a/src/com/hypixel/hytale/server/core/command/system/CommandContext.java b/src/com/hypixel/hytale/server/core/command/system/CommandContext.java index 9a12193c..fc564b27 100644 --- a/src/com/hypixel/hytale/server/core/command/system/CommandContext.java +++ b/src/com/hypixel/hytale/server/core/command/system/CommandContext.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.server.core.command.system.arguments.system.AbstractOp import com.hypixel.hytale.server.core.command.system.arguments.system.Argument; import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg; import com.hypixel.hytale.server.core.command.system.exceptions.SenderTypeException; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Map; @@ -87,7 +87,7 @@ public final class CommandContext { } public boolean isPlayer() { - return this.sender instanceof Player; + return this.sender instanceof PlayerRef; } @Nonnull @@ -101,7 +101,7 @@ public final class CommandContext { @Nullable public Ref senderAsPlayerRef() { - return this.senderAs(Player.class).getReference(); + return this.senderAs(PlayerRef.class).getReference(); } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/system/CommandManager.java b/src/com/hypixel/hytale/server/core/command/system/CommandManager.java index f8198931..b186c069 100644 --- a/src/com/hypixel/hytale/server/core/command/system/CommandManager.java +++ b/src/com/hypixel/hytale/server/core/command/system/CommandManager.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.server.core.command.system; import com.hypixel.hytale.common.util.CompletableFutureUtil; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.logger.sentry.SkipSentryException; +import com.hypixel.hytale.protocol.packets.interface_.ArgCacheInvalidation; +import com.hypixel.hytale.protocol.packets.interface_.CommandTreeSync; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.particle.commands.ParticleCommand; import com.hypixel.hytale.server.core.command.commands.debug.AssetsCommand; @@ -50,7 +50,6 @@ import com.hypixel.hytale.server.core.command.commands.utility.NotifyCommand; import com.hypixel.hytale.server.core.command.commands.utility.StashCommand; import com.hypixel.hytale.server.core.command.commands.utility.UIGalleryCommand; import com.hypixel.hytale.server.core.command.commands.utility.ValidateCPBCommand; -import com.hypixel.hytale.server.core.command.commands.utility.git.GitCommand; import com.hypixel.hytale.server.core.command.commands.utility.help.HelpCommand; import com.hypixel.hytale.server.core.command.commands.utility.lighting.LightingCommand; import com.hypixel.hytale.server.core.command.commands.utility.metacommands.CommandsCommand; @@ -62,11 +61,14 @@ import com.hypixel.hytale.server.core.command.commands.world.SpawnBlockCommand; import com.hypixel.hytale.server.core.command.commands.world.chunk.ChunkCommand; import com.hypixel.hytale.server.core.command.commands.world.entity.EntityCommand; import com.hypixel.hytale.server.core.command.commands.world.worldgen.WorldGenCommand; +import com.hypixel.hytale.server.core.command.system.arguments.system.AbstractOptionalArg; +import com.hypixel.hytale.server.core.command.system.arguments.system.Argument; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; +import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType; import com.hypixel.hytale.server.core.command.system.exceptions.CommandException; import com.hypixel.hytale.server.core.command.system.exceptions.GeneralCommandException; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.core.universe.Universe; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Deque; import java.util.HashSet; @@ -87,6 +89,8 @@ public class CommandManager implements CommandOwner { private static CommandManager instance; private final Map commandRegistration = new Object2ObjectOpenHashMap<>(); private final Map aliases = new Object2ObjectOpenHashMap<>(); + private final Map> argTypeRegistry = new Object2ObjectOpenHashMap<>(); + private final CommandTreeBuilder commandTreeBuilder = new CommandTreeBuilder(); public static CommandManager get() { return instance; @@ -100,6 +104,17 @@ public class CommandManager implements CommandOwner { this.aliases.clear(); } + @Nullable + public ArgumentType getArgTypeById(@Nonnull String typeId) { + return this.argTypeRegistry.get(typeId); + } + + public void broadcastArgCacheInvalidation(@Nonnull String... argTypeIds) { + ArgCacheInvalidation packet = new ArgCacheInvalidation(); + packet.argTypeIds = argTypeIds; + Universe.get().broadcastPacket(packet); + } + @Nonnull public Map getCommandRegistration() { return this.commandRegistration; @@ -161,7 +176,6 @@ public class CommandManager implements CommandOwner { this.registerSystemCommand(new NetworkCommand()); this.registerSystemCommand(new CommandsCommand()); this.registerSystemCommand(new UIGalleryCommand()); - this.registerSystemCommand(new GitCommand()); } public Map> createVirtualPermissionGroups() { @@ -169,7 +183,7 @@ public class CommandManager implements CommandOwner { for (AbstractCommand command : this.commandRegistration.values()) { for (Entry> entry : command.getPermissionGroupsRecursive().entrySet()) { - Set permissionsForGroup = permissionsByGroup.computeIfAbsent(entry.getKey(), k -> new HashSet<>()); + Set permissionsForGroup = permissionsByGroup.computeIfAbsent(entry.getKey(), groupName -> new HashSet<>()); permissionsForGroup.addAll(entry.getValue()); } } @@ -210,6 +224,7 @@ public class CommandManager implements CommandOwner { this.aliases.put(alias, name); } + this.registerArgTypes(command); return new CommandRegistration(command, () -> true, () -> { AbstractCommand remove = this.commandRegistration.remove(name); if (remove != null) { @@ -223,16 +238,39 @@ public class CommandManager implements CommandOwner { } } - @Nonnull - public CompletableFuture handleCommand(@Nonnull PlayerRef playerRef, @Nonnull String command) { - Ref ref = playerRef.getReference(); - if (ref == null) { - return new CompletableFuture<>(); - } else { - Store store = ref.getStore(); - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - return this.handleCommand(playerComponent, command); + private void registerArgTypes(@Nonnull AbstractCommand command) { + for (RequiredArg arg : command.getRequiredArguments()) { + ArgumentType type = arg.getArgumentType(); + this.argTypeRegistry.putIfAbsent(type.getSuggestionTypeId(), type); } + + for (Entry> entry : command.getOptionalArguments().entrySet()) { + AbstractOptionalArg, ?> arg = (AbstractOptionalArg, ?>)entry.getValue(); + if (!(arg instanceof FlagArg)) { + ArgumentType type = arg.getArgumentType(); + this.argTypeRegistry.putIfAbsent(type.getSuggestionTypeId(), type); + } + } + + List overrides = command.getSuggestionOverrides(); + if (overrides != null) { + for (AbstractCommand.SuggestionOverrideEntry override : overrides) { + this.argTypeRegistry.putIfAbsent(override.argTypeId(), override.overrideType()); + } + } + + for (AbstractCommand variant : command.getVariantCommands()) { + this.registerArgTypes(variant); + } + + for (AbstractCommand subcommand : command.getSubCommands().values()) { + this.registerArgTypes(subcommand); + } + } + + @Nonnull + public CommandTreeSync buildCommandTree(@Nonnull CommandSender sender) { + return this.commandTreeBuilder.build(sender, this.commandRegistration.values()); } @Nonnull @@ -246,7 +284,7 @@ public class CommandManager implements CommandOwner { thread.setName(oldName + " -- Running: " + commandString); try { - LOGGER.at(Level.FINE).log("%s sent command: %s", commandSender.getDisplayName(), commandString); + LOGGER.at(Level.FINE).log("%s sent command: %s", commandSender.getUsername(), commandString); int endIndex = commandString.indexOf(32); String commandName = (endIndex < 0 ? commandString : commandString.substring(0, endIndex)).toLowerCase(); AbstractCommand command = this.commandRegistration.get(commandName); @@ -275,7 +313,7 @@ public class CommandManager implements CommandOwner { @Nonnull CommandSender commandSender, @Nonnull String commandInput, @Nonnull AbstractCommand abstractCommand, @Nonnull CompletableFuture future ) { try { - LOGGER.at(Level.INFO).log("%s executed command: %s", commandSender.getDisplayName(), commandInput); + LOGGER.at(Level.INFO).log("%s executed command: %s", commandSender.getUsername(), commandInput); ParseResult parseResult = new ParseResult(); List tokens = Tokenizer.parseArguments(commandInput, parseResult); if (parseResult.failed()) { @@ -284,7 +322,7 @@ public class CommandManager implements CommandOwner { return; } - ParserContext parserContext = ParserContext.of(tokens, parseResult); + ParserContext parserContext = ParserContext.of(tokens, commandInput, parseResult); if (parseResult.failed()) { parseResult.sendMessages(commandSender); future.complete(null); @@ -305,7 +343,7 @@ public class CommandManager implements CommandOwner { if (!CompletableFutureUtil.isCanceled(throwable) && !isInternalException(throwable)) { LOGGER.at(Level.SEVERE) .withCause(new SkipSentryException(throwable)) - .log("Failed to execute command %s for %s", commandInput, commandSender.getDisplayName()); + .log("Failed to execute command %s for %s", commandInput, commandSender.getUsername()); commandSender.sendMessage( Message.translation("server.modules.command.error").param("cmd", commandInput).param("msg", throwable.getMessage()) ); @@ -324,7 +362,7 @@ public class CommandManager implements CommandOwner { if (var9 instanceof CommandException commandException) { commandException.sendTranslatedMessage(commandSender); } else { - LOGGER.at(Level.SEVERE).withCause(var9).log("Failed to execute command %s for %s", commandInput, commandSender.getDisplayName()); + LOGGER.at(Level.SEVERE).withCause(var9).log("Failed to execute command %s for %s", commandInput, commandSender.getUsername()); Message errorMsg = var9.getMessage() == null ? Message.translation("server.modules.command.noProvidedExceptionMessage") : Message.raw(var9.getMessage()); diff --git a/src/com/hypixel/hytale/server/core/command/system/CommandSender.java b/src/com/hypixel/hytale/server/core/command/system/CommandSender.java index c86ed655..22e911b3 100644 --- a/src/com/hypixel/hytale/server/core/command/system/CommandSender.java +++ b/src/com/hypixel/hytale/server/core/command/system/CommandSender.java @@ -5,7 +5,7 @@ import com.hypixel.hytale.server.core.receiver.IMessageReceiver; import java.util.UUID; public interface CommandSender extends IMessageReceiver, PermissionHolder { - String getDisplayName(); + String getUsername(); UUID getUuid(); } diff --git a/src/com/hypixel/hytale/server/core/command/system/CommandTreeBuilder.java b/src/com/hypixel/hytale/server/core/command/system/CommandTreeBuilder.java new file mode 100644 index 00000000..90743542 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/command/system/CommandTreeBuilder.java @@ -0,0 +1,212 @@ +package com.hypixel.hytale.server.core.command.system; + +import com.hypixel.hytale.protocol.packets.interface_.CommandArgInfo; +import com.hypixel.hytale.protocol.packets.interface_.CommandOptionalArgEntry; +import com.hypixel.hytale.protocol.packets.interface_.CommandSuggestionOverride; +import com.hypixel.hytale.protocol.packets.interface_.CommandTreeEntry; +import com.hypixel.hytale.protocol.packets.interface_.CommandTreeSync; +import com.hypixel.hytale.protocol.packets.interface_.CommandVariantEntry; +import com.hypixel.hytale.server.core.command.system.arguments.system.AbstractOptionalArg; +import com.hypixel.hytale.server.core.command.system.arguments.system.Argument; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; +import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; +import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map.Entry; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +class CommandTreeBuilder { + private static final Comparator BY_ARG_COUNT = Comparator.comparingInt(cmd -> cmd.getRequiredArguments().size()); + + @Nonnull + CommandTreeSync build(@Nonnull CommandSender sender, @Nonnull Collection commands) { + ArrayList entries = new ArrayList<>(); + + for (AbstractCommand command : commands) { + if (command.hasPermission(sender)) { + entries.add(this.buildTreeEntry(sender, command)); + } + } + + CommandTreeSync packet = new CommandTreeSync(); + packet.commands = entries.toArray(new CommandTreeEntry[0]); + return packet; + } + + @Nonnull + private CommandTreeEntry buildTreeEntry(@Nonnull CommandSender sender, @Nonnull AbstractCommand command) { + return this.buildTreeEntry(sender, command, "/" + command.getName()); + } + + private CommandTreeEntry buildTreeEntry(@Nonnull CommandSender sender, @Nonnull AbstractCommand command, @Nonnull String prefix) { + CommandTreeEntry entry = new CommandTreeEntry(); + entry.name = command.getName(); + entry.aliases = command.getAliases().toArray(new String[0]); + entry.description = command.getDescription(); + entry.usageText = this.buildUsageText(command, prefix); + entry.variants = this.buildVariantEntries(command, prefix); + ArrayList subEntries = new ArrayList<>(); + ArrayList subHints = new ArrayList<>(); + + for (AbstractCommand subCmd : command.getSubCommands().values()) { + if (subCmd.hasPermission(sender)) { + subEntries.add(this.buildTreeEntry(sender, subCmd, prefix + " " + subCmd.getName())); + subHints.add(this.buildSubcommandHint(subCmd)); + } + } + + entry.subcommands = subEntries.toArray(new CommandTreeEntry[0]); + entry.subcommandHints = subHints.toArray(new String[0]); + entry.requiredArgs = buildArgInfoArray(command.getRequiredArguments()); + entry.suggestionOverrides = buildSuggestionOverrides(command); + ArrayList optArgs = new ArrayList<>(); + + for (Entry> optEntry : command.getOptionalArguments().entrySet()) { + AbstractOptionalArg, ?> arg = (AbstractOptionalArg, ?>)optEntry.getValue(); + if (arg.hasPermission(sender)) { + CommandOptionalArgEntry opt = new CommandOptionalArgEntry(); + opt.name = "--" + optEntry.getKey(); + opt.description = arg.getDescription(); + if (arg instanceof FlagArg) { + opt.hint = ""; + opt.argTypeId = null; + } else { + opt.hint = "=<" + arg.getName() + ">"; + opt.argTypeId = arg.getArgumentType().getSuggestionTypeId(); + } + + optArgs.add(opt); + } + } + + entry.optionalArgs = optArgs.toArray(new CommandOptionalArgEntry[0]); + return entry; + } + + @Nonnull + private CommandVariantEntry[] buildVariantEntries(@Nonnull AbstractCommand command, @Nonnull String prefix) { + if (command.getVariantCommands().isEmpty()) { + return new CommandVariantEntry[0]; + } else { + ArrayList allCommands = new ArrayList<>(); + allCommands.add(command); + allCommands.addAll(command.getVariantCommands()); + allCommands.sort(BY_ARG_COUNT); + ArrayList entries = new ArrayList<>(); + + for (AbstractCommand variant : allCommands) { + if (variant != command + || !(variant.getRequiredArguments().isEmpty() && command instanceof AbstractCommandCollection collection) + || collection.isNoArgVariant()) { + CommandVariantEntry variantEntry = new CommandVariantEntry(); + if (variant.getRequiredArguments().isEmpty()) { + variantEntry.pattern = command.getName(); + } else { + variantEntry.pattern = this.buildVariantArgPattern(variant); + } + + variantEntry.description = variant.getDescription(); + variantEntry.requiredArgs = buildArgInfoArray(variant.getRequiredArguments()); + variantEntry.suggestionOverrides = buildSuggestionOverrides(variant); + variantEntry.usageText = buildArgUsageText(prefix, variant.getRequiredArguments()); + entries.add(variantEntry); + } + } + + return entries.toArray(new CommandVariantEntry[0]); + } + } + + @Nonnull + private static CommandArgInfo[] buildArgInfoArray(@Nonnull List> args) { + CommandArgInfo[] result = new CommandArgInfo[args.size()]; + + for (int i = 0; i < args.size(); i++) { + RequiredArg arg = args.get(i); + CommandArgInfo info = new CommandArgInfo(); + info.name = arg.getName(); + info.description = arg.getDescription(); + info.argTypeId = arg.getArgumentType().getSuggestionTypeId(); + info.argTypeName = arg.getArgumentType().getName().getMessageId(); + info.valueCount = arg.getArgumentType().getSuggestionValueCount(); + result[i] = info; + } + + return result; + } + + @Nullable + private static CommandSuggestionOverride[] buildSuggestionOverrides(@Nonnull AbstractCommand command) { + List overrides = command.getSuggestionOverrides(); + if (overrides == null) { + return null; + } else { + CommandSuggestionOverride[] result = new CommandSuggestionOverride[overrides.size()]; + + for (int i = 0; i < overrides.size(); i++) { + AbstractCommand.SuggestionOverrideEntry source = overrides.get(i); + CommandSuggestionOverride entry = new CommandSuggestionOverride(); + entry.argStart = source.argStart(); + entry.argCount = source.argCount(); + entry.argTypeId = source.argTypeId(); + result[i] = entry; + } + + return result; + } + } + + @Nonnull + private String buildSubcommandHint(@Nonnull AbstractCommand subCmd) { + if (!subCmd.getSubCommands().isEmpty() || !subCmd.getVariantCommands().isEmpty()) { + return " ..."; + } else { + return !subCmd.getRequiredArguments().isEmpty() ? this.buildVariantArgPattern(subCmd) : ""; + } + } + + @Nonnull + private String buildVariantArgPattern(@Nonnull AbstractCommand variant) { + List> args = variant.getRequiredArguments(); + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < args.size(); i++) { + if (i > 0) { + builder.append(" "); + } + + builder.append("<").append(args.get(i).getName()).append(">"); + } + + return builder.toString(); + } + + @Nonnull + private String buildUsageText(@Nonnull AbstractCommand command, @Nonnull String commandPrefix) { + Collection variants = command.getVariantCommands(); + if (!variants.isEmpty()) { + ArrayList allCommands = new ArrayList<>(); + allCommands.add(command); + allCommands.addAll(variants); + allCommands.sort(BY_ARG_COUNT); + return buildArgUsageText(commandPrefix, allCommands.getLast().getRequiredArguments()); + } else { + return buildArgUsageText(commandPrefix, command.getRequiredArguments()); + } + } + + @Nonnull + private static String buildArgUsageText(@Nonnull String prefix, @Nonnull List> args) { + StringBuilder builder = new StringBuilder(prefix); + + for (RequiredArg arg : args) { + builder.append(" <").append(arg.getName()).append(">"); + } + + return builder.toString(); + } +} diff --git a/src/com/hypixel/hytale/server/core/command/system/ParserContext.java b/src/com/hypixel/hytale/server/core/command/system/ParserContext.java index c09de2cf..814ff3a7 100644 --- a/src/com/hypixel/hytale/server/core/command/system/ParserContext.java +++ b/src/com/hypixel/hytale/server/core/command/system/ParserContext.java @@ -23,6 +23,8 @@ public class ParserContext { @Nonnull private final String inputString; @Nonnull + private final String rawInput; + @Nonnull private final BooleanArrayList parameterForwardingMap; @Nonnull private final Int2ObjectMap preOptionalSingleValueTokens; @@ -33,13 +35,12 @@ public class ParserContext { private String lastInsertedOptionalArgName; private int numPreOptSingleValueTokensBeforeListTokens; private int subCommandIndex; - private static final Pattern ARG_NAME_PATTERN = Pattern.compile("--(\\w*)"); - private static final Matcher ARG_NAME_MATCHER = ARG_NAME_PATTERN.matcher(""); - private static final Pattern ARG_NAME_AND_VALUE_PATTERN = Pattern.compile("--(\\w+)=\"*(.*)\"*"); - private static final Matcher ARG_NAME_AND_VALUE_MATCHER = ARG_NAME_AND_VALUE_PATTERN.matcher(""); + private static final Pattern ARG_NAME_PATTERN = Pattern.compile("--([\\w-]*)"); + private static final Pattern ARG_NAME_AND_VALUE_PATTERN = Pattern.compile("--(\\w+)=(.+)"); - public ParserContext(@Nonnull List tokens, @Nonnull ParseResult parseResult) { + public ParserContext(@Nonnull List tokens, @Nonnull String rawInput, @Nonnull ParseResult parseResult) { this.inputString = String.join(" ", tokens); + this.rawInput = rawInput; this.parameterForwardingMap = new BooleanArrayList(); this.preOptionalSingleValueTokens = new Int2ObjectOpenHashMap<>(); this.preOptionalListTokens = new Int2ObjectOpenHashMap<>(); @@ -48,8 +49,21 @@ public class ParserContext { } @Nonnull - public static ParserContext of(@Nonnull List tokens, @Nonnull ParseResult parseResult) { - return new ParserContext(tokens, parseResult); + public static ParserContext of(@Nonnull List tokens, @Nonnull String rawInput, @Nonnull ParseResult parseResult) { + return new ParserContext(tokens, rawInput, parseResult); + } + + @Nonnull + private static String stripSurroundingQuotes(@Nonnull String value) { + if (value.length() >= 2) { + char first = value.charAt(0); + char last = value.charAt(value.length() - 1); + if (first == '"' && last == '"' || first == '\'' && last == '\'') { + return value.substring(1, value.length() - 1); + } + } + + return value; } private void contextualizeTokens(@Nonnull List tokens, @Nonnull ParseResult parseResult) { @@ -58,6 +72,8 @@ public class ParserContext { boolean isSingleValueList = false; boolean wasLastTokenASpecialValue = false; boolean hasEnteredListBefore = false; + Matcher argMatcher = ARG_NAME_PATTERN.matcher(""); + Matcher argNameAndValueMatcher = ARG_NAME_AND_VALUE_PATTERN.matcher(""); for (int i = 0; i < tokens.size(); i++) { String token = tokens.get(i); @@ -86,13 +102,14 @@ public class ParserContext { wasLastTokenASpecialValue = false; } - ARG_NAME_MATCHER.reset(token); - if (ARG_NAME_MATCHER.lookingAt()) { + argMatcher.reset(token); + if (argMatcher.lookingAt()) { beganParsingOptionals = true; - this.addNewOptionalArg(ARG_NAME_MATCHER.group(1)); - ARG_NAME_AND_VALUE_MATCHER.reset(token); - if (ARG_NAME_AND_VALUE_MATCHER.matches()) { - this.appendOptionalParameter(ARG_NAME_AND_VALUE_MATCHER.group(2), parseResult); + this.addNewOptionalArg(argMatcher.group(1)); + argNameAndValueMatcher.reset(token); + if (argNameAndValueMatcher.matches()) { + String value = stripSurroundingQuotes(argNameAndValueMatcher.group(2)); + this.appendOptionalParameter(value, parseResult); if (parseResult.failed()) { return; } @@ -171,6 +188,15 @@ public class ParserContext { return this.inputString; } + @Nonnull + public String getRawInput() { + return this.rawInput; + } + + public int getSubCommandIndex() { + return this.subCommandIndex; + } + public boolean isListToken(int index) { index += this.subCommandIndex; return this.parameterForwardingMap.size() <= index ? false : this.parameterForwardingMap.getBoolean(index); diff --git a/src/com/hypixel/hytale/server/core/command/system/Tokenizer.java b/src/com/hypixel/hytale/server/core/command/system/Tokenizer.java index 87da2868..1c6cfde6 100644 --- a/src/com/hypixel/hytale/server/core/command/system/Tokenizer.java +++ b/src/com/hypixel/hytale/server/core/command/system/Tokenizer.java @@ -33,7 +33,7 @@ public class Tokenizer { boolean extractToken; char c = argsStr.charAt(i); extractToken = false; - label88: + label99: switch (c) { case ' ': if (quote == 0) { @@ -58,7 +58,7 @@ public class Tokenizer { } break; case '\'': - if (quote == 0) { + if (quote == 0 && tokenStart == i) { quote = '\''; } else if (quote == '\'') { quote = 0; @@ -109,7 +109,7 @@ public class Tokenizer { case ']': argsStr = argsStr.substring(0, i) + argsStr.substring(i + 1); i++; - break label88; + break label99; default: parseResult.fail( Message.translation("server.commands.parsing.error.invalidEscapeForSymbol") diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/system/RequiredArg.java b/src/com/hypixel/hytale/server/core/command/system/arguments/system/RequiredArg.java index 9c2ab822..469f23e9 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/system/RequiredArg.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/system/RequiredArg.java @@ -4,8 +4,12 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.AbstractCommand; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class RequiredArg extends Argument, DataType> { + @Nullable + private ArgumentType suggestionOverrideType; + public RequiredArg( @Nonnull AbstractCommand commandRegisteredTo, @Nonnull String name, @Nonnull String description, @Nonnull ArgumentType argumentType ) { @@ -15,6 +19,17 @@ public class RequiredArg extends Argument, DataT } } + @Nonnull + public RequiredArg withSuggestionOverride(@Nonnull ArgumentType overrideType) { + this.suggestionOverrideType = overrideType; + return this; + } + + @Nullable + public ArgumentType getSuggestionOverrideType() { + return this.suggestionOverrideType; + } + @Nonnull public Message getUsageMessageWithoutDescription() { return Message.raw("<").insert(this.getName()).insert(":").insert(this.getArgumentType().getName()).insert(">"); diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/AbstractAssetArgumentType.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/AbstractAssetArgumentType.java index 557b4b33..612bff9c 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/AbstractAssetArgumentType.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/AbstractAssetArgumentType.java @@ -3,11 +3,17 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.assetstore.AssetMap; import com.hypixel.hytale.assetstore.AssetRegistry; import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.common.util.StringCompareUtil; import com.hypixel.hytale.common.util.StringUtil; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.command.system.CommandUtil; import com.hypixel.hytale.server.core.command.system.ParseResult; +import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionResult; import java.awt.Color; +import java.util.List; +import java.util.Locale; +import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -54,4 +60,40 @@ public abstract class AbstractAssetArgumentType keys = this.getAssetMap().getAssetMap().keySet(); + int maxSuggestions = 20; + if (textAlreadyEntered.isEmpty()) { + int count = 0; + + for (AssetKeyType key : keys) { + result.suggest(key.toString()); + if (++count >= 20) { + break; + } + } + } else { + int minScore = textAlreadyEntered.length(); + List sorted = StringUtil.sortByFuzzyDistance(textAlreadyEntered, keys); + int count = 0; + + for (AssetKeyType keyx : sorted) { + if (StringCompareUtil.getFuzzyDistance(keyx.toString(), textAlreadyEntered, Locale.ENGLISH) < minScore) { + break; + } + + result.suggest(keyx.toString()); + if (++count >= 20) { + break; + } + } + } + } + + @Override + public int getSuggestionValueCount() { + return this.getAssetMap().getAssetMap().size(); + } } diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/ArgTypes.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/ArgTypes.java index 3f447fcf..e0840157 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/ArgTypes.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/ArgTypes.java @@ -1,8 +1,9 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.common.util.StringCompareUtil; +import com.hypixel.hytale.common.util.StringUtil; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.Message; @@ -30,6 +31,7 @@ import com.hypixel.hytale.server.core.modules.entity.hitboxcollision.HitboxColli import com.hypixel.hytale.server.core.modules.entity.repulsion.RepulsionConfig; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; +import com.hypixel.hytale.server.core.prefab.selection.mask.BlockFilter; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockPattern; import com.hypixel.hytale.server.core.universe.PlayerRef; @@ -39,11 +41,17 @@ import it.unimi.dsi.fastutil.Pair; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Locale; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.function.BiFunction; +import java.util.function.Function; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; +import org.joml.Vector3d; +import org.joml.Vector3i; public final class ArgTypes { public static final SingleArgumentType BOOLEAN = new SingleArgumentType( @@ -68,6 +76,11 @@ public final class ArgTypes { result.suggest("false"); } } + + @Override + public int getSuggestionValueCount() { + return 2; + } }; public static final SingleArgumentType INTEGER = new SingleArgumentType( "server.commands.parsing.argtype.integer.name", "server.commands.parsing.argtype.integer.usage", "-27432", "-1", "0", "1", "56346" @@ -93,6 +106,22 @@ public final class ArgTypes { return input; } }; + public static final SingleArgumentType GREEDY_STRING = new SingleArgumentType( + "server.commands.parsing.argtype.greedystring.name", + "server.commands.parsing.argtype.greedystring.usage", + "Hello world!", + "Let's go everyone", + "This is a multi-word sentence." + ) { + public String parse(String input, ParseResult parseResult) { + return input; + } + + @Override + public boolean isGreedyString() { + return true; + } + }; public static final SingleArgumentType FLOAT = new SingleArgumentType( "server.commands.parsing.argtype.float.name", "server.commands.parsing.argtype.float.usage", "3.14159", "-2.5", "7" ) { @@ -232,6 +261,11 @@ public final class ArgTypes { return null; } } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + ArgTypes.suggestOnlinePlayers(sender, textAlreadyEntered, result); + } }; public static final SingleArgumentType RELATIVE_DOUBLE_COORD = new SingleArgumentType( "server.commands.parsing.argtype.doubleCoordinate.name", "server.commands.parsing.argtype.doubleCoordinate.usage", "5.0", "~-2.3", "0.0" @@ -245,20 +279,35 @@ public final class ArgTypes { return null; } } - }; - public static final SingleArgumentType RELATIVE_INT_COORD = new SingleArgumentType( - "server.commands.parsing.argtype.integerCoordinate.name", "server.commands.parsing.argtype.integerCoordinate.usage", "5", "~-2", "0" - ) { - @Nullable - public IntCoord parse(@Nonnull String input, ParseResult parseResult) { - try { - return IntCoord.parse(input); - } catch (NumberFormatException var4) { - parseResult.fail(Message.translation("server.commands.parsing.argtype.integerCoordinate.fail").param("input", input)); - return null; + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + if (textAlreadyEntered.isEmpty() || textAlreadyEntered.startsWith("~")) { + result.suggest("~"); } } }; + public static final SingleArgumentType RELATIVE_INT_COORD = (new SingleArgumentType( + "server.commands.parsing.argtype.integerCoordinate.name", "server.commands.parsing.argtype.integerCoordinate.usage", "5", "~-2", "0" + ) { + @Nullable + public IntCoord parse(@Nonnull String input, ParseResult parseResult) { + try { + return IntCoord.parse(input); + } catch (NumberFormatException var4) { + parseResult.fail(Message.translation("server.commands.parsing.argtype.integerCoordinate.fail").param("input", input)); + return null; + } + } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + if (textAlreadyEntered.isEmpty() || textAlreadyEntered.startsWith("~")) { + result.suggest("~"); + } + } + }) + .withSharedSuggestions(RELATIVE_DOUBLE_COORD); public static final SingleArgumentType RELATIVE_INTEGER = new SingleArgumentType( "Relative Integer", "A tilde to mark an integer as relative to a base", "5", "~-2", "0" ) { @@ -267,49 +316,63 @@ public final class ArgTypes { return RelativeInteger.parse(input, parseResult); } }; - public static final SingleArgumentType RELATIVE_FLOAT = new SingleArgumentType( - "server.commands.parsing.argtype.relativeFloat.name", "server.commands.parsing.argtype.relativeFloat.usage", "90.0", "~-45.5", "~" - ) { - @Nullable - public RelativeFloat parse(@Nonnull String input, @Nonnull ParseResult parseResult) { - return RelativeFloat.parse(input, parseResult); - } - }; - public static final SingleArgumentType PLAYER_REF = new SingleArgumentType( - "server.commands.parsing.argtype.player.name", "server.commands.parsing.argtype.player.usage", "john_doe", "user123" - ) { - @Nullable - public PlayerRef parse(@Nonnull String input, @Nonnull ParseResult parseResult) { - PlayerRef playerRef = null; + public static final SingleArgumentType RELATIVE_FLOAT = (new SingleArgumentType( + "server.commands.parsing.argtype.relativeFloat.name", "server.commands.parsing.argtype.relativeFloat.usage", "90.0", "~-45.5", "~" + ) { + @Nullable + public RelativeFloat parse(@Nonnull String input, @Nonnull ParseResult parseResult) { + return RelativeFloat.parse(input, parseResult); + } - for (World world : Universe.get().getWorlds().values()) { - Collection playerRefs = world.getPlayerRefs(); - playerRef = NameMatching.DEFAULT.find(playerRefs, input, PlayerRef::getUsername); - if (playerRef != null) { - break; + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + if (textAlreadyEntered.isEmpty() || textAlreadyEntered.startsWith("~")) { + result.suggest("~"); + } + } + }) + .withSharedSuggestions(RELATIVE_DOUBLE_COORD); + public static final SingleArgumentType PLAYER_REF = (new SingleArgumentType( + "server.commands.parsing.argtype.player.name", "server.commands.parsing.argtype.player.usage", "john_doe", "user123" + ) { + @Nullable + public PlayerRef parse(@Nonnull String input, @Nonnull ParseResult parseResult) { + PlayerRef playerRef = null; + + for (World world : Universe.get().getWorlds().values()) { + Collection playerRefs = world.getPlayerRefs(); + playerRef = NameMatching.DEFAULT.find(playerRefs, input, PlayerRef::getUsername); + if (playerRef != null) { + break; + } + } + + if (playerRef == null) { + parseResult.fail(Message.translation("server.commands.errors.noSuchPlayer").param("username", input)); + return null; + } else { + return playerRef; } } - if (playerRef == null) { - parseResult.fail(Message.translation("server.commands.errors.noSuchPlayer").param("username", input)); - return null; - } else { - return playerRef; + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + ArgTypes.suggestOnlinePlayers(sender, textAlreadyEntered, result); } - } - @Nonnull - public PlayerRef processedGet(CommandSender sender, @Nonnull CommandContext context, Argument argument) { - PlayerRef playerRef = context.get(argument); - if (playerRef != null) { - return playerRef; - } else if (sender instanceof Player player) { - return player.getPlayerRef(); - } else { - throw new GeneralCommandException(Message.translation("server.commands.errors.playerOrArg").param("option", "player")); + @Nonnull + public PlayerRef processedGet(CommandSender sender, @Nonnull CommandContext context, Argument argument) { + PlayerRef playerRef = context.get(argument); + if (playerRef != null) { + return playerRef; + } else if (sender instanceof Player player) { + return player.getPlayerRef(); + } else { + throw new GeneralCommandException(Message.translation("server.commands.errors.playerOrArg").param("option", "player")); + } } - } - }; + }) + .withSharedSuggestions(GAME_PROFILE_LOOKUP); public static final SingleArgumentType WORLD = new SingleArgumentType( "server.commands.parsing.argtype.world.name", "server.commands.parsing.argtype.world.usage", "default" ) { @@ -324,15 +387,32 @@ public final class ArgTypes { } } + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + String lowerInput = textAlreadyEntered.toLowerCase(); + + for (World world : Universe.get().getWorlds().values()) { + String worldName = world.getName(); + if (worldName != null && worldName.toLowerCase().startsWith(lowerInput)) { + result.suggest(worldName); + } + } + } + @Nullable public World processedGet(CommandSender sender, @Nonnull CommandContext context, @Nonnull Argument argument) { World world = argument.get(context); if (world != null) { return world; - } else if (sender instanceof Player) { - return ((Player)sender).getWorld(); } else { Universe universe = Universe.get(); + if (sender instanceof PlayerRef playerRef) { + UUID worldUuid = playerRef.getWorldUuid(); + if (worldUuid != null) { + return universe.getWorld(playerRef.getWorldUuid()); + } + } + if (universe.getWorlds().size() == 1) { Iterator iterator = universe.getWorlds().values().iterator(); if (iterator.hasNext()) { @@ -531,6 +611,13 @@ public final class ArgTypes { public RelativeIntPosition parse(@Nonnull MultiArgumentContext context, ParseResult parseResult) { return new RelativeIntPosition(context.get(this.xValue), context.get(this.yValue), context.get(this.zValue)); } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + ArgTypes.suggestPosition( + sender, textAlreadyEntered, result, "~ ~ ~", pos -> (int)Math.floor(pos.x) + " " + (int)Math.floor(pos.y) + " " + (int)Math.floor(pos.z) + ); + } }; public static final ArgumentType RELATIVE_POSITION = new MultiArgumentType( "server.commands.parsing.argtype.relativePosition.name", @@ -547,6 +634,11 @@ public final class ArgTypes { public RelativeDoublePosition parse(@Nonnull MultiArgumentContext context, ParseResult parseResult) { return new RelativeDoublePosition(context.get(this.xValue), context.get(this.yValue), context.get(this.zValue)); } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + ArgTypes.suggestPosition(sender, textAlreadyEntered, result, "~ ~ ~", pos -> String.format("%.2f %.2f %.2f", pos.x, pos.y, pos.z)); + } }; public static final ArgumentType RELATIVE_CHUNK_POSITION = new MultiArgumentType( "server.commands.parsing.argtype.relativeChunkPosition.name", "server.commands.parsing.argtype.relativeChunkPosition.usage", "5 10", "~c2 ~c-3", "~ ~" @@ -558,8 +650,13 @@ public final class ArgTypes { public RelativeChunkPosition parse(@Nonnull MultiArgumentContext context, ParseResult parseResult) { return new RelativeChunkPosition(context.get(this.xValue), context.get(this.zValue)); } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + ArgTypes.suggestPosition(sender, textAlreadyEntered, result, "~ ~", pos -> ((int)Math.floor(pos.x) >> 5) + " " + ((int)Math.floor(pos.z) >> 5)); + } }; - public static final ArgumentType ROTATION = new MultiArgumentType( + public static final ArgumentType ROTATION = new MultiArgumentType( "server.commands.parsing.argtype.rotation.name", "server.commands.parsing.argtype.rotation.usage", "124.63 232.27 234.22" ) { private final WrappedArgumentType pitch = this.withParameter( @@ -573,8 +670,8 @@ public final class ArgTypes { ); @Nonnull - public Vector3f parse(@Nonnull MultiArgumentContext context, ParseResult parseResult) { - return new Vector3f(context.get(this.pitch), context.get(this.yaw), context.get(this.roll)); + public Rotation3fc parse(@Nonnull MultiArgumentContext context, ParseResult parseResult) { + return new Rotation3f(context.get(this.pitch), context.get(this.yaw), context.get(this.roll)); } }; public static final SingleArgumentType BLOCK_TYPE_KEY = new SingleArgumentType("Block Type Key", "A block type", "Wood_Drywood_Planks_Half") { @@ -587,6 +684,17 @@ public final class ArgTypes { return null; } } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + ArgTypes.suggestBlockTypeKeys(textAlreadyEntered, "", result); + } + + @Nonnull + @Override + public int getSuggestionValueCount() { + return BlockType.getAssetMap().getAssetMap().size(); + } }; public static final ArgumentType BLOCK_ID = new ProcessedArgumentType( "Block Id", Message.raw("A block type, converted to an int id"), BLOCK_TYPE_KEY, "Wood_Drywood_Planks_Half" @@ -611,16 +719,16 @@ public final class ArgTypes { long value = Long.parseLong(hexString, 16); switch (hexString.length()) { case 3: - int r = (int)(value >> 8 & 15L); - int g = (int)(value >> 4 & 15L); - int b = (int)(value & 15L); - return 0xFF000000 | r << 20 | r << 16 | g << 12 | g << 8 | b << 4 | b; + int red = (int)(value >> 8 & 15L); + int green = (int)(value >> 4 & 15L); + int blue = (int)(value & 15L); + return 0xFF000000 | red << 20 | red << 16 | green << 12 | green << 8 | blue << 4 | blue; case 4: - int r4 = (int)(value >> 12 & 15L); - int g4 = (int)(value >> 8 & 15L); - int b4 = (int)(value >> 4 & 15L); - int a4 = (int)(value & 15L); - return r4 << 28 | r4 << 24 | g4 << 20 | g4 << 16 | b4 << 12 | b4 << 8 | a4 << 4 | a4; + int red4 = (int)(value >> 12 & 15L); + int green4 = (int)(value >> 8 & 15L); + int blue4 = (int)(value >> 4 & 15L); + int alpha4 = (int)(value & 15L); + return red4 << 28 | red4 << 24 | green4 << 20 | green4 << 16 | blue4 << 12 | blue4 << 8 | alpha4 << 4 | alpha4; case 5: case 7: default: @@ -711,6 +819,28 @@ public final class ArgTypes { } } } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + int percentIndex = textAlreadyEntered.indexOf(37); + if (percentIndex >= 0) { + String prefix = textAlreadyEntered.substring(0, percentIndex + 1); + String blockPartial = textAlreadyEntered.substring(percentIndex + 1); + int pipeIndex = blockPartial.indexOf(124); + if (pipeIndex < 0) { + ArgTypes.suggestBlockTypeKeys(blockPartial, prefix, result); + if (!blockPartial.isEmpty() && ArgTypes.isExactBlockTypeKey(blockPartial)) { + result.suggestContinuation(textAlreadyEntered + ","); + } + } + } else if (!textAlreadyEntered.isEmpty() + && Character.isDigit(textAlreadyEntered.charAt(0)) + && textAlreadyEntered.chars().allMatch(c -> Character.isDigit((char)c) || c == 46)) { + result.suggestContinuation(textAlreadyEntered + "%"); + } else { + ArgTypes.suggestBlockTypeKeys(textAlreadyEntered, "", result); + } + } }; public static final ArgumentType BLOCK_PATTERN = new ProcessedArgumentType, BlockPattern>( "Block Pattern", @@ -726,18 +856,65 @@ public final class ArgTypes { return BlockPattern.parse(patternString); } }; + private static final String[] MASK_PREFIXES = new String[]{"!", ">", "<", "~", "^", "#", "+n", "+e", "+s", "+w", "%xy", "%xz", "%zy"}; + private static final String[] MASK_INVERT_PREFIXES = new String[]{"!>", "!<", "!~", "!^", "!#", "!+n", "!+e", "!+s", "!+w", "!%xy", "!%xz", "!%zy"}; private static final ArgumentType INDIVIDUAL_BLOCK_MASK = new SingleArgumentType( "Block Mask", "Create a block mask using symbols and block names", ">Grass_Full", "!Fluid_Water", "!^Fluid_Lava", "!#" ) { @Nullable public BlockMask parse(@Nonnull String input, @Nonnull ParseResult parseResult) { try { - return BlockMask.parse(input); - } catch (Exception var4) { + BlockMask mask = BlockMask.parse(input); + if (mask.hasInvalidBlocks()) { + BlockFilter.ParsedFilterParts parts = BlockFilter.parseComponents(input); + parseResult.fail(Message.translation("server.builderTools.invalidBlockType").param("key", parts.blocks())); + return null; + } else { + return mask; + } + } catch (Exception var5) { parseResult.fail(Message.raw("There was an error in the parsing of your block mask: " + input + ", please try again.")); return null; } } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + if (textAlreadyEntered.isEmpty()) { + for (String p : ArgTypes.MASK_PREFIXES) { + result.suggest(p); + } + } else if ("!".equals(textAlreadyEntered)) { + for (String p : ArgTypes.MASK_INVERT_PREFIXES) { + result.suggest(p); + } + + ArgTypes.suggestBlockTypeKeys(textAlreadyEntered, "!", result); + } else { + String stripped = textAlreadyEntered; + String prefix = ""; + + for (String p : ArgTypes.MASK_INVERT_PREFIXES) { + if (stripped.startsWith(p)) { + prefix = p; + stripped = stripped.substring(p.length()); + break; + } + } + + if (prefix.isEmpty()) { + for (String px : ArgTypes.MASK_PREFIXES) { + if (!px.equals("#") && stripped.startsWith(px)) { + prefix = px; + stripped = stripped.substring(px.length()); + break; + } + } + } + + ArgTypes.suggestBlockTypeKeys(stripped, prefix, result); + } + } }; public static final ArgumentType BLOCK_MASK = new ProcessedArgumentType, BlockMask>( "Block Mask", @@ -794,12 +971,82 @@ public final class ArgTypes { } }; public static final SingleArgumentType GAME_MODE = new GameModeArgumentType(); + private static final int MAX_BLOCK_SUGGESTIONS = 20; @Nonnull public static > SingleArgumentType forEnum(String name, @Nonnull Class enumType) { return new EnumArgumentType<>(name, enumType); } + private static boolean isExactBlockTypeKey(@Nonnull String name) { + for (String key : BlockType.getAssetMap().getAssetMap().keySet()) { + if (key.toString().equalsIgnoreCase(name)) { + return true; + } + } + + return false; + } + + private static void suggestBlockTypeKeys(@Nonnull String textAlreadyEntered, @Nonnull String prefix, @Nonnull SuggestionResult result) { + Set keys = BlockType.getAssetMap().getAssetMap().keySet(); + if (textAlreadyEntered.isEmpty()) { + int count = 0; + + for (String key : keys) { + result.suggest(prefix + key.toString()); + if (++count >= 20) { + break; + } + } + } else { + int minScore = textAlreadyEntered.length(); + List sorted = StringUtil.sortByFuzzyDistance(textAlreadyEntered, keys); + int count = 0; + + for (String keyx : sorted) { + if (StringCompareUtil.getFuzzyDistance(keyx.toString(), textAlreadyEntered, Locale.ENGLISH) < minScore) { + break; + } + + result.suggest(prefix + keyx.toString()); + if (++count >= 20) { + break; + } + } + } + } + + private static void suggestOnlinePlayers(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, @Nonnull SuggestionResult result) { + String lowerInput = textAlreadyEntered.toLowerCase(); + + for (World world : Universe.get().getWorlds().values()) { + for (PlayerRef player : world.getPlayerRefs()) { + String username = player.getUsername(); + if (username != null && username.toLowerCase().startsWith(lowerInput)) { + result.suggest(username); + } + } + } + } + + private static void suggestPosition( + @Nonnull CommandSender sender, + @Nonnull String textAlreadyEntered, + @Nonnull SuggestionResult result, + @Nonnull String tildeTemplate, + @Nonnull Function formatter + ) { + if (textAlreadyEntered.isEmpty() || textAlreadyEntered.startsWith("~")) { + result.suggest(tildeTemplate); + } + + if (sender instanceof Player player) { + Vector3d pos = player.getPlayerRef().getTransform().getPosition(); + result.suggest(formatter.apply(pos)); + } + } + public static enum IntegerComparisonOperator { GREATER_THAN((left, right) -> left > right, ">"), GREATER_THAN_EQUAL_TO((left, right) -> left >= right, ">="), diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/ArgumentType.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/ArgumentType.java index a31d0634..54b976c6 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/ArgumentType.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/ArgumentType.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.server.core.command.system.arguments.system.Argument; import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionProvider; import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionResult; import java.util.Arrays; +import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -20,6 +21,8 @@ public abstract class ArgumentType implements SuggestionProvider { @Nonnull protected final String[] examples; protected int numberOfParameters; + @Nonnull + private String suggestionTypeId = UUID.randomUUID().toString(); protected ArgumentType(@Nonnull Message name, @Nonnull Message argumentUsage, int numberOfParameters, @Nullable String... examples) { this.name = name; @@ -75,6 +78,24 @@ public abstract class ArgumentType implements SuggestionProvider { return false; } + public boolean isGreedyString() { + return false; + } + + @Nonnull + public final String getSuggestionTypeId() { + return this.suggestionTypeId; + } + + public > T withSharedSuggestions(@Nonnull ArgumentType other) { + this.suggestionTypeId = other.getSuggestionTypeId(); + return (T)this; + } + + public int getSuggestionValueCount() { + return -1; + } + @Nonnull @Override public String toString() { diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/EnumArgumentType.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/EnumArgumentType.java index b59b80e9..e1fcf4c8 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/EnumArgumentType.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/EnumArgumentType.java @@ -2,8 +2,10 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.common.util.StringUtil; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.command.system.CommandUtil; import com.hypixel.hytale.server.core.command.system.ParseResult; +import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionResult; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import javax.annotation.Nonnull; @@ -50,6 +52,22 @@ public class EnumArgumentType> extends SingleArgumentType { return null; } + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + String lowerInput = textAlreadyEntered.toLowerCase(); + + for (String enumName : this.enumNames) { + if (enumName.toLowerCase().startsWith(lowerInput)) { + result.suggest(enumName.toLowerCase()); + } + } + } + + @Override + public int getSuggestionValueCount() { + return this.enumConstants.length; + } + @Nonnull public static > String getArgumentUsageString(@Nonnull X[] enumConstants) { StringBuilder stringBuilder = new StringBuilder(); diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/GameModeArgumentType.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/GameModeArgumentType.java index 686ae895..5c0250f4 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/GameModeArgumentType.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/GameModeArgumentType.java @@ -3,8 +3,10 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.common.util.StringUtil; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.command.system.CommandUtil; import com.hypixel.hytale.server.core.command.system.ParseResult; +import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionResult; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.List; @@ -34,6 +36,22 @@ public class GameModeArgumentType extends SingleArgumentType { } } + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + String lowerInput = textAlreadyEntered.toLowerCase(); + + for (String key : GAMEMODE_MAP.keySet()) { + if (key.startsWith(lowerInput)) { + result.suggest(key); + } + } + } + + @Override + public int getSuggestionValueCount() { + return GAMEMODE_MAP.size(); + } + static { GAMEMODE_MAP.put("adventure", GameMode.Adventure); GAMEMODE_MAP.put("creative", GameMode.Creative); diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/ListArgumentType.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/ListArgumentType.java index 6b7e8b34..c2b1ee62 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/ListArgumentType.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/ListArgumentType.java @@ -1,7 +1,9 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.command.system.ParseResult; +import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionResult; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Arrays; import java.util.List; @@ -20,6 +22,7 @@ public class ListArgumentType extends ArgumentType> { argumentType.getExamples() ); this.argumentType = argumentType; + this.withSharedSuggestions(argumentType); } @Override @@ -43,4 +46,14 @@ public class ListArgumentType extends ArgumentType> { return returnList; } + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + this.argumentType.suggest(sender, textAlreadyEntered, numParametersTyped, result); + } + + @Override + public int getSuggestionValueCount() { + return this.argumentType.getSuggestionValueCount(); + } } diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/ProcessedArgumentType.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/ProcessedArgumentType.java index 0f619711..6d29b20c 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/ProcessedArgumentType.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/ProcessedArgumentType.java @@ -1,7 +1,9 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.command.system.ParseResult; +import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionResult; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -12,6 +14,7 @@ public abstract class ProcessedArgumentType extends Argum public ProcessedArgumentType(String name, Message argumentUsage, @Nonnull ArgumentType inputTypeArgumentType, @Nullable String... examples) { super(name, argumentUsage, inputTypeArgumentType.numberOfParameters, examples); this.inputTypeArgumentType = inputTypeArgumentType; + this.withSharedSuggestions(inputTypeArgumentType); } @Nonnull @@ -37,4 +40,14 @@ public abstract class ProcessedArgumentType extends Argum } public abstract OutputType processInput(InputType var1); + + @Override + public void suggest(@Nonnull CommandSender sender, @Nonnull String textAlreadyEntered, int numParametersTyped, @Nonnull SuggestionResult result) { + this.inputTypeArgumentType.suggest(sender, textAlreadyEntered, numParametersTyped, result); + } + + @Override + public int getSuggestionValueCount() { + return this.inputTypeArgumentType.getSuggestionValueCount(); + } } diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeChunkPosition.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeChunkPosition.java index 35c2eb28..ca73d1ff 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeChunkPosition.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeChunkPosition.java @@ -3,14 +3,14 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.exceptions.GeneralCommandException; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2i; +import org.joml.Vector3d; public class RelativeChunkPosition { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeDirection.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeDirection.java index 059e65e8..691a7c26 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeDirection.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeDirection.java @@ -1,12 +1,12 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.math.Axis; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.ParseResult; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public enum RelativeDirection { FORWARD, @@ -52,7 +52,7 @@ public enum RelativeDirection { } else { return switch (direction) { case FORWARD -> headRotation.getHorizontalAxisDirection(); - case BACKWARD -> headRotation.getHorizontalAxisDirection().clone().scale(-1); + case BACKWARD -> new Vector3i(headRotation.getHorizontalAxisDirection()).negate(); case LEFT -> rotateLeft(headRotation.getHorizontalAxisDirection()); case RIGHT -> rotateRight(headRotation.getHorizontalAxisDirection()); case UP -> new Vector3i(0, 1, 0); @@ -73,13 +73,13 @@ public enum RelativeDirection { @Nonnull private static Axis getHorizontalAxis(@Nonnull HeadRotation headRotation) { Vector3i horizontalDir = headRotation.getHorizontalAxisDirection(); - return horizontalDir.getX() != 0 ? Axis.X : Axis.Z; + return horizontalDir.x() != 0 ? Axis.X : Axis.Z; } @Nonnull private static Axis getPerpendicularHorizontalAxis(@Nonnull HeadRotation headRotation) { Vector3i horizontalDir = headRotation.getHorizontalAxisDirection(); - return horizontalDir.getX() != 0 ? Axis.Z : Axis.X; + return horizontalDir.x() != 0 ? Axis.Z : Axis.X; } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeDoublePosition.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeDoublePosition.java index ec508972..7e7b8d8f 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeDoublePosition.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeDoublePosition.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.exceptions.GeneralCommandException; @@ -10,6 +9,7 @@ import com.hypixel.hytale.server.core.modules.entity.component.TransformComponen import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class RelativeDoublePosition { @Nonnull @@ -40,7 +40,7 @@ public class RelativeDoublePosition { if (relative && !context.isPlayer()) { throw new GeneralCommandException(MESSAGE_COMMANDS_ERRORS_RELATIVE_POSITION_ARG); } else { - Vector3d basePosition; + Vector3d basePosition = new Vector3d(); if (relative) { Ref senderPlayerRef = context.senderAsPlayerRef(); if (senderPlayerRef == null || !senderPlayerRef.isValid()) { @@ -51,9 +51,7 @@ public class RelativeDoublePosition { assert transformComponent != null; - basePosition = transformComponent.getPosition(); - } else { - basePosition = Vector3d.ZERO; + basePosition.set(transformComponent.getPosition()); } return this.getRelativePosition(basePosition, world); diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeIntPosition.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeIntPosition.java index efe6c0ed..e654d630 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeIntPosition.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeIntPosition.java @@ -3,8 +3,6 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.exceptions.GeneralCommandException; @@ -13,6 +11,8 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class RelativeIntPosition { @Nonnull @@ -56,7 +56,7 @@ public class RelativeIntPosition { } else if (relative) { throw new GeneralCommandException(MESSAGE_COMMANDS_ERRORS_RELATIVE_POSITION_ARG); } else { - Vector3d base = Vector3d.ZERO; + Vector3d base = new Vector3d(); World world = componentAccessor.getExternalData().getWorld(); return this.getBlockPosition(base, world.getChunkStore()); } diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeVector3i.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeVector3i.java index bddb5ee5..3e541177 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeVector3i.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/RelativeVector3i.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class RelativeVector3i { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/SingleArgumentType.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/SingleArgumentType.java index 1eb5ff31..b45cdcd2 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/SingleArgumentType.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/SingleArgumentType.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.ParseResult; +import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -30,6 +31,10 @@ public abstract class SingleArgumentType extends ArgumentType withOverriddenUsage(@Nonnull String usage, @Nullable String... examples) { return new WrappedArgumentType(this.getName(), this, usage, examples) { + { + Objects.requireNonNull(SingleArgumentType.this); + } + @Override public DataType parse(String input, ParseResult parseResult) { return this.wrappedArgumentType.parse(new String[]{input}, parseResult); @@ -40,6 +45,10 @@ public abstract class SingleArgumentType extends ArgumentType withOverriddenUsage(@Nonnull String usage) { return new WrappedArgumentType(this.getName(), this, usage, this.examples) { + { + Objects.requireNonNull(SingleArgumentType.this); + } + @Override public DataType parse(String input, ParseResult parseResult) { return this.wrappedArgumentType.parse(new String[]{input}, parseResult); diff --git a/src/com/hypixel/hytale/server/core/command/system/arguments/types/WrappedArgumentType.java b/src/com/hypixel/hytale/server/core/command/system/arguments/types/WrappedArgumentType.java index 9e217aa7..f80fe593 100644 --- a/src/com/hypixel/hytale/server/core/command/system/arguments/types/WrappedArgumentType.java +++ b/src/com/hypixel/hytale/server/core/command/system/arguments/types/WrappedArgumentType.java @@ -1,6 +1,8 @@ package com.hypixel.hytale.server.core.command.system.arguments.types; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandSender; +import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionResult; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -11,6 +13,7 @@ public abstract class WrappedArgumentType extends SingleArgumentType wrappedArgumentType, @Nonnull String argumentUsage, @Nullable String... examples) { super(name, argumentUsage, examples); this.wrappedArgumentType = wrappedArgumentType; + this.withSharedSuggestions(wrappedArgumentType); } @Nonnull @@ -23,4 +26,14 @@ public abstract class WrappedArgumentType extends SingleArgumentType store = world.getEntityStore().getStore(); return this.runAsync(context, () -> { - ObjectList> entitiesToOperateOn; + List> entitiesToOperateOn; if (this.radiusArg.provided(context)) { if (!context.isPlayer()) { context.sendMessage(Message.translation("server.commands.errors.playerOrArg").param("option", "radius")); @@ -91,8 +92,8 @@ public abstract class AbstractTargetEntityCommand extends AbstractAsyncCommand { double radius = this.radiusArg.get(context); Vector3d position = transformComponent.getPosition(); - entitiesToOperateOn = new ObjectArrayList<>(); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + entitiesToOperateOn = new ReferenceArrayList<>(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> entitySpatialResource = store.getResource(EntityModule.get().getEntitySpatialResourceType()); entitySpatialResource.getSpatialStructure().collect(position, radius, results); entitiesToOperateOn.addAll(results); @@ -107,7 +108,7 @@ public abstract class AbstractTargetEntityCommand extends AbstractAsyncCommand { return; } - entitiesToOperateOn = ObjectLists.singleton(targetRef); + entitiesToOperateOn = ReferenceLists.singleton(targetRef); } else if (this.entityArg.provided(context)) { Ref entityRef = this.entityArg.get(store, context); if (entityRef == null || !entityRef.isValid()) { @@ -141,7 +142,5 @@ public abstract class AbstractTargetEntityCommand extends AbstractAsyncCommand { }, world); } - protected abstract void execute( - @Nonnull CommandContext var1, @Nonnull ObjectList> var2, @Nonnull World var3, @Nonnull Store var4 - ); + protected abstract void execute(@Nonnull CommandContext var1, @Nonnull List> var2, @Nonnull World var3, @Nonnull Store var4); } diff --git a/src/com/hypixel/hytale/server/core/command/system/pages/CommandListPage.java b/src/com/hypixel/hytale/server/core/command/system/pages/CommandListPage.java index 46b9b5cb..4dbc419c 100644 --- a/src/com/hypixel/hytale/server/core/command/system/pages/CommandListPage.java +++ b/src/com/hypixel/hytale/server/core/command/system/pages/CommandListPage.java @@ -165,11 +165,11 @@ public class CommandListPage extends InteractiveCustomUIPage commands = new Object2ObjectOpenHashMap<>(CommandManager.get().getCommandRegistration()); - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); + PlayerRef playerRefComponent = componentAccessor.getComponent(ref, PlayerRef.getComponentType()); - assert playerComponent != null; + assert playerRefComponent != null; - commands.values().removeIf(commandx -> !commandx.hasPermission(playerComponent)); + commands.values().removeIf(commandx -> !commandx.hasPermission(playerRefComponent)); if (this.searchQuery.isEmpty()) { this.visibleCommands.clear(); this.visibleCommands.addAll(commands.keySet()); @@ -237,14 +237,12 @@ public class CommandListPage extends InteractiveCustomUIPage subcommands = currentContext.getSubCommands(); AbstractCommand subcommand = subcommands.get(subcommandName); if (subcommand != null) { @@ -280,11 +278,9 @@ public class CommandListPage extends InteractiveCustomUIPage variants = (Int2ObjectMap)variantsField.get(currentContext); AbstractCommand variant = variants.get(variantIndex); - if (variant == null || !variant.hasPermission(playerComponent)) { + if (variant == null || !variant.hasPermission(playerRefComponent)) { return; } @@ -322,10 +318,12 @@ public class CommandListPage extends InteractiveCustomUIPage subcommands = command.getSubCommands(); @@ -389,7 +387,7 @@ public class CommandListPage extends InteractiveCustomUIPage entry : subcommands.entrySet()) { AbstractCommand subcommand = entry.getValue(); - if (subcommand.hasPermission(playerComponent)) { + if (subcommand.hasPermission(playerRefComponent)) { if (cardsInCurrentRow == 0) { commandBuilder.appendInline("#SubcommandCards", "Group { LayoutMode: Left; Anchor: (Bottom: 0); }"); } @@ -399,7 +397,7 @@ public class CommandListPage extends InteractiveCustomUIPage ").append(part); } - titleText.append(" [Variant]"); - commandBuilder.set("#CommandName.TextSpans", Message.raw(titleText.toString())); + commandBuilder.set( + "#CommandName.TextSpans", Message.raw(titleText.toString()).insert(" ").insert(Message.translation("server.customUI.commandListPage.variantSuffix")) + ); } private void buildAliasesSection(@Nonnull AbstractCommand command, @Nonnull UICommandBuilder commandBuilder) { @@ -465,7 +464,7 @@ public class CommandListPage extends InteractiveCustomUIPage entry : variants.int2ObjectEntrySet()) { AbstractCommand variant = entry.getValue(); int variantIndex = entry.getIntKey(); - if (variant.hasPermission(playerComponent)) { + if (variant.hasPermission(playerRefComponent)) { commandBuilder.append("#VariantsList", "Pages/VariantCard.ui"); - commandBuilder.set("#VariantsList[" + displayIndex + "] #VariantUsage.TextSpans", this.getSimplifiedUsage(variant, playerComponent)); + commandBuilder.set("#VariantsList[" + displayIndex + "] #VariantUsage.TextSpans", this.getSimplifiedUsage(variant, playerRefComponent)); eventBuilder.addEventBinding( CustomUIEventBindingType.Activating, "#VariantsList[" + displayIndex + "]", EventData.of("Variant", String.valueOf(variantIndex)) ); @@ -503,17 +502,19 @@ public class CommandListPage extends InteractiveCustomUIPage entry : optionalArgs.entrySet()) { Object arg = entry.getValue(); if (arg instanceof OptionalArg optArg) { - if (optArg.getPermission() == null || playerComponent.hasPermission(optArg.getPermission())) { + if (optArg.getPermission() == null || playerRefComponent.hasPermission(optArg.getPermission())) { commandBuilder.append("#OptionalArgumentsList", "Pages/ParameterItem.ui"); commandBuilder.set( "#OptionalArgumentsList[" + optIndex + "] #ParamName.TextSpans", Message.raw("--" + optArg.getName() + " <" + optArg.getName() + ">") @@ -609,7 +610,7 @@ public class CommandListPage extends InteractiveCustomUIPage defArg) { - if (defArg.getPermission() == null || playerComponent.hasPermission(defArg.getPermission())) { + if (defArg.getPermission() == null || playerRefComponent.hasPermission(defArg.getPermission())) { commandBuilder.append("#DefaultArgumentsList", "Pages/ParameterItem.ui"); commandBuilder.set( "#DefaultArgumentsList[" + defIndex + "] #ParamName.TextSpans", Message.raw("--" + defArg.getName() + " <" + defArg.getName() + ">") @@ -631,7 +632,7 @@ public class CommandListPage extends InteractiveCustomUIPage> allArgumentTypes = new HashSet<>(); diff --git a/src/com/hypixel/hytale/server/core/command/system/suggestion/SuggestionResult.java b/src/com/hypixel/hytale/server/core/command/system/suggestion/SuggestionResult.java index a4282bc6..814c5b41 100644 --- a/src/com/hypixel/hytale/server/core/command/system/suggestion/SuggestionResult.java +++ b/src/com/hypixel/hytale/server/core/command/system/suggestion/SuggestionResult.java @@ -1,24 +1,25 @@ package com.hypixel.hytale.server.core.command.system.suggestion; -import com.hypixel.hytale.common.util.StringCompareUtil; -import it.unimi.dsi.fastutil.ints.IntObjectPair; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.Collection; -import java.util.Comparator; import java.util.List; -import java.util.Locale; import java.util.function.Function; import javax.annotation.Nonnull; public class SuggestionResult { - private static final int FUZZY_SUGGESTION_MAX_RESULTS = 5; - @Nonnull - private static final Comparator> INTEGER_STRING_PAIR_COMPARATOR = Comparator.comparingInt(IntObjectPair::leftInt); private final List suggestions = new ObjectArrayList<>(); + private final List continuations = new ObjectArrayList<>(); @Nonnull public SuggestionResult suggest(@Nonnull String suggestion) { this.suggestions.add(suggestion); + this.continuations.add(false); + return this; + } + + @Nonnull + public SuggestionResult suggestContinuation(@Nonnull String suggestion) { + this.suggestions.add(suggestion); + this.continuations.add(true); return this; } @@ -38,33 +39,7 @@ public class SuggestionResult { } @Nonnull - public SuggestionResult fuzzySuggest( - @Nonnull String input, @Nonnull Collection items, @Nonnull Function toStringFunction - ) { - List> sorting = new ObjectArrayList<>(5); - int lowestStoredFuzzyValue = Integer.MIN_VALUE; - - for (DataType item : items) { - String toString = toStringFunction.apply(item); - int fuzzyValue = StringCompareUtil.getFuzzyDistance(toString, input, Locale.ENGLISH); - if (sorting.size() == 5) { - if (fuzzyValue < lowestStoredFuzzyValue) { - continue; - } - - sorting.set(0, IntObjectPair.of(fuzzyValue, toString)); - } else { - sorting.add(IntObjectPair.of(fuzzyValue, toString)); - } - - sorting.sort(INTEGER_STRING_PAIR_COMPARATOR); - lowestStoredFuzzyValue = sorting.getFirst().leftInt(); - } - - for (IntObjectPair integerStringPair : sorting) { - this.suggest(integerStringPair.right()); - } - - return this; + public List getContinuations() { + return this.continuations; } } diff --git a/src/com/hypixel/hytale/server/core/config/ServerWorldMapConfig.java b/src/com/hypixel/hytale/server/core/config/ServerWorldMapConfig.java new file mode 100644 index 00000000..bc13aa6c --- /dev/null +++ b/src/com/hypixel/hytale/server/core/config/ServerWorldMapConfig.java @@ -0,0 +1,54 @@ +package com.hypixel.hytale.server.core.config; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.server.core.HytaleServerConfig; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public final class ServerWorldMapConfig extends WorldMapConfig { + @Nonnull + public static final Codec CODEC = BuilderCodec.builder( + ServerWorldMapConfig.class, ServerWorldMapConfig::new, WorldMapConfig.ABSTRACT_CODEC + ) + .build(); + @Nullable + private transient HytaleServerConfig hytaleServerConfig; + + public ServerWorldMapConfig() { + } + + public ServerWorldMapConfig(@Nonnull HytaleServerConfig hytaleServerConfig) { + this.hytaleServerConfig = hytaleServerConfig; + } + + public void setHytaleServerConfig(@Nonnull HytaleServerConfig hytaleServerConfig) { + this.hytaleServerConfig = hytaleServerConfig; + } + + @Override + public int getDefaultViewRadiusMin() { + return 1; + } + + @Override + public int getDefaultViewRadiusMax() { + return 512; + } + + @Override + public void setViewRadiusMin(int viewRadiusMin) { + super.setViewRadiusMin(viewRadiusMin); + if (this.hytaleServerConfig != null) { + this.hytaleServerConfig.markChanged(); + } + } + + @Override + public void setViewRadiusMax(int viewRadiusMax) { + super.setViewRadiusMax(viewRadiusMax); + if (this.hytaleServerConfig != null) { + this.hytaleServerConfig.markChanged(); + } + } +} diff --git a/src/com/hypixel/hytale/server/core/config/WorldMapConfig.java b/src/com/hypixel/hytale/server/core/config/WorldMapConfig.java new file mode 100644 index 00000000..d829906a --- /dev/null +++ b/src/com/hypixel/hytale/server/core/config/WorldMapConfig.java @@ -0,0 +1,56 @@ +package com.hypixel.hytale.server.core.config; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.validation.Validators; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class WorldMapConfig { + public static final int ABSOLUTE_MAX_VIEW_RADIUS = 512; + @Nonnull + public static final BuilderCodec ABSTRACT_CODEC = BuilderCodec.abstractBuilder(WorldMapConfig.class) + .append(new KeyedCodec<>("ViewRadiusMin", Codec.INTEGER), (o, i) -> o.viewRadiusMin = i, o -> o.viewRadiusMin) + .addValidator(Validators.greaterThanOrEqual(0)) + .add() + .append(new KeyedCodec<>("ViewRadiusMax", Codec.INTEGER), (o, i) -> o.viewRadiusMax = i, o -> o.viewRadiusMax) + .addValidator(Validators.range(0, 512)) + .add() + .afterDecode(config -> validate(config, 512)) + .build(); + @Nullable + protected Integer viewRadiusMin; + @Nullable + protected Integer viewRadiusMax; + + public abstract int getDefaultViewRadiusMin(); + + public abstract int getDefaultViewRadiusMax(); + + public int getViewRadiusMin() { + return this.viewRadiusMin != null ? this.viewRadiusMin : this.getDefaultViewRadiusMin(); + } + + public void setViewRadiusMin(int viewRadiusMin) { + this.viewRadiusMin = viewRadiusMin; + } + + public int getViewRadiusMax() { + return this.viewRadiusMax != null ? this.viewRadiusMax : this.getDefaultViewRadiusMax(); + } + + public void setViewRadiusMax(int viewRadiusMax) { + this.viewRadiusMax = viewRadiusMax; + } + + protected static void validate(WorldMapConfig config, int ceiling) { + int min = config.getViewRadiusMin(); + int max = config.getViewRadiusMax(); + if (min > max) { + throw new IllegalArgumentException("ViewRadiusMin (" + min + ") must be less than or equal to ViewRadiusMax (" + max + ")"); + } else if (max > ceiling) { + throw new IllegalArgumentException("ViewRadiusMax (" + max + ") must not exceed " + ceiling); + } + } +} diff --git a/src/com/hypixel/hytale/server/core/config/WorldWorldMapConfig.java b/src/com/hypixel/hytale/server/core/config/WorldWorldMapConfig.java new file mode 100644 index 00000000..139732e4 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/config/WorldWorldMapConfig.java @@ -0,0 +1,30 @@ +package com.hypixel.hytale.server.core.config; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nonnull; + +public final class WorldWorldMapConfig extends WorldMapConfig { + public static final int DEFAULT_VIEW_RADIUS_MIN = 3; + public static final int DEFAULT_VIEW_RADIUS_MAX = 32; + public static final float DEFAULT_IMAGE_SCALE = 3.0F; + public static final float DEFAULT_VIEW_RADIUS_MULTIPLIER = 2.0F; + public static final float DEFAULT_SCALE = 128.0F; + public static final float DEFAULT_MIN_SCALE = 32.0F; + public static final float DEFAULT_MAX_SCALE = 175.0F; + @Nonnull + public static final Codec CODEC = BuilderCodec.builder( + WorldWorldMapConfig.class, WorldWorldMapConfig::new, WorldMapConfig.ABSTRACT_CODEC + ) + .build(); + + @Override + public int getDefaultViewRadiusMin() { + return 3; + } + + @Override + public int getDefaultViewRadiusMax() { + return 32; + } +} diff --git a/src/com/hypixel/hytale/server/core/console/ConsoleSender.java b/src/com/hypixel/hytale/server/core/console/ConsoleSender.java index 1f960abb..cdb4e4fa 100644 --- a/src/com/hypixel/hytale/server/core/console/ConsoleSender.java +++ b/src/com/hypixel/hytale/server/core/console/ConsoleSender.java @@ -25,7 +25,7 @@ public class ConsoleSender implements CommandSender { @Nonnull @Override - public String getDisplayName() { + public String getUsername() { return "Console"; } diff --git a/src/com/hypixel/hytale/server/core/console/command/SayCommand.java b/src/com/hypixel/hytale/server/core/console/command/SayCommand.java index e8f57409..a4e2c1e6 100644 --- a/src/com/hypixel/hytale/server/core/console/command/SayCommand.java +++ b/src/com/hypixel/hytale/server/core/console/command/SayCommand.java @@ -2,7 +2,8 @@ package com.hypixel.hytale.server.core.console.command; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; -import com.hypixel.hytale.server.core.command.system.CommandUtil; +import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase; import com.hypixel.hytale.server.core.console.ConsoleSender; import com.hypixel.hytale.server.core.universe.Universe; @@ -12,36 +13,33 @@ import javax.annotation.Nonnull; public class SayCommand extends CommandBase { @Nonnull private static final Color SAY_COMMAND_COLOR = Color.CYAN; + @Nonnull + private final RequiredArg messageArg = this.withRequiredArg("message", "server.commands.say.message.desc", ArgTypes.GREEDY_STRING); public SayCommand() { super("say", "server.commands.say.desc"); this.addAliases("broadcast"); - this.setAllowsExtraArguments(true); } @Override protected void executeSync(@Nonnull CommandContext context) { - String rawArgs = CommandUtil.stripCommandName(context.getInputString()).trim(); - if (rawArgs.isEmpty()) { - context.sendMessage(Message.translation("server.commands.parsing.error.wrongNumberRequiredParameters").param("expected", 1).param("actual", 0)); - } else { - Message result; - if (rawArgs.charAt(0) == '{') { - try { - result = Message.parse(rawArgs).color(SAY_COMMAND_COLOR); - } catch (IllegalArgumentException var5) { - context.sendMessage(Message.raw("Failed to parse formatted message: " + var5.getMessage())); - return; - } - } else { - result = Message.translation("server.chat.broadcastMessage") - .param("username", context.sender().getDisplayName()) - .param("message", rawArgs) - .color(SAY_COMMAND_COLOR); + String rawMessage = this.messageArg.get(context); + Message result; + if (rawMessage.charAt(0) == '{') { + try { + result = Message.parse(rawMessage).color(SAY_COMMAND_COLOR); + } catch (IllegalArgumentException var5) { + context.sendMessage(Message.raw("Failed to parse formatted message: " + var5.getMessage())); + return; } - - Universe.get().getWorlds().values().forEach(world -> world.getPlayerRefs().forEach(playerRef -> playerRef.sendMessage(result))); - ConsoleSender.INSTANCE.sendMessage(result); + } else { + result = Message.translation("server.chat.broadcastMessage") + .param("username", context.sender().getUsername()) + .param("message", rawMessage) + .color(SAY_COMMAND_COLOR); } + + Universe.get().getWorlds().values().forEach(world -> world.getPlayerRefs().forEach(playerRef -> playerRef.sendMessage(result))); + ConsoleSender.INSTANCE.sendMessage(result); } } diff --git a/src/com/hypixel/hytale/server/core/cosmetics/CosmeticRegistry.java b/src/com/hypixel/hytale/server/core/cosmetics/CosmeticRegistry.java index f3d95b0c..ee82839b 100644 --- a/src/com/hypixel/hytale/server/core/cosmetics/CosmeticRegistry.java +++ b/src/com/hypixel/hytale/server/core/cosmetics/CosmeticRegistry.java @@ -19,6 +19,8 @@ public class CosmeticRegistry { @Nonnull private final Map emotes; @Nonnull + private final Map emotesInGame; + @Nonnull private final Map eyeColors; @Nonnull private final Map gradientSets; @@ -66,6 +68,7 @@ public class CosmeticRegistry { public CosmeticRegistry(@Nonnull AssetPack pack) { Path assetsDirectory = pack.getRoot(); this.emotes = this.load(assetsDirectory, "Emotes.json", Emote::new); + this.emotesInGame = this.load(assetsDirectory, "EmotesInGame.json", Emote::new); this.eyeColors = this.load(assetsDirectory, "EyeColors.json", PlayerSkinTintColor::new); this.gradientSets = this.load(assetsDirectory, "GradientSets.json", PlayerSkinGradientSet::new); this.bodyCharacteristics = this.load(assetsDirectory, "BodyCharacteristics.json", PlayerSkinPart::new); @@ -112,6 +115,11 @@ public class CosmeticRegistry { return this.emotes; } + @Nonnull + public Map getEmotesInGame() { + return this.emotesInGame; + } + @Nonnull public Map getEyeColors() { return this.eyeColors; @@ -225,6 +233,7 @@ public class CosmeticRegistry { public Map getByType(@Nonnull CosmeticType type) { return switch (type) { case EMOTES -> this.getEmotes(); + case EMOTES_INGAME -> this.getEmotesInGame(); case SKIN_TONES -> this.getGradientSets().get("Skin").getGradients(); case EYE_COLORS -> this.getEyeColors(); case GRADIENT_SETS -> this.getGradientSets(); diff --git a/src/com/hypixel/hytale/server/core/cosmetics/CosmeticType.java b/src/com/hypixel/hytale/server/core/cosmetics/CosmeticType.java index 6e992d89..b324a1de 100644 --- a/src/com/hypixel/hytale/server/core/cosmetics/CosmeticType.java +++ b/src/com/hypixel/hytale/server/core/cosmetics/CosmeticType.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.server.core.cosmetics; public enum CosmeticType { EMOTES, + EMOTES_INGAME, SKIN_TONES, EYE_COLORS, GRADIENT_SETS, diff --git a/src/com/hypixel/hytale/server/core/cosmetics/CosmeticsModule.java b/src/com/hypixel/hytale/server/core/cosmetics/CosmeticsModule.java index 269699f1..963f4a5d 100644 --- a/src/com/hypixel/hytale/server/core/cosmetics/CosmeticsModule.java +++ b/src/com/hypixel/hytale/server/core/cosmetics/CosmeticsModule.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.server.core.cosmetics; +import com.hypixel.hytale.assetstore.AssetRegistry; +import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; import com.hypixel.hytale.common.plugin.PluginManifest; import com.hypixel.hytale.common.util.ArrayUtil; import com.hypixel.hytale.common.util.RandomUtil; import com.hypixel.hytale.server.core.Options; import com.hypixel.hytale.server.core.asset.AssetModule; +import com.hypixel.hytale.server.core.asset.HytaleAssetStore; import com.hypixel.hytale.server.core.asset.LoadAssetEvent; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; @@ -34,6 +37,18 @@ public class CosmeticsModule extends JavaPlugin { if (Options.getOptionSet().has(Options.VALIDATE_ASSETS)) { this.getEventRegistry().register((short)64, LoadAssetEvent.class, this::validateGeneratedSkin); } + + AssetRegistry.register( + ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( + EmoteAsset.class, new IndexedLookupTableAssetMap<>(EmoteAsset[]::new) + ) + .setPath("Emote")) + .setCodec(EmoteAsset.CODEC)) + .setKeyFunction(EmoteAsset::getId)) + .setPacketGenerator(new EmoteAssetPacketGenerator()) + .setReplaceOnRemove(EmoteAsset::new)) + .build() + ); } public CosmeticRegistry getRegistry() { diff --git a/src/com/hypixel/hytale/server/core/cosmetics/EmoteAsset.java b/src/com/hypixel/hytale/server/core/cosmetics/EmoteAsset.java new file mode 100644 index 00000000..27d67e7a --- /dev/null +++ b/src/com/hypixel/hytale/server/core/cosmetics/EmoteAsset.java @@ -0,0 +1,87 @@ +package com.hypixel.hytale.server.core.cosmetics; + +import com.hypixel.hytale.assetstore.AssetExtraInfo; +import com.hypixel.hytale.assetstore.AssetKeyValidator; +import com.hypixel.hytale.assetstore.AssetRegistry; +import com.hypixel.hytale.assetstore.AssetStore; +import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec; +import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; +import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.validation.ValidatorCache; +import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.protocol.ProtocolEmote; +import com.hypixel.hytale.server.core.asset.common.CommonAssetValidator; +import com.hypixel.hytale.server.core.io.NetworkSerializable; + +public class EmoteAsset implements JsonAssetWithMap>, NetworkSerializable { + public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( + EmoteAsset.class, + EmoteAsset::new, + Codec.STRING, + (emoteAsset, s) -> emoteAsset.id = s, + emoteAsset -> emoteAsset.id, + (emoteAsset, data) -> emoteAsset.data = data, + emoteAsset -> emoteAsset.data + ) + .append(new KeyedCodec<>("Name", Codec.STRING), (asset, s) -> asset.name = s, asset -> asset.name) + .documentation("The localization key of the emote name.") + .addValidator(Validators.nonNull()) + .addValidator(Validators.nonEmptyString()) + .add() + .append(new KeyedCodec<>("Animation", Codec.STRING), (asset, s) -> asset.animationPath = s, asset -> asset.animationPath) + .documentation("The path to the animation file (must be located in HytaleAssets/Common/Characters).") + .addValidator(Validators.nonNull()) + .addValidator(CommonAssetValidator.ANIMATION_EMOTE) + .add() + .append(new KeyedCodec<>("Icon", Codec.STRING), (asset, s) -> asset.iconPath = s, asset -> asset.iconPath) + .documentation("The path to the icon file (must be located in HytaleAssets/Common/Icons/Emotes).") + .addValidator(Validators.nonNull()) + .addValidator(CommonAssetValidator.ICON_EMOTE) + .add() + .append(new KeyedCodec<>("IsLooping", Codec.BOOLEAN), (asset, b) -> asset.isLooping = b, asset -> asset.isLooping) + .add() + .build(); + public static final ValidatorCache VALIDATOR_CACHE = new ValidatorCache<>(new AssetKeyValidator<>(EmoteAsset::getAssetStore)); + private static AssetStore> ASSET_STORE; + protected AssetExtraInfo.Data data; + protected String id; + protected String name; + protected String animationPath; + protected String iconPath; + protected boolean isLooping; + + public static AssetStore> getAssetStore() { + if (ASSET_STORE == null) { + ASSET_STORE = AssetRegistry.getAssetStore(EmoteAsset.class); + } + + return ASSET_STORE; + } + + public static IndexedLookupTableAssetMap getAssetMap() { + return (IndexedLookupTableAssetMap)getAssetStore().getAssetMap(); + } + + public EmoteAsset() { + } + + public EmoteAsset(String id) { + this.id = id; + } + + public String getId() { + return this.id; + } + + public ProtocolEmote toPacket() { + ProtocolEmote packet = new ProtocolEmote(); + packet.id = this.id; + packet.name = this.name; + packet.animation = this.animationPath; + packet.icon = this.iconPath; + packet.isLooping = this.isLooping; + return packet; + } +} diff --git a/src/com/hypixel/hytale/server/core/cosmetics/EmoteAssetPacketGenerator.java b/src/com/hypixel/hytale/server/core/cosmetics/EmoteAssetPacketGenerator.java new file mode 100644 index 00000000..148c57e8 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/cosmetics/EmoteAssetPacketGenerator.java @@ -0,0 +1,53 @@ +package com.hypixel.hytale.server.core.cosmetics; + +import com.hypixel.hytale.assetstore.AssetUpdateQuery; +import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; +import com.hypixel.hytale.protocol.ProtocolEmote; +import com.hypixel.hytale.protocol.ToClientPacket; +import com.hypixel.hytale.protocol.UpdateType; +import com.hypixel.hytale.protocol.packets.assets.UpdateEmotes; +import com.hypixel.hytale.server.core.asset.packet.AssetPacketGenerator; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; + +public class EmoteAssetPacketGenerator extends AssetPacketGenerator> { + public ToClientPacket generateInitPacket(IndexedLookupTableAssetMap assetMap, Map assets) { + Int2ObjectMap emoteAssets = new Int2ObjectOpenHashMap<>(); + + for (Entry entry : assets.entrySet()) { + emoteAssets.put(assetMap.getIndex(entry.getKey()), entry.getValue().toPacket()); + } + + return new UpdateEmotes(UpdateType.Init, assetMap.getNextIndex(), emoteAssets); + } + + public ToClientPacket generateUpdatePacket( + IndexedLookupTableAssetMap assetMap, Map loadedAssets, @NonNullDecl AssetUpdateQuery query + ) { + Int2ObjectMap emoteAssets = new Int2ObjectOpenHashMap<>(); + + for (Entry entry : loadedAssets.entrySet()) { + emoteAssets.put(assetMap.getIndex(entry.getKey()), entry.getValue().toPacket()); + } + + return new UpdateEmotes(UpdateType.AddOrUpdate, assetMap.getNextIndex(), emoteAssets); + } + + @NullableDecl + public ToClientPacket generateRemovePacket( + IndexedLookupTableAssetMap assetMap, Set removedAssets, @NonNullDecl AssetUpdateQuery query + ) { + Int2ObjectMap emoteAssets = new Int2ObjectOpenHashMap<>(); + + for (String entry : removedAssets) { + emoteAssets.put(assetMap.getIndex(entry), null); + } + + return new UpdateEmotes(UpdateType.Remove, assetMap.getNextIndex(), emoteAssets); + } +} diff --git a/src/com/hypixel/hytale/server/core/cosmetics/commands/EmoteCommand.java b/src/com/hypixel/hytale/server/core/cosmetics/commands/EmoteCommand.java index 516b09ee..d08cff42 100644 --- a/src/com/hypixel/hytale/server/core/cosmetics/commands/EmoteCommand.java +++ b/src/com/hypixel/hytale/server/core/cosmetics/commands/EmoteCommand.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.server.core.cosmetics.commands; +import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; import com.hypixel.hytale.common.util.StringUtil; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; @@ -11,14 +12,15 @@ import com.hypixel.hytale.server.core.command.system.CommandUtil; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; -import com.hypixel.hytale.server.core.cosmetics.CosmeticRegistry; import com.hypixel.hytale.server.core.cosmetics.CosmeticsModule; import com.hypixel.hytale.server.core.cosmetics.Emote; +import com.hypixel.hytale.server.core.cosmetics.EmoteAsset; import com.hypixel.hytale.server.core.entity.AnimationUtils; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Map; +import java.util.Set; import javax.annotation.Nonnull; public class EmoteCommand extends AbstractPlayerCommand { @@ -34,18 +36,18 @@ public class EmoteCommand extends AbstractPlayerCommand { protected void execute( @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world ) { - CosmeticRegistry cosmeticsRegistry = CosmeticsModule.get().getRegistry(); - Map emotes = cosmeticsRegistry.getEmotes(); String emoteId = this.emoteArg.get(context); - Emote emote = emotes.get(emoteId); - if (emote == null) { + Map builtinEmotes = CosmeticsModule.get().getRegistry().getEmotesInGame(); + IndexedLookupTableAssetMap serverEmotes = EmoteAsset.getAssetMap(); + if (builtinEmotes.get(emoteId) == null && serverEmotes.getAsset(emoteId) == null) { context.sendMessage(Message.translation("server.commands.emote.emoteNotFound").param("id", emoteId)); + Set> allEmoteIdsSet = Set.of(builtinEmotes.keySet(), serverEmotes.getAssetMap().keySet()); context.sendMessage( Message.translation("server.general.failed.didYouMean") - .param("choices", StringUtil.sortByFuzzyDistance(emoteId, emotes.keySet(), CommandUtil.RECOMMEND_COUNT).toString()) + .param("choices", StringUtil.sortByFuzzyDistance(emoteId, allEmoteIdsSet, CommandUtil.RECOMMEND_COUNT).toString()) ); } else { - AnimationUtils.playAnimation(ref, AnimationSlot.Emote, null, emote.getId(), true, store); + AnimationUtils.playAnimation(ref, AnimationSlot.Emote, null, emoteId, true, store); } } } diff --git a/src/com/hypixel/hytale/server/core/entity/AnimationUtils.java b/src/com/hypixel/hytale/server/core/entity/AnimationUtils.java index d7b74221..34884fc0 100644 --- a/src/com/hypixel/hytale/server/core/entity/AnimationUtils.java +++ b/src/com/hypixel/hytale/server/core/entity/AnimationUtils.java @@ -40,7 +40,11 @@ public class AnimationUtils { model = modelComponent.getModel(); } - if (animationSlot != AnimationSlot.Action && animationId != null && model != null && !model.getAnimationSetMap().containsKey(animationId)) { + if (animationSlot != AnimationSlot.Action + && animationSlot != AnimationSlot.Emote + && animationId != null + && model != null + && !model.getAnimationSetMap().containsKey(animationId)) { Entity.LOGGER.at(Level.WARNING).atMostEvery(1, TimeUnit.MINUTES).log("Missing animation '%s' for Model '%s'", animationId, model.getModelAssetId()); } else { NetworkId networkIdComponent = componentAccessor.getComponent(ref, NetworkId.getComponentType()); diff --git a/src/com/hypixel/hytale/server/core/entity/Entity.java b/src/com/hypixel/hytale/server/core/entity/Entity.java index 0f50a61f..bd60b43e 100644 --- a/src/com/hypixel/hytale/server/core/entity/Entity.java +++ b/src/com/hypixel/hytale/server/core/entity/Entity.java @@ -18,7 +18,6 @@ import com.hypixel.hytale.event.IEventDispatcher; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.protocol.MovementStates; import com.hypixel.hytale.server.core.HytaleServer; -import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.entity.EntityRemoveEvent; import com.hypixel.hytale.server.core.modules.entity.EntityModule; @@ -30,10 +29,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Arrays; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; -import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bson.BsonDocument; @@ -44,8 +41,6 @@ public abstract class Entity implements Component { public static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); public static final int VERSION = 5; @Nonnull - public static final KeyedCodec MODEL = new KeyedCodec<>("Model", Model.ModelReference.CODEC); - @Nonnull public static final KeyedCodec DISPLAY_NAME = new KeyedCodec<>("DisplayName", Codec.STRING); @Nonnull public static final KeyedCodec UUID = new KeyedCodec<>("UUID", Codec.UUID_BINARY); @@ -66,8 +61,6 @@ public abstract class Entity implements Component { protected World world; @Nullable protected Ref reference; - @Deprecated - private TransformComponent transformComponent; @Deprecated(forRemoval = true) protected String legacyDisplayName; @Nonnull @@ -87,10 +80,13 @@ public abstract class Entity implements Component { @Deprecated(forRemoval = true) public void markNeedsSave() { - if (this.transformComponent != null) { - WorldChunk chunk = this.transformComponent.getChunk(); - if (chunk != null) { - chunk.getEntityChunk().markNeedsSaving(); + if (this.reference != null && this.reference.isValid() && this.world != null && this.world.isInThread()) { + TransformComponent transformComponent = this.reference.getStore().getComponent(this.reference, TransformComponent.getComponentType()); + if (transformComponent != null) { + WorldChunk chunk = transformComponent.getChunk(); + if (chunk != null) { + chunk.getEntityChunk().markNeedsSaving(); + } } } } @@ -160,35 +156,13 @@ public abstract class Entity implements Component { return this.legacyUuid; } - @Deprecated(forRemoval = true) - public void setTransformComponent(TransformComponent transform) { - this.transformComponent = transform; - } - - @Deprecated(forRemoval = true) - public TransformComponent getTransformComponent() { - if (this.world == null || this.reference == null) { - throw new IllegalStateException("Called before entity was init"); - } else if (!this.world.isInThread()) { - LOGGER.at(Level.WARNING).atMostEvery(5, TimeUnit.MINUTES).withCause(new Throwable()).log("getPositionComponent called async"); - return this.transformComponent; - } else { - Store store = this.world.getEntityStore().getStore(); - TransformComponent transformComponent = store.getComponent(this.reference, TransformComponent.getComponentType()); - - assert transformComponent != null; - - return transformComponent; - } - } - @Deprecated public void moveTo(@Nonnull Ref ref, double locX, double locY, double locZ, @Nonnull ComponentAccessor componentAccessor) { TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; - transformComponent.getPosition().assign(locX, locY, locZ); + transformComponent.getPosition().set(locX, locY, locZ); } @Nullable diff --git a/src/com/hypixel/hytale/server/core/entity/EntitySnapshot.java b/src/com/hypixel/hytale/server/core/entity/EntitySnapshot.java index b07bd513..72ac8d38 100644 --- a/src/com/hypixel/hytale/server/core/entity/EntitySnapshot.java +++ b/src/com/hypixel/hytale/server/core/entity/EntitySnapshot.java @@ -1,26 +1,26 @@ package com.hypixel.hytale.server.core.entity; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EntitySnapshot { @Nonnull private final Vector3d position = new Vector3d(); @Nonnull - private final Vector3f bodyRotation = new Vector3f(); + private final Rotation3f bodyRotation = new Rotation3f(); public EntitySnapshot() { } - public EntitySnapshot(@Nonnull Vector3d position, @Nonnull Vector3f bodyRotation) { - this.position.assign(position); - this.bodyRotation.assign(bodyRotation); + public EntitySnapshot(@Nonnull Vector3d position, @Nonnull Rotation3f bodyRotation) { + this.position.set(position); + this.bodyRotation.set(bodyRotation); } - public void init(@Nonnull Vector3d position, @Nonnull Vector3f bodyRotation) { - this.position.assign(position); - this.bodyRotation.assign(bodyRotation); + public void init(@Nonnull Vector3d position, @Nonnull Rotation3f bodyRotation) { + this.position.set(position); + this.bodyRotation.set(bodyRotation); } @Nonnull @@ -29,7 +29,7 @@ public class EntitySnapshot { } @Nonnull - public Vector3f getBodyRotation() { + public Rotation3f getBodyRotation() { return this.bodyRotation; } diff --git a/src/com/hypixel/hytale/server/core/entity/EntityUtils.java b/src/com/hypixel/hytale/server/core/entity/EntityUtils.java index c4bf32f0..c1225f07 100644 --- a/src/com/hypixel/hytale/server/core/entity/EntityUtils.java +++ b/src/com/hypixel/hytale/server/core/entity/EntityUtils.java @@ -15,24 +15,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class EntityUtils { - @Nonnull - public static Holder toHolder(int index, @Nonnull ArchetypeChunk archetypeChunk) { - Holder holder = EntityStore.REGISTRY.newHolder(); - Archetype archetype = archetypeChunk.getArchetype(); - - for (int i = archetype.getMinIndex(); i < archetype.length(); i++) { - ComponentType componentType = archetype.get(i); - if (componentType != null) { - Component component = archetypeChunk.getComponent(index, componentType); - if (component != null) { - holder.addComponent(componentType, component); - } - } - } - - return holder; - } - @Nullable private static ComponentType findComponentType(@Nonnull Archetype archetype) { return findComponentType(archetype, Entity.class); diff --git a/src/com/hypixel/hytale/server/core/entity/ExplosionUtils.java b/src/com/hypixel/hytale/server/core/entity/ExplosionUtils.java index 4e17f566..2e0cce5a 100644 --- a/src/com/hypixel/hytale/server/core/entity/ExplosionUtils.java +++ b/src/com/hypixel/hytale/server/core/entity/ExplosionUtils.java @@ -1,6 +1,5 @@ package com.hypixel.hytale.server.core.entity; -import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; @@ -8,9 +7,7 @@ import com.hypixel.hytale.math.block.BlockSphereUtil; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockGathering; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.item.config.ItemTool; @@ -28,13 +25,17 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector3i; public class ExplosionUtils { private static final boolean DEBUG_SHAPES = false; @@ -53,15 +54,15 @@ public class ExplosionUtils { @Nonnull Vector3d position, @Nonnull ExplosionConfig config, @Nullable Ref ignoreRef, - @Nonnull CommandBuffer commandBuffer, + @Nonnull ComponentAccessor componentAccessor, @Nonnull ComponentAccessor chunkStore ) { if (config.damageBlocks || config.damageEntities) { - Set> targetRefs = new ObjectOpenHashSet<>(); + Set> targetRefs = new ReferenceOpenHashSet<>(); Vector3d blockPosition = new Vector3d(Math.floor(position.x) + 0.5, Math.floor(position.y) + 0.5, Math.floor(position.z) + 0.5); - processTargetBlocks(blockPosition, config, ignoreRef, targetRefs, commandBuffer, chunkStore); + processTargetBlocks(blockPosition, config, ignoreRef, targetRefs, componentAccessor, chunkStore); if (config.damageEntities) { - processTargetEntities(config, position, damageSource, ignoreRef, targetRefs, commandBuffer); + processTargetEntities(config, position, damageSource, ignoreRef, targetRefs, componentAccessor); } } } @@ -71,20 +72,20 @@ public class ExplosionUtils { @Nonnull ExplosionConfig config, @Nullable Ref ignoreRef, @Nonnull Set> targetRefs, - @Nonnull CommandBuffer commandBuffer, + @Nonnull ComponentAccessor componentAccessor, @Nonnull ComponentAccessor chunkStore ) { ThreadLocalRandom random = ThreadLocalRandom.current(); - World world = commandBuffer.getExternalData().getWorld(); + World world = componentAccessor.getExternalData().getWorld(); int explosionBlockRadius = config.blockDamageRadius; if (config.damageEntities && config.entityDamageRadius > config.blockDamageRadius) { explosionBlockRadius = (int)config.entityDamageRadius; } - List> potentialTargets = new ObjectArrayList<>(); + List> potentialTargets = new ReferenceArrayList<>(); if (config.damageEntities) { Selector.selectNearbyEntities( - commandBuffer, position, (double)config.entityDamageRadius, potentialTargets::add, e -> ignoreRef == null || !e.equals(ignoreRef) + componentAccessor, position, (double)config.entityDamageRadius, potentialTargets::add, e -> ignoreRef == null || !e.equals(ignoreRef) ); } @@ -101,15 +102,15 @@ public class ExplosionUtils { Set avoidBlocks = new ObjectOpenHashSet<>(); for (Vector3i targetBlock : targetBlocks) { - Vector3d targetBlockPosition = targetBlock.toVector3d().add(0.5, 0.5, 0.5); + Vector3d targetBlockPosition = Vector3iUtil.toVector3d(targetBlock).add(0.5, 0.5, 0.5); int setBlockSettings = 1028; if (random.nextFloat() > config.blockDropChance) { setBlockSettings |= 2048; } - double distance = position.distanceTo(targetBlockPosition); + double distance = position.distance(targetBlockPosition); if (!(distance <= 0.0) && !Double.isNaN(distance)) { - Vector3d direction = targetBlockPosition.clone().subtract(position); + Vector3d direction = new Vector3d(targetBlockPosition).sub(position); Vector3i targetBlockPos = TargetUtil.getTargetBlock( world, (id, fluidId) -> isValidTargetBlock(id, config.damageBlocks), @@ -123,16 +124,16 @@ public class ExplosionUtils { ); if (targetBlockPos == null) { if (config.damageEntities) { - Vector3d entityHitPos = position.clone().add(direction); - collectPotentialTargets(targetRefs, potentialTargets, entityHitPos, position, commandBuffer); + Vector3d entityHitPos = new Vector3d(position).add(direction); + collectPotentialTargets(targetRefs, potentialTargets, entityHitPos, position, componentAccessor); } } else if (!avoidBlocks.contains(targetBlockPos)) { - Vector3d targetBlockPosD = targetBlockPos.toVector3d().add(0.5, 0.5, 0.5); + Vector3d targetBlockPosD = Vector3iUtil.toVector3d(targetBlockPos).add(0.5, 0.5, 0.5); if (config.damageEntities) { - collectPotentialTargets(targetRefs, potentialTargets, targetBlockPosD, position, commandBuffer); + collectPotentialTargets(targetRefs, potentialTargets, targetBlockPosD, position, componentAccessor); } - float damageDistance = (float)position.distanceTo(targetBlockPosD); + float damageDistance = (float)position.distance(targetBlockPosD); float damageScale = calculateBlockDamageScale(damageDistance, explosionBlockRadius, config.blockDamageFalloff); long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlockPos.x, targetBlockPos.z); Ref chunkReference = chunkStore.getExternalData().getChunkReference(chunkIndex); @@ -141,7 +142,7 @@ public class ExplosionUtils { if (!config.damageBlocks || canDamageBlock && !BlockHarvestUtils.performBlockDamage( - targetBlockPos, null, itemTool, damageScale, setBlockSettings, chunkReference, commandBuffer, chunkStore + targetBlockPos, null, itemTool, damageScale, setBlockSettings, chunkReference, componentAccessor, chunkStore )) { avoidBlocks.add(targetBlockPos); } @@ -173,24 +174,24 @@ public class ExplosionUtils { @Nonnull List> potentialTargetRefs, @Nonnull Vector3d startPosition, @Nonnull Vector3d endPosition, - @Nonnull CommandBuffer commandBuffer + @Nonnull ComponentAccessor componentAccessor ) { - World world = commandBuffer.getExternalData().getWorld(); + World world = componentAccessor.getExternalData().getWorld(); for (Ref potentialTarget : potentialTargetRefs) { - if (processPotentialEntity(potentialTarget, startPosition, endPosition, commandBuffer) && targetRefs.add(potentialTarget)) { + if (processPotentialEntity(potentialTarget, startPosition, endPosition, componentAccessor) && targetRefs.add(potentialTarget)) { } } } private static boolean processPotentialEntity( - @Nonnull Ref ref, @Nonnull Vector3d startPosition, @Nonnull Vector3d endPosition, @Nonnull CommandBuffer commandBuffer + @Nonnull Ref ref, @Nonnull Vector3d startPosition, @Nonnull Vector3d endPosition, @Nonnull ComponentAccessor componentAccessor ) { - BoundingBox boundingBoxComponent = commandBuffer.getComponent(ref, BoundingBox.getComponentType()); + BoundingBox boundingBoxComponent = componentAccessor.getComponent(ref, BoundingBox.getComponentType()); if (boundingBoxComponent == null) { return false; } else { - TransformComponent transformComponent = commandBuffer.getComponent(ref, TransformComponent.getComponentType()); + TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); if (transformComponent == null) { return false; } else { @@ -216,10 +217,10 @@ public class ExplosionUtils { @Nonnull Damage.Source damageSource, @Nullable Ref ignoreRef, @Nonnull Set> targetRefs, - @Nonnull CommandBuffer commandBuffer + @Nonnull ComponentAccessor componentAccessor ) { for (Ref targetRef : targetRefs) { - processTargetEntity(config, targetRef, position, damageSource, commandBuffer); + processTargetEntity(config, targetRef, position, damageSource, componentAccessor); } } @@ -228,37 +229,37 @@ public class ExplosionUtils { @Nonnull Ref targetRef, @Nonnull Vector3d position, @Nonnull Damage.Source damageSource, - @Nonnull CommandBuffer commandBuffer + @Nonnull ComponentAccessor componentAccessor ) { float entityDamageRadius = config.entityDamageRadius; float explosionDamage = config.entityDamage; float explosionFalloff = config.entityDamageFalloff; - TransformComponent targetTransformComponent = commandBuffer.getComponent(targetRef, TransformComponent.getComponentType()); + TransformComponent targetTransformComponent = componentAccessor.getComponent(targetRef, TransformComponent.getComponentType()); assert targetTransformComponent != null; - Velocity targetVelocityComponent = commandBuffer.getComponent(targetRef, Velocity.getComponentType()); + Velocity targetVelocityComponent = componentAccessor.getComponent(targetRef, Velocity.getComponentType()); assert targetVelocityComponent != null; Vector3d targetPosition = targetTransformComponent.getPosition(); - Vector3d diff = targetPosition.clone().subtract(position); + Vector3d diff = new Vector3d(targetPosition).sub(position); double distance = diff.length(); float damage = (float)(explosionDamage * Math.pow(1.0 - distance / entityDamageRadius, explosionFalloff)); if (damage > 0.0F) { - DamageSystems.executeDamage(targetRef, commandBuffer, new Damage(damageSource, DamageCause.ENVIRONMENT, damage)); + DamageSystems.executeDamage(targetRef, componentAccessor, new Damage(damageSource, DamageCause.ENVIRONMENT, damage)); } Knockback knockbackConfig = config.knockback; if (knockbackConfig != null) { ComponentType knockbackComponentType = KnockbackComponent.getComponentType(); - KnockbackComponent knockbackComponent = commandBuffer.getComponent(targetRef, knockbackComponentType); + KnockbackComponent knockbackComponent = componentAccessor.getComponent(targetRef, knockbackComponentType); if (knockbackComponent == null) { knockbackComponent = new KnockbackComponent(); - commandBuffer.putComponent(targetRef, knockbackComponentType, knockbackComponent); + componentAccessor.putComponent(targetRef, knockbackComponentType, knockbackComponent); } - Vector3d direction = diff.clone().normalize(); + Vector3d direction = new Vector3d(diff).normalize(); knockbackComponent.setVelocity(knockbackConfig.calculateVector(position, (float)direction.y, targetPosition)); knockbackComponent.setVelocityType(knockbackConfig.getVelocityType()); knockbackComponent.setVelocityConfig(knockbackConfig.getVelocityConfig()); diff --git a/src/com/hypixel/hytale/server/core/entity/InteractionContext.java b/src/com/hypixel/hytale/server/core/entity/InteractionContext.java index abd826e8..53539bcc 100644 --- a/src/com/hypixel/hytale/server/core/entity/InteractionContext.java +++ b/src/com/hypixel/hytale/server/core/entity/InteractionContext.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.ForkedChainId; import com.hypixel.hytale.protocol.GameMode; @@ -13,10 +12,9 @@ import com.hypixel.hytale.protocol.InteractionSyncData; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.PrioritySlot; import com.hypixel.hytale.protocol.RootInteractionSettings; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemContext; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; @@ -36,6 +34,8 @@ import java.util.function.Function; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3f; +import org.joml.Vector4d; public class InteractionContext { @Nonnull @@ -464,16 +464,21 @@ public class InteractionContext { } @Nonnull - public static InteractionContext forProxyEntity(InteractionManager manager, @Nonnull LivingEntity entity, Ref runningForEntity) { - Inventory entityInventory = entity.getInventory(); + public static InteractionContext forProxyEntity( + @Nonnull InteractionManager manager, + @Nonnull Ref entityRef, + @Nonnull Ref runningForEntity, + @Nonnull ComponentAccessor componentAccessor + ) { + InventoryComponent.Hotbar hotbarComp = componentAccessor.getComponent(entityRef, InventoryComponent.Hotbar.getComponentType()); return new InteractionContext( manager, - entity.getReference(), + entityRef, runningForEntity, -1, - entityInventory.getHotbar(), - entityInventory.getActiveHotbarSlot(), - entityInventory.getItemInHand() + hotbarComp != null ? hotbarComp.getInventory() : null, + hotbarComp != null ? hotbarComp.getActiveSlot() : -1, + hotbarComp != null ? hotbarComp.getActiveItem() : null ); } @@ -499,16 +504,28 @@ public class InteractionContext { int equipSlot, @Nonnull ComponentAccessor componentAccessor ) { - LivingEntity entity = (LivingEntity)EntityUtils.getEntity(ref, componentAccessor); - Inventory entityInventory = entity.getInventory(); + InventoryComponent.Hotbar hotbarComp = componentAccessor.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + InventoryComponent.Utility utilityComp = componentAccessor.getComponent(ref, InventoryComponent.Utility.getComponentType()); + InventoryComponent.Armor armorComp = componentAccessor.getComponent(ref, InventoryComponent.Armor.getComponentType()); + InventoryComponent.Tool toolComp = componentAccessor.getComponent(ref, InventoryComponent.Tool.getComponentType()); switch (type) { case Equipped: return new InteractionContext( - manager, ref, -3, entityInventory.getArmor(), (byte)equipSlot, entityInventory.getArmor().getItemStack((short)equipSlot) + manager, + ref, + -3, + armorComp != null ? armorComp.getInventory() : null, + (byte)equipSlot, + armorComp != null ? armorComp.getInventory().getItemStack((short)equipSlot) : null ); case HeldOffhand: return new InteractionContext( - manager, ref, -5, entityInventory.getUtility(), entityInventory.getActiveUtilitySlot(), entityInventory.getUtilityItem() + manager, + ref, + -5, + utilityComp != null ? utilityComp.getInventory() : null, + utilityComp != null ? utilityComp.getActiveSlot() : -1, + utilityComp != null ? utilityComp.getActiveItem() : null ); case Ability1: case Ability2: @@ -516,11 +533,11 @@ public class InteractionContext { case Pick: case Primary: case Secondary: - if (entityInventory.usingToolsItem()) { - return new InteractionContext(manager, ref, -8, entityInventory.getTools(), entityInventory.getActiveToolsSlot(), entityInventory.getToolsItem()); + if (toolComp != null && toolComp.isUsingToolsItem()) { + return new InteractionContext(manager, ref, -8, toolComp.getInventory(), toolComp.getActiveSlot(), toolComp.getActiveItem()); } else { - ItemStack primary = entityInventory.getItemInHand(); - ItemStack secondary = entityInventory.getUtilityItem(); + ItemStack primary = hotbarComp != null ? hotbarComp.getActiveItem() : null; + ItemStack secondary = utilityComp != null ? utilityComp.getActiveItem() : null; int selectedInventory = -1; if (primary == null && secondary != null) { selectedInventory = -5; @@ -544,19 +561,34 @@ public class InteractionContext { } } - if (selectedInventory == -5) { - return new InteractionContext( - manager, ref, -5, entityInventory.getUtility(), entityInventory.getActiveUtilitySlot(), entityInventory.getUtilityItem() + return selectedInventory == -5 + ? new InteractionContext( + manager, + ref, + -5, + utilityComp != null ? utilityComp.getInventory() : null, + utilityComp != null ? utilityComp.getActiveSlot() : -1, + utilityComp != null ? utilityComp.getActiveItem() : null + ) + : new InteractionContext( + manager, + ref, + -1, + hotbarComp != null ? hotbarComp.getInventory() : null, + hotbarComp != null ? hotbarComp.getActiveSlot() : -1, + hotbarComp != null ? hotbarComp.getActiveItem() : null ); - } - - return new InteractionContext( - manager, ref, -1, entityInventory.getHotbar(), entityInventory.getActiveHotbarSlot(), entityInventory.getItemInHand() - ); } case Held: default: - return new InteractionContext(manager, ref, -1, entityInventory.getHotbar(), entityInventory.getActiveHotbarSlot(), entityInventory.getItemInHand()); + return new InteractionContext( + manager, + ref, + -1, + hotbarComp != null ? hotbarComp.getInventory() : null, + hotbarComp != null ? hotbarComp.getActiveSlot() : -1, + hotbarComp != null ? hotbarComp.getActiveItem() : null + ); } } diff --git a/src/com/hypixel/hytale/server/core/entity/InteractionManager.java b/src/com/hypixel/hytale/server/core/entity/InteractionManager.java index 30f764fc..46ab0eff 100644 --- a/src/com/hypixel/hytale/server/core/entity/InteractionManager.java +++ b/src/com/hypixel/hytale/server/core/entity/InteractionManager.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.function.function.TriFunction; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.ForkedChainId; import com.hypixel.hytale.protocol.GameMode; @@ -19,7 +18,6 @@ import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionSyncData; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.RootInteractionSettings; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.protocol.WaitForDataFrom; import com.hypixel.hytale.protocol.packets.interaction.CancelInteractionChain; import com.hypixel.hytale.protocol.packets.interaction.SyncInteractionChain; @@ -27,6 +25,7 @@ import com.hypixel.hytale.protocol.packets.inventory.SetActiveSlot; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.io.handlers.game.GamePacketHandler; import com.hypixel.hytale.server.core.modules.interaction.IInteractionSimulationHandler; @@ -60,6 +59,8 @@ import java.util.function.Predicate; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3fc; +import org.joml.Vector4d; public class InteractionManager implements Component { public static final double MAX_REACH_DISTANCE = 8.0; @@ -786,7 +787,7 @@ public class InteractionManager implements Component { return false; } - context = InteractionContext.forProxyEntity(this, this.entity, proxyTarget); + context = InteractionContext.forProxyEntity(this, ref, proxyTarget, this.commandBuffer); } else { context = InteractionContext.forInteraction(this, ref, type, packet.equipSlot, this.commandBuffer); } @@ -795,7 +796,7 @@ public class InteractionManager implements Component { if (rootInteractionId == null) { HytaleLogger.Api ctx = LOGGER.at(Level.FINE); if (ctx.isEnabled()) { - ctx.log("Missing root interaction: %d, %s, %s", index, this.entity.getInventory().getItemInHand(), type); + ctx.log("Missing root interaction: %d, %s, %s", index, InventoryComponent.getItemInHand(this.commandBuffer, ref), type); } this.sendCancelPacket(index, packet.forkedId); @@ -916,8 +917,8 @@ public class InteractionManager implements Component { } if (packet.data.hitLocation != null) { - Vector3f hit = packet.data.hitLocation; - context.getMetaStore().putMetaObject(Interaction.HIT_LOCATION, new Vector4d(hit.x, hit.y, hit.z, 1.0)); + Vector3fc hit = packet.data.hitLocation; + context.getMetaStore().putMetaObject(Interaction.HIT_LOCATION, new Vector4d(hit.x(), hit.y(), hit.z(), 1.0)); } if (packet.data.hitDetail != null) { diff --git a/src/com/hypixel/hytale/server/core/entity/ItemUtils.java b/src/com/hypixel/hytale/server/core/entity/ItemUtils.java index 8966ab2f..7ca27ff5 100644 --- a/src/com/hypixel/hytale/server/core/entity/ItemUtils.java +++ b/src/com/hypixel/hytale/server/core/entity/ItemUtils.java @@ -5,13 +5,15 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.ecs.DropItemEvent; import com.hypixel.hytale.server.core.event.events.ecs.InteractivelyPickupItemEvent; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; @@ -22,6 +24,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ItemUtils { @Nonnull @@ -30,12 +33,10 @@ public class ItemUtils { public static void interactivelyPickupItem( @Nonnull Ref ref, @Nonnull ItemStack itemStack, @Nullable Vector3d origin, @Nonnull ComponentAccessor componentAccessor ) { - LivingEntity entity = (LivingEntity)EntityUtils.getEntity(ref, componentAccessor); InteractivelyPickupItemEvent event = new InteractivelyPickupItemEvent(itemStack); componentAccessor.invoke(ref, event); - if (event.isCancelled()) { - dropItem(ref, itemStack, componentAccessor); - } else { + if (!event.isCancelled()) { + itemStack = event.getItemStack(); Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); if (playerComponent != null) { Holder pickupItemHolder = null; @@ -63,7 +64,8 @@ public class ItemUtils { componentAccessor.addEntity(pickupItemHolder, AddReason.SPAWN); } } else { - SimpleItemContainer.addOrDropItemStack(componentAccessor, ref, entity.getInventory().getCombinedHotbarFirst(), itemStack); + CombinedItemContainer hotbarFirstCombinedItemContainer = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); + SimpleItemContainer.addOrDropItemStack(componentAccessor, ref, hotbarFirstCombinedItemContainer, itemStack); } } } @@ -81,8 +83,8 @@ public class ItemUtils { itemStack = event.getItemStack(); if (!itemStack.isEmpty() && itemStack.isValid()) { HeadRotation headRotationComponent = componentAccessor.getComponent(ref, HeadRotation.getComponentType()); - Vector3f rotation = headRotationComponent != null ? headRotationComponent.getRotation() : new Vector3f(0.0F, 0.0F, 0.0F); - Vector3d direction = Transform.getDirection(rotation.getPitch(), rotation.getYaw()); + Rotation3f rotation = headRotationComponent != null ? headRotationComponent.getRotation() : new Rotation3f(0.0F, 0.0F, 0.0F); + Vector3d direction = Transform.getDirection(rotation.pitch(), rotation.yaw()); return throwItem(ref, componentAccessor, itemStack, direction, throwSpeed); } else { LOGGER.at(Level.WARNING).log("Attempted to throw invalid item %s at %s by %s", itemStack, throwSpeed, ref.getIndex()); @@ -114,13 +116,13 @@ public class ItemUtils { eyeHeight = modelComponent.getModel().getEyeHeight(ref, store); } - Vector3d throwPosition = transformComponent.getPosition().clone(); + Vector3d throwPosition = new Vector3d(transformComponent.getPosition()); throwPosition.add(0.0, eyeHeight, 0.0).add(throwDirection); Holder itemEntityHolder = ItemComponent.generateItemDrop( store, itemStack, throwPosition, - Vector3f.ZERO, + Rotation3f.IDENTITY, (float)throwDirection.x * throwSpeed, (float)throwDirection.y * throwSpeed, (float)throwDirection.z * throwSpeed @@ -145,4 +147,14 @@ public class ItemUtils { ) { return throwItem(ref, itemStack, 1.0F, componentAccessor); } + + public static boolean canDecreaseItemStackDurability(@Nonnull Ref ref, @Nonnull ComponentAccessor accessor) { + Player playerComponent = accessor.getComponent(ref, Player.getComponentType()); + return playerComponent != null ? playerComponent.getGameMode() != GameMode.Creative : false; + } + + public static boolean canApplyItemStackPenalties(@Nonnull Ref ref, @Nonnull ComponentAccessor accessor) { + Player playerComponent = accessor.getComponent(ref, Player.getComponentType()); + return playerComponent != null ? playerComponent.getGameMode() != GameMode.Creative : true; + } } diff --git a/src/com/hypixel/hytale/server/core/entity/LivingEntity.java b/src/com/hypixel/hytale/server/core/entity/LivingEntity.java index d8a88421..02ee7035 100644 --- a/src/com/hypixel/hytale/server/core/entity/LivingEntity.java +++ b/src/com/hypixel/hytale/server/core/entity/LivingEntity.java @@ -4,11 +4,9 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.event.EventRegistration; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.MovementStates; import com.hypixel.hytale.server.core.asset.type.item.config.Item; @@ -17,56 +15,47 @@ import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.inventory.transaction.ItemStackSlotTransaction; -import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction; -import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction; import com.hypixel.hytale.server.core.modules.collision.WorldUtil; -import com.hypixel.hytale.server.core.modules.entity.BlockMigrationExtraInfo; import com.hypixel.hytale.server.core.modules.entity.component.Invulnerable; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class LivingEntity extends Entity { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.abstractBuilder(LivingEntity.class, Entity.CODEC) - .append(new KeyedCodec<>("Inventory", Inventory.CODEC), (livingEntity, inventory, extraInfo) -> { - livingEntity.setInventory(inventory); - if (extraInfo instanceof BlockMigrationExtraInfo) { - livingEntity.inventory.doMigration(((BlockMigrationExtraInfo)extraInfo).getBlockMigration()); - } - }, (livingEntity, extraInfo) -> livingEntity.inventory) + .append( + new KeyedCodec<>("Inventory", Inventory.CODEC), + (livingEntity, inventory, extraInfo) -> livingEntity.setInventory(inventory), + (livingEntity, extraInfo) -> livingEntity.inventory + ) .add() .afterDecode(livingEntity -> { if (livingEntity.inventory == null) { - livingEntity.setInventory(livingEntity.createDefaultInventory()); + livingEntity.setInventory(new Inventory()); } }) .build(); public static final int DEFAULT_ITEM_THROW_SPEED = 6; - @Nonnull - private final StatModifiersManager statModifiersManager = new StatModifiersManager(); private Inventory inventory; protected double currentFallDistance; - private EventRegistration armorInventoryChangeEventRegistration; private boolean isEquipmentNetworkOutdated; public LivingEntity() { - this.setInventory(this.createDefaultInventory()); + this.setInventory(new Inventory()); } public LivingEntity(@Nonnull World world) { super(world); - this.setInventory(this.createDefaultInventory()); + this.setInventory(new Inventory()); } - protected abstract Inventory createDefaultInventory(); - public boolean canBreathe( @Nonnull Ref ref, @Nonnull BlockMaterial breathingMaterial, int fluidId, @Nonnull ComponentAccessor componentAccessor ) { @@ -91,41 +80,12 @@ public abstract class LivingEntity extends Entity { } @Nonnull - public Inventory setInventory(Inventory inventory) { - return this.setInventory(inventory, false); - } - - @Nonnull - public Inventory setInventory(Inventory inventory, boolean ensureCapacity) { - List remainder = ensureCapacity ? new ObjectArrayList<>() : null; - inventory = this.setInventory(inventory, ensureCapacity, remainder); - if (remainder != null && !remainder.isEmpty()) { - ListTransaction transactionList = inventory.getCombinedHotbarFirst().addItemStacks(remainder); - - for (ItemStackTransaction var6 : transactionList.getList()) { - ; - } - } - - return inventory; - } - - @Nonnull - public Inventory setInventory(Inventory inventory, boolean ensureCapacity, List remainder) { + private Inventory setInventory(Inventory inventory) { if (this.inventory != null) { this.inventory.unregister(); } - if (this.armorInventoryChangeEventRegistration != null) { - this.armorInventoryChangeEventRegistration.unregister(); - } - - if (ensureCapacity) { - inventory = Inventory.ensureCapacity(inventory, remainder); - } - inventory.setEntity(this); - this.armorInventoryChangeEventRegistration = inventory.getArmor().registerChangeEvent(event -> this.statModifiersManager.setRecalculate(true)); this.inventory = inventory; return inventory; } @@ -145,8 +105,8 @@ public abstract class LivingEntity extends Entity { if (fallDamageActive) { Vector3d position = transformComponent.getPosition(); if (!movementStates.onGround) { - if (position.getY() > locY) { - this.currentFallDistance = this.currentFallDistance + (position.getY() - locY); + if (position.y() > locY) { + this.currentFallDistance = this.currentFallDistance + (position.y() - locY); } } else { this.currentFallDistance = 0.0; @@ -158,43 +118,42 @@ public abstract class LivingEntity extends Entity { super.moveTo(ref, locX, locY, locZ, componentAccessor); } - public boolean canDecreaseItemStackDurability(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - return false; - } - - public boolean canApplyItemStackPenalties(Ref ref, ComponentAccessor componentAccessor) { - return true; - } - @Nullable - public ItemStackSlotTransaction decreaseItemStackDurability( + public static ItemStackSlotTransaction decreaseItemStackDurability( @Nonnull Ref ref, @Nullable ItemStack itemStack, int inventoryId, int slotId, @Nonnull ComponentAccessor componentAccessor ) { - if (!this.canDecreaseItemStackDurability(ref, componentAccessor)) { - return null; - } else if (itemStack == null || itemStack.isEmpty() || itemStack.getItem() == null) { - return null; - } else if (itemStack.isBroken()) { - return null; - } else { - Item item = itemStack.getItem(); - ItemContainer section = this.inventory.getSectionById(inventoryId); - if (section == null) { + if (EntityUtils.getEntity(ref, componentAccessor) instanceof LivingEntity livingEntity) { + if (!ItemUtils.canDecreaseItemStackDurability(ref, componentAccessor)) { + return null; + } else if (itemStack == null || itemStack.isEmpty() || itemStack.getItem() == null) { + return null; + } else if (itemStack.isBroken()) { return null; - } else if (item.getArmor() != null) { - ItemStackSlotTransaction transaction = this.updateItemStackDurability( - ref, itemStack, section, slotId, -item.getDurabilityLossOnHit(), componentAccessor - ); - if (transaction.getSlotAfter().isBroken()) { - this.statModifiersManager.setRecalculate(true); - } - - return transaction; } else { - return item.getWeapon() != null - ? this.updateItemStackDurability(ref, itemStack, section, slotId, -item.getDurabilityLossOnHit(), componentAccessor) - : null; + Item item = itemStack.getItem(); + ItemContainer section = livingEntity.getInventory().getSectionById(inventoryId); + if (section == null) { + return null; + } else if (item.getArmor() != null) { + ItemStackSlotTransaction transaction = livingEntity.updateItemStackDurability( + ref, itemStack, section, slotId, -item.getDurabilityLossOnHit(), componentAccessor + ); + if (transaction.getSlotAfter().isBroken()) { + EntityStatMap entityStatMap = componentAccessor.getComponent(ref, EntityStatMap.getComponentType()); + if (entityStatMap != null) { + entityStatMap.getStatModifiersManager().scheduleRecalculate(); + } + } + + return transaction; + } else { + return item.getWeapon() != null + ? livingEntity.updateItemStackDurability(ref, itemStack, section, slotId, -item.getDurabilityLossOnHit(), componentAccessor) + : null; + } } + } else { + return null; } } @@ -221,11 +180,6 @@ public abstract class LivingEntity extends Entity { return temp; } - @Nonnull - public StatModifiersManager getStatModifiersManager() { - return this.statModifiersManager; - } - public double getCurrentFallDistance() { return this.currentFallDistance; } diff --git a/src/com/hypixel/hytale/server/core/entity/StatModifiersManager.java b/src/com/hypixel/hytale/server/core/entity/StatModifiersManager.java index e1de46b0..4d847dde 100644 --- a/src/com/hypixel/hytale/server/core/entity/StatModifiersManager.java +++ b/src/com/hypixel/hytale/server/core/entity/StatModifiersManager.java @@ -7,7 +7,7 @@ import com.hypixel.hytale.server.core.asset.type.gameplay.BrokenPenalties; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.item.config.ItemArmor; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; @@ -23,19 +23,17 @@ import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.Object2FloatMap; import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2FloatMap.Entry; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class StatModifiersManager { - @Nonnull - private final AtomicBoolean recalculate = new AtomicBoolean(); + private boolean recalculate = true; @Nonnull private final IntSet statsToClear = new IntOpenHashSet(); - public void setRecalculate(boolean value) { - this.recalculate.set(value); + public void scheduleRecalculate() { + this.recalculate = true; } public void queueEntityStatsToClear(@Nonnull int[] entityStatsToClear) { @@ -47,7 +45,7 @@ public class StatModifiersManager { public void recalculateEntityStatModifiers( @Nonnull Ref ref, @Nonnull EntityStatMap statMap, @Nonnull ComponentAccessor componentAccessor ) { - if (this.recalculate.getAndSet(false)) { + if (this.recalculate) { if (!this.statsToClear.isEmpty()) { IntIterator iterator = this.statsToClear.iterator(); @@ -59,18 +57,21 @@ public class StatModifiersManager { } World world = componentAccessor.getExternalData().getWorld(); - if (EntityUtils.getEntity(ref, componentAccessor) instanceof LivingEntity livingEntity) { - Inventory var12 = livingEntity.getInventory(); - Int2ObjectOpenHashMap effectModifiers = calculateEffectStatModifiers(ref, componentAccessor); - applyEffectModifiers(statMap, effectModifiers); - BrokenPenalties brokenPenalties = world.getGameplayConfig().getItemDurabilityConfig().getBrokenPenalties(); - Int2ObjectMap statModifiers = computeStatModifiers(brokenPenalties, var12); + Int2ObjectOpenHashMap> effectModifiers = calculateEffectStatModifiers(ref, componentAccessor); + applyEffectModifiers(statMap, effectModifiers); + BrokenPenalties brokenPenalties = world.getGameplayConfig().getItemDurabilityConfig().getBrokenPenalties(); + InventoryComponent.Armor armorComponent = componentAccessor.getComponent(ref, InventoryComponent.Armor.getComponentType()); + if (armorComponent != null) { + Int2ObjectMap> statModifiers = computeStatModifiers(brokenPenalties, armorComponent.getInventory()); applyStatModifiers(statMap, statModifiers); - ItemStack itemInHand = var12.getItemInHand(); - addItemStatModifiers(itemInHand, statMap, "*Weapon_", v -> v.getWeapon() != null ? v.getWeapon().getStatModifiers() : null); - if (itemInHand == null || itemInHand.getItem().getUtility().isCompatible()) { - addItemStatModifiers(var12.getUtilityItem(), statMap, "*Utility_", v -> v.getUtility().getStatModifiers()); - } + } + + ItemStack itemInHand = InventoryComponent.getItemInHand(componentAccessor, ref); + addItemStatModifiers(itemInHand, statMap, "*Weapon_", v -> v.getWeapon() != null ? v.getWeapon().getStatModifiers() : null); + if (itemInHand == null || itemInHand.getItem().getUtility().isCompatible()) { + InventoryComponent.Utility utilityComponent = componentAccessor.getComponent(ref, InventoryComponent.Utility.getComponentType()); + ItemStack utilityItem = utilityComponent != null ? utilityComponent.getActiveItem() : null; + addItemStatModifiers(utilityItem, statMap, "*Utility_", v -> v.getUtility().getStatModifiers()); } } } @@ -162,11 +163,10 @@ public class StatModifiersManager { @Nonnull private static Int2ObjectMap> computeStatModifiers( - @Nonnull BrokenPenalties brokenPenalties, @Nonnull Inventory inventory + @Nonnull BrokenPenalties brokenPenalties, @Nonnull ItemContainer armorContainer ) { Int2ObjectOpenHashMap> statModifiers = new Int2ObjectOpenHashMap<>(); double armorBrokenPenalty = brokenPenalties.getArmor(0.0); - ItemContainer armorContainer = inventory.getArmor(); for (short i = 0; i < armorContainer.getCapacity(); i++) { ItemStack armorItemStack = armorContainer.getItemStack(i); diff --git a/src/com/hypixel/hytale/server/core/entity/effect/EffectControllerComponent.java b/src/com/hypixel/hytale/server/core/entity/effect/EffectControllerComponent.java index 00964bae..5cb673d9 100644 --- a/src/com/hypixel/hytale/server/core/entity/effect/EffectControllerComponent.java +++ b/src/com/hypixel/hytale/server/core/entity/effect/EffectControllerComponent.java @@ -15,12 +15,11 @@ import com.hypixel.hytale.server.core.asset.type.entityeffect.config.OverlapBeha import com.hypixel.hytale.server.core.asset.type.entityeffect.config.RemovalBehavior; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; -import com.hypixel.hytale.server.core.entity.EntityUtils; -import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.livingentity.LivingEntityEffectSystem; import com.hypixel.hytale.server.core.modules.entity.player.PlayerSkinComponent; +import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; @@ -122,14 +121,35 @@ public class EffectControllerComponent implements Component { return false; } else { ActiveEntityEffect currentActiveEntityEffectEntry = this.activeEffects.get(entityEffectIndex); - label21: - if (currentActiveEntityEffectEntry == null) { + if (currentActiveEntityEffectEntry != null) { + if (currentActiveEntityEffectEntry.isInfinite()) { + return true; + } else if (overlapBehavior != OverlapBehavior.IGNORE) { + currentActiveEntityEffectEntry.remainingDuration = overlapBehavior == OverlapBehavior.EXTEND + ? currentActiveEntityEffectEntry.remainingDuration + duration + : duration; + this.addChange( + new EntityEffectUpdate( + EffectOp.Add, + entityEffectIndex, + currentActiveEntityEffectEntry.remainingDuration, + false, + currentActiveEntityEffectEntry.debuff, + currentActiveEntityEffectEntry.statusEffectIcon + ) + ); + return true; + } else { + return true; + } + } else { ActiveEntityEffect activeEntityEffectEntry = new ActiveEntityEffect( entityEffect.getId(), entityEffectIndex, duration, entityEffect.isDebuff(), entityEffect.getStatusEffectIcon(), entityEffect.isInvulnerable() ); this.activeEffects.put(entityEffectIndex, activeEntityEffectEntry); - if (EntityUtils.getEntity(ownerRef, componentAccessor) instanceof LivingEntity ownerLivingEntity) { - ownerLivingEntity.getStatModifiersManager().setRecalculate(true); + EntityStatMap entityStatMapComponent = componentAccessor.getComponent(ownerRef, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + entityStatMapComponent.getStatModifiersManager().scheduleRecalculate(); } this.setModelChange(ownerRef, entityEffect, entityEffectIndex, componentAccessor); @@ -145,29 +165,6 @@ public class EffectControllerComponent implements Component { ); this.invalidateCache(); return true; - } else if (currentActiveEntityEffectEntry.isInfinite()) { - return true; - } else { - switch (overlapBehavior) { - case EXTEND: - currentActiveEntityEffectEntry.remainingDuration += duration; - this.addChange( - new EntityEffectUpdate( - EffectOp.Add, - entityEffectIndex, - currentActiveEntityEffectEntry.remainingDuration, - false, - currentActiveEntityEffectEntry.debuff, - currentActiveEntityEffectEntry.statusEffectIcon - ) - ); - return true; - case IGNORE: - return true; - case OVERWRITE: - default: - break label21; - } } } } @@ -182,8 +179,9 @@ public class EffectControllerComponent implements Component { if (currentActiveEntityEffectEntry == null) { currentActiveEntityEffectEntry = new ActiveEntityEffect(entityEffect.getId(), entityEffectIndex, true, entityEffect.isInvulnerable()); this.activeEffects.put(entityEffectIndex, currentActiveEntityEffectEntry); - if (EntityUtils.getEntity(ownerRef, componentAccessor) instanceof LivingEntity ownerLivingEntity) { - ownerLivingEntity.getStatModifiersManager().setRecalculate(true); + EntityStatMap entityStatMapComponent = componentAccessor.getComponent(ownerRef, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + entityStatMapComponent.getStatModifiersManager().scheduleRecalculate(); } this.invalidateCache(); @@ -281,8 +279,9 @@ public class EffectControllerComponent implements Component { switch (removalBehavior) { case COMPLETE: this.activeEffects.remove(entityEffectIndex); - if (EntityUtils.getEntity(ownerRef, componentAccessor) instanceof LivingEntity ownerLivingEntity) { - ownerLivingEntity.getStatModifiersManager().setRecalculate(true); + EntityStatMap entityStatMapComponent = componentAccessor.getComponent(ownerRef, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + entityStatMapComponent.getStatModifiersManager().scheduleRecalculate(); } this.addChange(new EntityEffectUpdate(EffectOp.Remove, entityEffectIndex, 0.0F, false, false, "")); @@ -295,8 +294,9 @@ public class EffectControllerComponent implements Component { activeEffectEntry.remainingDuration = 0.0F; } - if (EntityUtils.getEntity(ownerRef, componentAccessor) instanceof LivingEntity ownerLivingEntity) { - ownerLivingEntity.getStatModifiersManager().setRecalculate(true); + EntityStatMap entityStatMapComponent = componentAccessor.getComponent(ownerRef, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + entityStatMapComponent.getStatModifiersManager().scheduleRecalculate(); } this.addChange( @@ -404,6 +404,19 @@ public class EffectControllerComponent implements Component { } } + public boolean hasEffect(@Nullable EntityEffect entityEffect) { + if (entityEffect == null) { + return false; + } else { + int entityEffectIndex = EntityEffect.getAssetMap().getIndex(entityEffect.getId()); + return entityEffectIndex == Integer.MIN_VALUE ? false : this.activeEffects.containsKey(entityEffectIndex); + } + } + + public boolean hasEffect(int effectIndex) { + return this.activeEffects.containsKey(effectIndex); + } + @Nonnull @Override public String toString() { diff --git a/src/com/hypixel/hytale/server/core/entity/entities/BlockEntity.java b/src/com/hypixel/hytale/server/core/entity/entities/BlockEntity.java index 820e2a9c..aae7e399 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/BlockEntity.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/BlockEntity.java @@ -9,8 +9,7 @@ import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.projectile.config.Projectile; @@ -26,6 +25,7 @@ import com.hypixel.hytale.server.core.modules.time.TimeResource; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BlockEntity implements Component { public static final BuilderCodec CODEC = BuilderCodec.builder(BlockEntity.class, BlockEntity::new) @@ -59,7 +59,7 @@ public class BlockEntity implements Component { Holder holder = EntityStore.REGISTRY.newHolder(); holder.addComponent(getComponentType(), new BlockEntity(blockTypeKey)); holder.addComponent(DespawnComponent.getComponentType(), DespawnComponent.despawnInSeconds(time, 120)); - holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(position.clone(), Vector3f.FORWARD)); + holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(new Vector3d(position), Rotation3f.IDENTITY)); holder.ensureComponent(Velocity.getComponentType()); holder.ensureComponent(UUIDComponent.getComponentType()); return holder; diff --git a/src/com/hypixel/hytale/server/core/entity/entities/Player.java b/src/com/hypixel/hytale/server/core/entity/entities/Player.java index c03d4e31..1de1c5da 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/Player.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/Player.java @@ -11,9 +11,9 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.event.IEventDispatcher; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricResults; import com.hypixel.hytale.metrics.MetricsRegistry; @@ -29,7 +29,6 @@ import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.gamemode.GameModeType; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; -import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; @@ -92,8 +91,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; -public class Player extends LivingEntity implements CommandSender, PermissionHolder, MetricProvider { +public class Player extends LivingEntity implements PermissionHolder, MetricProvider { @Nonnull public static final MetricsRegistry METRICS_REGISTRY = new MetricsRegistry() .register("Uuid", Entity::getUuid, Codec.UUID_STRING) @@ -182,18 +182,6 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol this.networkId = id; } - @Nonnull - @Override - protected Inventory createDefaultInventory() { - return new Inventory(); - } - - @Nonnull - @Override - public Inventory setInventory(Inventory inventory) { - return super.setInventory(inventory, true); - } - @Override public boolean remove() { if (this.wasRemoved.getAndSet(true)) { @@ -229,7 +217,7 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol } if (this.playerRef.getPacketHandler().getChannel().isActive()) { - this.playerRef.getPacketHandler().disconnect("Player removed from world!"); + this.playerRef.getPacketHandler().disconnect(Message.translation("server.general.disconnect.playerRemovedFromWorld")); LOGGER.at(Level.WARNING).withCause(this.removedBy).log("Player removed from world! %s", this); } @@ -249,7 +237,7 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - this.addLocationChange(ref, locX - position.getX(), locY - position.getY(), locZ - position.getZ(), componentAccessor); + this.addLocationChange(ref, locX - position.x(), locY - position.y(), locZ - position.z(), componentAccessor); super.moveTo(ref, locX, locY, locZ, componentAccessor); this.windowManager.validateWindows(ref, componentAccessor); } @@ -284,7 +272,14 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol } public void startClientReadyTimeout() { - ScheduledFuture task = HytaleServer.SCHEDULED_EXECUTOR.schedule(() -> this.handleClientReady(true), 10000L, TimeUnit.MILLISECONDS); + ScheduledFuture task = HytaleServer.SCHEDULED_EXECUTOR.schedule(() -> { + World world = this.world; + if (world == null) { + this.waitingForClientReady.set(null); + } else { + world.execute(() -> this.handleClientReady(true)); + } + }, 10000L, TimeUnit.MILLISECONDS); ScheduledFuture oldTask = this.waitingForClientReady.getAndSet(task); if (oldTask != null) { oldTask.cancel(false); @@ -308,13 +303,8 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol } } - public void sendInventory() { - this.getInventory().consumeIsDirty(); - this.playerRef.getPacketHandler().write(this.getInventory().toPacket()); - } - @Nonnull - public CompletableFuture saveConfig(@Nonnull World world, @Nonnull Holder holder) { + public CompletableFuture saveConfig(@Nonnull World world, @Nonnull Holder holder, boolean required) { MovementStatesComponent movementStatesComponent = holder.getComponent(MovementStatesComponent.getComponentType()); assert movementStatesComponent != null; @@ -324,7 +314,7 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol assert uuidComponent != null; this.data.getPerWorldData(world.getName()).setLastMovementStates(movementStatesComponent.getMovementStates(), false); - return Universe.get().getPlayerStorage().save(uuidComponent.getUuid(), holder); + return Universe.get().getPlayerStorage().save(uuidComponent.getUuid(), holder, required); } @Deprecated(forRemoval = true) @@ -424,16 +414,13 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol playerRefComponent.getPacketHandler().writeNoCache(new SetBlockPlacementOverride(overrideBlockPlacementRestrictions)); } - @Override - public void sendMessage(@Nonnull Message message) { - this.playerRef.sendMessage(message); - } - + @Deprecated(forRemoval = true) @Override public boolean hasPermission(@Nonnull String id) { return PermissionsModule.get().hasPermission(this.getUuid(), id); } + @Deprecated(forRemoval = true) @Override public boolean hasPermission(@Nonnull String id, boolean def) { return PermissionsModule.get().hasPermission(this.getUuid(), id, def); @@ -453,7 +440,7 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - collisionResultComponent.getCollisionStartPosition().assign(position); + collisionResultComponent.getCollisionStartPosition().set(position); collisionResultComponent.markPendingCollisionCheck(); } } @@ -541,24 +528,29 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol List sortedRespawnPoints = Arrays.stream(respawnPoints).sorted((a, b) -> { Vector3d posA = a.getRespawnPosition(); Vector3d posB = b.getRespawnPosition(); - double distA = playerPosition.distanceSquaredTo(posA.x, playerPosition.y, posA.z); - double distB = playerPosition.distanceSquaredTo(posB.x, playerPosition.y, posB.z); + double distA = playerPosition.distanceSquared(posA.x, playerPosition.y, posA.z); + double distB = playerPosition.distanceSquared(posB.x, playerPosition.y, posB.z); return Double.compare(distA, distB); }).toList(); BoundingBox playerBoundingBoxComponent = componentAccessor.getComponent(ref, BoundingBox.getComponentType()); - return playerBoundingBoxComponent == null - ? CompletableFuture.completedFuture(new Transform(sortedRespawnPoints.getFirst().getRespawnPosition())) - : tryUseSpawnPoint(world, sortedRespawnPoints, 0, ref, playerComponent, playerBoundingBoxComponent.getBoundingBox()); + if (playerBoundingBoxComponent == null) { + return CompletableFuture.completedFuture(new Transform(sortedRespawnPoints.getFirst().getRespawnPosition())); + } else { + PlayerRef playerRef = componentAccessor.getComponent(ref, PlayerRef.getComponentType()); + + assert playerRef != null; + + return tryUseSpawnPoint(world, sortedRespawnPoints, 0, ref, playerRef, playerBoundingBoxComponent.getBoundingBox()); + } } else { Transform worldSpawnPoint = world.getWorldConfig().getSpawnProvider().getSpawnPoint(ref, componentAccessor); - worldSpawnPoint.setRotation(Vector3f.ZERO); return CompletableFuture.completedFuture(worldSpawnPoint); } } @Nonnull private static CompletableFuture tryUseSpawnPoint( - World world, List sortedRespawnPoints, int index, Ref ref, Player playerComponent, Box boundingBox + World world, List sortedRespawnPoints, int index, Ref ref, PlayerRef playerRef, Box boundingBox ) { if (sortedRespawnPoints != null && index < sortedRespawnPoints.size()) { PlayerRespawnPointData respawnPoint = sortedRespawnPoints.get(index); @@ -571,7 +563,7 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol } if (respawnPoint.getBlockPosition() != null) { - boundingBox.forEachBlock(respawnPoint.getBlockPosition().toVector3d(), 2.0, requiredChunks, (x, y, z, chunks) -> { + boundingBox.forEachBlock(Vector3iUtil.toVector3d(respawnPoint.getBlockPosition()), 2.0, requiredChunks, (x, y, z, chunks) -> { chunks.add(ChunkUtil.indexChunkFromBlock(x, z)); return true; }); @@ -601,9 +593,9 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol .thenApplyAsync(v -> { Vector3d pos = ensureNoCollisionAtRespawnPosition(respawnPoint, boundingBox, world); if (pos != null) { - return new Transform(pos, Vector3f.ZERO); + return new Transform(pos, new Rotation3f(Rotation3f.IDENTITY)); } else { - playerComponent.sendMessage(Message.translation("server.general.respawnPointObstructed").param("respawnPointName", respawnPoint.getName())); + playerRef.sendMessage(Message.translation("server.general.respawnPointObstructed").param("respawnPointName", respawnPoint.getName())); return null; } }, world) @@ -613,18 +605,16 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol } }) .thenCompose( - v -> v != null - ? CompletableFuture.completedFuture(v) - : tryUseSpawnPoint(world, sortedRespawnPoints, index + 1, ref, playerComponent, boundingBox) + v -> v != null ? CompletableFuture.completedFuture(v) : tryUseSpawnPoint(world, sortedRespawnPoints, index + 1, ref, playerRef, boundingBox) ); } else { - playerComponent.sendMessage(Message.translation("server.general.allRespawnPointsObstructed")); + playerRef.sendMessage(Message.translation("server.general.allRespawnPointsObstructed")); return CompletableFuture.supplyAsync(() -> { if (!ref.isValid()) { return new Transform(); } else { Transform worldSpawnPoint = world.getWorldConfig().getSpawnProvider().getSpawnPoint(ref, ref.getStore()); - worldSpawnPoint.setRotation(Vector3f.ZERO); + worldSpawnPoint.setRotation(Rotation3f.IDENTITY); return worldSpawnPoint; } }, world); @@ -703,24 +693,6 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol return Math.min(this.clientViewRadius, HytaleServer.get().getConfig().getMaxViewRadius()); } - @Override - public boolean canDecreaseItemStackDurability(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - Player playerComponent = componentAccessor.getComponent(ref, getComponentType()); - - assert playerComponent != null; - - return playerComponent.gameMode != GameMode.Creative; - } - - @Override - public boolean canApplyItemStackPenalties(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - Player playerComponent = componentAccessor.getComponent(ref, getComponentType()); - - assert playerComponent != null; - - return playerComponent.gameMode != GameMode.Creative; - } - @Nullable @Override public ItemStackSlotTransaction updateItemStackDurability( @@ -733,14 +705,14 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol ) { ItemStackSlotTransaction transaction = super.updateItemStackDurability(ref, itemStack, container, slotId, durabilityChange, componentAccessor); if (transaction != null && transaction.getSlotAfter().isBroken() && !itemStack.isBroken()) { - Message itemNameMessage = Message.translation(itemStack.getItem().getTranslationKey()); - this.sendMessage(Message.translation("server.general.repair.itemBroken").param("itemName", itemNameMessage).color("#ff5555")); PlayerRef playerRefComponent = componentAccessor.getComponent(ref, PlayerRef.getComponentType()); assert playerRefComponent != null; + Message itemNameMessage = Message.translation(itemStack.getItem().getTranslationKey()); + playerRefComponent.sendMessage(Message.translation("server.general.repair.itemBroken").param("itemName", itemNameMessage).color("#ff5555")); int soundEventIndex = TempAssetIdUtil.getSoundEventIndex("SFX_Item_Break"); - SoundUtil.playSoundEvent2dToPlayer(playerRefComponent, soundEventIndex, SoundCategory.SFX); + SoundUtil.playSoundEvent2dToPlayer(playerRefComponent, soundEventIndex, SoundCategory.UI); } return transaction; @@ -905,7 +877,7 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol playerSettings = PlayerSettings.defaults(); } - return this.getInventory().getContainerForItemPickup(stack.getItem(), playerSettings).addItemStack(stack); + return Inventory.getContainerForItemPickup(ref, stack.getItem(), playerSettings, componentAccessor).addItemStack(stack); } @Override @@ -933,9 +905,4 @@ public class Player extends LivingEntity implements CommandSender, PermissionHol public String toString() { return "Player{uuid=" + this.getUuid() + ", clientViewRadius='" + this.clientViewRadius + "', " + super.toString() + "}"; } - - @Override - public String getDisplayName() { - return this.playerRef.getUsername(); - } } diff --git a/src/com/hypixel/hytale/server/core/entity/entities/ProjectileComponent.java b/src/com/hypixel/hytale/server/core/entity/entities/ProjectileComponent.java index ff953e27..20619640 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/ProjectileComponent.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/ProjectileComponent.java @@ -11,8 +11,8 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.asset.type.particle.config.WorldParticle; import com.hypixel.hytale.server.core.asset.type.projectile.config.Projectile; @@ -22,13 +22,16 @@ import com.hypixel.hytale.server.core.entity.ExplosionConfig; import com.hypixel.hytale.server.core.entity.ExplosionUtils; import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.UUIDComponent; +import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; import com.hypixel.hytale.server.core.modules.entity.DespawnComponent; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; +import com.hypixel.hytale.server.core.modules.entity.component.Intangible; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.damage.Damage; import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; import com.hypixel.hytale.server.core.modules.entity.damage.DamageSystems; +import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems; import com.hypixel.hytale.server.core.modules.physics.SimplePhysicsProvider; import com.hypixel.hytale.server.core.modules.physics.component.Velocity; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; @@ -38,10 +41,11 @@ import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ProjectileComponent implements Component { @Nonnull @@ -77,7 +81,7 @@ public class ProjectileComponent implements Component { ) .add() .append( - new KeyedCodec<>("LastBouncePosition", Vector3d.CODEC), + new KeyedCodec<>("LastBouncePosition", Vector3dUtil.CODEC), (projectileEntity, lastBouncePosition) -> projectileEntity.lastBouncePosition = lastBouncePosition, projectileEntity -> projectileEntity.lastBouncePosition ) @@ -95,7 +99,7 @@ public class ProjectileComponent implements Component { ) .add() .append( - new KeyedCodec<>("SppVelocity", Vector3d.CODEC), + new KeyedCodec<>("SppVelocity", Vector3dUtil.CODEC), (projectileEntity, v) -> projectileEntity.simplePhysicsProvider.setVelocity(v), projectileEntity -> projectileEntity.simplePhysicsProvider.getVelocity() ) @@ -127,7 +131,7 @@ public class ProjectileComponent implements Component { @Nonnull public static Holder assembleDefaultProjectile( - @Nonnull TimeResource time, @Nonnull String projectileAssetName, @Nonnull Vector3d position, @Nonnull Vector3f rotation + @Nonnull TimeResource time, @Nonnull String projectileAssetName, @Nonnull Vector3d position, @Nonnull Rotation3f rotation ) { if (projectileAssetName.isEmpty()) { throw new IllegalArgumentException("No projectile config typeName provided"); @@ -135,10 +139,15 @@ public class ProjectileComponent implements Component { Holder holder = EntityStore.REGISTRY.newHolder(); ProjectileComponent projectileComponent = new ProjectileComponent(projectileAssetName); holder.putComponent(getComponentType(), projectileComponent); + holder.ensureComponent(Intangible.getComponentType()); holder.putComponent(DespawnComponent.getComponentType(), DespawnComponent.despawnInMilliseconds(time, 60000L)); - holder.putComponent(TransformComponent.getComponentType(), new TransformComponent(position.clone(), rotation)); + holder.putComponent(TransformComponent.getComponentType(), new TransformComponent(new Vector3d(position), rotation)); holder.ensureComponent(Velocity.getComponentType()); holder.ensureComponent(UUIDComponent.getComponentType()); + MovementStatesComponent movementStatesComponent = holder.ensureAndGetComponent(MovementStatesComponent.getComponentType()); + movementStatesComponent.getMovementStates().flying = true; + movementStatesComponent.getMovementStates().idle = true; + holder.ensureComponent(EntityTrackerSystems.Visible.getComponentType()); return holder; } } @@ -166,7 +175,7 @@ public class ProjectileComponent implements Component { WorldParticle bounceParticles = this.projectile.getBounceParticles(); if (bounceParticles != null) { SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, 75.0, results); ParticleUtil.spawnParticleEffect(bounceParticles, position, results, componentAccessor); } @@ -180,7 +189,7 @@ public class ProjectileComponent implements Component { WorldParticle hitParticles = this.projectile.getHitParticles(); if (hitParticles != null) { SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, 75.0, results); ParticleUtil.spawnParticleEffect(hitParticles, position, results, componentAccessor); } @@ -217,11 +226,11 @@ public class ProjectileComponent implements Component { if (this.lastBouncePosition == null) { this.lastBouncePosition = new Vector3d(position); } else { - if (!(this.lastBouncePosition.distanceSquaredTo(position) >= 0.5)) { + if (!(this.lastBouncePosition.distanceSquared(position) >= 0.5)) { return; } - this.lastBouncePosition.assign(position); + this.lastBouncePosition.set(position); } this.onProjectileBounce(position, componentAccessor); @@ -238,13 +247,18 @@ public class ProjectileComponent implements Component { } else { this.onProjectileMissEvent(position, componentAccessor); } + + MovementStatesComponent movementStatesComponent = componentAccessor.getComponent(ref, MovementStatesComponent.getComponentType()); + if (movementStatesComponent != null) { + movementStatesComponent.getMovementStates().flying = false; + } } private void onProjectileMissEvent(@Nonnull Vector3d position, @Nonnull ComponentAccessor componentAccessor) { WorldParticle missParticles = this.projectile.getMissParticles(); if (missParticles != null) { SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, 75.0, results); ParticleUtil.spawnParticleEffect(missParticles, position, results, componentAccessor); } @@ -268,7 +282,7 @@ public class ProjectileComponent implements Component { WorldParticle deathParticles = this.projectile.getDeathParticles(); if (deathParticles != null) { SpatialResource, EntityStore> playerSpatialResource = commandBuffer.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, 75.0, results); ParticleUtil.spawnParticleEffect(deathParticles, position, results, commandBuffer); } @@ -295,17 +309,17 @@ public class ProjectileComponent implements Component { z += direction.z; holder.ensureAndGetComponent(TransformComponent.getComponentType()).setPosition(new Vector3d(x, y, z)); PhysicsMath.vectorFromAngles(yaw, pitch, direction); - direction.setLength(this.projectile.getMuzzleVelocity()); + direction.normalize(this.projectile.getMuzzleVelocity()); this.simplePhysicsProvider.setVelocity(direction); } public static void computeStartOffset( boolean pitchAdjust, double verticalCenterShot, double horizontalCenterShot, double depthShot, float yaw, float pitch, @Nonnull Vector3d offset ) { - offset.assign(0.0, 0.0, 0.0); + offset.set(0.0, 0.0, 0.0); if (depthShot != 0.0) { PhysicsMath.vectorFromAngles(yaw, pitchAdjust ? pitch : 0.0F, offset); - offset.setLength(depthShot); + offset.normalize(depthShot); } offset.add(horizontalCenterShot * -PhysicsMath.headingZ(yaw), -verticalCenterShot, horizontalCenterShot * PhysicsMath.headingX(yaw)); @@ -336,6 +350,10 @@ public class ProjectileComponent implements Component { this.brokenDamageModifier = 1.0F - penalty; } + public UUID getCreatorUuid() { + return this.creatorUuid; + } + public ProjectileComponent(@Nonnull ProjectileComponent other) { this.simplePhysicsProvider = other.simplePhysicsProvider; this.projectileAssetName = other.projectileAssetName; diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/CameraManager.java b/src/com/hypixel/hytale/server/core/entity/entities/player/CameraManager.java index 27a1980f..52bdadc2 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/CameraManager.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/CameraManager.java @@ -2,8 +2,6 @@ package com.hypixel.hytale.server.core.entity.entities.player; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.ClientCameraView; import com.hypixel.hytale.protocol.MouseButtonState; import com.hypixel.hytale.protocol.MouseButtonType; @@ -14,12 +12,15 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.EnumMap; import java.util.Map; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector2dc; +import org.joml.Vector3i; public class CameraManager implements Component { private final Map mouseStates = new EnumMap<>(MouseButtonType.class); private final Map mousePressedPosition = new EnumMap<>(MouseButtonType.class); private final Map mouseReleasedPosition = new EnumMap<>(MouseButtonType.class); - private Vector2d lastScreenPoint = Vector2d.ZERO; + private Vector2d lastScreenPoint = new Vector2d(); private Vector3i lastTargetBlock; public static ComponentType getComponentType() { @@ -64,10 +65,10 @@ public class CameraManager implements Component { } public void setLastScreenPoint(Vector2d lastScreenPoint) { - this.lastScreenPoint = lastScreenPoint; + this.lastScreenPoint.set(lastScreenPoint); } - public Vector2d getLastScreenPoint() { + public Vector2dc getLastScreenPoint() { return this.lastScreenPoint; } diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/HotbarManager.java b/src/com/hypixel/hytale/server/core/entity/entities/player/HotbarManager.java index 2976ea92..a1d4316b 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/HotbarManager.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/HotbarManager.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -52,9 +53,12 @@ public class HotbarManager { playerRefComponent.sendMessage(MESSAGE_GENERAL_HOTBAR_INVALID_GAME_MODE); } else { this.currentlyLoadingHotbar = true; - this.savedHotbars[hotbarIndex] = playerComponent.getInventory().getHotbar().clone(); - this.currentHotbar = hotbarIndex; - this.currentlyLoadingHotbar = false; + InventoryComponent.Hotbar hotbarComponent = componentAccessor.getComponent(playerRef, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent != null) { + this.savedHotbars[hotbarIndex] = hotbarComponent.getInventory().clone(); + this.currentHotbar = hotbarIndex; + this.currentlyLoadingHotbar = false; + } } } else { playerRefComponent.sendMessage(MESSAGE_GENERAL_HOTBAR_INVALID_SLOT); @@ -75,16 +79,19 @@ public class HotbarManager { playerRefComponent.sendMessage(MESSAGE_GENERAL_HOTBAR_INVALID_GAME_MODE); } else { this.currentlyLoadingHotbar = true; - ItemContainer hotbar = playerComponent.getInventory().getHotbar(); - hotbar.removeAllItemStacks(); - if (this.savedHotbars[hotbarIndex] != null) { - ItemContainer savedHotbar = this.savedHotbars[hotbarIndex].clone(); - savedHotbar.forEach(hotbar::setItemStackForSlot); - } + InventoryComponent.Hotbar hotbarComponent = componentAccessor.getComponent(playerRef, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent != null) { + ItemContainer hotbar = hotbarComponent.getInventory(); + hotbar.removeAllItemStacks(); + if (this.savedHotbars[hotbarIndex] != null) { + ItemContainer savedHotbar = this.savedHotbars[hotbarIndex].clone(); + savedHotbar.forEach(hotbar::setItemStackForSlot); + } - this.currentHotbar = hotbarIndex; - this.currentlyLoadingHotbar = false; - playerRefComponent.sendMessage(Message.translation("server.general.hotbar.loaded").param("id", hotbarIndex + 1)); + this.currentHotbar = hotbarIndex; + this.currentlyLoadingHotbar = false; + playerRefComponent.sendMessage(Message.translation("server.general.hotbar.loaded").param("id", hotbarIndex + 1)); + } } } else { playerRefComponent.sendMessage(MESSAGE_GENERAL_HOTBAR_INVALID_SLOT); diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/data/PlayerConfigData.java b/src/com/hypixel/hytale/server/core/entity/entities/player/data/PlayerConfigData.java index d790a201..2dbf30ad 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/data/PlayerConfigData.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/data/PlayerConfigData.java @@ -6,8 +6,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.codecs.map.MapCodec; import com.hypixel.hytale.codec.codecs.map.Object2IntMapCodec; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockMigration; import com.hypixel.hytale.server.core.universe.Universe; import it.unimi.dsi.fastutil.objects.Object2IntMap; @@ -23,6 +22,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import javax.annotation.Nonnull; +import org.joml.Vector3d; public final class PlayerConfigData { @Nonnull @@ -122,7 +122,7 @@ public final class PlayerConfigData { @Nonnull private Set unmodifiableActiveObjectiveUUIDs = Collections.unmodifiableSet(this.activeObjectiveUUIDs); public final Vector3d lastSavedPosition = new Vector3d(); - public final Vector3f lastSavedRotation = new Vector3f(); + public final Rotation3f lastSavedRotation = new Rotation3f(); public int getBlockIdVersion() { return this.blockIdVersion; diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/data/PlayerRespawnPointData.java b/src/com/hypixel/hytale/server/core/entity/entities/player/data/PlayerRespawnPointData.java index 6232942d..ab158acd 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/data/PlayerRespawnPointData.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/data/PlayerRespawnPointData.java @@ -3,22 +3,24 @@ package com.hypixel.hytale.server.core.entity.entities.player.data; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public final class PlayerRespawnPointData { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder(PlayerRespawnPointData.class, PlayerRespawnPointData::new) .append( - new KeyedCodec<>("BlockPosition", Vector3i.CODEC), + new KeyedCodec<>("BlockPosition", Vector3iUtil.CODEC), (respawnPointData, vector3i) -> respawnPointData.blockPosition = vector3i, respawnPointData -> respawnPointData.blockPosition ) .documentation("The position of the respawn block.") .add() .append( - new KeyedCodec<>("RespawnPosition", Vector3d.CODEC), + new KeyedCodec<>("RespawnPosition", Vector3dUtil.CODEC), (respawnPointData, vector3f) -> respawnPointData.respawnPosition = vector3f, respawnPointData -> respawnPointData.respawnPosition ) diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/hud/CustomUIHud.java b/src/com/hypixel/hytale/server/core/entity/entities/player/hud/CustomUIHud.java index 04f3a826..85728c69 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/hud/CustomUIHud.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/hud/CustomUIHud.java @@ -6,11 +6,25 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import javax.annotation.Nonnull; public abstract class CustomUIHud { + public static final String DEFAULT_KEY = "default"; @Nonnull private final PlayerRef playerRef; + @Nonnull + private final String key; + private int zOrder; public CustomUIHud(@Nonnull PlayerRef playerRef) { + this(playerRef, "default"); + } + + public CustomUIHud(@Nonnull PlayerRef playerRef, @Nonnull String key) { + this(playerRef, key, 0); + } + + public CustomUIHud(@Nonnull PlayerRef playerRef, @Nonnull String key, int zOrder) { this.playerRef = playerRef; + this.key = key; + this.zOrder = zOrder; } public void show() { @@ -20,7 +34,7 @@ public abstract class CustomUIHud { } public void update(boolean clear, @Nonnull UICommandBuilder commandBuilder) { - CustomHud customHud = new CustomHud(clear, commandBuilder.getCommands()); + CustomHud customHud = new CustomHud(this.key, this.zOrder, clear, commandBuilder.getCommands()); this.playerRef.getPacketHandler().writeNoCache(customHud); } @@ -29,5 +43,19 @@ public abstract class CustomUIHud { return this.playerRef; } + @Nonnull + public String getKey() { + return this.key; + } + + public int getZOrder() { + return this.zOrder; + } + + public void setZOrder(int zOrder) { + this.zOrder = zOrder; + this.update(false, new UICommandBuilder()); + } + protected abstract void build(@Nonnull UICommandBuilder var1); } diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/hud/HudManager.java b/src/com/hypixel/hytale/server/core/entity/entities/player/hud/HudManager.java index f47deb09..626edbc8 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/hud/HudManager.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/hud/HudManager.java @@ -7,6 +7,8 @@ import com.hypixel.hytale.protocol.packets.interface_.UpdateVisibleHudComponents import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.universe.PlayerRef; import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; @@ -38,20 +40,26 @@ public class HudManager { ); private final Set visibleHudComponents = ConcurrentHashMap.newKeySet(); private final Set unmodifiableVisibleHudComponents = Collections.unmodifiableSet(this.visibleHudComponents); - @Nullable - private CustomUIHud customHud; + private final Map customHuds = new LinkedHashMap<>(); public HudManager() { this.visibleHudComponents.addAll(DEFAULT_HUD_COMPONENTS); } - public HudManager(@Nonnull HudManager other) { - this.customHud = other.customHud; + @Nullable + public CustomUIHud getCustomHud(@Nonnull String key) { + return this.customHuds.get(key); } + @Nonnull + public Map getCustomHuds() { + return Collections.unmodifiableMap(this.customHuds); + } + + @Deprecated @Nullable public CustomUIHud getCustomHud() { - return this.customHud; + return this.customHuds.get("default"); } @Nonnull @@ -89,21 +97,42 @@ public class HudManager { this.sendVisibleHudComponents(ref.getPacketHandler()); } - public void setCustomHud(@Nonnull PlayerRef ref, @Nullable CustomUIHud hud) { - CustomUIHud oldHud = this.getCustomHud(); + public void addCustomHud(@Nonnull PlayerRef ref, @Nonnull CustomUIHud hud) { + String key = hud.getKey(); + CustomUIHud oldHud = this.customHuds.get(key); if (oldHud != hud) { - this.customHud = hud; - if (hud == null) { - ref.getPacketHandler().writeNoCache(new CustomHud(true, null)); - } else { - hud.show(); + if (oldHud != null) { + ref.getPacketHandler().writeNoCache(new CustomHud(key, 0, true, null)); } + + this.customHuds.put(key, hud); + hud.show(); + } + } + + public void removeCustomHud(@Nonnull PlayerRef ref, @Nonnull String key) { + if (this.customHuds.remove(key) != null) { + ref.getPacketHandler().writeNoCache(new CustomHud(key, 0, true, null)); + } + } + + @Deprecated + public void setCustomHud(@Nonnull PlayerRef ref, @Nullable CustomUIHud hud) { + if (hud == null) { + this.removeCustomHud(ref, "default"); + } else { + this.addCustomHud(ref, hud); } } public void resetHud(@Nonnull PlayerRef ref) { this.setVisibleHudComponents(ref, DEFAULT_HUD_COMPONENTS); - this.setCustomHud(ref, null); + + for (String key : this.customHuds.keySet()) { + ref.getPacketHandler().writeNoCache(new CustomHud(key, 0, true, null)); + } + + this.customHuds.clear(); } public void resetUserInterface(@Nonnull PlayerRef ref) { @@ -117,12 +146,6 @@ public class HudManager { @Nonnull @Override public String toString() { - return "HudManager{visibleHudComponents=" - + this.visibleHudComponents - + ", unmodifiableVisibleHudComponents=" - + this.unmodifiableVisibleHudComponents - + ", customHud=" - + this.customHud - + "}"; + return "HudManager{visibleHudComponents=" + this.visibleHudComponents + ", customHuds=" + this.customHuds + "}"; } } diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/pages/itemrepair/ItemRepairPage.java b/src/com/hypixel/hytale/server/core/entity/entities/player/pages/itemrepair/ItemRepairPage.java index c7ec71a3..38a1ff58 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/pages/itemrepair/ItemRepairPage.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/pages/itemrepair/ItemRepairPage.java @@ -39,7 +39,10 @@ public class ItemRepairPage extends ChoiceBasePage { for (short slot = 0; slot < itemContainer.getCapacity(); slot++) { ItemStack itemStack = itemContainer.getItemStack(slot); - if (!ItemStack.isEmpty(itemStack) && !itemStack.isUnbreakable() && !(itemStack.getDurability() >= itemStack.getMaxDurability())) { + if (!ItemStack.isEmpty(itemStack) + && !itemStack.isUnbreakable() + && itemStack.getItem().isRepairable() + && !(itemStack.getDurability() >= itemStack.getMaxDurability())) { ItemContext itemContext = new ItemContext(itemContainer, slot, itemStack); elements.add(new ItemRepairElement(itemStack, new RepairItemInteraction(itemContext, repairPenalty, heldItemContext))); } diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/windows/BlockWindow.java b/src/com/hypixel/hytale/server/core/entity/entities/player/windows/BlockWindow.java index 3e5d455a..c617b3cf 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/windows/BlockWindow.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/windows/BlockWindow.java @@ -68,7 +68,7 @@ public abstract class BlockWindow extends Window implements ValidatedWindow { public boolean validate(@Nonnull Ref ref, @Nonnull ComponentAccessor store) { World world = store.getExternalData().getWorld(); TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); - if (transformComponent != null && !(transformComponent.getPosition().distanceSquaredTo(this.x, this.y, this.z) > this.maxDistanceSqr)) { + if (transformComponent != null && !(transformComponent.getPosition().distanceSquared(this.x, this.y, this.z) > this.maxDistanceSqr)) { ChunkStore chunkStore = world.getChunkStore(); long chunkIndex = ChunkUtil.indexChunkFromBlock(this.x, this.z); Ref chunkRef = chunkStore.getChunkReference(chunkIndex); diff --git a/src/com/hypixel/hytale/server/core/entity/entities/player/windows/ContainerBlockWindow.java b/src/com/hypixel/hytale/server/core/entity/entities/player/windows/ContainerBlockWindow.java index 1ca3dc86..a4f1c9a1 100644 --- a/src/com/hypixel/hytale/server/core/entity/entities/player/windows/ContainerBlockWindow.java +++ b/src/com/hypixel/hytale/server/core/entity/entities/player/windows/ContainerBlockWindow.java @@ -53,13 +53,11 @@ public class ContainerBlockWindow extends BlockWindow implements ItemContainerWi @Override public void handleAction(@Nonnull Ref ref, @Nonnull Store store, @Nonnull WindowAction action) { if (action instanceof SortItemsAction sortAction) { - SortType sortType = SortType.fromPacket(sortAction.sortType); Player playerComponent = store.getComponent(ref, Player.getComponentType()); assert playerComponent != null; - playerComponent.getInventory().setSortType(sortType); - this.itemContainer.sortItems(sortType); + this.itemContainer.sortItems(SortType.TYPE); this.invalidate(); } } diff --git a/src/com/hypixel/hytale/server/core/entity/group/EntityGroup.java b/src/com/hypixel/hytale/server/core/entity/group/EntityGroup.java index 56b6ca27..fda31dbb 100644 --- a/src/com/hypixel/hytale/server/core/entity/group/EntityGroup.java +++ b/src/com/hypixel/hytale/server/core/entity/group/EntityGroup.java @@ -9,8 +9,8 @@ import com.hypixel.hytale.function.consumer.QuadConsumer; import com.hypixel.hytale.function.consumer.TriConsumer; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.HashSet; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; @@ -19,9 +19,9 @@ import javax.annotation.Nullable; public class EntityGroup implements Component { @Nonnull - private final Set> memberSet = new HashSet<>(); + private final Set> memberSet = new ReferenceOpenHashSet<>(); @Nonnull - private final List> memberList = new ObjectArrayList<>(); + private final List> memberList = new ReferenceArrayList<>(); @Nullable private Ref leaderRef; private boolean dissolved; diff --git a/src/com/hypixel/hytale/server/core/entity/knockback/KnockbackComponent.java b/src/com/hypixel/hytale/server/core/entity/knockback/KnockbackComponent.java index c0f34cfd..4547b01d 100644 --- a/src/com/hypixel/hytale/server/core/entity/knockback/KnockbackComponent.java +++ b/src/com/hypixel/hytale/server/core/entity/knockback/KnockbackComponent.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.core.entity.knockback; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ChangeVelocityType; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.splitvelocity.VelocityConfig; @@ -11,6 +10,7 @@ import it.unimi.dsi.fastutil.doubles.DoubleArrayList; import it.unimi.dsi.fastutil.doubles.DoubleList; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class KnockbackComponent implements Component { @Nonnull @@ -59,7 +59,7 @@ public class KnockbackComponent implements Component { public void applyModifiers() { for (int i = 0; i < this.modifiers.size(); i++) { - this.velocity.scale(this.modifiers.getDouble(i)); + this.velocity.mul(this.modifiers.getDouble(i)); } this.modifiers.clear(); diff --git a/src/com/hypixel/hytale/server/core/entity/movement/MovementStatesSystems.java b/src/com/hypixel/hytale/server/core/entity/movement/MovementStatesSystems.java index 1fecec70..29dc9330 100644 --- a/src/com/hypixel/hytale/server/core/entity/movement/MovementStatesSystems.java +++ b/src/com/hypixel/hytale/server/core/entity/movement/MovementStatesSystems.java @@ -16,6 +16,7 @@ import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.protocol.MovementStates; import com.hypixel.hytale.protocol.MovementStatesUpdate; import com.hypixel.hytale.protocol.SavedMovementStates; +import com.hypixel.hytale.server.core.entity.Frozen; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerWorldData; import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery; @@ -39,6 +40,14 @@ public class MovementStatesSystems { @Override public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { holder.ensureComponent(this.movementStatesComponentComponentType); + ComponentType frozenType = Frozen.getComponentType(); + boolean isFrozen = holder.getComponent(frozenType) != null; + MovementStatesComponent movementStatesComponent = holder.getComponent(this.movementStatesComponentComponentType); + if (isFrozen && movementStatesComponent != null) { + MovementStates states = movementStatesComponent.getMovementStates(); + states.idle = true; + states.horizontalIdle = true; + } } @Override @@ -188,6 +197,7 @@ public class MovementStatesSystems { to.crouching = from.crouching; to.forcedCrouching = from.forcedCrouching; to.falling = from.falling; + to.fallingFar = from.fallingFar; to.climbing = from.climbing; to.inFluid = from.inFluid; to.swimming = from.swimming; diff --git a/src/com/hypixel/hytale/server/core/entity/reference/PersistentRef.java b/src/com/hypixel/hytale/server/core/entity/reference/PersistentRef.java index 2254625f..d5556f90 100644 --- a/src/com/hypixel/hytale/server/core/entity/reference/PersistentRef.java +++ b/src/com/hypixel/hytale/server/core/entity/reference/PersistentRef.java @@ -21,6 +21,13 @@ public class PersistentRef { @Nullable protected Ref reference; + public PersistentRef() { + } + + public PersistentRef(@Nullable UUID uuid) { + this.uuid = uuid; + } + @Nullable public UUID getUuid() { return this.uuid; diff --git a/src/com/hypixel/hytale/server/core/event/events/ecs/BreakBlockEvent.java b/src/com/hypixel/hytale/server/core/event/events/ecs/BreakBlockEvent.java index 3bb81d2b..b0ac1dfc 100644 --- a/src/com/hypixel/hytale/server/core/event/events/ecs/BreakBlockEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/ecs/BreakBlockEvent.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.server.core.event.events.ecs; import com.hypixel.hytale.component.system.CancellableEcsEvent; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.inventory.ItemStack; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class BreakBlockEvent extends CancellableEcsEvent { @Nullable diff --git a/src/com/hypixel/hytale/server/core/event/events/ecs/DamageBlockEvent.java b/src/com/hypixel/hytale/server/core/event/events/ecs/DamageBlockEvent.java index 86544d2d..5d84b119 100644 --- a/src/com/hypixel/hytale/server/core/event/events/ecs/DamageBlockEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/ecs/DamageBlockEvent.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.server.core.event.events.ecs; import com.hypixel.hytale.component.system.CancellableEcsEvent; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.inventory.ItemStack; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class DamageBlockEvent extends CancellableEcsEvent { @Nullable diff --git a/src/com/hypixel/hytale/server/core/event/events/ecs/PlaceBlockEvent.java b/src/com/hypixel/hytale/server/core/event/events/ecs/PlaceBlockEvent.java index a1087d40..fa758166 100644 --- a/src/com/hypixel/hytale/server/core/event/events/ecs/PlaceBlockEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/ecs/PlaceBlockEvent.java @@ -1,18 +1,18 @@ package com.hypixel.hytale.server.core.event.events.ecs; import com.hypixel.hytale.component.system.CancellableEcsEvent; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.inventory.ItemStack; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PlaceBlockEvent extends CancellableEcsEvent { @Nullable private final ItemStack itemInHand; @Nonnull - private Vector3i targetBlock; + private final Vector3i targetBlock; @Nonnull private RotationTuple rotation; @@ -34,7 +34,7 @@ public class PlaceBlockEvent extends CancellableEcsEvent { public void setTargetBlock(@Nonnull Vector3i targetBlock) { Objects.requireNonNull(targetBlock, "Block can't be null"); - this.targetBlock = targetBlock; + this.targetBlock.set(targetBlock); } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/event/events/ecs/UseBlockEvent.java b/src/com/hypixel/hytale/server/core/event/events/ecs/UseBlockEvent.java index f4901455..5e3ca256 100644 --- a/src/com/hypixel/hytale/server/core/event/events/ecs/UseBlockEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/ecs/UseBlockEvent.java @@ -2,11 +2,11 @@ package com.hypixel.hytale.server.core.event.events.ecs; import com.hypixel.hytale.component.system.EcsEvent; import com.hypixel.hytale.component.system.ICancellableEcsEvent; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.entity.InteractionContext; import javax.annotation.Nonnull; +import org.joml.Vector3i; public abstract class UseBlockEvent extends EcsEvent { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/event/events/entity/LivingEntityInventoryChangeEvent.java b/src/com/hypixel/hytale/server/core/event/events/entity/LivingEntityInventoryChangeEvent.java deleted file mode 100644 index 762b522f..00000000 --- a/src/com/hypixel/hytale/server/core/event/events/entity/LivingEntityInventoryChangeEvent.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.hypixel.hytale.server.core.event.events.entity; - -import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.inventory.container.ItemContainer; -import com.hypixel.hytale.server.core.inventory.transaction.Transaction; -import javax.annotation.Nonnull; - -public class LivingEntityInventoryChangeEvent extends EntityEvent { - private ItemContainer itemContainer; - private Transaction transaction; - - public LivingEntityInventoryChangeEvent(LivingEntity entity, ItemContainer itemContainer, Transaction transaction) { - super(entity); - this.itemContainer = itemContainer; - this.transaction = transaction; - } - - public ItemContainer getItemContainer() { - return this.itemContainer; - } - - public Transaction getTransaction() { - return this.transaction; - } - - @Nonnull - @Override - public String toString() { - return "LivingEntityInventoryChangeEvent{itemContainer=" + this.itemContainer + ", transaction=" + this.transaction + "} " + super.toString(); - } -} diff --git a/src/com/hypixel/hytale/server/core/event/events/player/AddPlayerToWorldEvent.java b/src/com/hypixel/hytale/server/core/event/events/player/AddPlayerToWorldEvent.java index eeaa4567..dc5cf9a9 100644 --- a/src/com/hypixel/hytale/server/core/event/events/player/AddPlayerToWorldEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/player/AddPlayerToWorldEvent.java @@ -2,9 +2,11 @@ package com.hypixel.hytale.server.core.event.events.player; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.event.IEvent; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class AddPlayerToWorldEvent implements IEvent { @Nonnull @@ -12,10 +14,13 @@ public class AddPlayerToWorldEvent implements IEvent { @Nonnull private final World world; private boolean broadcastJoinMessage = true; + @Nullable + private Message joinMessage; - public AddPlayerToWorldEvent(@Nonnull Holder holder, @Nonnull World world) { + public AddPlayerToWorldEvent(@Nonnull Holder holder, @Nonnull World world, @Nullable Message joinMessage) { this.holder = holder; this.world = world; + this.joinMessage = joinMessage; } @Nonnull @@ -29,16 +34,32 @@ public class AddPlayerToWorldEvent implements IEvent { } public boolean shouldBroadcastJoinMessage() { - return this.broadcastJoinMessage; + return this.broadcastJoinMessage && this.joinMessage != null; } public void setBroadcastJoinMessage(boolean broadcastJoinMessage) { this.broadcastJoinMessage = broadcastJoinMessage; } + @Nullable + public Message getJoinMessage() { + return this.joinMessage; + } + + public void setJoinMessage(@Nullable Message joinMessage) { + this.joinMessage = joinMessage; + } + @Nonnull @Override public String toString() { - return "AddPlayerToWorldEvent{world=" + this.world + ", broadcastJoinMessage=" + this.broadcastJoinMessage + "} " + super.toString(); + return "AddPlayerToWorldEvent{world=" + + this.world + + ", broadcastJoinMessage=" + + this.broadcastJoinMessage + + ", joinMessage=" + + this.joinMessage + + "} " + + super.toString(); } } diff --git a/src/com/hypixel/hytale/server/core/event/events/player/PlayerInteractEvent.java b/src/com/hypixel/hytale/server/core/event/events/player/PlayerInteractEvent.java index 104062ce..a4245f93 100644 --- a/src/com/hypixel/hytale/server/core/event/events/player/PlayerInteractEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/player/PlayerInteractEvent.java @@ -2,13 +2,13 @@ package com.hypixel.hytale.server.core.event.events.player; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.event.ICancellable; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; @Deprecated public class PlayerInteractEvent extends PlayerEvent implements ICancellable { diff --git a/src/com/hypixel/hytale/server/core/event/events/player/PlayerMouseButtonEvent.java b/src/com/hypixel/hytale/server/core/event/events/player/PlayerMouseButtonEvent.java index 829fba98..b9b93600 100644 --- a/src/com/hypixel/hytale/server/core/event/events/player/PlayerMouseButtonEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/player/PlayerMouseButtonEvent.java @@ -2,15 +2,15 @@ package com.hypixel.hytale.server.core.event.events.player; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.event.ICancellable; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.MouseButtonEvent; -import com.hypixel.hytale.protocol.Vector2f; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2fc; +import org.joml.Vector3i; public class PlayerMouseButtonEvent extends PlayerEvent implements ICancellable { @Nonnull @@ -19,7 +19,7 @@ public class PlayerMouseButtonEvent extends PlayerEvent implements ICancel private final Item itemInHand; private final Vector3i targetBlock; private final Entity targetEntity; - private final Vector2f screenPoint; + private final Vector2fc screenPoint; private final MouseButtonEvent mouseButton; private boolean cancelled; @@ -31,7 +31,7 @@ public class PlayerMouseButtonEvent extends PlayerEvent implements ICancel @Nonnull Item itemInHand, @Nonnull Vector3i targetBlock, @Nonnull Entity targetEntity, - @Nonnull Vector2f screenPoint, + @Nonnull Vector2fc screenPoint, @Nonnull MouseButtonEvent mouseButton ) { super(ref, player); @@ -75,7 +75,7 @@ public class PlayerMouseButtonEvent extends PlayerEvent implements ICancel return this.targetEntity; } - public Vector2f getScreenPoint() { + public Vector2fc getScreenPoint() { return this.screenPoint; } diff --git a/src/com/hypixel/hytale/server/core/event/events/player/PlayerMouseMotionEvent.java b/src/com/hypixel/hytale/server/core/event/events/player/PlayerMouseMotionEvent.java index 45f2843a..d59d4e82 100644 --- a/src/com/hypixel/hytale/server/core/event/events/player/PlayerMouseMotionEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/player/PlayerMouseMotionEvent.java @@ -2,21 +2,21 @@ package com.hypixel.hytale.server.core.event.events.player; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.event.ICancellable; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.MouseMotionEvent; -import com.hypixel.hytale.protocol.Vector2f; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector2fc; +import org.joml.Vector3i; public class PlayerMouseMotionEvent extends PlayerEvent implements ICancellable { private final long clientUseTime; private final Item itemInHand; private final Vector3i targetBlock; private final Entity targetEntity; - private final Vector2f screenPoint; + private final Vector2fc screenPoint; private final MouseMotionEvent mouseMotion; private boolean cancelled; @@ -27,7 +27,7 @@ public class PlayerMouseMotionEvent extends PlayerEvent implements ICancel Item itemInHand, Vector3i targetBlock, Entity targetEntity, - Vector2f screenPoint, + Vector2fc screenPoint, MouseMotionEvent mouseMotion ) { super(ref, player); @@ -65,7 +65,7 @@ public class PlayerMouseMotionEvent extends PlayerEvent implements ICancel return this.targetEntity; } - public Vector2f getScreenPoint() { + public Vector2fc getScreenPoint() { return this.screenPoint; } diff --git a/src/com/hypixel/hytale/server/core/event/events/player/PlayerSetupConnectEvent.java b/src/com/hypixel/hytale/server/core/event/events/player/PlayerSetupConnectEvent.java index dbbc4c4c..ca5b59d3 100644 --- a/src/com/hypixel/hytale/server/core/event/events/player/PlayerSetupConnectEvent.java +++ b/src/com/hypixel/hytale/server/core/event/events/player/PlayerSetupConnectEvent.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.event.IEvent; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.protocol.HostAddress; import com.hypixel.hytale.protocol.packets.auth.ClientReferral; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.auth.PlayerAuthentication; import com.hypixel.hytale.server.core.io.PacketHandler; import java.util.Objects; @@ -14,19 +15,20 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class PlayerSetupConnectEvent implements IEvent, ICancellable { - public static final String DEFAULT_REASON = "You have been disconnected from the server!"; + public static final Message DEFAULT_REASON = Message.translation("client.general.disconnect.setupCancelled"); private final PacketHandler packetHandler; private final String username; + @Nonnull private final UUID uuid; private final PlayerAuthentication auth; private final byte[] referralData; private final HostAddress referralSource; private boolean cancelled; - private String reason; + private Message reason; private ClientReferral clientReferral; public PlayerSetupConnectEvent( - PacketHandler packetHandler, String username, UUID uuid, PlayerAuthentication auth, byte[] referralData, HostAddress referralSource + PacketHandler packetHandler, String username, @Nonnull UUID uuid, PlayerAuthentication auth, byte[] referralData, HostAddress referralSource ) { this.packetHandler = packetHandler; this.username = username; @@ -34,7 +36,7 @@ public class PlayerSetupConnectEvent implements IEvent, ICancellable { this.auth = auth; this.referralData = referralData; this.referralSource = referralSource; - this.reason = "You have been disconnected from the server!"; + this.reason = DEFAULT_REASON; this.cancelled = false; } @@ -42,6 +44,7 @@ public class PlayerSetupConnectEvent implements IEvent, ICancellable { return this.packetHandler; } + @Nonnull public UUID getUuid() { return this.uuid; } @@ -94,11 +97,11 @@ public class PlayerSetupConnectEvent implements IEvent, ICancellable { } } - public String getReason() { + public Message getReason() { return this.reason; } - public void setReason(String reason) { + public void setReason(Message reason) { Objects.requireNonNull(reason, "Reason can't be null"); this.reason = reason; } diff --git a/src/com/hypixel/hytale/server/core/event/events/player/RemovedPlayerFromWorldEvent.java b/src/com/hypixel/hytale/server/core/event/events/player/RemovedPlayerFromWorldEvent.java new file mode 100644 index 00000000..ce529ea3 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/event/events/player/RemovedPlayerFromWorldEvent.java @@ -0,0 +1,64 @@ +package com.hypixel.hytale.server.core.event.events.player; + +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.event.IEvent; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class RemovedPlayerFromWorldEvent implements IEvent { + @Nonnull + private final Holder holder; + @Nonnull + private final World world; + private boolean broadcastLeaveMessage = true; + @Nullable + private Message leaveMessage; + + public RemovedPlayerFromWorldEvent(@Nonnull Holder holder, @Nonnull World world, @Nullable Message leaveMessage) { + this.holder = holder; + this.world = world; + this.leaveMessage = leaveMessage; + } + + @Nonnull + public Holder getHolder() { + return this.holder; + } + + @Nonnull + public World getWorld() { + return this.world; + } + + public boolean shouldBroadcastLeaveMessage() { + return this.broadcastLeaveMessage && this.leaveMessage != null; + } + + public void setBroadcastLeaveMessage(boolean broadcastLeaveMessage) { + this.broadcastLeaveMessage = broadcastLeaveMessage; + } + + @Nullable + public Message getLeaveMessage() { + return this.leaveMessage; + } + + public void setLeaveMessage(@Nullable Message leaveMessage) { + this.leaveMessage = leaveMessage; + } + + @Override + public String toString() { + return "RemovePlayerFromWorldEvent{, world=" + + this.world + + ", broadcastLeaveMessage=" + + this.broadcastLeaveMessage + + ", leaveMessage=" + + this.leaveMessage + + "} " + + super.toString(); + } +} diff --git a/src/com/hypixel/hytale/server/core/inventory/Inventory.java b/src/com/hypixel/hytale/server/core/inventory/Inventory.java index ad406ecb..2f70f2c1 100644 --- a/src/com/hypixel/hytale/server/core/inventory/Inventory.java +++ b/src/com/hypixel/hytale/server/core/inventory/Inventory.java @@ -3,11 +3,10 @@ package com.hypixel.hytale.server.core.inventory; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.event.EventRegistration; -import com.hypixel.hytale.event.IEventDispatcher; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.InteractionChainData; import com.hypixel.hytale.protocol.InteractionType; @@ -15,12 +14,12 @@ import com.hypixel.hytale.protocol.ItemArmorSlot; import com.hypixel.hytale.protocol.PickupLocation; import com.hypixel.hytale.protocol.SmartMoveType; import com.hypixel.hytale.protocol.packets.inventory.UpdatePlayerInventory; -import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.item.config.ItemArmor; import com.hypixel.hytale.server.core.asset.type.item.config.ItemUtility; import com.hypixel.hytale.server.core.asset.type.item.config.ItemWeapon; +import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.InteractionManager; @@ -29,19 +28,14 @@ import com.hypixel.hytale.server.core.entity.StatModifiersManager; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.windows.ItemContainerWindow; import com.hypixel.hytale.server.core.entity.entities.player.windows.Window; -import com.hypixel.hytale.server.core.event.events.entity.LivingEntityInventoryChangeEvent; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; -import com.hypixel.hytale.server.core.inventory.container.EmptyItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; -import com.hypixel.hytale.server.core.inventory.container.ItemContainerUtil; -import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; import com.hypixel.hytale.server.core.inventory.container.SortType; import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction; import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction; import com.hypixel.hytale.server.core.inventory.transaction.MoveTransaction; -import com.hypixel.hytale.server.core.inventory.transaction.SlotTransaction; -import com.hypixel.hytale.server.core.io.NetworkSerializable; import com.hypixel.hytale.server.core.modules.entity.player.PlayerSettings; +import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; import com.hypixel.hytale.server.core.modules.interaction.InteractionModule; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; @@ -52,12 +46,11 @@ import com.hypixel.hytale.server.core.util.UUIDUtil; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Function; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class Inventory implements NetworkSerializable { +@Deprecated(forRemoval = true) +public class Inventory { public static final short DEFAULT_HOTBAR_CAPACITY = 9; public static final short DEFAULT_UTILITY_CAPACITY = 4; public static final short DEFAULT_TOOLS_CAPACITY = 23; @@ -73,280 +66,54 @@ public class Inventory implements NetworkSerializable { public static final int BACKPACK_SECTION_ID = -9; public static final byte INACTIVE_SLOT_INDEX = -1; public static final int VERSION = 4; - public static final BuilderCodec CODEC = BuilderCodec.builder(Inventory.class, () -> new Inventory(null)) + public static final BuilderCodec CODEC = BuilderCodec.builder(Inventory.class, Inventory::new) .versioned() .codecVersion(4) - .append(new KeyedCodec<>("Storage", ItemContainer.CODEC), (o, i) -> o.storage = i, o -> o.storage) + .append(new KeyedCodec<>("Storage", ItemContainer.CODEC), (o, i) -> o.deserializedStorage = i, o -> o.deserializedStorage) .add() - .append(new KeyedCodec<>("Armor", ItemContainer.CODEC), (o, i) -> o.armor = i, o -> o.armor) + .append(new KeyedCodec<>("Armor", ItemContainer.CODEC), (o, i) -> o.deserializedArmor = i, o -> o.deserializedArmor) .add() - .append(new KeyedCodec<>("HotBar", ItemContainer.CODEC), (o, i) -> o.hotbar = i, o -> o.hotbar) + .append(new KeyedCodec<>("HotBar", ItemContainer.CODEC), (o, i) -> o.deserializedHotbar = i, o -> o.deserializedHotbar) .add() - .append(new KeyedCodec<>("Utility", ItemContainer.CODEC), (o, i) -> o.utility = i, o -> o.utility) + .append(new KeyedCodec<>("Utility", ItemContainer.CODEC), (o, i) -> o.deserializedUtility = i, o -> o.deserializedUtility) .add() - .append(new KeyedCodec<>("Backpack", ItemContainer.CODEC), (o, i) -> o.backpack = i, o -> o.backpack) + .append(new KeyedCodec<>("Backpack", ItemContainer.CODEC), (o, i) -> o.deserializedBackpack = i, o -> o.deserializedBackpack) .add() - .append(new KeyedCodec<>("ActiveHotbarSlot", Codec.BYTE), (o, i) -> o.activeHotbarSlot = i, o -> o.activeHotbarSlot) + .append(new KeyedCodec<>("ActiveHotbarSlot", Codec.BYTE), (o, i) -> o.deserializedActiveHotbarSlot = i, o -> o.deserializedActiveHotbarSlot) .add() - .append(new KeyedCodec<>("Tool", ItemContainer.CODEC), (o, i) -> o.tools = i, o -> o.tools) + .append(new KeyedCodec<>("Tool", ItemContainer.CODEC), (o, i) -> o.deserializedTools = i, o -> o.deserializedTools) .add() - .append(new KeyedCodec<>("ActiveToolsSlot", Codec.BYTE), (o, i) -> o.activeToolsSlot = i, o -> o.activeToolsSlot) + .append(new KeyedCodec<>("ActiveToolsSlot", Codec.BYTE), (o, i) -> o.deserializedActiveToolsSlot = i, o -> o.deserializedActiveToolsSlot) .add() - .append(new KeyedCodec<>("ActiveUtilitySlot", Codec.BYTE), (o, i) -> o.activeUtilitySlot = i, o -> o.activeUtilitySlot) + .append(new KeyedCodec<>("ActiveUtilitySlot", Codec.BYTE), (o, i) -> o.deserializedActiveUtilitySlot = i, o -> o.deserializedActiveUtilitySlot) .add() - .append(new KeyedCodec<>("SortType", new EnumCodec<>(SortType.class, EnumCodec.EnumStyle.LEGACY)), (o, i) -> o.sortType = i, o -> o.sortType) - .setVersionRange(0, 3) - .add() - .append(new KeyedCodec<>("SortType", new EnumCodec<>(SortType.class)), (o, i) -> o.sortType = i, o -> o.sortType) - .setVersionRange(4, 4) - .add() - .afterDecode(Inventory::postDecode) .build(); - private final AtomicBoolean isDirty = new AtomicBoolean(); - private final AtomicBoolean needsSaving = new AtomicBoolean(); - private ItemContainer storage = EmptyItemContainer.INSTANCE; - private ItemContainer armor = EmptyItemContainer.INSTANCE; - private ItemContainer hotbar = EmptyItemContainer.INSTANCE; - private ItemContainer utility = EmptyItemContainer.INSTANCE; - @Deprecated - private ItemContainer tools = EmptyItemContainer.INSTANCE; - private ItemContainer backpack = EmptyItemContainer.INSTANCE; - private CombinedItemContainer combinedHotbarStorageBackpack; - private CombinedItemContainer combinedHotbarFirst; - private CombinedItemContainer combinedStorageFirst; - private CombinedItemContainer combinedBackpackStorageHotbar; - private CombinedItemContainer combinedBackpackHotbarStorage; - private CombinedItemContainer combinedStorageHotbarBackpack; - private CombinedItemContainer combinedArmorHotbarStorage; - private CombinedItemContainer combinedArmorHotbarUtilityStorage; - private CombinedItemContainer combinedHotbarUtilityConsumableStorage; - private CombinedItemContainer combinedEverything; - private byte activeHotbarSlot; - private byte activeUtilitySlot = -1; - private byte activeToolsSlot = -1; + private ItemContainer deserializedStorage; + private ItemContainer deserializedArmor; + private ItemContainer deserializedHotbar; + private ItemContainer deserializedUtility; + private ItemContainer deserializedTools; + private ItemContainer deserializedBackpack; + private byte deserializedActiveHotbarSlot; + private byte deserializedActiveUtilitySlot = -1; + private byte deserializedActiveToolsSlot = -1; + @Nullable + private InventoryComponent.Storage storage; + @Nullable + private InventoryComponent.Armor armor; + @Nullable + private InventoryComponent.Hotbar hotbar; + @Nullable + private InventoryComponent.Utility utility; + @Nullable + private InventoryComponent.Tool tools; + @Nullable + private InventoryComponent.Backpack backpack; @Nullable private LivingEntity entity; - @Deprecated - private SortType sortType = SortType.NAME; - @Nullable - private EventRegistration armorChange; - @Nullable - private EventRegistration storageChange; - @Nullable - private EventRegistration hotbarChange; - @Nullable - private EventRegistration utilityChange; - @Nullable - private EventRegistration toolChange; - @Nullable - private EventRegistration backpackChange; - private boolean _usingToolsItem = false; - - private Inventory(Void dummy) { - } - - public Inventory() { - this((short)36, DEFAULT_ARMOR_CAPACITY, (short)9, (short)4, (short)23); - } - - public Inventory(short storageCapacity, short armorCapacity, short hotbarCapacity, short utilityCapacity, short toolCapacity) { - this( - (ItemContainer)(storageCapacity == 0 ? EmptyItemContainer.INSTANCE : new SimpleItemContainer(storageCapacity)), - (ItemContainer)(armorCapacity == 0 ? EmptyItemContainer.INSTANCE : new SimpleItemContainer(armorCapacity)), - (ItemContainer)(hotbarCapacity == 0 ? EmptyItemContainer.INSTANCE : new SimpleItemContainer(hotbarCapacity)), - (ItemContainer)(utilityCapacity == 0 ? EmptyItemContainer.INSTANCE : new SimpleItemContainer(utilityCapacity)), - (ItemContainer)(toolCapacity == 0 ? EmptyItemContainer.INSTANCE : new SimpleItemContainer(toolCapacity)), - EmptyItemContainer.INSTANCE - ); - } - - public Inventory(ItemContainer storage, ItemContainer armor, ItemContainer hotbar, ItemContainer utility, ItemContainer tools, ItemContainer backpack) { - this.storage = storage; - this.armor = ItemContainerUtil.trySetArmorFilters(armor); - this.hotbar = hotbar; - this.utility = ItemContainerUtil.trySetSlotFilters( - utility, (type, container, slot, itemStack) -> itemStack == null || itemStack.getItem().getUtility().isUsable() - ); - this.tools = tools; - this.backpack = backpack; - this.buildCombinedContains(); - this.registerChangeEvents(); - } - - protected void registerChangeEvents() { - this.storageChange = this.storage - .registerChangeEvent( - e -> { - this.markChanged(); - IEventDispatcher dispatcher = HytaleServer.get() - .getEventBus() - .dispatchFor(LivingEntityInventoryChangeEvent.class, this.entity.getWorld().getName()); - if (dispatcher.hasListener()) { - dispatcher.dispatch(new LivingEntityInventoryChangeEvent(this.entity, e.container(), e.transaction())); - } - } - ); - this.armorChange = this.armor - .registerChangeEvent( - e -> { - this.markChanged(); - IEventDispatcher dispatcher = HytaleServer.get() - .getEventBus() - .dispatchFor(LivingEntityInventoryChangeEvent.class, this.entity.getWorld().getName()); - if (dispatcher.hasListener()) { - dispatcher.dispatch(new LivingEntityInventoryChangeEvent(this.entity, e.container(), e.transaction())); - } - - this.entity.invalidateEquipmentNetwork(); - } - ); - this.hotbarChange = this.hotbar - .registerChangeEvent( - e -> { - this.markChanged(); - IEventDispatcher dispatcher = HytaleServer.get() - .getEventBus() - .dispatchFor(LivingEntityInventoryChangeEvent.class, this.entity.getWorld().getName()); - if (dispatcher.hasListener()) { - dispatcher.dispatch(new LivingEntityInventoryChangeEvent(this.entity, e.container(), e.transaction())); - } - - if (this.activeHotbarSlot != -1 && this.entity != null && e.transaction().wasSlotModified(this.activeHotbarSlot)) { - if (e.transaction() instanceof SlotTransaction slot && ItemStack.isEquivalentType(slot.getSlotBefore(), slot.getSlotAfter())) { - return; - } - - StatModifiersManager statModifiersManager = this.entity.getStatModifiersManager(); - this.entity.invalidateEquipmentNetwork(); - statModifiersManager.setRecalculate(true); - ItemStack itemStack = this.getItemInHand(); - if (itemStack == null) { - return; - } - - ItemWeapon itemWeapon = itemStack.getItem().getWeapon(); - if (itemWeapon == null) { - return; - } - - int[] entityStatsToClear = itemWeapon.getEntityStatsToClear(); - if (entityStatsToClear == null) { - return; - } - - statModifiersManager.queueEntityStatsToClear(entityStatsToClear); - } - } - ); - this.utilityChange = this.utility - .registerChangeEvent( - e -> { - this.markChanged(); - IEventDispatcher dispatcher = HytaleServer.get() - .getEventBus() - .dispatchFor(LivingEntityInventoryChangeEvent.class, this.entity.getWorld().getName()); - if (dispatcher.hasListener()) { - dispatcher.dispatch(new LivingEntityInventoryChangeEvent(this.entity, e.container(), e.transaction())); - } - - if (this.activeUtilitySlot != -1 && this.entity != null && e.transaction().wasSlotModified(this.activeUtilitySlot)) { - if (e.transaction() instanceof SlotTransaction slot && ItemStack.isEquivalentType(slot.getSlotBefore(), slot.getSlotAfter())) { - return; - } - - StatModifiersManager statModifiersManager = this.entity.getStatModifiersManager(); - this.entity.invalidateEquipmentNetwork(); - statModifiersManager.setRecalculate(true); - ItemStack itemStack = this.getUtilityItem(); - if (itemStack == null) { - return; - } - - ItemUtility itemUtility = itemStack.getItem().getUtility(); - if (itemUtility == null) { - return; - } - - int[] entityStatsToClear = itemUtility.getEntityStatsToClear(); - if (entityStatsToClear == null) { - return; - } - - statModifiersManager.queueEntityStatsToClear(entityStatsToClear); - } - } - ); - this.toolChange = this.tools - .registerChangeEvent( - e -> { - this.markChanged(); - IEventDispatcher dispatcher = HytaleServer.get() - .getEventBus() - .dispatchFor(LivingEntityInventoryChangeEvent.class, this.entity.getWorld().getName()); - if (dispatcher.hasListener()) { - dispatcher.dispatch(new LivingEntityInventoryChangeEvent(this.entity, e.container(), e.transaction())); - } - } - ); - this.registerBackpackListener(); - } - - private void registerBackpackListener() { - this.unregisterBackpackChange(); - this.backpackChange = this.backpack - .registerChangeEvent( - e -> { - this.markChanged(); - IEventDispatcher dispatcher = HytaleServer.get() - .getEventBus() - .dispatchFor(LivingEntityInventoryChangeEvent.class, this.entity.getWorld().getName()); - if (dispatcher.hasListener()) { - dispatcher.dispatch(new LivingEntityInventoryChangeEvent(this.entity, e.container(), e.transaction())); - } - } - ); - } public void unregister() { this.entity = null; - if (this.storageChange != null) { - this.storageChange.unregister(); - this.storageChange = null; - } - - if (this.armorChange != null) { - this.armorChange.unregister(); - this.armorChange = null; - } - - if (this.hotbarChange != null) { - this.hotbarChange.unregister(); - this.hotbarChange = null; - } - - if (this.utilityChange != null) { - this.utilityChange.unregister(); - this.utilityChange = null; - } - - if (this.toolChange != null) { - this.toolChange.unregister(); - this.toolChange = null; - } - - this.unregisterBackpackChange(); - } - - private void unregisterBackpackChange() { - if (this.backpackChange != null) { - this.backpackChange.unregister(); - this.backpackChange = null; - } - } - - public void markChanged() { - this.isDirty.set(true); - this.needsSaving.set(true); } public void moveItem(int fromSectionId, int fromSlotId, int quantity, int toSectionId, int toSlotId) { @@ -355,7 +122,8 @@ public class Inventory implements NetworkSerializable { ItemContainer toContainer = this.getSectionById(toSectionId); if (toContainer != null) { if (this.entity instanceof Player - && (toSectionId == -1 && this.activeHotbarSlot == toSlotId || fromSectionId == -1 && this.activeHotbarSlot == fromSlotId)) { + && this.hotbar != null + && (toSectionId == -1 && this.hotbar.getActiveSlot() == toSlotId || fromSectionId == -1 && this.hotbar.getActiveSlot() == fromSlotId)) { ItemStack fromItem = fromContainer.getItemStack((short)fromSlotId); ItemStack currentItem = toContainer.getItemStack((short)toSlotId); if (ItemStack.isStackableWith(fromItem, currentItem) || ItemStack.isSameItemType(fromItem, currentItem)) { @@ -363,7 +131,7 @@ public class Inventory implements NetworkSerializable { return; } - int interactionSlot = toSectionId == -1 && this.activeHotbarSlot == toSlotId ? toSlotId : this.activeHotbarSlot; + int interactionSlot = toSectionId == -1 && this.hotbar.getActiveSlot() == toSlotId ? toSlotId : this.hotbar.getActiveSlot(); Ref ref = this.entity.getReference(); Store store = ref.getStore(); InteractionManager interactionManagerComponent = store.getComponent(ref, InteractionModule.get().getInteractionManagerComponent()); @@ -374,10 +142,32 @@ public class Inventory implements NetworkSerializable { InteractionContext context = InteractionContext.forInteraction(interactionManagerComponent, ref, InteractionType.SwapFrom, store); context.getMetaStore().putMetaObject(Interaction.TARGET_SLOT, interactionSlot); - context.getMetaStore().putMetaObject(ChangeActiveSlotInteraction.PLACE_MOVED_ITEM, () -> { - fromContainer.moveItemStackFromSlotToSlot((short)fromSlotId, quantity, toContainer, (short)toSlotId); - playerRefComponent.getPacketHandler().write(this.toPacket()); - }); + context.getMetaStore() + .putMetaObject( + ChangeActiveSlotInteraction.PLACE_MOVED_ITEM, + () -> { + fromContainer.moveItemStackFromSlotToSlot((short)fromSlotId, quantity, toContainer, (short)toSlotId); + if (ref.isValid()) { + InventoryComponent.Storage storage = store.getComponent(ref, InventoryComponent.Storage.getComponentType()); + InventoryComponent.Armor armor = store.getComponent(ref, InventoryComponent.Armor.getComponentType()); + InventoryComponent.Hotbar hotbar = store.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + InventoryComponent.Utility utility = store.getComponent(ref, InventoryComponent.Utility.getComponentType()); + InventoryComponent.Tool tool = store.getComponent(ref, InventoryComponent.Tool.getComponentType()); + InventoryComponent.Backpack backpack = store.getComponent(ref, InventoryComponent.Backpack.getComponentType()); + playerRefComponent.getPacketHandler() + .writeNoCache( + new UpdatePlayerInventory( + storage != null ? storage.getInventory().toPacket() : null, + armor != null ? armor.getInventory().toPacket() : null, + hotbar != null ? hotbar.getInventory().toPacket() : null, + utility != null ? utility.getInventory().toPacket() : null, + tool != null ? tool.getInventory().toPacket() : null, + backpack != null ? backpack.getInventory().toPacket() : null + ) + ); + } + } + ); String interactions = context.getRootInteractionId(InteractionType.SwapFrom); InteractionChainData data = new InteractionChainData(-1, UUIDUtil.EMPTY_UUID, null, null, null, -interactionSlot - 1, null); InteractionChain chain = interactionManagerComponent.initChain( @@ -393,7 +183,15 @@ public class Inventory implements NetworkSerializable { } } - public void smartMoveItem(int fromSectionId, int fromSlotId, int quantity, @Nonnull SmartMoveType moveType, PlayerSettings settings) { + public void smartMoveItem( + @Nonnull Ref ref, + int fromSectionId, + int fromSlotId, + int quantity, + @Nonnull SmartMoveType moveType, + PlayerSettings settings, + @Nonnull ComponentAccessor accessor + ) { ItemContainer targetContainer = this.getSectionById(fromSectionId); if (targetContainer != null) { ItemStack itemStack = targetContainer.getItemStack((short)fromSlotId); @@ -404,26 +202,27 @@ public class Inventory implements NetworkSerializable { return; } - if (this.entity instanceof Player) { - for (Window window : ((Player)this.entity).getWindowManager().getWindows()) { - if (window instanceof ItemContainerWindow) { - ((ItemContainerWindow)window).getItemContainer().combineItemStacksIntoSlot(targetContainer, (short)fromSlotId); + if (this.entity instanceof Player player) { + for (Window window : player.getWindowManager().getWindows()) { + if (window instanceof ItemContainerWindow itemContainerWindow) { + itemContainerWindow.getItemContainer().combineItemStacksIntoSlot(targetContainer, (short)fromSlotId); } } } - this.combinedEverything.combineItemStacksIntoSlot(targetContainer, (short)fromSlotId); + CombinedItemContainer everythingInventoryComponent = InventoryComponent.getCombined(accessor, ref, InventoryComponent.EVERYTHING); + everythingInventoryComponent.combineItemStacksIntoSlot(targetContainer, (short)fromSlotId); break; case PutInHotbarOrWindow: if (fromSectionId >= 0) { - this.moveItemFromCheckToInventory(itemStack, targetContainer, (short)fromSlotId, quantity, settings); + moveItemFromCheckToInventory(ref, itemStack, targetContainer, (short)fromSlotId, quantity, settings, accessor); return; } - if (this.entity instanceof Player) { - for (Window windowx : ((Player)this.entity).getWindowManager().getWindows()) { - if (windowx instanceof ItemContainerWindow) { - ItemContainer itemContainer = ((ItemContainerWindow)windowx).getItemContainer(); + if (this.entity instanceof Player player) { + for (Window windowx : player.getWindowManager().getWindows()) { + if (windowx instanceof ItemContainerWindow itemContainerWindow) { + ItemContainer itemContainer = itemContainerWindow.getItemContainer(); MoveTransaction transaction = targetContainer.moveItemStackFromSlot((short)fromSlotId, quantity, itemContainer); ItemStack remainder = transaction.getAddTransaction().getRemainder(); if (ItemStack.isEmpty(remainder) || remainder.getQuantity() != quantity) { @@ -439,21 +238,21 @@ public class Inventory implements NetworkSerializable { switch (fromSectionId) { case -2: - targetContainer.moveItemStackFromSlot((short)fromSlotId, quantity, this.hotbar); + targetContainer.moveItemStackFromSlot((short)fromSlotId, quantity, this.hotbar.getInventory()); return; case -1: - targetContainer.moveItemStackFromSlot((short)fromSlotId, quantity, this.storage); + targetContainer.moveItemStackFromSlot((short)fromSlotId, quantity, this.storage.getInventory()); return; default: - this.moveItemFromCheckToInventory(itemStack, targetContainer, (short)fromSlotId, quantity, settings); + moveItemFromCheckToInventory(ref, itemStack, targetContainer, (short)fromSlotId, quantity, settings, accessor); return; } case PutInHotbarOrBackpack: if (fromSectionId == -9) { - this.moveItemFromCheckToInventory(itemStack, targetContainer, (short)fromSlotId, quantity, settings); + moveItemFromCheckToInventory(ref, itemStack, targetContainer, (short)fromSlotId, quantity, settings, accessor); } else { targetContainer.moveItemStackFromSlot( - (short)fromSlotId, quantity, this.getContainerForItemPickup(itemStack.getItem(), settings, PickupLocation.Backpack) + (short)fromSlotId, quantity, getContainerForItemPickup(ref, itemStack.getItem(), settings, PickupLocation.Backpack, accessor) ); } } @@ -466,33 +265,43 @@ public class Inventory implements NetworkSerializable { ) { Item item = itemStack.getItem(); ItemArmor itemArmor = item.getArmor(); - if (itemArmor == null || fromSectionId == -3 || !forceEquip && this.armor.getItemStack((short)itemArmor.getArmorSlot().ordinal()) != null) { + if (itemArmor == null || fromSectionId == -3 || !forceEquip && this.armor.getInventory().getItemStack((short)itemArmor.getArmorSlot().ordinal()) != null) { return false; } else { - targetContainer.moveItemStackFromSlotToSlot(fromSlotId, quantity, this.armor, (short)itemArmor.getArmorSlot().ordinal()); + targetContainer.moveItemStackFromSlotToSlot(fromSlotId, quantity, this.armor.getInventory(), (short)itemArmor.getArmorSlot().ordinal()); return true; } } - private MoveTransaction moveItemFromCheckToInventory( - @Nonnull ItemStack itemStack, @Nonnull ItemContainer targetContainer, short fromSlotId, int quantity, PlayerSettings settings + private static MoveTransaction moveItemFromCheckToInventory( + @Nonnull Ref ref, + @Nonnull ItemStack itemStack, + @Nonnull ItemContainer targetContainer, + short fromSlotId, + int quantity, + PlayerSettings settings, + @Nonnull ComponentAccessor componentAccessor ) { - return targetContainer.moveItemStackFromSlot(fromSlotId, quantity, this.getContainerForItemPickup(itemStack.getItem(), settings)); + return targetContainer.moveItemStackFromSlot(fromSlotId, quantity, getContainerForItemPickup(ref, itemStack.getItem(), settings, componentAccessor)); } @Nullable - public ListTransaction> takeAll(int inventorySectionId, PlayerSettings settings) { + public ListTransaction> takeAll( + @Nonnull Ref ref, int inventorySectionId, PlayerSettings settings, @Nonnull ComponentAccessor componentAccessor + ) { ItemContainer container = this.getSectionById(inventorySectionId); - return container == null ? null : this.takeAllWithPriority(container, settings); + return container == null ? null : takeAllWithPriority(ref, container, settings, componentAccessor); } - public ListTransaction> takeAllWithPriority(ItemContainer fromContainer, PlayerSettings settings) { + public static ListTransaction> takeAllWithPriority( + @Nonnull Ref ref, ItemContainer fromContainer, PlayerSettings settings, @Nonnull ComponentAccessor componentAccessor + ) { List> transactions = new ObjectArrayList<>(); for (int slot = 0; slot < fromContainer.getCapacity(); slot++) { ItemStack stack = fromContainer.getItemStack((short)slot); if (!ItemStack.isEmpty(stack)) { - transactions.add(this.moveItemFromCheckToInventory(stack, fromContainer, (short)slot, stack.getQuantity(), settings)); + transactions.add(moveItemFromCheckToInventory(ref, stack, fromContainer, (short)slot, stack.getQuantity(), settings, componentAccessor)); } } @@ -502,125 +311,207 @@ public class Inventory implements NetworkSerializable { @Nullable public ListTransaction> putAll(int inventorySectionId) { ItemContainer sectionById = this.getSectionById(inventorySectionId); - return sectionById != null ? this.storage.moveAllItemStacksTo(sectionById) : null; + return sectionById != null ? this.storage.getInventory().moveAllItemStacksTo(sectionById) : null; } @Nullable - public ListTransaction> quickStack(int inventorySectionId) { + public ListTransaction> quickStack( + @Nonnull Ref ref, int inventorySectionId, @Nonnull ComponentAccessor componentAccessor + ) { ItemContainer sectionById = this.getSectionById(inventorySectionId); - return sectionById != null ? this.combinedHotbarFirst.quickStackTo(sectionById) : null; + return sectionById != null ? InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST).quickStackTo(sectionById) : null; } @Nonnull public List dropAllItemStacks() { List items = new ObjectArrayList<>(); - items.addAll(this.storage.dropAllItemStacks()); - items.addAll(this.armor.dropAllItemStacks()); - items.addAll(this.hotbar.dropAllItemStacks()); - items.addAll(this.utility.dropAllItemStacks()); - items.addAll(this.backpack.dropAllItemStacks()); + if (this.storage != null) { + items.addAll(this.storage.getInventory().dropAllItemStacks()); + } + + if (this.armor != null) { + items.addAll(this.armor.getInventory().dropAllItemStacks()); + } + + if (this.hotbar != null) { + items.addAll(this.hotbar.getInventory().dropAllItemStacks()); + } + + if (this.utility != null) { + items.addAll(this.utility.getInventory().dropAllItemStacks()); + } + + if (this.backpack != null) { + items.addAll(this.backpack.getInventory().dropAllItemStacks()); + } + return items; } public void clear() { - this.storage.clear(); - this.armor.clear(); - this.hotbar.clear(); - this.utility.clear(); - this.backpack.clear(); + if (this.storage != null) { + this.storage.getInventory().clear(); + } + + if (this.armor != null) { + this.armor.getInventory().clear(); + } + + if (this.hotbar != null) { + this.hotbar.getInventory().clear(); + } + + if (this.utility != null) { + this.utility.getInventory().clear(); + } + + if (this.backpack != null) { + this.backpack.getInventory().clear(); + } } + @Nullable public ItemContainer getStorage() { - return this.storage; + return this.storage != null ? this.storage.getInventory() : null; } + @Nullable public ItemContainer getArmor() { - return this.armor; + return this.armor != null ? this.armor.getInventory() : null; } + @Nullable public ItemContainer getHotbar() { - return this.hotbar; + return this.hotbar != null ? this.hotbar.getInventory() : null; } + @Nullable public ItemContainer getUtility() { - return this.utility; + return this.utility != null ? this.utility.getInventory() : null; } + @Nullable public ItemContainer getTools() { - return this.tools; + return this.tools != null ? this.tools.getInventory() : null; } + @Nullable public ItemContainer getBackpack() { - return this.backpack; - } - - public void resizeBackpack(short capacity, List remainder) { - if (capacity > 0) { - this.backpack = ItemContainer.ensureContainerCapacity(this.backpack, capacity, SimpleItemContainer::new, remainder); - } else { - this.backpack = ItemContainer.copy(this.backpack, EmptyItemContainer.INSTANCE, remainder); - } - - this.buildCombinedContains(); - if (this.entity != null) { - this.registerBackpackListener(); - } - - this.markChanged(); + return this.backpack != null ? this.backpack.getInventory() : null; } + @Deprecated(forRemoval = true) + @Nullable public CombinedItemContainer getCombinedHotbarFirst() { - return this.combinedHotbarFirst; + if (this.entity == null) { + return null; + } else { + Ref ref = this.entity.getReference(); + return ref != null && ref.isValid() ? InventoryComponent.getCombined(ref.getStore(), ref, InventoryComponent.HOTBAR_FIRST) : null; + } } + /** @deprecated */ + @Nullable public CombinedItemContainer getCombinedStorageFirst() { - return this.combinedStorageFirst; + if (this.entity == null) { + return null; + } else { + Ref ref = this.entity.getReference(); + return ref != null && ref.isValid() ? InventoryComponent.getCombined(ref.getStore(), ref, InventoryComponent.STORAGE_FIRST) : null; + } } + /** @deprecated */ + @Nullable public CombinedItemContainer getCombinedBackpackStorageHotbar() { - return this.combinedBackpackStorageHotbar; + if (this.entity == null) { + return null; + } else { + Ref ref = this.entity.getReference(); + return ref != null && ref.isValid() ? InventoryComponent.getCombined(ref.getStore(), ref, InventoryComponent.BACKPACK_STORAGE_HOTBAR) : null; + } } + /** @deprecated */ + @Nullable public CombinedItemContainer getCombinedBackpackStorageHotbarFirst() { - return this.combinedHotbarStorageBackpack; + if (this.entity == null) { + return null; + } else { + Ref ref = this.entity.getReference(); + return ref != null && ref.isValid() ? InventoryComponent.getCombined(ref.getStore(), ref, InventoryComponent.HOTBAR_STORAGE_BACKPACK) : null; + } } + /** @deprecated */ + @Nullable public CombinedItemContainer getCombinedArmorHotbarUtilityStorage() { - return this.combinedArmorHotbarUtilityStorage; + if (this.entity == null) { + return null; + } else { + Ref ref = this.entity.getReference(); + return ref != null && ref.isValid() ? InventoryComponent.getCombined(ref.getStore(), ref, InventoryComponent.ARMOR_HOTBAR_UTILITY_STORAGE) : null; + } } + /** @deprecated */ + @Nullable public CombinedItemContainer getCombinedHotbarUtilityConsumableStorage() { - return this.combinedHotbarUtilityConsumableStorage; + if (this.entity == null) { + return null; + } else { + Ref ref = this.entity.getReference(); + return ref != null && ref.isValid() ? InventoryComponent.getCombined(ref.getStore(), ref, InventoryComponent.HOTBAR_UTILITY_CONSUMABLE_STORAGE) : null; + } } - public CombinedItemContainer getCombinedEverything() { - return this.combinedEverything; + /** @deprecated */ + @Nullable + public CombinedItemContainer getCombinedStorageHotbarBackpack() { + if (this.entity == null) { + return null; + } else { + Ref ref = this.entity.getReference(); + return ref != null && ref.isValid() ? InventoryComponent.getCombined(ref.getStore(), ref, InventoryComponent.STORAGE_HOTBAR_BACKPACK) : null; + } } - private ItemContainer getItemContainerForPickupLocation(@Nonnull PickupLocation pickupLocation) { + @Nonnull + private static ItemContainer getItemContainerForPickupLocation( + @Nonnull PickupLocation pickupLocation, @Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor + ) { return switch (pickupLocation) { - case Hotbar -> this.combinedHotbarStorageBackpack; - case Storage -> this.combinedStorageHotbarBackpack; - case Backpack -> this.combinedBackpackStorageHotbar; - default -> this.combinedHotbarStorageBackpack; + case Hotbar -> InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_STORAGE_BACKPACK); + case Storage -> InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.STORAGE_HOTBAR_BACKPACK); + case Backpack -> InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.BACKPACK_STORAGE_HOTBAR); + default -> InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_STORAGE_BACKPACK); }; } @Nonnull - public ItemContainer getContainerForItemPickup(@Nonnull Item item, PlayerSettings playerSettings) { - return this.getContainerForItemPickup(item, playerSettings, null); + public static ItemContainer getContainerForItemPickup( + @Nonnull Ref ref, @Nonnull Item item, PlayerSettings playerSettings, @Nonnull ComponentAccessor componentAccessor + ) { + return getContainerForItemPickup(ref, item, playerSettings, null, componentAccessor); } @Nonnull - public ItemContainer getContainerForItemPickup(@Nonnull Item item, PlayerSettings playerSettings, @Nullable PickupLocation overridePickupLocation) { + public static ItemContainer getContainerForItemPickup( + @Nonnull Ref ref, + @Nonnull Item item, + PlayerSettings playerSettings, + @Nullable PickupLocation overridePickupLocation, + @Nonnull ComponentAccessor componentAccessor + ) { if (overridePickupLocation != null) { - return this.getItemContainerForPickupLocation(overridePickupLocation); + return getItemContainerForPickupLocation(overridePickupLocation, ref, componentAccessor); } else if (item.getArmor() != null) { - return this.getItemContainerForPickupLocation(playerSettings.armorItemsPreferredPickupLocation()); + return getItemContainerForPickupLocation(playerSettings.armorItemsPreferredPickupLocation(), ref, componentAccessor); } else if (item.getWeapon() != null || item.getTool() != null) { - return this.getItemContainerForPickupLocation(playerSettings.weaponAndToolItemsPreferredPickupLocation()); + return getItemContainerForPickupLocation(playerSettings.weaponAndToolItemsPreferredPickupLocation(), ref, componentAccessor); } else if (item.getUtility().isUsable()) { - return this.getItemContainerForPickupLocation(playerSettings.usableItemsItemsPreferredPickupLocation()); + return getItemContainerForPickupLocation(playerSettings.usableItemsItemsPreferredPickupLocation(), ref, componentAccessor); } else { BlockType blockType = item.hasBlockType() ? BlockType.getAssetMap().getAsset(item.getBlockId()) : BlockType.EMPTY; if (blockType == null) { @@ -628,19 +519,71 @@ public class Inventory implements NetworkSerializable { } return blockType.getMaterial() == BlockMaterial.Solid - ? this.getItemContainerForPickupLocation(playerSettings.solidBlockItemsPreferredPickupLocation()) - : this.getItemContainerForPickupLocation(playerSettings.miscItemsPreferredPickupLocation()); + ? getItemContainerForPickupLocation(playerSettings.solidBlockItemsPreferredPickupLocation(), ref, componentAccessor) + : getItemContainerForPickupLocation(playerSettings.miscItemsPreferredPickupLocation(), ref, componentAccessor); } } - public void setActiveSlot(int inventorySectionId, byte slot) { + public static void setActiveSlot(@Nonnull Ref ref, int inventorySectionId, byte slot, @Nonnull ComponentAccessor componentAccessor) { int[] entityStatsToClear = null; switch (inventorySectionId) { case -8: - this.activeToolsSlot = slot; + InventoryComponent.Tool toolsComponent = componentAccessor.getComponent(ref, InventoryComponent.Tool.getComponentType()); + if (toolsComponent != null) { + toolsComponent.setActiveSlot(slot); + } break; case -5: - this.activeUtilitySlot = slot; + InventoryComponent.Utility utilityComponent = componentAccessor.getComponent(ref, InventoryComponent.Utility.getComponentType()); + if (utilityComponent != null) { + utilityComponent.setActiveSlot(slot); + ItemStack itemStack = utilityComponent.getActiveItem(); + if (itemStack != null) { + ItemUtility utility = itemStack.getItem().getUtility(); + entityStatsToClear = utility.getEntityStatsToClear(); + } + } + break; + case -1: + InventoryComponent.Hotbar hotbarComponent = componentAccessor.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent != null) { + hotbarComponent.setActiveSlot(slot); + } + + ItemStack itemStack = InventoryComponent.getItemInHand(componentAccessor, ref); + if (itemStack != null) { + ItemWeapon weapon = itemStack.getItem().getWeapon(); + if (weapon != null) { + entityStatsToClear = weapon.getEntityStatsToClear(); + } + } + break; + default: + throw new IllegalArgumentException("Inventory section with id " + inventorySectionId + " cannot select an active slot"); + } + + if (EntityUtils.getEntity(ref, componentAccessor) instanceof LivingEntity livingEntity) { + livingEntity.invalidateEquipmentNetwork(); + } + + EntityStatMap entityStatMapComponent = componentAccessor.getComponent(ref, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + StatModifiersManager statModifiersManager = entityStatMapComponent.getStatModifiersManager(); + statModifiersManager.scheduleRecalculate(); + if (entityStatsToClear != null) { + statModifiersManager.queueEntityStatsToClear(entityStatsToClear); + } + } + } + + public void setActiveSlot(@Nonnull Holder holder, int inventorySectionId, byte slot) { + int[] entityStatsToClear = null; + switch (inventorySectionId) { + case -8: + this.tools.setActiveSlot(slot); + break; + case -5: + this.utility.setActiveSlot(slot); ItemStack itemStack = this.getUtilityItem(); if (itemStack != null) { ItemUtility utility = itemStack.getItem().getUtility(); @@ -648,7 +591,7 @@ public class Inventory implements NetworkSerializable { } break; case -1: - this.activeHotbarSlot = slot; + this.hotbar.setActiveSlot(slot); ItemStack itemStackx = this.getItemInHand(); if (itemStackx != null) { ItemWeapon weapon = itemStackx.getItem().getWeapon(); @@ -661,180 +604,165 @@ public class Inventory implements NetworkSerializable { throw new IllegalArgumentException("Inventory section with id " + inventorySectionId + " cannot select an active slot"); } - StatModifiersManager statModifiersManager = this.entity.getStatModifiersManager(); this.entity.invalidateEquipmentNetwork(); - statModifiersManager.setRecalculate(true); - if (entityStatsToClear != null) { - statModifiersManager.queueEntityStatsToClear(entityStatsToClear); + EntityStatMap entityStatMapComponent = holder.getComponent(EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + StatModifiersManager statModifiersManager = entityStatMapComponent.getStatModifiersManager(); + statModifiersManager.scheduleRecalculate(); + if (entityStatsToClear != null) { + statModifiersManager.queueEntityStatsToClear(entityStatsToClear); + } } } public byte getActiveSlot(int inventorySectionId) { return switch (inventorySectionId) { - case -8 -> this.activeToolsSlot; - case -5 -> this.activeUtilitySlot; - case -1 -> this.activeHotbarSlot; + case -8 -> this.tools.getActiveSlot(); + case -5 -> this.utility.getActiveSlot(); + case -1 -> this.hotbar.getActiveSlot(); default -> throw new IllegalArgumentException("Inventory section with id " + inventorySectionId + " cannot select an active slot"); }; } public byte getActiveHotbarSlot() { - return this.activeHotbarSlot; + return this.hotbar.getActiveSlot(); } - public void setActiveHotbarSlot(byte slot) { + public void setActiveHotbarSlot(@Nonnull Ref ref, byte slot, @Nonnull ComponentAccessor componentAccessor) { this.setUsingToolsItem(false); - this.setActiveSlot(-1, slot); + setActiveSlot(ref, -1, slot, componentAccessor); } @Nullable public ItemStack getActiveHotbarItem() { - return this.activeHotbarSlot == -1 ? null : this.hotbar.getItemStack(this.activeHotbarSlot); + return this.hotbar.getActiveItem(); } @Nullable public ItemStack getActiveToolItem() { - return this.activeToolsSlot == -1 ? null : this.tools.getItemStack(this.activeToolsSlot); + return this.tools.getActiveItem(); } @Nullable public ItemStack getItemInHand() { - return this._usingToolsItem ? this.getActiveToolItem() : this.getActiveHotbarItem(); + return this.tools != null && this.tools.usingToolsItem ? this.getActiveToolItem() : this.getActiveHotbarItem(); } public byte getActiveUtilitySlot() { - return this.activeUtilitySlot; + return this.utility.getActiveSlot(); } - public void setActiveUtilitySlot(byte slot) { - this.setActiveSlot(-5, slot); + public void setActiveUtilitySlot(@Nonnull Ref ref, byte slot, @Nonnull ComponentAccessor componentAccessor) { + setActiveSlot(ref, -5, slot, componentAccessor); + } + + public void setActiveUtilitySlot(@Nonnull Holder holder, byte slot) { + this.setActiveSlot(holder, -5, slot); } @Nullable public ItemStack getUtilityItem() { - return this.activeUtilitySlot == -1 ? null : this.utility.getItemStack(this.activeUtilitySlot); + return this.utility.getActiveItem(); } public byte getActiveToolsSlot() { - return this.activeToolsSlot; + return this.tools.getActiveSlot(); } - public void setActiveToolsSlot(byte slot) { + public void setActiveToolsSlot(@Nonnull Ref ref, byte slot, @Nonnull ComponentAccessor componentAccessor) { this.setUsingToolsItem(true); - this.setActiveSlot(-8, slot); + setActiveSlot(ref, -8, slot, componentAccessor); } @Nullable public ItemStack getToolsItem() { - return this.activeToolsSlot == -1 ? null : this.tools.getItemStack(this.activeToolsSlot); + return this.tools != null ? this.tools.getActiveItem() : null; } @Nullable public ItemContainer getSectionById(int id) { if (id >= 0) { - if (this.entity instanceof Player) { - Window window = ((Player)this.entity).getWindowManager().getWindow(id); - if (window instanceof ItemContainerWindow) { - return ((ItemContainerWindow)window).getItemContainer(); - } - } - - return null; + return this.entity instanceof Player player && player.getWindowManager().getWindow(id) instanceof ItemContainerWindow itemContainerWindow + ? itemContainerWindow.getItemContainer() + : null; } else { return switch (id) { - case -9 -> this.backpack; - case -8 -> this.tools; + case -9 -> this.backpack.getInventory(); + case -8 -> this.tools.getInventory(); default -> null; - case -5 -> this.utility; - case -3 -> this.armor; - case -2 -> this.storage; - case -1 -> this.hotbar; + case -5 -> this.utility.getInventory(); + case -3 -> this.armor.getInventory(); + case -2 -> this.storage.getInventory(); + case -1 -> this.hotbar.getInventory(); }; } } - public boolean consumeIsDirty() { - return this.isDirty.getAndSet(false); - } - - public boolean consumeNeedsSaving() { - return this.needsSaving.getAndSet(false); - } - public void setEntity(LivingEntity entity) { this.entity = entity; } - public void sortStorage(@Nonnull SortType type) { - this.sortType = type; - this.storage.sortItems(type); - this.markChanged(); + public void sortStorage() { + this.storage.getInventory().sortItems(SortType.TYPE); + this.storage.markChanged(); } - public void setSortType(SortType type) { - this.sortType = type; - this.markChanged(); - } + public static boolean containsBrokenItem(@Nonnull Ref ref, @Nonnull ComponentAccessor accessor) { + CombinedItemContainer everythingInventoryComponent = InventoryComponent.getCombined(accessor, ref, InventoryComponent.EVERYTHING); - public boolean containsBrokenItem() { - boolean hasBrokenItem = false; - - for (short i = 0; i < this.combinedEverything.getCapacity(); i++) { - ItemStack itemStack = this.combinedEverything.getItemStack(i); + for (short i = 0; i < everythingInventoryComponent.getCapacity(); i++) { + ItemStack itemStack = everythingInventoryComponent.getItemStack(i); if (!ItemStack.isEmpty(itemStack) && itemStack.isBroken()) { - hasBrokenItem = true; + return true; } } - return hasBrokenItem; + return false; } - @Nonnull - public UpdatePlayerInventory toPacket() { - UpdatePlayerInventory packet = new UpdatePlayerInventory(); - packet.storage = this.storage.toPacket(); - packet.armor = this.armor.toPacket(); - packet.hotbar = this.hotbar.toPacket(); - packet.utility = this.utility.toPacket(); - packet.tools = this.tools.toPacket(); - packet.backpack = this.backpack.toPacket(); - packet.sortType = this.sortType.toPacket(); - return packet; + public void migrateToComponents(Holder holder) { + if (this.deserializedStorage != null) { + holder.putComponent(InventoryComponent.Storage.getComponentType(), new InventoryComponent.Storage(this.deserializedStorage)); + this.deserializedStorage = null; + } + + if (this.deserializedArmor != null) { + holder.putComponent(InventoryComponent.Armor.getComponentType(), new InventoryComponent.Armor(this.deserializedArmor)); + this.deserializedArmor = null; + } + + if (this.deserializedHotbar != null) { + holder.putComponent( + InventoryComponent.Hotbar.getComponentType(), new InventoryComponent.Hotbar(this.deserializedHotbar, this.deserializedActiveHotbarSlot) + ); + this.deserializedHotbar = null; + } + + if (this.deserializedUtility != null) { + holder.putComponent( + InventoryComponent.Utility.getComponentType(), new InventoryComponent.Utility(this.deserializedUtility, this.deserializedActiveUtilitySlot) + ); + this.deserializedUtility = null; + } + + if (this.deserializedTools != null) { + holder.putComponent(InventoryComponent.Tool.getComponentType(), new InventoryComponent.Tool(this.deserializedTools, this.deserializedActiveToolsSlot)); + this.deserializedTools = null; + } + + if (this.deserializedBackpack != null) { + holder.putComponent(InventoryComponent.Backpack.getComponentType(), new InventoryComponent.Backpack(this.deserializedBackpack)); + this.deserializedBackpack = null; + } } - public void doMigration(Function blockMigration) { - Objects.requireNonNull(blockMigration); - this.hotbar.doMigration(blockMigration); - this.storage.doMigration(blockMigration); - this.armor.doMigration(blockMigration); - this.utility.doMigration(blockMigration); - this.tools.doMigration(blockMigration); - this.backpack.doMigration(blockMigration); - } - - private void postDecode() { - this.armor = ItemContainerUtil.trySetArmorFilters(this.armor); - this.utility = ItemContainerUtil.trySetSlotFilters( - this.utility, (type, container, slot, itemStack) -> itemStack == null || itemStack.getItem().getUtility().isUsable() - ); - this.activeHotbarSlot = (byte)(this.activeHotbarSlot < this.hotbar.getCapacity() ? this.activeHotbarSlot : (this.hotbar.getCapacity() > 0 ? 0 : -1)); - this.activeUtilitySlot = this.activeUtilitySlot < this.utility.getCapacity() ? this.activeUtilitySlot : -1; - this.activeToolsSlot = this.activeToolsSlot < this.tools.getCapacity() ? this.activeToolsSlot : -1; - this.buildCombinedContains(); - this.registerChangeEvents(); - } - - private void buildCombinedContains() { - this.combinedHotbarFirst = new CombinedItemContainer(this.hotbar, this.storage); - this.combinedStorageFirst = new CombinedItemContainer(this.storage, this.hotbar); - this.combinedBackpackStorageHotbar = new CombinedItemContainer(this.backpack, this.storage, this.hotbar); - this.combinedBackpackHotbarStorage = new CombinedItemContainer(this.backpack, this.hotbar, this.storage); - this.combinedStorageHotbarBackpack = new CombinedItemContainer(this.storage, this.hotbar, this.backpack); - this.combinedHotbarStorageBackpack = new CombinedItemContainer(this.hotbar, this.storage, this.backpack); - this.combinedArmorHotbarStorage = new CombinedItemContainer(this.armor, this.hotbar, this.storage); - this.combinedArmorHotbarUtilityStorage = new CombinedItemContainer(this.armor, this.hotbar, this.utility, this.storage); - this.combinedHotbarUtilityConsumableStorage = new CombinedItemContainer(this.hotbar, this.utility, this.storage); - this.combinedEverything = new CombinedItemContainer(this.armor, this.hotbar, this.utility, this.storage, this.backpack); + public void backwardsCompatHook(Holder holder) { + this.storage = holder.getComponent(InventoryComponent.Storage.getComponentType()); + this.armor = holder.getComponent(InventoryComponent.Armor.getComponentType()); + this.hotbar = holder.getComponent(InventoryComponent.Hotbar.getComponentType()); + this.utility = holder.getComponent(InventoryComponent.Utility.getComponentType()); + this.tools = holder.getComponent(InventoryComponent.Tool.getComponentType()); + this.backpack = holder.getComponent(InventoryComponent.Backpack.getComponentType()); } @Override @@ -843,15 +771,7 @@ public class Inventory implements NetworkSerializable { return true; } else if (o != null && this.getClass() == o.getClass()) { Inventory inventory = (Inventory)o; - if (this.activeHotbarSlot != inventory.activeHotbarSlot) { - return false; - } else if (this.activeUtilitySlot != inventory.activeUtilitySlot) { - return false; - } else if (this.isDirty != inventory.isDirty) { - return false; - } else if (this.needsSaving != inventory.needsSaving) { - return false; - } else if (!Objects.equals(this.storage, inventory.storage)) { + if (!Objects.equals(this.storage, inventory.storage)) { return false; } else if (!Objects.equals(this.armor, inventory.armor)) { return false; @@ -874,88 +794,23 @@ public class Inventory implements NetworkSerializable { result = 31 * result + (this.hotbar != null ? this.hotbar.hashCode() : 0); result = 31 * result + (this.utility != null ? this.utility.hashCode() : 0); result = 31 * result + (this.tools != null ? this.tools.hashCode() : 0); - result = 31 * result + (this.backpack != null ? this.backpack.hashCode() : 0); - result = 31 * result + this.activeHotbarSlot; - result = 31 * result + this.activeUtilitySlot; - result = 31 * result + this.activeToolsSlot; - result = 31 * result + this.isDirty.hashCode(); - return 31 * result + this.needsSaving.hashCode(); + return 31 * result + (this.backpack != null ? this.backpack.hashCode() : 0); } @Nonnull @Override public String toString() { - return "Inventory{, storage=" - + this.storage - + ", armor=" - + this.armor - + ", hotbar=" - + this.hotbar - + ", utility=" - + this.utility - + ", activeHotbarSlot=" - + this.activeHotbarSlot - + ", activeUtilitySlot=" - + this.activeUtilitySlot - + ", activeToolsSlot=" - + this.activeToolsSlot - + ", isDirty=" - + this.isDirty - + ", needsSaving=" - + this.needsSaving - + "}"; - } - - @Nonnull - public static Inventory ensureCapacity(@Nonnull Inventory inventory, List remainder) { - ItemContainer storage = inventory.getStorage(); - ItemContainer armor = inventory.getArmor(); - ItemContainer hotbar = inventory.getHotbar(); - ItemContainer utility = inventory.getUtility(); - ItemContainer tool = inventory.getTools(); - if (storage.getCapacity() == 36 - && armor.getCapacity() == DEFAULT_ARMOR_CAPACITY - && hotbar.getCapacity() == 9 - && utility.getCapacity() == 4 - && tool.getCapacity() == 23) { - return inventory; - } else { - ItemContainer newStorage = ItemContainer.ensureContainerCapacity(storage, (short)36, SimpleItemContainer::new, remainder); - ItemContainer newArmor = ItemContainer.ensureContainerCapacity(armor, DEFAULT_ARMOR_CAPACITY, SimpleItemContainer::new, remainder); - ItemContainer newHotbar = ItemContainer.ensureContainerCapacity(hotbar, (short)9, SimpleItemContainer::new, remainder); - ItemContainer newUtility = ItemContainer.ensureContainerCapacity(utility, (short)4, SimpleItemContainer::new, remainder); - ItemContainer newTool = ItemContainer.ensureContainerCapacity(tool, (short)23, SimpleItemContainer::new, remainder); - byte activeHotbarSlot = inventory.getActiveHotbarSlot(); - if (activeHotbarSlot > newHotbar.getCapacity()) { - activeHotbarSlot = (byte)(hotbar.getCapacity() > 0 ? 0 : -1); - } - - byte activeUtilitySlot = inventory.getActiveUtilitySlot(); - if (activeUtilitySlot > newUtility.getCapacity()) { - activeUtilitySlot = -1; - } - - byte activeToolsSlot = inventory.getActiveToolsSlot(); - if (activeToolsSlot > newTool.getCapacity()) { - activeToolsSlot = -1; - } - - inventory.unregister(); - Inventory newInventory = new Inventory(newStorage, newArmor, newHotbar, newUtility, newTool, EmptyItemContainer.INSTANCE); - newInventory.activeHotbarSlot = activeHotbarSlot; - newInventory.activeUtilitySlot = activeUtilitySlot; - newInventory.activeToolsSlot = activeToolsSlot; - newInventory.setSortType(inventory.sortType); - return newInventory; - } + return "Inventory{, storage=" + this.storage + ", armor=" + this.armor + ", hotbar=" + this.hotbar + ", utility=" + this.utility + "}"; } public void setUsingToolsItem(boolean value) { - this._usingToolsItem = value; + if (this.tools != null) { + this.tools.setUsingToolsItem(value); + } } public boolean usingToolsItem() { - return this._usingToolsItem; + return this.tools != null && this.tools.isUsingToolsItem(); } public static enum ItemPickupType { diff --git a/src/com/hypixel/hytale/server/core/inventory/InventoryChangeEvent.java b/src/com/hypixel/hytale/server/core/inventory/InventoryChangeEvent.java new file mode 100644 index 00000000..53bcf52b --- /dev/null +++ b/src/com/hypixel/hytale/server/core/inventory/InventoryChangeEvent.java @@ -0,0 +1,42 @@ +package com.hypixel.hytale.server.core.inventory; + +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.system.EcsEvent; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.inventory.transaction.Transaction; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; + +public class InventoryChangeEvent extends EcsEvent { + private final ComponentType componentType; + private final InventoryComponent inventory; + private final ItemContainer itemContainer; + private final Transaction transaction; + + public InventoryChangeEvent( + ComponentType componentType, + InventoryComponent inventory, + ItemContainer itemContainer, + Transaction transaction + ) { + this.componentType = componentType; + this.inventory = inventory; + this.itemContainer = itemContainer; + this.transaction = transaction; + } + + public ComponentType getComponentType() { + return this.componentType; + } + + public InventoryComponent getInventory() { + return this.inventory; + } + + public ItemContainer getItemContainer() { + return this.itemContainer; + } + + public Transaction getTransaction() { + return this.transaction; + } +} diff --git a/src/com/hypixel/hytale/server/core/inventory/InventoryComponent.java b/src/com/hypixel/hytale/server/core/inventory/InventoryComponent.java new file mode 100644 index 00000000..68ee19bd --- /dev/null +++ b/src/com/hypixel/hytale/server/core/inventory/InventoryComponent.java @@ -0,0 +1,637 @@ +package com.hypixel.hytale.server.core.inventory; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.Archetype; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Component; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.event.EventRegistration; +import com.hypixel.hytale.protocol.ItemArmorSlot; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; +import com.hypixel.hytale.server.core.inventory.container.EmptyItemContainer; +import com.hypixel.hytale.server.core.inventory.container.FetchedItemContainer; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.inventory.container.ItemContainerUtil; +import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; +import com.hypixel.hytale.server.core.modules.entity.EntityModule; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import it.unimi.dsi.fastutil.Hash.Strategy; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class InventoryComponent implements Component { + public static final byte INACTIVE_SLOT_INDEX = -1; + public static final short DEFAULT_HOTBAR_CAPACITY = 9; + public static final short DEFAULT_UTILITY_CAPACITY = 4; + public static final short DEFAULT_TOOLS_CAPACITY = 23; + public static final short DEFAULT_ARMOR_CAPACITY = (short)ItemArmorSlot.VALUES.length; + public static final short DEFAULT_STORAGE_ROWS = 4; + public static final short DEFAULT_STORAGE_COLUMNS = 9; + public static final short DEFAULT_STORAGE_CAPACITY = 36; + public static final int HOTBAR_SECTION_ID = -1; + public static final int STORAGE_SECTION_ID = -2; + public static final int ARMOR_SECTION_ID = -3; + public static final int UTILITY_SECTION_ID = -5; + public static final int TOOLS_SECTION_ID = -8; + public static final int BACKPACK_SECTION_ID = -9; + public static final BuilderCodec CODEC = BuilderCodec.abstractBuilder(InventoryComponent.class) + .append(new KeyedCodec<>("Inventory", ItemContainer.CODEC), (o, i) -> o.inventory = i, o -> o.inventory) + .add() + .afterDecode(InventoryComponent::postDecode) + .build(); + protected final AtomicBoolean isDirty = new AtomicBoolean(); + protected final AtomicBoolean needsSaving = new AtomicBoolean(); + protected ItemContainer inventory = EmptyItemContainer.INSTANCE; + @Nullable + protected EventRegistration changeEvent = null; + protected ConcurrentLinkedQueue changeEvents = new ConcurrentLinkedQueue<>(); + public static ComponentType[] HOTBAR_STORAGE_BACKPACK; + public static ComponentType[] HOTBAR_FIRST; + public static ComponentType[] STORAGE_FIRST; + public static ComponentType[] BACKPACK_STORAGE_HOTBAR; + public static ComponentType[] BACKPACK_HOTBAR_STORAGE; + public static ComponentType[] STORAGE_HOTBAR_BACKPACK; + public static ComponentType[] ARMOR_HOTBAR_UTILITY_STORAGE; + public static ComponentType[] HOTBAR_UTILITY_CONSUMABLE_STORAGE; + public static ComponentType[] EVERYTHING; + + public InventoryComponent() { + } + + public InventoryComponent(short capacity) { + this.inventory = (ItemContainer)(capacity == 0 ? EmptyItemContainer.INSTANCE : new SimpleItemContainer(capacity)); + this.registerChangeEvent(); + } + + public void ensureCapacity(short capacity, @Nonnull List remainder) { + if (this.inventory.getCapacity() != capacity) { + this.unregisterChangeEvent(); + this.inventory = ItemContainer.ensureContainerCapacity(this.inventory, capacity, SimpleItemContainer::new, remainder); + this.registerChangeEvent(); + } + } + + protected void registerChangeEvent() { + if (this.inventory != EmptyItemContainer.INSTANCE) { + this.changeEvent = this.inventory.registerChangeEvent(itemContainerChangeEvent -> { + this.markChanged(); + this.changeEvents.add(itemContainerChangeEvent); + }); + } + } + + protected void unregisterChangeEvent() { + if (this.changeEvent != null) { + this.changeEvent.unregister(); + this.changeEvent = null; + } + } + + protected void markChanged() { + this.isDirty.set(true); + this.needsSaving.set(true); + } + + public void markDirty() { + this.isDirty.set(true); + } + + public boolean consumeIsDirty() { + return this.isDirty.getAndSet(false); + } + + public boolean consumeNeedsSaving() { + return this.needsSaving.getAndSet(false); + } + + public ItemContainer getInventory() { + return this.inventory; + } + + private void postDecode() { + this.registerChangeEvent(); + } + + public ConcurrentLinkedQueue getChangeEvents() { + return this.changeEvents; + } + + @Nullable + @Override + public abstract Component clone(); + + public static void setupCombined( + ComponentType storageInventoryComponentType, + ComponentType armorInventoryComponentType, + ComponentType hotbarInventoryComponentType, + ComponentType utilityInventoryComponentType, + ComponentType backpackInventoryComponentType, + ComponentType toolInventoryComponentType + ) { + HOTBAR_STORAGE_BACKPACK = new ComponentType[]{hotbarInventoryComponentType, storageInventoryComponentType, backpackInventoryComponentType}; + HOTBAR_FIRST = new ComponentType[]{hotbarInventoryComponentType, storageInventoryComponentType}; + STORAGE_FIRST = new ComponentType[]{storageInventoryComponentType, hotbarInventoryComponentType}; + BACKPACK_STORAGE_HOTBAR = new ComponentType[]{backpackInventoryComponentType, storageInventoryComponentType, hotbarInventoryComponentType}; + BACKPACK_HOTBAR_STORAGE = new ComponentType[]{backpackInventoryComponentType, hotbarInventoryComponentType, storageInventoryComponentType}; + STORAGE_HOTBAR_BACKPACK = new ComponentType[]{storageInventoryComponentType, hotbarInventoryComponentType, backpackInventoryComponentType}; + ARMOR_HOTBAR_UTILITY_STORAGE = new ComponentType[]{ + armorInventoryComponentType, hotbarInventoryComponentType, utilityInventoryComponentType, storageInventoryComponentType + }; + HOTBAR_UTILITY_CONSUMABLE_STORAGE = new ComponentType[]{hotbarInventoryComponentType, utilityInventoryComponentType, storageInventoryComponentType}; + EVERYTHING = new ComponentType[]{ + armorInventoryComponentType, + hotbarInventoryComponentType, + utilityInventoryComponentType, + storageInventoryComponentType, + backpackInventoryComponentType + }; + } + + @Nullable + public static ComponentType getComponentTypeById(int id) { + if (id >= 0) { + return null; + } else { + return switch (id) { + case -9 -> InventoryComponent.Backpack.getComponentType(); + case -8 -> InventoryComponent.Tool.getComponentType(); + default -> null; + case -5 -> InventoryComponent.Utility.getComponentType(); + case -3 -> InventoryComponent.Armor.getComponentType(); + case -2 -> InventoryComponent.Storage.getComponentType(); + case -1 -> InventoryComponent.Hotbar.getComponentType(); + }; + } + } + + @Nonnull + @SafeVarargs + public static CombinedItemContainer getCombined( + @Nonnull ComponentAccessor accessor, + @Nonnull Ref ref, + @Nonnull ComponentType... types + ) { + InventoryComponent.Combined combined = accessor.getComponent(ref, InventoryComponent.Combined.getComponentType()); + if (combined == null) { + combined = new InventoryComponent.Combined(); + if (accessor instanceof Store store) { + if (store.isProcessing()) { + store.getExternalData().getWorld().execute(() -> { + if (ref.isValid()) { + store.putComponent(ref, InventoryComponent.Combined.getComponentType(), combined); + } + }); + } else { + accessor.putComponent(ref, InventoryComponent.Combined.getComponentType(), combined); + } + } else { + accessor.putComponent(ref, InventoryComponent.Combined.getComponentType(), combined); + } + } + + CombinedItemContainer inv = combined.inventories.get(types); + if (inv != null) { + return inv; + } else { + int count = 0; + Archetype archetype = accessor.getArchetype(ref); + + for (ComponentType type : types) { + if (archetype.contains(type)) { + count++; + } + } + + ItemContainer[] containers = new ItemContainer[count]; + int i = 0; + + for (ComponentType typex : types) { + InventoryComponent innerInv = accessor.getComponent(ref, typex); + if (innerInv != null) { + containers[i++] = new FetchedItemContainer(innerInv::getInventory); + } + } + + inv = new CombinedItemContainer(containers); + combined.inventories.put(types, inv); + return inv; + } + } + + @Nonnull + @SafeVarargs + public static CombinedItemContainer getCombined( + @Nonnull CommandBuffer commandBuffer, + @Nonnull ArchetypeChunk archetypeChunk, + int index, + @Nonnull ComponentType... types + ) { + InventoryComponent.Combined combined = archetypeChunk.getComponent(index, InventoryComponent.Combined.getComponentType()); + if (combined == null) { + combined = new InventoryComponent.Combined(); + commandBuffer.putComponent(archetypeChunk.getReferenceTo(index), InventoryComponent.Combined.getComponentType(), combined); + } + + CombinedItemContainer inv = combined.inventories.get(types); + if (inv != null) { + return inv; + } else { + int count = 0; + Archetype archetype = archetypeChunk.getArchetype(); + + for (ComponentType type : types) { + if (archetype.contains(type)) { + count++; + } + } + + ItemContainer[] containers = new ItemContainer[count]; + int i = 0; + + for (ComponentType typex : types) { + InventoryComponent innerInv = archetypeChunk.getComponent(index, typex); + if (innerInv != null) { + containers[i++] = new FetchedItemContainer(innerInv::getInventory); + } + } + + inv = new CombinedItemContainer(containers); + combined.inventories.put(types, inv); + return inv; + } + } + + @Nullable + public static ItemStack getItemInHand(@Nonnull ComponentAccessor accessor, @Nonnull Ref ref) { + InventoryComponent.Tool toolComponent = accessor.getComponent(ref, InventoryComponent.Tool.getComponentType()); + if (toolComponent != null && toolComponent.isUsingToolsItem()) { + return toolComponent.getActiveItem(); + } else { + InventoryComponent.Hotbar hotbarComponent = accessor.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + return hotbarComponent != null ? hotbarComponent.getActiveItem() : null; + } + } + + public static class Armor extends InventoryComponent { + public static final BuilderCodec CODEC = BuilderCodec.builder( + InventoryComponent.Armor.class, InventoryComponent.Armor::new, InventoryComponent.CODEC + ) + .afterDecode(InventoryComponent.Armor::afterDecode) + .build(); + + public static ComponentType getComponentType() { + return EntityModule.get().getArmorInventoryComponentType(); + } + + public Armor() { + } + + public Armor(short capacity) { + super(capacity); + this.afterDecode(); + } + + public Armor(ItemContainer armor) { + this.inventory = armor; + this.registerChangeEvent(); + this.afterDecode(); + } + + private void afterDecode() { + this.inventory = ItemContainerUtil.trySetArmorFilters(this.inventory); + } + + @Override + public void ensureCapacity(short capacity, @Nonnull List remainder) { + super.ensureCapacity(capacity, remainder); + this.inventory = ItemContainerUtil.trySetArmorFilters(this.inventory); + } + + @Nullable + @Override + public Component clone() { + InventoryComponent.Armor armor = new InventoryComponent.Armor(); + armor.inventory = this.inventory.clone(); + return armor; + } + } + + public static class Backpack extends InventoryComponent { + public static final BuilderCodec CODEC = BuilderCodec.builder( + InventoryComponent.Backpack.class, InventoryComponent.Backpack::new, InventoryComponent.CODEC + ) + .build(); + + public static ComponentType getComponentType() { + return EntityModule.get().getBackpackInventoryComponentType(); + } + + public Backpack() { + } + + public Backpack(short capacity) { + super(capacity); + } + + public Backpack(ItemContainer backpack) { + this.inventory = backpack; + this.registerChangeEvent(); + } + + public void resize(short capacity, @Nullable List remainder) { + this.unregisterChangeEvent(); + if (capacity > 0) { + this.inventory = ItemContainer.ensureContainerCapacity(this.inventory, capacity, SimpleItemContainer::new, remainder); + } else { + this.inventory = ItemContainer.copy(this.inventory, EmptyItemContainer.INSTANCE, remainder); + } + + this.registerChangeEvent(); + this.markChanged(); + } + + @Nullable + @Override + public Component clone() { + InventoryComponent.Backpack backpack = new InventoryComponent.Backpack(); + backpack.inventory = this.inventory.clone(); + return backpack; + } + } + + public static class Combined implements Component { + private final Object2ObjectOpenCustomHashMap inventories = new Object2ObjectOpenCustomHashMap<>( + new Strategy() { + { + Objects.requireNonNull(Combined.this); + } + + public int hashCode(ComponentType[] o) { + return Arrays.hashCode((Object[])o); + } + + public boolean equals(ComponentType[] a, ComponentType[] b) { + return Arrays.equals((Object[])a, (Object[])b); + } + } + ); + + public static ComponentType getComponentType() { + return EntityModule.get().getCombinedInventoryComponentType(); + } + + @Nullable + @Override + public Component clone() { + return new InventoryComponent.Combined(); + } + } + + public static class Hotbar extends InventoryComponent { + public static final BuilderCodec CODEC = BuilderCodec.builder( + InventoryComponent.Hotbar.class, InventoryComponent.Hotbar::new, InventoryComponent.CODEC + ) + .append(new KeyedCodec<>("ActiveSlot", Codec.BYTE), (o, i) -> o.activeSlot = i, o -> o.activeSlot) + .add() + .afterDecode(InventoryComponent.Hotbar::afterDecode) + .build(); + protected byte activeSlot; + + public static ComponentType getComponentType() { + return EntityModule.get().getHotbarInventoryComponentType(); + } + + public Hotbar() { + } + + public Hotbar(short capacity) { + super(capacity); + } + + public Hotbar(ItemContainer hotbar, byte activeHotbarSlot) { + this.inventory = hotbar; + this.activeSlot = activeHotbarSlot; + this.registerChangeEvent(); + } + + @Override + public void ensureCapacity(short capacity, @Nonnull List remainder) { + super.ensureCapacity(capacity, remainder); + if (this.activeSlot >= this.inventory.getCapacity()) { + this.activeSlot = (byte)(this.inventory.getCapacity() > 0 ? 0 : -1); + } + } + + private void afterDecode() { + this.activeSlot = (byte)(this.activeSlot < this.inventory.getCapacity() ? this.activeSlot : (this.inventory.getCapacity() > 0 ? 0 : -1)); + } + + public byte getActiveSlot() { + return this.activeSlot; + } + + public void setActiveSlot(byte activeSlot) { + this.activeSlot = activeSlot; + } + + @Nullable + public ItemStack getActiveItem() { + return this.activeSlot != -1 && this.activeSlot < this.inventory.getCapacity() ? this.inventory.getItemStack(this.activeSlot) : null; + } + + @Nullable + @Override + public Component clone() { + InventoryComponent.Hotbar hotbar = new InventoryComponent.Hotbar(); + hotbar.inventory = this.inventory.clone(); + hotbar.activeSlot = this.activeSlot; + return hotbar; + } + } + + public static class Storage extends InventoryComponent { + public static final BuilderCodec CODEC = BuilderCodec.builder( + InventoryComponent.Storage.class, InventoryComponent.Storage::new, InventoryComponent.CODEC + ) + .build(); + + public static ComponentType getComponentType() { + return EntityModule.get().getStorageInventoryComponentType(); + } + + public Storage() { + } + + public Storage(short capacity) { + super(capacity); + } + + public Storage(ItemContainer storage) { + this.inventory = storage; + this.registerChangeEvent(); + } + + @Nullable + @Override + public Component clone() { + InventoryComponent.Storage storage = new InventoryComponent.Storage(); + storage.inventory = this.inventory.clone(); + return storage; + } + } + + public static class Tool extends InventoryComponent { + public static final BuilderCodec CODEC = BuilderCodec.builder( + InventoryComponent.Tool.class, InventoryComponent.Tool::new, InventoryComponent.CODEC + ) + .append(new KeyedCodec<>("ActiveSlot", Codec.BYTE), (o, i) -> o.activeSlot = i, o -> o.activeSlot) + .add() + .afterDecode(InventoryComponent.Tool::afterDecode) + .build(); + protected byte activeSlot = -1; + protected boolean usingToolsItem = false; + + public static ComponentType getComponentType() { + return EntityModule.get().getToolInventoryComponentType(); + } + + public Tool() { + } + + public Tool(short capacity) { + super(capacity); + } + + public Tool(ItemContainer tools, byte toolsSlot) { + this.inventory = tools; + this.activeSlot = toolsSlot; + this.registerChangeEvent(); + } + + @Override + public void ensureCapacity(short capacity, @Nonnull List remainder) { + super.ensureCapacity(capacity, remainder); + if (this.activeSlot >= this.inventory.getCapacity()) { + this.activeSlot = -1; + } + } + + private void afterDecode() { + this.activeSlot = this.activeSlot < this.inventory.getCapacity() ? this.activeSlot : -1; + } + + public byte getActiveSlot() { + return this.activeSlot; + } + + public void setActiveSlot(byte activeSlot) { + this.activeSlot = activeSlot; + } + + @Nullable + public ItemStack getActiveItem() { + return this.activeSlot != -1 && this.activeSlot < this.inventory.getCapacity() ? this.inventory.getItemStack(this.activeSlot) : null; + } + + public boolean isUsingToolsItem() { + return this.usingToolsItem; + } + + public void setUsingToolsItem(boolean usingToolsItem) { + this.usingToolsItem = usingToolsItem; + } + + @Nullable + @Override + public Component clone() { + InventoryComponent.Tool tool = new InventoryComponent.Tool(); + tool.inventory = this.inventory.clone(); + tool.activeSlot = this.activeSlot; + tool.usingToolsItem = this.usingToolsItem; + return tool; + } + } + + public static class Utility extends InventoryComponent { + public static final BuilderCodec CODEC = BuilderCodec.builder( + InventoryComponent.Utility.class, InventoryComponent.Utility::new, InventoryComponent.CODEC + ) + .append(new KeyedCodec<>("ActiveSlot", Codec.BYTE), (o, i) -> o.activeSlot = i, o -> o.activeSlot) + .add() + .afterDecode(InventoryComponent.Utility::afterDecode) + .build(); + protected byte activeSlot = -1; + + public static ComponentType getComponentType() { + return EntityModule.get().getUtilityInventoryComponentType(); + } + + public Utility() { + } + + public Utility(short capacity) { + super(capacity); + this.afterDecode(); + } + + public Utility(ItemContainer utility, byte utilitySlot) { + this.inventory = utility; + this.activeSlot = utilitySlot; + this.registerChangeEvent(); + this.afterDecode(); + } + + @Override + public void ensureCapacity(short capacity, @Nonnull List remainder) { + super.ensureCapacity(capacity, remainder); + if (this.activeSlot >= this.inventory.getCapacity()) { + this.activeSlot = -1; + } + + this.inventory = ItemContainerUtil.trySetSlotFilters( + this.inventory, (type, container, slot, itemStack) -> itemStack == null || itemStack.getItem().getUtility().isUsable() + ); + } + + private void afterDecode() { + this.inventory = ItemContainerUtil.trySetSlotFilters( + this.inventory, (type, container, slot, itemStack) -> itemStack == null || itemStack.getItem().getUtility().isUsable() + ); + this.activeSlot = this.activeSlot < this.inventory.getCapacity() ? this.activeSlot : -1; + } + + public byte getActiveSlot() { + return this.activeSlot; + } + + public void setActiveSlot(byte activeSlot) { + this.activeSlot = activeSlot; + } + + @Nullable + public ItemStack getActiveItem() { + return this.activeSlot != -1 && this.activeSlot < this.inventory.getCapacity() ? this.inventory.getItemStack(this.activeSlot) : null; + } + + @Nullable + @Override + public Component clone() { + InventoryComponent.Utility utility = new InventoryComponent.Utility(); + utility.inventory = this.inventory.clone(); + utility.activeSlot = this.activeSlot; + return utility; + } + } +} diff --git a/src/com/hypixel/hytale/server/core/inventory/InventorySystems.java b/src/com/hypixel/hytale/server/core/inventory/InventorySystems.java new file mode 100644 index 00000000..64a36d43 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/inventory/InventorySystems.java @@ -0,0 +1,337 @@ +package com.hypixel.hytale.server.core.inventory; + +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.dependency.Dependency; +import com.hypixel.hytale.component.dependency.Order; +import com.hypixel.hytale.component.dependency.SystemDependency; +import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.component.system.tick.EntityTickingSystem; +import com.hypixel.hytale.protocol.GameMode; +import com.hypixel.hytale.server.core.asset.type.item.config.ItemUtility; +import com.hypixel.hytale.server.core.asset.type.item.config.ItemWeapon; +import com.hypixel.hytale.server.core.entity.EntityUtils; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.entity.StatModifiersManager; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.entities.player.HotbarManager; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.inventory.transaction.SlotTransaction; +import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery; +import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class InventorySystems { + public static class ArmorChangeEventSystem extends InventorySystems.InventoryChangeEventSystem { + public ArmorChangeEventSystem() { + super(InventoryComponent.Armor.getComponentType()); + } + } + + public static class BackpackChangeEventSystem extends InventorySystems.InventoryChangeEventSystem { + public BackpackChangeEventSystem() { + super(InventoryComponent.Backpack.getComponentType()); + } + } + + public static class HotbarChangeEventSystem extends InventorySystems.InventoryChangeEventSystem { + public HotbarChangeEventSystem() { + super(InventoryComponent.Hotbar.getComponentType()); + } + } + + public abstract static class InventoryChangeEventSystem extends EntityTickingSystem { + protected final ComponentType componentType; + + protected InventoryChangeEventSystem(ComponentType componentType) { + this.componentType = componentType; + } + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + Inv inventory = archetypeChunk.getComponent(index, this.componentType); + + assert inventory != null; + + ConcurrentLinkedQueue changeEvents = inventory.getChangeEvents(); + ItemContainer.ItemContainerChangeEvent changeEvent = null; + Ref ref = archetypeChunk.getReferenceTo(index); + + while ((changeEvent = changeEvents.poll()) != null) { + InventoryChangeEvent event = new InventoryChangeEvent(this.componentType, inventory, changeEvent.container(), changeEvent.transaction()); + commandBuffer.invoke(ref, event); + } + } + + @Nullable + @Override + public Query getQuery() { + return this.componentType; + } + } + + @Deprecated(forRemoval = true) + public static class LegacyArmorChangeStatSystem extends EntityTickingSystem { + private final Query query = Query.and(InventoryComponent.Armor.getComponentType(), AllLegacyLivingEntityTypesQuery.INSTANCE); + private final Set> dependencies = Set.of(new SystemDependency<>(Order.BEFORE, InventorySystems.ArmorChangeEventSystem.class)); + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + LivingEntity entity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk); + + assert entity != null; + + InventoryComponent.Armor inventory = archetypeChunk.getComponent(index, InventoryComponent.Armor.getComponentType()); + + assert inventory != null; + + ConcurrentLinkedQueue changeEvents = inventory.getChangeEvents(); + if (!changeEvents.isEmpty()) { + entity.invalidateEquipmentNetwork(); + EntityStatMap entityStatMapComponent = archetypeChunk.getComponent(index, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + entityStatMapComponent.getStatModifiersManager().scheduleRecalculate(); + } + } + } + + @Nullable + @Override + public Query getQuery() { + return this.query; + } + + @Nonnull + @Override + public Set> getDependencies() { + return this.dependencies; + } + } + + @Deprecated(forRemoval = true) + public static class LegacyHotbarChangeStatSystem extends EntityTickingSystem { + private final Query query = Query.and(InventoryComponent.Hotbar.getComponentType(), AllLegacyLivingEntityTypesQuery.INSTANCE); + private final Set> dependencies = Set.of(new SystemDependency<>(Order.BEFORE, InventorySystems.HotbarChangeEventSystem.class)); + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + LivingEntity entity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk); + + assert entity != null; + + InventoryComponent.Hotbar inventory = archetypeChunk.getComponent(index, InventoryComponent.Hotbar.getComponentType()); + + assert inventory != null; + + ConcurrentLinkedQueue changeEvents = inventory.getChangeEvents(); + if (!changeEvents.isEmpty()) { + boolean changed = false; + byte activeHotbarSlot = inventory.getActiveSlot(); + + for (ItemContainer.ItemContainerChangeEvent event : changeEvents) { + if (activeHotbarSlot != -1 + && event.transaction().wasSlotModified(activeHotbarSlot) + && !( + event.transaction() instanceof SlotTransaction slot + && slot.getSlotAfter() != null + && ItemStack.isEquivalentType(slot.getSlotBefore(), slot.getSlotAfter()) + )) { + changed = true; + } + } + + if (changed) { + entity.invalidateEquipmentNetwork(); + EntityStatMap entityStatMapComponent = archetypeChunk.getComponent(index, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + StatModifiersManager statModifiersManager = entityStatMapComponent.getStatModifiersManager(); + statModifiersManager.scheduleRecalculate(); + ItemStack itemStack = inventory.getActiveItem(); + if (itemStack != null) { + ItemWeapon itemWeapon = itemStack.getItem().getWeapon(); + if (itemWeapon != null) { + int[] entityStatsToClear = itemWeapon.getEntityStatsToClear(); + if (entityStatsToClear != null) { + statModifiersManager.queueEntityStatsToClear(entityStatsToClear); + } + } + } + } + } + } + } + + @Nullable + @Override + public Query getQuery() { + return this.query; + } + + @Nonnull + @Override + public Set> getDependencies() { + return this.dependencies; + } + } + + @Deprecated(forRemoval = true) + public static class LegacyUtilityChangeStatSystem extends EntityTickingSystem { + private final Query query = Query.and(InventoryComponent.Utility.getComponentType(), AllLegacyLivingEntityTypesQuery.INSTANCE); + private final Set> dependencies = Set.of(new SystemDependency<>(Order.BEFORE, InventorySystems.UtilityChangeEventSystem.class)); + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + LivingEntity entity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk); + + assert entity != null; + + InventoryComponent.Utility inventory = archetypeChunk.getComponent(index, InventoryComponent.Utility.getComponentType()); + + assert inventory != null; + + ConcurrentLinkedQueue changeEvents = inventory.getChangeEvents(); + if (!changeEvents.isEmpty()) { + boolean changed = false; + byte activeHotbarSlot = inventory.getActiveSlot(); + + for (ItemContainer.ItemContainerChangeEvent event : changeEvents) { + if (activeHotbarSlot != -1 + && event.transaction().wasSlotModified(activeHotbarSlot) + && !( + event.transaction() instanceof SlotTransaction slot + && slot.getSlotAfter() != null + && ItemStack.isEquivalentType(slot.getSlotBefore(), slot.getSlotAfter()) + )) { + changed = true; + } + } + + if (changed) { + entity.invalidateEquipmentNetwork(); + EntityStatMap entityStatMapComponent = archetypeChunk.getComponent(index, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + StatModifiersManager statModifiersManager = entityStatMapComponent.getStatModifiersManager(); + statModifiersManager.scheduleRecalculate(); + ItemStack itemStack = inventory.getActiveItem(); + if (itemStack == null) { + return; + } + + ItemUtility itemUtility = itemStack.getItem().getUtility(); + if (itemUtility == null) { + return; + } + + int[] entityStatsToClear = itemUtility.getEntityStatsToClear(); + if (entityStatsToClear == null) { + return; + } + + statModifiersManager.queueEntityStatsToClear(entityStatsToClear); + } + } + } + } + + @Nullable + @Override + public Query getQuery() { + return this.query; + } + + @Nonnull + @Override + public Set> getDependencies() { + return this.dependencies; + } + } + + public static class PlayerInventoryChangeEventSystem extends EntityEventSystem { + public PlayerInventoryChangeEventSystem() { + super(InventoryChangeEvent.class); + } + + public void handle( + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer, + @Nonnull InventoryChangeEvent event + ) { + Player playerComponent = archetypeChunk.getComponent(index, Player.getComponentType()); + + assert playerComponent != null; + + HotbarManager hotbarManager = playerComponent.getHotbarManager(); + if (!hotbarManager.getIsCurrentlyLoadingHotbar()) { + if (playerComponent.getGameMode().equals(GameMode.Creative)) { + InventoryComponent.Hotbar hotbarComponent = archetypeChunk.getComponent(index, InventoryComponent.Hotbar.getComponentType()); + + assert hotbarComponent != null; + + ItemContainer hotbarInventory = hotbarComponent.getInventory(); + if (event.getItemContainer().equals(hotbarInventory)) { + Ref ref = archetypeChunk.getReferenceTo(index); + hotbarManager.saveHotbar(ref, (short)hotbarManager.getCurrentHotbarIndex(), store); + } + } + } + } + + @Nullable + @Override + public Query getQuery() { + return Query.and(Player.getComponentType(), InventoryComponent.Hotbar.getComponentType()); + } + } + + public static class StorageChangeEventSystem extends InventorySystems.InventoryChangeEventSystem { + public StorageChangeEventSystem() { + super(InventoryComponent.Storage.getComponentType()); + } + } + + public static class ToolChangeEventSystem extends InventorySystems.InventoryChangeEventSystem { + public ToolChangeEventSystem() { + super(InventoryComponent.Tool.getComponentType()); + } + } + + public static class UtilityChangeEventSystem extends InventorySystems.InventoryChangeEventSystem { + public UtilityChangeEventSystem() { + super(InventoryComponent.Utility.getComponentType()); + } + } +} diff --git a/src/com/hypixel/hytale/server/core/inventory/ItemStack.java b/src/com/hypixel/hytale/server/core/inventory/ItemStack.java index e7fa4e94..77b6360e 100644 --- a/src/com/hypixel/hytale/server/core/inventory/ItemStack.java +++ b/src/com/hypixel/hytale/server/core/inventory/ItemStack.java @@ -386,6 +386,6 @@ public class ItemStack implements NetworkSerializable { } public static class Metadata { - public static final String BLOCK_STATE = "BlockState"; + public static final String BLOCK_HOLDER = "BlockHolder"; } } diff --git a/src/com/hypixel/hytale/server/core/inventory/container/CombinedItemContainer.java b/src/com/hypixel/hytale/server/core/inventory/container/CombinedItemContainer.java index f09a855e..48f81e53 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/CombinedItemContainer.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/CombinedItemContainer.java @@ -45,38 +45,114 @@ public class CombinedItemContainer extends ItemContainer { @Override protected V readAction(@Nonnull Supplier action) { - return this.readAction0(0, action); - } + this.lockForRead(); - private V readAction0(int i, @Nonnull Supplier action) { - return i >= this.containers.length ? action.get() : this.containers[i].readAction(() -> this.readAction0(i + 1, action)); + Object var2; + try { + var2 = action.get(); + } finally { + this.unlockForRead(); + } + + return (V)var2; } @Override protected V readAction(@Nonnull Function action, X x) { - return this.readAction0(0, action, x); - } + this.lockForRead(); - private V readAction0(int i, @Nonnull Function action, X x) { - return i >= this.containers.length ? action.apply(x) : this.containers[i].readAction(() -> this.readAction0(i + 1, action, x)); + Object var3; + try { + var3 = action.apply(x); + } finally { + this.unlockForRead(); + } + + return (V)var3; } @Override protected V writeAction(@Nonnull Supplier action) { - return this.writeAction0(0, action); - } + this.lockForWrite(); - private V writeAction0(int i, @Nonnull Supplier action) { - return i >= this.containers.length ? action.get() : this.containers[i].writeAction(() -> this.writeAction0(i + 1, action)); + Object var2; + try { + var2 = action.get(); + } finally { + this.unlockForWrite(); + } + + return (V)var2; } @Override protected V writeAction(@Nonnull Function action, X x) { - return this.writeAction0(0, action, x); + this.lockForWrite(); + + Object var3; + try { + var3 = action.apply(x); + } finally { + this.unlockForWrite(); + } + + return (V)var3; } - private V writeAction0(int i, @Nonnull Function action, X x) { - return i >= this.containers.length ? action.apply(x) : this.containers[i].writeAction(() -> this.writeAction0(i + 1, action, x)); + @Override + protected void lockForRead() { + for (int i = 0; i < this.containers.length; i++) { + try { + this.containers[i].lockForRead(); + } catch (Throwable var6) { + Throwable t = var6; + + for (int j = i - 1; j >= 0; j--) { + try { + this.containers[j].unlockForRead(); + } catch (Throwable var5) { + t.addSuppressed(var5); + } + } + + throw t; + } + } + } + + @Override + protected void unlockForRead() { + for (int i = this.containers.length - 1; i >= 0; i--) { + this.containers[i].unlockForRead(); + } + } + + @Override + protected void lockForWrite() { + for (int i = 0; i < this.containers.length; i++) { + try { + this.containers[i].lockForWrite(); + } catch (Throwable var6) { + Throwable t = var6; + + for (int j = i - 1; j >= 0; j--) { + try { + this.containers[j].unlockForWrite(); + } catch (Throwable var5) { + t.addSuppressed(var5); + } + } + + throw t; + } + } + } + + @Override + protected void unlockForWrite() { + for (int i = this.containers.length - 1; i >= 0; i--) { + this.containers[i].unlockForWrite(); + } } @Nonnull @@ -89,7 +165,7 @@ public class CombinedItemContainer extends ItemContainer { ClearTransaction clear = container.internal_clear(); ItemStack[] items = clear.getItems(); - for (short slot = 0; slot < itemStacks.length; slot++) { + for (short slot = 0; slot < items.length; slot++) { itemStacks[(short)(start + slot)] = items[slot]; } @@ -219,20 +295,19 @@ public class CombinedItemContainer extends ItemContainer { @Nonnull @Override - public EventRegistration registerChangeEvent(short priority, @Nonnull Consumer consumer) { - EventRegistration thisRegistration = super.registerChangeEvent(priority, consumer); - EventRegistration[] containerRegistrations = new EventRegistration[this.containers.length]; + public EventRegistration registerChangeEvent( + short priority, @Nonnull Consumer consumer + ) { + EventRegistration thisRegistration = super.registerChangeEvent(priority, consumer); + EventRegistration[] containerRegistrations = new EventRegistration[this.containers.length]; short start = 0; for (int i = 0; i < this.containers.length; i++) { ItemContainer container = this.containers[i]; short finalStart = start; - containerRegistrations[i] = container.internalChangeEventRegistry - .register( - priority, - null, - event -> consumer.accept(new ItemContainer.ItemContainerChangeEvent(this, event.transaction().toParent(this, finalStart, container))) - ); + containerRegistrations[i] = container.registerChangeEvent( + priority, event -> consumer.accept(new ItemContainer.ItemContainerChangeEvent(this, event.transaction().toParent(this, finalStart, container))) + ); start += container.getCapacity(); } diff --git a/src/com/hypixel/hytale/server/core/inventory/container/DelegateItemContainer.java b/src/com/hypixel/hytale/server/core/inventory/container/DelegateItemContainer.java index 981c5622..6e8ff03e 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/DelegateItemContainer.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/DelegateItemContainer.java @@ -52,6 +52,26 @@ public class DelegateItemContainer extends ItemContaine return this.delegate.writeAction(action, x); } + @Override + protected void lockForRead() { + this.delegate.lockForRead(); + } + + @Override + protected void unlockForRead() { + this.delegate.unlockForRead(); + } + + @Override + protected void lockForWrite() { + this.delegate.lockForWrite(); + } + + @Override + protected void unlockForWrite() { + this.delegate.unlockForWrite(); + } + @Override protected ClearTransaction internal_clear() { return this.delegate.internal_clear(); @@ -150,9 +170,11 @@ public class DelegateItemContainer extends ItemContaine @Nonnull @Override - public EventRegistration registerChangeEvent(short priority, @Nonnull Consumer consumer) { - EventRegistration thisRegistration = super.registerChangeEvent(priority, consumer); - EventRegistration[] delegateRegistration = new EventRegistration[]{ + public EventRegistration registerChangeEvent( + short priority, @Nonnull Consumer consumer + ) { + EventRegistration thisRegistration = super.registerChangeEvent(priority, consumer); + EventRegistration[] delegateRegistration = new EventRegistration[]{ this.delegate .internalChangeEventRegistry .register( diff --git a/src/com/hypixel/hytale/server/core/inventory/container/EmptyItemContainer.java b/src/com/hypixel/hytale/server/core/inventory/container/EmptyItemContainer.java index 5e27f94b..93ff09d9 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/EmptyItemContainer.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/EmptyItemContainer.java @@ -62,6 +62,22 @@ public class EmptyItemContainer extends ItemContainer { return action.apply(x); } + @Override + protected void lockForRead() { + } + + @Override + protected void unlockForRead() { + } + + @Override + protected void lockForWrite() { + } + + @Override + protected void unlockForWrite() { + } + @Nonnull @Override protected ClearTransaction internal_clear() { diff --git a/src/com/hypixel/hytale/server/core/inventory/container/FetchedItemContainer.java b/src/com/hypixel/hytale/server/core/inventory/container/FetchedItemContainer.java new file mode 100644 index 00000000..a319afed --- /dev/null +++ b/src/com/hypixel/hytale/server/core/inventory/container/FetchedItemContainer.java @@ -0,0 +1,777 @@ +package com.hypixel.hytale.server.core.inventory.container; + +import com.hypixel.fastutil.shorts.Short2ObjectConcurrentHashMap; +import com.hypixel.hytale.event.EventPriority; +import com.hypixel.hytale.event.EventRegistration; +import com.hypixel.hytale.function.consumer.ShortObjectConsumer; +import com.hypixel.hytale.protocol.InventorySection; +import com.hypixel.hytale.protocol.ItemWithAllMetadata; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.MaterialQuantity; +import com.hypixel.hytale.server.core.inventory.ResourceQuantity; +import com.hypixel.hytale.server.core.inventory.container.filter.FilterActionType; +import com.hypixel.hytale.server.core.inventory.container.filter.FilterType; +import com.hypixel.hytale.server.core.inventory.container.filter.SlotFilter; +import com.hypixel.hytale.server.core.inventory.transaction.ClearTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.ItemStackSlotTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.MaterialSlotTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.MaterialTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.MoveTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.ResourceSlotTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.ResourceTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.SlotTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.TagSlotTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.TagTransaction; +import com.hypixel.hytale.server.core.inventory.transaction.Transaction; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class FetchedItemContainer extends ItemContainer { + private final Supplier fetcher; + + public FetchedItemContainer(Supplier fetcher) { + this.fetcher = fetcher; + } + + @Override + public short getCapacity() { + return this.fetcher.get().getCapacity(); + } + + @Override + public void setGlobalFilter(FilterType globalFilter) { + this.fetcher.get().setGlobalFilter(globalFilter); + } + + @Override + public void setSlotFilter(FilterActionType actionType, short slot, SlotFilter filter) { + this.fetcher.get().setSlotFilter(actionType, slot, filter); + } + + @Override + public ItemContainer clone() { + return new FetchedItemContainer(this.fetcher); + } + + @Override + protected V readAction(Supplier action) { + return this.fetcher.get().readAction(action); + } + + @Override + protected V readAction(Function action, X x) { + return this.fetcher.get().readAction(action, x); + } + + @Override + protected V writeAction(Supplier action) { + return this.fetcher.get().writeAction(action); + } + + @Override + protected V writeAction(Function action, X x) { + return this.fetcher.get().writeAction(action, x); + } + + @Override + protected void lockForRead() { + this.fetcher.get().lockForRead(); + } + + @Override + protected void unlockForRead() { + this.fetcher.get().unlockForRead(); + } + + @Override + protected void lockForWrite() { + this.fetcher.get().lockForWrite(); + } + + @Override + protected void unlockForWrite() { + this.fetcher.get().unlockForWrite(); + } + + @Override + protected ClearTransaction internal_clear() { + return this.fetcher.get().internal_clear(); + } + + @Nullable + @Override + protected ItemStack internal_getSlot(short slot) { + return this.fetcher.get().internal_getSlot(slot); + } + + @Nullable + @Override + protected ItemStack internal_setSlot(short slot, ItemStack itemStack) { + return this.fetcher.get().internal_setSlot(slot, itemStack); + } + + @Nullable + @Override + protected ItemStack internal_removeSlot(short slot) { + return this.fetcher.get().internal_removeSlot(slot); + } + + @Override + protected boolean cantAddToSlot(short slot, ItemStack itemStack, ItemStack slotItemStack) { + return this.fetcher.get().cantAddToSlot(slot, itemStack, slotItemStack); + } + + @Override + protected boolean cantRemoveFromSlot(short slot) { + return this.fetcher.get().cantRemoveFromSlot(slot); + } + + @Override + protected boolean cantDropFromSlot(short slot) { + return this.fetcher.get().cantDropFromSlot(slot); + } + + @Override + protected boolean cantMoveToSlot(ItemContainer fromContainer, short slotFrom) { + return this.fetcher.get().cantMoveToSlot(fromContainer, slotFrom); + } + + @Nonnull + @Override + public InventorySection toPacket() { + return this.fetcher.get().toPacket(); + } + + @Nonnull + @Override + public Map toProtocolMap() { + return this.fetcher.get().toProtocolMap(); + } + + @Override + public EventRegistration registerChangeEvent( + @Nonnull Consumer consumer + ) { + return this.fetcher.get().registerChangeEvent(consumer); + } + + @Override + public EventRegistration registerChangeEvent( + @Nonnull EventPriority priority, @Nonnull Consumer consumer + ) { + return this.fetcher.get().registerChangeEvent(priority, consumer); + } + + @Override + public EventRegistration registerChangeEvent( + short priority, @Nonnull Consumer consumer + ) { + return this.fetcher.get().registerChangeEvent(priority, consumer); + } + + @Override + public ClearTransaction clear() { + return this.fetcher.get().clear(); + } + + @Override + public boolean canAddItemStackToSlot(short slot, @Nonnull ItemStack itemStack, boolean allOrNothing, boolean filter) { + return this.fetcher.get().canAddItemStackToSlot(slot, itemStack, allOrNothing, filter); + } + + @Nonnull + @Override + public ItemStackSlotTransaction addItemStackToSlot(short slot, @Nonnull ItemStack itemStack) { + return this.fetcher.get().addItemStackToSlot(slot, itemStack); + } + + @Nonnull + @Override + public ItemStackSlotTransaction addItemStackToSlot(short slot, @Nonnull ItemStack itemStack, boolean allOrNothing, boolean filter) { + return this.fetcher.get().addItemStackToSlot(slot, itemStack, allOrNothing, filter); + } + + @Nonnull + @Override + public ItemStackSlotTransaction setItemStackForSlot(short slot, ItemStack itemStack) { + return this.fetcher.get().setItemStackForSlot(slot, itemStack); + } + + @Nonnull + @Override + public ItemStackSlotTransaction setItemStackForSlot(short slot, ItemStack itemStack, boolean filter) { + return this.fetcher.get().setItemStackForSlot(slot, itemStack, filter); + } + + @Nullable + @Override + public ItemStack getItemStack(short slot) { + return this.fetcher.get().getItemStack(slot); + } + + @Nonnull + @Override + public ItemStackSlotTransaction replaceItemStackInSlot(short slot, ItemStack itemStackToRemove, ItemStack itemStack) { + return this.fetcher.get().replaceItemStackInSlot(slot, itemStackToRemove, itemStack); + } + + @Override + public ListTransaction replaceAll(SlotReplacementFunction func) { + return this.fetcher.get().replaceAll(func); + } + + @Override + protected ItemStackSlotTransaction internal_replaceItemStack(short slot, @Nullable ItemStack itemStackToRemove, ItemStack itemStack) { + return this.fetcher.get().internal_replaceItemStack(slot, itemStackToRemove, itemStack); + } + + @Nonnull + @Override + public SlotTransaction removeItemStackFromSlot(short slot) { + return this.fetcher.get().removeItemStackFromSlot(slot); + } + + @Nonnull + @Override + public SlotTransaction removeItemStackFromSlot(short slot, boolean filter) { + return this.fetcher.get().removeItemStackFromSlot(slot, filter); + } + + @Nonnull + @Override + public ItemStackSlotTransaction removeItemStackFromSlot(short slot, int quantityToRemove) { + return this.fetcher.get().removeItemStackFromSlot(slot, quantityToRemove); + } + + @Nonnull + @Override + public ItemStackSlotTransaction removeItemStackFromSlot(short slot, int quantityToRemove, boolean allOrNothing, boolean filter) { + return this.fetcher.get().removeItemStackFromSlot(slot, quantityToRemove, allOrNothing, filter); + } + + @Deprecated + @Override + public ItemStackSlotTransaction internal_removeItemStack(short slot, int quantityToRemove) { + return this.fetcher.get().internal_removeItemStack(slot, quantityToRemove); + } + + @Nonnull + @Override + public ItemStackSlotTransaction removeItemStackFromSlot(short slot, ItemStack itemStackToRemove, int quantityToRemove) { + return this.fetcher.get().removeItemStackFromSlot(slot, itemStackToRemove, quantityToRemove); + } + + @Nonnull + @Override + public ItemStackSlotTransaction removeItemStackFromSlot(short slot, ItemStack itemStackToRemove, int quantityToRemove, boolean allOrNothing, boolean filter) { + return this.fetcher.get().removeItemStackFromSlot(slot, itemStackToRemove, quantityToRemove, allOrNothing, filter); + } + + @Nonnull + @Override + public MaterialSlotTransaction removeMaterialFromSlot(short slot, @Nonnull MaterialQuantity material) { + return this.fetcher.get().removeMaterialFromSlot(slot, material); + } + + @Nonnull + @Override + public MaterialSlotTransaction removeMaterialFromSlot( + short slot, @Nonnull MaterialQuantity material, boolean allOrNothing, boolean exactAmount, boolean filter + ) { + return this.fetcher.get().removeMaterialFromSlot(slot, material, allOrNothing, exactAmount, filter); + } + + @Nonnull + @Override + public ResourceSlotTransaction removeResourceFromSlot(short slot, @Nonnull ResourceQuantity resource) { + return this.fetcher.get().removeResourceFromSlot(slot, resource); + } + + @Nonnull + @Override + public ResourceSlotTransaction removeResourceFromSlot( + short slot, @Nonnull ResourceQuantity resource, boolean allOrNothing, boolean exactAmount, boolean filter + ) { + return this.fetcher.get().removeResourceFromSlot(slot, resource, allOrNothing, exactAmount, filter); + } + + @Nonnull + @Override + public TagSlotTransaction removeTagFromSlot(short slot, int tagIndex, int quantity) { + return this.fetcher.get().removeTagFromSlot(slot, tagIndex, quantity); + } + + @Nonnull + @Override + public TagSlotTransaction removeTagFromSlot(short slot, int tagIndex, int quantity, boolean allOrNothing, boolean filter) { + return this.fetcher.get().removeTagFromSlot(slot, tagIndex, quantity, allOrNothing, filter); + } + + @Nonnull + @Override + public MoveTransaction moveItemStackFromSlot(short slot, @Nonnull ItemContainer containerTo) { + return this.fetcher.get().moveItemStackFromSlot(slot, containerTo); + } + + @Nonnull + @Override + public MoveTransaction moveItemStackFromSlot(short slot, @Nonnull ItemContainer containerTo, boolean filter) { + return this.fetcher.get().moveItemStackFromSlot(slot, containerTo, filter); + } + + @Nonnull + @Override + public MoveTransaction moveItemStackFromSlot(short slot, @Nonnull ItemContainer containerTo, boolean allOrNothing, boolean filter) { + return this.fetcher.get().moveItemStackFromSlot(slot, containerTo, allOrNothing, filter); + } + + @Override + protected MoveTransaction internal_moveItemStackFromSlot( + short slot, @Nonnull ItemContainer containerTo, boolean allOrNothing, boolean filter + ) { + return this.fetcher.get().internal_moveItemStackFromSlot(slot, containerTo, allOrNothing, filter); + } + + @Nonnull + @Override + public MoveTransaction moveItemStackFromSlot(short slot, int quantity, @Nonnull ItemContainer containerTo) { + return this.fetcher.get().moveItemStackFromSlot(slot, quantity, containerTo); + } + + @Nonnull + @Override + public MoveTransaction moveItemStackFromSlot( + short slot, int quantity, @Nonnull ItemContainer containerTo, boolean allOrNothing, boolean filter + ) { + return this.fetcher.get().moveItemStackFromSlot(slot, quantity, containerTo, allOrNothing, filter); + } + + @Override + protected MoveTransaction internal_moveItemStackFromSlot( + short slot, int quantity, @Nonnull ItemContainer containerTo, boolean allOrNothing, boolean filter + ) { + return this.fetcher.get().internal_moveItemStackFromSlot(slot, quantity, containerTo, allOrNothing, filter); + } + + @Nonnull + @Override + public ListTransaction> moveItemStackFromSlot(short slot, ItemContainer... containerTo) { + return this.fetcher.get().moveItemStackFromSlot(slot, containerTo); + } + + @Nonnull + @Override + public ListTransaction> moveItemStackFromSlot( + short slot, boolean allOrNothing, boolean filter, @Nonnull ItemContainer... containerTo + ) { + return this.fetcher.get().moveItemStackFromSlot(slot, allOrNothing, filter, containerTo); + } + + @Nonnull + @Override + public ListTransaction> moveItemStackFromSlot(short slot, int quantity, ItemContainer... containerTo) { + return this.fetcher.get().moveItemStackFromSlot(slot, quantity, containerTo); + } + + @Nonnull + @Override + public ListTransaction> moveItemStackFromSlot( + short slot, int quantity, boolean allOrNothing, boolean filter, @Nonnull ItemContainer... containerTo + ) { + return this.fetcher.get().moveItemStackFromSlot(slot, quantity, allOrNothing, filter, containerTo); + } + + @Nonnull + @Override + public MoveTransaction moveItemStackFromSlotToSlot(short slot, int quantity, @Nonnull ItemContainer containerTo, short slotTo) { + return this.fetcher.get().moveItemStackFromSlotToSlot(slot, quantity, containerTo, slotTo); + } + + @Nonnull + @Override + public MoveTransaction moveItemStackFromSlotToSlot( + short slot, int quantity, @Nonnull ItemContainer containerTo, short slotTo, boolean filter + ) { + return this.fetcher.get().moveItemStackFromSlotToSlot(slot, quantity, containerTo, slotTo, filter); + } + + @Override + protected MoveTransaction internal_moveItemStackFromSlot( + short slot, int quantity, @Nonnull ItemContainer containerTo, short slotTo, boolean filter + ) { + return this.fetcher.get().internal_moveItemStackFromSlot(slot, quantity, containerTo, slotTo, filter); + } + + @Nonnull + @Override + public ListTransaction> moveAllItemStacksTo(ItemContainer... containerTo) { + return this.fetcher.get().moveAllItemStacksTo(containerTo); + } + + @Nonnull + @Override + public ListTransaction> moveAllItemStacksTo(Predicate itemPredicate, ItemContainer... containerTo) { + return this.fetcher.get().moveAllItemStacksTo(itemPredicate, containerTo); + } + + @Nonnull + @Override + protected ListTransaction> internal_moveAllItemStacksTo( + @Nullable Predicate itemPredicate, ItemContainer[] containerTo + ) { + return this.fetcher.get().internal_moveAllItemStacksTo(itemPredicate, containerTo); + } + + @Nonnull + @Override + public ListTransaction> quickStackTo(@Nonnull ItemContainer... containerTo) { + return this.fetcher.get().quickStackTo(containerTo); + } + + @Nonnull + @Override + public ListTransaction> combineItemStacksIntoSlot(@Nonnull ItemContainer containerTo, short slotTo) { + return this.fetcher.get().combineItemStacksIntoSlot(containerTo, slotTo); + } + + @Nonnull + @Override + protected ListTransaction> internal_combineItemStacksIntoSlot(@Nonnull ItemContainer containerTo, short slotTo) { + return this.fetcher.get().internal_combineItemStacksIntoSlot(containerTo, slotTo); + } + + @Nonnull + @Override + public ListTransaction> swapItems(short srcPos, @Nonnull ItemContainer containerTo, short destPos, short length) { + return this.fetcher.get().swapItems(srcPos, containerTo, destPos, length); + } + + @Nonnull + @Override + protected ListTransaction> internal_swapItems(short srcPos, @Nonnull ItemContainer containerTo, short destPos, short length) { + return this.fetcher.get().internal_swapItems(srcPos, containerTo, destPos, length); + } + + @Nonnull + @Override + protected MoveTransaction internal_swapItems(@Nonnull ItemContainer containerTo, short slotFrom, short slotTo) { + return this.fetcher.get().internal_swapItems(containerTo, slotFrom, slotTo); + } + + @Override + public boolean canAddItemStack(@Nonnull ItemStack itemStack) { + return this.fetcher.get().canAddItemStack(itemStack); + } + + @Override + public boolean canAddItemStack(@Nonnull ItemStack itemStack, boolean fullStacks, boolean filter) { + return this.fetcher.get().canAddItemStack(itemStack, fullStacks, filter); + } + + @Nonnull + @Override + public ItemStackTransaction addItemStack(@Nonnull ItemStack itemStack) { + return this.fetcher.get().addItemStack(itemStack); + } + + @Nonnull + @Override + public ItemStackTransaction addItemStack(@Nonnull ItemStack itemStack, boolean allOrNothing, boolean fullStacks, boolean filter) { + return this.fetcher.get().addItemStack(itemStack, allOrNothing, fullStacks, filter); + } + + @Override + public boolean canAddItemStacks(List itemStacks) { + return this.fetcher.get().canAddItemStacks(itemStacks); + } + + @Override + public boolean canAddItemStacks(@Nullable List itemStacks, boolean fullStacks, boolean filter) { + return this.fetcher.get().canAddItemStacks(itemStacks, fullStacks, filter); + } + + @Override + public ListTransaction addItemStacks(List itemStacks) { + return this.fetcher.get().addItemStacks(itemStacks); + } + + @Override + public ListTransaction addItemStacks(@Nullable List itemStacks, boolean allOrNothing, boolean fullStacks, boolean filter) { + return this.fetcher.get().addItemStacks(itemStacks, allOrNothing, fullStacks, filter); + } + + @Override + public ListTransaction addItemStacksOrdered(List itemStacks) { + return this.fetcher.get().addItemStacksOrdered(itemStacks); + } + + @Override + public ListTransaction addItemStacksOrdered(short offset, List itemStacks) { + return this.fetcher.get().addItemStacksOrdered(offset, itemStacks); + } + + @Override + public ListTransaction addItemStacksOrdered(List itemStacks, boolean allOrNothing, boolean filter) { + return this.fetcher.get().addItemStacksOrdered(itemStacks, allOrNothing, filter); + } + + @Override + public ListTransaction addItemStacksOrdered( + short offset, @Nullable List itemStacks, boolean allOrNothing, boolean filter + ) { + return this.fetcher.get().addItemStacksOrdered(offset, itemStacks, allOrNothing, filter); + } + + @Override + public boolean canRemoveItemStack(ItemStack itemStack) { + return this.fetcher.get().canRemoveItemStack(itemStack); + } + + @Override + public boolean canRemoveItemStack(@Nullable ItemStack itemStack, boolean exactAmount, boolean filter) { + return this.fetcher.get().canRemoveItemStack(itemStack, exactAmount, filter); + } + + @Nonnull + @Override + public ItemStackTransaction removeItemStack(@Nonnull ItemStack itemStack) { + return this.fetcher.get().removeItemStack(itemStack); + } + + @Nonnull + @Override + public ItemStackTransaction removeItemStack(@Nonnull ItemStack itemStack, boolean allOrNothing, boolean filter) { + return this.fetcher.get().removeItemStack(itemStack, allOrNothing, filter); + } + + @Override + public boolean canRemoveItemStacks(List itemStacks) { + return this.fetcher.get().canRemoveItemStacks(itemStacks); + } + + @Override + public boolean canRemoveItemStacks(@Nullable List itemStacks, boolean exactAmount, boolean filter) { + return this.fetcher.get().canRemoveItemStacks(itemStacks, exactAmount, filter); + } + + @Override + public ListTransaction removeItemStacks(List itemStacks) { + return this.fetcher.get().removeItemStacks(itemStacks); + } + + @Override + public ListTransaction removeItemStacks(@Nullable List itemStacks, boolean allOrNothing, boolean filter) { + return this.fetcher.get().removeItemStacks(itemStacks, allOrNothing, filter); + } + + @Override + public boolean canRemoveTag(int tagIndex, int quantity) { + return this.fetcher.get().canRemoveTag(tagIndex, quantity); + } + + @Override + public boolean canRemoveTag(int tagIndex, int quantity, boolean exactAmount, boolean filter) { + return this.fetcher.get().canRemoveTag(tagIndex, quantity, exactAmount, filter); + } + + @Nonnull + @Override + public TagTransaction removeTag(int tagIndex, int quantity) { + return this.fetcher.get().removeTag(tagIndex, quantity); + } + + @Nonnull + @Override + public TagTransaction removeTag(int tagIndex, int quantity, boolean allOrNothing, boolean exactAmount, boolean filter) { + return this.fetcher.get().removeTag(tagIndex, quantity, allOrNothing, exactAmount, filter); + } + + @Override + public boolean canRemoveResource(ResourceQuantity resource) { + return this.fetcher.get().canRemoveResource(resource); + } + + @Override + public boolean canRemoveResource(@Nullable ResourceQuantity resource, boolean exactAmount, boolean filter) { + return this.fetcher.get().canRemoveResource(resource, exactAmount, filter); + } + + @Nonnull + @Override + public ResourceTransaction removeResource(@Nonnull ResourceQuantity resource) { + return this.fetcher.get().removeResource(resource); + } + + @Nonnull + @Override + public ResourceTransaction removeResource(@Nonnull ResourceQuantity resource, boolean allOrNothing, boolean exactAmount, boolean filter) { + return this.fetcher.get().removeResource(resource, allOrNothing, exactAmount, filter); + } + + @Override + public boolean canRemoveResources(List resources) { + return this.fetcher.get().canRemoveResources(resources); + } + + @Override + public boolean canRemoveResources(@Nullable List resources, boolean exactAmount, boolean filter) { + return this.fetcher.get().canRemoveResources(resources, exactAmount, filter); + } + + @Override + public ListTransaction removeResources(List resources) { + return this.fetcher.get().removeResources(resources); + } + + @Override + public ListTransaction removeResources( + @Nullable List resources, boolean allOrNothing, boolean exactAmount, boolean filter + ) { + return this.fetcher.get().removeResources(resources, allOrNothing, exactAmount, filter); + } + + @Override + public boolean canRemoveMaterial(MaterialQuantity material) { + return this.fetcher.get().canRemoveMaterial(material); + } + + @Override + public boolean canRemoveMaterial(@Nullable MaterialQuantity material, boolean exactAmount, boolean filter) { + return this.fetcher.get().canRemoveMaterial(material, exactAmount, filter); + } + + @Nonnull + @Override + public MaterialTransaction removeMaterial(@Nonnull MaterialQuantity material) { + return this.fetcher.get().removeMaterial(material); + } + + @Nonnull + @Override + public MaterialTransaction removeMaterial(@Nonnull MaterialQuantity material, boolean allOrNothing, boolean exactAmount, boolean filter) { + return this.fetcher.get().removeMaterial(material, allOrNothing, exactAmount, filter); + } + + @Override + public boolean canRemoveMaterials(List materials) { + return this.fetcher.get().canRemoveMaterials(materials); + } + + @Override + public boolean canRemoveMaterials(@Nullable List materials, boolean exactAmount, boolean filter) { + return this.fetcher.get().canRemoveMaterials(materials, exactAmount, filter); + } + + @Override + public List getSlotMaterialsToRemove(@Nullable List materials, boolean exactAmount, boolean filter) { + return this.fetcher.get().getSlotMaterialsToRemove(materials, exactAmount, filter); + } + + @Override + public ListTransaction removeMaterials(List materials) { + return this.fetcher.get().removeMaterials(materials); + } + + @Override + public ListTransaction removeMaterials( + @Nullable List materials, boolean allOrNothing, boolean exactAmount, boolean filter + ) { + return this.fetcher.get().removeMaterials(materials, allOrNothing, exactAmount, filter); + } + + @Override + public ListTransaction removeMaterialsOrdered(short offset, List materials) { + return this.fetcher.get().removeMaterialsOrdered(offset, materials); + } + + @Override + public ListTransaction removeMaterialsOrdered( + List materials, boolean allOrNothing, boolean exactAmount, boolean filter + ) { + return this.fetcher.get().removeMaterialsOrdered(materials, allOrNothing, exactAmount, filter); + } + + @Override + public ListTransaction removeMaterialsOrdered( + short offset, @Nullable List materials, boolean allOrNothing, boolean exactAmount, boolean filter + ) { + return this.fetcher.get().removeMaterialsOrdered(offset, materials, allOrNothing, exactAmount, filter); + } + + @Override + public boolean isEmpty() { + return this.fetcher.get().isEmpty(); + } + + @Override + public int countItemStacks(@Nonnull Predicate itemPredicate) { + return this.fetcher.get().countItemStacks(itemPredicate); + } + + @Override + public boolean containsItemStacksStackableWith(@Nonnull ItemStack itemStack) { + return this.fetcher.get().containsItemStacksStackableWith(itemStack); + } + + @Override + public void forEach(@Nonnull ShortObjectConsumer action) { + this.fetcher.get().forEach(action); + } + + @Override + public void forEachWithMeta(@Nonnull Short2ObjectConcurrentHashMap.ShortBiObjConsumer consumer, T meta) { + this.fetcher.get().forEachWithMeta(consumer, meta); + } + + @Nonnull + @Override + public List removeAllItemStacks() { + return this.fetcher.get().removeAllItemStacks(); + } + + @Nonnull + @Override + public List dropAllItemStacks() { + return this.fetcher.get().dropAllItemStacks(); + } + + @Nonnull + @Override + public List dropAllItemStacks(boolean filter) { + return this.fetcher.get().dropAllItemStacks(filter); + } + + @Nonnull + @Override + public ListTransaction sortItems(@Nonnull SortType sort) { + return this.fetcher.get().sortItems(sort); + } + + @Override + protected ListTransaction internal_sortItems(@Nonnull SortType sort) { + return this.fetcher.get().internal_sortItems(sort); + } + + @Override + protected void sendUpdate(@Nonnull Transaction transaction) { + this.fetcher.get().sendUpdate(transaction); + } + + @Override + public boolean containsContainer(ItemContainer itemContainer) { + return this.fetcher.get().containsContainer(itemContainer); + } + + @Override + public void doMigration(Function blockMigration) { + this.fetcher.get().doMigration(blockMigration); + } +} diff --git a/src/com/hypixel/hytale/server/core/inventory/container/InternalContainerUtilItemStack.java b/src/com/hypixel/hytale/server/core/inventory/container/InternalContainerUtilItemStack.java index 3480d1f3..f215aa62 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/InternalContainerUtilItemStack.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/InternalContainerUtilItemStack.java @@ -327,12 +327,6 @@ public class InternalContainerUtilItemStack { ); } - protected static int testRemoveItemStackFromSlot( - @Nonnull ItemContainer container, short slot, ItemStack itemStack, int testQuantityRemaining, boolean filter - ) { - return testRemoveItemStackFromSlot(container, slot, itemStack, testQuantityRemaining, filter, (a, b) -> ItemStack.isStackableWith(a, b)); - } - protected static int testRemoveItemStackFromSlot( @Nonnull ItemContainer container, short slot, ItemStack itemStack, int testQuantityRemaining, boolean filter, BiPredicate predicate ) { @@ -564,10 +558,16 @@ public class InternalContainerUtilItemStack { } protected static int testRemoveItemStackFromItems(@Nonnull ItemContainer container, ItemStack itemStack, int testQuantityRemaining, boolean filter) { + return testRemoveItemStackFromItems(container, itemStack, testQuantityRemaining, filter, (a, b) -> ItemStack.isStackableWith(a, b)); + } + + protected static int testRemoveItemStackFromItems( + @Nonnull ItemContainer container, ItemStack itemStack, int testQuantityRemaining, boolean filter, BiPredicate predicate + ) { for (short i = 0; i < container.getCapacity() && testQuantityRemaining > 0; i++) { if (!filter || !container.cantRemoveFromSlot(i)) { ItemStack slotItemStack = container.internal_getSlot(i); - if (!ItemStack.isEmpty(slotItemStack) && slotItemStack.isStackableWith(itemStack)) { + if (!ItemStack.isEmpty(slotItemStack) && predicate.test(slotItemStack, itemStack)) { int quantity = slotItemStack.getQuantity(); int quantityAdjustment = Math.min(quantity, testQuantityRemaining); testQuantityRemaining -= quantityAdjustment; diff --git a/src/com/hypixel/hytale/server/core/inventory/container/InternalContainerUtilMaterial.java b/src/com/hypixel/hytale/server/core/inventory/container/InternalContainerUtilMaterial.java index 0d632bb0..1eabebbf 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/InternalContainerUtilMaterial.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/InternalContainerUtilMaterial.java @@ -136,7 +136,9 @@ public class InternalContainerUtilMaterial { @Nonnull ItemContainer container, @Nonnull MaterialQuantity material, int testQuantityRemaining, boolean filter ) { if (material.getItemId() != null) { - return InternalContainerUtilItemStack.testRemoveItemStackFromItems(container, material.toItemStack(), testQuantityRemaining, filter); + return InternalContainerUtilItemStack.testRemoveItemStackFromItems( + container, material.toItemStack(), testQuantityRemaining, filter, (a, b) -> ItemStack.isEquivalentType(a, b) + ); } else { return material.getTagIndex() != Integer.MIN_VALUE ? InternalContainerUtilTag.testRemoveTagFromItems(container, material.getTagIndex(), testQuantityRemaining, filter) diff --git a/src/com/hypixel/hytale/server/core/inventory/container/ItemContainer.java b/src/com/hypixel/hytale/server/core/inventory/container/ItemContainer.java index e21c0bff..73202baf 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/ItemContainer.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/ItemContainer.java @@ -78,6 +78,14 @@ public abstract class ItemContainer { protected abstract V writeAction(Function var1, X var2); + protected abstract void lockForRead(); + + protected abstract void unlockForRead(); + + protected abstract void lockForWrite(); + + protected abstract void unlockForWrite(); + protected abstract ClearTransaction internal_clear(); @Nullable @@ -116,15 +124,21 @@ public abstract class ItemContainer { return map; } - public EventRegistration registerChangeEvent(@Nonnull Consumer consumer) { + public EventRegistration registerChangeEvent( + @Nonnull Consumer consumer + ) { return this.registerChangeEvent((short)0, consumer); } - public EventRegistration registerChangeEvent(@Nonnull EventPriority priority, @Nonnull Consumer consumer) { + public EventRegistration registerChangeEvent( + @Nonnull EventPriority priority, @Nonnull Consumer consumer + ) { return this.registerChangeEvent(priority.getValue(), consumer); } - public EventRegistration registerChangeEvent(short priority, @Nonnull Consumer consumer) { + public EventRegistration registerChangeEvent( + short priority, @Nonnull Consumer consumer + ) { return this.externalChangeEventRegistry.register(priority, null, consumer); } @@ -1390,7 +1404,10 @@ public abstract class ItemContainer { } public static T ensureContainerCapacity( - @Nullable T inputContainer, short capacity, @Nonnull Short2ObjectConcurrentHashMap.ShortFunction newContainerSupplier, List remainder + @Nullable T inputContainer, + short capacity, + @Nonnull Short2ObjectConcurrentHashMap.ShortFunction newContainerSupplier, + @Nullable List remainder ) { if (inputContainer == null) { return newContainerSupplier.apply(capacity); diff --git a/src/com/hypixel/hytale/server/core/inventory/container/ItemStackItemContainer.java b/src/com/hypixel/hytale/server/core/inventory/container/ItemStackItemContainer.java index 3076e32a..215dde9b 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/ItemStackItemContainer.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/ItemStackItemContainer.java @@ -148,6 +148,26 @@ public class ItemStackItemContainer extends ItemContainer { return (V)var3; } + @Override + protected void lockForRead() { + this.lock.readLock().lock(); + } + + @Override + protected void unlockForRead() { + this.lock.readLock().unlock(); + } + + @Override + protected void lockForWrite() { + this.lock.writeLock().lock(); + } + + @Override + protected void unlockForWrite() { + this.lock.writeLock().unlock(); + } + @Override public boolean isEmpty() { this.lock.readLock().lock(); diff --git a/src/com/hypixel/hytale/server/core/inventory/container/SimpleItemContainer.java b/src/com/hypixel/hytale/server/core/inventory/container/SimpleItemContainer.java index 2577d70f..f9a09fa7 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/SimpleItemContainer.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/SimpleItemContainer.java @@ -19,8 +19,8 @@ import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMap.Entry; import java.util.List; import java.util.Map; import java.util.Objects; @@ -37,19 +37,44 @@ public class SimpleItemContainer extends ItemContainer { .append(new KeyedCodec<>("Capacity", Codec.SHORT), (o, i) -> o.capacity = i, o -> o.capacity) .addValidator(Validators.greaterThanOrEqual((short)0)) .add() - .append(new KeyedCodec<>("Items", new Short2ObjectMapCodec<>(ItemStack.CODEC, Short2ObjectOpenHashMap::new, false)), (o, i) -> o.items = i, o -> o.items) + .append(new KeyedCodec<>("Items", new Short2ObjectMapCodec<>(ItemStack.CODEC, Short2ObjectOpenHashMap::new, false)), (o, i) -> { + o.items = new ItemStack[o.capacity]; + int count = 0; + + for (Entry entry : i.short2ObjectEntrySet()) { + short slot = entry.getShortKey(); + ItemStack stack = entry.getValue(); + if (slot >= 0 && slot < o.capacity && !ItemStack.isEmpty(stack)) { + o.items[slot] = stack; + count++; + } + } + + o.itemsCount = count; + }, o -> { + Short2ObjectOpenHashMap map = new Short2ObjectOpenHashMap<>(); + + for (short slot = 0; slot < o.capacity; slot++) { + ItemStack stack = o.items[slot]; + if (stack != null && !ItemStack.isEmpty(stack)) { + map.put(slot, stack); + } + } + + return map; + }) .add() .afterDecode(i -> { if (i.items == null) { - i.items = new Short2ObjectOpenHashMap<>(i.capacity); + i.items = new ItemStack[i.capacity]; + i.itemsCount = 0; } - - i.items.short2ObjectEntrySet().removeIf(e -> e.getShortKey() < 0 || e.getShortKey() >= i.capacity || ItemStack.isEmpty(e.getValue())); }) .build(); protected short capacity; protected final ReadWriteLock lock = new ReentrantReadWriteLock(); - protected Short2ObjectMap items; + protected ItemStack[] items; + protected int itemsCount; private final Map> slotFilters = new ConcurrentHashMap<>(); private FilterType globalFilter = FilterType.ALLOW_ALL; @@ -61,7 +86,7 @@ public class SimpleItemContainer extends ItemContainer { throw new IllegalArgumentException("Capacity is less than or equal zero! " + capacity + " <= 0"); } else { this.capacity = capacity; - this.items = new Short2ObjectOpenHashMap<>(capacity); + this.items = new ItemStack[capacity]; } } @@ -70,7 +95,9 @@ public class SimpleItemContainer extends ItemContainer { other.lock.readLock().lock(); try { - this.items = new Short2ObjectOpenHashMap<>(other.items); + this.items = new ItemStack[other.capacity]; + System.arraycopy(other.items, 0, this.items, 0, other.capacity); + this.itemsCount = other.itemsCount; } finally { other.lock.readLock().unlock(); } @@ -135,19 +162,55 @@ public class SimpleItemContainer extends ItemContainer { return (V)var3; } + @Override + protected void lockForRead() { + this.lock.readLock().lock(); + } + + @Override + protected void unlockForRead() { + this.lock.readLock().unlock(); + } + + @Override + protected void lockForWrite() { + this.lock.writeLock().lock(); + } + + @Override + protected void unlockForWrite() { + this.lock.writeLock().unlock(); + } + @Override protected ItemStack internal_getSlot(short slot) { - return this.items.get(slot); + return this.items[slot]; } @Override protected ItemStack internal_setSlot(short slot, ItemStack itemStack) { - return ItemStack.isEmpty(itemStack) ? this.internal_removeSlot(slot) : this.items.put(slot, itemStack); + if (ItemStack.isEmpty(itemStack)) { + return this.internal_removeSlot(slot); + } else { + ItemStack previous = this.items[slot]; + this.items[slot] = itemStack; + if (previous == null) { + this.itemsCount++; + } + + return previous; + } } @Override protected ItemStack internal_removeSlot(short slot) { - return this.items.remove(slot); + ItemStack previous = this.items[slot]; + this.items[slot] = null; + if (previous != null) { + this.itemsCount--; + } + + return previous; } @Override @@ -191,10 +254,11 @@ public class SimpleItemContainer extends ItemContainer { ItemStack[] itemStacks = new ItemStack[this.getCapacity()]; for (short i = 0; i < itemStacks.length; i++) { - itemStacks[i] = this.items.get(i); + itemStacks[i] = this.items[i]; + this.items[i] = null; } - this.items.clear(); + this.itemsCount = 0; return new ClearTransaction(true, (short)0, itemStacks); } @@ -208,7 +272,7 @@ public class SimpleItemContainer extends ItemContainer { this.lock.readLock().lock(); try { - if (this.items.isEmpty()) { + if (this.itemsCount == 0) { return true; } } finally { @@ -256,38 +320,46 @@ public class SimpleItemContainer extends ItemContainer { public boolean equals(Object o) { if (this == o) { return true; - } else if (o instanceof SimpleItemContainer that) { - if (this.capacity != that.capacity) { - return false; - } else { - this.lock.readLock().lock(); - - boolean var3; - try { - var3 = this.items.equals(that.items); - } finally { - this.lock.readLock().unlock(); - } - - return var3; - } - } else { + } else if (!(o instanceof SimpleItemContainer that)) { return false; + } else if (this.capacity != that.capacity) { + return false; + } else { + this.lock.readLock().lock(); + + try { + if (this.itemsCount != that.itemsCount) { + return false; + } else { + for (int itr = 0; itr < this.capacity; itr++) { + if (!Objects.equals(this.items[itr], that.items[itr])) { + return false; + } + } + + return true; + } + } finally { + this.lock.readLock().unlock(); + } } } @Override public int hashCode() { + int result = this.capacity; this.lock.readLock().lock(); - int result; try { - result = this.items.hashCode(); + for (int i = 0; i < this.capacity; i++) { + ItemStack item = this.items[i]; + result = 31 * result + (item != null ? item.hashCode() : 0); + } } finally { this.lock.readLock().unlock(); } - return 31 * result + this.capacity; + return result; } public static ItemContainer getNewContainer(short capacity) { diff --git a/src/com/hypixel/hytale/server/core/inventory/container/SortType.java b/src/com/hypixel/hytale/server/core/inventory/container/SortType.java index 8f0d000b..7b3ef0c9 100644 --- a/src/com/hypixel/hytale/server/core/inventory/container/SortType.java +++ b/src/com/hypixel/hytale/server/core/inventory/container/SortType.java @@ -40,24 +40,6 @@ public enum SortType { return this.comparator; } - @Nonnull - public com.hypixel.hytale.protocol.SortType toPacket() { - return switch (this) { - case NAME -> com.hypixel.hytale.protocol.SortType.Name; - case TYPE -> com.hypixel.hytale.protocol.SortType.Type; - case RARITY -> com.hypixel.hytale.protocol.SortType.Rarity; - }; - } - - @Nonnull - public static SortType fromPacket(@Nonnull com.hypixel.hytale.protocol.SortType sortType_) { - return switch (sortType_) { - case Type -> TYPE; - case Rarity -> RARITY; - case Name -> NAME; - }; - } - @Nonnull private static > Comparator comparatorFor(@Nonnull Function key) { return (a, b) -> { @@ -90,7 +72,7 @@ public enum SortType { } else if (item.getTool() != null) { return TOOL; } else { - return item.getBuilderToolData() != null ? SPECIAL : ITEM; + return item.getBuilderTool() != null ? SPECIAL : ITEM; } } } diff --git a/src/com/hypixel/hytale/server/core/io/NetworkSerializers.java b/src/com/hypixel/hytale/server/core/io/NetworkSerializers.java index 02174ddb..5c04d278 100644 --- a/src/com/hypixel/hytale/server/core/io/NetworkSerializers.java +++ b/src/com/hypixel/hytale/server/core/io/NetworkSerializers.java @@ -6,12 +6,12 @@ import com.hypixel.hytale.protocol.Hitbox; public interface NetworkSerializers { NetworkSerializer BOX = t -> { Hitbox packet = new Hitbox(); - packet.minX = (float)t.getMin().getX(); - packet.minY = (float)t.getMin().getY(); - packet.minZ = (float)t.getMin().getZ(); - packet.maxX = (float)t.getMax().getX(); - packet.maxY = (float)t.getMax().getY(); - packet.maxZ = (float)t.getMax().getZ(); + packet.minX = (float)t.getMin().x(); + packet.minY = (float)t.getMin().y(); + packet.minZ = (float)t.getMin().z(); + packet.maxX = (float)t.getMax().x(); + packet.maxY = (float)t.getMax().y(); + packet.maxZ = (float)t.getMax().z(); return packet; }; } diff --git a/src/com/hypixel/hytale/server/core/io/PacketHandler.java b/src/com/hypixel/hytale/server/core/io/PacketHandler.java index b6924ee4..231f6fc7 100644 --- a/src/com/hypixel/hytale/server/core/io/PacketHandler.java +++ b/src/com/hypixel/hytale/server/core/io/PacketHandler.java @@ -10,16 +10,19 @@ import com.hypixel.hytale.metrics.MetricsRegistry; import com.hypixel.hytale.metrics.metric.HistoricMetric; import com.hypixel.hytale.metrics.metric.Metric; import com.hypixel.hytale.protocol.CachedPacket; +import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.ToServerPacket; import com.hypixel.hytale.protocol.io.PacketStatsRecorder; import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; import com.hypixel.hytale.protocol.packets.connection.DisconnectType; import com.hypixel.hytale.protocol.packets.connection.Ping; import com.hypixel.hytale.protocol.packets.connection.Pong; import com.hypixel.hytale.protocol.packets.connection.PongType; +import com.hypixel.hytale.protocol.packets.connection.ServerDisconnect; +import com.hypixel.hytale.protocol.packets.stream.StreamType; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.auth.PlayerAuthentication; import com.hypixel.hytale.server.core.io.adapter.PacketAdapters; import com.hypixel.hytale.server.core.io.handlers.login.AuthenticationPacketHandler; @@ -28,6 +31,7 @@ import com.hypixel.hytale.server.core.io.netty.NettyUtil; import com.hypixel.hytale.server.core.io.transport.QUICTransport; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; import com.hypixel.hytale.server.core.receiver.IPacketReceiver; +import com.hypixel.hytale.server.core.util.MessageUtil; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.local.LocalAddress; @@ -46,11 +50,14 @@ import java.net.SocketAddress; import java.security.SecureRandom; import java.time.Duration; import java.time.Instant; +import java.util.Collections; +import java.util.EnumMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BooleanSupplier; @@ -91,6 +98,10 @@ public abstract class PacketHandler implements IPacketReceiver { protected CompletableFuture clientReadyForChunksFuture; @Nonnull protected final PacketHandler.DisconnectReason disconnectReason = new PacketHandler.DisconnectReason(); + @Nonnull + private final Map auxiliaryChannels = Collections.synchronizedMap(new EnumMap<>(StreamType.class)); + private final AtomicLong lastStreamOpenTimeNanos = new AtomicLong(); + private static final long STREAM_OPEN_MIN_INTERVAL_NANOS = TimeUnit.SECONDS.toNanos(1L); public PacketHandler(@Nonnull Channel channel, @Nonnull ProtocolVersion protocolVersion) { this.channels[0] = channel; @@ -107,14 +118,9 @@ public abstract class PacketHandler implements IPacketReceiver { return this.channels[0]; } - @Deprecated(forRemoval = true) - public void setCompressionEnabled(boolean compressionEnabled) { - HytaleLogger.getLogger().at(Level.INFO).log(this.getIdentifier() + " compression now handled by encoder"); - } - - @Deprecated(forRemoval = true) - public boolean isCompressionEnabled() { - return true; + @Nullable + public Channel getChannel(@Nonnull StreamType type) { + return type == StreamType.Game ? this.channels[0] : this.auxiliaryChannels.get(type); } @Nonnull @@ -256,17 +262,26 @@ public abstract class PacketHandler implements IPacketReceiver { return (ToClientPacket)(packet instanceof CachedPacket ? packet : CachedPacket.cache(packet)); } - public void disconnect(@Nonnull String message) { + public void disconnect(@Nonnull Message message) { + this.disconnect(message.getFormattedMessage()); + } + + public void disconnect(@Nonnull FormattedMessage message) { this.disconnectReason.setServerDisconnectReason(message); String sni = this.getSniHostname(); HytaleLogger.getLogger() .at(Level.INFO) - .log("Disconnecting %s (SNI: %s) with the message: %s", NettyUtil.formatRemoteAddress(this.getChannel()), sni, message); + .log( + "Disconnecting %s (SNI: %s) with the message: %s", + NettyUtil.formatRemoteAddress(this.getChannel()), + sni, + MessageUtil.formatMessageToPlainString(message) + ); this.disconnect0(message); } - protected void disconnect0(@Nonnull String message) { - this.getChannel().writeAndFlush(new Disconnect(message, DisconnectType.Disconnect)).addListener(ProtocolUtil.CLOSE_ON_COMPLETE); + protected void disconnect0(@Nonnull FormattedMessage message) { + this.getChannel().writeAndFlush(new ServerDisconnect(message, DisconnectType.Disconnect)).addListener(ProtocolUtil.CLOSE_ON_COMPLETE); } @Nullable @@ -357,7 +372,7 @@ public abstract class PacketHandler implements IPacketReceiver { HytaleLogger.getLogger() .at(Level.WARNING) .log("Stage timeout for %s at stage '%s' after %s connected", this.getIdentifier(), stageId, duration); - this.disconnect("Either you took too long to login or we took too long to process your request! Retry again in a moment."); + this.disconnect(Message.translation("client.general.disconnect.stageTimeout")); } } }, @@ -433,6 +448,12 @@ public abstract class PacketHandler implements IPacketReceiver { return this.getChannel() instanceof QuicStreamChannel quicStreamChannel ? quicStreamChannel.parent().attr(QUICTransport.SNI_HOSTNAME_ATTR).get() : null; } + public boolean checkStreamOpenRateLimit() { + long now = System.nanoTime(); + long prev = this.lastStreamOpenTimeNanos.getAndUpdate(last -> now - last >= STREAM_OPEN_MIN_INTERVAL_NANOS ? now : last); + return now - prev < STREAM_OPEN_MIN_INTERVAL_NANOS; + } + @Nonnull public PacketHandler.DisconnectReason getDisconnectReason() { return this.disconnectReason; @@ -462,6 +483,43 @@ public abstract class PacketHandler implements IPacketReceiver { this.channels[networkChannel.getValue()] = channel; } + public void setChannel(@Nonnull StreamType type, @Nullable Channel channel) { + if (type == StreamType.Game) { + throw new IllegalArgumentException("Cannot set Game stream via auxiliary channel API"); + } else { + if (channel != null) { + this.auxiliaryChannels.put(type, channel); + } else { + this.auxiliaryChannels.remove(type); + } + } + } + + public boolean compareAndSetChannel(@Nonnull StreamType type, @Nullable Channel expected, @Nullable Channel newValue) { + if (type == StreamType.Game) { + throw new IllegalArgumentException("Cannot CAS Game stream via auxiliary channel API"); + } else { + synchronized (this.auxiliaryChannels) { + Channel current = this.auxiliaryChannels.get(type); + if (current == expected) { + if (newValue != null) { + this.auxiliaryChannels.put(type, newValue); + } else { + this.auxiliaryChannels.remove(type); + } + + return true; + } else { + return false; + } + } + } + } + + public int getAuxiliaryChannelCount() { + return this.auxiliaryChannels.size(); + } + public static void logConnectionTimings(@Nonnull Channel channel, @Nonnull String message, @Nonnull Level level) { Attribute loginStartAttribute = channel.attr(LOGIN_START_ATTRIBUTE_KEY); long now = System.nanoTime(); @@ -481,7 +539,7 @@ public abstract class PacketHandler implements IPacketReceiver { public static class DisconnectReason { @Nullable - private String serverDisconnectReason; + private FormattedMessage serverDisconnectReason; @Nullable private DisconnectType clientDisconnectType; @@ -490,14 +548,24 @@ public abstract class PacketHandler implements IPacketReceiver { @Nullable public String getServerDisconnectReason() { + return this.serverDisconnectReason != null ? MessageUtil.formatMessageToPlainString(this.serverDisconnectReason) : null; + } + + @Nullable + public FormattedMessage getServerDisconnectReasonFormatted() { return this.serverDisconnectReason; } - public void setServerDisconnectReason(String serverDisconnectReason) { + public void setServerDisconnectReason(@Nullable FormattedMessage serverDisconnectReason) { this.serverDisconnectReason = serverDisconnectReason; this.clientDisconnectType = null; } + @Deprecated + public void setServerDisconnectReason(@Nullable String serverDisconnectReason) { + this.setServerDisconnectReason(serverDisconnectReason != null ? Message.raw(serverDisconnectReason).getFormattedMessage() : null); + } + @Nullable public DisconnectType getClientDisconnectType() { return this.clientDisconnectType; diff --git a/src/com/hypixel/hytale/server/core/io/handlers/InitialPacketHandler.java b/src/com/hypixel/hytale/server/core/io/handlers/InitialPacketHandler.java index 5df409a0..93b2e248 100644 --- a/src/com/hypixel/hytale/server/core/io/handlers/InitialPacketHandler.java +++ b/src/com/hypixel/hytale/server/core/io/handlers/InitialPacketHandler.java @@ -2,15 +2,18 @@ package com.hypixel.hytale.server.core.io.handlers; import com.hypixel.hytale.common.util.java.ManifestUtil; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.ToServerPacket; import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; import com.hypixel.hytale.protocol.packets.auth.ConnectAccept; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnect; import com.hypixel.hytale.protocol.packets.connection.ClientType; import com.hypixel.hytale.protocol.packets.connection.Connect; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; +import com.hypixel.hytale.protocol.packets.connection.QuicApplicationErrorCode; import com.hypixel.hytale.server.core.Constants; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.HytaleServerConfig; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.Options; import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.io.ProtocolVersion; @@ -18,7 +21,6 @@ import com.hypixel.hytale.server.core.io.handlers.login.AuthenticationPacketHand import com.hypixel.hytale.server.core.io.handlers.login.PasswordPacketHandler; import com.hypixel.hytale.server.core.io.netty.NettyUtil; import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule; -import com.hypixel.hytale.server.core.plugin.PluginManager; import io.netty.channel.Channel; import io.netty.handler.codec.quic.QuicStreamChannel; import java.security.SecureRandom; @@ -55,14 +57,14 @@ public class InitialPacketHandler extends PacketHandler { if (packet.getId() == 0) { this.handle((Connect)packet); } else if (packet.getId() == 1) { - this.handle((Disconnect)packet); + this.handle((ClientDisconnect)packet); } else { - this.disconnect("Protocol error: unexpected packet " + packet.getId()); + this.disconnect(Message.translation("client.general.disconnect.protocol.unexpectedPacket").param("packetId", packet.getId())); } } @Override - public void disconnect(@Nonnull String message) { + public void disconnect(@Nonnull FormattedMessage message) { if (this.receivedConnect) { super.disconnect(message); } else { @@ -75,22 +77,16 @@ public class InitialPacketHandler extends PacketHandler { this.receivedConnect = true; this.clearTimeout(); PacketHandler.logConnectionTimings(this.getChannel(), "Connect", Level.FINE); - if (packet.protocolCrc != -1356075132) { + if (packet.protocolCrc != 1608127164) { int clientBuild = packet.protocolBuildNumber; - int serverBuild = 20; - int errorCode; - if (clientBuild < serverBuild) { - errorCode = 5; - } else { - errorCode = 6; - } - + int serverBuild = 63; + QuicApplicationErrorCode errorCode = clientBuild < serverBuild ? QuicApplicationErrorCode.ClientOutdated : QuicApplicationErrorCode.ServerOutdated; String serverVersion = ManifestUtil.getImplementationVersion(); ProtocolUtil.closeApplicationConnection(this.getChannel(), errorCode, serverVersion != null ? serverVersion : "unknown"); } else if (HytaleServer.get().isShuttingDown()) { - this.disconnect("Server is shutting down!"); + this.disconnect(Message.translation("client.general.disconnect.serverShuttingDown")); } else if (!HytaleServer.get().isBooted()) { - this.disconnect("Server is booting up! Please try again in a moment. [" + PluginManager.get().getState() + "]"); + this.disconnect(Message.translation("client.general.disconnect.serverBooting")); } else { ProtocolVersion protocolVersion = new ProtocolVersion(packet.protocolCrc); String language = packet.language; @@ -106,26 +102,26 @@ public class InitialPacketHandler extends PacketHandler { } if (packet.uuid == null) { - this.disconnect("Missing UUID"); + this.disconnect(Message.translation("client.general.disconnect.missingUuid")); } else if (packet.username != null && !packet.username.isEmpty()) { if (packet.referralData != null && packet.referralData.length > 4096) { HytaleLogger.getLogger() .at(Level.WARNING) .log("Rejecting connection from %s - referral data too large: %d bytes (max: %d)", packet.username, packet.referralData.length, 4096); - this.disconnect("Referral data exceeds maximum size of 4096 bytes"); + this.disconnect(Message.translation("client.general.disconnect.referralDataTooLarge").param("maxSize", 4096)); } else { if (packet.referralData != null) { if (packet.referralSource == null) { HytaleLogger.getLogger() .at(Level.WARNING) .log("Rejecting connection from %s - referral data provided without source address", packet.username); - this.disconnect("Referral connections must include source server address"); + this.disconnect(Message.translation("client.general.disconnect.referralMissingSource")); return; } if (packet.referralSource.host == null || packet.referralSource.host.isEmpty()) { HytaleLogger.getLogger().at(Level.WARNING).log("Rejecting connection from %s - referral source has empty host", packet.username); - this.disconnect("Referral source address is invalid"); + this.disconnect(Message.translation("client.general.disconnect.referralInvalidSource")); return; } } @@ -138,13 +134,13 @@ public class InitialPacketHandler extends PacketHandler { HytaleLogger.getLogger() .at(Level.WARNING) .log("Rejecting authenticated connection from %s - TCP only supports insecure auth", NettyUtil.formatRemoteAddress(this.getChannel())); - this.disconnect("TCP connections only support insecure authentication. Use QUIC for authenticated connections."); + this.disconnect(Message.translation("client.general.disconnect.tcpAuthenticationNotSupported")); return; } AuthenticationPacketHandler.AuthHandlerSupplier supplier = isEditorClient ? EDITOR_PACKET_HANDLER_SUPPLIER : SetupPacketHandler::new; if (isEditorClient && supplier == null) { - this.disconnect("Editor isn't supported on this server!"); + this.disconnect(Message.translation("client.general.disconnect.editorNotSupported")); return; } @@ -175,7 +171,7 @@ public class InitialPacketHandler extends PacketHandler { NettyUtil.formatRemoteAddress(this.getChannel()), authMode ); - this.disconnect("This server requires authentication!"); + this.disconnect(Message.translation("client.general.disconnect.serverRequiresAuthentication")); return; } @@ -184,7 +180,7 @@ public class InitialPacketHandler extends PacketHandler { HytaleLogger.getLogger() .at(Level.WARNING) .log("Rejecting connection from %s - offline mode is only valid in singleplayer", NettyUtil.formatRemoteAddress(this.getChannel())); - this.disconnect("Offline mode is only available in singleplayer."); + this.disconnect(Message.translation("client.general.disconnect.offlineModeSingleplayerOnly")); return; } @@ -197,7 +193,7 @@ public class InitialPacketHandler extends PacketHandler { packet.uuid, SingleplayerModule.getUuid() ); - this.disconnect("This world is in offline mode and only the owner can connect."); + this.disconnect(Message.translation("client.general.disconnect.offlineModeOwnerOnly")); return; } } @@ -227,7 +223,7 @@ public class InitialPacketHandler extends PacketHandler { } } } else { - this.disconnect("Missing username"); + this.disconnect(Message.translation("client.general.disconnect.missingUsername")); } } } @@ -250,7 +246,7 @@ public class InitialPacketHandler extends PacketHandler { } } - public void handle(@Nonnull Disconnect packet) { + public void handle(@Nonnull ClientDisconnect packet) { this.disconnectReason.setClientDisconnectType(packet.type); HytaleLogger.getLogger().at(Level.WARNING).log("Disconnecting %s - Sent disconnect packet???", NettyUtil.formatRemoteAddress(this.getChannel())); ProtocolUtil.closeApplicationConnection(this.getChannel()); diff --git a/src/com/hypixel/hytale/server/core/io/handlers/SetupPacketHandler.java b/src/com/hypixel/hytale/server/core/io/handlers/SetupPacketHandler.java index 1d67535f..fd479ecd 100644 --- a/src/com/hypixel/hytale/server/core/io/handlers/SetupPacketHandler.java +++ b/src/com/hypixel/hytale/server/core/io/handlers/SetupPacketHandler.java @@ -10,7 +10,7 @@ import com.hypixel.hytale.protocol.HostAddress; import com.hypixel.hytale.protocol.ToServerPacket; import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; import com.hypixel.hytale.protocol.packets.auth.ClientReferral; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnect; import com.hypixel.hytale.protocol.packets.connection.DisconnectType; import com.hypixel.hytale.protocol.packets.interface_.ServerInfo; import com.hypixel.hytale.protocol.packets.setup.PlayerOptions; @@ -49,6 +49,7 @@ import java.util.logging.Level; import javax.annotation.Nonnull; public class SetupPacketHandler extends GenericConnectionPacketHandler { + @Nonnull private final UUID uuid; private final String username; private final byte[] referralData; @@ -57,7 +58,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { private boolean receivedRequest; private int clientViewRadiusChunks = 6; - public SetupPacketHandler(@Nonnull Channel channel, @Nonnull ProtocolVersion protocolVersion, String language, UUID uuid, String username) { + public SetupPacketHandler(@Nonnull Channel channel, @Nonnull ProtocolVersion protocolVersion, String language, @Nonnull UUID uuid, String username) { this(channel, protocolVersion, language, uuid, username, null, null); } @@ -65,7 +66,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { @Nonnull Channel channel, @Nonnull ProtocolVersion protocolVersion, String language, - UUID uuid, + @Nonnull UUID uuid, String username, byte[] referralData, HostAddress referralSource @@ -133,7 +134,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { HytaleLogger.getLogger().at(Level.INFO).log("Found match of player %s on %s", this.uuid, otherPlayer.getUsername()); Channel otherPlayerChannel = otherPlayer.getPacketHandler().getChannel(); if (!NettyUtil.isFromSameOrigin(otherPlayerChannel, this.getChannel())) { - this.disconnect("You are already logged in on that account!"); + this.disconnect(Message.translation("client.general.disconnect.alreadyLoggedIn")); otherPlayer.sendMessage(Message.translation("server.io.setuppackethandler.otherLoginAttempt")); return; } @@ -144,12 +145,12 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { if (world != null) { CompletableFuture removalFuture = new CompletableFuture<>(); world.execute(() -> { - otherPlayer.getPacketHandler().disconnect("You logged in again with the account!"); + otherPlayer.getPacketHandler().disconnect(Message.translation("server.general.disconnect.loggedInAgain")); world.execute(() -> removalFuture.complete(null)); }); removalFuture.join(); } else { - otherPlayer.getPacketHandler().disconnect("You logged in again with the account!"); + otherPlayer.getPacketHandler().disconnect(Message.translation("server.general.disconnect.loggedInAgain")); } } } @@ -162,7 +163,9 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { worldSettings.requiredAssets = requiredAssets; this.write(worldSettings); HytaleServerConfig serverConfig = HytaleServer.get().getConfig(); - this.write(new ServerInfo(HytaleServer.get().getServerName(), serverConfig.getMotd(), serverConfig.getMaxPlayers())); + this.write( + new ServerInfo(HytaleServer.get().getServerName(), serverConfig.getMotd(), serverConfig.getMaxPlayers(), serverConfig.getFallbackServer()) + ); this.continueStage("setup:assets-request", timeouts.getSetupAssetsRequest(), () -> this.receivedRequest); } } @@ -172,7 +175,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { public void accept(@Nonnull ToServerPacket packet) { switch (packet.getId()) { case 1: - this.handle((Disconnect)packet); + this.handle((ClientDisconnect)packet); break; case 23: this.handle((RequestAssets)packet); @@ -184,7 +187,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { this.handle((PlayerOptions)packet); break; default: - this.disconnect("Protocol error: unexpected packet " + packet.getId()); + this.disconnect(Message.translation("client.general.disconnect.protocol.unexpectedPacket").param("packetId", packet.getId())); } } @@ -204,13 +207,17 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { HytaleServer.get().shutdownServer(); } else if (SingleplayerModule.isOwner(this.auth, this.uuid)) { HytaleLogger.getLogger().at(Level.INFO).log("Owner left the singleplayer server shutting down!"); - Universe.get().getPlayers().forEach(p -> p.getPacketHandler().disconnect(this.username + " left! Shutting down singleplayer world!")); + Universe.get() + .getPlayers() + .forEach( + p -> p.getPacketHandler().disconnect(Message.translation("server.general.disconnect.singleplayerOwnerLeft").param("username", this.username)) + ); HytaleServer.get().shutdownServer(); } } } - public void handle(@Nonnull Disconnect packet) { + public void handle(@Nonnull ClientDisconnect packet) { this.disconnectReason.setClientDisconnectType(packet.type); HytaleLogger.getLogger() .at(Level.INFO) @@ -220,7 +227,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { this.username, NettyUtil.formatRemoteAddress(this.getChannel()), packet.type.name(), - packet.reason + packet.reason.name() ); ProtocolUtil.closeApplicationConnection(this.getChannel()); if (packet.type == DisconnectType.Crash @@ -256,7 +263,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { if (!this.getChannel().isActive()) { return null; } else { - this.disconnect("An exception occurred while trying to login!"); + this.disconnect(Message.translation("client.general.disconnect.loginException")); throw new RuntimeException("Exception when player was joining", throwable); } }) @@ -280,9 +287,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { try { CosmeticsModule.get().validateSkin(packet.skin); } catch (CosmeticsModule.InvalidSkinException var4) { - String msg = "Your skin contains parts that aren't available on this server.\nThis usually happens when assets are out of sync.\n\n" - + var4.getMessage(); - this.disconnect(msg); + this.disconnect(Message.translation("client.general.disconnect.invalidSkin").param("details", var4.getMessage())); return; } } @@ -302,7 +307,7 @@ public class SetupPacketHandler extends GenericConnectionPacketHandler { if (!this.getChannel().isActive()) { return null; } else { - this.disconnect("An exception occurred when adding to the universe!"); + this.disconnect(Message.translation("client.general.disconnect.universeException")); throw new RuntimeException("Exception when player adding to universe", throwable); } }) diff --git a/src/com/hypixel/hytale/server/core/io/handlers/game/GamePacketHandler.java b/src/com/hypixel/hytale/server/core/io/handlers/game/GamePacketHandler.java index 6656d802..95d092c5 100644 --- a/src/com/hypixel/hytale/server/core/io/handlers/game/GamePacketHandler.java +++ b/src/com/hypixel/hytale/server/core/io/handlers/game/GamePacketHandler.java @@ -6,22 +6,26 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.protocol.BlockRotation; +import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.HostAddress; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; import com.hypixel.hytale.protocol.packets.camera.RequestFlyCameraMode; import com.hypixel.hytale.protocol.packets.camera.SetFlyCameraMode; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnect; import com.hypixel.hytale.protocol.packets.connection.Pong; import com.hypixel.hytale.protocol.packets.entities.MountMovement; +import com.hypixel.hytale.protocol.packets.entities.PlayEmote; +import com.hypixel.hytale.protocol.packets.interaction.CancelInteractionChain; import com.hypixel.hytale.protocol.packets.interaction.SyncInteractionChain; import com.hypixel.hytale.protocol.packets.interaction.SyncInteractionChains; +import com.hypixel.hytale.protocol.packets.interface_.ArgValuesRequest; +import com.hypixel.hytale.protocol.packets.interface_.ArgValuesResponse; import com.hypixel.hytale.protocol.packets.interface_.ChatMessage; import com.hypixel.hytale.protocol.packets.interface_.CustomPageEvent; import com.hypixel.hytale.protocol.packets.interface_.Page; @@ -60,7 +64,13 @@ import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; import com.hypixel.hytale.server.core.auth.PlayerAuthentication; import com.hypixel.hytale.server.core.command.system.CommandManager; +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType; +import com.hypixel.hytale.server.core.command.system.suggestion.SuggestionResult; import com.hypixel.hytale.server.core.console.ConsoleModule; +import com.hypixel.hytale.server.core.cosmetics.CosmeticsModule; +import com.hypixel.hytale.server.core.cosmetics.Emote; +import com.hypixel.hytale.server.core.cosmetics.EmoteAsset; +import com.hypixel.hytale.server.core.entity.AnimationUtils; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.PageManager; import com.hypixel.hytale.server.core.entity.entities.player.windows.ValidatedWindow; @@ -107,9 +117,11 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.net.InetSocketAddress; -import java.util.Collections; +import java.util.Collection; import java.util.Deque; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedDeque; @@ -117,14 +129,16 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Supplier; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class GamePacketHandler extends GenericPacketHandler implements IPacketHandler { private static final double RELATIVE_POSITION_DELTA_SCALE = 10000.0; + private static final int MAX_INTERACTION_QUEUE_SIZE = 1000; private PlayerRef playerRef; - @Deprecated - private Player playerComponent; @Nonnull private final Deque interactionPacketQueue = new ConcurrentLinkedDeque<>(); + private final Map lastArgValuesRequestTimes = new HashMap<>(); public GamePacketHandler(@Nonnull Channel channel, @Nonnull ProtocolVersion protocolVersion, @Nonnull PlayerAuthentication auth) { super(channel, protocolVersion); @@ -144,9 +158,8 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa return this.playerRef; } - public void setPlayerRef(@Nonnull PlayerRef playerRef, @Nonnull Player playerComponent) { + public void setPlayerRef(@Nonnull PlayerRef playerRef) { this.playerRef = playerRef; - this.playerComponent = playerComponent; } @Nonnull @@ -163,13 +176,24 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa protected void registered0(PacketHandler oldHandler) { HytaleServerConfig.TimeoutProfile timeouts = HytaleServer.get().getConfig().getConnectionTimeouts(); this.enterStage("play", timeouts.getPlay()); + this.sendCommandTree(); + } + + public void sendCommandTree() { + if (this.playerRef != null) { + Ref ref = this.playerRef.getReference(); + if (ref != null && ref.isValid()) { + this.write(CommandManager.get().buildCommandTree(this.playerRef)); + } + } } protected void registerHandlers() { - this.registerHandler(1, p -> this.handle((Disconnect)p)); - this.registerHandler(3, p -> this.handlePong((Pong)p)); + this.registerHandler(1, p -> this.handle((ClientDisconnect)p)); + this.registerHandler(4, p -> this.handlePong((Pong)p)); this.registerHandler(108, p -> this.handle((ClientMovement)p)); this.registerHandler(211, p -> this.handle((ChatMessage)p)); + this.registerHandler(239, p -> this.handle((ArgValuesRequest)p)); this.registerHandler(23, p -> this.handle((RequestAssets)p)); this.registerHandler(219, p -> this.handle((CustomPageEvent)p)); IWorldPacketHandler.registerHandler(this, 32, this::handleViewRadius); @@ -184,6 +208,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa IWorldPacketHandler.registerHandler(this, 262, this::handleUpdateMachinimaScene); this.registerHandler(105, p -> this.handle((ClientReady)p)); IWorldPacketHandler.registerHandler(this, 166, this::handleMountMovement); + IWorldPacketHandler.registerHandler(this, 167, this::handlePlayEmote); IWorldPacketHandler.registerHandler(this, 116, this::handleSyncPlayerPreferences); IWorldPacketHandler.registerHandler(this, 117, this::handleClientPlaceBlock); IWorldPacketHandler.registerHandler(this, 119, this::handleRemoveMapMarker); @@ -207,7 +232,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa } @Override - public void disconnect(@Nonnull String message) { + public void disconnect(@Nonnull FormattedMessage message) { this.disconnectReason.setServerDisconnectReason(message); if (this.playerRef != null) { HytaleLogger.getLogger() @@ -217,7 +242,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa this.playerRef.getUsername(), NettyUtil.formatRemoteAddress(this.getChannel()), this.getSniHostname(), - message + MessageUtil.formatMessageToPlainString(message) ); this.disconnect0(message); Universe.get().removePlayer(this.playerRef); @@ -226,7 +251,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa } } - public void handle(@Nonnull Disconnect packet) { + public void handle(@Nonnull ClientDisconnect packet) { this.disconnectReason.setClientDisconnectType(packet.type); HytaleLogger.getLogger() .at(Level.INFO) @@ -236,7 +261,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa this.playerRef.getUsername(), NettyUtil.formatRemoteAddress(this.getChannel()), packet.type.name(), - packet.reason + packet.reason.name() ); ProtocolUtil.closeApplicationConnection(this.getChannel()); } @@ -253,7 +278,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa public void handle(@Nonnull ClientMovement packet) { if (packet.absolutePosition != null && !ValidateUtil.isSafePosition(packet.absolutePosition)) { - this.disconnect("Sent impossible position data!"); + this.disconnect(Message.translation("server.general.disconnect.impossiblePosition")); } else if ((packet.bodyOrientation == null || ValidateUtil.isSafeDirection(packet.bodyOrientation)) && (packet.lookOrientation == null || ValidateUtil.isSafeDirection(packet.lookOrientation))) { Ref ref = this.playerRef.getReference(); @@ -279,68 +304,62 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa } PendingTeleport pendingTeleport = store.getComponent(ref, PendingTeleport.getComponentType()); - if (pendingTeleport != null) { - if (packet.teleportAck == null) { - return; - } + label76: + if (pendingTeleport == null) { + if (packet.mountedTo == playerInputComponent.getMountId()) { + if (packet.mountedTo != 0 && packet.riderMovementStates != null) { + playerInputComponent.queue(new PlayerInput.SetRiderMovementStates(packet.riderMovementStates)); + } + if (packet.bodyOrientation != null) { + playerInputComponent.queue(new PlayerInput.SetBody(packet.bodyOrientation)); + } + + if (packet.lookOrientation != null) { + playerInputComponent.queue(new PlayerInput.SetHead(packet.lookOrientation)); + } + + if (packet.wishMovement != null) { + playerInputComponent.queue( + new PlayerInput.WishMovement(packet.wishMovement.x, packet.wishMovement.y, packet.wishMovement.z) + ); + } + + if (packet.absolutePosition != null) { + playerInputComponent.queue( + new PlayerInput.AbsoluteMovement(packet.absolutePosition.x, packet.absolutePosition.y, packet.absolutePosition.z) + ); + } else if (packet.relativePosition != null + && ( + packet.relativePosition.x != 0 + || packet.relativePosition.y != 0 + || packet.relativePosition.z != 0 + || packet.movementStates != null + )) { + playerInputComponent.queue( + new PlayerInput.RelativeMovement( + packet.relativePosition.x / 10000.0, packet.relativePosition.y / 10000.0, packet.relativePosition.z / 10000.0 + ) + ); + } + } + } else if (packet.teleportAck != null) { switch (pendingTeleport.validate(packet.teleportAck.teleportId, packet.absolutePosition)) { case OK: default: if (!pendingTeleport.isEmpty()) { return; + } else { + store.removeComponent(ref, PendingTeleport.getComponentType()); + break label76; } - - store.removeComponent(ref, PendingTeleport.getComponentType()); - break; case INVALID_ID: - this.disconnect("Incorrect teleportId"); + this.disconnect(Message.translation("server.general.disconnect.incorrectTeleportId")); return; case INVALID_POSITION: - this.disconnect("Invalid teleport"); - return; + this.disconnect(Message.translation("server.general.disconnect.invalidTeleport")); } } - - if (packet.mountedTo != 0) { - if (packet.mountedTo != playerInputComponent.getMountId()) { - return; - } - - if (packet.riderMovementStates != null) { - playerInputComponent.queue(new PlayerInput.SetRiderMovementStates(packet.riderMovementStates)); - } - } - - if (packet.bodyOrientation != null) { - playerInputComponent.queue(new PlayerInput.SetBody(packet.bodyOrientation)); - } - - if (packet.lookOrientation != null) { - playerInputComponent.queue(new PlayerInput.SetHead(packet.lookOrientation)); - } - - if (packet.wishMovement != null) { - playerInputComponent.queue(new PlayerInput.WishMovement(packet.wishMovement.x, packet.wishMovement.y, packet.wishMovement.z)); - } - - if (packet.absolutePosition != null) { - playerInputComponent.queue( - new PlayerInput.AbsoluteMovement(packet.absolutePosition.x, packet.absolutePosition.y, packet.absolutePosition.z) - ); - } else if (packet.relativePosition != null - && ( - packet.relativePosition.x != 0 - || packet.relativePosition.y != 0 - || packet.relativePosition.z != 0 - || packet.movementStates != null - )) { - playerInputComponent.queue( - new PlayerInput.RelativeMovement( - packet.relativePosition.x / 10000.0, packet.relativePosition.y / 10000.0, packet.relativePosition.z / 10000.0 - ) - ); - } } } } @@ -348,16 +367,25 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa ); } } else { - this.disconnect("Sent impossible orientation data!"); + this.disconnect(Message.translation("server.general.disconnect.impossibleOrientation")); } } public void handle(@Nonnull ChatMessage packet) { - if (packet.message != null && !packet.message.isEmpty()) { + if (packet.message == null || packet.message.isEmpty()) { + this.disconnect(Message.translation("server.general.disconnect.invalidChatMessage")); + } else if (MessageUtil.containsControlCharacters(packet.message)) { + this.playerRef.sendMessage(Message.translation("server.io.gamepackethandler.invalidMessageContent").param("msg", packet.message)); + } else { String message = packet.message; char firstChar = message.charAt(0); if (firstChar == '/') { - CommandManager.get().handleCommand(this.playerComponent, message.substring(1)); + Ref ref = this.playerRef.getReference(); + if (ref == null || !ref.isValid()) { + return; + } + + CommandManager.get().handleCommand(this.playerRef, message.substring(1)); } else if (firstChar == '.') { this.playerRef.sendMessage(Message.translation("server.io.gamepackethandler.localCommandDenied").param("msg", message)); } else { @@ -367,8 +395,15 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa } UUID playerUUID = this.playerRef.getUuid(); - List targetPlayerRefs = new ObjectArrayList<>(Universe.get().getPlayers()); - targetPlayerRefs.removeIf(targetPlayerRef -> targetPlayerRef.getHiddenPlayersManager().isPlayerHidden(playerUUID)); + Collection players = Universe.get().getPlayers(); + List targetPlayerRefs = new ObjectArrayList<>(players.size()); + + for (PlayerRef targetPlayerRef : players) { + if (!targetPlayerRef.getHiddenPlayersManager().isPlayerHidden(playerUUID)) { + targetPlayerRefs.add(targetPlayerRef); + } + } + HytaleServer.get() .getEventBus() .dispatchForAsync(PlayerChatEvent.class) @@ -384,18 +419,53 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa Message sentMessage = playerChatEvent.getFormatter().format(this.playerRef, playerChatEvent.getContent()); HytaleLogger.getLogger().at(Level.INFO).log(MessageUtil.toAnsiString(sentMessage).toAnsi(ConsoleModule.get().getTerminal())); - for (PlayerRef targetPlayerRef : playerChatEvent.getTargets()) { - targetPlayerRef.sendMessage(sentMessage); + for (PlayerRef targetPlayerRefx : playerChatEvent.getTargets()) { + targetPlayerRefx.sendMessage(sentMessage); } } } ); } - } else { - this.disconnect("Invalid chat message packet! Message was empty."); } } + public void handle(@Nonnull ArgValuesRequest packet) { + if (packet.argTypeId != null && packet.argTypeId.length() <= 128) { + if (packet.partial == null || packet.partial.length() <= 256) { + long now = System.currentTimeMillis(); + if (now - this.lastArgValuesRequestTimes.getOrDefault(packet.argTypeId, 0L) >= 50L) { + this.lastArgValuesRequestTimes.put(packet.argTypeId, now); + ArgumentType argType = CommandManager.get().getArgTypeById(packet.argTypeId); + if (argType != null) { + if (this.playerRef != null) { + Ref ref = this.playerRef.getReference(); + if (ref != null && ref.isValid()) { + SuggestionResult result = new SuggestionResult(); + argType.suggest(this.playerRef, packet.partial != null ? packet.partial : "", 0, result); + ArgValuesResponse response = new ArgValuesResponse(); + response.argTypeId = packet.argTypeId; + response.values = result.getSuggestions().toArray(new String[0]); + response.continuations = toBooleanArray(result.getContinuations()); + response.isComplete = (packet.partial == null || packet.partial.isEmpty()) && argType.getSuggestionValueCount() >= 0; + this.write(response); + } + } + } + } + } + } + } + + private static boolean[] toBooleanArray(@Nonnull List list) { + boolean[] arr = new boolean[list.size()]; + + for (int i = 0; i < arr.length; i++) { + arr[i] = list.get(i); + } + + return arr; + } + public void handle(@Nonnull RequestAssets packet) { CommonAssetModule.get().sendAssetsToPlayer(this, packet.assets, true); } @@ -528,7 +598,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa if (transformComponent != null && playerComponent.getGameMode() != GameMode.Creative) { Vector3d position = transformComponent.getPosition(); Vector3d blockCenter = new Vector3d(targetBlock.x + 0.5, targetBlock.y + 0.5, targetBlock.z + 0.5); - if (position.distanceSquaredTo(blockCenter) > 49.0) { + if (position.distanceSquared(blockCenter) > 49.0) { section.invalidateBlock(targetBlock.x, targetBlock.y, targetBlock.z); return; } @@ -555,7 +625,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa itemInHand, heldBlockKey, inventory.getHotbar(), - Vector3i.ZERO, + new Vector3i(), targetBlock, blockRotation, inventory, @@ -563,7 +633,8 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa playerComponent.getGameMode() != GameMode.Creative, chunkReference, chunkStore, - store + store, + packet.quickReplace ); } } @@ -675,8 +746,6 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa Player playerComponent = store.getComponent(ref, Player.getComponentType()); assert playerComponent != null; - - playerComponent.getWorldMapTracker().setClientHasWorldMapVisible(packet.visible); } public void handleTeleportToWorldMapMarker( @@ -692,17 +761,17 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa WorldMapTracker worldMapTracker = playerComponent.getWorldMapTracker(); if (!worldMapTracker.isAllowTeleportToMarkers()) { - this.disconnect("You are not allowed to use TeleportToWorldMapMarker!"); + playerRef.sendMessage(Message.translation("server.general.disconnect.teleportToMarkersNotAllowed")); } else { MapMarker marker = worldMapTracker.getSentMarkers().get(packet.id); if (marker != null) { Transform transform = PositionUtil.toTransform(marker.transform); if (MapMarkerUtils.isUserMarker(marker)) { - int blockX = (int)transform.getPosition().getX(); - int blockZ = (int)transform.getPosition().getZ(); + int blockX = (int)transform.getPosition().x(); + int blockZ = (int)transform.getPosition().z(); WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(blockX, blockZ)); int height = chunk == null ? 319 : chunk.getHeight(blockX, blockZ); - transform.getPosition().setY(height); + transform.getPosition().y = height; } Teleport teleportComponent = Teleport.createForPlayer(transform); @@ -724,7 +793,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa WorldMapTracker worldMapTracker = playerComponent.getWorldMapTracker(); if (!worldMapTracker.isAllowTeleportToCoordinates()) { - this.disconnect("You are not allowed to use TeleportToWorldMapMarker!"); + playerRef.sendMessage(Message.translation("server.general.disconnect.teleportToCoordinatesNotAllowed")); } else { world.getChunkStore().getChunkReferenceAsync(ChunkUtil.indexChunkFromBlock(packet.x, packet.y)).thenAcceptAsync(chunkRef -> { BlockChunk blockChunkComponent = world.getChunkStore().getStore().getComponent((Ref)chunkRef, BlockChunk.getComponentType()); @@ -732,7 +801,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa assert blockChunkComponent != null; Vector3d position = new Vector3d(packet.x, blockChunkComponent.getHeight(packet.x, packet.y) + 2, packet.y); - Teleport teleportComponent = Teleport.createForPlayer(null, position, new Vector3f(0.0F, 0.0F, 0.0F)); + Teleport teleportComponent = Teleport.createForPlayer(null, position, new Rotation3f(0.0F, 0.0F, 0.0F)); world.getEntityStore().getStore().addComponent(playerRef.getReference(), Teleport.getComponentType(), teleportComponent); }, world); } @@ -746,7 +815,31 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa } public void handle(@Nonnull SyncInteractionChains packet) { - Collections.addAll(this.interactionPacketQueue, packet.updates); + int capacity = 1000 - this.interactionPacketQueue.size(); + int accepted = Math.clamp((long)capacity, 0, packet.updates.length); + + for (int i = 0; i < accepted; i++) { + this.interactionPacketQueue.add(packet.updates[i]); + } + + if (accepted < packet.updates.length) { + int dropped = packet.updates.length - accepted; + HytaleLogger.getLogger() + .at(Level.WARNING) + .log( + "Dropped %d interaction updates from %s (queue full at %d)", + dropped, + this.playerRef != null ? this.playerRef.getUuid() : "unknown", + this.interactionPacketQueue.size() + ); + + for (int i = accepted; i < packet.updates.length; i++) { + SyncInteractionChain update = packet.updates[i]; + if (update.initial) { + this.write(new CancelInteractionChain(update.chainId, update.forkedId)); + } + } + } } public void handleMountMovement( @@ -756,19 +849,40 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa assert playerComponent != null; - Ref entityReference = world.getEntityStore().getRefFromNetworkId(playerComponent.getMountEntityId()); - if (entityReference != null && entityReference.isValid()) { - TransformComponent transformComponent = store.getComponent(entityReference, TransformComponent.getComponentType()); + if (playerComponent != null) { + Ref entityReference = world.getEntityStore().getRefFromNetworkId(playerComponent.getMountEntityId()); + if (entityReference != null && entityReference.isValid()) { + if (packet.absolutePosition != null && packet.bodyOrientation != null && packet.movementStates != null) { + TransformComponent transformComponent = store.getComponent(entityReference, TransformComponent.getComponentType()); - assert transformComponent != null; + assert transformComponent != null; - transformComponent.setPosition(PositionUtil.toVector3d(packet.absolutePosition)); - transformComponent.setRotation(PositionUtil.toRotation(packet.bodyOrientation)); - MovementStatesComponent movementStatesComponent = store.getComponent(entityReference, MovementStatesComponent.getComponentType()); + if (transformComponent != null) { + transformComponent.setPosition(PositionUtil.toVector3d(packet.absolutePosition)); + transformComponent.setRotation(PositionUtil.toRotation(packet.bodyOrientation)); + MovementStatesComponent movementStatesComponent = store.getComponent(entityReference, MovementStatesComponent.getComponentType()); - assert movementStatesComponent != null; + assert movementStatesComponent != null; - movementStatesComponent.setMovementStates(packet.movementStates); + if (movementStatesComponent != null) { + movementStatesComponent.setMovementStates(packet.movementStates); + } + } + } + } + } + } + + public void handlePlayEmote( + @Nonnull PlayEmote packet, @Nonnull PlayerRef playerRef, @Nonnull Ref ref, @Nonnull World world, @Nonnull Store store + ) { + if (packet.emoteId != null && !packet.emoteId.isEmpty()) { + Map builtinEmotes = CosmeticsModule.get().getRegistry().getEmotesInGame(); + if (builtinEmotes.get(packet.emoteId) != null || EmoteAsset.getAssetMap().getAsset(packet.emoteId) != null) { + AnimationUtils.playAnimation(ref, AnimationSlot.Emote, null, packet.emoteId, true, store); + } + } else { + AnimationUtils.stopAnimation(ref, AnimationSlot.Emote, true, store); } } @@ -787,11 +901,7 @@ public class GamePacketHandler extends GenericPacketHandler implements IPacketHa @Nonnull World world, @Nonnull Store store ) { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - - if (playerComponent.hasPermission("hytale.camera.flycam")) { + if (playerRef.hasPermission("hytale.camera.flycam")) { this.writeNoCache(new SetFlyCameraMode(packet.entering)); if (packet.entering) { playerRef.sendMessage(Message.translation("server.general.flyCamera.enabled")); diff --git a/src/com/hypixel/hytale/server/core/io/handlers/game/InventoryPacketHandler.java b/src/com/hypixel/hytale/server/core/io/handlers/game/InventoryPacketHandler.java index c3c0a7ce..bb41a223 100644 --- a/src/com/hypixel/hytale/server/core/io/handlers/game/InventoryPacketHandler.java +++ b/src/com/hypixel/hytale/server/core/io/handlers/game/InventoryPacketHandler.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.server.core.io.handlers.game; +import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; @@ -30,6 +31,7 @@ import com.hypixel.hytale.server.core.entity.entities.player.windows.Window; import com.hypixel.hytale.server.core.event.events.ecs.DropItemEvent; import com.hypixel.hytale.server.core.event.events.ecs.SwitchActiveSlotEvent; import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; @@ -96,7 +98,8 @@ public class InventoryPacketHandler implements SubPacketHandler { if (quantity > 0) { ItemStack itemStack = ItemStack.fromPacket(packet.item); if (packet.slotId < 0) { - ItemStackTransaction transaction = inventory.getCombinedHotbarFirst().addItemStack(itemStack); + CombinedItemContainer combinedInventory = InventoryComponent.getCombined(store, ref, InventoryComponent.HOTBAR_FIRST); + ItemStackTransaction transaction = combinedInventory.addItemStack(itemStack); ItemStack remainder = transaction.getRemainder(); if (remainder != null && !remainder.isEmpty()) { ItemUtils.dropItem(ref, remainder, store); @@ -170,11 +173,14 @@ public class InventoryPacketHandler implements SubPacketHandler { assert playerComponent != null; - Inventory inventory = playerComponent.getInventory(); - byte slot = inventory.getActiveHotbarSlot(); - if (slot != -1) { - ItemContainer hotbar = inventory.getHotbar(); - ItemStack stack = hotbar.getItemStack(slot); + InventoryComponent.Hotbar hotbarComponent = store.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + + assert hotbarComponent != null; + + ItemContainer hotbar = hotbarComponent.getInventory(); + byte activeHotbarSlot = hotbarComponent.getActiveSlot(); + if (activeHotbarSlot != -1) { + ItemStack stack = hotbar.getItemStack(activeHotbarSlot); if (stack != null && !stack.isEmpty()) { BlockGroup set = BlockGroup.findItemGroup(stack.getItem()); if (set != null) { @@ -186,7 +192,9 @@ public class InventoryPacketHandler implements SubPacketHandler { if (desiredIndex != -1 && desiredIndex != currentIndex) { ItemStack maxSelectorTool = null; short maxSlot = -1; - CombinedItemContainer combinedInventory = inventory.getCombinedArmorHotbarUtilityStorage(); + CombinedItemContainer combinedInventory = InventoryComponent.getCombined( + store, ref, InventoryComponent.ARMOR_HOTBAR_UTILITY_STORAGE + ); for (short i = 0; i < combinedInventory.getCapacity(); i++) { ItemStack potentialSelector = combinedInventory.getItemStack(i); @@ -203,14 +211,14 @@ public class InventoryPacketHandler implements SubPacketHandler { if (maxSelectorTool != null) { BlockSelectorToolData toolData = maxSelectorTool.getItem().getBlockSelectorToolData(); - if (playerComponent.canDecreaseItemStackDurability(ref, store) && !maxSelectorTool.isUnbreakable()) { + if (ItemUtils.canDecreaseItemStackDurability(ref, store) && !maxSelectorTool.isUnbreakable()) { playerComponent.updateItemStackDurability( ref, maxSelectorTool, combinedInventory, maxSlot, -toolData.getDurabilityLossOnUse(), store ); } ItemStack replacement = new ItemStack(set.get(desiredIndex), stack.getQuantity()); - hotbar.setItemStackForSlot(slot, replacement); + hotbar.setItemStackForSlot(activeHotbarSlot, replacement); ItemSoundSet soundSet = ItemSoundSet.getAssetMap().getAsset(desiredItem.getItemSoundSetIndex()); if (soundSet != null) { String dragSound = soundSet.getSoundEventIds().get(ItemSoundEvent.Drop); @@ -273,7 +281,7 @@ public class InventoryPacketHandler implements SubPacketHandler { if (ItemStack.isEmpty(remainder) || remainder.getQuantity() != quantity) { for (ItemStackSlotTransaction slotTransaction : transaction.getSlotTransactions()) { if (slotTransaction.succeeded()) { - inventory.setActiveUtilitySlot((byte)slotTransaction.getSlot()); + inventory.setActiveUtilitySlot(ref, (byte)slotTransaction.getSlot(), store); } } } @@ -324,7 +332,17 @@ public class InventoryPacketHandler implements SubPacketHandler { ItemUtils.throwItem(ref, item, 6.0F, store); SoundUtil.playSoundEvent2d(ref, TempAssetIdUtil.getSoundEventIndex("SFX_Player_Drop_Item"), SoundCategory.UI, store); } else { - playerComponent.sendInventory(); + ComponentType type = InventoryComponent.getComponentTypeById(packet.inventorySectionId); + if (type == null) { + return; + } + + InventoryComponent inv = store.getComponent(ref, type); + if (inv == null) { + return; + } + + inv.markDirty(); } } ); @@ -359,7 +377,7 @@ public class InventoryPacketHandler implements SubPacketHandler { } newSlot = event.getNewSlot(); - inventory.setActiveSlot(inventorySectionId, newSlot); + Inventory.setActiveSlot(ref, inventorySectionId, newSlot, store); playerRef.getPacketHandler().writeNoCache(new SetActiveSlot(inventorySectionId, newSlot)); } }); @@ -383,7 +401,7 @@ public class InventoryPacketHandler implements SubPacketHandler { settings = PlayerSettings.defaults(); } - inventory.smartMoveItem(packet.fromSectionId, packet.fromSlotId, packet.quantity, packet.moveType, settings); + inventory.smartMoveItem(ref, packet.fromSectionId, packet.fromSlotId, packet.quantity, packet.moveType, settings, store); }); } } @@ -394,39 +412,43 @@ public class InventoryPacketHandler implements SubPacketHandler { if (ref != null && ref.isValid()) { Store store = ref.getStore(); World world = store.getExternalData().getWorld(); - world.execute(() -> { - Player playerComponent = store.getComponent(ref, Player.getComponentType()); + world.execute( + () -> { + Player playerComponent = store.getComponent(ref, Player.getComponentType()); - assert playerComponent != null; + assert playerComponent != null; - Inventory inventory = playerComponent.getInventory(); - PacketHandler packetHandler = playerRef.getPacketHandler(); - if (packet.inventorySectionId == -1) { - packetHandler.disconnect("Attempted to change the hotbar slot without an interaction"); - } else if (packet.activeSlot < -1 || packet.activeSlot >= inventory.getSectionById(packet.inventorySectionId).getCapacity()) { - packetHandler.disconnect("Attempted to set " + packet.inventorySectionId + " slot outside of range!"); - } else if (packet.activeSlot == inventory.getActiveSlot(packet.inventorySectionId)) { - packetHandler.disconnect("Attempted to set hotbar slot to already selected hotbar slot!"); - } else { - byte previousSlot = inventory.getActiveSlot(packet.inventorySectionId); - byte targetSlot = (byte)packet.activeSlot; - SwitchActiveSlotEvent event = new SwitchActiveSlotEvent(packet.inventorySectionId, previousSlot, targetSlot, false); - store.invoke(ref, event); - if (event.isCancelled()) { - targetSlot = previousSlot; - } else if (targetSlot != event.getNewSlot()) { - targetSlot = event.getNewSlot(); - } + Inventory inventory = playerComponent.getInventory(); + PacketHandler packetHandler = playerRef.getPacketHandler(); + if (packet.inventorySectionId == -1) { + packetHandler.disconnect(Message.translation("server.general.disconnect.hotbarChangeWithoutInteraction")); + } else if (packet.activeSlot < -1 || packet.activeSlot >= inventory.getSectionById(packet.inventorySectionId).getCapacity()) { + packetHandler.disconnect( + Message.translation("server.general.disconnect.hotbarSlotOutOfRange").param("inventorySectionId", packet.inventorySectionId) + ); + } else if (packet.activeSlot == inventory.getActiveSlot(packet.inventorySectionId)) { + packetHandler.disconnect(Message.translation("server.general.disconnect.hotbarSlotAlreadySelected")); + } else { + byte previousSlot = inventory.getActiveSlot(packet.inventorySectionId); + byte targetSlot = (byte)packet.activeSlot; + SwitchActiveSlotEvent event = new SwitchActiveSlotEvent(packet.inventorySectionId, previousSlot, targetSlot, false); + store.invoke(ref, event); + if (event.isCancelled()) { + targetSlot = previousSlot; + } else if (targetSlot != event.getNewSlot()) { + targetSlot = event.getNewSlot(); + } - if (targetSlot != packet.activeSlot) { - packetHandler.writeNoCache(new SetActiveSlot(packet.inventorySectionId, targetSlot)); - } + if (targetSlot != packet.activeSlot) { + packetHandler.writeNoCache(new SetActiveSlot(packet.inventorySectionId, targetSlot)); + } - if (targetSlot != previousSlot) { - inventory.setActiveSlot(packet.inventorySectionId, targetSlot); + if (targetSlot != previousSlot) { + Inventory.setActiveSlot(ref, packet.inventorySectionId, targetSlot, store); + } } } - }); + ); } } @@ -452,7 +474,7 @@ public class InventoryPacketHandler implements SubPacketHandler { switch (packet.inventoryActionType) { case TakeAll: if (packet.inventorySectionId == -9) { - inventory.takeAll(packet.inventorySectionId, settings); + inventory.takeAll(ref, packet.inventorySectionId, settings, store); return; } @@ -462,10 +484,10 @@ public class InventoryPacketHandler implements SubPacketHandler { if (itemContainerWindow.getItemContainer() instanceof CombinedItemContainer combinedItemContainer && combinedItemContainer.getContainersSize() >= 3) { ItemContainer outputContainer = combinedItemContainer.getContainer(2); - inventory.takeAllWithPriority(outputContainer, settings); + Inventory.takeAllWithPriority(ref, outputContainer, settings, store); } } else { - inventory.takeAll(packet.inventorySectionId, settings); + inventory.takeAll(ref, packet.inventorySectionId, settings, store); } } break; @@ -482,27 +504,26 @@ public class InventoryPacketHandler implements SubPacketHandler { break; case QuickStack: if (packet.inventorySectionId == -9) { - inventory.quickStack(packet.inventorySectionId); + inventory.quickStack(ref, packet.inventorySectionId, store); return; } Window window = playerComponent.getWindowManager().getWindow(packet.inventorySectionId); if (window instanceof ItemContainerWindow) { - inventory.quickStack(packet.inventorySectionId); + inventory.quickStack(ref, packet.inventorySectionId, store); } break; case Sort: - SortType sortType = SortType.VALUES[packet.actionData]; if (packet.inventorySectionId == 0) { - inventory.sortStorage(sortType); + inventory.sortStorage(); } else { if (packet.inventorySectionId == -9 && inventory.getBackpack() != null) { - inventory.getBackpack().sortItems(sortType); + inventory.getBackpack().sortItems(SortType.TYPE); return; } if (playerComponent.getWindowManager().getWindow(packet.inventorySectionId) instanceof ItemContainerWindow itemContainerWindow) { - itemContainerWindow.getItemContainer().sortItems(sortType); + itemContainerWindow.getItemContainer().sortItems(SortType.TYPE); } } } diff --git a/src/com/hypixel/hytale/server/core/io/handlers/login/AuthenticationPacketHandler.java b/src/com/hypixel/hytale/server/core/io/handlers/login/AuthenticationPacketHandler.java index 56a9bcf2..296af7c8 100644 --- a/src/com/hypixel/hytale/server/core/io/handlers/login/AuthenticationPacketHandler.java +++ b/src/com/hypixel/hytale/server/core/io/handlers/login/AuthenticationPacketHandler.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.server.core.io.handlers.login; import com.hypixel.hytale.protocol.HostAddress; import com.hypixel.hytale.protocol.packets.connection.ClientType; import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.auth.PlayerAuthentication; import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.io.ProtocolVersion; @@ -43,7 +44,7 @@ public class AuthenticationPacketHandler extends HandshakeHandler { public void registered0(PacketHandler oldHandler) { int maxPlayers = HytaleServer.get().getConfig().getMaxPlayers(); if (maxPlayers > 0 && Universe.get().getPlayerCount() >= maxPlayers) { - this.disconnect("Too many players!"); + this.disconnect(Message.translation("client.general.disconnect.serverFull")); } else { super.registered0(oldHandler); } diff --git a/src/com/hypixel/hytale/server/core/io/handlers/login/HandshakeHandler.java b/src/com/hypixel/hytale/server/core/io/handlers/login/HandshakeHandler.java index c9e366fb..44e87678 100644 --- a/src/com/hypixel/hytale/server/core/io/handlers/login/HandshakeHandler.java +++ b/src/com/hypixel/hytale/server/core/io/handlers/login/HandshakeHandler.java @@ -7,11 +7,12 @@ import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; import com.hypixel.hytale.protocol.packets.auth.AuthGrant; import com.hypixel.hytale.protocol.packets.auth.AuthToken; import com.hypixel.hytale.protocol.packets.auth.ServerAuthToken; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnect; import com.hypixel.hytale.protocol.packets.connection.ClientType; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; import com.hypixel.hytale.server.core.Constants; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.HytaleServerConfig; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.auth.AuthConfig; import com.hypixel.hytale.server.core.auth.JWTValidator; import com.hypixel.hytale.server.core.auth.PlayerAuthentication; @@ -93,13 +94,13 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { public void accept(@Nonnull ToServerPacket packet) { switch (packet.getId()) { case 1: - this.handle((Disconnect)packet); + this.handle((ClientDisconnect)packet); break; case 12: this.handle((AuthToken)packet); break; default: - this.disconnect("Protocol error: unexpected packet " + packet.getId()); + this.disconnect(Message.translation("client.general.disconnect.protocol.unexpectedPacket").param("packetId", packet.getId())); } } @@ -110,7 +111,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { JWTValidator.IdentityTokenClaims identityClaims = getJwtValidator().validateIdentityToken(this.identityToken); if (identityClaims == null) { LOGGER.at(Level.WARNING).log("Identity token validation failed for %s from %s", this.username, NettyUtil.formatRemoteAddress(this.getChannel())); - this.disconnect("Invalid or expired identity token"); + this.disconnect(Message.translation("client.general.disconnect.invalidIdentityToken")); } else { UUID tokenUuid = identityClaims.getSubjectAsUUID(); if (tokenUuid != null && tokenUuid.equals(this.playerUuid)) { @@ -125,7 +126,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { requiredScope, identityClaims.scope ); - this.disconnect("Invalid identity token: missing " + requiredScope + " scope"); + this.disconnect(Message.translation("client.general.disconnect.identityTokenMissingScope").param("scope", requiredScope)); } else { LOGGER.at(Level.INFO) .log( @@ -147,7 +148,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { this.playerUuid, tokenUuid ); - this.disconnect("Invalid identity token: UUID mismatch"); + this.disconnect(Message.translation("client.general.disconnect.identityTokenUuidMismatch")); } } } @@ -162,7 +163,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { authGrant -> { if (channel.isActive()) { if (authGrant == null) { - channel.eventLoop().execute(() -> this.disconnect("Failed to obtain authorization grant from session service")); + channel.eventLoop().execute(() -> this.disconnect(Message.translation("client.general.disconnect.authGrantFailed"))); } else { String serverIdentityToken = ServerAuthManager.getInstance().getIdentityToken(); if (serverIdentityToken != null && !serverIdentityToken.isEmpty()) { @@ -192,7 +193,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { ); } else { LOGGER.at(Level.SEVERE).log("Server identity token not available - cannot complete mutual authentication"); - channel.eventLoop().execute(() -> this.disconnect("Server authentication unavailable - please try again later")); + channel.eventLoop().execute(() -> this.disconnect(Message.translation("client.general.disconnect.serverAuthUnavailable"))); } } } @@ -200,16 +201,16 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { ) .exceptionally(ex -> { LOGGER.at(Level.WARNING).withCause(ex).log("Error requesting auth grant"); - channel.eventLoop().execute(() -> this.disconnect("Authentication error: " + ex.getMessage())); + channel.eventLoop().execute(() -> this.disconnect(Message.translation("client.general.disconnect.authError"))); return null; }); } else { LOGGER.at(Level.SEVERE).log("Server session token not available - cannot request auth grant"); - this.disconnect("Server authentication unavailable - please try again later"); + this.disconnect(Message.translation("client.general.disconnect.serverAuthUnavailable")); } } - public void handle(@Nonnull Disconnect packet) { + public void handle(@Nonnull ClientDisconnect packet) { this.disconnectReason.setClientDisconnectType(packet.type); LOGGER.at(Level.INFO) .log( @@ -218,7 +219,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { this.username, NettyUtil.formatRemoteAddress(this.getChannel()), packet.type.name(), - packet.reason + packet.reason.name() ); ProtocolUtil.closeApplicationConnection(this.getChannel()); } @@ -227,10 +228,10 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { Channel channel = this.getChannel(); if (this.authState != HandshakeHandler.AuthState.AWAITING_AUTH_TOKEN) { LOGGER.at(Level.WARNING).log("Received unexpected AuthToken packet in state %s from %s", this.authState, NettyUtil.formatRemoteAddress(channel)); - this.disconnect("Protocol error: unexpected AuthToken packet"); + this.disconnect(Message.translation("client.general.disconnect.protocol.unexpectedAuthToken")); } else if (this.authTokenPacketReceived) { LOGGER.at(Level.WARNING).log("Received duplicate AuthToken packet from %s", NettyUtil.formatRemoteAddress(channel)); - this.disconnect("Protocol error: duplicate AuthToken packet"); + this.disconnect(Message.translation("client.general.disconnect.protocol.duplicateAuthToken")); } else { this.authTokenPacketReceived = true; this.authState = HandshakeHandler.AuthState.PROCESSING_AUTH_TOKEN; @@ -249,21 +250,21 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { JWTValidator.JWTClaims claims = getJwtValidator().validateToken(accessToken, clientCert); if (claims == null) { LOGGER.at(Level.WARNING).log("JWT validation failed for %s", NettyUtil.formatRemoteAddress(channel)); - this.disconnect("Invalid access token"); + this.disconnect(Message.translation("client.general.disconnect.invalidAccessToken")); } else { UUID tokenUuid = claims.getSubjectAsUUID(); String tokenUsername = claims.username; if (tokenUuid == null || !tokenUuid.equals(this.playerUuid)) { LOGGER.at(Level.WARNING) .log("JWT UUID mismatch for %s (expected: %s, got: %s)", NettyUtil.formatRemoteAddress(channel), this.playerUuid, tokenUuid); - this.disconnect("Invalid token claims: UUID mismatch"); + this.disconnect(Message.translation("client.general.disconnect.tokenUuidMismatch")); } else if (tokenUsername == null || tokenUsername.isEmpty()) { LOGGER.at(Level.WARNING).log("JWT missing username for %s", NettyUtil.formatRemoteAddress(channel)); - this.disconnect("Invalid token claims: missing username"); + this.disconnect(Message.translation("client.general.disconnect.tokenMissingUsername")); } else if (!tokenUsername.equals(this.username)) { LOGGER.at(Level.WARNING) .log("JWT username mismatch for %s (expected: %s, got: %s)", NettyUtil.formatRemoteAddress(channel), this.username, tokenUsername); - this.disconnect("Invalid token claims: username mismatch"); + this.disconnect(Message.translation("client.general.disconnect.tokenUsernameMismatch")); } else { this.authenticatedUsername = tokenUsername; if (serverAuthGrant != null && !serverAuthGrant.isEmpty()) { @@ -275,13 +276,13 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { this.exchangeServerAuthGrant(serverAuthGrant); } else { LOGGER.at(Level.WARNING).log("Client did not provide server auth grant for mutual authentication"); - this.disconnect("Mutual authentication required - please update your client"); + this.disconnect(Message.translation("client.general.disconnect.mutualAuthRequired")); } } } } else { LOGGER.at(Level.WARNING).log("Received AuthToken packet with empty access token from %s", NettyUtil.formatRemoteAddress(channel)); - this.disconnect("Invalid access token"); + this.disconnect(Message.translation("client.general.disconnect.invalidAccessToken")); } } } @@ -291,7 +292,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { String serverCertFingerprint = serverAuthManager.getServerCertificateFingerprint(); if (serverCertFingerprint == null) { LOGGER.at(Level.SEVERE).log("Server certificate fingerprint not available for mutual auth"); - this.disconnect("Server authentication unavailable - please try again later"); + this.disconnect(Message.translation("client.general.disconnect.serverAuthUnavailable")); } else { String serverSessionToken = serverAuthManager.getSessionToken(); LOGGER.at(Level.FINE) @@ -305,7 +306,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { serverAuthManager.hasSessionToken(), serverAuthManager.hasIdentityToken() ); - this.disconnect("Server authentication unavailable - please try again later"); + this.disconnect(Message.translation("client.general.disconnect.serverAuthUnavailable")); } else { LOGGER.at(Level.FINE) .log("Using session token (first 20 chars): %s...", serverSessionToken.length() > 20 ? serverSessionToken.substring(0, 20) : serverSessionToken); @@ -323,7 +324,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { LOGGER.at(Level.WARNING).log("State changed during server token exchange, current state: %s", this.authState); } else if (serverAccessToken == null) { LOGGER.at(Level.SEVERE).log("Failed to exchange server auth grant for access token"); - this.disconnect("Server authentication failed - please try again later"); + this.disconnect(Message.translation("client.general.disconnect.serverAuthFailed")); } else { byte[] passwordChallenge = this.generatePasswordChallengeIfNeeded(); LOGGER.at(Level.INFO) @@ -345,7 +346,7 @@ public abstract class HandshakeHandler extends GenericConnectionPacketHandler { LOGGER.at(Level.WARNING).withCause(ex).log("Error exchanging server auth grant"); channel.eventLoop().execute(() -> { if (this.authState == HandshakeHandler.AuthState.EXCHANGING_SERVER_TOKEN) { - this.disconnect("Server authentication failed - please try again later"); + this.disconnect(Message.translation("client.general.disconnect.serverAuthFailed")); } }); return null; diff --git a/src/com/hypixel/hytale/server/core/io/handlers/login/PasswordPacketHandler.java b/src/com/hypixel/hytale/server/core/io/handlers/login/PasswordPacketHandler.java index f89a71b0..17a9879e 100644 --- a/src/com/hypixel/hytale/server/core/io/handlers/login/PasswordPacketHandler.java +++ b/src/com/hypixel/hytale/server/core/io/handlers/login/PasswordPacketHandler.java @@ -7,9 +7,10 @@ import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; import com.hypixel.hytale.protocol.packets.auth.PasswordAccepted; import com.hypixel.hytale.protocol.packets.auth.PasswordRejected; import com.hypixel.hytale.protocol.packets.auth.PasswordResponse; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; +import com.hypixel.hytale.protocol.packets.connection.ClientDisconnect; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.HytaleServerConfig; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.auth.PlayerAuthentication; import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.io.ProtocolVersion; @@ -79,17 +80,17 @@ public class PasswordPacketHandler extends GenericConnectionPacketHandler { public void accept(@Nonnull ToServerPacket packet) { switch (packet.getId()) { case 1: - this.handle((Disconnect)packet); + this.handle((ClientDisconnect)packet); break; case 15: this.handle((PasswordResponse)packet); break; default: - this.disconnect("Protocol error: unexpected packet " + packet.getId()); + this.disconnect(Message.translation("client.general.disconnect.protocol.unexpectedPacket").param("packetId", packet.getId())); } } - public void handle(@Nonnull Disconnect packet) { + public void handle(@Nonnull ClientDisconnect packet) { this.disconnectReason.setClientDisconnectType(packet.type); LOGGER.at(Level.INFO) .log( @@ -98,7 +99,7 @@ public class PasswordPacketHandler extends GenericConnectionPacketHandler { this.username, NettyUtil.formatRemoteAddress(this.getChannel()), packet.type.name(), - packet.reason + packet.reason.name() ); ProtocolUtil.closeApplicationConnection(this.getChannel()); } @@ -113,7 +114,7 @@ public class PasswordPacketHandler extends GenericConnectionPacketHandler { byte[] expectedHash = computePasswordHash(this.passwordChallenge, password); if (expectedHash == null) { LOGGER.at(Level.SEVERE).log("Failed to compute password hash"); - this.disconnect("Server error"); + this.disconnect(Message.translation("client.general.disconnect.serverError")); } else if (!MessageDigest.isEqual(expectedHash, clientHash)) { this.attemptsRemaining--; LOGGER.at(Level.WARNING) @@ -124,7 +125,7 @@ public class PasswordPacketHandler extends GenericConnectionPacketHandler { this.attemptsRemaining ); if (this.attemptsRemaining <= 0) { - this.disconnect("Too many failed password attempts"); + this.disconnect(Message.translation("client.general.disconnect.tooManyPasswordAttempts")); } else { this.passwordChallenge = generateChallenge(); this.write(new PasswordRejected(this.passwordChallenge, this.attemptsRemaining)); @@ -138,15 +139,15 @@ public class PasswordPacketHandler extends GenericConnectionPacketHandler { } } else { LOGGER.at(Level.SEVERE).log("Password validation failed - no password configured but challenge was sent"); - this.disconnect("Server configuration error"); + this.disconnect(Message.translation("client.general.disconnect.serverConfigError")); } } else { LOGGER.at(Level.WARNING).log("Received empty password hash from %s", NettyUtil.formatRemoteAddress(this.getChannel())); - this.disconnect("Invalid password response"); + this.disconnect(Message.translation("client.general.disconnect.invalidPasswordResponse")); } } else { LOGGER.at(Level.WARNING).log("Received unexpected PasswordResponse from %s - no password required", NettyUtil.formatRemoteAddress(this.getChannel())); - this.disconnect("Protocol error: unexpected PasswordResponse"); + this.disconnect(Message.translation("client.general.disconnect.protocol.unexpectedPasswordResponse")); } } diff --git a/src/com/hypixel/hytale/server/core/io/netty/HytaleChannelInitializer.java b/src/com/hypixel/hytale/server/core/io/netty/HytaleChannelInitializer.java index 03f4e20c..ca3f5ff1 100644 --- a/src/com/hypixel/hytale/server/core/io/netty/HytaleChannelInitializer.java +++ b/src/com/hypixel/hytale/server/core/io/netty/HytaleChannelInitializer.java @@ -2,16 +2,22 @@ package com.hypixel.hytale.server.core.io.netty; import com.hypixel.hytale.common.util.FormatUtil; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.io.PacketStatsRecorder; import com.hypixel.hytale.protocol.io.netty.PacketDecoder; import com.hypixel.hytale.protocol.io.netty.PacketEncoder; import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; import com.hypixel.hytale.protocol.packets.connection.DisconnectType; +import com.hypixel.hytale.protocol.packets.connection.QuicApplicationErrorCode; +import com.hypixel.hytale.protocol.packets.connection.ServerDisconnect; import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.config.RateLimitConfig; +import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.io.PacketStatsRecorderImpl; import com.hypixel.hytale.server.core.io.handlers.InitialPacketHandler; +import com.hypixel.hytale.server.core.io.stream.PendingStreamHandler; +import com.hypixel.hytale.server.core.io.stream.StreamManager; import com.hypixel.hytale.server.core.io.transport.QUICTransport; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; @@ -21,8 +27,10 @@ import io.netty.channel.ChannelInitializer; import io.netty.handler.codec.quic.QuicChannel; import io.netty.handler.codec.quic.QuicStreamChannel; import io.netty.handler.timeout.ReadTimeoutException; +import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.handler.timeout.TimeoutException; import io.netty.handler.timeout.WriteTimeoutException; +import io.netty.util.AttributeKey; import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.security.cert.X509Certificate; @@ -33,17 +41,25 @@ import java.util.logging.Level; import javax.annotation.Nonnull; public class HytaleChannelInitializer extends ChannelInitializer { + public static final AttributeKey GAME_PACKET_HANDLER_ATTR = AttributeKey.valueOf("GAME_PACKET_HANDLER"); + @Override protected void initChannel(Channel channel) { if (channel instanceof QuicStreamChannel quicStreamChannel) { - HytaleLogger.getLogger().at(Level.INFO).log("Received stream %s to %s", NettyUtil.formatRemoteAddress(channel), NettyUtil.formatLocalAddress(channel)); QuicChannel parentChannel = quicStreamChannel.parent(); - Integer rejectErrorCode = parentChannel.attr(QUICTransport.ALPN_REJECT_ERROR_CODE_ATTR).get(); + HytaleLogger.getLogger() + .at(Level.INFO) + .log( + "Received stream %d from %s to %s", quicStreamChannel.streamId(), NettyUtil.formatRemoteAddress(channel), NettyUtil.formatLocalAddress(channel) + ); + QuicApplicationErrorCode rejectErrorCode = parentChannel.attr(QUICTransport.ALPN_REJECT_ERROR_CODE_ATTR).get(); if (rejectErrorCode != null) { HytaleLogger.getLogger().at(Level.INFO).log("Rejecting stream from %s: client outdated (ALPN mismatch)", NettyUtil.formatRemoteAddress(channel)); channel.config().setAutoRead(false); channel.pipeline().addLast("packetEncoder", new PacketEncoder()); - channel.writeAndFlush(new Disconnect("Your game client needs to be updated.", DisconnectType.Disconnect)) + channel.writeAndFlush( + new ServerDisconnect(Message.translation("server.general.disconnect.clientOutdated").getFormattedMessage(), DisconnectType.Disconnect) + ) .addListener( future -> channel.eventLoop().schedule(() -> ProtocolUtil.closeApplicationConnection(channel, rejectErrorCode), 100L, TimeUnit.MILLISECONDS) ); @@ -55,6 +71,13 @@ public class HytaleChannelInitializer extends ChannelInitializer { channel.attr(QUICTransport.CLIENT_CERTIFICATE_ATTR).set(clientCert); HytaleLogger.getLogger().at(Level.FINE).log("Copied client certificate to stream: %s", clientCert.getSubjectX500Principal().getName()); } + + PacketHandler existingHandler = parentChannel.attr(GAME_PACKET_HANDLER_ATTR).get(); + if (existingHandler != null) { + HytaleLogger.getLogger().at(Level.INFO).log("Setting up auxiliary stream %d for %s", quicStreamChannel.streamId(), existingHandler.getIdentifier()); + this.initAuxiliaryStream(channel, existingHandler); + return; + } } else { HytaleLogger.getLogger() .at(Level.INFO) @@ -92,14 +115,31 @@ public class HytaleChannelInitializer extends ChannelInitializer { InitialPacketHandler playerConnection = new InitialPacketHandler(channel); channel.pipeline().addLast("handler", new PlayerChannelHandler(playerConnection)); channel.pipeline().addLast(new HytaleChannelInitializer.ExceptionHandler()); + if (channel instanceof QuicStreamChannel quicStreamChannel) { + quicStreamChannel.parent().attr(GAME_PACKET_HANDLER_ATTR).set(playerConnection); + quicStreamChannel.updatePriority(StreamManager.GAME_STREAM_PRIORITY); + } + playerConnection.registered(null); } + private void initAuxiliaryStream(Channel channel, PacketHandler packetHandler) { + channel.pipeline().addLast("aux_read_timeout", new ReadTimeoutHandler(5L, TimeUnit.SECONDS)); + channel.pipeline().addLast("packetDecoder", new PacketDecoder()); + channel.pipeline().addLast("packetEncoder", new PacketEncoder()); + channel.pipeline().addLast("pending_stream", new PendingStreamHandler(packetHandler)); + channel.pipeline().addLast(new HytaleChannelInitializer.AuxiliaryStreamExceptionHandler(packetHandler.getIdentifier())); + } + @Override public void exceptionCaught(@Nonnull ChannelHandlerContext ctx, Throwable cause) { HytaleLogger.getLogger().at(Level.WARNING).withCause(cause).log("Got exception from netty pipeline in HytaleChannelInitializer!"); if (ctx.channel().isWritable()) { - ctx.channel().writeAndFlush(new Disconnect("Internal server error!", DisconnectType.Crash)).addListener(ProtocolUtil.CLOSE_ON_COMPLETE); + ctx.channel() + .writeAndFlush( + new ServerDisconnect(Message.translation("server.general.disconnect.internalServerError").getFormattedMessage(), DisconnectType.Crash) + ) + .addListener(ProtocolUtil.CLOSE_ON_COMPLETE); } else { ProtocolUtil.closeApplicationConnection(ctx.channel()); } @@ -111,8 +151,33 @@ public class HytaleChannelInitializer extends ChannelInitializer { super.channelInactive(ctx); } - private static class ExceptionHandler extends ChannelInboundHandlerAdapter { + private static class AuxiliaryStreamExceptionHandler extends ChannelInboundHandlerAdapter { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + private final String identifier; + + AuxiliaryStreamExceptionHandler(String identifier) { + this.identifier = identifier; + } + + @Override + public void exceptionCaught(@Nonnull ChannelHandlerContext ctx, Throwable cause) { + if (!(cause instanceof ClosedChannelException)) { + LOGGER.at(Level.WARNING).withCause(cause).log("Exception in auxiliary stream for %s", this.identifier); + ctx.close(); + } + } + } + + private static class ExceptionHandler extends ChannelInboundHandlerAdapter { + @Nonnull + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + @Nonnull + private static final Message MESSAGE_DISCONNECT_TIMEOUT_READ = Message.translation("server.general.disconnect.timeout.read"); + @Nonnull + private static final Message MESSAGE_DISCONNECT_TIMEOUT_WRITE = Message.translation("server.general.disconnect.timeout.write"); + @Nonnull + private static final Message MESSAGE_DISCONNECT_TIMEOUT_CONNECTION = Message.translation("server.general.disconnect.timeout.connection"); + @Nonnull private final AtomicBoolean handled = new AtomicBoolean(); @Override @@ -142,7 +207,7 @@ public class HytaleChannelInitializer extends ChannelInitializer { this.handleTimeout(ctx, cause, identifier); } else { LOGGER.at(Level.SEVERE).withCause(cause).log("Got exception from netty pipeline in ExceptionHandler: %s", cause.getMessage()); - this.gracefulDisconnect(ctx, identifier, "Internal server error!"); + this.gracefulDisconnect(ctx, identifier, Message.translation("server.general.disconnect.internalServerError").getFormattedMessage()); } } } @@ -151,6 +216,9 @@ public class HytaleChannelInitializer extends ChannelInitializer { boolean readTimeout = cause instanceof ReadTimeoutException; boolean writeTimeout = cause instanceof WriteTimeoutException; String timeoutType = readTimeout ? "Read" : (writeTimeout ? "Write" : "Connection"); + Message timeoutMessage = readTimeout + ? MESSAGE_DISCONNECT_TIMEOUT_READ + : (writeTimeout ? MESSAGE_DISCONNECT_TIMEOUT_WRITE : MESSAGE_DISCONNECT_TIMEOUT_CONNECTION); NettyUtil.TimeoutContext context = ctx.channel().attr(NettyUtil.TimeoutContext.KEY).get(); String stage = context != null ? context.stage() : "unknown"; String duration = context != null ? FormatUtil.nanosToString(System.nanoTime() - context.connectionStartNs()) : "unknown"; @@ -159,21 +227,22 @@ public class HytaleChannelInitializer extends ChannelInitializer { .at(Level.FINE) .withCause(cause) .log("%s timeout for %s at stage '%s' after %s connected", timeoutType, identifier, stage, duration); - this.gracefulDisconnect(ctx, identifier, timeoutType + " timeout"); + this.gracefulDisconnect(ctx, identifier, timeoutMessage.getFormattedMessage()); } - private void gracefulDisconnect(@Nonnull ChannelHandlerContext ctx, String identifier, String reason) { + private void gracefulDisconnect(@Nonnull ChannelHandlerContext ctx, String identifier, FormattedMessage reason) { Channel channel = ctx.channel(); if (channel.isWritable()) { - channel.writeAndFlush(new Disconnect(reason, DisconnectType.Disconnect)).addListener(future -> ProtocolUtil.closeApplicationConnection(channel, 4)); + channel.writeAndFlush(new ServerDisconnect(reason, DisconnectType.Disconnect)) + .addListener(future -> ProtocolUtil.closeApplicationConnection(channel, QuicApplicationErrorCode.Timeout)); channel.eventLoop().schedule(() -> { if (channel.isOpen()) { LOGGER.at(Level.FINE).log("Force closing %s after graceful disconnect attempt", identifier); - ProtocolUtil.closeApplicationConnection(channel, 4); + ProtocolUtil.closeApplicationConnection(channel, QuicApplicationErrorCode.Timeout); } }, 1L, TimeUnit.SECONDS); } else { - ProtocolUtil.closeApplicationConnection(channel, 4); + ProtocolUtil.closeApplicationConnection(channel, QuicApplicationErrorCode.Timeout); } } } diff --git a/src/com/hypixel/hytale/server/core/io/netty/NettyUtil.java b/src/com/hypixel/hytale/server/core/io/netty/NettyUtil.java index 3833d28d..f53b3e41 100644 --- a/src/com/hypixel/hytale/server/core/io/netty/NettyUtil.java +++ b/src/com/hypixel/hytale/server/core/io/netty/NettyUtil.java @@ -3,6 +3,8 @@ package com.hypixel.hytale.server.core.io.netty; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.logger.backend.HytaleLoggerBackend; import com.hypixel.hytale.protocol.NetworkChannel; +import com.hypixel.hytale.protocol.io.netty.PacketDecoder; +import com.hypixel.hytale.protocol.io.netty.PacketEncoder; import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.universe.PlayerRef; @@ -12,6 +14,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFactory; import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.ServerChannel; import io.netty.channel.epoll.Epoll; @@ -73,11 +76,21 @@ public class NettyUtil { } public static void setChannelHandler(@Nonnull Channel channel, @Nonnull PacketHandler packetHandler) { - ChannelHandler oldHandler = channel.pipeline().replace("handler", "handler", new PlayerChannelHandler(packetHandler)); + PlayerChannelHandler newHandler = new PlayerChannelHandler(packetHandler); PacketHandler oldPlayerConnection = null; - if (oldHandler instanceof PlayerChannelHandler) { - oldPlayerConnection = ((PlayerChannelHandler)oldHandler).getHandler(); - oldPlayerConnection.unregistered(packetHandler); + ChannelHandler existingHandler = channel.pipeline().get("handler"); + if (existingHandler != null) { + channel.pipeline().replace("handler", "handler", newHandler); + if (existingHandler instanceof PlayerChannelHandler playerHandler) { + oldPlayerConnection = playerHandler.getHandler(); + oldPlayerConnection.unregistered(packetHandler); + } + } else { + channel.pipeline().addLast("handler", newHandler); + } + + if (channel instanceof QuicStreamChannel quicStreamChannel) { + quicStreamChannel.parent().attr(HytaleChannelInitializer.GAME_PACKET_HANDLER_ATTR).set(packetHandler); } packetHandler.registered(oldPlayerConnection); @@ -92,7 +105,14 @@ public class NettyUtil { @Nonnull PacketHandler packetHandler ) { CompletableFuture future = new CompletableFuture<>(); - conn.createStream(streamType, new HytaleChannelInitializer()).addListener(result -> { + conn.createStream(streamType, new ChannelInitializer() { + @Override + protected void initChannel(@Nonnull Channel ch) { + ch.pipeline().addLast("packetDecoder", new PacketDecoder()); + ch.pipeline().addLast("packetEncoder", new PacketEncoder()); + ch.pipeline().addLast("packetArrayEncoder", NettyUtil.PACKET_ARRAY_ENCODER_INSTANCE); + } + }).addListener(result -> { if (!result.isSuccess()) { future.completeExceptionally(result.cause()); } else { diff --git a/src/com/hypixel/hytale/server/core/io/netty/RateLimitHandler.java b/src/com/hypixel/hytale/server/core/io/netty/RateLimitHandler.java index 27a7ef66..f1559af6 100644 --- a/src/com/hypixel/hytale/server/core/io/netty/RateLimitHandler.java +++ b/src/com/hypixel/hytale/server/core/io/netty/RateLimitHandler.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.server.core.io.netty; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; +import com.hypixel.hytale.protocol.packets.connection.QuicApplicationErrorCode; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.util.logging.Level; @@ -38,7 +39,7 @@ public class RateLimitHandler extends ChannelInboundHandlerAdapter { ctx.fireChannelRead(msg); } else { LOGGER.at(Level.WARNING).log("Rate limit exceeded for %s, disconnecting", NettyUtil.formatRemoteAddress(ctx.channel())); - ProtocolUtil.closeApplicationConnection(ctx.channel(), 1); + ProtocolUtil.closeApplicationConnection(ctx.channel(), QuicApplicationErrorCode.RateLimited); } } } diff --git a/src/com/hypixel/hytale/server/core/io/stream/PendingStreamHandler.java b/src/com/hypixel/hytale/server/core/io/stream/PendingStreamHandler.java new file mode 100644 index 00000000..f7fafaab --- /dev/null +++ b/src/com/hypixel/hytale/server/core/io/stream/PendingStreamHandler.java @@ -0,0 +1,109 @@ +package com.hypixel.hytale.server.core.io.stream; + +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.io.ProtocolException; +import com.hypixel.hytale.protocol.packets.stream.StreamOpen; +import com.hypixel.hytale.protocol.packets.stream.StreamOpenResponse; +import com.hypixel.hytale.protocol.packets.stream.StreamType; +import com.hypixel.hytale.server.core.io.PacketHandler; +import com.hypixel.hytale.server.core.io.netty.NettyUtil; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.quic.QuicStreamChannel; +import io.netty.util.ReferenceCountUtil; +import java.util.logging.Level; +import javax.annotation.Nonnull; + +public class PendingStreamHandler extends ChannelInboundHandlerAdapter { + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + private static final int MAX_AUXILIARY_STREAMS = 4; + private final PacketHandler packetHandler; + private final StreamManager streamManager; + + public PendingStreamHandler(@Nonnull PacketHandler packetHandler) { + this(packetHandler, StreamManager.getInstance()); + } + + public PendingStreamHandler(@Nonnull PacketHandler packetHandler, @Nonnull StreamManager streamManager) { + this.packetHandler = packetHandler; + this.streamManager = streamManager; + } + + @Override + public void channelRead(@Nonnull ChannelHandlerContext ctx, @Nonnull Object msg) { + if (msg instanceof Packet packet) { + if (packet instanceof StreamOpen open) { + StreamType type = open.type; + if (this.packetHandler.checkStreamOpenRateLimit()) { + LOGGER.at(Level.WARNING).log("Stream open rate limited for %s requesting %s", this.packetHandler.getIdentifier(), type.name()); + ctx.writeAndFlush(new StreamOpenResponse(type, false, "Rate limited - try again later")).addListener(future -> ctx.close()); + } else if (type == StreamType.Game) { + LOGGER.at(Level.WARNING).log("Cannot open Game stream - stream 0 is already the game stream, from %s", this.packetHandler.getIdentifier()); + ctx.writeAndFlush(new StreamOpenResponse(type, false, "Game stream cannot be opened explicitly")).addListener(future -> ctx.close()); + } else if (!this.streamManager.isSupported(type)) { + LOGGER.at(Level.INFO).log("Unsupported stream type %s from %s", type.name(), this.packetHandler.getIdentifier()); + ctx.writeAndFlush(new StreamOpenResponse(type, false, "Stream type not supported")).addListener(future -> ctx.close()); + } else { + Channel existingChannel = this.packetHandler.getChannel(type); + if (existingChannel != null) { + LOGGER.at(Level.INFO) + .log( + "Replacing stale %s stream for %s (old channel active=%s)", type.name(), this.packetHandler.getIdentifier(), existingChannel.isActive() + ); + this.packetHandler.setChannel(type, null); + existingChannel.close(); + } + + if (this.packetHandler.getAuxiliaryChannelCount() >= 4) { + LOGGER.at(Level.WARNING).log("Maximum auxiliary streams exceeded for %s requesting %s", this.packetHandler.getIdentifier(), type.name()); + ctx.writeAndFlush(new StreamOpenResponse(type, false, "Maximum auxiliary streams exceeded")).addListener(future -> ctx.close()); + } else { + ChannelHandler handler = this.streamManager.createHandler(type, this.packetHandler); + if (handler == null) { + LOGGER.at(Level.SEVERE).log("Failed to create handler for stream type %s from %s", type.name(), this.packetHandler.getIdentifier()); + ctx.writeAndFlush(new StreamOpenResponse(type, false, "Internal error")).addListener(future -> ctx.close()); + } else { + LOGGER.at(Level.INFO).log("Opening %s stream for %s", type.name(), this.packetHandler.getIdentifier()); + ctx.pipeline().replace(this, type.name() + "Handler", handler); + ctx.pipeline().remove("aux_read_timeout"); + this.packetHandler.setChannel(type, ctx.channel()); + if (ctx.channel() instanceof QuicStreamChannel quicStreamChannel) { + quicStreamChannel.updatePriority(this.streamManager.getStreamPriority(type)); + } + + ctx.writeAndFlush(new StreamOpenResponse(type, true, null)); + } + } + } + } else { + LOGGER.at(Level.WARNING).log("Auxiliary stream first packet was not StreamOpen, closing: %s", packet.getClass().getSimpleName()); + ctx.close(); + } + } else { + LOGGER.at(Level.WARNING) + .log("Expected Packet but got %s on pending stream from %s", msg.getClass().getSimpleName(), NettyUtil.formatRemoteAddress(ctx.channel())); + ReferenceCountUtil.release(msg); + ctx.close(); + } + } + + @Override + public void exceptionCaught(@Nonnull ChannelHandlerContext ctx, @Nonnull Throwable cause) { + if (cause instanceof ProtocolException) { + LOGGER.at(Level.WARNING).log("Protocol error on pending stream for %s: %s", this.packetHandler.getIdentifier(), cause.getMessage()); + } else { + LOGGER.at(Level.WARNING).withCause(cause).log("Exception in pending stream handler for %s", this.packetHandler.getIdentifier()); + } + + ctx.close(); + } + + @Override + public void channelInactive(@Nonnull ChannelHandlerContext ctx) throws Exception { + LOGGER.at(Level.FINE).log("Pending stream closed for %s", this.packetHandler.getIdentifier()); + super.channelInactive(ctx); + } +} diff --git a/src/com/hypixel/hytale/server/core/io/stream/StreamManager.java b/src/com/hypixel/hytale/server/core/io/stream/StreamManager.java new file mode 100644 index 00000000..db331d81 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/io/stream/StreamManager.java @@ -0,0 +1,70 @@ +package com.hypixel.hytale.server.core.io.stream; + +import com.hypixel.hytale.protocol.packets.stream.StreamType; +import com.hypixel.hytale.server.core.io.PacketHandler; +import io.netty.channel.ChannelHandler; +import io.netty.handler.codec.quic.QuicStreamPriority; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class StreamManager { + public static final QuicStreamPriority GAME_STREAM_PRIORITY = new QuicStreamPriority(0, false); + public static final QuicStreamPriority DEFAULT_AUXILIARY_PRIORITY = new QuicStreamPriority(64, false); + private static final StreamManager INSTANCE = new StreamManager(); + private final Map handlers = new ConcurrentHashMap<>(); + + StreamManager() { + } + + @Nonnull + public static StreamManager getInstance() { + return INSTANCE; + } + + public void registerHandler(@Nonnull StreamType type, @Nonnull StreamManager.StreamHandlerFactory factory) { + this.registerHandler(type, factory, DEFAULT_AUXILIARY_PRIORITY); + } + + public void registerHandler(@Nonnull StreamType type, @Nonnull StreamManager.StreamHandlerFactory factory, @Nonnull QuicStreamPriority priority) { + if (type == StreamType.Game) { + throw new IllegalArgumentException("Cannot register handler for Game stream type - it uses the main pipeline"); + } else { + this.handlers.put(type, new StreamManager.StreamRegistration(factory, priority)); + } + } + + public void unregisterHandler(@Nonnull StreamType type) { + this.handlers.remove(type); + } + + public boolean isSupported(@Nonnull StreamType type) { + return type == StreamType.Game || this.handlers.containsKey(type); + } + + @Nullable + public ChannelHandler createHandler(@Nonnull StreamType type, @Nonnull PacketHandler packetHandler) { + StreamManager.StreamRegistration registration = this.handlers.get(type); + return registration != null ? registration.factory().create(packetHandler) : null; + } + + public void clearAll() { + this.handlers.clear(); + } + + @Nonnull + public QuicStreamPriority getStreamPriority(@Nonnull StreamType type) { + StreamManager.StreamRegistration registration = this.handlers.get(type); + return registration != null ? registration.priority() : DEFAULT_AUXILIARY_PRIORITY; + } + + @FunctionalInterface + public interface StreamHandlerFactory { + @Nonnull + ChannelHandler create(@Nonnull PacketHandler var1); + } + + private record StreamRegistration(@Nonnull StreamManager.StreamHandlerFactory factory, @Nonnull QuicStreamPriority priority) { + } +} diff --git a/src/com/hypixel/hytale/server/core/io/transport/QUICTransport.java b/src/com/hypixel/hytale/server/core/io/transport/QUICTransport.java index 40bb2b16..3f6f22a4 100644 --- a/src/com/hypixel/hytale/server/core/io/transport/QUICTransport.java +++ b/src/com/hypixel/hytale/server/core/io/transport/QUICTransport.java @@ -1,10 +1,13 @@ package com.hypixel.hytale.server.core.io.transport; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.io.netty.ProtocolUtil; -import com.hypixel.hytale.protocol.packets.connection.Disconnect; import com.hypixel.hytale.protocol.packets.connection.DisconnectType; +import com.hypixel.hytale.protocol.packets.connection.QuicApplicationErrorCode; +import com.hypixel.hytale.protocol.packets.connection.ServerDisconnect; import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.auth.CertificateUtil; import com.hypixel.hytale.server.core.auth.ServerAuthManager; import com.hypixel.hytale.server.core.io.netty.HytaleChannelInitializer; @@ -41,6 +44,7 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.time.Duration; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.Nonnull; @@ -51,7 +55,7 @@ import jdk.net.ExtendedSocketOptions; public class QUICTransport implements Transport { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); public static final AttributeKey CLIENT_CERTIFICATE_ATTR = AttributeKey.valueOf("CLIENT_CERTIFICATE"); - public static final AttributeKey ALPN_REJECT_ERROR_CODE_ATTR = AttributeKey.valueOf("ALPN_REJECT_ERROR_CODE"); + public static final AttributeKey ALPN_REJECT_ERROR_CODE_ATTR = AttributeKey.valueOf("ALPN_REJECT_ERROR_CODE"); public static final AttributeKey SNI_HOSTNAME_ATTR = AttributeKey.valueOf("SNI_HOSTNAME"); @Nonnull private final EventLoopGroup workerGroup = NettyUtil.getEventLoopGroup("ServerWorkerGroup"); @@ -165,12 +169,16 @@ public class QUICTransport implements Transport { .initialMaxStreamsUnidirectional(0L) .initialMaxStreamDataBidirectionalLocal(131072L) .initialMaxStreamDataBidirectionalRemote(131072L) - .initialMaxStreamsBidirectional(1L) + .initialMaxStreamsBidirectional(8L) .discoverPmtu(true) .congestionControlAlgorithm(QuicCongestionControlAlgorithm.BBR) .option(QuicChannelOption.QLOG, System.getProperty("hytale.qlog") != null ? new QLogConfiguration(".", "hytale-server-quic-qlogs", "") : null) .handler( new ChannelInboundHandlerAdapter() { + { + Objects.requireNonNull(QuicChannelInboundHandlerAdapter.this); + } + @Override public boolean isSharable() { return true; @@ -204,7 +212,7 @@ public class QUICTransport implements Transport { negotiatedAlpn, 2 ); - channel.attr(QUICTransport.ALPN_REJECT_ERROR_CODE_ATTR).set(5); + channel.attr(QUICTransport.ALPN_REJECT_ERROR_CODE_ATTR).set(QuicApplicationErrorCode.ClientOutdated); } X509Certificate clientCert = QuicChannelInboundHandlerAdapter.this.extractClientCertificate(channel); @@ -245,7 +253,8 @@ public class QUICTransport implements Transport { QUICTransport.LOGGER.at(Level.WARNING).withCause(cause).log("Got exception from netty pipeline in ChannelInitializer!"); Channel channel = ctx.channel(); if (channel.isWritable()) { - channel.writeAndFlush(new Disconnect("Internal server error!", DisconnectType.Crash)).addListener(ProtocolUtil.CLOSE_ON_COMPLETE); + FormattedMessage disconnectReason = Message.translation("server.general.disconnect.internalServerError").getFormattedMessage(); + channel.writeAndFlush(new ServerDisconnect(disconnectReason, DisconnectType.Crash)).addListener(ProtocolUtil.CLOSE_ON_COMPLETE); } else { ProtocolUtil.closeApplicationConnection(channel); } diff --git a/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigModule.java b/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigModule.java new file mode 100644 index 00000000..8d4504be --- /dev/null +++ b/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigModule.java @@ -0,0 +1,122 @@ +package com.hypixel.hytale.server.core.liveconfig; + +import com.hypixel.hytale.common.plugin.PluginManifest; +import com.hypixel.hytale.common.util.java.ManifestUtil; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.auth.ServerAuthManager; +import com.hypixel.hytale.server.core.plugin.JavaPlugin; +import com.hypixel.hytale.server.core.plugin.JavaPluginInit; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class LiveConfigModule extends JavaPlugin { + @Nonnull + public static final PluginManifest MANIFEST = PluginManifest.corePlugin(LiveConfigModule.class).build(); + @Nonnull + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + private static final long REFRESH_INTERVAL_SECONDS = 300L; + private static final long INITIAL_DELAY_SECONDS = Long.getLong("liveconfig.initial.delay", 10L); + private static LiveConfigModule instance; + private volatile LiveConfigSnapshot snapshot = LiveConfigSnapshot.EMPTY; + @Nullable + private ScheduledFuture refreshTask; + private final LiveConfigService liveConfigService = new LiveConfigService(); + @Nonnull + private final String patchline; + @Nonnull + private final String os; + @Nonnull + private final String arch; + + public LiveConfigModule(@Nonnull JavaPluginInit init) { + super(init); + instance = this; + this.patchline = resolvePatchline(); + this.os = System.getProperty("liveconfig.os", LiveConfigService.detectOS()); + this.arch = System.getProperty("liveconfig.arch", LiveConfigService.detectArch()); + } + + @Nullable + public static LiveConfigModule get() { + return instance; + } + + public boolean getBoolFlag(@Nonnull String name, boolean defaultValue) { + return this.snapshot.getBoolFlag(name, defaultValue); + } + + public int getIntFlag(@Nonnull String name, int defaultValue) { + return this.snapshot.getIntFlag(name, defaultValue); + } + + @Nonnull + public String getStringFlag(@Nonnull String name, @Nonnull String defaultValue) { + return this.snapshot.getStringFlag(name, defaultValue); + } + + public boolean hasFlag(@Nonnull String name) { + return this.snapshot.hasFlag(name); + } + + @Nonnull + public LiveConfigSnapshot getSnapshot() { + return this.snapshot; + } + + public void forceRefresh() { + this.performRefresh(); + } + + @Override + protected void setup() { + } + + @Override + protected void start() { + LOGGER.at(Level.INFO).log("LiveConfig starting (patchline=%s, os=%s, arch=%s)", this.patchline, this.os, this.arch); + this.refreshTask = HytaleServer.SCHEDULED_EXECUTOR.scheduleAtFixedRate(this::performRefresh, INITIAL_DELAY_SECONDS, 300L, TimeUnit.SECONDS); + } + + @Override + protected void shutdown() { + if (this.refreshTask != null) { + this.refreshTask.cancel(false); + } + } + + private void performRefresh() { + if (!LiveConfigService.hasUrlOverride()) { + ServerAuthManager authManager = ServerAuthManager.getInstance(); + if (!authManager.hasSessionToken()) { + LOGGER.at(Level.FINE).log("No session token - skipping live config refresh"); + return; + } + } + + try { + String currentEtag = this.snapshot.getEtag(); + LiveConfigSnapshot newSnapshot = this.liveConfigService.fetchServerConfigs(this.patchline, this.os, this.arch, currentEtag); + if (newSnapshot != null) { + this.snapshot = newSnapshot; + LOGGER.at(Level.INFO).log("LiveConfig updated: version=%s, %d flag(s)", newSnapshot.getVersion(), newSnapshot.getFlagCount()); + } + } catch (Exception var3) { + LOGGER.at(Level.WARNING).withCause(var3).log("Failed to refresh live config"); + } + } + + @Nonnull + private static String resolvePatchline() { + String override = System.getProperty("liveconfig.patchline"); + if (override != null) { + return override; + } else { + String patchline = ManifestUtil.getPatchline(); + return patchline != null ? patchline : "dev"; + } + } +} diff --git a/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigService.java b/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigService.java new file mode 100644 index 00000000..03dbf9c8 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigService.java @@ -0,0 +1,147 @@ +package com.hypixel.hytale.server.core.liveconfig; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.server.core.auth.AuthConfig; +import com.hypixel.hytale.server.core.auth.ServerAuthManager; +import com.hypixel.hytale.server.core.util.ServiceHttpClientFactory; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpRequest.Builder; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class LiveConfigService { + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + public static final String URL_OVERRIDE_PROPERTY = "liveconfig.url"; + private final HttpClient httpClient = ServiceHttpClientFactory.newBuilder(AuthConfig.HTTP_TIMEOUT).build(); + private final String baseUrl = System.getProperty("liveconfig.url", "https://liveconfig.hytale.com"); + + public static boolean hasUrlOverride() { + return System.getProperty("liveconfig.url") != null; + } + + @Nullable + public LiveConfigSnapshot fetchServerConfigs(@Nonnull String patchline, @Nonnull String os, @Nonnull String arch, @Nullable String currentEtag) { + ServerAuthManager authManager = ServerAuthManager.getInstance(); + String sessionToken = authManager.getSessionToken(); + boolean urlOverride = hasUrlOverride(); + if (urlOverride || sessionToken != null && !sessionToken.isEmpty()) { + try { + URI uri = URI.create(String.format("%s/server-configs/%s?os=%s&arch=%s", this.baseUrl, patchline, os, arch)); + String bearerToken = sessionToken != null && !sessionToken.isEmpty() ? sessionToken : "dev-test-token"; + Builder requestBuilder = HttpRequest.newBuilder() + .uri(uri) + .header("Authorization", "Bearer " + bearerToken) + .header("User-Agent", AuthConfig.USER_AGENT) + .header("Accept", "application/json") + .timeout(AuthConfig.HTTP_TIMEOUT) + .GET(); + if (currentEtag != null) { + requestBuilder.header("If-None-Match", currentEtag); + } + + HttpResponse response = this.httpClient.send(requestBuilder.build(), BodyHandlers.ofString()); + return this.handleResponse(response); + } catch (InterruptedException var12) { + Thread.currentThread().interrupt(); + LOGGER.at(Level.FINE).log("Live config fetch interrupted"); + return null; + } catch (Exception var13) { + LOGGER.at(Level.WARNING).withCause(var13).log("Failed to fetch live config"); + return null; + } + } else { + LOGGER.at(Level.FINE).log("No session token available for live config fetch"); + return null; + } + } + + @Nullable + private LiveConfigSnapshot handleResponse(@Nonnull HttpResponse response) { + int status = response.statusCode(); + if (status == 304) { + LOGGER.at(Level.FINE).log("Live config not modified (304)"); + return null; + } else if (status != 200) { + LOGGER.at(Level.WARNING).log("Live config fetch failed: HTTP %d", status); + return null; + } else { + return this.parseResponse(response); + } + } + + @Nullable + private LiveConfigSnapshot parseResponse(@Nonnull HttpResponse response) { + try { + JsonObject root = JsonParser.parseString(response.body()).getAsJsonObject(); + String version = root.get("version").getAsString(); + JsonObject flagsObj = root.getAsJsonObject("flags"); + Map flags = new HashMap<>(); + if (flagsObj != null) { + for (Entry entry : flagsObj.entrySet()) { + JsonObject flagObj = entry.getValue().getAsJsonObject(); + String type = flagObj.get("type").getAsString(); + Object value = parseValue(type, flagObj); + flags.put(entry.getKey(), new LiveConfigSnapshot.ResolvedFlag(type, value)); + } + } + + String etag = response.headers().firstValue("ETag").orElse(version); + return new LiveConfigSnapshot(version, etag, flags); + } catch (Exception var11) { + LOGGER.at(Level.WARNING).withCause(var11).log("Failed to parse live config response"); + return null; + } + } + + @Nullable + private static Object parseValue(@Nonnull String type, @Nonnull JsonObject flagObj) { + JsonElement valueElement = flagObj.get("value"); + if (valueElement != null && !valueElement.isJsonNull()) { + return switch (type) { + case "boolean" -> valueElement.getAsBoolean(); + case "integer" -> valueElement.getAsInt(); + case "string" -> valueElement.getAsString(); + default -> { + LOGGER.at(Level.WARNING).log("Unknown flag type: %s", type); + yield null; + } + }; + } else { + return null; + } + } + + @Nonnull + public static String detectOS() { + String osName = System.getProperty("os.name", "").toLowerCase(Locale.ROOT); + if (osName.contains("win")) { + return "windows"; + } else if (osName.contains("mac") || osName.contains("darwin")) { + return "darwin"; + } else { + return !osName.contains("linux") && !osName.contains("nux") ? "any" : "linux"; + } + } + + @Nonnull + public static String detectArch() { + String arch = System.getProperty("os.arch", "").toLowerCase(Locale.ROOT); + if (arch.equals("amd64") || arch.equals("x86_64")) { + return "amd64"; + } else { + return !arch.equals("aarch64") && !arch.equals("arm64") ? "any" : "arm64"; + } + } +} diff --git a/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigSnapshot.java b/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigSnapshot.java new file mode 100644 index 00000000..1efe2846 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/liveconfig/LiveConfigSnapshot.java @@ -0,0 +1,65 @@ +package com.hypixel.hytale.server.core.liveconfig; + +import java.util.Collections; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public final class LiveConfigSnapshot { + public static final LiveConfigSnapshot EMPTY = new LiveConfigSnapshot(); + @Nullable + private final String version; + @Nullable + private final String etag; + @Nonnull + private final Map flags; + + private LiveConfigSnapshot() { + this.version = null; + this.etag = null; + this.flags = Collections.emptyMap(); + } + + public LiveConfigSnapshot(@Nonnull String version, @Nullable String etag, @Nonnull Map flags) { + this.version = version; + this.etag = etag; + this.flags = Collections.unmodifiableMap(flags); + } + + public boolean getBoolFlag(@Nonnull String name, boolean defaultValue) { + LiveConfigSnapshot.ResolvedFlag flag = this.flags.get(name); + return flag != null && flag.value() instanceof Boolean b ? b : defaultValue; + } + + public int getIntFlag(@Nonnull String name, int defaultValue) { + LiveConfigSnapshot.ResolvedFlag flag = this.flags.get(name); + return flag != null && flag.value() instanceof Integer i ? i : defaultValue; + } + + @Nonnull + public String getStringFlag(@Nonnull String name, @Nonnull String defaultValue) { + LiveConfigSnapshot.ResolvedFlag flag = this.flags.get(name); + return flag != null && flag.value() instanceof String s ? s : defaultValue; + } + + public boolean hasFlag(@Nonnull String name) { + return this.flags.containsKey(name); + } + + @Nullable + public String getVersion() { + return this.version; + } + + @Nullable + public String getEtag() { + return this.etag; + } + + public int getFlagCount() { + return this.flags.size(); + } + + public record ResolvedFlag(@Nonnull String type, @Nullable Object value) { + } +} diff --git a/src/com/hypixel/hytale/server/core/meta/AbstractMetaStore.java b/src/com/hypixel/hytale/server/core/meta/AbstractMetaStore.java index c59645e2..7fd26521 100644 --- a/src/com/hypixel/hytale/server/core/meta/AbstractMetaStore.java +++ b/src/com/hypixel/hytale/server/core/meta/AbstractMetaStore.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.codec.DirectDecodeCodec; import com.hypixel.hytale.codec.ExtraInfo; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; +import java.util.Objects; import java.util.Map.Entry; import java.util.function.BiConsumer; import javax.annotation.Nonnull; @@ -103,6 +104,10 @@ public abstract class AbstractMetaStore implements IMetaStoreImpl { final BsonDocument document = new BsonDocument(); document.putAll(this.unknownValues); this.getRegistry().forEachMetaEntry(this, new IMetaRegistry.MetaEntryConsumer() { + { + Objects.requireNonNull(AbstractMetaStore.this); + } + @Override public void accept(MetaKey key, T value) { if (key instanceof PersistentMetaKey persistentKey) { diff --git a/src/com/hypixel/hytale/server/core/meta/MetaRegistry.java b/src/com/hypixel/hytale/server/core/meta/MetaRegistry.java index eb6ea775..eb6c897b 100644 --- a/src/com/hypixel/hytale/server/core/meta/MetaRegistry.java +++ b/src/com/hypixel/hytale/server/core/meta/MetaRegistry.java @@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; import javax.annotation.Nonnull; @@ -68,6 +69,10 @@ public class MetaRegistry implements IMetaRegistry { @Override public void forEachMetaEntry(@Nonnull IMetaStore store, @Nonnull final IMetaRegistry.MetaEntryConsumer consumer) { store.forEachMetaObject(new IMetaStore.MetaEntryConsumer() { + { + Objects.requireNonNull(MetaRegistry.this); + } + @Override public void accept(int id, T value) { MetaRegistry.MetaRegistryEntry entry = MetaRegistry.this.suppliers.get(id); @@ -88,6 +93,8 @@ public class MetaRegistry implements IMetaRegistry { private final MetaKey key; public MetaRegistryEntry(Function function, MetaKey key) { + Objects.requireNonNull(MetaRegistry.this); + super(); this.function = function; this.key = key; } diff --git a/src/com/hypixel/hytale/server/core/modules/LegacyModule.java b/src/com/hypixel/hytale/server/core/modules/LegacyModule.java index 49ae1edf..eef98edf 100644 --- a/src/com/hypixel/hytale/server/core/modules/LegacyModule.java +++ b/src/com/hypixel/hytale/server/core/modules/LegacyModule.java @@ -1,27 +1,9 @@ package com.hypixel.hytale.server.core.modules; import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.codecs.array.ArrayCodec; -import com.hypixel.hytale.codec.store.StoredCodec; import com.hypixel.hytale.common.plugin.PluginManifest; -import com.hypixel.hytale.component.AddReason; -import com.hypixel.hytale.component.Archetype; -import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.component.RemoveReason; -import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.component.dependency.Dependency; -import com.hypixel.hytale.component.dependency.Order; -import com.hypixel.hytale.component.dependency.RootDependency; -import com.hypixel.hytale.component.dependency.SystemDependency; -import com.hypixel.hytale.component.query.Query; -import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; -import com.hypixel.hytale.server.core.modules.migrations.ChunkColumnMigrationSystem; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; @@ -35,12 +17,8 @@ import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.blockpositions.BlockPositionProvider; import com.hypixel.hytale.server.core.universe.world.chunk.systems.ChunkSystems; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.worldmap.WorldMapManager; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import java.util.Set; -import java.util.logging.Level; import javax.annotation.Nonnull; public class LegacyModule extends JavaPlugin { @@ -81,11 +59,9 @@ public class LegacyModule extends JavaPlugin { this.blockPositionProviderComponentType = this.getChunkStoreRegistry().registerComponent(BlockPositionProvider.class, () -> { throw new UnsupportedOperationException("BlockPositionProvider cannot be constructed"); }); - this.getChunkStoreRegistry().registerSystem(new ChunkSystems.OnNewChunk()); this.getChunkStoreRegistry().registerSystem(new ChunkSystems.OnChunkLoad()); this.getChunkStoreRegistry().registerSystem(new ChunkSystems.OnNonTicking()); this.getChunkStoreRegistry().registerSystem(new ChunkSystems.EnsureBlockSection()); - this.getChunkStoreRegistry().registerSystem(new LegacyModule.MigrateLegacySections()); this.getChunkStoreRegistry().registerSystem(new ChunkSystems.LoadBlockSection()); this.getChunkStoreRegistry().registerSystem(new ChunkSystems.ReplicateChanges()); this.getChunkStoreRegistry().registerSystem(new BlockChunk.LoadBlockChunkPacketSystem(this.blockChunkComponentType)); @@ -93,10 +69,6 @@ public class LegacyModule extends JavaPlugin { this.getChunkStoreRegistry().registerSystem(new BlockComponentChunk.BlockComponentChunkLoadingSystem()); this.getChunkStoreRegistry().registerSystem(new BlockComponentChunk.LoadBlockComponentPacketSystem(this.blockComponentChunkComponentType)); this.getChunkStoreRegistry().registerSystem(new BlockComponentChunk.UnloadBlockComponentPacketSystem(this.blockComponentChunkComponentType)); - ComponentType legacyBlockStateComponentType = this.getChunkStoreRegistry() - .registerComponent(LegacyModule.LegacyBlockStateChunk.class, "BlockStateChunk", LegacyModule.LegacyBlockStateChunk.CODEC, true); - this.getChunkStoreRegistry() - .registerSystem(new LegacyModule.MigrateLegacyBlockStateChunkSystem(legacyBlockStateComponentType, this.blockComponentChunkComponentType)); this.getEventRegistry().register(LoadedAssetsEvent.class, GameplayConfig.class, event -> WorldMapManager.sendSettingsToAllWorlds()); } @@ -139,140 +111,4 @@ public class LegacyModule extends JavaPlugin { public ComponentType getBlockPositionProviderComponentType() { return this.blockPositionProviderComponentType; } - - private static class LegacyBlockStateChunk implements Component { - public static final BuilderCodec CODEC = BuilderCodec.builder( - LegacyModule.LegacyBlockStateChunk.class, LegacyModule.LegacyBlockStateChunk::new - ) - .addField( - new KeyedCodec<>("States", new ArrayCodec<>(new StoredCodec<>(ChunkStore.HOLDER_CODEC_KEY), Holder[]::new)), - (entityChunk, array) -> entityChunk.holders = array, - entityChunk -> { - throw new UnsupportedOperationException("Serialise is not allowed for BlockStateChunk"); - } - ) - .build(); - public Holder[] holders; - - public LegacyBlockStateChunk() { - } - - public LegacyBlockStateChunk(Holder[] holders) { - this.holders = holders; - } - - @Nonnull - @Override - public Component clone() { - Holder[] newHolders = new Holder[this.holders.length]; - - for (int i = 0; i < this.holders.length; i++) { - newHolders[i] = this.holders[i].clone(); - } - - return new LegacyModule.LegacyBlockStateChunk(newHolders); - } - } - - private static class MigrateLegacyBlockStateChunkSystem extends ChunkColumnMigrationSystem { - private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - private final ComponentType legacyComponentType; - private final ComponentType componentType; - private final Archetype archetype; - - public MigrateLegacyBlockStateChunkSystem( - ComponentType legacyComponentType, ComponentType componentType - ) { - this.legacyComponentType = legacyComponentType; - this.componentType = componentType; - this.archetype = Archetype.of(legacyComponentType, WorldChunk.getComponentType()); - } - - @Override - public Query getQuery() { - return this.archetype; - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - LegacyModule.LegacyBlockStateChunk component = holder.getComponent(this.legacyComponentType); - - assert component != null; - - holder.removeComponent(this.legacyComponentType); - Int2ObjectOpenHashMap> holders = new Int2ObjectOpenHashMap<>(); - - for (Holder blockComponentHolder : component.holders) { - BlockState blockState = BlockState.getBlockState(blockComponentHolder); - Vector3i position = blockState.__internal_getPosition(); - if (position == null) { - LOGGER.at(Level.SEVERE).log("Skipping migration for BlockState with null position!", blockComponentHolder); - } else { - holders.put(blockState.getIndex(), blockComponentHolder); - } - } - - BlockComponentChunk blockComponentChunk = new BlockComponentChunk(holders, new Int2ObjectOpenHashMap<>()); - holder.addComponent(this.componentType, blockComponentChunk); - holder.getComponent(WorldChunk.getComponentType()).setBlockComponentChunk(blockComponentChunk); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } - } - - @Deprecated(forRemoval = true) - public static class MigrateLegacySections extends ChunkColumnMigrationSystem { - private final Query QUERY = Query.and(ChunkColumn.getComponentType(), BlockChunk.getComponentType()); - private final Set> DEPENDENCIES = Set.of( - new SystemDependency<>(Order.AFTER, ChunkSystems.OnNewChunk.class), RootDependency.first() - ); - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - ChunkColumn column = holder.getComponent(ChunkColumn.getComponentType()); - - assert column != null; - - BlockChunk blockChunk = holder.getComponent(BlockChunk.getComponentType()); - - assert blockChunk != null; - - Holder[] sections = column.getSectionHolders(); - BlockSection[] migratedSections = blockChunk.takeMigratedSections(); - if (migratedSections != null) { - for (int i = 0; i < sections.length; i++) { - Holder section = sections[i]; - BlockSection blockSection = migratedSections[i]; - if (section != null && blockSection != null) { - section.putComponent(BlockSection.getComponentType(), blockSection); - blockChunk.markNeedsSaving(); - } - } - } - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return this.QUERY; - } - - @Nonnull - @Override - public Set> getDependencies() { - return this.DEPENDENCIES; - } - } } diff --git a/src/com/hypixel/hytale/server/core/modules/accesscontrol/AccessControlModule.java b/src/com/hypixel/hytale/server/core/modules/accesscontrol/AccessControlModule.java index 991545d6..57d02614 100644 --- a/src/com/hypixel/hytale/server/core/modules/accesscontrol/AccessControlModule.java +++ b/src/com/hypixel/hytale/server/core/modules/accesscontrol/AccessControlModule.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.server.core.modules.accesscontrol; import com.google.gson.JsonObject; import com.hypixel.hytale.common.plugin.PluginManifest; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.event.events.player.PlayerSetupConnectEvent; import com.hypixel.hytale.server.core.modules.accesscontrol.ban.Ban; import com.hypixel.hytale.server.core.modules.accesscontrol.ban.BanParser; @@ -17,6 +18,7 @@ import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -31,6 +33,7 @@ public class AccessControlModule extends JavaPlugin { private final HytaleBanProvider banProvider = new HytaleBanProvider(); private final List providerRegistry = new CopyOnWriteArrayList() { { + Objects.requireNonNull(AccessControlModule.this); this.add(AccessControlModule.this.whitelistProvider); this.add(AccessControlModule.this.banProvider); } @@ -54,8 +57,8 @@ public class AccessControlModule extends JavaPlugin { this.registerBanParser("timed", TimedBan::fromJsonObject); this.registerBanParser("infinite", InfiniteBan::fromJsonObject); this.getEventRegistry().register(PlayerSetupConnectEvent.class, event -> { - CompletableFuture> completableFuture = this.getDisconnectReason(event.getUuid()); - Optional disconnectReason = completableFuture.join(); + CompletableFuture> completableFuture = this.getDisconnectReason(event.getUuid()); + Optional disconnectReason = completableFuture.join(); if (disconnectReason.isPresent()) { event.setReason(disconnectReason.get()); event.setCancelled(true); @@ -98,7 +101,7 @@ public class AccessControlModule extends JavaPlugin { } @Nonnull - private CompletableFuture> getDisconnectReason(UUID uuid) { + private CompletableFuture> getDisconnectReason(@Nonnull UUID uuid) { return this.providerRegistry .stream() .map(p -> p.getDisconnectReason(uuid)) diff --git a/src/com/hypixel/hytale/server/core/modules/accesscontrol/ban/InfiniteBan.java b/src/com/hypixel/hytale/server/core/modules/accesscontrol/ban/InfiniteBan.java index 7a4dbc22..bdebc2fc 100644 --- a/src/com/hypixel/hytale/server/core/modules/accesscontrol/ban/InfiniteBan.java +++ b/src/com/hypixel/hytale/server/core/modules/accesscontrol/ban/InfiniteBan.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.server.core.modules.accesscontrol.ban; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.hypixel.hytale.server.core.Message; import java.time.Instant; import java.util.Optional; import java.util.UUID; @@ -43,9 +44,10 @@ public class InfiniteBan extends AbstractBan { @Nonnull @Override - public CompletableFuture> getDisconnectReason(UUID uuid) { - StringBuilder message = new StringBuilder("You are permanently banned!"); - this.reason.ifPresent(s -> message.append(" Reason: ").append(s)); - return CompletableFuture.completedFuture(Optional.of(message.toString())); + public CompletableFuture> getDisconnectReason(@Nonnull UUID uuid) { + Message message = this.reason.isPresent() + ? Message.translation("client.general.disconnect.banned.permanent.withReason").param("reason", this.reason.get()) + : Message.translation("client.general.disconnect.banned.permanent"); + return CompletableFuture.completedFuture(Optional.of(message)); } } diff --git a/src/com/hypixel/hytale/server/core/modules/accesscontrol/ban/TimedBan.java b/src/com/hypixel/hytale/server/core/modules/accesscontrol/ban/TimedBan.java index be023daa..4d0e4387 100644 --- a/src/com/hypixel/hytale/server/core/modules/accesscontrol/ban/TimedBan.java +++ b/src/com/hypixel/hytale/server/core/modules/accesscontrol/ban/TimedBan.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.server.core.modules.accesscontrol.ban; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.hypixel.hytale.common.util.StringUtil; +import com.hypixel.hytale.server.core.Message; import java.time.Duration; import java.time.Instant; import java.util.Optional; @@ -53,11 +53,19 @@ public class TimedBan extends AbstractBan { @Nonnull @Override - public CompletableFuture> getDisconnectReason(UUID uuid) { - Duration timeRemaining = Duration.between(Instant.now(), this.expiresOn); - StringBuilder message = new StringBuilder("You are temporarily banned for ").append(StringUtil.humanizeTime(timeRemaining)).append('!'); - this.reason.ifPresent(s -> message.append(" Reason: ").append(s)); - return CompletableFuture.completedFuture(Optional.of(message.toString())); + public CompletableFuture> getDisconnectReason(@Nonnull UUID uuid) { + long length = Duration.between(Instant.now(), this.expiresOn).toMillis(); + long days = length / 86400000L; + long hours = (length - days * 86400000L) / 3600000L; + long mins = (length - (days * 86400000L + hours * 3600000L)) / 60000L; + Message message = this.reason.isPresent() + ? Message.translation("client.general.disconnect.banned.timed.withReason") + .param("days", days) + .param("hours", hours) + .param("mins", mins) + .param("reason", this.reason.get()) + : Message.translation("client.general.disconnect.banned.timed").param("days", days).param("hours", hours).param("mins", mins); + return CompletableFuture.completedFuture(Optional.of(message)); } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/accesscontrol/commands/BanCommand.java b/src/com/hypixel/hytale/server/core/modules/accesscontrol/commands/BanCommand.java index 336111e1..df8544a8 100644 --- a/src/com/hypixel/hytale/server/core/modules/accesscontrol/commands/BanCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/accesscontrol/commands/BanCommand.java @@ -28,6 +28,7 @@ public class BanCommand extends AbstractAsyncCommand { public BanCommand(@Nonnull HytaleBanProvider banProvider) { super("ban", "server.commands.ban.desc"); this.setUnavailableInSingleplayer(true); + this.setAllowsExtraArguments(true); this.banProvider = banProvider; } @@ -61,16 +62,16 @@ public class BanCommand extends AbstractAsyncCommand { return Boolean.TRUE; }); if (playerRef != null) { - CompletableFuture> disconnectReason = ban.getDisconnectReason(uuid); - return disconnectReason.whenComplete((string, disconnectEx) -> { - Optional optional = (Optional)string; + CompletableFuture> disconnectReason = ban.getDisconnectReason(uuid); + return disconnectReason.whenComplete((message, disconnectEx) -> { + Optional optional = (Optional)message; if (disconnectEx != null) { context.sendMessage(Message.translation("server.modules.ban.failedDisconnectReason").param("name", displayMessage)); disconnectEx.printStackTrace(); } - if (string == null || !string.isPresent()) { - optional = Optional.of("Failed to get disconnect reason."); + if (message == null || !message.isPresent()) { + optional = Optional.of(Message.translation("server.general.disconnect.internalServerError")); } playerRef.getPacketHandler().disconnect(optional.get()); diff --git a/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/AccessProvider.java b/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/AccessProvider.java index dbe2b6da..bcab8570 100644 --- a/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/AccessProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/AccessProvider.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.server.core.modules.accesscontrol.provider; +import com.hypixel.hytale.server.core.Message; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -7,5 +8,5 @@ import javax.annotation.Nonnull; public interface AccessProvider { @Nonnull - CompletableFuture> getDisconnectReason(UUID var1); + CompletableFuture> getDisconnectReason(@Nonnull UUID var1); } diff --git a/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/ClientDelegatingProvider.java b/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/ClientDelegatingProvider.java index c09822a1..af89e25f 100644 --- a/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/ClientDelegatingProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/ClientDelegatingProvider.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.server.core.modules.accesscontrol.provider; +import com.hypixel.hytale.server.core.Message; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -8,7 +9,7 @@ import javax.annotation.Nonnull; public class ClientDelegatingProvider implements AccessProvider { @Nonnull @Override - public CompletableFuture> getDisconnectReason(UUID uuid) { + public CompletableFuture> getDisconnectReason(@Nonnull UUID uuid) { return CompletableFuture.completedFuture(Optional.empty()); } } diff --git a/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/HytaleBanProvider.java b/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/HytaleBanProvider.java index b401dc2b..b56588c5 100644 --- a/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/HytaleBanProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/HytaleBanProvider.java @@ -4,6 +4,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.stream.JsonWriter; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.modules.accesscontrol.AccessControlModule; import com.hypixel.hytale.server.core.modules.accesscontrol.ban.Ban; import com.hypixel.hytale.server.core.util.io.BlockingDiskFile; @@ -29,7 +30,7 @@ public class HytaleBanProvider extends BlockingDiskFile implements AccessProvide @Nonnull @Override - public CompletableFuture> getDisconnectReason(UUID uuid) { + public CompletableFuture> getDisconnectReason(@Nonnull UUID uuid) { Ban ban = this.bans.get(uuid); if (ban != null && !ban.isInEffect()) { this.bans.remove(uuid); diff --git a/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/HytaleWhitelistProvider.java b/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/HytaleWhitelistProvider.java index 9a44e8cf..24fdb2cd 100644 --- a/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/HytaleWhitelistProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/accesscontrol/provider/HytaleWhitelistProvider.java @@ -5,6 +5,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import com.google.gson.stream.JsonWriter; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.util.io.BlockingDiskFile; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -67,7 +68,7 @@ public class HytaleWhitelistProvider extends BlockingDiskFile implements AccessP @Nonnull @Override - public CompletableFuture> getDisconnectReason(UUID uuid) { + public CompletableFuture> getDisconnectReason(@Nonnull UUID uuid) { this.lock.readLock().lock(); CompletableFuture var2; @@ -76,7 +77,7 @@ public class HytaleWhitelistProvider extends BlockingDiskFile implements AccessP return CompletableFuture.completedFuture(Optional.empty()); } - var2 = CompletableFuture.completedFuture(Optional.of("You are not whitelisted!")); + var2 = CompletableFuture.completedFuture(Optional.of(Message.translation("client.general.disconnect.notWhitelisted"))); } finally { this.lock.readLock().unlock(); } diff --git a/src/com/hypixel/hytale/server/core/modules/block/BlockEntity.java b/src/com/hypixel/hytale/server/core/modules/block/BlockEntity.java new file mode 100644 index 00000000..a9a75e84 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/block/BlockEntity.java @@ -0,0 +1,129 @@ +package com.hypixel.hytale.server.core.modules.block; + +import com.hypixel.hytale.component.AddReason; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.RemoveReason; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.util.FillerBlockUtil; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class BlockEntity { + public static void setBlockEntity( + @Nonnull ComponentAccessor accessor, + @Nonnull Ref chunkRef, + @Nonnull BlockComponentChunk componentChunk, + int x, + int y, + int z, + @Nonnull BlockType blockType, + int rotation, + @Nullable Holder holder + ) { + int index = ChunkUtil.indexBlockInColumn(x, y, z); + if (holder == null) { + Ref reference = componentChunk.getEntityReference(index); + if (reference != null) { + accessor.removeEntity(reference, ChunkStore.REGISTRY.newHolder(), RemoveReason.REMOVE); + } else { + componentChunk.removeEntityHolder(index); + } + } else if (accessor.getArchetype(chunkRef).contains(ChunkStore.REGISTRY.getNonTickingComponentType())) { + Holder oldHolder = componentChunk.removeEntityHolder(index); + BlockReplaceEvent event = new BlockReplaceEvent(chunkRef, x, y, z, holder, x, y, z); + BlockBoundingBoxes.RotatedVariantBoxes hitbox = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()).get(rotation); + FillerBlockUtil.forEachFillerBlock(hitbox, (x1, y1, z1) -> { + if (x1 != 0 || y1 != 0 || z1 != 0) { + int ox = x + x1; + int oy = y + y1; + int oz = z + z1; + if (ChunkUtil.isSameChunk(x, z, ox, oz)) { + Holder otherHolder = componentChunk.getEntityHolder(ChunkUtil.indexBlockInColumn(ox, oy, oz)); + if (otherHolder != null) { + event.next(ox, oy, oz); + accessor.invoke(otherHolder, event); + } + } else { + long chunkIndex = ChunkUtil.indexChunkFromBlock(ox, oz); + Ref otherChunk = accessor.getExternalData().getChunkReference(chunkIndex); + if (otherChunk != null) { + BlockComponentChunk otherComponentChunk = accessor.getComponent(otherChunk, BlockComponentChunk.getComponentType()); + if (otherComponentChunk != null) { + Ref otherReference = otherComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(ox, oy, oz)); + if (otherReference != null) { + event.next(ox, oy, oz); + accessor.invoke(otherReference, event); + } + + Holder otherHolder = otherComponentChunk.getEntityHolder(ChunkUtil.indexBlockInColumn(ox, oy, oz)); + if (otherHolder != null) { + event.next(ox, oy, oz); + accessor.invoke(otherHolder, event); + } + } + } + } + } else if (oldHolder != null) { + event.next(x + x1, y + y1, z + z1); + accessor.invoke(oldHolder, event); + } + }); + holder.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(index, chunkRef)); + componentChunk.addEntityHolder(index, holder); + } else { + Ref oldReference = componentChunk.getEntityReference(index); + BlockReplaceEvent event = new BlockReplaceEvent(chunkRef, x, y, z, holder, x, y, z); + BlockBoundingBoxes.RotatedVariantBoxes hitbox = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()).get(rotation); + FillerBlockUtil.forEachFillerBlock(hitbox, (x1, y1, z1) -> { + if (x1 != 0 || y1 != 0 || z1 != 0) { + int ox = x + x1; + int oy = y + y1; + int oz = z + z1; + if (ChunkUtil.isSameChunk(x, z, ox, oz)) { + Ref otherReference = componentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(ox, oy, oz)); + if (otherReference != null) { + event.next(x + x1, y + y1, z + z1); + accessor.invoke(otherReference, event); + } + } else { + long chunkIndex = ChunkUtil.indexChunkFromBlock(ox, oz); + Ref otherChunk = accessor.getExternalData().getChunkReference(chunkIndex); + if (otherChunk != null) { + BlockComponentChunk otherComponentChunk = accessor.getComponent(otherChunk, BlockComponentChunk.getComponentType()); + if (otherComponentChunk != null) { + Ref otherReference = otherComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(ox, oy, oz)); + if (otherReference != null) { + event.next(ox, oy, oz); + accessor.invoke(otherReference, event); + } + + Holder otherHolder = otherComponentChunk.getEntityHolder(ChunkUtil.indexBlockInColumn(ox, oy, oz)); + if (otherHolder != null) { + event.next(ox, oy, oz); + accessor.invoke(otherHolder, event); + } + } + } + } + } else if (oldReference != null) { + event.next(x + x1, y + y1, z + z1); + accessor.invoke(oldReference, event); + } + }); + if (oldReference != null) { + accessor.removeEntity(oldReference, ChunkStore.REGISTRY.newHolder(), RemoveReason.REMOVE); + } else { + componentChunk.removeEntityHolder(index); + } + + holder.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(index, chunkRef)); + accessor.addEntity(holder, AddReason.SPAWN); + } + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/block/BlockModule.java b/src/com/hypixel/hytale/server/core/modules/block/BlockModule.java index fc0fb4ff..00c2f7dd 100644 --- a/src/com/hypixel/hytale/server/core/modules/block/BlockModule.java +++ b/src/com/hypixel/hytale/server/core/modules/block/BlockModule.java @@ -1,6 +1,5 @@ package com.hypixel.hytale.server.core.modules.block; -import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.common.plugin.PluginManifest; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.CommandBuffer; @@ -17,14 +16,18 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.SystemType; import com.hypixel.hytale.component.data.unknown.UnknownComponents; import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.spatial.KDTree; +import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.event.EventPriority; +import com.hypixel.hytale.function.consumer.BiIntConsumer; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData; import com.hypixel.hytale.server.core.modules.LegacyModule; +import com.hypixel.hytale.server.core.modules.block.components.ItemContainerBlock; +import com.hypixel.hytale.server.core.modules.block.system.ItemContainerBlockSpatialSystem; +import com.hypixel.hytale.server.core.modules.block.system.ItemContainerSystems; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.world.World; @@ -34,13 +37,16 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.events.AddWorldEvent; import com.hypixel.hytale.server.core.universe.world.events.ChunkPreLoadProcessEvent; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.universe.world.meta.state.BlockMapMarker; import com.hypixel.hytale.server.core.universe.world.meta.state.BlockMapMarkersResource; import com.hypixel.hytale.server.core.universe.world.meta.state.LaunchPad; import com.hypixel.hytale.server.core.universe.world.meta.state.RespawnBlock; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntConsumer; +import it.unimi.dsi.fastutil.ints.IntList; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -53,8 +59,10 @@ public class BlockModule extends JavaPlugin { private ComponentType respawnBlockComponentType; private ComponentType blockMapMarkerComponentType; private ResourceType blockMapMarkersResourceType; + private ComponentType itemContainerBlockComponentType; private ComponentType blockStateInfoComponentType; private ResourceType blockStateInfoNeedRebuildResourceType; + private ResourceType, ChunkStore>> itemContainerSpatialResourceType; public static BlockModule get() { return instance; @@ -74,9 +82,13 @@ public class BlockModule extends JavaPlugin { }); chunkStoreRegistry.registerSystem(new BlockModule.BlockStateInfoRefSystem(this.blockStateInfoComponentType)); this.launchPadComponentType = chunkStoreRegistry.registerComponent(LaunchPad.class, "LaunchPad", LaunchPad.CODEC); - chunkStoreRegistry.registerSystem(new BlockModule.MigrateLaunchPad()); this.respawnBlockComponentType = chunkStoreRegistry.registerComponent(RespawnBlock.class, "RespawnBlock", RespawnBlock.CODEC); chunkStoreRegistry.registerSystem(new RespawnBlock.OnRemove()); + this.itemContainerBlockComponentType = chunkStoreRegistry.registerComponent(ItemContainerBlock.class, "ItemContainerBlock", ItemContainerBlock.CODEC); + chunkStoreRegistry.registerSystem(new BlockModule.MigrateItemContainer()); + chunkStoreRegistry.registerSystem(new ItemContainerSystems.OnAddedOrRemoved()); + chunkStoreRegistry.registerSystem(new ItemContainerSystems.OnReplaced()); + chunkStoreRegistry.registerSystem(new ItemContainerSystems.OnReplacedHolder()); this.blockMapMarkerComponentType = chunkStoreRegistry.registerComponent(BlockMapMarker.class, "BlockMapMarker", BlockMapMarker.CODEC); this.blockMapMarkersResourceType = chunkStoreRegistry.registerResource(BlockMapMarkersResource.class, "BlockMapMarkers", BlockMapMarkersResource.CODEC); chunkStoreRegistry.registerSystem(new BlockMapMarker.OnAddRemove()); @@ -88,6 +100,9 @@ public class BlockModule extends JavaPlugin { this.blockStateInfoNeedRebuildResourceType = chunkStoreRegistry.registerResource( BlockModule.BlockStateInfoNeedRebuild.class, BlockModule.BlockStateInfoNeedRebuild::new ); + this.itemContainerSpatialResourceType = this.getChunkStoreRegistry().registerSpatialResource(() -> new KDTree<>(Ref::isValid)); + this.getChunkStoreRegistry().registerSystem(new ItemContainerBlockSpatialSystem(this.itemContainerSpatialResourceType)); + this.getChunkStoreRegistry().registerSystem(new BlockModule.ItemContainerStateRefSystem()); this.getEventRegistry().registerGlobal(EventPriority.EARLY, ChunkPreLoadProcessEvent.class, BlockModule::onChunkPreLoadProcessEnsureBlockEntity); } @@ -108,53 +123,45 @@ public class BlockModule extends JavaPlugin { ); return chunk.getWorld().getChunkStore().getStore().addEntity(data, AddReason.SPAWN); } else { - BlockState state = BlockState.ensureState(chunk, x, y, z); - return state != null ? state.getReference() : null; + return null; } } } private static void onChunkPreLoadProcessEnsureBlockEntity(@Nonnull ChunkPreLoadProcessEvent event) { if (event.isNewlyGenerated()) { - BlockTypeAssetMap blockTypeAssetMap = BlockType.getAssetMap(); Holder holder = event.getHolder(); - WorldChunk chunk = event.getChunk(); ChunkColumn chunkColumnComponent = holder.getComponent(ChunkColumn.getComponentType()); if (chunkColumnComponent != null) { Holder[] sectionHolders = chunkColumnComponent.getSectionHolders(); if (sectionHolders != null) { BlockComponentChunk blockComponentModule = holder.getComponent(BlockComponentChunk.getComponentType()); if (blockComponentModule != null) { + BlockModule.BlockEntityPreprocessor preprocessor = BlockModule.BlockEntityPreprocessor.LOCAL.get(); + for (int sectionIndex = 0; sectionIndex < 10; sectionIndex++) { BlockSection section = sectionHolders[sectionIndex].ensureAndGetComponent(BlockSection.getComponentType()); if (!section.isSolidAir()) { - int sectionYBlock = sectionIndex << 5; + preprocessor.clear(); + section.forEachValue(preprocessor.typeCollector); + if (!preprocessor.ids.isEmpty()) { + section.find(preprocessor.ids, preprocessor.blockCollector); - for (int sectionY = 0; sectionY < 32; sectionY++) { - int y = sectionYBlock | sectionY; + assert preprocessor.indices.size() == preprocessor.blockIds.size(); - for (int z = 0; z < 32; z++) { - for (int x = 0; x < 32; x++) { - int blockId = section.get(x, y, z); - BlockType blockType = blockTypeAssetMap.getAsset(blockId); - if (blockType != null && !blockType.isUnknown() && section.getFiller(x, y, z) == 0) { - int index = ChunkUtil.indexBlockInColumn(x, y, z); - if (blockType.getBlockEntity() != null) { - if (blockComponentModule.getEntityHolder(index) != null) { - continue; - } + int sectionMinBlockY = ChunkUtil.minBlock(sectionIndex); - blockComponentModule.addEntityHolder(index, blockType.getBlockEntity().clone()); - } - - StateData state = blockType.getState(); - if (state != null && state.getId() != null && blockComponentModule.getEntityHolder(index) == null) { - Vector3i position = new Vector3i(x, y, z); - BlockState blockState = BlockStateModule.get().createBlockState(state.getId(), chunk, position, blockType); - if (blockState != null) { - blockComponentModule.addEntityHolder(index, blockState.toHolder()); - } - } + for (int i = 0; i < preprocessor.indices.size(); i++) { + int index = preprocessor.indices.getInt(i); + int blockId = preprocessor.blockIds.getInt(i); + Holder entity = preprocessor.blockEntities.get(blockId); + if (entity != null) { + int x = ChunkUtil.xFromIndex(index); + int z = ChunkUtil.zFromIndex(index); + int y = ChunkUtil.yFromIndex(index) | sectionMinBlockY; + int chunkIndex = ChunkUtil.indexBlockInColumn(x, y, z); + if (section.getFiller(index) == 0 && blockComponentModule.getEntityHolder(chunkIndex) == null) { + blockComponentModule.addEntityHolder(chunkIndex, entity.clone()); } } } @@ -195,6 +202,14 @@ public class BlockModule extends JavaPlugin { return this.blockStateInfoNeedRebuildResourceType; } + public ComponentType getItemContainerBlockComponentType() { + return this.itemContainerBlockComponentType; + } + + public ResourceType, ChunkStore>> getItemContainerSpatialResourceType() { + return this.itemContainerSpatialResourceType; + } + @Nullable public static Ref getBlockEntity(@Nonnull World world, int x, int y, int z) { ChunkStore chunkStore = world.getChunkStore(); @@ -231,6 +246,36 @@ public class BlockModule extends JavaPlugin { } } + public static final class BlockEntityPreprocessor { + public static final ThreadLocal LOCAL = ThreadLocal.withInitial(BlockModule.BlockEntityPreprocessor::new); + public final IntList ids = new IntArrayList(); + public final Int2ObjectMap> blockEntities = new Int2ObjectOpenHashMap<>(); + public final IntList indices = new IntArrayList(); + public final IntList blockIds = new IntArrayList(); + public final IntConsumer typeCollector = this::collectType; + public final BiIntConsumer blockCollector = this::collectBlock; + + public void clear() { + this.ids.clear(); + this.blockEntities.clear(); + this.indices.clear(); + this.blockIds.clear(); + } + + private void collectType(int value) { + BlockType type = BlockType.getAssetMap().getAsset(value); + if (type != null && !type.isUnknown() && type.getBlockEntity() != null) { + this.ids.add(value); + this.blockEntities.put(value, type.getBlockEntity()); + } + } + + private void collectBlock(int index, int blockId) { + this.indices.add(index); + this.blockIds.add(blockId); + } + } + public static class BlockStateInfo implements Component { private final int index; @Nonnull @@ -377,17 +422,55 @@ public class BlockModule extends JavaPlugin { } } - @Deprecated(forRemoval = true) - public static class MigrateLaunchPad extends BlockModule.MigrationSystem { + public static class ItemContainerStateRefSystem extends RefSystem { + private static final Query query = ItemContainerBlock.getComponentType(); + + @Override + public Query getQuery() { + return query; + } + + @Override + public void onEntityAdded( + @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + commandBuffer.getExternalData() + .getWorld() + .getChunkStore() + .getStore() + .getResource(BlockModule.BlockStateInfoNeedRebuild.getResourceType()) + .markAsNeedRebuild(); + } + + @Override + public void onEntityRemove( + @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + commandBuffer.getExternalData() + .getWorld() + .getChunkStore() + .getStore() + .getResource(BlockModule.BlockStateInfoNeedRebuild.getResourceType()) + .markAsNeedRebuild(); + } + + @Nonnull + @Override + public String toString() { + return "ItemContainerStateRefSystem{}"; + } + } + + public static class MigrateItemContainer extends BlockModule.MigrationSystem { @Override public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { UnknownComponents unknownComponents = holder.getComponent(ChunkStore.REGISTRY.getUnknownComponentType()); assert unknownComponents != null; - LaunchPad launchPadComponent = unknownComponents.removeComponent("launchPad", LaunchPad.CODEC); - if (launchPadComponent != null) { - holder.putComponent(LaunchPad.getComponentType(), launchPadComponent); + ItemContainerBlock itemContainerBlock = unknownComponents.removeComponent("container", ItemContainerBlock.CODEC); + if (itemContainerBlock != null) { + holder.putComponent(ItemContainerBlock.getComponentType(), itemContainerBlock); } } diff --git a/src/com/hypixel/hytale/server/core/modules/block/BlockReplaceEvent.java b/src/com/hypixel/hytale/server/core/modules/block/BlockReplaceEvent.java new file mode 100644 index 00000000..4857e08e --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/block/BlockReplaceEvent.java @@ -0,0 +1,54 @@ +package com.hypixel.hytale.server.core.modules.block; + +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.system.EcsEvent; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; + +public class BlockReplaceEvent extends EcsEvent { + private final Ref chunkRef; + private int selfX; + private int selfY; + private int selfZ; + private final Holder newEntity; + private final int baseX; + private final int baseY; + private final int baseZ; + + public BlockReplaceEvent(Ref chunkRef, int selfX, int selfY, int selfZ, Holder newEntity, int baseX, int baseY, int baseZ) { + this.chunkRef = chunkRef; + this.selfX = selfX; + this.selfY = selfY; + this.selfZ = selfZ; + this.newEntity = newEntity; + this.baseX = baseX; + this.baseY = baseY; + this.baseZ = baseZ; + } + + public Ref getChunkRef() { + return this.chunkRef; + } + + public int getSelfX() { + return this.selfX; + } + + public int getSelfY() { + return this.selfY; + } + + public int getSelfZ() { + return this.selfZ; + } + + public Holder getNewEntity() { + return this.newEntity; + } + + public void next(int selfX, int selfY, int selfZ) { + this.selfX = selfX; + this.selfY = selfY; + this.selfZ = selfZ; + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/block/components/ItemContainerBlock.java b/src/com/hypixel/hytale/server/core/modules/block/components/ItemContainerBlock.java new file mode 100644 index 00000000..bb4a6b24 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/block/components/ItemContainerBlock.java @@ -0,0 +1,88 @@ +package com.hypixel.hytale.server.core.modules.block.components; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.Component; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList; +import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerBlockWindow; +import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; +import com.hypixel.hytale.server.core.modules.block.BlockModule; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class ItemContainerBlock implements Component { + public static final BuilderCodec CODEC = BuilderCodec.builder(ItemContainerBlock.class, ItemContainerBlock::new) + .appendInherited(new KeyedCodec<>("Droplist", Codec.STRING), (state, o) -> state.droplist = o, state -> state.droplist, (o, p) -> o.droplist = p.droplist) + .addValidator(ItemDropList.VALIDATOR_CACHE.getValidator()) + .add() + .appendInherited( + new KeyedCodec<>("ItemContainer", SimpleItemContainer.CODEC), + (state, o) -> state.itemContainer = o, + state -> state.itemContainer, + (o, p) -> o.itemContainer = p.itemContainer.clone() + ) + .add() + .appendInherited(new KeyedCodec<>("Capacity", Codec.SHORT), (o, i) -> o.capacity = i, o -> o.capacity, (o, p) -> o.capacity = p.capacity) + .add() + .build(); + private final transient Map windows = new ConcurrentHashMap<>(); + @Nullable + protected String droplist; + protected SimpleItemContainer itemContainer; + protected short capacity = 20; + + public static ComponentType getComponentType() { + return BlockModule.get().getItemContainerBlockComponentType(); + } + + private ItemContainerBlock() { + } + + public ItemContainerBlock(ItemContainerBlock itemContainerBlock) { + this.droplist = itemContainerBlock.droplist; + this.itemContainer = itemContainerBlock.itemContainer != null ? itemContainerBlock.itemContainer.clone() : null; + this.capacity = itemContainerBlock.capacity; + } + + public void setItemContainer(SimpleItemContainer itemContainer) { + this.itemContainer = itemContainer; + } + + @Nullable + public String getDroplist() { + return this.droplist; + } + + public void setDroplist(@Nullable String droplist) { + this.droplist = droplist; + } + + @Nonnull + public Map getWindows() { + return this.windows; + } + + public SimpleItemContainer getItemContainer() { + if (this.itemContainer == null) { + this.itemContainer = new SimpleItemContainer(this.capacity); + } + + return this.itemContainer; + } + + public short getCapacity() { + return this.capacity; + } + + @Nullable + @Override + public Component clone() { + return new ItemContainerBlock(this); + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerStateSpatialSystem.java b/src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerBlockSpatialSystem.java similarity index 79% rename from src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerStateSpatialSystem.java rename to src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerBlockSpatialSystem.java index 075c3d04..b91b769b 100644 --- a/src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerStateSpatialSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerBlockSpatialSystem.java @@ -8,21 +8,19 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.block.BlockModule; +import com.hypixel.hytale.server.core.modules.block.components.ItemContainerBlock; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; -import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; -public class ItemContainerStateSpatialSystem extends SpatialSystem { +public class ItemContainerBlockSpatialSystem extends SpatialSystem { @Nonnull - public static final Query QUERY = (Query)Objects.requireNonNull(BlockStateModule.get().getComponentType(ItemContainerState.class)); + public static final Query QUERY = ItemContainerBlock.getComponentType(); - public ItemContainerStateSpatialSystem(ResourceType, ChunkStore>> resourceType) { + public ItemContainerBlockSpatialSystem(ResourceType, ChunkStore>> resourceType) { super(resourceType); } diff --git a/src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerSystems.java b/src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerSystems.java new file mode 100644 index 00000000..2c578411 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/block/system/ItemContainerSystems.java @@ -0,0 +1,206 @@ +package com.hypixel.hytale.server.core.modules.block.system; + +import com.hypixel.hytale.component.AddReason; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.RemoveReason; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.component.system.EntityHolderEventSystem; +import com.hypixel.hytale.component.system.RefSystem; +import com.hypixel.hytale.event.EventPriority; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.entity.entities.player.windows.WindowManager; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; +import com.hypixel.hytale.server.core.modules.block.BlockModule; +import com.hypixel.hytale.server.core.modules.block.BlockReplaceEvent; +import com.hypixel.hytale.server.core.modules.block.components.ItemContainerBlock; +import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; + +public class ItemContainerSystems { + public static class OnAddedOrRemoved extends RefSystem { + private final ComponentType blockStateInfoComponentType = BlockModule.BlockStateInfo.getComponentType(); + private final ComponentType itemContainerBlockComponentType = ItemContainerBlock.getComponentType(); + private final Query query = Query.and(this.itemContainerBlockComponentType, this.blockStateInfoComponentType); + + @Override + public void onEntityAdded( + @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + BlockModule.BlockStateInfo blockStateInfoComponent = commandBuffer.getComponent(ref, this.blockStateInfoComponentType); + + assert blockStateInfoComponent != null; + + ItemContainerBlock itemContainerComponent = commandBuffer.getComponent(ref, this.itemContainerBlockComponentType); + + assert itemContainerComponent != null; + + Ref chunkRef = blockStateInfoComponent.getChunkRef(); + if (chunkRef.isValid()) { + int index = blockStateInfoComponent.getIndex(); + int x = ChunkUtil.xFromBlockInColumn(index); + int y = ChunkUtil.yFromBlockInColumn(index); + int z = ChunkUtil.zFromBlockInColumn(index); + BlockChunk blockChunkComponent = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(y); + int blockId = blockSection.get(x, y, z); + BlockType blockType = BlockType.getAssetMap().getAsset(blockId); + if (blockType != null && blockType.getBlockEntity() != null) { + short capacity = itemContainerComponent.getCapacity(); + ItemContainerBlock assetConfig = blockType.getBlockEntity().getComponent(this.itemContainerBlockComponentType); + if (assetConfig != null) { + capacity = assetConfig.getCapacity(); + } + + List remainder = new ObjectArrayList<>(); + SimpleItemContainer itemContainer = itemContainerComponent.getItemContainer(); + itemContainer = ItemContainer.ensureContainerCapacity(itemContainer, capacity, SimpleItemContainer::new, remainder); + World world = store.getExternalData().getWorld(); + itemContainer.registerChangeEvent(EventPriority.LAST, itemContainerChangeEvent -> { + if (world.isInThread()) { + blockStateInfoComponent.markNeedsSaving(); + } else { + world.execute(blockStateInfoComponent::markNeedsSaving); + } + }); + itemContainerComponent.setItemContainer(itemContainer); + if (!remainder.isEmpty()) { + Store entityStore = world.getEntityStore().getStore(); + Vector3d blockPosition = new Vector3d( + ChunkUtil.worldCoordFromLocalCoord(blockChunkComponent.getX(), x), y, ChunkUtil.worldCoordFromLocalCoord(blockChunkComponent.getZ(), z) + ); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, remainder, blockPosition, Rotation3f.IDENTITY); + entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); + } + } + } + } + + @Override + public void onEntityRemove( + @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + if (reason != RemoveReason.UNLOAD) { + BlockModule.BlockStateInfo blockStateInfoComponent = commandBuffer.getComponent(ref, this.blockStateInfoComponentType); + + assert blockStateInfoComponent != null; + + ItemContainerBlock itemContainerComponent = commandBuffer.getComponent(ref, this.itemContainerBlockComponentType); + + assert itemContainerComponent != null; + + WindowManager.closeAndRemoveAll(itemContainerComponent.getWindows()); + Ref chunkRef = blockStateInfoComponent.getChunkRef(); + if (chunkRef.isValid()) { + int index = blockStateInfoComponent.getIndex(); + int x = ChunkUtil.xFromBlockInColumn(index); + int y = ChunkUtil.yFromBlockInColumn(index); + int z = ChunkUtil.zFromBlockInColumn(index); + BlockChunk blockChunkComponent = commandBuffer.getComponent(chunkRef, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + World world = store.getExternalData().getWorld(); + Store entityStore = world.getEntityStore().getStore(); + Vector3d blockPosition = new Vector3d( + ChunkUtil.worldCoordFromLocalCoord(blockChunkComponent.getX(), x), y, ChunkUtil.worldCoordFromLocalCoord(blockChunkComponent.getZ(), z) + ); + List allItemStacks = itemContainerComponent.getItemContainer().dropAllItemStacks(); + Vector3d dropPosition = blockPosition.add(0.5, 0.0, 0.5); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, allItemStacks, dropPosition, Rotation3f.IDENTITY); + if (itemEntityHolders.length > 0) { + world.execute(() -> entityStore.addEntities(itemEntityHolders, AddReason.SPAWN)); + } + } + } + } + + @Nullable + @Override + public Query getQuery() { + return this.query; + } + } + + public static class OnReplaced extends EntityEventSystem { + private final ComponentType itemContainerBlockComponentType = ItemContainerBlock.getComponentType(); + + public OnReplaced() { + super(BlockReplaceEvent.class); + } + + public void handle( + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer, + @Nonnull BlockReplaceEvent event + ) { + ItemContainerBlock otherContainer = event.getNewEntity().getComponent(this.itemContainerBlockComponentType); + if (otherContainer != null) { + ItemContainerBlock selfContainer = archetypeChunk.getComponent(index, this.itemContainerBlockComponentType); + + assert selfContainer != null; + + selfContainer.getItemContainer().moveAllItemStacksTo(otherContainer.getItemContainer()); + } + } + + @Nullable + @Override + public Query getQuery() { + return this.itemContainerBlockComponentType; + } + } + + public static class OnReplacedHolder extends EntityHolderEventSystem { + private final ComponentType itemContainerBlockComponentType = ItemContainerBlock.getComponentType(); + + public OnReplacedHolder() { + super(BlockReplaceEvent.class); + } + + public void handle( + @Nonnull Holder holder, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer, + @Nonnull BlockReplaceEvent event + ) { + ItemContainerBlock otherContainer = event.getNewEntity().getComponent(this.itemContainerBlockComponentType); + if (otherContainer != null) { + ItemContainerBlock selfContainer = holder.getComponent(this.itemContainerBlockComponentType); + + assert selfContainer != null; + + selfContainer.getItemContainer().moveAllItemStacksTo(otherContainer.getItemContainer()); + } + } + + @Nullable + @Override + public Query getQuery() { + return this.itemContainerBlockComponentType; + } + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/blockhealth/BlockHealthChunk.java b/src/com/hypixel/hytale/server/core/modules/blockhealth/BlockHealthChunk.java index b1da8850..9878df93 100644 --- a/src/com/hypixel/hytale/server/core/modules/blockhealth/BlockHealthChunk.java +++ b/src/com/hypixel/hytale/server/core/modules/blockhealth/BlockHealthChunk.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.Component; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.packets.world.UpdateBlockDamage; @@ -22,6 +21,7 @@ import java.util.Objects; import java.util.Map.Entry; import java.util.function.Predicate; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BlockHealthChunk implements Component { private static final byte SERIALIZATION_VERSION = 2; @@ -68,7 +68,7 @@ public class BlockHealthChunk implements Component { }); if (blockHealth != null && !blockHealth.isDestroyed()) { Predicate filter = player -> true; - world.getNotificationHandler().updateBlockDamage(block.getX(), block.getY(), block.getZ(), blockHealth.getHealth(), -health, filter); + world.getNotificationHandler().updateBlockDamage(block.x(), block.y(), block.z(), blockHealth.getHealth(), -health, filter); } return Objects.requireNonNullElse(blockHealth, BlockHealth.NO_DAMAGE_INSTANCE); @@ -80,13 +80,13 @@ public class BlockHealthChunk implements Component { value.setHealth(value.getHealth() + progress); return (BlockHealth)(value.getHealth() > 1.0 ? value : null); }), BlockHealth.NO_DAMAGE_INSTANCE); - world.getNotificationHandler().updateBlockDamage(block.getX(), block.getY(), block.getZ(), blockHealth.getHealth(), progress); + world.getNotificationHandler().updateBlockDamage(block.x(), block.y(), block.z(), blockHealth.getHealth(), progress); return blockHealth; } public void removeBlock(@Nonnull World world, @Nonnull Vector3i block) { if (this.blockHealthMap.remove(block) != null) { - world.getNotificationHandler().updateBlockDamage(block.getX(), block.getY(), block.getZ(), BlockHealth.NO_DAMAGE_INSTANCE.getHealth(), 0.0F); + world.getNotificationHandler().updateBlockDamage(block.x(), block.y(), block.z(), BlockHealth.NO_DAMAGE_INSTANCE.getHealth(), 0.0F); } } @@ -112,7 +112,7 @@ public class BlockHealthChunk implements Component { public void createBlockDamagePackets(@Nonnull List list) { for (Entry entry : this.blockHealthMap.entrySet()) { Vector3i block = entry.getKey(); - BlockPosition blockPosition = new BlockPosition(block.getX(), block.getY(), block.getZ()); + BlockPosition blockPosition = new BlockPosition(block.x(), block.y(), block.z()); list.add(new UpdateBlockDamage(blockPosition, entry.getValue().getHealth(), 0.0F)); } } diff --git a/src/com/hypixel/hytale/server/core/modules/blockhealth/BlockHealthModule.java b/src/com/hypixel/hytale/server/core/modules/blockhealth/BlockHealthModule.java index f0ff66da..1c38b712 100644 --- a/src/com/hypixel/hytale/server/core/modules/blockhealth/BlockHealthModule.java +++ b/src/com/hypixel/hytale/server/core/modules/blockhealth/BlockHealthModule.java @@ -17,7 +17,6 @@ import com.hypixel.hytale.component.system.EntityEventSystem; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.packets.world.UpdateBlockDamage; @@ -43,6 +42,7 @@ import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class BlockHealthModule extends JavaPlugin { @Nonnull @@ -211,7 +211,7 @@ public class BlockHealthModule extends JavaPlugin { healthDelta = health - blockHealth.getHealth(); } - UpdateBlockDamage packet = new UpdateBlockDamage(new BlockPosition(position.getX(), position.getY(), position.getZ()), health, healthDelta); + UpdateBlockDamage packet = new UpdateBlockDamage(new BlockPosition(position.x(), position.y(), position.z()), health, healthDelta); for (int i = 0; i < visibleTo.size(); i++) { visibleTo.get(i).getPacketHandler().writeNoCache(packet); diff --git a/src/com/hypixel/hytale/server/core/modules/collision/BasicCollisionData.java b/src/com/hypixel/hytale/server/core/modules/collision/BasicCollisionData.java index 20dab917..3a717dce 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/BasicCollisionData.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/BasicCollisionData.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.server.core.modules.collision; -import com.hypixel.hytale.math.vector.Vector3d; import java.util.Comparator; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BasicCollisionData { public static final Comparator COLLISION_START_COMPARATOR = Comparator.comparingDouble(a -> a.collisionStart); @@ -10,7 +10,7 @@ public class BasicCollisionData { public double collisionStart; public void setStart(@Nonnull Vector3d point, double start) { - this.collisionPoint.assign(point); + this.collisionPoint.set(point); this.collisionStart = start; } } diff --git a/src/com/hypixel/hytale/server/core/modules/collision/BlockCollisionProvider.java b/src/com/hypixel/hytale/server/core/modules/collision/BlockCollisionProvider.java index eb7f032d..1bce8258 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/BlockCollisionProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/BlockCollisionProvider.java @@ -3,14 +3,14 @@ package com.hypixel.hytale.server.core.modules.collision; import com.hypixel.hytale.math.iterator.BoxBlockIterator; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.universe.world.World; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class BlockCollisionProvider implements BoxBlockIterator.BoxIterationConsumer { protected final BoxBlockIntersectionEvaluator boxBlockIntersectionEvaluator = new BoxBlockIntersectionEvaluator(); diff --git a/src/com/hypixel/hytale/server/core/modules/collision/BlockContactData.java b/src/com/hypixel/hytale/server/core/modules/collision/BlockContactData.java index 0ddd1d8f..29c66ded 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/BlockContactData.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/BlockContactData.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.server.core.modules.collision; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BlockContactData { protected final Vector3d collisionNormal = new Vector3d(); @@ -21,8 +21,8 @@ public class BlockContactData { } public void assign(@Nonnull BlockContactData other, int damage, boolean isSubmergedFluid) { - this.collisionNormal.assign(other.collisionNormal); - this.collisionPoint.assign(other.collisionPoint); + this.collisionNormal.set(other.collisionNormal); + this.collisionPoint.set(other.collisionPoint); this.collisionStart = other.collisionStart; this.collisionEnd = other.collisionEnd; this.onGround = other.onGround; diff --git a/src/com/hypixel/hytale/server/core/modules/collision/BlockTracker.java b/src/com/hypixel/hytale/server/core/modules/collision/BlockTracker.java index d2bd73e6..9b2cba4e 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/BlockTracker.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/BlockTracker.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.server.core.modules.collision; -import com.hypixel.hytale.math.vector.Vector3i; import java.util.Arrays; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BlockTracker implements IBlockTracker { public static final int NOT_FOUND = -1; diff --git a/src/com/hypixel/hytale/server/core/modules/collision/BoxBlockIntersectionEvaluator.java b/src/com/hypixel/hytale/server/core/modules/collision/BoxBlockIntersectionEvaluator.java index d337c88a..7f95157e 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/BoxBlockIntersectionEvaluator.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/BoxBlockIntersectionEvaluator.java @@ -1,13 +1,14 @@ package com.hypixel.hytale.server.core.modules.collision; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BoxBlockIntersectionEvaluator extends BlockContactData implements IBlockCollisionEvaluator { @Nonnull protected Box box = new Box(); - protected Vector3d worldUp = Vector3d.UP; + protected Vector3d worldUp = new Vector3d(Vector3dUtil.UP); protected boolean touchCeil; protected int resultCode; @@ -46,7 +47,7 @@ public class BoxBlockIntersectionEvaluator extends BlockContactData implements I @Nonnull public BoxBlockIntersectionEvaluator setPosition(@Nonnull Vector3d pos) { - this.collisionPoint.assign(pos); + this.collisionPoint.set(pos); return this; } @@ -77,37 +78,37 @@ public class BoxBlockIntersectionEvaluator extends BlockContactData implements I this.resultCode = code; this.onGround = false; this.touchCeil = false; - this.collisionNormal.assign(0.0, 0.0, 0.0); + this.collisionNormal.set(0.0, 0.0, 0.0); this.overlapping = CollisionMath.isOverlapping(this.resultCode); if ((code & 7) != 0) { if (this.worldUp.y != 0.0) { if ((code & 2) != 0) { - this.collisionNormal.assign(0.0, y + otherBox.min.y < this.collisionPoint.y + this.box.min.y ? 1.0 : -1.0, 0.0); + this.collisionNormal.set(0.0, y + otherBox.min.y < this.collisionPoint.y + this.box.min.y ? 1.0 : -1.0, 0.0); this.onGround = this.collisionNormal.y == this.worldUp.y; this.touchCeil = !this.onGround; } else if ((code & 1) != 0) { - this.collisionNormal.assign(x + otherBox.min.x < this.collisionPoint.x + this.box.min.x ? 1.0 : -1.0, 0.0, 0.0); + this.collisionNormal.set(x + otherBox.min.x < this.collisionPoint.x + this.box.min.x ? 1.0 : -1.0, 0.0, 0.0); } else { - this.collisionNormal.assign(0.0, 0.0, z + otherBox.min.z < this.collisionPoint.z + this.box.min.z ? 1.0 : -1.0); + this.collisionNormal.set(0.0, 0.0, z + otherBox.min.z < this.collisionPoint.z + this.box.min.z ? 1.0 : -1.0); } } else if (this.worldUp.x != 0.0) { if ((code & 1) != 0) { - this.collisionNormal.assign(x + otherBox.min.x < this.collisionPoint.x + this.box.min.x ? 1.0 : -1.0, 0.0, 0.0); + this.collisionNormal.set(x + otherBox.min.x < this.collisionPoint.x + this.box.min.x ? 1.0 : -1.0, 0.0, 0.0); this.onGround = this.collisionNormal.x == this.worldUp.x; this.touchCeil = !this.onGround; } else if ((code & 2) != 0) { - this.collisionNormal.assign(0.0, y + otherBox.min.y < this.collisionPoint.y + this.box.min.y ? 1.0 : -1.0, 0.0); + this.collisionNormal.set(0.0, y + otherBox.min.y < this.collisionPoint.y + this.box.min.y ? 1.0 : -1.0, 0.0); } else { - this.collisionNormal.assign(0.0, 0.0, z + otherBox.min.z < this.collisionPoint.z + this.box.min.z ? 1.0 : -1.0); + this.collisionNormal.set(0.0, 0.0, z + otherBox.min.z < this.collisionPoint.z + this.box.min.z ? 1.0 : -1.0); } } else if ((code & 4) != 0) { - this.collisionNormal.assign(0.0, 0.0, z + otherBox.min.z < this.collisionPoint.z + this.box.min.z ? 1.0 : -1.0); + this.collisionNormal.set(0.0, 0.0, z + otherBox.min.z < this.collisionPoint.z + this.box.min.z ? 1.0 : -1.0); this.onGround = this.collisionNormal.z == this.worldUp.z; this.touchCeil = !this.onGround; } else if ((code & 2) != 0) { - this.collisionNormal.assign(0.0, y + otherBox.min.y < this.collisionPoint.y + this.box.min.y ? 1.0 : -1.0, 0.0); + this.collisionNormal.set(0.0, y + otherBox.min.y < this.collisionPoint.y + this.box.min.y ? 1.0 : -1.0, 0.0); } else { - this.collisionNormal.assign(x + otherBox.min.x < this.collisionPoint.x + this.box.min.x ? 1.0 : -1.0, 0.0, 0.0); + this.collisionNormal.set(x + otherBox.min.x < this.collisionPoint.x + this.box.min.x ? 1.0 : -1.0, 0.0, 0.0); } } diff --git a/src/com/hypixel/hytale/server/core/modules/collision/BoxCollisionData.java b/src/com/hypixel/hytale/server/core/modules/collision/BoxCollisionData.java index e0b058b9..ca64cbe7 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/BoxCollisionData.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/BoxCollisionData.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.server.core.modules.collision; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BoxCollisionData extends BasicCollisionData { public double collisionEnd; @@ -9,6 +9,6 @@ public class BoxCollisionData extends BasicCollisionData { public void setEnd(double collisionEnd, @Nonnull Vector3d collisionNormal) { this.collisionEnd = collisionEnd; - this.collisionNormal.assign(collisionNormal); + this.collisionNormal.set(collisionNormal); } } diff --git a/src/com/hypixel/hytale/server/core/modules/collision/CharacterCollisionData.java b/src/com/hypixel/hytale/server/core/modules/collision/CharacterCollisionData.java index 53f30bcb..cc9eaa12 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/CharacterCollisionData.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/CharacterCollisionData.java @@ -1,16 +1,16 @@ package com.hypixel.hytale.server.core.modules.collision; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CharacterCollisionData extends BasicCollisionData { public Ref entityReference; public boolean isPlayer; public void assign(@Nonnull Vector3d collisionPoint, double collisionVectorScale, Ref entityReference, boolean isPlayer) { - this.collisionPoint.assign(collisionPoint); + this.collisionPoint.set(collisionPoint); this.collisionStart = collisionVectorScale; this.entityReference = entityReference; this.isPlayer = isPlayer; diff --git a/src/com/hypixel/hytale/server/core/modules/collision/CollisionMath.java b/src/com/hypixel/hytale/server/core/modules/collision/CollisionMath.java index a47225fc..03b8d6cd 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/CollisionMath.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/CollisionMath.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.server.core.modules.collision; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector3d; public class CollisionMath { public static final ThreadLocal MIN_MAX = ThreadLocal.withInitial(Vector2d::new); @@ -35,9 +35,9 @@ public class CollisionMath { minMax.y = Double.MAX_VALUE; Vector3d min = box.getMin(); Vector3d max = box.getMax(); - return intersect1D(pos.x, ray.getX(), x + min.x, x + max.x, minMax) - && intersect1D(pos.y, ray.getY(), y + min.y, y + max.y, minMax) - && intersect1D(pos.z, ray.getZ(), z + min.z, z + max.z, minMax) + return intersect1D(pos.x, ray.x(), x + min.x, x + max.x, minMax) + && intersect1D(pos.y, ray.y(), y + min.y, y + max.y, minMax) + && intersect1D(pos.z, ray.z(), z + min.z, z + max.z, minMax) && minMax.x >= 0.0; } @@ -57,9 +57,9 @@ public class CollisionMath { ) { minMax.x = 0.0; minMax.y = Double.MAX_VALUE; - return intersect1D(pos.x, ray.getX(), x - radius, x + radius, minMax) - && intersect1D(pos.y, ray.getY(), y, y + height, minMax) - && intersect1D(pos.z, ray.getZ(), z - radius, z + radius, minMax) + return intersect1D(pos.x, ray.x(), x - radius, x + radius, minMax) + && intersect1D(pos.y, ray.y(), y, y + height, minMax) + && intersect1D(pos.z, ray.z(), z - radius, z + radius, minMax) && minMax.x >= 0.0; } diff --git a/src/com/hypixel/hytale/server/core/modules/collision/CollisionModule.java b/src/com/hypixel/hytale/server/core/modules/collision/CollisionModule.java index 7dddd400..18002e93 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/CollisionModule.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/CollisionModule.java @@ -10,8 +10,7 @@ import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.spatial.KDTree; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.entity.Entity; @@ -32,6 +31,8 @@ import java.util.function.Predicate; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; public class CollisionModule extends JavaPlugin { @Nonnull @@ -170,7 +171,10 @@ public class CollisionModule extends JavaPlugin { result.getLogger() .at(Level.INFO) .log( - ">>>>>> Start findBlockCollisionIterative collider=[%s] pos=%s dir=%s", collider, Vector3d.formatShortString(pos), Vector3d.formatShortString(v) + ">>>>>> Start findBlockCollisionIterative collider=[%s] pos=%s dir=%s", + collider, + Vector3dUtil.formatShortString(pos), + Vector3dUtil.formatShortString(v) ); } @@ -184,7 +188,7 @@ public class CollisionModule extends JavaPlugin { result.acquireCollisionModule(); collider.forEachBlock(pos, 1.0E-5, result, (x, y, z, aResult) -> aResult.accept(x, y, z)); if (result.shouldLog()) { - result.getLogger().at(Level.INFO).log(">>>> line collider=[%s] dir=%s len=%s", collider, Vector3d.formatShortString(v), v.length()); + result.getLogger().at(Level.INFO).log(">>>> line collider=[%s] dir=%s len=%s", collider, Vector3dUtil.formatShortString(v), v.length()); } result.iterateBlocks(collider, pos, v, v.length(), stopOnCollisionFound); @@ -218,9 +222,8 @@ public class CollisionModule extends JavaPlugin { if (entityBoundingBoxComponent != null) { Vector3d position = entityTransformComponent.getPosition(); Box boundingBox = entityBoundingBoxComponent.getBoundingBox(); - if (boundingBox != null - && CollisionMath.intersectVectorAABB(pos, v, position.getX(), position.getY(), position.getZ(), boundingBox, minMax)) { - coll.assign(pos).addScaled(v, minMax.x); + if (boundingBox != null && CollisionMath.intersectVectorAABB(pos, v, position.x(), position.y(), position.z(), boundingBox, minMax)) { + coll.set(pos).fma(minMax.x, v); result.allocCharacterCollision().assign(coll, minMax.x, entity.getReference(), entity instanceof Player); } } @@ -342,7 +345,7 @@ public class CollisionModule extends JavaPlugin { "++ Short: Sliding block start=%s end=%s normal=%s", boxBlockIntersectionEvaluator.getCollisionStart(), boxBlockIntersectionEvaluator.getCollisionEnd(), - Vector3d.formatShortString(boxBlockIntersectionEvaluator.getCollisionNormal()) + Vector3dUtil.formatShortString(boxBlockIntersectionEvaluator.getCollisionNormal()) ); } @@ -356,7 +359,7 @@ public class CollisionModule extends JavaPlugin { "?? Short: Sliding block is unwalkable start=%s end=%s normal=%s", boxBlockIntersectionEvaluator.getCollisionStart(), boxBlockIntersectionEvaluator.getCollisionEnd(), - Vector3d.formatShortString(boxBlockIntersectionEvaluator.getCollisionNormal()) + Vector3dUtil.formatShortString(boxBlockIntersectionEvaluator.getCollisionNormal()) ); } } @@ -370,7 +373,7 @@ public class CollisionModule extends JavaPlugin { "++ Short: Collision with block start=%s end=%s normal=%s", boxBlockIntersectionEvaluator.getCollisionStart(), boxBlockIntersectionEvaluator.getCollisionEnd(), - Vector3d.formatShortString(boxBlockIntersectionEvaluator.getCollisionNormal()) + Vector3dUtil.formatShortString(boxBlockIntersectionEvaluator.getCollisionNormal()) ); } } @@ -383,7 +386,7 @@ public class CollisionModule extends JavaPlugin { "++ Short: Trigger block start=%s end=%s normal=%s", boxBlockIntersectionEvaluator.getCollisionStart(), boxBlockIntersectionEvaluator.getCollisionEnd(), - Vector3d.formatShortString(boxBlockIntersectionEvaluator.getCollisionNormal()) + Vector3dUtil.formatShortString(boxBlockIntersectionEvaluator.getCollisionNormal()) ); } @@ -589,7 +592,7 @@ public class CollisionModule extends JavaPlugin { } public static boolean isBelowMovementThreshold(@Nonnull Vector3d v) { - return v.squaredLength() < 1.0000000000000002E-10; + return v.lengthSquared() < 1.0000000000000002E-10; } private static void logOverlap( @@ -604,7 +607,7 @@ public class CollisionModule extends JavaPlugin { (intersectType & 16) != 0 ? "Y" : "", (intersectType & 32) != 0 ? "Z" : "", index, - Vector3d.formatShortString(pos), + Vector3dUtil.formatShortString(pos), x + config.getBoundingBoxOffsetX(), y + config.getBoundingBoxOffsetY(), z + config.getBoundingBoxOffsetZ(), @@ -612,8 +615,8 @@ public class CollisionModule extends JavaPlugin { config.blockMaterial != null ? config.blockMaterial.name() : "none", config.blockType != null ? config.blockType.getId() : "none", collider, - Vector3d.formatShortString(hitBox.min), - Vector3d.formatShortString(hitBox.max) + Vector3dUtil.formatShortString(hitBox.min), + Vector3dUtil.formatShortString(hitBox.max) ); } } diff --git a/src/com/hypixel/hytale/server/core/modules/collision/CollisionResult.java b/src/com/hypixel/hytale/server/core/modules/collision/CollisionResult.java index a7ee1276..ce8e7177 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/CollisionResult.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/CollisionResult.java @@ -7,7 +7,7 @@ import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.iterator.BoxBlockIterator; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -32,6 +32,7 @@ import java.util.function.Predicate; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CollisionResult implements BoxBlockIterator.BoxIterationConsumer { public static final Comparator BLOCK_COLLISION_DATA_COMPARATOR = Comparator.comparingDouble(a -> a.collisionStart) @@ -407,7 +408,7 @@ public class CollisionResult implements BoxBlockIterator.BoxIterationConsumer { "++ Sliding block start=%s end=%s normal=%s", this.movingBoxBoxCollision.getCollisionStart(), this.movingBoxBoxCollision.getCollisionEnd(), - Vector3d.formatShortString(this.movingBoxBoxCollision.getCollisionNormal()) + Vector3dUtil.formatShortString(this.movingBoxBoxCollision.getCollisionNormal()) ); } @@ -422,7 +423,7 @@ public class CollisionResult implements BoxBlockIterator.BoxIterationConsumer { "?? Sliding block is unwalkable start=%s end=%s normal=%s", this.movingBoxBoxCollision.getCollisionStart(), this.movingBoxBoxCollision.getCollisionEnd(), - Vector3d.formatShortString(this.movingBoxBoxCollision.getCollisionNormal()) + Vector3dUtil.formatShortString(this.movingBoxBoxCollision.getCollisionNormal()) ); } } @@ -440,7 +441,7 @@ public class CollisionResult implements BoxBlockIterator.BoxIterationConsumer { "++ Collision with block start=%s end=%s normal=%s", this.movingBoxBoxCollision.collisionStart, this.movingBoxBoxCollision.collisionEnd, - Vector3d.formatShortString(this.movingBoxBoxCollision.collisionNormal) + Vector3dUtil.formatShortString(this.movingBoxBoxCollision.collisionNormal) ); } } @@ -454,7 +455,7 @@ public class CollisionResult implements BoxBlockIterator.BoxIterationConsumer { "++ Trigger block start=%s end=%s normal=%s", this.movingBoxBoxCollision.getCollisionStart(), this.movingBoxBoxCollision.getCollisionEnd(), - Vector3d.formatShortString(this.movingBoxBoxCollision.getCollisionNormal()) + Vector3dUtil.formatShortString(this.movingBoxBoxCollision.getCollisionNormal()) ); } diff --git a/src/com/hypixel/hytale/server/core/modules/collision/EntityCollisionProvider.java b/src/com/hypixel/hytale/server/core/modules/collision/EntityCollisionProvider.java index c3868e61..6dd6589d 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/EntityCollisionProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/EntityCollisionProvider.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.entities.ProjectileComponent; @@ -16,11 +14,13 @@ import com.hypixel.hytale.server.core.modules.entity.component.TransformComponen import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.modules.projectile.component.Projectile; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.function.BiConsumer; import java.util.function.BiPredicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; public class EntityCollisionProvider { protected static final int ALLOC_SIZE = 4; @@ -129,7 +129,7 @@ public class EntityCollisionProvider { this.position = pos; this.direction = dir; this.boundingBox = boundingBox; - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> entitySpatialResource = componentAccessor.getResource(EntityModule.get().getEntitySpatialResourceType()); entitySpatialResource.getSpatialStructure().collect(pos, radius, results); @@ -151,7 +151,7 @@ public class EntityCollisionProvider { } protected void setContact(@Nonnull Entity entity) { - this.collisionPosition.assign(this.position).addScaled(this.direction, this.minMax.x); + this.collisionPosition.set(this.position).fma(this.minMax.x, this.direction); this.contacts[0].assign(this.collisionPosition, this.minMax.x, this.minMax.y, entity.getReference(), null); this.count = 1; } diff --git a/src/com/hypixel/hytale/server/core/modules/collision/EntityContactData.java b/src/com/hypixel/hytale/server/core/modules/collision/EntityContactData.java index 28240e1a..35959e27 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/EntityContactData.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/EntityContactData.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.server.core.modules.collision; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class EntityContactData { protected final Vector3d collisionPoint = new Vector3d(); @@ -37,7 +37,7 @@ public class EntityContactData { } public void assign(@Nonnull Vector3d position, double start, double end, Ref entity, String collisionDetailName) { - this.collisionPoint.assign(position); + this.collisionPoint.set(position); this.collisionStart = start; this.collisionEnd = end; this.entityReference = entity; diff --git a/src/com/hypixel/hytale/server/core/modules/collision/EntityRefCollisionProvider.java b/src/com/hypixel/hytale/server/core/modules/collision/EntityRefCollisionProvider.java index cd63093a..7a46f090 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/EntityRefCollisionProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/EntityRefCollisionProvider.java @@ -6,8 +6,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.function.consumer.TriConsumer; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.model.config.DetailBox; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.EntityUtils; @@ -16,12 +14,14 @@ import com.hypixel.hytale.server.core.modules.entity.component.TransformComponen import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.modules.projectile.component.Projectile; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import java.util.Map.Entry; import java.util.function.BiPredicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; public class EntityRefCollisionProvider { protected static final int ALLOC_SIZE = 4; @@ -46,7 +46,7 @@ public class EntityRefCollisionProvider { @Nullable protected Ref ignoreOther; @Nonnull - protected List> tmpResults = new ObjectArrayList<>(); + protected List> tmpResults = new ReferenceArrayList<>(); @Nonnull protected Vector3d tmpVector = new Vector3d(); @Nullable @@ -139,7 +139,7 @@ public class EntityRefCollisionProvider { } protected void setContact(@Nonnull Ref ref, @Nonnull String detailName) { - this.collisionPosition.assign(this.position).addScaled(this.direction, this.minMax.x); + this.collisionPosition.set(this.position).fma(this.minMax.x, this.direction); this.contacts[0].assign(this.collisionPosition, this.minMax.x, this.minMax.y, ref, detailName); this.count = 1; } @@ -157,8 +157,8 @@ public class EntityRefCollisionProvider { if (boundingBoxComponent.getDetailBoxes() != null && !boundingBoxComponent.getDetailBoxes().isEmpty()) { for (Entry e : boundingBoxComponent.getDetailBoxes().entrySet()) { for (DetailBox v : e.getValue()) { - this.tmpVector.assign(v.getOffset()); - this.tmpVector.rotateY(transformComponent.getRotation().getYaw()); + this.tmpVector.set(v.getOffset()); + this.tmpVector.rotateY(transformComponent.getRotation().yaw()); this.tmpVector.add(transformComponent.getPosition()); if (CollisionMath.intersectSweptAABBs(this.position, this.direction, this.boundingBox, this.tmpVector, v.getBox(), minMax, this.tempBox) && minMax.x <= 1.0) { diff --git a/src/com/hypixel/hytale/server/core/modules/collision/IBlockCollisionConsumer.java b/src/com/hypixel/hytale/server/core/modules/collision/IBlockCollisionConsumer.java index a8bab5d5..d4bd7063 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/IBlockCollisionConsumer.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/IBlockCollisionConsumer.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.server.core.modules.collision; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; +import org.joml.Vector3d; public interface IBlockCollisionConsumer { IBlockCollisionConsumer.Result onCollision(int var1, int var2, int var3, Vector3d var4, BlockContactData var5, BlockData var6, Box var7); diff --git a/src/com/hypixel/hytale/server/core/modules/collision/IBlockTracker.java b/src/com/hypixel/hytale/server/core/modules/collision/IBlockTracker.java index ba341a86..7370a629 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/IBlockTracker.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/IBlockTracker.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.server.core.modules.collision; -import com.hypixel.hytale.math.vector.Vector3i; +import org.joml.Vector3i; public interface IBlockTracker { Vector3i getPosition(int var1); diff --git a/src/com/hypixel/hytale/server/core/modules/collision/MovingBoxBoxCollisionEvaluator.java b/src/com/hypixel/hytale/server/core/modules/collision/MovingBoxBoxCollisionEvaluator.java index 12ce64b2..76db9737 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/MovingBoxBoxCollisionEvaluator.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/MovingBoxBoxCollisionEvaluator.java @@ -2,8 +2,8 @@ package com.hypixel.hytale.server.core.modules.collision; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class MovingBoxBoxCollisionEvaluator extends BlockContactData implements IBlockCollisionEvaluator { protected boolean touching; @@ -67,8 +67,8 @@ public class MovingBoxBoxCollisionEvaluator extends BlockContactData implements @Nonnull public MovingBoxBoxCollisionEvaluator setMove(@Nonnull Vector3d pos, @Nonnull Vector3d v) { - this.pos.assign(pos); - this.v.assign(v); + this.pos.set(pos); + this.v.set(v); this.cX.v = v.x; this.cY.v = v.y; this.cZ.v = v.z; @@ -95,20 +95,20 @@ public class MovingBoxBoxCollisionEvaluator extends BlockContactData implements } else { this.collisionStart = 0.0; this.collisionEnd = Double.MAX_VALUE; - this.collisionNormal.assign(0.0, 0.0, 0.0); + this.collisionNormal.set(0.0, 0.0, 0.0); if (this.cX.tLeave < this.collisionEnd) { this.collisionEnd = this.cX.tLeave; - this.collisionNormal.assign(this.cX.normal, 0.0, 0.0); + this.collisionNormal.set(this.cX.normal, 0.0, 0.0); } if (this.cY.tLeave < this.collisionEnd) { this.collisionEnd = this.cY.tLeave; - this.collisionNormal.assign(0.0, this.cY.normal, 0.0); + this.collisionNormal.set(0.0, this.cY.normal, 0.0); } if (this.cZ.tLeave < this.collisionEnd) { this.collisionEnd = this.cZ.tLeave; - this.collisionNormal.assign(0.0, 0.0, this.cZ.normal); + this.collisionNormal.set(0.0, 0.0, this.cZ.normal); } return true; @@ -117,17 +117,17 @@ public class MovingBoxBoxCollisionEvaluator extends BlockContactData implements this.collisionStart = -Double.MAX_VALUE; this.collisionEnd = Double.MAX_VALUE; if (this.cX.kind == 0) { - this.collisionNormal.assign(this.cX.normal, 0.0, 0.0); + this.collisionNormal.set(this.cX.normal, 0.0, 0.0); this.collisionStart = this.cX.tEnter; } if (this.cY.kind == 0 && this.cY.tEnter > this.collisionStart) { - this.collisionNormal.assign(0.0, this.cY.normal, 0.0); + this.collisionNormal.set(0.0, this.cY.normal, 0.0); this.collisionStart = this.cY.tEnter; } if (this.cZ.kind == 0 && this.cZ.tEnter > this.collisionStart) { - this.collisionNormal.assign(0.0, 0.0, this.cZ.normal); + this.collisionNormal.set(0.0, 0.0, this.cZ.normal); this.collisionStart = this.cZ.tEnter; } @@ -135,9 +135,9 @@ public class MovingBoxBoxCollisionEvaluator extends BlockContactData implements if (this.checkForOnGround && this.cY.kind == 3) { this.collisionStart = MathUtil.maxValue(this.cX.tEnter, this.cY.tEnter, this.cZ.tEnter); this.collisionEnd = MathUtil.minValue(this.cX.tLeave, this.cY.tLeave, this.cZ.tLeave); - this.collisionPoint.assign(this.pos); - this.collisionPoint.addScaled(this.v, this.collisionStart); - this.collisionNormal.assign(0.0, this.cY.normal, 0.0); + this.collisionPoint.set(this.pos); + this.collisionPoint.fma(this.collisionStart, this.v); + this.collisionNormal.set(0.0, this.cY.normal, 0.0); this.onGround = true; this.touching = true; } @@ -148,10 +148,10 @@ public class MovingBoxBoxCollisionEvaluator extends BlockContactData implements if (this.collisionStart > this.collisionEnd) { return false; } else { - this.collisionPoint.assign(this.pos); - this.collisionPoint.addScaled(this.v, this.collisionStart); + this.collisionPoint.set(this.pos); + this.collisionPoint.fma(this.collisionStart, this.v); if (this.checkForOnGround && this.cY.kind == 3) { - this.collisionNormal.assign(0.0, this.cY.normal, 0.0); + this.collisionNormal.set(0.0, this.cY.normal, 0.0); this.onGround = true; this.touching = true; return false; diff --git a/src/com/hypixel/hytale/server/core/modules/collision/TangiableEntitySpatialSystem.java b/src/com/hypixel/hytale/server/core/modules/collision/TangiableEntitySpatialSystem.java index b8f89ac2..0daeda75 100644 --- a/src/com/hypixel/hytale/server/core/modules/collision/TangiableEntitySpatialSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/collision/TangiableEntitySpatialSystem.java @@ -7,12 +7,12 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; import com.hypixel.hytale.server.core.modules.entity.component.Intangible; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TangiableEntitySpatialSystem extends SpatialSystem { private static final Query QUERY = Query.and( diff --git a/src/com/hypixel/hytale/server/core/modules/debug/DebugUtils.java b/src/com/hypixel/hytale/server/core/modules/debug/DebugUtils.java index 070b83dc..0e4c9bec 100644 --- a/src/com/hypixel/hytale/server/core/modules/debug/DebugUtils.java +++ b/src/com/hypixel/hytale/server/core/modules/debug/DebugUtils.java @@ -1,8 +1,7 @@ package com.hypixel.hytale.server.core.modules.debug; -import com.hypixel.hytale.math.matrix.Matrix4d; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.matrix.Matrix4dUtil; +import com.hypixel.hytale.protocol.DebugFlags; import com.hypixel.hytale.protocol.DebugShape; import com.hypixel.hytale.protocol.packets.player.ClearDebugShapes; import com.hypixel.hytale.protocol.packets.player.DisplayDebug; @@ -14,6 +13,9 @@ import com.hypixel.hytale.server.core.universe.world.World; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Matrix4d; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DebugUtils { public static final Vector3f COLOR_BLACK = new Vector3f(0.0F, 0.0F, 0.0F); @@ -38,15 +40,19 @@ public class DebugUtils { public static final String[] INDEXED_COLOR_NAMES = new String[]{"Red", "Blue", "Lime", "Yellow", "Cyan", "Magenta", "Purple", "Green"}; public static boolean DISPLAY_FORCES = false; public static final float DEFAULT_OPACITY = 0.8F; + public static final int FLAG_NONE = 0; + public static final int FLAG_FADE = 1 << DebugFlags.Fade.getValue(); + public static final int FLAG_NO_WIREFRAME = 1 << DebugFlags.NoWireframe.getValue(); + public static final int FLAG_NO_SOLID = 1 << DebugFlags.NoSolid.getValue(); - public static void add(@Nonnull World world, @Nonnull DebugShape shape, @Nonnull Matrix4d matrix, @Nonnull Vector3f color, float time, boolean fade) { - add(world, shape, matrix, color, 0.8F, time, fade, null); + public static void add(@Nonnull World world, @Nonnull DebugShape shape, @Nonnull Matrix4d matrix, @Nonnull Vector3f color, float time, int flags) { + add(world, shape, matrix, color, 0.8F, time, flags, null); } public static void add( - @Nonnull World world, @Nonnull DebugShape shape, @Nonnull Matrix4d matrix, @Nonnull Vector3f color, float opacity, float time, boolean fade + @Nonnull World world, @Nonnull DebugShape shape, @Nonnull Matrix4d matrix, @Nonnull Vector3f color, float opacity, float time, int flags ) { - add(world, shape, matrix, color, opacity, time, fade, null); + add(world, shape, matrix, color, opacity, time, flags, null); } private static void add( @@ -56,12 +62,10 @@ public class DebugUtils { @Nonnull Vector3f color, float opacity, float time, - boolean fade, + int flags, @Nullable float[] shapeParams ) { - DisplayDebug packet = new DisplayDebug( - shape, matrix.asFloatData(), new com.hypixel.hytale.protocol.Vector3f(color.x, color.y, color.z), time, fade, shapeParams, opacity - ); + DisplayDebug packet = new DisplayDebug(shape, Matrix4dUtil.asFloatData(matrix), color, time, (byte)flags, shapeParams, opacity); for (PlayerRef playerRef : world.getPlayerRefs()) { playerRef.getPacketHandler().write(packet); @@ -69,9 +73,9 @@ public class DebugUtils { } public static void addFrustum( - @Nonnull World world, @Nonnull Matrix4d matrix, @Nonnull Matrix4d frustumProjection, @Nonnull Vector3f color, float time, boolean fade + @Nonnull World world, @Nonnull Matrix4d matrix, @Nonnull Matrix4d frustumProjection, @Nonnull Vector3f color, float time, int flags ) { - add(world, DebugShape.Frustum, matrix, color, 0.8F, time, fade, frustumProjection.asFloatData()); + add(world, DebugShape.Frustum, matrix, color, 0.8F, time, flags, Matrix4dUtil.asFloatData(frustumProjection)); } public static void clear(@Nonnull World world) { @@ -82,19 +86,23 @@ public class DebugUtils { } } - public static void addArrow(@Nonnull World world, @Nonnull Matrix4d baseMatrix, @Nonnull Vector3f color, double length, float time, boolean fade) { + public static void addArrow(@Nonnull World world, @Nonnull Matrix4d baseMatrix, @Nonnull Vector3f color, float opacity, double length, float time, int flags) { double adjustedLength = length - 0.3; if (adjustedLength > 0.0) { Matrix4d matrix = new Matrix4d(baseMatrix); matrix.translate(0.0, adjustedLength * 0.5, 0.0); matrix.scale(0.1F, adjustedLength, 0.1F); - add(world, DebugShape.Cylinder, matrix, color, time, fade); + add(world, DebugShape.Cylinder, matrix, color, time, flags); } Matrix4d matrix = new Matrix4d(baseMatrix); matrix.translate(0.0, adjustedLength + 0.15, 0.0); matrix.scale(0.3F, 0.3F, 0.3F); - add(world, DebugShape.Cone, matrix, color, time, fade); + add(world, DebugShape.Cone, matrix, color, opacity, time, flags); + } + + public static void addArrow(@Nonnull World world, @Nonnull Matrix4d baseMatrix, @Nonnull Vector3f color, double length, float time, int flags) { + addArrow(world, baseMatrix, color, 0.8F, length, time, flags); } public static void addSphere(@Nonnull World world, @Nonnull Vector3d pos, @Nonnull Vector3f color, double scale, float time) { @@ -106,12 +114,24 @@ public class DebugUtils { matrix.identity(); matrix.translate(x, y, z); matrix.scale(scale, scale, scale); - add(world, DebugShape.Sphere, matrix, color, time, true); + add(world, DebugShape.Sphere, matrix, color, time, FLAG_FADE); + } + + public static void addSphere(@Nonnull World world, @Nonnull Vector3d pos, @Nonnull Vector3f color, float opacity, double scale, float time) { + addSphere(world, pos.x, pos.y, pos.z, color, opacity, scale, time); + } + + public static void addSphere(@Nonnull World world, double x, double y, double z, @Nonnull Vector3f color, float opacity, double scale, float time) { + Matrix4d matrix = new Matrix4d(); + matrix.identity(); + matrix.translate(x, y, z); + matrix.scale(scale, scale, scale); + add(world, DebugShape.Sphere, matrix, color, opacity, time, FLAG_FADE); } public static void addCone(@Nonnull World world, @Nonnull Vector3d pos, @Nonnull Vector3f color, double scale, float time) { Matrix4d matrix = makeMatrix(pos, scale); - add(world, DebugShape.Cone, matrix, color, time, true); + add(world, DebugShape.Cone, matrix, color, time, FLAG_FADE); } public static void addCube(@Nonnull World world, @Nonnull Vector3d pos, @Nonnull Vector3f color, double scale, float time) { @@ -123,18 +143,18 @@ public class DebugUtils { matrix.identity(); matrix.translate(x, y, z); matrix.scale(scale, scale, scale); - add(world, DebugShape.Cube, matrix, color, time, true); + add(world, DebugShape.Cube, matrix, color, time, FLAG_FADE); } public static void addCylinder(@Nonnull World world, @Nonnull Vector3d pos, @Nonnull Vector3f color, double scale, float time) { Matrix4d matrix = makeMatrix(pos, scale); - add(world, DebugShape.Cylinder, matrix, color, time, true); + add(world, DebugShape.Cylinder, matrix, color, time, FLAG_FADE); } public static void addLine( - @Nonnull World world, @Nonnull Vector3d start, @Nonnull Vector3d end, @Nonnull Vector3f color, double thickness, float time, boolean fade + @Nonnull World world, @Nonnull Vector3d start, @Nonnull Vector3d end, @Nonnull Vector3f color, double thickness, float time, int flags ) { - addLine(world, start.x, start.y, start.z, end.x, end.y, end.z, color, thickness, time, fade); + addLine(world, start.x, start.y, start.z, end.x, end.y, end.z, color, thickness, time, flags); } public static void addLine( @@ -148,24 +168,24 @@ public class DebugUtils { @Nonnull Vector3f color, double thickness, float time, - boolean fade + int flags ) { double dirX = endX - startX; double dirY = endY - startY; double dirZ = endZ - startZ; double length = Math.sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ); if (!(length < 0.001)) { - Matrix4d tmp = new Matrix4d(); + new Matrix4d(); Matrix4d matrix = new Matrix4d(); matrix.identity(); matrix.translate(startX, startY, startZ); double angleY = Math.atan2(dirZ, dirX); - matrix.rotateAxis(angleY + (Math.PI / 2), 0.0, 1.0, 0.0, tmp); + matrix.rotate(-(angleY + (Math.PI / 2)), 0.0, 1.0, 0.0); double angleX = Math.atan2(Math.sqrt(dirX * dirX + dirZ * dirZ), dirY); - matrix.rotateAxis(angleX, 1.0, 0.0, 0.0, tmp); + matrix.rotate(-angleX, 1.0, 0.0, 0.0); matrix.translate(0.0, length / 2.0, 0.0); matrix.scale(thickness, length, thickness); - add(world, DebugShape.Cylinder, matrix, color, time, fade); + add(world, DebugShape.Cylinder, matrix, color, time, flags); } } @@ -178,47 +198,36 @@ public class DebugUtils { float opacity, int segmentCount, float time, - boolean fade + int flags ) { float[] shapeParams = new float[]{ (float)outerRadius, segmentCount, (float)innerRadius, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F }; - add(world, DebugShape.Disc, matrix, color, opacity, time, fade, shapeParams); + add(world, DebugShape.Disc, matrix, color, opacity, time, flags, shapeParams); } public static void addDisc( - @Nonnull World world, @Nonnull Matrix4d matrix, double outerRadius, double innerRadius, @Nonnull Vector3f color, float opacity, float time, boolean fade + @Nonnull World world, @Nonnull Matrix4d matrix, double outerRadius, double innerRadius, @Nonnull Vector3f color, float opacity, float time, int flags ) { - addDisc(world, matrix, outerRadius, innerRadius, color, opacity, 32, time, fade); + addDisc(world, matrix, outerRadius, innerRadius, color, opacity, 32, time, flags); } - public static void addDisc(@Nonnull World world, @Nonnull Vector3d center, double radius, @Nonnull Vector3f color, float time, boolean fade) { - addDisc(world, center.x, center.y, center.z, radius, 0.0, color, 0.8F, time, fade); + public static void addDisc(@Nonnull World world, @Nonnull Vector3d center, double radius, @Nonnull Vector3f color, float time, int flags) { + addDisc(world, center.x, center.y, center.z, radius, 0.0, color, 0.8F, time, flags); } - public static void addDisc(@Nonnull World world, double x, double y, double z, double radius, @Nonnull Vector3f color, float time, boolean fade) { - addDisc(world, x, y, z, radius, 0.0, color, 0.8F, time, fade); + public static void addDisc(@Nonnull World world, double x, double y, double z, double radius, @Nonnull Vector3f color, float time, int flags) { + addDisc(world, x, y, z, radius, 0.0, color, 0.8F, time, flags); + } + + public static void addDisc(@Nonnull World world, double x, double y, double z, double radius, @Nonnull Vector3f color, float opacity, float time, int flags) { + addDisc(world, x, y, z, radius, 0.0, color, opacity, 32, time, flags); } public static void addDisc( - @Nonnull World world, double x, double y, double z, double radius, @Nonnull Vector3f color, float opacity, float time, boolean fade + @Nonnull World world, double x, double y, double z, double outerRadius, double innerRadius, @Nonnull Vector3f color, float opacity, float time, int flags ) { - addDisc(world, x, y, z, radius, 0.0, color, opacity, 32, time, fade); - } - - public static void addDisc( - @Nonnull World world, - double x, - double y, - double z, - double outerRadius, - double innerRadius, - @Nonnull Vector3f color, - float opacity, - float time, - boolean fade - ) { - addDisc(world, x, y, z, outerRadius, innerRadius, color, opacity, 32, time, fade); + addDisc(world, x, y, z, outerRadius, innerRadius, color, opacity, 32, time, flags); } public static void addDisc( @@ -232,18 +241,18 @@ public class DebugUtils { float opacity, int segmentCount, float time, - boolean fade + int flags ) { Matrix4d matrix = new Matrix4d(); matrix.identity(); matrix.translate(x, y, z); - addDisc(world, matrix, outerRadius, innerRadius, color, opacity, segmentCount, time, fade); + addDisc(world, matrix, outerRadius, innerRadius, color, opacity, segmentCount, time, flags); } public static void addSector( - @Nonnull World world, double x, double y, double z, double heading, double radius, double angle, @Nonnull Vector3f color, float time, boolean fade + @Nonnull World world, double x, double y, double z, double heading, double radius, double angle, @Nonnull Vector3f color, float time, int flags ) { - addSector(world, x, y, z, heading, radius, angle, 0.0, color, 0.8F, 16, time, fade); + addSector(world, x, y, z, heading, radius, angle, 0.0, color, 0.8F, 16, time, flags); } public static void addSector( @@ -257,9 +266,9 @@ public class DebugUtils { @Nonnull Vector3f color, float opacity, float time, - boolean fade + int flags ) { - addSector(world, x, y, z, heading, radius, angle, 0.0, color, opacity, 16, time, fade); + addSector(world, x, y, z, heading, radius, angle, 0.0, color, opacity, 16, time, flags); } public static void addSector( @@ -274,9 +283,9 @@ public class DebugUtils { @Nonnull Vector3f color, float opacity, float time, - boolean fade + int flags ) { - addSector(world, x, y, z, heading, outerRadius, angle, innerRadius, color, opacity, 16, time, fade); + addSector(world, x, y, z, heading, outerRadius, angle, innerRadius, color, opacity, 16, time, flags); } public static void addSector( @@ -292,56 +301,59 @@ public class DebugUtils { float opacity, int segmentCount, float time, - boolean fade + int flags ) { - Matrix4d tmp = new Matrix4d(); Matrix4d matrix = new Matrix4d(); matrix.identity(); matrix.translate(x, y, z); - matrix.rotateAxis(heading, 0.0, 1.0, 0.0, tmp); + matrix.rotate(-heading, 0.0, 1.0, 0.0); float[] shapeParams = new float[]{ (float)outerRadius, (float)angle, (float)innerRadius, segmentCount, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F }; - add(world, DebugShape.Sector, matrix, color, opacity, time, fade, shapeParams); + add(world, DebugShape.Sector, matrix, color, opacity, time, flags, shapeParams); } - public static void addArrow(@Nonnull World world, @Nonnull Vector3d position, @Nonnull Vector3d direction, @Nonnull Vector3f color, float time, boolean fade) { - Vector3d directionClone = direction.clone(); - Matrix4d tmp = new Matrix4d(); + public static void addArrow( + @Nonnull World world, @Nonnull Vector3d position, @Nonnull Vector3d direction, @Nonnull Vector3f color, float opacity, float time, int flags + ) { + Vector3d directionClone = new Vector3d(direction); Matrix4d matrix = new Matrix4d(); matrix.identity(); matrix.translate(position); double angleY = Math.atan2(directionClone.z, directionClone.x); - matrix.rotateAxis(angleY + (Math.PI / 2), 0.0, 1.0, 0.0, tmp); + matrix.rotate(-(angleY + (Math.PI / 2)), 0.0, 1.0, 0.0); double angleX = Math.atan2(Math.sqrt(directionClone.x * directionClone.x + directionClone.z * directionClone.z), directionClone.y); - matrix.rotateAxis(angleX, 1.0, 0.0, 0.0, tmp); - addArrow(world, matrix, color, directionClone.length(), time, fade); + matrix.rotate(-angleX, 1.0, 0.0, 0.0); + addArrow(world, matrix, color, opacity, directionClone.length(), time, flags); + } + + public static void addArrow(World world, Vector3d position, Vector3d direction, Vector3f color, float time, int flags) { + addArrow(world, position, direction, color, 0.8F, time, flags); } public static void addForce(@Nonnull World world, @Nonnull Vector3d position, @Nonnull Vector3d force, @Nullable VelocityConfig velocityConfig) { if (DISPLAY_FORCES) { - Vector3d forceClone = force.clone(); + Vector3d forceClone = new Vector3d(force); if (velocityConfig == null || SplitVelocity.SHOULD_MODIFY_VELOCITY) { forceClone.x = forceClone.x / DamageSystems.HackKnockbackValues.PLAYER_KNOCKBACK_SCALE; forceClone.z = forceClone.z / DamageSystems.HackKnockbackValues.PLAYER_KNOCKBACK_SCALE; } - Matrix4d tmp = new Matrix4d(); Matrix4d matrix = new Matrix4d(); matrix.identity(); matrix.translate(position); double angleY = Math.atan2(forceClone.z, forceClone.x); - matrix.rotateAxis(angleY + (Math.PI / 2), 0.0, 1.0, 0.0, tmp); + matrix.rotate(-(angleY + (Math.PI / 2)), 0.0, 1.0, 0.0); double angleX = Math.atan2(Math.sqrt(forceClone.x * forceClone.x + forceClone.z * forceClone.z), forceClone.y); - matrix.rotateAxis(angleX, 1.0, 0.0, 0.0, tmp); + matrix.rotate(-angleX, 1.0, 0.0, 0.0); Random random = new Random(); Vector3f color = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); - addArrow(world, matrix, color, forceClone.length(), 10.0F, true); + addArrow(world, matrix, color, forceClone.length(), 10.0F, FLAG_FADE); } } @Nonnull - private static Matrix4d makeMatrix(@Nonnull Vector3d pos, double scale) { + public static Matrix4d makeMatrix(@Nonnull Vector3d pos, double scale) { Matrix4d matrix = new Matrix4d(); matrix.identity(); matrix.translate(pos); diff --git a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeArrowCommand.java b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeArrowCommand.java index 7b202a68..7159d020 100644 --- a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeArrowCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeArrowCommand.java @@ -2,12 +2,11 @@ package com.hypixel.hytale.server.core.modules.debug.commands; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.matrix.Matrix4d; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; @@ -19,10 +18,19 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import org.joml.Matrix4d; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DebugShapeArrowCommand extends AbstractPlayerCommand { @Nonnull private static final Message MESSAGE_COMMANDS_DEBUG_SHAPE_ARROW_SUCCESS = Message.translation("server.commands.debug.shape.arrow.success"); + @Nonnull + private final FlagArg fadeFlag = this.withFlagArg("fade", "server.commands.debug.shape.flag.fade.desc"); + @Nonnull + private final FlagArg noWireframeFlag = this.withFlagArg("no-wireframe", "server.commands.debug.shape.flag.noWireframe.desc"); + @Nonnull + private final FlagArg noSolidFlag = this.withFlagArg("no-solid", "server.commands.debug.shape.flag.noSolid.desc"); public DebugShapeArrowCommand() { super("arrow", "server.commands.debug.shape.arrow.desc"); @@ -50,19 +58,19 @@ public class DebugShapeArrowCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); - float lookYaw = headRotation.getYaw(); - float lookPitch = headRotation.getPitch(); - Matrix4d tmp = new Matrix4d(); + Rotation3f headRotation = headRotationComponent.getRotation(); + float lookYaw = headRotation.yaw(); + float lookPitch = headRotation.pitch(); float eyeHeight = model != null ? model.getEyeHeight(ref, store) : 0.0F; ThreadLocalRandom random = ThreadLocalRandom.current(); Vector3f color = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); Matrix4d matrix = new Matrix4d(); matrix.identity(); matrix.translate(pos.x, pos.y + eyeHeight, pos.z); - matrix.rotateAxis(-lookYaw, 0.0, 1.0, 0.0, tmp); - matrix.rotateAxis((Math.PI / 2) - lookPitch, 1.0, 0.0, 0.0, tmp); - DebugUtils.addArrow(world, matrix, color, 1.0, 30.0F, true); + matrix.rotate(lookYaw, 0.0, 1.0, 0.0); + matrix.rotate(-((Math.PI / 2) - lookPitch), 1.0, 0.0, 0.0); + int flags = DebugShapeSubCommand.buildFlags(context, this.fadeFlag, this.noWireframeFlag, this.noSolidFlag); + DebugUtils.addArrow(world, matrix, color, 1.0, 30.0F, flags); context.sendMessage(MESSAGE_COMMANDS_DEBUG_SHAPE_ARROW_SUCCESS); } } diff --git a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeConeCommand.java b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeConeCommand.java index 4812f28a..96207936 100644 --- a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeConeCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeConeCommand.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.server.core.modules.debug.commands; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.protocol.DebugShape; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -14,10 +14,18 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DebugShapeConeCommand extends AbstractPlayerCommand { @Nonnull private static final Message MESSAGE_COMMANDS_DEBUG_SHAPE_CONE_SUCCESS = Message.translation("server.commands.debug.shape.cone.success"); + @Nonnull + private final FlagArg fadeFlag = this.withFlagArg("fade", "server.commands.debug.shape.flag.fade.desc"); + @Nonnull + private final FlagArg noWireframeFlag = this.withFlagArg("no-wireframe", "server.commands.debug.shape.flag.noWireframe.desc"); + @Nonnull + private final FlagArg noSolidFlag = this.withFlagArg("no-solid", "server.commands.debug.shape.flag.noSolid.desc"); public DebugShapeConeCommand() { super("cone", "server.commands.debug.shape.cone.desc"); @@ -34,7 +42,8 @@ public class DebugShapeConeCommand extends AbstractPlayerCommand { Vector3d position = transformComponent.getPosition(); ThreadLocalRandom random = ThreadLocalRandom.current(); Vector3f color = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); - DebugUtils.addCone(world, position, color, 2.0, 30.0F); + int flags = DebugShapeSubCommand.buildFlags(context, this.fadeFlag, this.noWireframeFlag, this.noSolidFlag); + DebugUtils.add(world, DebugShape.Cone, DebugUtils.makeMatrix(position, 2.0), color, 30.0F, flags); context.sendMessage(MESSAGE_COMMANDS_DEBUG_SHAPE_CONE_SUCCESS); } } diff --git a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeCubeCommand.java b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeCubeCommand.java index 2cca1913..9e2f5f51 100644 --- a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeCubeCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeCubeCommand.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.server.core.modules.debug.commands; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.protocol.DebugShape; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -14,10 +14,18 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DebugShapeCubeCommand extends AbstractPlayerCommand { @Nonnull private static final Message MESSAGE_COMMANDS_DEBUG_SHAPE_CUBE_SUCCESS = Message.translation("server.commands.debug.shape.cube.success"); + @Nonnull + private final FlagArg fadeFlag = this.withFlagArg("fade", "server.commands.debug.shape.flag.fade.desc"); + @Nonnull + private final FlagArg noWireframeFlag = this.withFlagArg("no-wireframe", "server.commands.debug.shape.flag.noWireframe.desc"); + @Nonnull + private final FlagArg noSolidFlag = this.withFlagArg("no-solid", "server.commands.debug.shape.flag.noSolid.desc"); public DebugShapeCubeCommand() { super("cube", "server.commands.debug.shape.cube.desc"); @@ -34,7 +42,8 @@ public class DebugShapeCubeCommand extends AbstractPlayerCommand { Vector3d position = transformComponent.getPosition(); ThreadLocalRandom random = ThreadLocalRandom.current(); Vector3f color = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); - DebugUtils.addCube(world, position, color, 2.0, 30.0F); + int flags = DebugShapeSubCommand.buildFlags(context, this.fadeFlag, this.noWireframeFlag, this.noSolidFlag); + DebugUtils.add(world, DebugShape.Cube, DebugUtils.makeMatrix(position, 2.0), color, 30.0F, flags); context.sendMessage(MESSAGE_COMMANDS_DEBUG_SHAPE_CUBE_SUCCESS); } } diff --git a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeCylinderCommand.java b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeCylinderCommand.java index 63c2b97b..49316993 100644 --- a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeCylinderCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeCylinderCommand.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.server.core.modules.debug.commands; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.protocol.DebugShape; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -14,10 +14,18 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DebugShapeCylinderCommand extends AbstractPlayerCommand { @Nonnull private static final Message MESSAGE_COMMANDS_DEBUG_SHAPE_CYLINDER_SUCCESS = Message.translation("server.commands.debug.shape.cylinder.success"); + @Nonnull + private final FlagArg fadeFlag = this.withFlagArg("fade", "server.commands.debug.shape.flag.fade.desc"); + @Nonnull + private final FlagArg noWireframeFlag = this.withFlagArg("no-wireframe", "server.commands.debug.shape.flag.noWireframe.desc"); + @Nonnull + private final FlagArg noSolidFlag = this.withFlagArg("no-solid", "server.commands.debug.shape.flag.noSolid.desc"); public DebugShapeCylinderCommand() { super("cylinder", "server.commands.debug.shape.cylinder.desc"); @@ -34,7 +42,8 @@ public class DebugShapeCylinderCommand extends AbstractPlayerCommand { Vector3d position = transformComponent.getPosition(); ThreadLocalRandom random = ThreadLocalRandom.current(); Vector3f color = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); - DebugUtils.addCylinder(world, position, color, 2.0, 30.0F); + int flags = DebugShapeSubCommand.buildFlags(context, this.fadeFlag, this.noWireframeFlag, this.noSolidFlag); + DebugUtils.add(world, DebugShape.Cylinder, DebugUtils.makeMatrix(position, 2.0), color, 30.0F, flags); context.sendMessage(MESSAGE_COMMANDS_DEBUG_SHAPE_CYLINDER_SUCCESS); } } diff --git a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeSphereCommand.java b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeSphereCommand.java index c3df097f..47125aba 100644 --- a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeSphereCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeSphereCommand.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.server.core.modules.debug.commands; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.protocol.DebugShape; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -14,10 +14,18 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class DebugShapeSphereCommand extends AbstractPlayerCommand { @Nonnull private static final Message MESSAGE_COMMANDS_DEBUG_SHAPE_SPHERE_SUCCESS = Message.translation("server.commands.debug.shape.sphere.success"); + @Nonnull + private final FlagArg fadeFlag = this.withFlagArg("fade", "server.commands.debug.shape.flag.fade.desc"); + @Nonnull + private final FlagArg noWireframeFlag = this.withFlagArg("no-wireframe", "server.commands.debug.shape.flag.noWireframe.desc"); + @Nonnull + private final FlagArg noSolidFlag = this.withFlagArg("no-solid", "server.commands.debug.shape.flag.noSolid.desc"); public DebugShapeSphereCommand() { super("sphere", "server.commands.debug.shape.sphere.desc"); @@ -34,7 +42,8 @@ public class DebugShapeSphereCommand extends AbstractPlayerCommand { Vector3d position = transformComponent.getPosition(); ThreadLocalRandom random = ThreadLocalRandom.current(); Vector3f color = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); - DebugUtils.addSphere(world, position, color, 2.0, 30.0F); + int flags = DebugShapeSubCommand.buildFlags(context, this.fadeFlag, this.noWireframeFlag, this.noSolidFlag); + DebugUtils.add(world, DebugShape.Sphere, DebugUtils.makeMatrix(position, 2.0), color, 30.0F, flags); context.sendMessage(MESSAGE_COMMANDS_DEBUG_SHAPE_SPHERE_SUCCESS); } } diff --git a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeSubCommand.java b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeSubCommand.java index ef882d73..eec3953c 100644 --- a/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeSubCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/debug/commands/DebugShapeSubCommand.java @@ -1,6 +1,10 @@ package com.hypixel.hytale.server.core.modules.debug.commands; +import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; +import com.hypixel.hytale.server.core.modules.debug.DebugUtils; +import javax.annotation.Nonnull; public class DebugShapeSubCommand extends AbstractCommandCollection { public DebugShapeSubCommand() { @@ -13,4 +17,21 @@ public class DebugShapeSubCommand extends AbstractCommandCollection { this.addSubCommand(new DebugShapeShowForceCommand()); this.addSubCommand(new DebugShapeClearCommand()); } + + static int buildFlags(@Nonnull CommandContext context, @Nonnull FlagArg fadeFlag, @Nonnull FlagArg noWireframeFlag, @Nonnull FlagArg noSolidFlag) { + int flags = 0; + if (context.get(fadeFlag)) { + flags |= DebugUtils.FLAG_FADE; + } + + if (context.get(noWireframeFlag)) { + flags |= DebugUtils.FLAG_NO_WIREFRAME; + } + + if (context.get(noSolidFlag)) { + flags |= DebugUtils.FLAG_NO_SOLID; + } + + return flags; + } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/EntityModule.java b/src/com/hypixel/hytale/server/core/modules/entity/EntityModule.java index 3273d7bd..f9e63662 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/EntityModule.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/EntityModule.java @@ -29,7 +29,6 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.ISystem; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.component.system.RefSystem; -import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.packets.player.UpdateMovementSettings; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; @@ -46,7 +45,6 @@ import com.hypixel.hytale.server.core.entity.entities.BlockEntity; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.ProjectileComponent; import com.hypixel.hytale.server.core.entity.entities.player.CameraManager; -import com.hypixel.hytale.server.core.entity.entities.player.HotbarManager; import com.hypixel.hytale.server.core.entity.entities.player.data.UniqueItemUsagesComponent; import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementConfig; import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager; @@ -57,13 +55,15 @@ import com.hypixel.hytale.server.core.entity.movement.MovementStatesSystems; import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; import com.hypixel.hytale.server.core.entity.nameplate.NameplateSystems; import com.hypixel.hytale.server.core.entity.reference.PersistentRefCount; -import com.hypixel.hytale.server.core.event.events.entity.LivingEntityInventoryChangeEvent; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; +import com.hypixel.hytale.server.core.inventory.InventorySystems; import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.modules.collision.CollisionModule; import com.hypixel.hytale.server.core.modules.collision.TangiableEntitySpatialSystem; import com.hypixel.hytale.server.core.modules.entity.component.ActiveAnimationComponent; import com.hypixel.hytale.server.core.modules.entity.component.AudioComponent; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; +import com.hypixel.hytale.server.core.modules.entity.component.CachedStatsComponent; import com.hypixel.hytale.server.core.modules.entity.component.CollisionResultComponent; import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent; import com.hypixel.hytale.server.core.modules.entity.component.DynamicLight; @@ -88,6 +88,23 @@ import com.hypixel.hytale.server.core.modules.entity.component.RotateObjectCompo import com.hypixel.hytale.server.core.modules.entity.component.SnapshotBuffer; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.component.WorldGenId; +import com.hypixel.hytale.server.core.modules.entity.condition.AliveCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.ChargingCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.CheckPlayerGameModeCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.Condition; +import com.hypixel.hytale.server.core.modules.entity.condition.EnvironmentCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.GlidingCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.HasEffectCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.InFluidCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.IsPlayerCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.LogicCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.NoDamageTakenCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.OutOfCombatCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.RegenHealthCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.SprintingCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.StatCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.SuffocatingCondition; +import com.hypixel.hytale.server.core.modules.entity.condition.WieldingCondition; import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; import com.hypixel.hytale.server.core.modules.entity.dynamiclight.DynamicLightSystems; import com.hypixel.hytale.server.core.modules.entity.hitboxcollision.HitboxCollision; @@ -168,7 +185,6 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Collections; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -178,9 +194,6 @@ import javax.annotation.Nullable; public class EntityModule extends JavaPlugin { public static final PluginManifest MANIFEST = PluginManifest.corePlugin(EntityModule.class).depends(Universe.class).depends(CollisionModule.class).build(); - public static final String[] LEGACY_ENTITY_CLASS_NAMES = new String[]{ - "SpawnSuppressor", "Block", "LegacySpawnBeacon", "PatrolPathMarker", "Player", "SpawnBeacon", "SpawnMarker" - }; public static final String MOUNT_MOVEMENT_SETTINGS_ASSET_ID = "Mount"; private static EntityModule instance; private final Map> idMap = new ConcurrentHashMap<>(); @@ -257,6 +270,7 @@ public class EntityModule extends JavaPlugin { private ComponentType movementAudioComponentType; private ComponentType positionDataComponentType; private ComponentType activeAnimationComponentType; + private ComponentType cachedStatsComponentType; private ComponentType newSpawnComponentType; private ComponentType itemComponentType; private ComponentType pickupItemComponentType; @@ -266,6 +280,13 @@ public class EntityModule extends JavaPlugin { private ComponentType persistentDynamicLightComponentType; private ComponentType prefabCopyableComponentType; private ComponentType uniqueItemUsagesComponentType; + private ComponentType storageInventoryComponentType; + private ComponentType armorInventoryComponentType; + private ComponentType hotbarInventoryComponentType; + private ComponentType utilityInventoryComponentType; + private ComponentType backpackInventoryComponentType; + private ComponentType toolInventoryComponentType; + private ComponentType combinedInventoryComponentType; public static EntityModule get() { return instance; @@ -278,28 +299,6 @@ public class EntityModule extends JavaPlugin { @Override protected void setup() { - this.getEventRegistry().registerGlobal(LivingEntityInventoryChangeEvent.class, event -> { - Ref entityRef = event.getEntity().getReference(); - if (entityRef != null && entityRef.isValid()) { - Store store = entityRef.getStore(); - World world = store.getExternalData().getWorld(); - world.execute(() -> { - if (entityRef.isValid()) { - Player playerComponent = store.getComponent(entityRef, Player.getComponentType()); - if (playerComponent != null) { - HotbarManager hotbarManager = playerComponent.getHotbarManager(); - if (!hotbarManager.getIsCurrentlyLoadingHotbar()) { - if (playerComponent.getGameMode().equals(GameMode.Creative)) { - if (event.getItemContainer().equals(playerComponent.getInventory().getHotbar())) { - hotbarManager.saveHotbar(entityRef, (short)hotbarManager.getCurrentHotbarIndex(), store); - } - } - } - } - } - }); - } - }); ComponentRegistryProxy entityStoreRegistry = this.getEntityStoreRegistry(); this.physicsValuesComponentType = entityStoreRegistry.registerComponent(PhysicsValues.class, PhysicsValues::new); this.velocityComponentType = entityStoreRegistry.registerComponent(Velocity.class, "Velocity", Velocity.CODEC); @@ -330,6 +329,7 @@ public class EntityModule extends JavaPlugin { this.movementAudioComponentType = entityStoreRegistry.registerComponent(MovementAudioComponent.class, MovementAudioComponent::new); this.positionDataComponentType = entityStoreRegistry.registerComponent(PositionDataComponent.class, PositionDataComponent::new); this.activeAnimationComponentType = entityStoreRegistry.registerComponent(ActiveAnimationComponent.class, ActiveAnimationComponent::new); + this.cachedStatsComponentType = entityStoreRegistry.registerComponent(CachedStatsComponent.class, CachedStatsComponent::new); this.newSpawnComponentType = entityStoreRegistry.registerComponent(NewSpawnComponent.class, () -> { throw new UnsupportedOperationException("Not implemented"); }); @@ -358,7 +358,6 @@ public class EntityModule extends JavaPlugin { entityStoreRegistry.registerSystem(new SnapshotSystems.Resize()); entityStoreRegistry.registerSystem(new SnapshotSystems.Capture()); entityStoreRegistry.registerSystem(new UpdateEntitySeedSystem()); - entityStoreRegistry.registerSystem(new EntityModule.LegacyTransformSystem()); entityStoreRegistry.registerSystem(new EntityModule.LegacyUUIDSystem()); entityStoreRegistry.registerSystem(new EntityModule.LegacyUUIDUpdateSystem()); entityStoreRegistry.registerSystem(new EntitySystems.UnloadEntityFromChunk()); @@ -393,8 +392,6 @@ public class EntityModule extends JavaPlugin { this.displayNameComponentType = entityStoreRegistry.registerComponent(DisplayNameComponent.class, "DisplayName", DisplayNameComponent.CODEC); entityStoreRegistry.registerSystem(new PlayerSystems.PlayerSpawnedSystem()); entityStoreRegistry.registerSystem(new PlayerSystems.PlayerAddedSystem(this.movementManagerComponentType)); - entityStoreRegistry.registerSystem(new PlayerSystems.EnsurePlayerInput()); - entityStoreRegistry.registerSystem(new PlayerSystems.EnsureEffectControllerSystem()); entityStoreRegistry.registerSystem(new PlayerSystems.PlayerRemovedSystem()); entityStoreRegistry.registerSystem(new PlayerSystems.ProcessPlayerInput()); entityStoreRegistry.registerSystem(new PlayerSystems.UpdatePlayerRef()); @@ -490,6 +487,7 @@ public class EntityModule extends JavaPlugin { entityStoreRegistry.registerSystem(new LivingEntityEffectClearChangesSystem()); entityStoreRegistry.registerSystem(new PlayerSendInventorySystem(this.playerComponentType)); entityStoreRegistry.registerSystem(new PlayerSavingSystems.WorldRemovedSystem(this.playerComponentType)); + entityStoreRegistry.registerSystem(new PlayerSavingSystems.EntityRemovedSystem(this.playerComponentType)); entityStoreRegistry.registerSystem(new PlayerSavingSystems.TickingSystem(this.playerComponentType)); this.entityGroupComponentType = entityStoreRegistry.registerComponent(EntityGroup.class, () -> { throw new UnsupportedOperationException("Not implemented"); @@ -511,7 +509,6 @@ public class EntityModule extends JavaPlugin { this.intangibleQueueResourceType = entityStoreRegistry.registerResource(IntangibleSystems.QueueResource.class, IntangibleSystems.QueueResource::new); entityStoreRegistry.registerSystem(new IntangibleSystems.EntityTrackerUpdate(this.visibleComponentType)); entityStoreRegistry.registerSystem(new IntangibleSystems.EntityTrackerAddAndRemove(this.visibleComponentType)); - entityStoreRegistry.registerSystem(new EntityModule.TangibleMigrationSystem(ProjectileComponent.getComponentType()), true); this.invulnerableQueueResourceType = entityStoreRegistry.registerResource(InvulnerableSystems.QueueResource.class, InvulnerableSystems.QueueResource::new); entityStoreRegistry.registerSystem(new InvulnerableSystems.EntityTrackerUpdate(this.visibleComponentType)); entityStoreRegistry.registerSystem(new InvulnerableSystems.EntityTrackerAddAndRemove(this.visibleComponentType)); @@ -589,7 +586,56 @@ public class EntityModule extends JavaPlugin { this.uniqueItemUsagesComponentType = entityStoreRegistry.registerComponent( UniqueItemUsagesComponent.class, "UniqueItemUsages", UniqueItemUsagesComponent.CODEC ); - entityStoreRegistry.registerSystem(new PlayerSystems.EnsureUniqueItemUsagesSystem()); + this.storageInventoryComponentType = entityStoreRegistry.registerComponent( + InventoryComponent.Storage.class, "StorageInventory", InventoryComponent.Storage.CODEC + ); + this.armorInventoryComponentType = entityStoreRegistry.registerComponent(InventoryComponent.Armor.class, "ArmorInventory", InventoryComponent.Armor.CODEC); + this.hotbarInventoryComponentType = entityStoreRegistry.registerComponent( + InventoryComponent.Hotbar.class, "HotbarInventory", InventoryComponent.Hotbar.CODEC + ); + this.utilityInventoryComponentType = entityStoreRegistry.registerComponent( + InventoryComponent.Utility.class, "UtilityInventory", InventoryComponent.Utility.CODEC + ); + this.backpackInventoryComponentType = entityStoreRegistry.registerComponent( + InventoryComponent.Backpack.class, "BackpackInventory", InventoryComponent.Backpack.CODEC + ); + this.toolInventoryComponentType = entityStoreRegistry.registerComponent(InventoryComponent.Tool.class, "ToolInventory", InventoryComponent.Tool.CODEC); + this.combinedInventoryComponentType = entityStoreRegistry.registerComponent(InventoryComponent.Combined.class, InventoryComponent.Combined::new); + InventoryComponent.setupCombined( + this.storageInventoryComponentType, + this.armorInventoryComponentType, + this.hotbarInventoryComponentType, + this.utilityInventoryComponentType, + this.backpackInventoryComponentType, + this.toolInventoryComponentType + ); + entityStoreRegistry.registerSystem(new InventorySystems.StorageChangeEventSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.ArmorChangeEventSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.HotbarChangeEventSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.UtilityChangeEventSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.BackpackChangeEventSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.ToolChangeEventSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.LegacyArmorChangeStatSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.LegacyHotbarChangeStatSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.LegacyUtilityChangeStatSystem()); + entityStoreRegistry.registerSystem(new InventorySystems.PlayerInventoryChangeEventSystem()); + entityStoreRegistry.registerSystem(new PlayerSystems.PlayerInitSystem()); + Condition.CODEC.register("LogicCondition", LogicCondition.class, LogicCondition.CODEC); + Condition.CODEC.register("RegenHealth", RegenHealthCondition.class, RegenHealthCondition.CODEC); + Condition.CODEC.register("NoDamageTaken", NoDamageTakenCondition.class, NoDamageTakenCondition.CODEC); + Condition.CODEC.register("Suffocating", SuffocatingCondition.class, SuffocatingCondition.CODEC); + Condition.CODEC.register("Charging", ChargingCondition.class, ChargingCondition.CODEC); + Condition.CODEC.register("Alive", AliveCondition.class, AliveCondition.CODEC); + Condition.CODEC.register("Environment", EnvironmentCondition.class, EnvironmentCondition.CODEC); + Condition.CODEC.register("CheckPlayerGameMode", CheckPlayerGameModeCondition.class, CheckPlayerGameModeCondition.CODEC); + Condition.CODEC.register("OutOfCombat", OutOfCombatCondition.class, OutOfCombatCondition.CODEC); + Condition.CODEC.register("Wielding", WieldingCondition.class, WieldingCondition.CODEC); + Condition.CODEC.register("Sprinting", SprintingCondition.class, SprintingCondition.CODEC); + Condition.CODEC.register("Gliding", GlidingCondition.class, GlidingCondition.CODEC); + Condition.CODEC.register("Stat", StatCondition.class, StatCondition.CODEC); + Condition.CODEC.register("InFluid", InFluidCondition.class, InFluidCondition.CODEC); + Condition.CODEC.register("HasEffect", HasEffectCondition.class, HasEffectCondition.CODEC); + Condition.CODEC.register("IsPlayer", IsPlayerCondition.class, IsPlayerCondition.CODEC); } @Override @@ -1004,10 +1050,42 @@ public class EntityModule extends JavaPlugin { return this.uniqueItemUsagesComponentType; } + public ComponentType getStorageInventoryComponentType() { + return this.storageInventoryComponentType; + } + + public ComponentType getArmorInventoryComponentType() { + return this.armorInventoryComponentType; + } + + public ComponentType getHotbarInventoryComponentType() { + return this.hotbarInventoryComponentType; + } + + public ComponentType getUtilityInventoryComponentType() { + return this.utilityInventoryComponentType; + } + + public ComponentType getBackpackInventoryComponentType() { + return this.backpackInventoryComponentType; + } + + public ComponentType getToolInventoryComponentType() { + return this.toolInventoryComponentType; + } + + public ComponentType getCombinedInventoryComponentType() { + return this.combinedInventoryComponentType; + } + public ComponentType getActiveAnimationComponentType() { return this.activeAnimationComponentType; } + public ComponentType getCachedStatsComponentType() { + return this.cachedStatsComponentType; + } + @Nullable public EntityRegistration registerEntity( @Nonnull String id, @Nonnull Class clazz, Function entityConstructor, @Nullable DirectDecodeCodec codec @@ -1102,32 +1180,6 @@ public class EntityModule extends JavaPlugin { return entity != null && this.getConstructor(entity.getClass()) != null; } - @Deprecated(forRemoval = true) - public static class HiddenFromPlayerMigrationSystem extends EntityModule.MigrationSystem { - private final ComponentType hiddenFromAdventurePlayersComponentType = HiddenFromAdventurePlayers.getComponentType(); - @Nonnull - private final Query query; - - public HiddenFromPlayerMigrationSystem(Query query) { - this.query = Query.and(query, Query.not(this.hiddenFromAdventurePlayersComponentType)); - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - holder.ensureComponent(this.hiddenFromAdventurePlayersComponentType); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return this.query; - } - } - public static class LegacyEntityHolderSystem extends HolderSystem { private final ComponentType componentType; @@ -1227,32 +1279,6 @@ public class EntityModule extends JavaPlugin { } } - public static class LegacyTransformSystem extends EntityModule.MigrationSystem { - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - TransformComponent transformComponent = holder.getComponent(TransformComponent.getComponentType()); - Objects.requireNonNull(transformComponent); - Entity entity = EntityUtils.getEntity(holder); - entity.setTransformComponent(transformComponent); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } - - @Nonnull - @Override - public Query getQuery() { - return AllLegacyEntityTypesQuery.INSTANCE; - } - } - public static class LegacyUUIDSystem extends EntityModule.MigrationSystem { private final Set> dependencies = Set.of( new SystemDependency<>(Order.BEFORE, EntityStore.UUIDSystem.class), RootDependency.first() @@ -1328,32 +1354,6 @@ public class EntityModule extends JavaPlugin { public abstract static class MigrationSystem extends HolderSystem { } - @Deprecated(forRemoval = true) - public static class TangibleMigrationSystem extends EntityModule.MigrationSystem { - private final ComponentType intangibleComponentType = Intangible.getComponentType(); - @Nonnull - private final Query query; - - public TangibleMigrationSystem(Query query) { - this.query = Query.and(query, Query.not(this.intangibleComponentType)); - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - holder.ensureComponent(this.intangibleComponentType); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return this.query; - } - } - public static enum Type { PLAYERS, ALL; diff --git a/src/com/hypixel/hytale/server/core/modules/entity/component/CachedStatsComponent.java b/src/com/hypixel/hytale/server/core/modules/entity/component/CachedStatsComponent.java new file mode 100644 index 00000000..7de0589e --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/entity/component/CachedStatsComponent.java @@ -0,0 +1,31 @@ +package com.hypixel.hytale.server.core.modules.entity.component; + +import com.hypixel.hytale.component.Component; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.server.core.modules.entity.EntityModule; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import javax.annotation.Nonnull; + +public class CachedStatsComponent implements Component { + private boolean canBreathe; + + public static ComponentType getComponentType() { + return EntityModule.get().getCachedStatsComponentType(); + } + + public boolean isCanBreathe() { + return this.canBreathe; + } + + public void setCanBreathe(boolean canBreathe) { + this.canBreathe = canBreathe; + } + + @Nonnull + @Override + public Component clone() { + CachedStatsComponent component = new CachedStatsComponent(); + component.canBreathe = this.canBreathe; + return component; + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/entity/component/CollisionResultComponent.java b/src/com/hypixel/hytale/server/core/modules/entity/component/CollisionResultComponent.java index 8d6cb2c8..9965b010 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/component/CollisionResultComponent.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/component/CollisionResultComponent.java @@ -2,11 +2,11 @@ package com.hypixel.hytale.server.core.modules.entity.component; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.collision.CollisionResult; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CollisionResultComponent implements Component { private final CollisionResult collisionResult; @@ -70,7 +70,7 @@ public class CollisionResultComponent implements Component { } public void resetLocationChange() { - this.collisionPositionOffset.assign(Vector3d.ZERO); + this.collisionPositionOffset.zero(); this.pendingCollisionCheck = false; } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/component/HeadRotation.java b/src/com/hypixel/hytale/server/core/modules/entity/component/HeadRotation.java index 1a09b01a..615588b3 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/component/HeadRotation.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/component/HeadRotation.java @@ -6,19 +6,21 @@ import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class HeadRotation implements Component { public static final BuilderCodec CODEC = BuilderCodec.builder(HeadRotation.class, HeadRotation::new) - .append(new KeyedCodec<>("Rotation", Vector3f.ROTATION), (o, i) -> o.rotation.assign(i), o -> o.rotation) + .append(new KeyedCodec<>("Rotation", Rotation3f.CODEC), (o, i) -> o.rotation.set(i), o -> o.rotation) .add() .build(); - private final Vector3f rotation = new Vector3f(); + private final Rotation3f rotation = new Rotation3f(); public static ComponentType getComponentType() { return EntityModule.get().getHeadRotationComponentType(); @@ -27,45 +29,45 @@ public class HeadRotation implements Component { public HeadRotation() { } - public HeadRotation(@Nonnull Vector3f rotation) { - this.rotation.assign(rotation); + public HeadRotation(@Nonnull Rotation3fc rotation) { + this.rotation.set(rotation); } @Nonnull - public Vector3f getRotation() { + public Rotation3f getRotation() { return this.rotation; } - public void setRotation(@Nonnull Vector3f rotation) { - this.rotation.assign(rotation); + public void setRotation(@Nonnull Rotation3fc rotation) { + this.rotation.set(rotation); } public Vector3d getDirection() { - return getDirection(this.rotation.getPitch(), this.rotation.getYaw(), new Vector3d()); + return getDirection(this.rotation.pitch(), this.rotation.yaw(), new Vector3d()); } @Nonnull public Vector3i getAxisDirection() { - return getAxisDirection(this.rotation.getPitch(), this.rotation.getYaw(), new Vector3i()); + return getAxisDirection(this.rotation.pitch(), this.rotation.yaw(), new Vector3i()); } @Nonnull public Vector3i getAxisDirection(@Nonnull Vector3i result) { - return getAxisDirection(this.rotation.getPitch(), this.rotation.getYaw(), result); + return getAxisDirection(this.rotation.pitch(), this.rotation.yaw(), result); } @Nonnull public Vector3i getHorizontalAxisDirection() { - return getAxisDirection(0.0F, this.rotation.getYaw(), new Vector3i()); + return getAxisDirection(0.0F, this.rotation.yaw(), new Vector3i()); } @Nonnull public Axis getAxis() { Vector3i axisDirection = this.getAxisDirection(); - if (axisDirection.getX() != 0) { + if (axisDirection.x() != 0) { return Axis.X; } else { - return axisDirection.getY() != 0 ? Axis.Y : Axis.Z; + return axisDirection.y() != 0 ? Axis.Y : Axis.Z; } } @@ -80,7 +82,7 @@ public class HeadRotation implements Component { double x = len * -TrigMathUtil.sin(yaw); double y = TrigMathUtil.sin(pitch); double z = len * -TrigMathUtil.cos(yaw); - return result.assign((int)Math.round(x), (int)Math.round(y), (int)Math.round(z)); + return result.set((int)Math.round(x), (int)Math.round(y), (int)Math.round(z)); } } @@ -91,22 +93,22 @@ public class HeadRotation implements Component { } else if (Float.isNaN(yaw)) { throw new IllegalStateException("Yaw can't be NaN"); } else { - return result.assign(yaw, pitch); + return Vector3dUtil.setYawPitch(yaw, pitch, result); } } - public void teleportRotation(@Nonnull Vector3f rotation) { - float yaw = rotation.getYaw(); + public void teleportRotation(@Nonnull Rotation3f rotation) { + float yaw = rotation.yaw(); if (!Float.isNaN(yaw)) { this.rotation.setYaw(yaw); } - float pitch = rotation.getPitch(); + float pitch = rotation.pitch(); if (!Float.isNaN(pitch)) { this.rotation.setPitch(pitch); } - float roll = rotation.getRoll(); + float roll = rotation.roll(); if (!Float.isNaN(roll)) { this.rotation.setRoll(roll); } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/component/SnapshotBuffer.java b/src/com/hypixel/hytale/server/core/modules/entity/component/SnapshotBuffer.java index 7075672a..4be0c721 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/component/SnapshotBuffer.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/component/SnapshotBuffer.java @@ -2,14 +2,14 @@ package com.hypixel.hytale.server.core.modules.entity.component; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.entity.EntitySnapshot; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SnapshotBuffer implements Component { private EntitySnapshot[] snapshots; @@ -61,7 +61,7 @@ public class SnapshotBuffer implements Component { return this.snapshots[index]; } - public void storeSnapshot(int tickIndex, @Nonnull Vector3d position, @Nonnull Vector3f bodyRotation) { + public void storeSnapshot(int tickIndex, @Nonnull Vector3d position, @Nonnull Rotation3f bodyRotation) { if (this.currentIndex != -1 && this.currentTickIndex != tickIndex - 1) { this.currentIndex = -1; this.currentTickIndex = Integer.MIN_VALUE; diff --git a/src/com/hypixel/hytale/server/core/modules/entity/component/TransformComponent.java b/src/com/hypixel/hytale/server/core/modules/entity/component/TransformComponent.java index 8b881dae..76c81a32 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/component/TransformComponent.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/component/TransformComponent.java @@ -7,9 +7,10 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.ModelTransform; import com.hypixel.hytale.protocol.Position; @@ -21,19 +22,21 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.PositionUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class TransformComponent implements Component { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder(TransformComponent.class, TransformComponent::new) - .append(new KeyedCodec<>("Position", Vector3d.CODEC), (o, i) -> o.position.assign(i), o -> o.position) + .append(new KeyedCodec<>("Position", Vector3dUtil.CODEC), (o, i) -> o.position.set(i), o -> o.position) .add() - .append(new KeyedCodec<>("Rotation", Vector3f.ROTATION), (o, i) -> o.rotation.assign(i), o -> o.rotation) + .append(new KeyedCodec<>("Rotation", Rotation3f.CODEC), (o, i) -> o.rotation.set(i), o -> o.rotation) .add() .build(); @Nonnull private final Vector3d position = new Vector3d(); @Nonnull - private final Vector3f rotation = new Vector3f(); + private final Rotation3f rotation = new Rotation3f(); @Nonnull private final ModelTransform sentTransform = new ModelTransform(new Position(), new Direction(), new Direction()); @Nullable @@ -49,9 +52,9 @@ public class TransformComponent implements Component { public TransformComponent() { } - public TransformComponent(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { - this.position.assign(position); - this.rotation.assign(rotation); + public TransformComponent(@Nonnull Vector3dc position, @Nonnull Rotation3fc rotation) { + this.position.set(position); + this.rotation.set(rotation); } @Nonnull @@ -59,34 +62,34 @@ public class TransformComponent implements Component { return this.position; } - public void setPosition(@Nonnull Vector3d position) { - this.position.assign(position); + public void setPosition(@Nonnull Vector3dc position) { + this.position.set(position); } - public void teleportPosition(@Nonnull Vector3d position) { - double x = position.getX(); + public void teleportPosition(@Nonnull Vector3dc position) { + double x = position.x(); if (!Double.isNaN(x)) { - this.position.setX(x); + this.position.x = x; } - double y = position.getY(); + double y = position.y(); if (!Double.isNaN(y)) { - this.position.setY(y); + this.position.y = y; } - double z = position.getZ(); + double z = position.z(); if (!Double.isNaN(z)) { - this.position.setZ(z); + this.position.z = z; } } @Nonnull - public Vector3f getRotation() { + public Rotation3f getRotation() { return this.rotation; } - public void setRotation(@Nonnull Vector3f rotation) { - this.rotation.assign(rotation); + public void setRotation(@Nonnull Rotation3f rotation) { + this.rotation.set(rotation); } @Nonnull @@ -94,18 +97,18 @@ public class TransformComponent implements Component { return new Transform(this.position, this.rotation); } - public void teleportRotation(@Nonnull Vector3f rotation) { - float yaw = rotation.getYaw(); + public void teleportRotation(@Nonnull Rotation3f rotation) { + float yaw = rotation.yaw(); if (!Float.isNaN(yaw)) { this.rotation.setYaw(yaw); } - float pitch = rotation.getPitch(); + float pitch = rotation.pitch(); if (!Float.isNaN(pitch)) { this.rotation.setPitch(pitch); } - float roll = rotation.getRoll(); + float roll = rotation.roll(); if (!Float.isNaN(roll)) { this.rotation.setRoll(roll); } diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/AliveCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/AliveCondition.java similarity index 93% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/AliveCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/AliveCondition.java index 13369153..41541ca2 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/AliveCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/AliveCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/ChargingCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/ChargingCondition.java similarity index 96% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/ChargingCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/ChargingCondition.java index 7617c131..e1a1656d 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/ChargingCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/ChargingCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/PlayerCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/CheckPlayerGameModeCondition.java similarity index 59% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/PlayerCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/CheckPlayerGameModeCondition.java index da3f2a7d..96f60bef 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/PlayerCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/CheckPlayerGameModeCondition.java @@ -1,8 +1,9 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.EnumCodec; +import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.GameMode; @@ -12,36 +13,35 @@ import java.time.Instant; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class PlayerCondition extends Condition { +public class CheckPlayerGameModeCondition extends Condition { @Nonnull - public static final BuilderCodec CODEC = BuilderCodec.builder(PlayerCondition.class, PlayerCondition::new, Condition.BASE_CODEC) + public static final BuilderCodec CODEC = BuilderCodec.builder( + CheckPlayerGameModeCondition.class, CheckPlayerGameModeCondition::new, BASE_CODEC + ) .append( new KeyedCodec<>("GameMode", new EnumCodec<>(GameMode.class)), (condition, gameMode) -> condition.gameModeToCheck = gameMode, condition -> condition.gameModeToCheck ) + .addValidator(Validators.nonNull()) .documentation("The game mode to check for. If null, the condition always passes.") .add() .build(); @Nullable private GameMode gameModeToCheck; - protected PlayerCondition() { + protected CheckPlayerGameModeCondition() { } @Override public boolean eval0(@Nonnull ComponentAccessor componentAccessor, @Nonnull Ref ref, @Nonnull Instant currentTime) { - if (this.gameModeToCheck == null) { - return true; - } else { - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - return playerComponent == null ? false : playerComponent.getGameMode() == this.gameModeToCheck; - } + Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); + return playerComponent == null ? false : playerComponent.getGameMode() == this.gameModeToCheck; } @Nonnull @Override public String toString() { - return "PlayerCondition{} " + super.toString(); + return "CheckPlayerGameModeCondition{gameModeToCheck=" + this.gameModeToCheck + ", inverse=" + this.inverse + "}"; } } diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/Condition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/Condition.java similarity index 97% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/Condition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/Condition.java index 9b2e79d8..8cf5eaa3 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/Condition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/Condition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/EntityStatBoundCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/EntityStatBoundCondition.java similarity index 97% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/EntityStatBoundCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/EntityStatBoundCondition.java index 80a321f0..aad7f08a 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/EntityStatBoundCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/EntityStatBoundCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/EnvironmentCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/EnvironmentCondition.java similarity index 94% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/EnvironmentCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/EnvironmentCondition.java index 4adaa637..d048ca64 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/EnvironmentCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/EnvironmentCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; @@ -8,7 +8,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; @@ -19,6 +18,7 @@ import java.time.Instant; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class EnvironmentCondition extends Condition { @Nonnull @@ -61,7 +61,7 @@ public class EnvironmentCondition extends Condition { Vector3d position = transformComponent.getPosition(); World world = componentAccessor.getExternalData().getWorld(); ChunkStore chunkStore = world.getChunkStore(); - long chunkIndex = ChunkUtil.indexChunkFromBlock(position.getX(), position.getZ()); + long chunkIndex = ChunkUtil.indexChunkFromBlock(position.x(), position.z()); Ref chunkRef = chunkStore.getChunkReference(chunkIndex); if (chunkRef != null && chunkRef.isValid()) { Store chunkComponentStore = chunkStore.getStore(); diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/GlidingCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/GlidingCondition.java similarity index 94% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/GlidingCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/GlidingCondition.java index 658b90bc..a95c9e7a 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/GlidingCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/GlidingCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; diff --git a/src/com/hypixel/hytale/server/core/modules/entity/condition/HasEffectCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/HasEffectCondition.java new file mode 100644 index 00000000..998f1318 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/HasEffectCondition.java @@ -0,0 +1,57 @@ +package com.hypixel.hytale.server.core.modules.entity.condition; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; +import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.time.Instant; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class HasEffectCondition extends Condition { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(HasEffectCondition.class, HasEffectCondition::new, Condition.BASE_CODEC) + .appendInherited( + new KeyedCodec<>("EffectId", Codec.STRING), + (condition, effectId) -> condition.entityEffectId = effectId, + condition -> condition.entityEffectId, + (condition, parent) -> condition.entityEffectId = parent.entityEffectId + ) + .addValidatorLate(() -> EntityEffect.VALIDATOR_CACHE.getValidator().late()) + .documentation( + "The effect to check for. Returns true if the entity has an active effect matching this one. If null, this condition always returns false." + ) + .add() + .build(); + @Nullable + private String entityEffectId; + @Nullable + private EntityEffect entityEffect; + + protected HasEffectCondition() { + } + + @Override + public boolean eval0(@Nonnull ComponentAccessor componentAccessor, @Nonnull Ref ref, @Nonnull Instant currentTime) { + if (this.entityEffect == null) { + if (this.entityEffectId == null || this.entityEffectId.isEmpty()) { + return false; + } + + this.entityEffect = EntityEffect.getAssetMap().getAsset(this.entityEffectId); + } + + EffectControllerComponent effectControllerComponent = componentAccessor.getComponent(ref, EffectControllerComponent.getComponentType()); + return effectControllerComponent == null ? false : effectControllerComponent.hasEffect(this.entityEffect); + } + + @Nonnull + @Override + public String toString() { + return "HasEffectCondition{entityEffect=" + this.entityEffect + "}"; + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/entity/condition/InFluidCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/InFluidCondition.java new file mode 100644 index 00000000..ef5fb217 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/InFluidCondition.java @@ -0,0 +1,136 @@ +package com.hypixel.hytale.server.core.modules.entity.condition; + +import com.hypixel.hytale.assetstore.AssetExtraInfo; +import com.hypixel.hytale.assetstore.AssetRegistry; +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.array.ArrayCodec; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.shape.Box; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; +import com.hypixel.hytale.server.core.modules.collision.WorldUtil; +import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.IntSet; +import java.time.Instant; +import java.util.Arrays; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; + +public class InFluidCondition extends Condition { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(InFluidCondition.class, InFluidCondition::new, Condition.BASE_CODEC) + .appendInherited( + new KeyedCodec<>("FluidIds", new ArrayCodec<>(Codec.STRING, String[]::new)), + (condition, value) -> condition.fluidIds = value, + condition -> condition.fluidIds, + (condition, parent) -> condition.fluidIds = parent.fluidIds + ) + .addValidatorLate(() -> Fluid.VALIDATOR_CACHE.getArrayValidator().late()) + .documentation("Fluid IDs to match against. Returns true when at least one block within the entity's bounding box contains one of these fluids.") + .add() + .appendInherited( + new KeyedCodec<>("Tags", new ArrayCodec<>(Codec.STRING, String[]::new)), + (condition, value) -> condition.tags = value, + condition -> condition.tags, + (condition, parent) -> condition.tags = parent.tags + ) + .documentation( + "Fluid tags to match against. Returns true when at least one block within the entity's bounding box contains a fluid with any of these tags." + ) + .add() + .afterDecode(condition -> { + if (condition.fluidIds != null) { + condition.fluidIndexes = Arrays.stream(condition.fluidIds).mapToInt(id -> Fluid.getAssetMap().getIndex(id)).sorted().toArray(); + } + + if (condition.tags != null) { + condition.tagIndexes = Arrays.stream(condition.tags).mapToInt(AssetRegistry::getOrCreateTagIndex).sorted().toArray(); + } + }) + .build(); + @Nullable + protected String[] fluidIds; + @Nullable + protected String[] tags; + @Nullable + private transient int[] fluidIndexes; + @Nullable + private transient int[] tagIndexes; + + protected InFluidCondition() { + } + + private boolean isMatchingFluid(int fluidId) { + if (this.fluidIndexes != null && this.fluidIndexes.length > 0 && Arrays.binarySearch(this.fluidIndexes, fluidId) >= 0) { + return true; + } else { + if (this.tagIndexes != null && this.tagIndexes.length > 0) { + Fluid fluid = Fluid.getAssetMap().getAsset(fluidId); + if (fluid != null) { + AssetExtraInfo.Data data = fluid.getData(); + if (data != null) { + Int2ObjectMap fluidTags = data.getTags(); + + for (int tagIndex : this.tagIndexes) { + if (fluidTags.containsKey(tagIndex)) { + return true; + } + } + } + } + } + + return false; + } + } + + @Override + public boolean eval0(@Nonnull ComponentAccessor componentAccessor, @Nonnull Ref ref, @Nonnull Instant currentTime) { + TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); + + assert transformComponent != null; + + BoundingBox boundingBoxComponent = componentAccessor.getComponent(ref, BoundingBox.getComponentType()); + if (boundingBoxComponent == null) { + return false; + } else { + World world = componentAccessor.getExternalData().getWorld(); + ChunkStore chunkStore = world.getChunkStore(); + Store chunkComponentStore = chunkStore.getStore(); + Vector3d position = transformComponent.getPosition(); + Box box = boundingBoxComponent.getBoundingBox(); + return !box.forEachBlock(position.x, position.y, position.z, 0.0, (bx, by, bz) -> { + Ref chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(bx, bz)); + if (chunkRef != null && chunkRef.isValid()) { + long packed = WorldUtil.getPackedMaterialAndFluidAtPosition(chunkRef, chunkComponentStore, bx, by, bz); + int fluidId = MathUtil.unpackRight(packed); + return fluidId == 0 || !this.isMatchingFluid(fluidId); + } else { + return true; + } + }); + } + } + + @Nonnull + @Override + public String toString() { + return "InFluidCondition{fluidIds=" + + Arrays.toString((Object[])this.fluidIds) + + ", tags=" + + Arrays.toString((Object[])this.tags) + + "} " + + super.toString(); + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/entity/condition/IsPlayerCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/IsPlayerCondition.java new file mode 100644 index 00000000..4581c171 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/IsPlayerCondition.java @@ -0,0 +1,29 @@ +package com.hypixel.hytale.server.core.modules.entity.condition; + +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.time.Instant; +import javax.annotation.Nonnull; + +public class IsPlayerCondition extends Condition { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder(IsPlayerCondition.class, IsPlayerCondition::new, Condition.BASE_CODEC) + .build(); + + protected IsPlayerCondition() { + } + + @Override + public boolean eval0(@Nonnull ComponentAccessor componentAccessor, @Nonnull Ref ref, @Nonnull Instant currentTime) { + return componentAccessor.getComponent(ref, Player.getComponentType()) != null; + } + + @Nonnull + @Override + public String toString() { + return "IsPlayerCondition{} " + super.toString(); + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/LogicCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/LogicCondition.java similarity index 97% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/LogicCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/LogicCondition.java index be175b94..3c7fac7e 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/LogicCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/LogicCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/NoDamageTakenCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/NoDamageTakenCondition.java similarity index 96% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/NoDamageTakenCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/NoDamageTakenCondition.java index 3efd33cd..6e46a67c 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/NoDamageTakenCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/NoDamageTakenCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/OutOfCombatCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/OutOfCombatCondition.java similarity index 96% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/OutOfCombatCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/OutOfCombatCondition.java index 5f0d329b..e373b2ad 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/OutOfCombatCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/OutOfCombatCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/RegenHealthCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/RegenHealthCondition.java similarity index 92% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/RegenHealthCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/RegenHealthCondition.java index e7743660..b7fd28a4 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/RegenHealthCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/RegenHealthCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/SprintingCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/SprintingCondition.java similarity index 94% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/SprintingCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/SprintingCondition.java index 79227a9f..1575f1ce 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/SprintingCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/SprintingCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/StatCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/StatCondition.java similarity index 97% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/StatCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/StatCondition.java index 4ae3f15a..e7fe744d 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/StatCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/StatCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/SuffocatingCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/SuffocatingCondition.java similarity index 95% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/SuffocatingCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/SuffocatingCondition.java index 5cb8f6eb..52c475e5 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/SuffocatingCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/SuffocatingCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; @@ -6,7 +6,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.LivingEntity; @@ -17,6 +16,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import java.time.Instant; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SuffocatingCondition extends Condition { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/WieldingCondition.java b/src/com/hypixel/hytale/server/core/modules/entity/condition/WieldingCondition.java similarity index 94% rename from src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/WieldingCondition.java rename to src/com/hypixel/hytale/server/core/modules/entity/condition/WieldingCondition.java index 35bce17b..404f0f7f 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/condition/WieldingCondition.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/condition/WieldingCondition.java @@ -1,4 +1,4 @@ -package com.hypixel.hytale.server.core.modules.entitystats.asset.condition; +package com.hypixel.hytale.server.core.modules.entity.condition; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; diff --git a/src/com/hypixel/hytale/server/core/modules/entity/damage/Damage.java b/src/com/hypixel/hytale/server/core/modules/entity/damage/Damage.java index 468acd79..8cca87a7 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/damage/Damage.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/damage/Damage.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.modules.entity.damage; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.system.CancellableEcsEvent; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle; import com.hypixel.hytale.server.core.asset.type.particle.config.WorldParticle; @@ -20,6 +19,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Locale; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector4d; public class Damage extends CancellableEcsEvent implements IMetaStore { @Nonnull @@ -143,7 +143,7 @@ public class Damage extends CancellableEcsEvent implements IMetaStore { @Override public Message getDeathMessage(@Nonnull Damage info, @Nonnull Ref targetRef, @Nonnull ComponentAccessor componentAccessor) { return Message.translation("server.general.killedByCommand") - .param("displayName", this.commandSender.getDisplayName()) + .param("displayName", this.commandSender.getUsername()) .param("commandName", this.commandName != null ? this.commandName : "Unknown"); } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/damage/DamageModule.java b/src/com/hypixel/hytale/server/core/modules/entity/damage/DamageModule.java index faed3cb6..35b4b6eb 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/damage/DamageModule.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/damage/DamageModule.java @@ -89,6 +89,8 @@ public class DamageModule extends JavaPlugin { entityStoreRegistry.registerSystem(new DeathSystems.KillFeed()); entityStoreRegistry.registerSystem(new DeathSystems.PlayerDeathScreen()); entityStoreRegistry.registerSystem(new DeathSystems.PlayerDeathMarker()); + entityStoreRegistry.registerSystem(new DeathSystems.StopVoiceOnDeath()); + entityStoreRegistry.registerSystem(new DeathSystems.TickCorpseRemoval()); entityStoreRegistry.registerSystem(new DeathSystems.CorpseRemoval()); entityStoreRegistry.registerSystem(new DeathSystems.DeathAnimation()); entityStoreRegistry.registerSystem(new DeathSystems.SpawnedDeathAnimation()); @@ -98,6 +100,7 @@ public class DamageModule extends JavaPlugin { entityStoreRegistry.registerSystem(new RespawnSystems.ClearInteractionsRespawnSystem()); entityStoreRegistry.registerSystem(new RespawnSystems.CheckBrokenItemsRespawnSystem()); entityStoreRegistry.registerSystem(new RespawnSystems.ClearRespawnUI()); + entityStoreRegistry.registerSystem(new RespawnSystems.ReenableVoiceOnRespawn()); entityStoreRegistry.registerSystem(new DamageCalculatorSystems.SequenceModifier()); this.getCommandRegistry().registerCommand(new DesyncDamageCommand()); } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/damage/DamageSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/damage/DamageSystems.java index dc2c63bd..0e3b911e 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/damage/DamageSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/damage/DamageSystems.java @@ -22,9 +22,7 @@ import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector4d; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.CombatTextUpdate; @@ -49,6 +47,7 @@ import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.InteractionManager; +import com.hypixel.hytale.server.core.entity.ItemUtils; import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.damage.DamageDataComponent; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; @@ -56,12 +55,14 @@ import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementConfig; import com.hypixel.hytale.server.core.entity.knockback.KnockbackComponent; import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.EmptyItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.meta.DynamicMetaStore; import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery; import com.hypixel.hytale.server.core.modules.entity.EntityModule; +import com.hypixel.hytale.server.core.modules.entity.component.CachedStatsComponent; import com.hypixel.hytale.server.core.modules.entity.component.Intangible; import com.hypixel.hytale.server.core.modules.entity.component.Invulnerable; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; @@ -93,7 +94,6 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2DoubleMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectList; import it.unimi.dsi.fastutil.shorts.ShortArrayList; import java.time.Instant; import java.util.List; @@ -105,6 +105,8 @@ import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bouncycastle.util.Arrays; +import org.joml.Vector3d; +import org.joml.Vector4d; public class DamageSystems { public static final float DEFAULT_DAMAGE_DELAY = 1.0F; @@ -224,7 +226,7 @@ public class DamageSystems { SpatialResource, EntityStore> playerSpatialResource = commandBuffer.getResource( EntityModule.get().getPlayerSpatialResourceType() ); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(targetPosition, particlesViewDistance, results); Ref particleSource = damageCanBePredicted ? sourceRef : null; @@ -250,7 +252,7 @@ public class DamageSystems { SpawnModelParticles packet = new SpawnModelParticles(targetNetworkId, modelParticlesProtocol); SpatialResource, EntityStore> spatialResource = store.getResource(PLAYER_SPATIAL_RESOURCE_TYPE); SpatialStructure> spatialStructure = spatialResource.getSpatialStructure(); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialStructure.ordered(targetPosition, particlesViewDistance, results); for (Ref targetRef : results) { @@ -324,15 +326,18 @@ public class DamageSystems { Vector4d hitLocation = damage.getIfPresentMetaObject(Damage.HIT_LOCATION); Vector3d targetPosition = hitLocation == null ? transformComponent.getPosition() : new Vector3d(hitLocation.x, hitLocation.y, hitLocation.z); + boolean hasPlayerSound = playerComponent != null && playerSoundEffect != null && playerSoundEffect.getSoundEventIndex() != 0; if (soundEffect != null && soundEffect.getSoundEventIndex() != 0) { - Predicate> filter = sourceRef != null ? p -> !p.equals(sourceRef) : p -> true; + Predicate> filter = p -> sourceRef != null && p.equals(sourceRef) ? false : !hasPlayerSound || !p.equals(ref); SoundUtil.playSoundEvent3d(soundEffect.getSoundEventIndex(), targetPosition.x, targetPosition.y, targetPosition.z, filter, commandBuffer); } - if (playerComponent != null && playerSoundEffect != null && playerSoundEffect.getSoundEventIndex() != 0) { - SoundUtil.playSoundEvent3dToPlayer( - ref, playerSoundEffect.getSoundEventIndex(), SoundCategory.SFX, targetPosition.x, targetPosition.y, targetPosition.z, commandBuffer - ); + if (hasPlayerSound) { + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent != null) { + int worldIndex = soundEffect != null ? soundEffect.getSoundEventIndex() : 0; + SoundUtil.playLocalPlayerSoundEvent(playerRefComponent, playerSoundEffect.getSoundEventIndex(), worldIndex, SoundCategory.SFX); + } } } } @@ -368,17 +373,13 @@ public class DamageSystems { @Nonnull CommandBuffer commandBuffer, @Nonnull Damage damage ) { - LivingEntity entity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk); - - assert entity != null; - World world = commandBuffer.getExternalData().getWorld(); Ref ref = archetypeChunk.getReferenceTo(index); + InventoryComponent.Armor armorComponent = commandBuffer.getComponent(ref, InventoryComponent.Armor.getComponentType()); + ItemContainer armorContainer = (ItemContainer)(armorComponent != null ? armorComponent.getInventory() : EmptyItemContainer.INSTANCE); + EffectControllerComponent effectControllerComponent = archetypeChunk.getComponent(index, EffectControllerComponent.getComponentType()); Map resistances = getResistanceModifiers( - world, - entity.getInventory().getArmor(), - entity.canApplyItemStackPenalties(ref, commandBuffer), - archetypeChunk.getComponent(index, EffectControllerComponent.getComponentType()) + world, armorContainer, ItemUtils.canApplyItemStackPenalties(ref, commandBuffer), effectControllerComponent ); if (!damage.getCause().doesBypassResistances() && !resistances.isEmpty()) { DamageSystems.ArmorDamageReduction.ArmorResistanceModifiers damageModEntry = resistances.get(damage.getCause()); @@ -513,7 +514,10 @@ public class DamageSystems { private static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); @Nonnull private static final Query QUERY = Query.and( - AllLegacyLivingEntityTypesQuery.INSTANCE, DamageDataComponent.getComponentType(), TRANSFORM_COMPONENT_TYPE + AllLegacyLivingEntityTypesQuery.INSTANCE, + InventoryComponent.Armor.getComponentType(), + DamageDataComponent.getComponentType(), + TRANSFORM_COMPONENT_TYPE ); @Nullable @@ -544,35 +548,32 @@ public class DamageSystems { @Nonnull CommandBuffer commandBuffer, @Nonnull Damage damage ) { - LivingEntity entity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk); + InventoryComponent.Armor armorComponent = archetypeChunk.getComponent(index, InventoryComponent.Armor.getComponentType()); - assert entity != null; + assert armorComponent != null; - Inventory inventory = entity.getInventory(); - if (inventory != null) { - ItemContainer armorContainer = inventory.getArmor(); - if (armorContainer != null) { - KnockbackComponent knockbackComponent = damage.getIfPresentMetaObject(Damage.KNOCKBACK_COMPONENT); - if (knockbackComponent != null) { - float knockbackResistanceModifier = 0.0F; + ItemContainer armorContainer = armorComponent.getInventory(); + if (armorContainer != null) { + KnockbackComponent knockbackComponent = damage.getIfPresentMetaObject(Damage.KNOCKBACK_COMPONENT); + if (knockbackComponent != null) { + float knockbackResistanceModifier = 0.0F; - for (short i = 0; i < armorContainer.getCapacity(); i++) { - ItemStack itemStack = armorContainer.getItemStack(i); - if (itemStack != null && !itemStack.isEmpty()) { - Item item = itemStack.getItem(); - ItemArmor itemArmor = item.getArmor(); - if (itemArmor != null) { - Map knockbackResistances = itemArmor.getKnockbackResistances(); - if (knockbackResistances != null) { - DamageCause damageCause = damage.getCause(); - knockbackResistanceModifier += knockbackResistances.get(damageCause); - } + for (short i = 0; i < armorContainer.getCapacity(); i++) { + ItemStack itemStack = armorContainer.getItemStack(i); + if (itemStack != null && !itemStack.isEmpty()) { + Item item = itemStack.getItem(); + ItemArmor itemArmor = item.getArmor(); + if (itemArmor != null) { + Map knockbackResistances = itemArmor.getKnockbackResistances(); + if (knockbackResistances != null) { + DamageCause damageCause = damage.getCause(); + knockbackResistanceModifier += knockbackResistances.get(damageCause); } } } - - knockbackComponent.addModifier(Math.max(1.0F - knockbackResistanceModifier, 0.0F)); } + + knockbackComponent.addModifier(Math.max(1.0F - knockbackResistanceModifier, 0.0F)); } } } @@ -626,7 +627,13 @@ public class DamageSystems { long packed = LivingEntity.getPackedMaterialAndFluidAtBreathingHeight(ref, commandBuffer); BlockMaterial material = BlockMaterial.VALUES[MathUtil.unpackLeft(packed)]; int fluidId = MathUtil.unpackRight(packed); - if (!entity.canBreathe(ref, material, fluidId, commandBuffer) && oxygenStatValue.get() <= oxygenStatValue.getMin()) { + boolean canBreathe = entity.canBreathe(ref, material, fluidId, commandBuffer); + CachedStatsComponent cachedStatsComponent = archetypeChunk.getComponent(index, CachedStatsComponent.getComponentType()); + if (cachedStatsComponent != null) { + cachedStatsComponent.setCanBreathe(canBreathe); + } + + if (!canBreathe && oxygenStatValue.get() <= oxygenStatValue.getMin()) { Damage damage; if (fluidId != 0) { assert DamageCause.DROWNING != null; @@ -679,16 +686,19 @@ public class DamageSystems { Ref ref = archetypeChunk.getReferenceTo(index); DamageCause damageCause = damage.getCause(); if (damageCause.isDurabilityLoss()) { - ItemContainer armor = entity.getInventory().getArmor(); - ShortArrayList armorPartIndexes = new ShortArrayList(); - armor.forEachWithMeta((slotx, itemStack, _armorPartIndexes) -> { - if (!itemStack.isBroken()) { - _armorPartIndexes.add(slotx); + InventoryComponent.Armor armorComponent = commandBuffer.getComponent(ref, InventoryComponent.Armor.getComponentType()); + if (armorComponent != null) { + ItemContainer armor = armorComponent.getInventory(); + ShortArrayList armorPartIndexes = new ShortArrayList(); + armor.forEachWithMeta((slotx, itemStack, _armorPartIndexes) -> { + if (!itemStack.isBroken()) { + _armorPartIndexes.add(slotx); + } + }, armorPartIndexes); + if (!armorPartIndexes.isEmpty()) { + short slot = armorPartIndexes.getShort(RandomExtra.randomRange(armorPartIndexes.size())); + LivingEntity.decreaseItemStackDurability(ref, armor.getItemStack(slot), -3, slot, commandBuffer); } - }, armorPartIndexes); - if (!armorPartIndexes.isEmpty()) { - int slot = armorPartIndexes.getShort(RandomExtra.randomRange(armorPartIndexes.size())); - entity.decreaseItemStackDurability(ref, armor.getItemStack((short)slot), -3, slot, commandBuffer); } } } @@ -696,7 +706,7 @@ public class DamageSystems { public static class DamageAttackerTool extends DamageEventSystem { @Nonnull - private static final Query QUERY = AllLegacyLivingEntityTypesQuery.INSTANCE; + private static final Query QUERY = Query.and(AllLegacyLivingEntityTypesQuery.INSTANCE, InventoryComponent.Hotbar.getComponentType()); @Nullable @Override @@ -720,14 +730,14 @@ public class DamageSystems { if (damage.getCause().isDurabilityLoss() && damage.getSource() instanceof Damage.EntitySource entitySource) { Ref sourceRef = entitySource.getRef(); if (sourceRef.isValid()) { - if (EntityUtils.getEntity(sourceRef, commandBuffer) instanceof LivingEntity sourceLivingEntity) { - Inventory sourceInventory = sourceLivingEntity.getInventory(); - byte activeHotbarSlot = sourceInventory.getActiveHotbarSlot(); - if (activeHotbarSlot != -1) { - sourceLivingEntity.decreaseItemStackDurability( - sourceRef, sourceInventory.getItemInHand(), -1, sourceInventory.getActiveHotbarSlot(), commandBuffer - ); - } + InventoryComponent.Hotbar hotbarComponent = commandBuffer.getComponent(sourceRef, InventoryComponent.Hotbar.getComponentType()); + + assert hotbarComponent != null; + + byte activeHotbarSlot = hotbarComponent.getActiveSlot(); + if (activeHotbarSlot != -1) { + ItemStack itemInHand = InventoryComponent.getItemInHand(commandBuffer, sourceRef); + LivingEntity.decreaseItemStackDurability(sourceRef, itemInHand, -1, activeHotbarSlot, commandBuffer); } } } @@ -893,7 +903,7 @@ public class DamageSystems { assert velocityComponent != null; - double yVelocity = Math.abs(velocityComponent.getVelocity().getY()); + double yVelocity = Math.abs(velocityComponent.getVelocity().y()); World world = commandBuffer.getExternalData().getWorld(); int movementConfigIndex = world.getGameplayConfig().getPlayerConfig().getMovementConfigIndex(); MovementConfig movementConfig = MovementConfig.getAssetMap().getAsset(movementConfigIndex); @@ -993,7 +1003,7 @@ public class DamageSystems { assert velocityComponent != null; - double yVelocity = Math.abs(velocityComponent.getClientVelocity().getY()); + double yVelocity = Math.abs(velocityComponent.getClientVelocity().y()); World world = commandBuffer.getExternalData().getWorld(); PlayerConfig worldPlayerConfig = world.getGameplayConfig().getPlayerConfig(); List queue = playerInputComponent.getMovementUpdateQueue(); @@ -1274,7 +1284,7 @@ public class DamageSystems { assert transformComponent != null; - double posY = transformComponent.getPosition().getY(); + double posY = transformComponent.getPosition().y(); if (!(posY >= 0.0)) { boolean belowMinimum = posY < -32.0; Damage damage = new Damage(Damage.NULL_SOURCE, DamageCause.OUT_OF_WORLD, belowMinimum ? 2.1474836E9F : 50.0F); @@ -1372,9 +1382,7 @@ public class DamageSystems { playerRefComponent.getPacketHandler() .writeNoCache( new DamageInfo( - new com.hypixel.hytale.protocol.Vector3d(position.getX(), position.getY(), position.getZ()), - damage.getAmount(), - damageCause.toPacket() + new com.hypixel.hytale.protocol.Vector3d(position.x(), position.y(), position.z()), damage.getAmount(), damageCause.toPacket() ) ); } @@ -1552,7 +1560,7 @@ public class DamageSystems { assert transformComponent != null; Vector3d targetPosition = transformComponent.getPosition(); - Vector3f targetRotation = transformComponent.getRotation(); + Rotation3f targetRotation = transformComponent.getRotation(); if (damage.getSource() instanceof Damage.EntitySource entitySource) { Ref sourceRef = entitySource.getRef(); if (sourceRef.isValid()) { @@ -1583,7 +1591,7 @@ public class DamageSystems { if (angledWieldingDamageModifiers.containsKey(damageCauseIndex)) { Vector3d sourcePosition = sourceTransformComponent.getPosition(); float angleBetween = TrigMathUtil.atan2(sourcePosition.x - targetPosition.x, sourcePosition.z - targetPosition.z); - angleBetween = MathUtil.wrapAngle(angleBetween + (float) Math.PI - targetRotation.getYaw()); + angleBetween = MathUtil.wrapAngle(angleBetween + (float) Math.PI - targetRotation.yaw()); if (Math.abs(MathUtil.compareAngle(angleBetween, angledWielding.getAngleRad())) < angledWielding.getAngleDistanceRad()) { angledWieldingModifier = angledWieldingDamageModifiers.getOrDefault(damageCauseIndex, 1.0F); DamageEffects wieldingBlockedEffectsx = wielding.getBlockedEffects(); @@ -1688,7 +1696,7 @@ public class DamageSystems { Vector3d targetPos = transformComponent.getPosition(); Vector3d attackerPos = sourceTransformComponent.getPosition(); float angleBetween = TrigMathUtil.atan2(attackerPos.x - targetPos.x, attackerPos.z - targetPos.z); - angleBetween = MathUtil.wrapAngle(angleBetween + (float) Math.PI - transformComponent.getRotation().getYaw()); + angleBetween = MathUtil.wrapAngle(angleBetween + (float) Math.PI - transformComponent.getRotation().yaw()); if (Math.abs(MathUtil.compareAngle(angleBetween, angledWielding.getAngleRad())) < angledWielding.getAngleDistanceRad()) { angledWieldingModifier = angledWieldingKnockbackModifiers.getOrDefault(damageCauseIndex, 1.0); } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/damage/DeathSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/damage/DeathSystems.java index be7388ac..1bba7088 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/damage/DeathSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/damage/DeathSystems.java @@ -18,10 +18,10 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; +import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.InteractionState; @@ -44,6 +44,7 @@ import com.hypixel.hytale.server.core.entity.entities.player.pages.PageManager; import com.hypixel.hytale.server.core.entity.entities.player.pages.RespawnPage; import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.transaction.ItemStackSlotTransaction; @@ -61,17 +62,24 @@ import com.hypixel.hytale.server.core.modules.interaction.Interactions; import com.hypixel.hytale.server.core.modules.interaction.interaction.UnarmedInteractions; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; +import com.hypixel.hytale.server.core.modules.voice.VoiceModule; +import com.hypixel.hytale.server.core.modules.voice.VoicePlayerState; +import com.hypixel.hytale.server.core.modules.voice.VoiceRouter; import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.ParticleUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class DeathSystems { private static void playDeathAnimation( @@ -167,9 +175,13 @@ public class DeathSystems { public static class CorpseRemoval extends EntityTickingSystem { @Nonnull - private static final ComponentType DEFERRED_CORPSE_REMOVAL_COMPONENT_TYPE = DeferredCorpseRemoval.getComponentType(); + private static final Query QUERY = Query.and( + DeathComponent.getComponentType(), Query.not(Player.getComponentType()), TransformComponent.getComponentType() + ); @Nonnull - private static final Query QUERY = Query.and(DeathComponent.getComponentType(), Query.not(Player.getComponentType())); + private static final Set> DEPENDENCIES = Collections.singleton( + new SystemDependency<>(Order.AFTER, DeathSystems.TickCorpseRemoval.class) + ); @Nonnull @Override @@ -177,6 +189,12 @@ public class DeathSystems { return QUERY; } + @Nonnull + @Override + public Set> getDependencies() { + return DEPENDENCIES; + } + @Override public void tick( float dt, @@ -191,9 +209,19 @@ public class DeathSystems { InteractionChain deathInteractionChain = deathComponent.getInteractionChain(); if (deathInteractionChain == null || deathInteractionChain.getServerState() != InteractionState.NotFinished) { - DeferredCorpseRemoval corpseRemoval = archetypeChunk.getComponent(index, DEFERRED_CORPSE_REMOVAL_COMPONENT_TYPE); - if (corpseRemoval == null || corpseRemoval.tick(dt)) { + DeferredCorpseRemoval corpseRemoval = archetypeChunk.getComponent(index, DeferredCorpseRemoval.getComponentType()); + if (corpseRemoval == null) { commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE); + } else if (corpseRemoval.shouldRemove()) { + commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE); + String deathParticles = corpseRemoval.getDeathParticles(); + if (deathParticles != null) { + TransformComponent transformComponent = archetypeChunk.getComponent(index, TransformComponent.getComponentType()); + + assert transformComponent != null; + + ParticleUtil.spawnParticleEffect(deathParticles, transformComponent.getPosition(), commandBuffer); + } } } } @@ -253,17 +281,17 @@ public class DeathSystems { if (playerComponent.getGameMode() != GameMode.Creative) { component.setDisplayDataOnDeathScreen(true); - CombinedItemContainer combinedItemContainer = playerComponent.getInventory().getCombinedEverything(); + CombinedItemContainer combinedInventoryComponent = InventoryComponent.getCombined(commandBuffer, ref, InventoryComponent.EVERYTHING); if (component.getItemsDurabilityLossPercentage() > 0.0) { double durabilityLossRatio = component.getItemsDurabilityLossPercentage() / 100.0; boolean hasArmorBroken = false; - for (short i = 0; i < combinedItemContainer.getCapacity(); i++) { - ItemStack itemStack = combinedItemContainer.getItemStack(i); + for (short i = 0; i < combinedInventoryComponent.getCapacity(); i++) { + ItemStack itemStack = combinedInventoryComponent.getItemStack(i); if (!ItemStack.isEmpty(itemStack) && !itemStack.isBroken() && itemStack.getItem().getDurabilityLossOnDeath()) { double durabilityLoss = itemStack.getMaxDurability() * durabilityLossRatio; ItemStack updatedItemStack = itemStack.withIncreasedDurability(-durabilityLoss); - ItemStackSlotTransaction transaction = combinedItemContainer.replaceItemStackInSlot(i, itemStack, updatedItemStack); + ItemStackSlotTransaction transaction = combinedInventoryComponent.replaceItemStackInSlot(i, itemStack, updatedItemStack); if (transaction.getSlotAfter().isBroken() && itemStack.getItem().getArmor() != null) { hasArmorBroken = true; } @@ -271,7 +299,10 @@ public class DeathSystems { } if (hasArmorBroken) { - playerComponent.getStatModifiersManager().setRecalculate(true); + EntityStatMap entityStatMapComponent = commandBuffer.getComponent(ref, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + entityStatMapComponent.getStatModifiersManager().scheduleRecalculate(); + } } } @@ -286,17 +317,17 @@ public class DeathSystems { double itemAmountLossRatio = itemsAmountLossPercentage / 100.0; itemsToDrop = new ObjectArrayList<>(); - for (short ix = 0; ix < combinedItemContainer.getCapacity(); ix++) { - ItemStack itemStack = combinedItemContainer.getItemStack(ix); + for (short ix = 0; ix < combinedInventoryComponent.getCapacity(); ix++) { + ItemStack itemStack = combinedInventoryComponent.getItemStack(ix); if (!ItemStack.isEmpty(itemStack) && itemStack.getItem().dropsOnDeath()) { int quantityToLose = Math.max(1, MathUtil.floor(itemStack.getQuantity() * itemAmountLossRatio)); itemsToDrop.add(itemStack.withQuantity(quantityToLose)); int newQuantity = itemStack.getQuantity() - quantityToLose; if (newQuantity > 0) { ItemStack updatedItemStack = itemStack.withQuantity(newQuantity); - combinedItemContainer.replaceItemStackInSlot(ix, itemStack, updatedItemStack); + combinedInventoryComponent.replaceItemStackInSlot(ix, itemStack, updatedItemStack); } else { - combinedItemContainer.removeItemStackFromSlot(ix); + combinedInventoryComponent.removeItemStackFromSlot(ix); } } } @@ -314,8 +345,8 @@ public class DeathSystems { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); - Holder[] drops = ItemComponent.generateItemDrops(store, itemsToDrop, position.clone().add(0.0, 1.0, 0.0), headRotation); + Rotation3f headRotation = headRotationComponent.getRotation(); + Holder[] drops = ItemComponent.generateItemDrops(store, itemsToDrop, new Vector3d(position).add(0.0, 1.0, 0.0), headRotation); commandBuffer.addEntities(drops, AddReason.SPAWN); component.setItemsLostOnDeath(itemsToDrop); } @@ -422,7 +453,7 @@ public class DeathSystems { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - Transform transform = new Transform(position.getX(), position.getY(), position.getZ(), 0.0F, 0.0F, 0.0F); + Transform transform = new Transform(position.x(), position.y(), position.z(), 0.0F, 0.0F, 0.0F); WorldTimeResource worldTimeResource = commandBuffer.getResource(WorldTimeResource.getResourceType()); Instant gameTime = worldTimeResource.getGameTime(); int daysSinceWorldStart = (int)WorldTimeResource.ZERO_YEAR.until(gameTime, ChronoUnit.DAYS); @@ -509,7 +540,7 @@ public class DeathSystems { if (deathInfo != null && deathInfo.getSource() instanceof Damage.EntitySource entitySource) { Ref sourceRef = entitySource.getRef(); if (sourceRef.isValid()) { - Player attacker = store.getComponent(sourceRef, Player.getComponentType()); + PlayerRef attacker = store.getComponent(sourceRef, PlayerRef.getComponentType()); if (attacker != null) { attacker.sendMessage(Message.translation("server.general.killedEntity").param("entityName", nameplateComponent.getText())); } @@ -604,4 +635,75 @@ public class DeathSystems { ) { } } + + public static class StopVoiceOnDeath extends DeathSystems.OnDeathSystem { + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + + @Nonnull + @Override + public Query getQuery() { + return Query.and(Player.getComponentType(), PlayerRef.getComponentType()); + } + + public void onComponentAdded( + @Nonnull Ref ref, @Nonnull DeathComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent != null) { + UUID playerId = playerRefComponent.getUuid(); + VoiceModule voiceModule = VoiceModule.get(); + if (voiceModule != null) { + VoicePlayerState voiceState = voiceModule.getPlayerState(playerId); + if (voiceState != null) { + voiceState.setSilenced(true); + voiceState.setSpeaking(false); + VoiceRouter voiceRouter = voiceModule.getVoiceRouter(); + if (voiceRouter != null) { + PlayerRef playerRef = Universe.get().getPlayer(playerId); + if (playerRef != null) { + voiceRouter.sendVoiceConfig(playerRef); + } + } + } + } + } + } + } + + public static class TickCorpseRemoval extends EntityTickingSystem { + @Nonnull + private static final ComponentType DEFERRED_CORPSE_REMOVAL_COMPONENT_TYPE = DeferredCorpseRemoval.getComponentType(); + @Nonnull + private static final Query QUERY = Query.and( + DeathComponent.getComponentType(), Query.not(Player.getComponentType()), TransformComponent.getComponentType(), DEFERRED_CORPSE_REMOVAL_COMPONENT_TYPE + ); + + @Nonnull + @Override + public Query getQuery() { + return QUERY; + } + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer + ) { + DeathComponent deathComponent = archetypeChunk.getComponent(index, DeathComponent.getComponentType()); + + assert deathComponent != null; + + InteractionChain deathInteractionChain = deathComponent.getInteractionChain(); + if (deathInteractionChain == null || deathInteractionChain.getServerState() != InteractionState.NotFinished) { + DeferredCorpseRemoval corpseRemoval = archetypeChunk.getComponent(index, DEFERRED_CORPSE_REMOVAL_COMPONENT_TYPE); + + assert corpseRemoval != null; + + corpseRemoval.tick(dt); + } + } + } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/damage/DeferredCorpseRemoval.java b/src/com/hypixel/hytale/server/core/modules/entity/damage/DeferredCorpseRemoval.java index 791d9503..8eacc34f 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/damage/DeferredCorpseRemoval.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/damage/DeferredCorpseRemoval.java @@ -4,25 +4,38 @@ import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class DeferredCorpseRemoval implements Component { protected double timeRemaining; + @Nullable + protected String deathParticles; public static ComponentType getComponentType() { return DamageModule.get().getDeferredCorpseRemovalComponentType(); } - public DeferredCorpseRemoval(double timeUntilCorpseRemoval) { + public DeferredCorpseRemoval(double timeUntilCorpseRemoval, @Nullable String deathParticles) { this.timeRemaining = timeUntilCorpseRemoval; + this.deathParticles = deathParticles; } - public boolean tick(float dt) { - return (this.timeRemaining -= dt) <= 0.0; + public void tick(float dt) { + this.timeRemaining -= dt; + } + + public boolean shouldRemove() { + return this.timeRemaining <= 0.0; + } + + @Nullable + public String getDeathParticles() { + return this.deathParticles; } @Nonnull @Override public Component clone() { - return new DeferredCorpseRemoval(this.timeRemaining); + return new DeferredCorpseRemoval(this.timeRemaining, this.deathParticles); } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/damage/RespawnSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/damage/RespawnSystems.java index 6d0614e4..224bee19 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/damage/RespawnSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/damage/RespawnSystems.java @@ -12,9 +12,15 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.InteractionManager; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatValue; import com.hypixel.hytale.server.core.modules.interaction.InteractionModule; +import com.hypixel.hytale.server.core.modules.voice.VoiceModule; +import com.hypixel.hytale.server.core.modules.voice.VoicePlayerState; +import com.hypixel.hytale.server.core.modules.voice.VoiceRouter; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -30,12 +36,12 @@ public class RespawnSystems { public void onComponentRemoved( @Nonnull Ref ref, @Nonnull DeathComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { - Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); - assert playerComponent != null; + assert playerRefComponent != null; - if (playerComponent.getInventory().containsBrokenItem()) { - playerComponent.sendMessage(Message.translation("server.general.repair.itemBrokenOnRespawn").color("#ff5555")); + if (Inventory.containsBrokenItem(ref, commandBuffer)) { + playerRefComponent.sendMessage(Message.translation("server.general.repair.itemBrokenOnRespawn").color("#ff5555")); } } } @@ -112,6 +118,39 @@ public class RespawnSystems { } } + public static class ReenableVoiceOnRespawn extends RespawnSystems.OnRespawnSystem { + @Nonnull + @Override + public Query getQuery() { + return Query.and(Player.getComponentType(), PlayerRef.getComponentType()); + } + + public void onComponentRemoved( + @Nonnull Ref ref, @Nonnull DeathComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + ) { + PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent != null) { + VoiceModule voiceModule = VoiceModule.get(); + if (voiceModule != null) { + VoicePlayerState voiceState = voiceModule.getPlayerState(playerRefComponent.getUuid()); + if (voiceState != null) { + voiceState.setSilenced(false); + } + + VoiceRouter voiceRouter = voiceModule.getVoiceRouter(); + PlayerRef playerRef = Universe.get().getPlayer(playerRefComponent.getUuid()); + if (playerRef != null) { + if (voiceRouter != null) { + voiceRouter.sendVoiceConfig(playerRef); + } + + voiceModule.scheduleImmediatePositionUpdate(playerRef); + } + } + } + } + } + public static class ResetPlayerRespawnSystem extends RespawnSystems.OnRespawnSystem { @Nonnull @Override diff --git a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemComponent.java b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemComponent.java index 6c7e9b71..05ed7fc6 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemComponent.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemComponent.java @@ -12,8 +12,9 @@ import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.iterator.CircleIterator; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.ColorLight; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; @@ -39,6 +40,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ItemComponent implements Component { @Nonnull @@ -204,17 +206,17 @@ public class ItemComponent implements Component { @Nonnull public static Holder[] generateItemDrops( - @Nonnull ComponentAccessor accessor, @Nonnull List itemStacks, @Nonnull Vector3d position, @Nonnull Vector3f rotation + @Nonnull ComponentAccessor accessor, @Nonnull List itemStacks, @Nonnull Vector3d position, @Nonnull Rotation3fc rotation ) { if (itemStacks.size() == 1) { Holder itemEntityHolder = generateItemDrop(accessor, itemStacks.getFirst(), position, rotation, 0.0F, 3.25F, 0.0F); return itemEntityHolder == null ? Holder.emptyArray() : new Holder[]{itemEntityHolder}; } else { float randomAngleOffset = ThreadLocalRandom.current().nextFloat() * (float) (Math.PI * 2); - CircleIterator iterator = new CircleIterator(Vector3d.ZERO, 3.0, itemStacks.size(), randomAngleOffset); + CircleIterator iterator = new CircleIterator(Vector3dUtil.ZERO, 3.0, itemStacks.size(), randomAngleOffset); return itemStacks.stream().map(item -> { Vector3d circlePos = iterator.next(); - return generateItemDrop(accessor, item, position, rotation, (float)circlePos.getX(), 3.25F, (float)circlePos.getZ()); + return generateItemDrop(accessor, item, position, rotation, (float)circlePos.x(), 3.25F, (float)circlePos.z()); }).filter(Objects::nonNull).toArray(Holder[]::new); } } @@ -224,7 +226,7 @@ public class ItemComponent implements Component { @Nonnull ComponentAccessor accessor, @Nullable ItemStack itemStack, @Nonnull Vector3d position, - @Nonnull Vector3f rotation, + @Nonnull Rotation3fc rotation, float velocityX, float velocityY, float velocityZ @@ -270,7 +272,7 @@ public class ItemComponent implements Component { return null; } else { Holder holder = EntityStore.REGISTRY.newHolder(); - PickupItemComponent pickupItemComponent = new PickupItemComponent(targetRef, targetPosition.clone()); + PickupItemComponent pickupItemComponent = new PickupItemComponent(targetRef, new Vector3d(targetPosition)); holder.addComponent(PickupItemComponent.getComponentType(), pickupItemComponent); holder.addComponent(getComponentType(), itemItemComponent.clone()); holder.addComponent(TransformComponent.getComponentType(), itemTransformComponent.clone()); @@ -289,10 +291,10 @@ public class ItemComponent implements Component { @Nonnull ItemStack itemStack, @Nonnull Vector3d position, @Nonnull ComponentAccessor componentAccessor, @Nonnull Ref targetRef ) { Holder holder = EntityStore.REGISTRY.newHolder(); - PickupItemComponent pickupItemComponent = new PickupItemComponent(targetRef, position.clone()); + PickupItemComponent pickupItemComponent = new PickupItemComponent(targetRef, new Vector3d(position)); holder.addComponent(PickupItemComponent.getComponentType(), pickupItemComponent); holder.addComponent(getComponentType(), new ItemComponent(new ItemStack(itemStack.getItemId()))); - holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(position.clone(), Vector3f.ZERO.clone())); + holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(new Vector3d(position), new Rotation3f(Rotation3f.IDENTITY))); holder.ensureComponent(PreventItemMerging.getComponentType()); holder.ensureComponent(Intangible.getComponentType()); holder.addComponent(NetworkId.getComponentType(), new NetworkId(componentAccessor.getExternalData().takeNextNetworkId())); diff --git a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemMergeSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemMergeSystem.java index 4831d96c..c39769c9 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemMergeSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemMergeSystem.java @@ -10,7 +10,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ColorLight; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.inventory.ItemStack; @@ -20,8 +19,9 @@ import com.hypixel.hytale.server.core.modules.entity.component.Interactable; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.time.TimeResource; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ItemMergeSystem extends EntityTickingSystem { public static final float RADIUS = 2.0F; @@ -86,7 +86,7 @@ public class ItemMergeSystem extends EntityTickingSystem { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialResource.getSpatialStructure().ordered(position, 2.0, results); Ref reference = archetypeChunk.getReferenceTo(index); diff --git a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsComponent.java b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsComponent.java index 5b5b6b8e..410dfdbd 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsComponent.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsComponent.java @@ -2,11 +2,11 @@ package com.hypixel.hytale.server.core.modules.entity.item; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.collision.CollisionResult; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; @Deprecated public class ItemPhysicsComponent implements Component { diff --git a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsSystem.java index 7e547b46..33b29a9e 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPhysicsSystem.java @@ -9,7 +9,7 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.collision.BlockCollisionData; import com.hypixel.hytale.server.core.modules.collision.CollisionModule; import com.hypixel.hytale.server.core.modules.collision.CollisionResult; @@ -20,6 +20,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ItemPhysicsSystem extends EntityTickingSystem { @Nonnull @@ -82,7 +83,7 @@ public class ItemPhysicsSystem extends EntityTickingSystem { Vector3d position = transformComponent.getPosition(); Vector3d scaledVelocity = itemPhysicsComponent.scaledVelocity; CollisionResult collisionResult = itemPhysicsComponent.collisionResult; - velocityComponent.assignVelocityTo(scaledVelocity).scale(dt); + velocityComponent.assignVelocityTo(scaledVelocity).mul(dt); BoundingBox boundingBoxComponent = archetypeChunk.getComponent(index, this.boundingBoxComponentType); assert boundingBoxComponent != null; @@ -96,22 +97,22 @@ public class ItemPhysicsSystem extends EntityTickingSystem { BlockCollisionData blockCollisionData = collisionResult.getFirstBlockCollision(); if (blockCollisionData != null) { - if (blockCollisionData.collisionNormal.equals(Vector3d.UP)) { + if (blockCollisionData.collisionNormal.equals(Vector3dUtil.UP)) { velocityComponent.setZero(); - position.assign(blockCollisionData.collisionPoint); + position.set(blockCollisionData.collisionPoint); } else { Vector3d velocity = velocityComponent.getVelocity(); double dot = velocity.dot(blockCollisionData.collisionNormal); - Vector3d velocityToCancel = blockCollisionData.collisionNormal.clone().scale(dot); - velocity.subtract(velocityToCancel); + Vector3d velocityToCancel = new Vector3d(blockCollisionData.collisionNormal).mul(dot); + velocity.sub(velocityToCancel); } } else { - velocityComponent.assignVelocityTo(scaledVelocity).scale(dt); + velocityComponent.assignVelocityTo(scaledVelocity).mul(dt); position.add(scaledVelocity); } collisionResult.reset(); - if (position.getY() < -32.0) { + if (position.y() < -32.0) { LOGGER.at(Level.WARNING).log("Item fell out of the world %s", archetypeChunk.getReferenceTo(index)); commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE); } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPrePhysicsSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPrePhysicsSystem.java index 671fffe0..c6ed4025 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPrePhysicsSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/item/ItemPrePhysicsSystem.java @@ -10,8 +10,6 @@ import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.NearestBlockUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -27,6 +25,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class ItemPrePhysicsSystem extends EntityTickingSystem { public static final NearestBlockUtil.IterationElement[] SEARCH_ELEMENTS = new NearestBlockUtil.IterationElement[]{ @@ -140,7 +140,7 @@ public class ItemPrePhysicsSystem extends EntityTickingSystem { return testBlockType.getMaterial() != BlockMaterial.Solid; }, chunk); if (nearestBlock != null) { - position.assign(nearestBlock.x + 0.5, nearestBlock.y, nearestBlock.z + 0.5); + position.set(nearestBlock.x + 0.5, nearestBlock.y, nearestBlock.z + 0.5); } else { velocityComponent.setY(7.0 * blockBoundingBoxes.getBoundingBox().height()); } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/item/PickupItemComponent.java b/src/com/hypixel/hytale/server/core/modules/entity/item/PickupItemComponent.java index 101ae6fa..6dbbd433 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/item/PickupItemComponent.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/item/PickupItemComponent.java @@ -3,11 +3,11 @@ package com.hypixel.hytale.server.core.modules.entity.item; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PickupItemComponent implements Component { public static final float PICKUP_TRAVEL_TIME_DEFAULT = 0.15F; diff --git a/src/com/hypixel/hytale/server/core/modules/entity/item/PickupItemSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/item/PickupItemSystem.java index 830462d7..44528357 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/item/PickupItemSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/item/PickupItemSystem.java @@ -8,11 +8,11 @@ import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PickupItemSystem extends EntityTickingSystem { private static final float EYE_HEIGHT_SCALE = 5.0F; @@ -56,7 +56,7 @@ public class PickupItemSystem extends EntityTickingSystem { Vector3d position = transformComponent.getPosition(); TransformComponent targetTransformComponent = commandBuffer.getComponent(targetRef, this.transformComponentType); if (targetTransformComponent != null) { - Vector3d targetPosition = targetTransformComponent.getPosition().clone(); + Vector3d targetPosition = new Vector3d(targetTransformComponent.getPosition()); ModelComponent targetModelComponent = commandBuffer.getComponent(targetRef, ModelComponent.getComponentType()); if (targetModelComponent != null) { float targetModelEyeHeight = targetModelComponent.getModel().getEyeHeight(targetRef, commandBuffer); @@ -78,10 +78,10 @@ public class PickupItemSystem extends EntityTickingSystem { float originalLifeTime = pickupItemComponent.getOriginalLifeTime(); float progress = 1.0F - remainingTime / originalLifeTime; if (progress >= 1.0F) { - current.assign(target); + current.set(target); return true; } else { - current.assign(Vector3d.lerp(pickupItemComponent.getStartPosition(), target, progress)); + pickupItemComponent.getStartPosition().lerp(target, progress, current); pickupItemComponent.decreaseLifetime(dt); return false; } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/livingentity/LivingEntityEffectSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/livingentity/LivingEntityEffectSystem.java index 55e8cb2c..c0b9284c 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/livingentity/LivingEntityEffectSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/livingentity/LivingEntityEffectSystem.java @@ -10,38 +10,25 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.SystemGroup; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; -import com.hypixel.hytale.server.core.entity.EntityUtils; -import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.RemovalBehavior; import com.hypixel.hytale.server.core.entity.effect.ActiveEntityEffect; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; -import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.entity.condition.Condition; import com.hypixel.hytale.server.core.modules.entity.damage.DamageModule; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import java.time.Instant; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class LivingEntityEffectSystem extends EntityTickingSystem implements DisableProcessingAssert { @Nonnull - private static final Query QUERY = Query.and( - EffectControllerComponent.getComponentType(), TransformComponent.getComponentType(), BoundingBox.getComponentType() - ); - @Nonnull - private static final String EFFECT_NAME_BURN = "Burn"; - @Nonnull - private static final String BLOCK_TYPE_FLUID_WATER = "Fluid_Water"; + private static final Query QUERY = Query.and(EffectControllerComponent.getComponentType(), TransformComponent.getComponentType()); @Nonnull @Override @@ -70,22 +57,26 @@ public class LivingEntityEffectSystem extends EntityTickingSystem i if (!activeEffects.isEmpty()) { IndexedLookupTableAssetMap entityEffectAssetMap = EntityEffect.getAssetMap(); Ref entityRef = archetypeChunk.getReferenceTo(index); - ObjectIterator iterator = activeEffects.values().iterator(); EntityStatMap entityStatMapComponent = commandBuffer.getComponent(entityRef, EntityStatMap.getComponentType()); if (entityStatMapComponent != null) { - boolean invalidated = false; + IntList effectsToRemove = null; boolean invulnerable = false; - while (iterator.hasNext()) { - ActiveEntityEffect activeEntityEffect = iterator.next(); + for (ActiveEntityEffect activeEntityEffect : activeEffects.values()) { int entityEffectIndex = activeEntityEffect.getEntityEffectIndex(); EntityEffect entityEffect = entityEffectAssetMap.getAsset(entityEffectIndex); if (entityEffect == null) { - iterator.remove(); - invalidated = true; + if (effectsToRemove == null) { + effectsToRemove = new IntArrayList(); + } + + effectsToRemove.add(entityEffectIndex); } else if (!canApplyEffect(entityRef, entityEffect, commandBuffer)) { - iterator.remove(); - invalidated = true; + if (effectsToRemove == null) { + effectsToRemove = new IntArrayList(); + } + + effectsToRemove.add(entityEffectIndex); } else { float tickDelta = Math.min(activeEntityEffect.getRemainingDuration(), dt); activeEntityEffect.tick(commandBuffer, entityRef, entityEffect, entityStatMapComponent, tickDelta); @@ -94,9 +85,11 @@ public class LivingEntityEffectSystem extends EntityTickingSystem i } if (!activeEntityEffect.isInfinite() && activeEntityEffect.getRemainingDuration() <= 0.0F) { - iterator.remove(); - effectControllerComponent.tryResetModelChange(entityRef, activeEntityEffect.getEntityEffectIndex(), commandBuffer); - invalidated = true; + if (effectsToRemove == null) { + effectsToRemove = new IntArrayList(); + } + + effectsToRemove.add(entityEffectIndex); } if (activeEntityEffect.isInvulnerable()) { @@ -106,10 +99,9 @@ public class LivingEntityEffectSystem extends EntityTickingSystem i } effectControllerComponent.setInvulnerable(invulnerable); - if (invalidated) { - effectControllerComponent.invalidateCache(); - if (EntityUtils.getEntity(index, archetypeChunk) instanceof LivingEntity livingEntity) { - livingEntity.getStatModifiersManager().setRecalculate(true); + if (effectsToRemove != null) { + for (int effectIndex : effectsToRemove) { + effectControllerComponent.removeEffect(entityRef, effectIndex, RemovalBehavior.COMPLETE, commandBuffer); } } } @@ -125,40 +117,9 @@ public class LivingEntityEffectSystem extends EntityTickingSystem i public static boolean canApplyEffect( @Nonnull Ref ownerRef, @Nonnull EntityEffect entityEffect, @Nonnull ComponentAccessor componentAccessor ) { - if ("Burn".equals(entityEffect.getId())) { - TransformComponent transformComponent = componentAccessor.getComponent(ownerRef, TransformComponent.getComponentType()); - - assert transformComponent != null; - - Ref chunkRef = transformComponent.getChunkRef(); - if (chunkRef != null && chunkRef.isValid()) { - World world = componentAccessor.getExternalData().getWorld(); - Store chunkComponentStore = world.getChunkStore().getStore(); - WorldChunk worldChunkComponent = chunkComponentStore.getComponent(chunkRef, WorldChunk.getComponentType()); - - assert worldChunkComponent != null; - - BoundingBox boundingBoxComponent = componentAccessor.getComponent(ownerRef, BoundingBox.getComponentType()); - - assert boundingBoxComponent != null; - - Vector3d position = transformComponent.getPosition(); - Box boundingBox = boundingBoxComponent.getBoundingBox(); - LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atChunkCoords(world, worldChunkComponent.getX(), worldChunkComponent.getZ(), 1); - return boundingBox.forEachBlock(position, chunkAccessor, (x, y, z, _chunkAccessor) -> { - WorldChunk localChunk = _chunkAccessor.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(x, z)); - if (localChunk == null) { - return true; - } else { - BlockType blockType = localChunk.getBlockType(x, y, z); - return blockType == null ? true : !blockType.getId().contains("Fluid_Water"); - } - }); - } else { - return false; - } - } else { - return true; - } + Condition[] applyConditions = entityEffect.getApplyConditions(); + return applyConditions != null && applyConditions.length != 0 + ? Condition.allConditionsMet(componentAccessor, ownerRef, Instant.now(), applyConditions) + : true; } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/player/ChunkTracker.java b/src/com/hypixel/hytale/server/core/modules/entity/player/ChunkTracker.java index 34275dd3..690d310a 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/player/ChunkTracker.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/player/ChunkTracker.java @@ -13,7 +13,6 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.iterator.CircleSpiralIterator; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.metrics.MetricsRegistry; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.ToClientPacket; @@ -40,6 +39,7 @@ import java.util.concurrent.locks.StampedLock; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ChunkTracker implements Component { @Nonnull @@ -146,8 +146,8 @@ public class ChunkTracker implements Component { this.transformComponent = transformComponent; int chunkViewRadius = this.chunkViewRadius = playerComponent.getViewRadius(); Vector3d position = transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; int xDiff = Math.abs(this.lastChunkX - chunkX); int zDiff = Math.abs(this.lastChunkZ - chunkZ); int chunkMoveDistance = xDiff <= 0 && zDiff <= 0 ? 0 : (int)Math.ceil(Math.sqrt(xDiff * xDiff + zDiff * zDiff)); @@ -273,8 +273,8 @@ public class ChunkTracker implements Component { return false; } else { Vector3d position = this.transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; int x = ChunkUtil.xOfChunkIndex(chunkCoordinates); int z = ChunkUtil.zOfChunkIndex(chunkCoordinates); int minLoadedRadius = Math.max(this.minLoadedChunksRadius, this.chunkViewRadius); @@ -288,8 +288,8 @@ public class ChunkTracker implements Component { return ChunkTracker.ChunkVisibility.NONE; } else { Vector3d position = this.transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; int x = ChunkUtil.xOfChunkIndex(indexChunk); int z = ChunkUtil.zOfChunkIndex(indexChunk); int xDiff = Math.abs(x - chunkX); @@ -554,8 +554,8 @@ public class ChunkTracker implements Component { ) { World world = componentAccessor.getExternalData().getWorld(); Vector3d position = transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; int x = ChunkUtil.xOfChunkIndex(chunkIndex); int z = ChunkUtil.zOfChunkIndex(chunkIndex); boolean isHot = shouldBeVisible(this.maxHotLoadedChunksRadius, chunkX, chunkZ, x, z); diff --git a/src/com/hypixel/hytale/server/core/modules/entity/player/KnockbackPredictionSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/player/KnockbackPredictionSystems.java index 1d5abcf4..b559ee93 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/player/KnockbackPredictionSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/player/KnockbackPredictionSystems.java @@ -17,7 +17,6 @@ import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.Color; import com.hypixel.hytale.protocol.MovementSettings; import com.hypixel.hytale.protocol.MovementStates; @@ -38,10 +37,10 @@ import com.hypixel.hytale.server.core.universe.world.ParticleUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.PositionUtil; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class KnockbackPredictionSystems { public static boolean DEBUG_KNOCKBACK_POSITION = false; @@ -88,18 +87,18 @@ public class KnockbackPredictionSystems { Vector3d client = knockbackSimulationComponent.getClientPosition(); Vector3d clientLast = knockbackSimulationComponent.getClientLastPosition(); Vector3d relativeMovement = knockbackSimulationComponent.getRelativeMovement(); - clientLast.assign(client); + clientLast.set(client); boolean hasWishMovement = false; if (!queue.isEmpty()) { for (int i = 0; i < queue.size(); i++) { PlayerInput.InputUpdate update = queue.get(i); if (update instanceof PlayerInput.AbsoluteMovement abs) { - client.assign(abs.getX(), abs.getY(), abs.getZ()); + client.set(abs.getX(), abs.getY(), abs.getZ()); } else if (update instanceof PlayerInput.RelativeMovement rel) { client.add(rel.getX(), rel.getY(), rel.getZ()); } else if (update instanceof PlayerInput.WishMovement wish) { hasWishMovement = true; - relativeMovement.assign(wish.getX(), wish.getY(), wish.getZ()); + relativeMovement.set(wish.getX(), wish.getY(), wish.getZ()); } else { if (!(update instanceof PlayerInput.SetMovementStates)) { continue; @@ -118,7 +117,7 @@ public class KnockbackPredictionSystems { } if (!hasWishMovement) { - relativeMovement.assign(client).subtract(clientLast); + relativeMovement.set(client).sub(clientLast); if (knockbackSimulationComponent.hadWishMovement()) { knockbackSimulationComponent.setClientFinished(true); } @@ -220,8 +219,8 @@ public class KnockbackPredictionSystems { assert transformComponent != null; - component.getClientPosition().assign(transformComponent.getPosition()); - component.getSimPosition().assign(transformComponent.getPosition()); + component.getClientPosition().set(transformComponent.getPosition()); + component.getSimPosition().set(transformComponent.getPosition()); MovementStatesComponent movementStatesComponent = commandBuffer.getComponent(ref, MovementStatesComponent.getComponentType()); assert movementStatesComponent != null; @@ -312,7 +311,7 @@ public class KnockbackPredictionSystems { if (KnockbackPredictionSystems.DEBUG_KNOCKBACK_POSITION) { Vector3d particlePosition = knockbackSimulationComponent.getClientPosition(); SpatialResource, EntityStore> playerSpatialResource = store.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(particlePosition, 75.0, results); Color color = knockbackSimulationComponent.hadWishMovement() ? new Color((byte)-1, (byte)0, (byte)0) : new Color((byte)0, (byte)0, (byte)-1); ParticleUtil.spawnParticleEffect("Example_Simple", particlePosition, 0.0F, 0.0F, 0.0F, 1.0F, color, results, commandBuffer); @@ -377,7 +376,7 @@ public class KnockbackPredictionSystems { knockbackSimulationComponent.setJumpCombo(Math.min(knockbackSimulationComponent.getJumpCombo() + 1, 3)); } } else { - Vector3d checkPosition = simPos.clone(); + Vector3d checkPosition = new Vector3d(simPos); checkPosition.y += 0.1F; movementStates.falling = velocity.y < 0.0 && CollisionModule.get().validatePosition(world, hitBox, checkPosition, new CollisionResult()) != 0; if (movementStates.falling) { @@ -402,7 +401,7 @@ public class KnockbackPredictionSystems { ); break; case Set: - velocity.assign(requestedVelocity); + velocity.set(requestedVelocity); } PlayerRef playerRefComponent = archetypeChunk.getComponent(index, PlayerRef.getComponentType()); @@ -421,23 +420,23 @@ public class KnockbackPredictionSystems { ); } - requestedVelocity.assign(0.0); + requestedVelocity.zero(); knockbackSimulationComponent.setRequestedVelocityChangeType(null); Vector3d movementOffset = knockbackSimulationComponent.getMovementOffset(); - movementOffset.assign(0.0); + movementOffset.zero(); if (knockbackSimulationComponent.hadWishMovement()) { float converter = this.convertWishMovement(knockbackSimulationComponent, movementStates, movementManagerSettings); - velocity.addScaled(rel, converter); + velocity.fma((double)converter, rel); } else { - movementOffset.addScaled(rel, friction); - rel.assign(0.0); + movementOffset.fma((double)friction, rel); + rel.zero(); } - movementOffset.addScaled(velocity, 0.016666668F); + movementOffset.fma(0.016666668F, velocity); this.applyMovementOffset(world, hitBox, knockbackSimulationComponent, movementStates, movementOffset); Ref ref = archetypeChunk.getReferenceTo(index); if (time < 0.2F) { - Vector3d move = Vector3d.lerp(knockbackSimulationComponent.getClientPosition(), simPos, time / 0.2F); + Vector3d move = knockbackSimulationComponent.getClientPosition().lerp(simPos, time / 0.2F, new Vector3d()); playerComponent.moveTo(ref, move.x, move.y, move.z, commandBuffer); } else { playerComponent.moveTo(ref, simPos.x, simPos.y, simPos.z, commandBuffer); @@ -546,7 +545,7 @@ public class KnockbackPredictionSystems { @Nonnull Vector3d movementOffset ) { int moveCycles = (int)Math.ceil(movementOffset.length() / 0.25); - Vector3d cycleMovementOffset = moveCycles == 1 ? movementOffset : movementOffset.clone().scale(1.0F / moveCycles); + Vector3d cycleMovementOffset = moveCycles == 1 ? movementOffset : new Vector3d(movementOffset).mul(1.0F / moveCycles); simulation.setWasOnGround(movementStates.onGround); for (int i = 0; i < moveCycles; i++) { @@ -564,7 +563,7 @@ public class KnockbackPredictionSystems { Vector3d simPos = simulation.getSimPosition(); Vector3d velocity = simulation.getSimVelocity(); Vector3d checkPosition = simulation.getCheckPosition(); - checkPosition.assign(simPos); + checkPosition.set(simPos); CollisionResult collisionResult = simulation.getCollisionResult(); collisionResult.reset(); checkPosition.y = checkPosition.y + offset.y; @@ -613,7 +612,7 @@ public class KnockbackPredictionSystems { } } - simPos.assign(checkPosition); + simPos.set(checkPosition); } private boolean checkCollision( @@ -626,7 +625,7 @@ public class KnockbackPredictionSystems { @Nonnull CollisionResult result ) { Vector3d tempPosition = simulation.getTempPosition(); - tempPosition.assign(position); + tempPosition.set(position); tempPosition.add(0.0, 1.0E-4F, 0.0); return CollisionModule.get().validatePosition(world, hitBox, tempPosition, result) != 0; } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/player/KnockbackSimulation.java b/src/com/hypixel/hytale/server/core/modules/entity/player/KnockbackSimulation.java index f0baffe0..96416439 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/player/KnockbackSimulation.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/player/KnockbackSimulation.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.core.modules.entity.player; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ChangeVelocityType; import com.hypixel.hytale.protocol.MovementStates; import com.hypixel.hytale.server.core.modules.collision.CollisionResult; @@ -10,6 +9,7 @@ import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class KnockbackSimulation implements Component { public static final float KNOCKBACK_SIMULATION_TIME = 0.5F; @@ -65,7 +65,7 @@ public class KnockbackSimulation implements Component { this.requestedVelocityChangeType = ChangeVelocityType.Set; } - this.requestedVelocity.assign(velocity); + this.requestedVelocity.set(velocity); } @Nullable @@ -188,7 +188,7 @@ public class KnockbackSimulation implements Component { @Override public Component clone() { KnockbackSimulation simulation = new KnockbackSimulation(); - simulation.requestedVelocity.assign(this.requestedVelocity); + simulation.requestedVelocity.set(this.requestedVelocity); return simulation; } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerInput.java b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerInput.java index 8ef1961e..9626cdb6 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerInput.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerInput.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.MovementStates; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PlayerInput implements Component { @Nonnull @@ -159,7 +159,7 @@ public class PlayerInput implements Component { public void apply(CommandBuffer commandBuffer, @Nonnull ArchetypeChunk archetypeChunk, int index) { TransformComponent transformComponent = archetypeChunk.getComponent(index, TransformComponent.getComponentType()); if (transformComponent != null) { - transformComponent.getRotation().assign(this.direction.pitch, this.direction.yaw, this.direction.roll); + transformComponent.getRotation().set(this.direction.pitch, this.direction.yaw, this.direction.roll); } } } @@ -189,7 +189,7 @@ public class PlayerInput implements Component { public void apply(CommandBuffer commandBuffer, @Nonnull ArchetypeChunk archetypeChunk, int index) { HeadRotation headRotationComponent = archetypeChunk.getComponent(index, HeadRotation.getComponentType()); if (headRotationComponent != null) { - headRotationComponent.getRotation().assign(this.direction.pitch, this.direction.yaw, this.direction.roll); + headRotationComponent.getRotation().set(this.direction.pitch, this.direction.yaw, this.direction.roll); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerItemEntityPickupSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerItemEntityPickupSystem.java index 8bcda79c..144785a1 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerItemEntityPickupSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerItemEntityPickupSystem.java @@ -17,7 +17,6 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialStructure; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.InteractionChain; @@ -39,9 +38,10 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Int import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import com.hypixel.hytale.server.core.modules.time.TimeResource; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PlayerItemEntityPickupSystem extends EntityTickingSystem { @Nonnull @@ -133,7 +133,7 @@ public class PlayerItemEntityPickupSystem extends EntityTickingSystem pickupRadius)) { Ref reference = archetypeChunk.getReferenceTo(index); commandBuffer.run( @@ -152,7 +152,7 @@ public class PlayerItemEntityPickupSystem extends EntityTickingSystem> targetPlayerRefs = SpatialResource.getThreadLocalReferenceList(); + List> targetPlayerRefs = SpatialResource.getThreadLocalReferenceList(); spatialStructure.ordered(itemEntityPosition, pickupRadius, targetPlayerRefs); for (Ref targetPlayerRef : targetPlayerRefs) { diff --git a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerProcessMovementSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerProcessMovementSystem.java index d2a234de..e0c334fe 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerProcessMovementSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerProcessMovementSystem.java @@ -8,7 +8,7 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.InteractionManager; @@ -30,6 +30,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class PlayerProcessMovementSystem extends EntityTickingSystem { @Nonnull @@ -117,10 +118,10 @@ public class PlayerProcessMovementSystem extends EntityTickingSystem= 100.0) { + if (collisionResultComponent.getCollisionPositionOffsetCopy().lengthSquared() >= 100.0) { if (playerComponent.getGameMode() == GameMode.Adventure) { Entity.LOGGER .at(Level.WARNING) @@ -158,7 +159,7 @@ public class PlayerProcessMovementSystem extends EntityTickingSystem createShallowHolder(int index, @Nonnull ArchetypeChunk archetypeChunk) { + Archetype archetype = archetypeChunk.getArchetype(); + Component[] components = new Component[archetype.length()]; + + for (int i = archetype.getMinIndex(); i < archetype.length(); i++) { + ComponentType componentType = archetype.get(i); + if (componentType != null) { + components[i] = archetypeChunk.getComponent(index, componentType); + } + } + + return EntityStore.REGISTRY.newHolder(archetype, components); + } + + public static class EntityRemovedSystem extends HolderSystem { + @Nonnull + private final ComponentType playerComponentType; + + public EntityRemovedSystem(@Nonnull ComponentType playerComponentType) { + this.playerComponentType = playerComponentType; + } + + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + } + + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + World world = store.getExternalData().getWorld(); + if (world.getWorldConfig().isSavingPlayers()) { + Player playerComponent = holder.getComponent(this.playerComponentType); + + assert playerComponent != null; + + playerComponent.saveConfig(world, holder, true); + } + } + + @Nonnull + @Override + public Query getQuery() { + return this.playerComponentType; + } + } + public static class SaveDataResource implements Resource { private float delay = 10.0F; @@ -77,7 +131,7 @@ public class PlayerSavingSystems { @Override public void tick(float dt, int systemIndex, @Nonnull Store store) { World world = store.getExternalData().getWorld(); - if (world.getWorldConfig().isSavingPlayers()) { + if (!world.isSavingLocked() && world.getWorldConfig().isSavingPlayers()) { PlayerSavingSystems.SaveDataResource data = store.getResource(this.dataResourceType); data.delay -= dt; if (data.delay <= 0.0F) { @@ -109,16 +163,26 @@ public class PlayerSavingSystems { PlayerConfigData data = playerComponent.getPlayerConfigData(); Vector3d position = transformComponent.getPosition(); - Vector3f rotation = headRotationComponent.getRotation(); + Rotation3f rotation = headRotationComponent.getRotation(); Vector3d lastSavedPosition = data.lastSavedPosition; - Vector3f lastSavedRotation = data.lastSavedRotation; - if (!lastSavedPosition.equals(position) - || !lastSavedRotation.equals(rotation) - || data.consumeHasChanged() - || playerComponent.getInventory().consumeNeedsSaving()) { - lastSavedPosition.assign(position); - lastSavedRotation.assign(rotation); - playerComponent.saveConfig(store.getExternalData().getWorld(), EntityUtils.toHolder(index, archetypeChunk)); + Rotation3f lastSavedRotation = data.lastSavedRotation; + InventoryComponent.Storage storage = archetypeChunk.getComponent(index, InventoryComponent.Storage.getComponentType()); + InventoryComponent.Armor armor = archetypeChunk.getComponent(index, InventoryComponent.Armor.getComponentType()); + InventoryComponent.Hotbar hotbar = archetypeChunk.getComponent(index, InventoryComponent.Hotbar.getComponentType()); + InventoryComponent.Utility utility = archetypeChunk.getComponent(index, InventoryComponent.Utility.getComponentType()); + InventoryComponent.Tool tool = archetypeChunk.getComponent(index, InventoryComponent.Tool.getComponentType()); + InventoryComponent.Backpack backpack = archetypeChunk.getComponent(index, InventoryComponent.Backpack.getComponentType()); + boolean needsSaving = data.consumeHasChanged(); + needsSaving |= storage != null && storage.consumeNeedsSaving(); + needsSaving |= armor != null && armor.consumeNeedsSaving(); + needsSaving |= hotbar != null && hotbar.consumeNeedsSaving(); + needsSaving |= utility != null && utility.consumeNeedsSaving(); + needsSaving |= tool != null && tool.consumeNeedsSaving(); + needsSaving |= backpack != null && backpack.consumeNeedsSaving(); + if (!lastSavedPosition.equals(position) || !lastSavedRotation.equals(rotation) || needsSaving) { + lastSavedPosition.set(position); + lastSavedRotation.set(rotation); + playerComponent.saveConfig(store.getExternalData().getWorld(), PlayerSavingSystems.createShallowHolder(index, archetypeChunk), false); } } } @@ -149,6 +213,7 @@ public class PlayerSavingSystems { PlayerSavingSystems.LOGGER.at(Level.INFO).log("Disconnecting Players..."); } + ConcurrentLinkedDeque> futures = new ConcurrentLinkedDeque<>(); store.forEachEntityParallel(this.query, (index, archetypeChunk, commandBuffer) -> { Player playerComponent = archetypeChunk.getComponent(index, this.playerComponentType); @@ -160,11 +225,12 @@ public class PlayerSavingSystems { World world = commandBuffer.getExternalData().getWorld(); if (world.getWorldConfig().isSavingPlayers()) { - playerComponent.saveConfig(world, EntityUtils.toHolder(index, archetypeChunk)); + futures.add(playerComponent.saveConfig(world, PlayerSavingSystems.createShallowHolder(index, archetypeChunk), true)); } - playerRefComponent.getPacketHandler().disconnect("Stopping world!"); + playerRefComponent.getPacketHandler().disconnect(Message.translation("server.general.disconnect.stoppingWorld")); }); + CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerSendInventorySystem.java b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerSendInventorySystem.java index 0f0ae874..ce398a11 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerSendInventorySystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerSendInventorySystem.java @@ -6,8 +6,9 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; +import com.hypixel.hytale.protocol.packets.inventory.UpdatePlayerInventory; import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -48,13 +49,34 @@ public class PlayerSendInventorySystem extends EntityTickingSystem assert playerComponent != null; - Inventory inventory = playerComponent.getInventory(); - if (inventory.consumeIsDirty()) { + InventoryComponent.Storage storage = archetypeChunk.getComponent(index, InventoryComponent.Storage.getComponentType()); + InventoryComponent.Armor armor = archetypeChunk.getComponent(index, InventoryComponent.Armor.getComponentType()); + InventoryComponent.Hotbar hotbar = archetypeChunk.getComponent(index, InventoryComponent.Hotbar.getComponentType()); + InventoryComponent.Utility utility = archetypeChunk.getComponent(index, InventoryComponent.Utility.getComponentType()); + InventoryComponent.Tool tool = archetypeChunk.getComponent(index, InventoryComponent.Tool.getComponentType()); + InventoryComponent.Backpack backpack = archetypeChunk.getComponent(index, InventoryComponent.Backpack.getComponentType()); + boolean isStorageDirty = storage != null && storage.consumeIsDirty(); + boolean isArmorDirty = armor != null && armor.consumeIsDirty(); + boolean isHotbarDirty = hotbar != null && hotbar.consumeIsDirty(); + boolean isUtilityDirty = utility != null && utility.consumeIsDirty(); + boolean isToolDirty = tool != null && tool.consumeIsDirty(); + boolean isBackpackDirty = backpack != null && backpack.consumeIsDirty(); + if (isStorageDirty || isArmorDirty || isHotbarDirty || isUtilityDirty || isToolDirty || isBackpackDirty) { PlayerRef playerRefComponent = archetypeChunk.getComponent(index, this.refComponentType); assert playerRefComponent != null; - playerRefComponent.getPacketHandler().write(inventory.toPacket()); + playerRefComponent.getPacketHandler() + .writeNoCache( + new UpdatePlayerInventory( + isStorageDirty ? storage.getInventory().toPacket() : null, + isArmorDirty ? armor.getInventory().toPacket() : null, + isHotbarDirty ? hotbar.getInventory().toPacket() : null, + isUtilityDirty ? utility.getInventory().toPacket() : null, + isToolDirty ? tool.getInventory().toPacket() : null, + isBackpackDirty ? backpack.getInventory().toPacket() : null + ) + ); } playerComponent.getWindowManager().updateWindows(); diff --git a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerSystems.java index 90cc7cc4..d10d4846 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/player/PlayerSystems.java @@ -22,9 +22,8 @@ import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.component.system.tick.RunWhenPausedSystem; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.ComponentUpdate; import com.hypixel.hytale.protocol.EntityEffectsUpdate; import com.hypixel.hytale.protocol.EntityStatsUpdate; @@ -44,7 +43,9 @@ import com.hypixel.hytale.protocol.TransformUpdate; import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolsSetSoundSet; import com.hypixel.hytale.protocol.packets.entities.EntityUpdates; import com.hypixel.hytale.protocol.packets.inventory.SetActiveSlot; +import com.hypixel.hytale.protocol.packets.inventory.UpdatePlayerInventory; import com.hypixel.hytale.protocol.packets.player.SetBlockPlacementOverride; +import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; import com.hypixel.hytale.server.core.asset.type.gameplay.PlayerConfig; @@ -58,7 +59,9 @@ import com.hypixel.hytale.server.core.entity.entities.player.data.UniqueItemUsag import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager; import com.hypixel.hytale.server.core.entity.entities.player.pages.RespawnPage; import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; +import com.hypixel.hytale.server.core.event.events.player.RemovedPlayerFromWorldEvent; import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.io.PacketHandler; @@ -87,13 +90,13 @@ import com.hypixel.hytale.server.core.universe.world.WorldConfig; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.PositionUtil; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PlayerSystems { @Nonnull @@ -130,12 +133,12 @@ public class PlayerSystems { for (PlayerInput.InputUpdate entry : movementUpdateQueue) { switch (entry) { case PlayerInput.AbsoluteMovement abs: - shouldTeleport = transformComponent.getPosition().distanceSquaredTo(abs.getX(), abs.getY(), abs.getZ()) > 0.01F; + shouldTeleport = transformComponent.getPosition().distanceSquared(abs.getX(), abs.getY(), abs.getZ()) > 0.01F; break; case PlayerInput.RelativeMovement rel: Vector3d position = transformComponent.getPosition(); shouldTeleport = transformComponent.getPosition() - .distanceSquaredTo(position.x + rel.getX(), position.y + rel.getY(), position.z + rel.getZ()) + .distanceSquared(position.x + rel.getX(), position.y + rel.getY(), position.z + rel.getZ()) > 0.01F; break; case PlayerInput.SetHead head: @@ -164,55 +167,6 @@ public class PlayerSystems { } } - public static class EnsureEffectControllerSystem extends HolderSystem { - @Override - public Query getQuery() { - return PlayerRef.getComponentType(); - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - holder.ensureComponent(EffectControllerComponent.getComponentType()); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - } - - public static class EnsurePlayerInput extends HolderSystem { - @Override - public Query getQuery() { - return PlayerRef.getComponentType(); - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - holder.ensureComponent(PlayerInput.getComponentType()); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - holder.removeComponent(PlayerInput.getComponentType()); - } - } - - public static class EnsureUniqueItemUsagesSystem extends HolderSystem { - @Override - public Query getQuery() { - return Query.and(PlayerRef.getComponentType(), Query.not(UniqueItemUsagesComponent.getComponentType())); - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - holder.ensureComponent(UniqueItemUsagesComponent.getComponentType()); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - } - public static class KillFeedDecedentEventSystem extends EntityEventSystem { @Nonnull private final ComponentType playerRefComponentType = PlayerRef.getComponentType(); @@ -414,13 +368,49 @@ public class PlayerSystems { PlayerWorldData perWorldData = playerComponent.getPlayerConfigData().getPerWorldData(world.getName()); Player.initGameMode(ref, commandBuffer); playerConnection.writeNoCache(new BuilderToolsSetSoundSet(world.getGameplayConfig().getCreativePlaySoundSetIndex())); - playerComponent.sendInventory(); + List remainder = new ObjectArrayList<>(); + InventoryComponent.Storage storage = commandBuffer.getComponent(ref, InventoryComponent.Storage.getComponentType()); + if (storage != null) { + storage.ensureCapacity((short)36, remainder); + } + + InventoryComponent.Armor armor = commandBuffer.getComponent(ref, InventoryComponent.Armor.getComponentType()); + if (armor != null) { + armor.ensureCapacity(InventoryComponent.DEFAULT_ARMOR_CAPACITY, remainder); + } + + InventoryComponent.Hotbar hotbar = commandBuffer.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbar != null) { + hotbar.ensureCapacity((short)9, remainder); + } + + InventoryComponent.Utility utility = commandBuffer.getComponent(ref, InventoryComponent.Utility.getComponentType()); + if (utility != null) { + utility.ensureCapacity((short)4, remainder); + } + + InventoryComponent.Tool tool = commandBuffer.getComponent(ref, InventoryComponent.Tool.getComponentType()); + if (tool != null) { + tool.ensureCapacity((short)23, remainder); + } + + InventoryComponent.Backpack backpack = commandBuffer.getComponent(ref, InventoryComponent.Backpack.getComponentType()); + playerConnection.writeNoCache( + new UpdatePlayerInventory( + storage != null ? storage.getInventory().toPacket() : null, + armor != null ? armor.getInventory().toPacket() : null, + hotbar != null ? hotbar.getInventory().toPacket() : null, + utility != null ? utility.getInventory().toPacket() : null, + tool != null ? tool.getInventory().toPacket() : null, + backpack != null ? backpack.getInventory().toPacket() : null + ) + ); Inventory playerInventory = playerComponent.getInventory(); playerConnection.writeNoCache(new SetActiveSlot(-1, playerInventory.getActiveHotbarSlot())); playerConnection.writeNoCache(new SetActiveSlot(-5, playerInventory.getActiveUtilitySlot())); playerConnection.writeNoCache(new SetActiveSlot(-8, playerInventory.getActiveToolsSlot())); - if (playerInventory.containsBrokenItem()) { - playerComponent.sendMessage(Message.translation("server.general.repair.itemBrokenOnRespawn").color("#ff5555")); + if (Inventory.containsBrokenItem(ref, commandBuffer)) { + playerRefComponent.sendMessage(Message.translation("server.general.repair.itemBrokenOnRespawn").color("#ff5555")); } playerConnection.writeNoCache(new SetBlockPlacementOverride(playerComponent.isOverrideBlockPlacementRestrictions())); @@ -444,7 +434,7 @@ public class PlayerSystems { if (transform != null) { Vector3d position = transform.getPosition(); SpatialResource, EntityStore> playerSpatialResource = commandBuffer.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, 75.0, results); results.add(ref); if (playerComponent.isFirstSpawn()) { @@ -480,6 +470,37 @@ public class PlayerSystems { } } + public static class PlayerInitSystem extends HolderSystem { + @Override + public Query getQuery() { + return PlayerRef.getComponentType(); + } + + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + holder.ensureComponent(PlayerInput.getComponentType()); + holder.ensureComponent(EffectControllerComponent.getComponentType()); + holder.ensureComponent(UniqueItemUsagesComponent.getComponentType()); + Player player = holder.getComponent(Player.getComponentType()); + + assert player != null; + + player.getInventory().migrateToComponents(holder); + holder.ensureComponent(InventoryComponent.Storage.getComponentType()); + holder.ensureComponent(InventoryComponent.Armor.getComponentType()); + holder.ensureComponent(InventoryComponent.Hotbar.getComponentType()); + holder.ensureComponent(InventoryComponent.Utility.getComponentType()); + holder.ensureComponent(InventoryComponent.Tool.getComponentType()); + holder.ensureComponent(InventoryComponent.Backpack.getComponentType()); + player.getInventory().backwardsCompatHook(holder); + } + + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + holder.removeComponent(PlayerInput.getComponentType()); + } + } + public static class PlayerRemovedSystem extends HolderSystem { @Override public Query getQuery() { @@ -531,17 +552,20 @@ public class PlayerSystems { ); playerComponent.getPlayerConfigData() .getPerWorldData(world.getName()) - .setLastPosition(new Transform(transformComponent.getPosition().clone(), headRotationComponent.getRotation().clone())); + .setLastPosition(new Transform(new Vector3d(transformComponent.getPosition()), new Rotation3f(headRotationComponent.getRotation()))); playerRefComponent.getPacketHandler().setQueuePackets(false); playerRefComponent.getPacketHandler().tryFlush(); WorldConfig worldConfig = world.getWorldConfig(); - PlayerUtil.broadcastMessageToPlayers( - playerRefComponent.getUuid(), - Message.translation("server.general.playerLeftWorld") - .param("username", playerRefComponent.getUsername()) - .param("world", worldConfig.getDisplayName() != null ? worldConfig.getDisplayName() : WorldConfig.formatDisplayName(world.getName())), - store - ); + Message leaveMessage = Message.translation("server.general.playerLeftWorld") + .param("username", playerRefComponent.getUsername()) + .param("world", worldConfig.getDisplayName() != null ? worldConfig.getDisplayName() : WorldConfig.formatDisplayName(world.getName())); + RemovedPlayerFromWorldEvent event = HytaleServer.get() + .getEventBus() + .dispatchFor(RemovedPlayerFromWorldEvent.class, world.getName()) + .dispatch(new RemovedPlayerFromWorldEvent(holder, world, leaveMessage)); + if (event.shouldBroadcastLeaveMessage()) { + PlayerUtil.broadcastMessageToPlayers(playerRefComponent.getUuid(), event.getLeaveMessage(), store); + } } } @@ -744,7 +768,7 @@ public class PlayerSystems { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); + Rotation3f headRotation = headRotationComponent.getRotation(); PlayerRef playerRefComponent = archetypeChunk.getComponent(index, PlayerRef.getComponentType()); assert playerRefComponent != null; diff --git a/src/com/hypixel/hytale/server/core/modules/entity/repulsion/RepulsionSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/repulsion/RepulsionSystems.java index 29c33ac1..7aed7cef 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/repulsion/RepulsionSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/repulsion/RepulsionSystems.java @@ -19,8 +19,6 @@ import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ChangeVelocityType; import com.hypixel.hytale.protocol.ComponentUpdateType; import com.hypixel.hytale.protocol.RepulsionUpdate; @@ -32,12 +30,14 @@ import com.hypixel.hytale.server.core.modules.physics.component.Velocity; import com.hypixel.hytale.server.core.modules.physics.systems.IVelocityModifyingSystem; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; public class RepulsionSystems { public static class EntityTrackerRemove extends RefChangeSystem { @@ -248,7 +248,7 @@ public class RepulsionSystems { Vector2d position = new Vector2d(transformComponent.getPosition().x, transformComponent.getPosition().z); SpatialResource, EntityStore> spatialResource = store.getResource(this.spatialComponent); - List> results = new ObjectArrayList<>(); + List> results = new ReferenceArrayList<>(); spatialResource.getSpatialStructure().ordered(transformComponent.getPosition(), radius, results); for (Ref entityRef : results) { @@ -256,7 +256,7 @@ public class RepulsionSystems { if (entityTransformComponent != null) { Vector2d entityPosition = new Vector2d(entityTransformComponent.getPosition().x, entityTransformComponent.getPosition().z); if (!entityPosition.equals(position)) { - double distance = position.distanceTo(entityPosition); + double distance = position.distance(entityPosition); if (!(distance < 0.1)) { double fraction = (radius - distance) / radius; float maxForce = repulsion.maxForce; @@ -268,9 +268,9 @@ public class RepulsionSystems { double force = Math.max((double)repulsion.minForce, maxForce * fraction); force *= flip; - Vector2d push = entityPosition.subtract(position); + Vector2d push = entityPosition.sub(position); push.normalize(); - push.scale(force); + push.mul(force); Velocity entityVelocityComponent = commandBuffer.getComponent(entityRef, Velocity.getComponentType()); if (entityVelocityComponent != null) { Vector3d addedVelocity = new Vector3d((float)push.x, 0.0, (float)push.y); diff --git a/src/com/hypixel/hytale/server/core/modules/entity/system/AudioSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/system/AudioSystems.java index 095c5fd1..5502e0b5 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/system/AudioSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/system/AudioSystems.java @@ -10,7 +10,6 @@ import com.hypixel.hytale.component.SystemGroup; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.AudioUpdate; import com.hypixel.hytale.protocol.BlockSoundEvent; import com.hypixel.hytale.protocol.SoundCategory; @@ -28,6 +27,7 @@ import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class AudioSystems { public static class EntityTrackerUpdate extends EntityTickingSystem { diff --git a/src/com/hypixel/hytale/server/core/modules/entity/system/EntitySpatialSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/system/EntitySpatialSystem.java index 89280e7b..284f0e08 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/system/EntitySpatialSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/system/EntitySpatialSystem.java @@ -7,12 +7,12 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.Intangible; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EntitySpatialSystem extends SpatialSystem { public static final Query QUERY = Query.and( diff --git a/src/com/hypixel/hytale/server/core/modules/entity/system/ItemSpatialSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/system/ItemSpatialSystem.java index 3919b255..bcb4a0ab 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/system/ItemSpatialSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/system/ItemSpatialSystem.java @@ -8,12 +8,12 @@ import com.hypixel.hytale.component.query.AndQuery; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; import com.hypixel.hytale.server.core.modules.entity.item.PreventItemMerging; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ItemSpatialSystem extends SpatialSystem { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/entity/system/NetworkSendableSpatialSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/system/NetworkSendableSpatialSystem.java index 7fcbce45..383dd917 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/system/NetworkSendableSpatialSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/system/NetworkSendableSpatialSystem.java @@ -8,11 +8,11 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NetworkSendableSpatialSystem extends SpatialSystem { private static final Query QUERY = Archetype.of(TransformComponent.getComponentType(), NetworkId.getComponentType()); diff --git a/src/com/hypixel/hytale/server/core/modules/entity/system/PlayerSpatialSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/system/PlayerSpatialSystem.java index 431be59f..f0711588 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/system/PlayerSpatialSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/system/PlayerSpatialSystem.java @@ -8,11 +8,11 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PlayerSpatialSystem extends SpatialSystem { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/entity/system/RotateObjectSystem.java b/src/com/hypixel/hytale/server/core/modules/entity/system/RotateObjectSystem.java index 1b9e448a..2e4a5c9b 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/system/RotateObjectSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/system/RotateObjectSystem.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.entity.component.RotateObjectComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -47,7 +47,7 @@ public class RotateObjectSystem extends EntityTickingSystem { assert transformComponent != null; - Vector3f rotation = transformComponent.getRotation(); + Rotation3f rotation = transformComponent.getRotation(); rotation.y = rotation.y + rotateObjectComponent.getRotationSpeed() * dt; if (rotation.y >= 360.0F) { rotation.y %= 360.0F; diff --git a/src/com/hypixel/hytale/server/core/modules/entity/system/TransformSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/system/TransformSystems.java index ae0cdcad..fa1f2750 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/system/TransformSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/system/TransformSystems.java @@ -12,8 +12,8 @@ import com.hypixel.hytale.component.SystemGroup; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.ModelTransform; import com.hypixel.hytale.protocol.Position; @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class TransformSystems { public static class EntityTrackerUpdate extends EntityTickingSystem { @@ -75,8 +76,8 @@ public class TransformSystems { HeadRotation headRotationComponent = archetypeChunk.getComponent(index, this.headRotationComponentType); ModelTransform sentTransform = transformComponent.getSentTransform(); Vector3d position = transformComponent.getPosition(); - Vector3f headRotation = headRotationComponent != null ? headRotationComponent.getRotation() : Vector3f.ZERO; - Vector3f bodyRotation = transformComponent.getRotation(); + Rotation3fc headRotation = (Rotation3fc)(headRotationComponent != null ? headRotationComponent.getRotation() : Rotation3f.IDENTITY); + Rotation3f bodyRotation = transformComponent.getRotation(); Position sentPosition = sentTransform.position; Direction sentLookOrientation = sentTransform.lookOrientation; Direction sentBodyOrientation = sentTransform.bodyOrientation; diff --git a/src/com/hypixel/hytale/server/core/modules/entity/system/UpdateLocationSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/system/UpdateLocationSystems.java index 3b702552..d48f2e21 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/system/UpdateLocationSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/system/UpdateLocationSystems.java @@ -15,8 +15,7 @@ import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -31,6 +30,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class UpdateLocationSystems { @Nonnull @@ -43,14 +43,14 @@ public class UpdateLocationSystems { ) { if (world != null) { Vector3d position = transformComponent.getPosition(); - if (position.getY() < -32.0 && !commandBuffer.getArchetype(ref).contains(Player.getComponentType())) { + if (position.y() < -32.0 && !commandBuffer.getArchetype(ref).contains(Player.getComponentType())) { LOGGER.at(Level.WARNING).log("Unable to move entity below the world! -32 < " + position); commandBuffer.removeEntity(ref, RemoveReason.REMOVE); } else { ChunkStore chunkStore = world.getChunkStore(); Store chunkComponentStore = chunkStore.getStore(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; Ref oldChunkRef = transformComponent.getChunkRef(); boolean hasOldChunk = false; int oldChunkX = 0; @@ -136,8 +136,8 @@ public class UpdateLocationSystems { } else { LOGGER.at(Level.SEVERE).log("Player is in a chunk that can't be loaded! Moving (-%d,0,0)! %s", 32, transformComponent); Vector3d position = transformComponent.getPosition(); - Vector3d targetPosition = position.clone().subtract(32.0, 0.0, 0.0); - Vector3f bodyRotation = transformComponent.getRotation(); + Vector3d targetPosition = new Vector3d(position).sub(32.0, 0.0, 0.0); + Rotation3f bodyRotation = transformComponent.getRotation(); Teleport teleportComponent = Teleport.createForPlayer(targetPosition, bodyRotation); entityComponentAccessor.addComponent(ref, Teleport.getComponentType(), teleportComponent); PlayerRef playerRefComponent = entityComponentAccessor.getComponent(ref, PlayerRef.getComponentType()); diff --git a/src/com/hypixel/hytale/server/core/modules/entity/teleport/PendingTeleport.java b/src/com/hypixel/hytale/server/core/modules/entity/teleport/PendingTeleport.java index aaa4d9f4..3f3d4a0d 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/teleport/PendingTeleport.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/teleport/PendingTeleport.java @@ -2,13 +2,13 @@ package com.hypixel.hytale.server.core.modules.entity.teleport; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PendingTeleport implements Component { public static final double MAX_OFFSET = 0.001; @@ -28,10 +28,10 @@ public class PendingTeleport implements Component { if (teleportId != this.lastTeleportId) { return PendingTeleport.Result.INVALID_ID; } else { - this.position.assign(teleportPosition.x, teleportPosition.y, teleportPosition.z); + this.position.set(teleportPosition.x, teleportPosition.y, teleportPosition.z); Teleport teleport = this.pendingTeleports.removeFirst(); this.lastTeleportId++; - return teleport.getPosition().distanceSquaredTo(this.position) <= 0.001 ? PendingTeleport.Result.OK : PendingTeleport.Result.INVALID_POSITION; + return teleport.getPosition().distanceSquared(this.position) <= 0.001 ? PendingTeleport.Result.OK : PendingTeleport.Result.INVALID_POSITION; } } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/teleport/Teleport.java b/src/com/hypixel/hytale/server/core/modules/entity/teleport/Teleport.java index 25e53026..ef85bd35 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/teleport/Teleport.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/teleport/Teleport.java @@ -2,15 +2,16 @@ package com.hypixel.hytale.server.core.modules.entity.teleport; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Teleport implements Component { @Nullable @@ -18,9 +19,9 @@ public class Teleport implements Component { @Nonnull private final Vector3d position = new Vector3d(); @Nonnull - private final Vector3f rotation = new Vector3f(); + private final Rotation3f rotation = new Rotation3f(); @Nullable - private Vector3f headRotation; + private Rotation3f headRotation; private boolean resetVelocity = true; private CompletableFuture onComplete; @@ -29,34 +30,34 @@ public class Teleport implements Component { return EntityModule.get().getTeleportComponentType(); } - public Teleport(@Nullable World world, @Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public Teleport(@Nullable World world, @Nonnull Vector3d position, @Nonnull Rotation3f rotation) { this.world = world; - this.position.assign(position); - this.rotation.assign(rotation); + this.position.set(position); + this.rotation.set(rotation); } - public Teleport(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public Teleport(@Nonnull Vector3d position, @Nonnull Rotation3f rotation) { this.world = null; - this.position.assign(position); - this.rotation.assign(rotation); + this.position.set(position); + this.rotation.set(rotation); } @Nonnull public static Teleport createForPlayer(@Nullable World world, @Nonnull Transform transform) { - Vector3f headRotation = transform.getRotation(); - Vector3f bodyRotation = new Vector3f(0.0F, headRotation.getYaw(), 0.0F); + Rotation3f headRotation = transform.getRotation(); + Rotation3f bodyRotation = new Rotation3f(0.0F, headRotation.yaw(), 0.0F); return new Teleport(world, transform.getPosition(), bodyRotation).setHeadRotation(headRotation); } @Nonnull - public static Teleport createForPlayer(@Nullable World world, @Nonnull Vector3d position, @Nonnull Vector3f rotation) { - Vector3f headRotation = rotation.clone(); - Vector3f bodyRotation = new Vector3f(0.0F, headRotation.getYaw(), 0.0F); + public static Teleport createForPlayer(@Nullable World world, @Nonnull Vector3d position, @Nonnull Rotation3fc rotation) { + Rotation3f headRotation = new Rotation3f(rotation); + Rotation3f bodyRotation = new Rotation3f(0.0F, headRotation.yaw(), 0.0F); return new Teleport(world, position, bodyRotation).setHeadRotation(headRotation); } @Nonnull - public static Teleport createForPlayer(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public static Teleport createForPlayer(@Nonnull Vector3d position, @Nonnull Rotation3fc rotation) { return createForPlayer(null, position, rotation); } @@ -66,26 +67,26 @@ public class Teleport implements Component { } @Nonnull - public static Teleport createExact(@Nonnull Vector3d position, @Nonnull Vector3f bodyRotation, @Nonnull Vector3f headRotation) { + public static Teleport createExact(@Nonnull Vector3d position, @Nonnull Rotation3f bodyRotation, @Nonnull Rotation3f headRotation) { return new Teleport(position, bodyRotation).setHeadRotation(headRotation); } @Nonnull - public static Teleport createExact(@Nonnull Vector3d position, @Nonnull Vector3f bodyRotation) { + public static Teleport createExact(@Nonnull Vector3d position, @Nonnull Rotation3f bodyRotation) { return new Teleport(position, bodyRotation); } public void setPosition(@Nonnull Vector3d position) { - this.position.assign(position); + this.position.set(position); } - public void setRotation(@Nonnull Vector3f rotation) { - this.rotation.assign(rotation); + public void setRotation(@Nonnull Rotation3f rotation) { + this.rotation.set(rotation); } @Nonnull - public Teleport setHeadRotation(@Nonnull Vector3f headRotation) { - this.headRotation = headRotation.clone(); + public Teleport setHeadRotation(@Nonnull Rotation3f headRotation) { + this.headRotation = new Rotation3f(headRotation); return this; } @@ -113,12 +114,12 @@ public class Teleport implements Component { } @Nonnull - public Vector3f getRotation() { + public Rotation3f getRotation() { return this.rotation; } @Nullable - public Vector3f getHeadRotation() { + public Rotation3f getHeadRotation() { return this.headRotation; } diff --git a/src/com/hypixel/hytale/server/core/modules/entity/teleport/TeleportSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/teleport/TeleportSystems.java index d2ada8d2..08b5191e 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/teleport/TeleportSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/teleport/TeleportSystems.java @@ -10,9 +10,8 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.ModelTransform; import com.hypixel.hytale.protocol.packets.player.ClientTeleport; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -25,6 +24,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.PositionUtil; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TeleportSystems { public static class MoveSystem extends RefChangeSystem { @@ -153,7 +153,7 @@ public class TeleportSystems { CollisionResultComponent collisionResultComponent = commandBuffer.getComponent(ref, this.collisionResultComponentType); if (collisionResultComponent != null) { - collisionResultComponent.getCollisionStartPosition().assign(transformComponent.getPosition()); + collisionResultComponent.getCollisionStartPosition().set(transformComponent.getPosition()); } playerComponent.moveTo(ref, component.getPosition().x, component.getPosition().y, component.getPosition().z, commandBuffer); @@ -225,8 +225,8 @@ public class TeleportSystems { commandBuffer.removeComponent(ref, this.teleportComponentType); TransformComponent transformComponent = commandBuffer.getComponent(ref, this.transformComponentType); String originWorldName = commandBuffer.getExternalData().getWorld().getName(); - Location origin = new Location(originWorldName, transformComponent.getTransform().clone()); - Location destination = new Location(targetWorld.getName(), teleport.getPosition().clone(), teleport.getRotation().clone()); + Location origin = new Location(originWorldName, new Transform(transformComponent.getTransform())); + Location destination = new Location(targetWorld.getName(), new Vector3d(teleport.getPosition()), new Rotation3f(teleport.getRotation())); commandBuffer.run(s -> { TeleportRecord teleportRecord = s.ensureAndGetComponent(ref, TeleportRecord.getComponentType()); teleportRecord.setLastTeleport(new TeleportRecord.Entry(origin, destination, System.nanoTime())); @@ -264,19 +264,19 @@ public class TeleportSystems { PendingTeleport pendingTeleportComponent = commandBuffer.ensureAndGetComponent(ref, this.pendingTeleportComponentType); Vector3d teleportPosition = teleport.getPosition(); - Vector3f teleportRotation = teleport.getRotation(); + Rotation3f teleportRotation = teleport.getRotation(); transformComponent.teleportPosition(teleportPosition); transformComponent.teleportRotation(teleportRotation); HeadRotation headRotationComponent = commandBuffer.getComponent(ref, this.headRotationComponentType); if (headRotationComponent != null) { - Vector3f teleportHeadRotation = teleport.getHeadRotation(); + Rotation3f teleportHeadRotation = teleport.getHeadRotation(); headRotationComponent.teleportRotation(teleportHeadRotation != null ? teleportHeadRotation : teleportRotation); } TeleportRecord teleportHistory = commandBuffer.ensureAndGetComponent(ref, TeleportRecord.getComponentType()); World world = commandBuffer.getExternalData().getWorld(); - Location origin = new Location(world.getName(), teleportPosition.clone(), teleportRotation.clone()); - Location destination = new Location(world.getName(), teleportPosition.clone(), teleportRotation.clone()); + Location origin = new Location(world.getName(), new Vector3d(teleportPosition), new Rotation3f(teleportRotation)); + Location destination = new Location(world.getName(), new Vector3d(teleportPosition), new Rotation3f(teleportRotation)); teleportHistory.setLastTeleport(new TeleportRecord.Entry(origin, destination, System.nanoTime())); playerComponent.getWindowManager().validateWindows(ref, commandBuffer); int id = pendingTeleportComponent.queueTeleport(teleport); diff --git a/src/com/hypixel/hytale/server/core/modules/entity/tracker/EntityTrackerSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/tracker/EntityTrackerSystems.java index 0e8278d5..234efe2a 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/tracker/EntityTrackerSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/tracker/EntityTrackerSystems.java @@ -21,7 +21,6 @@ import com.hypixel.hytale.component.spatial.SpatialStructure; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ComponentUpdate; import com.hypixel.hytale.protocol.ComponentUpdateType; import com.hypixel.hytale.protocol.EntityEffectsUpdate; @@ -34,16 +33,14 @@ import com.hypixel.hytale.server.core.receiver.IPacketReceiver; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectIterator; -import it.unimi.dsi.fastutil.objects.ObjectList; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import java.util.Collections; import java.util.EnumSet; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -53,6 +50,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.StampedLock; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class EntityTrackerSystems { @Nonnull @@ -310,7 +308,7 @@ public class EntityTrackerSystems { SpatialStructure> spatialStructure = store.getResource(EntityModule.get().getNetworkSendableSpatialResourceType()) .getSpatialStructure(); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialStructure.collect(position, entityViewerComponent.viewRadiusBlocks, results); entityViewerComponent.visible.addAll(results); } @@ -530,7 +528,7 @@ public class EntityTrackerSystems { @Nonnull public Map, EntityTrackerSystems.EntityUpdate> updates; @Nonnull - public Object2IntMap> sent; + public Reference2IntMap> sent; public int lodExcludedCount; public int hiddenCount; @@ -541,23 +539,23 @@ public class EntityTrackerSystems { public EntityViewer(int viewRadiusBlocks, @Nonnull IPacketReceiver packetReceiver) { this.viewRadiusBlocks = viewRadiusBlocks; this.packetReceiver = packetReceiver; - this.visible = new ObjectOpenHashSet<>(); + this.visible = new ReferenceOpenHashSet<>(); this.updates = new ConcurrentHashMap<>(); - this.sent = new Object2IntOpenHashMap<>(); + this.sent = new Reference2IntOpenHashMap<>(); this.sent.defaultReturnValue(-1); } public EntityViewer(@Nonnull EntityTrackerSystems.EntityViewer other) { this.viewRadiusBlocks = other.viewRadiusBlocks; this.packetReceiver = other.packetReceiver; - this.visible = new HashSet<>(other.visible); + this.visible = new ReferenceOpenHashSet<>(other.visible); this.updates = new ConcurrentHashMap<>(other.updates.size()); for (Entry, EntityTrackerSystems.EntityUpdate> entry : other.updates.entrySet()) { this.updates.put(entry.getKey(), entry.getValue().clone()); } - this.sent = new Object2IntOpenHashMap<>(other.sent); + this.sent = new Reference2IntOpenHashMap<>(other.sent); this.sent.defaultReturnValue(-1); } @@ -710,12 +708,12 @@ public class EntityTrackerSystems { LOGGER.atWarning().log("Removed %d invalid updates for removed entities.", before - entityViewerComponent.updates.size()); } - ObjectIterator>> iterator = entityViewerComponent.sent - .object2IntEntrySet() + ObjectIterator>> iterator = entityViewerComponent.sent + .reference2IntEntrySet() .iterator(); while (iterator.hasNext()) { - it.unimi.dsi.fastutil.objects.Object2IntMap.Entry> entry = iterator.next(); + it.unimi.dsi.fastutil.objects.Reference2IntMap.Entry> entry = iterator.next(); Ref ref = entry.getKey(); if (ref == null || !ref.isValid() || !entityViewerComponent.visible.contains(ref)) { removedEntities.add(entry.getIntValue()); @@ -770,11 +768,11 @@ public class EntityTrackerSystems { @Nonnull private final StampedLock lock = new StampedLock(); @Nonnull - public Map, EntityTrackerSystems.EntityViewer> previousVisibleTo = new Object2ObjectOpenHashMap<>(); + public Map, EntityTrackerSystems.EntityViewer> previousVisibleTo = new Reference2ObjectOpenHashMap<>(); @Nonnull - public Map, EntityTrackerSystems.EntityViewer> visibleTo = new Object2ObjectOpenHashMap<>(); + public Map, EntityTrackerSystems.EntityViewer> visibleTo = new Reference2ObjectOpenHashMap<>(); @Nonnull - public Map, EntityTrackerSystems.EntityViewer> newlyVisibleTo = new Object2ObjectOpenHashMap<>(); + public Map, EntityTrackerSystems.EntityViewer> newlyVisibleTo = new Reference2ObjectOpenHashMap<>(); @Nonnull public static ComponentType getComponentType() { diff --git a/src/com/hypixel/hytale/server/core/modules/entity/tracker/LegacyEntityTrackerSystems.java b/src/com/hypixel/hytale/server/core/modules/entity/tracker/LegacyEntityTrackerSystems.java index 57ddbaa3..c8a58dc0 100644 --- a/src/com/hypixel/hytale/server/core/modules/entity/tracker/LegacyEntityTrackerSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entity/tracker/LegacyEntityTrackerSystems.java @@ -12,7 +12,6 @@ import com.hypixel.hytale.component.dependency.Order; import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.EquipmentUpdate; import com.hypixel.hytale.protocol.ItemArmorSlot; import com.hypixel.hytale.protocol.ModelUpdate; @@ -44,6 +43,7 @@ import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class LegacyEntityTrackerSystems { @Deprecated @@ -449,7 +449,7 @@ public class LegacyEntityTrackerSystems { if (targetBoundingBoxComponent != null) { TransformComponent targetTransformComponent = commandBuffer.getComponent(targetRef, TransformComponent.getComponentType()); if (targetTransformComponent != null) { - double distanceSq = targetTransformComponent.getPosition().distanceSquaredTo(position); + double distanceSq = targetTransformComponent.getPosition().distanceSquared(position); double maximumThickness = targetBoundingBoxComponent.getBoundingBox().getMaximumThickness(); if (maximumThickness < ENTITY_LOD_RATIO * distanceSq) { entityViewerComponent.lodExcludedCount++; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatMap.java b/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatMap.java index 8b1bea91..042b2587 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatMap.java +++ b/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatMap.java @@ -12,6 +12,7 @@ import com.hypixel.hytale.protocol.ChangeStatBehaviour; import com.hypixel.hytale.protocol.EntityStatOp; import com.hypixel.hytale.protocol.EntityStatUpdate; import com.hypixel.hytale.protocol.ValueType; +import com.hypixel.hytale.server.core.entity.StatModifiersManager; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.modules.entitystats.modifier.Modifier; import com.hypixel.hytale.server.core.modules.entitystats.modifier.StaticModifier; @@ -60,12 +61,17 @@ public class EntityStatMap implements Component { map.update(); }) .build(); + @Nonnull + private final StatModifiersManager statModifiersManager = new StatModifiersManager(); private Map unknown; @Nonnull private EntityStatValue[] values = EntityStatValue.EMPTY_ARRAY; float[] tempRegenerationValues = ArrayUtil.EMPTY_FLOAT_ARRAY; + @Nonnull public final Int2ObjectMap> selfUpdates = new Int2ObjectOpenHashMap<>(); + @Nonnull public final Int2ObjectMap selfStatValues = new Int2ObjectOpenHashMap<>(); + @Nonnull public final Int2ObjectMap> otherUpdates = new Int2ObjectOpenHashMap<>(); protected boolean isSelfNetworkOutdated; protected boolean isNetworkOutdated; @@ -74,6 +80,11 @@ public class EntityStatMap implements Component { return EntityStatsModule.get().getEntityStatMapComponentType(); } + @Nonnull + public StatModifiersManager getStatModifiersManager() { + return this.statModifiersManager; + } + public int size() { return this.values.length; } @@ -95,7 +106,7 @@ public class EntityStatMap implements Component { for (int index = 0; index < this.values.length; index++) { EntityStatType asset = assetMap.getAsset(index); EntityStatValue value = this.values[index]; - if (value != null) { + if (asset != null && value != null) { if (asset.isUnknown()) { if (this.unknown == null) { this.unknown = new Object2ObjectOpenHashMap<>(); @@ -116,18 +127,20 @@ public class EntityStatMap implements Component { for (int indexx = oldLength; indexx < assetCount; indexx++) { EntityStatType asset = assetMap.getAsset(indexx); - if (asset.isUnknown()) { - EntityStatValue value = this.values[indexx] = new EntityStatValue(indexx, asset); - this.addInitChange(indexx, value); - } else { - EntityStatValue value = this.unknown == null ? null : this.unknown.remove(asset.getId()); - if (value != null) { - value.synchronizeAsset(indexx, asset); - this.values[indexx] = value; + if (asset != null) { + if (asset.isUnknown()) { + EntityStatValue value = this.values[indexx] = new EntityStatValue(indexx, asset); this.addInitChange(indexx, value); } else { - value = this.values[indexx] = new EntityStatValue(indexx, asset); - this.addInitChange(indexx, value); + EntityStatValue value = this.unknown == null ? null : this.unknown.remove(asset.getId()); + if (value != null) { + value.synchronizeAsset(indexx, asset); + this.values[indexx] = value; + this.addInitChange(indexx, value); + } else { + value = this.values[indexx] = new EntityStatValue(indexx, asset); + this.addInitChange(indexx, value); + } } } } diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatsModule.java b/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatsModule.java index cbfbb837..c4ffb39f 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatsModule.java +++ b/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatsModule.java @@ -16,28 +16,12 @@ import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSystem; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.entity.Entity; -import com.hypixel.hytale.server.core.entity.EntityUtils; -import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entitystats.asset.DefaultEntityStatTypes; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatTypePacketGenerator; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.AliveCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.ChargingCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.Condition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.EnvironmentCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.GlidingCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.LogicCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.NoDamageTakenCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.OutOfCombatCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.PlayerCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.RegenHealthCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.SprintingCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.StatCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.SuffocatingCondition; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.WieldingCondition; import com.hypixel.hytale.server.core.modules.entitystats.modifier.Modifier; import com.hypixel.hytale.server.core.modules.entitystats.modifier.StaticModifier; import com.hypixel.hytale.server.core.modules.interaction.InteractionModule; @@ -53,6 +37,7 @@ import it.unimi.dsi.fastutil.objects.Object2FloatMap; import it.unimi.dsi.fastutil.objects.Object2FloatMap.Entry; import java.util.Collections; import java.util.Map; +import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bouncycastle.util.Arrays; @@ -79,19 +64,6 @@ public class EntityStatsModule extends JavaPlugin { protected void setup() { Modifier.CODEC.register("Boost", StaticModifier.class, StaticModifier.ENTITY_CODEC); Modifier.CODEC.register("Static", StaticModifier.class, StaticModifier.ENTITY_CODEC); - Condition.CODEC.register("LogicCondition", LogicCondition.class, LogicCondition.CODEC); - Condition.CODEC.register("RegenHealth", RegenHealthCondition.class, RegenHealthCondition.CODEC); - Condition.CODEC.register("NoDamageTaken", NoDamageTakenCondition.class, NoDamageTakenCondition.CODEC); - Condition.CODEC.register("Suffocating", SuffocatingCondition.class, SuffocatingCondition.CODEC); - Condition.CODEC.register("Charging", ChargingCondition.class, ChargingCondition.CODEC); - Condition.CODEC.register("Alive", AliveCondition.class, AliveCondition.CODEC); - Condition.CODEC.register("Environment", EnvironmentCondition.class, EnvironmentCondition.CODEC); - Condition.CODEC.register("Player", PlayerCondition.class, PlayerCondition.CODEC); - Condition.CODEC.register("OutOfCombat", OutOfCombatCondition.class, OutOfCombatCondition.CODEC); - Condition.CODEC.register("Wielding", WieldingCondition.class, WieldingCondition.CODEC); - Condition.CODEC.register("Sprinting", SprintingCondition.class, SprintingCondition.CODEC); - Condition.CODEC.register("Gliding", GlidingCondition.class, GlidingCondition.CODEC); - Condition.CODEC.register("Stat", StatCondition.class, StatCondition.CODEC); AssetRegistry.register( ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( EntityStatType.class, new IndexedLookupTableAssetMap<>(EntityStatType[]::new) @@ -165,11 +137,10 @@ public class EntityStatsModule extends JavaPlugin { Universe.get().getWorlds().forEach((s, world) -> world.execute(() -> { Store store = world.getEntityStore().getStore(); store.forEachEntityParallel(AllLegacyLivingEntityTypesQuery.INSTANCE, (index, archetypeChunk, commandBuffer) -> { - LivingEntity livingEntity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk); - - assert livingEntity != null; - - livingEntity.getStatModifiersManager().setRecalculate(true); + EntityStatMap entityStatMapComponent = archetypeChunk.getComponent(index, EntityStatMap.getComponentType()); + if (entityStatMapComponent != null) { + entityStatMapComponent.getStatModifiersManager().scheduleRecalculate(); + } }); })); } @@ -251,6 +222,7 @@ public class EntityStatsModule extends JavaPlugin { public class PlayerRegenerateStatsSystem extends EntityStatsSystems.Regenerate { public PlayerRegenerateStatsSystem() { + Objects.requireNonNull(EntityStatsModule.this); super(EntityStatsModule.this.entityStatMapComponentType, Player.getComponentType()); } } diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatsSystems.java b/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatsSystems.java index eff8bc7a..50ecb833 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatsSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/entitystats/EntityStatsSystems.java @@ -24,11 +24,11 @@ import com.hypixel.hytale.protocol.EntityStatUpdate; import com.hypixel.hytale.protocol.EntityStatsUpdate; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.InteractionManager; import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery; @@ -389,16 +389,12 @@ public class EntityStatsSystems { @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { - LivingEntity livingEntity = (LivingEntity)EntityUtils.getEntity(index, archetypeChunk); - - assert livingEntity != null; - EntityStatMap entityStatMapComponent = archetypeChunk.getComponent(index, this.entityStatMapComponentType); assert entityStatMapComponent != null; Ref ref = archetypeChunk.getReferenceTo(index); - livingEntity.getStatModifiersManager().recalculateEntityStatModifiers(ref, entityStatMapComponent, commandBuffer); + entityStatMapComponent.getStatModifiersManager().recalculateEntityStatModifiers(ref, entityStatMapComponent, commandBuffer); } } @@ -459,29 +455,28 @@ public class EntityStatsSystems { } } - EntityType entity = archetypeChunk.getComponent(index, this.entityTypeComponent); + InventoryComponent.Armor armorComponent = commandBuffer.getComponent(ref, InventoryComponent.Armor.getComponentType()); + if (armorComponent != null) { + ItemContainer armorContainer = armorComponent.getInventory(); + short armorContainerCapacity = armorContainer.getCapacity(); - assert entity != null; - - ItemContainer armorContainer = entity.getInventory().getArmor(); - short armorContainerCapacity = armorContainer.getCapacity(); - - for (short i = 0; i < armorContainerCapacity; i++) { - ItemStack itemStack = armorContainer.getItemStack(i); - if (!ItemStack.isEmpty(itemStack)) { - Item item = itemStack.getItem(); - if (item.getArmor() != null && item.getArmor().getRegeneratingValues() != null && !item.getArmor().getRegeneratingValues().isEmpty()) { - for (int statIndexx = 1; statIndexx < size; statIndexx++) { - EntityStatValue value = map.get(statIndexx); - if (value != null) { - List regenValues = item.getArmor().getRegeneratingValues().get(statIndexx); - if (regenValues != null && !regenValues.isEmpty()) { - for (RegeneratingValue regeneratingValuex : regenValues) { - if (regeneratingValuex.getRegenerating().getAmount() > 0.0F ? !(value.get() >= value.getMax()) : !(value.get() <= value.getMin()) - ) - { - map.tempRegenerationValues[statIndexx] = map.tempRegenerationValues[statIndexx] - + regeneratingValuex.regenerate(commandBuffer, ref, now, dt, value, map.tempRegenerationValues[statIndexx]); + for (short i = 0; i < armorContainerCapacity; i++) { + ItemStack itemStack = armorContainer.getItemStack(i); + if (!ItemStack.isEmpty(itemStack)) { + Item item = itemStack.getItem(); + if (item.getArmor() != null && item.getArmor().getRegeneratingValues() != null && !item.getArmor().getRegeneratingValues().isEmpty()) { + for (int statIndexx = 1; statIndexx < size; statIndexx++) { + EntityStatValue value = map.get(statIndexx); + if (value != null) { + List regenValues = item.getArmor().getRegeneratingValues().get(statIndexx); + if (regenValues != null && !regenValues.isEmpty()) { + for (RegeneratingValue regeneratingValuex : regenValues) { + if (regeneratingValuex.getRegenerating().getAmount() > 0.0F + ? !(value.get() >= value.getMax()) + : !(value.get() <= value.getMin())) { + map.tempRegenerationValues[statIndexx] = map.tempRegenerationValues[statIndexx] + + regeneratingValuex.regenerate(commandBuffer, ref, now, dt, value, map.tempRegenerationValues[statIndexx]); + } } } } @@ -489,19 +484,19 @@ public class EntityStatsSystems { } } } - } - for (int statIndexxx = 1; statIndexxx < size; statIndexxx++) { - EntityStatValue value = map.get(statIndexxx); - if (value != null) { - float amount = map.tempRegenerationValues[statIndexxx]; - boolean invulnerable = commandBuffer.getArchetype(ref).contains(Invulnerable.getComponentType()); - if (amount < 0.0F && !value.getIgnoreInvulnerability() && invulnerable) { - return; - } + for (int statIndexxx = 1; statIndexxx < size; statIndexxx++) { + EntityStatValue value = map.get(statIndexxx); + if (value != null) { + float amount = map.tempRegenerationValues[statIndexxx]; + boolean invulnerable = commandBuffer.getArchetype(ref).contains(Invulnerable.getComponentType()); + if (amount < 0.0F && !value.getIgnoreInvulnerability() && invulnerable) { + return; + } - if (amount != 0.0F) { - map.addStatValue(statIndexxx, amount); + if (amount != 0.0F) { + map.addStatValue(statIndexxx, amount); + } } } } diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/RegeneratingValue.java b/src/com/hypixel/hytale/server/core/modules/entitystats/RegeneratingValue.java index 2060340b..244d6362 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/RegeneratingValue.java +++ b/src/com/hypixel/hytale/server/core/modules/entitystats/RegeneratingValue.java @@ -2,8 +2,8 @@ package com.hypixel.hytale.server.core.modules.entitystats; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.server.core.modules.entity.condition.Condition; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.Condition; import com.hypixel.hytale.server.core.modules.entitystats.asset.modifier.RegeneratingModifier; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.time.Instant; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/EntityStatType.java b/src/com/hypixel/hytale/server/core/modules/entitystats/asset/EntityStatType.java index 7ac317bb..8057db80 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/EntityStatType.java +++ b/src/com/hypixel/hytale/server/core/modules/entitystats/asset/EntityStatType.java @@ -18,8 +18,8 @@ import com.hypixel.hytale.protocol.EntityStatResetBehavior; import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.io.NetworkSerializable; +import com.hypixel.hytale.server.core.modules.entity.condition.Condition; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatValue; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.Condition; import com.hypixel.hytale.server.core.modules.entitystats.asset.modifier.RegeneratingModifier; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import java.lang.ref.SoftReference; diff --git a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/modifier/RegeneratingModifier.java b/src/com/hypixel/hytale/server/core/modules/entitystats/asset/modifier/RegeneratingModifier.java index a4c2ec05..7ef9aece 100644 --- a/src/com/hypixel/hytale/server/core/modules/entitystats/asset/modifier/RegeneratingModifier.java +++ b/src/com/hypixel/hytale/server/core/modules/entitystats/asset/modifier/RegeneratingModifier.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.server.core.modules.entitystats.asset.condition.Condition; +import com.hypixel.hytale.server.core.modules.entity.condition.Condition; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.time.Instant; import java.util.Arrays; diff --git a/src/com/hypixel/hytale/server/core/modules/entityui/asset/CombatTextUIComponentPositionAnimationEvent.java b/src/com/hypixel/hytale/server/core/modules/entityui/asset/CombatTextUIComponentPositionAnimationEvent.java index 86aa7783..239521d8 100644 --- a/src/com/hypixel/hytale/server/core/modules/entityui/asset/CombatTextUIComponentPositionAnimationEvent.java +++ b/src/com/hypixel/hytale/server/core/modules/entityui/asset/CombatTextUIComponentPositionAnimationEvent.java @@ -3,11 +3,11 @@ package com.hypixel.hytale.server.core.modules.entityui.asset; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.vector.Vector2fUtil; import com.hypixel.hytale.protocol.CombatTextEntityUIAnimationEventType; import com.hypixel.hytale.protocol.CombatTextEntityUIComponentAnimationEvent; -import com.hypixel.hytale.protocol.Vector2f; -import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import javax.annotation.Nonnull; +import org.joml.Vector2f; public class CombatTextUIComponentPositionAnimationEvent extends CombatTextUIComponentAnimationEvent { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -16,7 +16,7 @@ public class CombatTextUIComponentPositionAnimationEvent extends CombatTextUICom CombatTextUIComponentAnimationEvent.ABSTRACT_CODEC ) .appendInherited( - new KeyedCodec<>("PositionOffset", ProtocolCodecs.VECTOR2F), + new KeyedCodec<>("PositionOffset", Vector2fUtil.CODEC), (event, f) -> event.positionOffset = f, event -> event.positionOffset, (parent, event) -> event.positionOffset = parent.positionOffset diff --git a/src/com/hypixel/hytale/server/core/modules/entityui/asset/EntityUIComponent.java b/src/com/hypixel/hytale/server/core/modules/entityui/asset/EntityUIComponent.java index 1ab6c6e3..1c3e3792 100644 --- a/src/com/hypixel/hytale/server/core/modules/entityui/asset/EntityUIComponent.java +++ b/src/com/hypixel/hytale/server/core/modules/entityui/asset/EntityUIComponent.java @@ -10,11 +10,11 @@ import com.hypixel.hytale.assetstore.map.JsonAssetWithMap; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.protocol.Vector2f; -import com.hypixel.hytale.server.core.codec.ProtocolCodecs; +import com.hypixel.hytale.math.vector.Vector2fUtil; import com.hypixel.hytale.server.core.io.NetworkSerializable; import java.lang.ref.SoftReference; import javax.annotation.Nonnull; +import org.joml.Vector2f; public abstract class EntityUIComponent implements JsonAssetWithMap>, @@ -23,7 +23,7 @@ public abstract class EntityUIComponent Codec.STRING, (t, k) -> t.id = k, t -> t.id, (t, data) -> t.data = data, t -> t.data ); public static final BuilderCodec ABSTRACT_CODEC = AssetBuilderCodec.abstractBuilder(EntityUIComponent.class) - .append(new KeyedCodec<>("HitboxOffset", ProtocolCodecs.VECTOR2F), (config, v) -> config.hitboxOffset = v, config -> config.hitboxOffset) + .append(new KeyedCodec<>("HitboxOffset", Vector2fUtil.CODEC), (config, v) -> config.hitboxOffset = v, config -> config.hitboxOffset) .documentation("Offset from the centre of the entity's hitbox to display this component.") .add() .build(); diff --git a/src/com/hypixel/hytale/server/core/modules/i18n/I18nModule.java b/src/com/hypixel/hytale/server/core/modules/i18n/I18nModule.java index 3b3e185d..7f16263d 100644 --- a/src/com/hypixel/hytale/server/core/modules/i18n/I18nModule.java +++ b/src/com/hypixel/hytale/server/core/modules/i18n/I18nModule.java @@ -38,10 +38,11 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Properties; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; @@ -74,12 +75,12 @@ public class I18nModule extends JavaPlugin { @Override protected void setup() { - this.getEventRegistry().register(LoadAssetEvent.class, event -> { + this.getEventRegistry().register((short)-32, LoadAssetEvent.class, event -> { for (AssetPack pack : AssetModule.get().getAssetPacks()) { this.loadMessagesFromPack(pack); } }); - this.getEventRegistry().register(AssetPackRegisterEvent.class, event -> this.loadMessagesFromPack(event.getAssetPack())); + this.getEventRegistry().register((short)-32, AssetPackRegisterEvent.class, event -> this.loadMessagesFromPack(event.getAssetPack())); this.getEventRegistry().register(AssetPackUnregisterEvent.class, event -> {}); this.getEventRegistry() .register( @@ -326,6 +327,8 @@ public class I18nModule extends JavaPlugin { private final Path languagesPath; public I18nAssetMonitorHandler(Path languagesPath) { + Objects.requireNonNull(I18nModule.this); + super(); this.languagesPath = languagesPath; } @@ -416,7 +419,7 @@ public class I18nModule extends JavaPlugin { } } - List players = Universe.get().getPlayers(); + Collection players = Universe.get().getPlayers(); Map updatePackets = new Object2ObjectOpenHashMap<>(); for (PlayerRef playerRef : players) { diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.java b/src/com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.java index 0025d255..18c55dd6 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.java @@ -1,16 +1,15 @@ package com.hypixel.hytale.server.core.modules.interaction; import com.hypixel.hytale.component.AddReason; -import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.BlockSoundEvent; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; @@ -34,8 +33,8 @@ import com.hypixel.hytale.server.core.entity.ItemUtils; import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent; import com.hypixel.hytale.server.core.event.events.ecs.DamageBlockEvent; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; -import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealth; import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealthChunk; import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealthModule; @@ -56,12 +55,13 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; import it.unimi.dsi.fastutil.objects.ObjectLists; import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class BlockHarvestUtils { @Nullable @@ -80,7 +80,7 @@ public class BlockHarvestUtils { String gatherType = breaking.getGatherType(); if (gatherType == null) { return null; - } else if (item == null || item.getWeapon() == null && item.getBuilderToolData() == null) { + } else if (item == null || item.getWeapon() == null && item.getBuilderTool() == null) { int requiredQuality = breaking.getQuality(); if (tool == null) { ItemToolSpec defaultSpec = ItemToolSpec.getAssetMap().getAsset(gatherType); @@ -161,10 +161,12 @@ public class BlockHarvestUtils { float damageScale, int setBlockSettings, @Nonnull Ref chunkReference, - @Nonnull CommandBuffer commandBuffer, + @Nonnull ComponentAccessor componentAccessor, @Nonnull ComponentAccessor chunkStore ) { - return performBlockDamage(null, null, targetBlock, itemStack, tool, null, false, damageScale, setBlockSettings, chunkReference, commandBuffer, chunkStore); + return performBlockDamage( + null, null, targetBlock, itemStack, tool, null, false, damageScale, setBlockSettings, chunkReference, componentAccessor, chunkStore + ); } public static boolean performBlockDamage( @@ -213,7 +215,7 @@ public class BlockHarvestUtils { } else { Vector3d targetBlockCenterPos = new Vector3d(); targetBlockType.getBlockCenter(targetRotationIndex, targetBlockCenterPos); - targetBlockCenterPos.add(targetBlockPos); + targetBlockCenterPos.add(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); Vector3i originBlock = new Vector3i(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); if (!targetBlockType.isUnknown()) { int filler = targetSection.getFiller(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); @@ -221,9 +223,9 @@ public class BlockHarvestUtils { int fillerY = FillerBlockUtil.unpackY(filler); int fillerZ = FillerBlockUtil.unpackZ(filler); if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { - originBlock = originBlock.clone().subtract(fillerX, fillerY, fillerZ); + originBlock = new Vector3i(originBlock).sub(fillerX, fillerY, fillerZ); String oldBlockTypeKey = targetBlockType.getId(); - targetBlockType = world.getBlockType(originBlock.getX(), originBlock.getY(), originBlock.getZ()); + targetBlockType = world.getBlockType(originBlock.x(), originBlock.y(), originBlock.z()); if (targetBlockType == null) { return false; } @@ -247,7 +249,7 @@ public class BlockHarvestUtils { ItemToolSpec itemToolSpec = getSpecPowerDamageBlock(heldItem, targetBlockType, tool); float specPower = itemToolSpec != null ? itemToolSpec.getPower() : 0.0F; - boolean canApplyItemStackPenalties = entity != null && entity.canApplyItemStackPenalties(ref, entityStore); + boolean canApplyItemStackPenalties = ref != null && ItemUtils.canApplyItemStackPenalties(ref, entityStore); if (specPower != 0.0F && heldItem != null && heldItem.getTool() != null && itemStack.isBroken() && canApplyItemStackPenalties) { BrokenPenalties brokenPenalties = gameplayConfig.getItemDurabilityConfig().getBrokenPenalties(); specPower *= 1.0F - (float)brokenPenalties.getTool(0.0); @@ -271,7 +273,7 @@ public class BlockHarvestUtils { if ((setBlockSettings & 4) == 0) { String particleSystemId = unbreakableBlockConfig.getParticleSystemId(); if (particleSystemId != null) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(targetBlockCenterPos, 75.0, results); ParticleUtil.spawnParticleEffect(particleSystemId, targetBlockCenterPos, results, entityStore); } @@ -346,7 +348,7 @@ public class BlockHarvestUtils { targetSection = blockChunkComponent.getSectionAtBlockY(targetBlockPos.y); targetRotationIndex = targetSection.getRotationIndex(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); targetBlockType.getBlockCenter(targetRotationIndex, targetBlockCenterPos); - targetBlockCenterPos.add(targetBlockPos); + targetBlockCenterPos.add(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); BlockHealth blockDamage = blockHealthComponent.damageBlock(timeResource.getNow(), world, targetBlockPos, damage); if (blockHealthComponent.isBlockFragile(targetBlockPos) || blockDamage.isDestroyed()) { BlockGathering.BlockToolData requiredTool = blockGathering.getToolData().get(toolId); @@ -387,7 +389,9 @@ public class BlockHarvestUtils { if ((setBlockSettings & 2048) == 0) { List itemStacks = getDrops(targetBlockType, 1, requiredTool.getItemId(), requiredTool.getDropListId()); Vector3d dropPosition = new Vector3d(targetBlockPos.x + 0.5, targetBlockPos.y, targetBlockPos.z + 0.5); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops( + entityStore, itemStacks, dropPosition, Rotation3f.IDENTITY + ); entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); } } else { @@ -411,7 +415,7 @@ public class BlockHarvestUtils { if (!toolDrops.isEmpty()) { Vector3d dropPosition = new Vector3d(targetBlockPos.x + 0.5, targetBlockPos.y, targetBlockPos.z + 0.5); Holder[] itemEntityHolders = ItemComponent.generateItemDrops( - entityStore, toolDrops, dropPosition, Vector3f.ZERO + entityStore, toolDrops, dropPosition, Rotation3f.IDENTITY ); entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); } @@ -429,7 +433,7 @@ public class BlockHarvestUtils { } if (ref != null && entity != null) { - if ((setBlockSettings & 1024) == 0 && !targetBlockCenterPos.equals(Vector3d.MAX)) { + if ((setBlockSettings & 1024) == 0 && !targetBlockCenterPos.equals(Vector3dUtil.MAX)) { int hitSoundEventLayerIndex = 0; if (itemToolSpec != null) { hitSoundEventLayerIndex = itemToolSpec.getHitSoundLayerIndex(); @@ -441,12 +445,7 @@ public class BlockHarvestUtils { if (hitSoundEventLayerIndex != 0) { SoundUtil.playSoundEvent3d( - ref, - hitSoundEventLayerIndex, - targetBlockCenterPos.getX(), - targetBlockCenterPos.getY(), - targetBlockCenterPos.getZ(), - entityStore + ref, hitSoundEventLayerIndex, targetBlockCenterPos.x(), targetBlockCenterPos.y(), targetBlockCenterPos.z(), entityStore ); } } @@ -457,7 +456,7 @@ public class BlockHarvestUtils { if ((setBlockSettings & 4) == 0) { String particleSystemId = incorrectToolConfig.getParticleSystemId(); if (particleSystemId != null) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(targetBlockCenterPos, 75.0, results); ParticleUtil.spawnParticleEffect(particleSystemId, targetBlockCenterPos, results, entityStore); } @@ -475,14 +474,15 @@ public class BlockHarvestUtils { if (entity != null && ref != null - && entity.canDecreaseItemStackDurability(ref, entityStore) + && ItemUtils.canDecreaseItemStackDurability(ref, entityStore) && itemStack != null && !itemStack.isUnbreakable()) { - byte activeHotbarSlot = entity.getInventory().getActiveHotbarSlot(); - if (activeHotbarSlot != -1) { + InventoryComponent.Hotbar hotbarComponent = entityStore.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent != null && hotbarComponent.getActiveSlot() != -1) { double durability = calculateDurabilityUse(heldItem, targetBlockType); - ItemContainer hotbar = entity.getInventory().getHotbar(); - entity.updateItemStackDurability(ref, itemStack, hotbar, activeHotbarSlot, -durability, entityStore); + entity.updateItemStackDurability( + ref, itemStack, hotbarComponent.getInventory(), hotbarComponent.getActiveSlot(), -durability, entityStore + ); } } @@ -514,41 +514,43 @@ public class BlockHarvestUtils { @Nonnull ComponentAccessor entityStore, @Nonnull ComponentAccessor chunkStore ) { - World world = chunkStore.getExternalData().getWorld(); - int targetBlockX = targetBlock.getX(); - int targetBlockY = targetBlock.getY(); - int targetBlockZ = targetBlock.getZ(); + int targetBlockX = targetBlock.x(); + int targetBlockY = targetBlock.y(); + int targetBlockZ = targetBlock.z(); WorldChunk worldChunkComponent = chunkStore.getComponent(chunkReference, WorldChunk.getComponentType()); assert worldChunkComponent != null; - int targetBlockTypeIndex = worldChunkComponent.getBlock(targetBlockX, targetBlockY, targetBlockZ); - BlockType targetBlockTypeAsset = BlockType.getAssetMap().getAsset(targetBlockTypeIndex); - if (targetBlockTypeAsset != null) { - Vector3i affectedBlock = targetBlock; - if (!targetBlockTypeAsset.isUnknown()) { - BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + int targetBlockTypeId = worldChunkComponent.getBlock(targetBlockX, targetBlockY, targetBlockZ); + if (targetBlockTypeId != 0) { + BlockType targetBlockTypeAsset = BlockType.getAssetMap().getAsset(targetBlockTypeId); + if (targetBlockTypeAsset != null) { + World world = chunkStore.getExternalData().getWorld(); + Vector3i affectedBlock = targetBlock; + if (!targetBlockTypeAsset.isUnknown()) { + BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); - assert blockChunkComponent != null; + assert blockChunkComponent != null; - BlockSection targetBlockSection = blockChunkComponent.getSectionAtBlockY(targetBlockY); - int filler = targetBlockSection.getFiller(targetBlockX, targetBlockY, targetBlockZ); - int fillerX = FillerBlockUtil.unpackX(filler); - int fillerY = FillerBlockUtil.unpackY(filler); - int fillerZ = FillerBlockUtil.unpackZ(filler); - if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { - affectedBlock = targetBlock.clone().subtract(fillerX, fillerY, fillerZ); - BlockType originBlock = world.getBlockType(affectedBlock); - if (originBlock != null && !targetBlockTypeAsset.getId().equals(originBlock.getId())) { - world.breakBlock(targetBlockX, targetBlockY, targetBlockZ, setBlockSettings); - return; + BlockSection targetBlockSection = blockChunkComponent.getSectionAtBlockY(targetBlockY); + int filler = targetBlockSection.getFiller(targetBlockX, targetBlockY, targetBlockZ); + int fillerX = FillerBlockUtil.unpackX(filler); + int fillerY = FillerBlockUtil.unpackY(filler); + int fillerZ = FillerBlockUtil.unpackZ(filler); + if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { + affectedBlock = new Vector3i(targetBlock).sub(fillerX, fillerY, fillerZ); + BlockType originBlock = world.getBlockType(affectedBlock); + if (originBlock != null && !targetBlockTypeAsset.getId().equals(originBlock.getId())) { + world.breakBlock(targetBlockX, targetBlockY, targetBlockZ, setBlockSettings); + return; + } } } - } - performBlockBreak( - world, affectedBlock, targetBlockTypeAsset, heldItemStack, 0, null, null, setBlockSettings, ref, chunkReference, entityStore, chunkStore - ); + performBlockBreak( + world, affectedBlock, targetBlockTypeAsset, heldItemStack, 0, null, null, setBlockSettings, ref, chunkReference, entityStore, chunkStore + ); + } } } @@ -566,69 +568,71 @@ public class BlockHarvestUtils { @Nonnull ComponentAccessor entityStore, @Nonnull ComponentAccessor chunkStore ) { - Vector3i targetBlockPosition = blockPosition; - Ref targetChunkReference = chunkReference; - ComponentAccessor targetChunkStore = chunkStore; - if (ref != null) { - BreakBlockEvent event = new BreakBlockEvent(heldItemStack, blockPosition, targetBlockTypeKey); - entityStore.invoke(ref, event); - if (event.isCancelled()) { + if (targetBlockTypeKey != BlockType.EMPTY) { + Vector3i targetBlockPosition = blockPosition; + Ref targetChunkReference = chunkReference; + ComponentAccessor targetChunkStore = chunkStore; + if (ref != null) { + BreakBlockEvent event = new BreakBlockEvent(heldItemStack, blockPosition, targetBlockTypeKey); + entityStore.invoke(ref, event); + if (event.isCancelled()) { + BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.y()); + blockSection.invalidateBlock(blockPosition.x(), blockPosition.y(), blockPosition.z()); + return; + } + + targetBlockPosition = event.getTargetBlock(); + targetChunkStore = world.getChunkStore().getStore(); + long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlockPosition.x, targetBlockPosition.z); + targetChunkReference = targetChunkStore.getExternalData().getChunkReference(chunkIndex); + if (targetChunkReference == null || !targetChunkReference.isValid()) { + return; + } + } + + if (!targetBlockPosition.equals(blockPosition) || !world.equals(world)) { BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); assert blockChunkComponent != null; - BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.getY()); - blockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); - return; + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.y()); + blockSection.invalidateBlock(blockPosition.x(), blockPosition.y(), blockPosition.z()); } - targetBlockPosition = event.getTargetBlock(); - targetChunkStore = world.getChunkStore().getStore(); - long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlockPosition.x, targetBlockPosition.z); - targetChunkReference = targetChunkStore.getExternalData().getChunkReference(chunkIndex); - if (targetChunkReference == null || !targetChunkReference.isValid()) { - return; - } - } - - if (!targetBlockPosition.equals(blockPosition) || !world.equals(world)) { - BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType()); + int x = blockPosition.x(); + int y = blockPosition.y(); + int z = blockPosition.z(); + BlockChunk blockChunkComponent = chunkStore.getComponent(targetChunkReference, BlockChunk.getComponentType()); assert blockChunkComponent != null; - BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockPosition.getY()); - blockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(y); + int filler = blockSection.getFiller(x, y, z); + int blockTypeIndex = blockSection.get(x, y, z); + BlockType blockTypeAsset = BlockType.getAssetMap().getAsset(blockTypeIndex); + boolean isNaturalBlockBreak = BlockInteractionUtils.isNaturalAction(ref, entityStore); + setBlockSettings |= 256; + if (!isNaturalBlockBreak) { + setBlockSettings |= 2048; + } + + naturallyRemoveBlock( + targetBlockPosition, + blockTypeAsset, + filler, + dropQuantity, + dropItemId, + dropListId, + setBlockSettings, + targetChunkReference, + entityStore, + targetChunkStore + ); } - - int x = blockPosition.getX(); - int y = blockPosition.getY(); - int z = blockPosition.getZ(); - BlockChunk blockChunkComponent = chunkStore.getComponent(targetChunkReference, BlockChunk.getComponentType()); - - assert blockChunkComponent != null; - - BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(y); - int filler = blockSection.getFiller(x, y, z); - int blockTypeIndex = blockSection.get(x, y, z); - BlockType blockTypeAsset = BlockType.getAssetMap().getAsset(blockTypeIndex); - boolean isNaturalBlockBreak = BlockInteractionUtils.isNaturalAction(ref, entityStore); - setBlockSettings |= 256; - if (!isNaturalBlockBreak) { - setBlockSettings |= 2048; - } - - naturallyRemoveBlock( - targetBlockPosition, - blockTypeAsset, - filler, - dropQuantity, - dropItemId, - dropListId, - setBlockSettings, - targetChunkReference, - entityStore, - targetChunkStore - ); } @Deprecated @@ -697,15 +701,15 @@ public class BlockHarvestUtils { int fillerY = FillerBlockUtil.unpackY(filler); int fillerZ = FillerBlockUtil.unpackZ(filler); if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { - affectedBlock = blockPosition.clone().subtract(fillerX, fillerY, fillerZ); + affectedBlock = new Vector3i(blockPosition).sub(fillerX, fillerY, fillerZ); String oldBlockTypeKey = blockType.getId(); - blockType = worldChunkComponent.getBlockType(affectedBlock.getX(), affectedBlock.getY(), affectedBlock.getZ()); + blockType = worldChunkComponent.getBlockType(affectedBlock.x(), affectedBlock.y(), affectedBlock.z()); if (blockType == null) { throw new IllegalStateException("Null block type fetched for " + affectedBlock + " during block break"); } if (!oldBlockTypeKey.equals(blockType.getId())) { - worldChunkComponent.breakBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), setBlockSettings); + worldChunkComponent.breakBlock(blockPosition.x(), blockPosition.y(), blockPosition.z(), setBlockSettings); return; } } @@ -716,11 +720,11 @@ public class BlockHarvestUtils { if (soundSet != null) { int soundEventIndex = soundSet.getSoundEventIndices().getOrDefault(BlockSoundEvent.Break, 0); if (soundEventIndex != 0) { - BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.getY()); - int rotationIndex = section.getRotationIndex(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.y()); + int rotationIndex = section.getRotationIndex(blockPosition.x(), blockPosition.y(), blockPosition.z()); Vector3d centerPosition = new Vector3d(); blockType.getBlockCenter(rotationIndex, centerPosition); - centerPosition.add(blockPosition); + centerPosition.add(blockPosition.x(), blockPosition.y(), blockPosition.z()); SoundUtil.playSoundEvent3d(soundEventIndex, SoundCategory.SFX, centerPosition, entityStore); } } @@ -728,9 +732,9 @@ public class BlockHarvestUtils { removeBlock(affectedBlock, blockType, setBlockSettings, chunkReference, chunkStore); if ((setBlockSettings & 2048) == 0 && quantity > 0) { - Vector3d dropPosition = blockPosition.toVector3d().add(0.5, 0.0, 0.5); + Vector3d dropPosition = Vector3iUtil.toVector3d(blockPosition).add(0.5, 0.0, 0.5); List itemStacks = getDrops(blockType, quantity, itemId, dropListId); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Vector3f.ZERO); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops(entityStore, itemStacks, dropPosition, Rotation3f.IDENTITY); entityStore.addEntities(itemEntityHolders, AddReason.SPAWN); } } @@ -763,25 +767,25 @@ public class BlockHarvestUtils { int fillerY = FillerBlockUtil.unpackY(filler); int fillerZ = FillerBlockUtil.unpackZ(filler); if (fillerX != 0 || fillerY != 0 || fillerZ != 0) { - affectedBlock = targetBlock.clone().subtract(fillerX, fillerY, fillerZ); + affectedBlock = new Vector3i(targetBlock).sub(fillerX, fillerY, fillerZ); String oldBlockTypeKey = blockType.getId(); - blockType = worldChunkComponent.getBlockType(affectedBlock.getX(), affectedBlock.getY(), affectedBlock.getZ()); + blockType = worldChunkComponent.getBlockType(affectedBlock.x(), affectedBlock.y(), affectedBlock.z()); if (blockType == null) { return; } if (!oldBlockTypeKey.equals(blockType.getId())) { - worldChunkComponent.breakBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()); + worldChunkComponent.breakBlock(targetBlock.x(), targetBlock.y(), targetBlock.z()); return; } } } - BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.getY()); + BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.y()); Vector3d centerPosition = new Vector3d(); - int rotationIndex = section.getRotationIndex(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ()); + int rotationIndex = section.getRotationIndex(targetBlock.x(), targetBlock.y(), targetBlock.z()); blockType.getBlockCenter(rotationIndex, centerPosition); - centerPosition.add(targetBlock); + centerPosition.add(targetBlock.x(), targetBlock.y(), targetBlock.z()); int setBlockSettings = 0; setBlockSettings |= 256; if (!BlockInteractionUtils.isNaturalAction(ref, entityStore)) { @@ -830,21 +834,20 @@ public class BlockHarvestUtils { assert blockChunkComponent != null; - worldChunkComponent.breakBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), setBlockSettings); + worldChunkComponent.breakBlock(blockPosition.x(), blockPosition.y(), blockPosition.z(), setBlockSettings); if ((setBlockSettings & 256) != 0) { BlockSection section = blockChunkComponent.getSectionAtBlockY(blockPosition.y); int rotationIndex = section.getRotationIndex(blockPosition.x, blockPosition.y, blockPosition.z); BlockBoundingBoxes hitBoxType = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); if (hitBoxType != null) { FillerBlockUtil.forEachFillerBlock( - hitBoxType.get(rotationIndex), - (x, y, z) -> world.performBlockUpdate(blockPosition.getX() + x, blockPosition.getY() + y, blockPosition.getZ() + z, false) + hitBoxType.get(rotationIndex), (x, y, z) -> world.performBlockUpdate(blockPosition.x() + x, blockPosition.y() + y, blockPosition.z() + z, false) ); } } ConnectedBlocksUtil.setConnectedBlockAndNotifyNeighbors( - BlockType.getAssetMap().getIndex("Empty"), RotationTuple.NONE, Vector3i.ZERO, blockPosition, worldChunkComponent, blockChunkComponent + BlockType.getAssetMap().getIndex("Empty"), RotationTuple.NONE, Vector3iUtil.ZERO, blockPosition, worldChunkComponent, blockChunkComponent ); } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/BlockPlaceUtils.java b/src/com/hypixel/hytale/server/core/modules/interaction/BlockPlaceUtils.java index cc292b82..8f5701e2 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/BlockPlaceUtils.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/BlockPlaceUtils.java @@ -1,11 +1,12 @@ package com.hypixel.hytale.server.core.modules.interaction; +import com.hypixel.hytale.codec.EmptyExtraInfo; import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.BlockRotation; import com.hypixel.hytale.protocol.GameMode; @@ -40,8 +41,6 @@ import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.connectedblocks.ConnectedBlocksUtil; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.PlacedByBlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; @@ -55,6 +54,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bson.BsonDocument; import org.bson.BsonValue; +import org.joml.Vector3i; public class BlockPlaceUtils { @Nonnull @@ -87,9 +87,10 @@ public class BlockPlaceUtils { boolean removeItemInHand, @Nonnull Ref chunkReference, @Nonnull ComponentAccessor chunkStore, - @Nonnull ComponentAccessor entityStore + @Nonnull ComponentAccessor entityStore, + boolean quickReplace ) { - if (blockPosition.getY() >= 0 && blockPosition.getY() < 320) { + if (blockPosition.y() >= 0 && blockPosition.y() < 320) { Ref targetChunkReference = chunkReference; RotationTuple targetRotation = RotationTuple.of( Rotation.valueOf(blockRotation.rotationYaw), Rotation.valueOf(blockRotation.rotationPitch), Rotation.valueOf(blockRotation.rotationRoll) @@ -98,11 +99,11 @@ public class BlockPlaceUtils { assert targetBlockChunkComponent != null; - BlockSection targetBlockSection = targetBlockChunkComponent.getSectionAtBlockY(blockPosition.getY()); + BlockSection targetBlockSection = targetBlockChunkComponent.getSectionAtBlockY(blockPosition.y()); PlaceBlockEvent event = new PlaceBlockEvent(itemStack, blockPosition, targetRotation); entityStore.invoke(ref, event); if (event.isCancelled()) { - targetBlockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + targetBlockSection.invalidateBlock(blockPosition.x(), blockPosition.y(), blockPosition.z()); } else { Vector3i targetBlockPosition = event.getTargetBlock(); targetRotation = event.getRotation(); @@ -116,7 +117,7 @@ public class BlockPlaceUtils { } if (positionIsDifferent) { - targetBlockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + targetBlockSection.invalidateBlock(blockPosition.x(), blockPosition.y(), blockPosition.z()); } if (!targetChunkReference.equals(chunkReference) || targetBlockPosition.y != blockPosition.y) { @@ -124,7 +125,7 @@ public class BlockPlaceUtils { assert targetBlockChunkComponent != null; - targetBlockSection = targetBlockChunkComponent.getSectionAtBlockY(targetBlockPosition.getY()); + targetBlockSection = targetBlockChunkComponent.getSectionAtBlockY(targetBlockPosition.y()); } PlayerRef playerRefComponent = entityStore.getComponent(ref, PlayerRef.getComponentType()); @@ -170,12 +171,13 @@ public class BlockPlaceUtils { targetBlockChunkComponent, chunkReference, chunkStore, - entityStore + entityStore, + quickReplace ); if (success) { - onPlaceBlockSuccess(itemStack, worldChunkComponent, targetBlockPosition); + onPlaceBlockSuccess(itemStack, worldChunkComponent, targetBlockPosition, blockTypeAsset, targetRotation); } else { - onPlaceBlockFailure(itemStack, inventory, activeSlot, playerComponent, targetBlockSection, targetBlockPosition); + onPlaceBlockFailure(itemStack, inventory, activeSlot, playerComponent, playerRefComponent, targetBlockSection, targetBlockPosition); } } } @@ -188,6 +190,7 @@ public class BlockPlaceUtils { @Nullable Inventory inventory, byte activeSlot, @Nullable Player playerComponent, + @Nullable PlayerRef playerRefComponent, @Nonnull BlockSection blockSection, @Nonnull Vector3i blockPosition ) { @@ -197,33 +200,39 @@ public class BlockPlaceUtils { ItemStackSlotTransaction transaction = hotbar.addItemStackToSlot(activeSlot, itemStack); if (!transaction.succeeded()) { ItemStackTransaction itemStackTransaction = hotbar.addItemStack(itemStack); - if (!itemStackTransaction.succeeded() && playerComponent != null) { - playerComponent.sendMessage(MESSAGE_MODULES_INTERACTION_FAILED_ADD_BACK_AFTER_FAILED_PLACE); + if (!itemStackTransaction.succeeded() && playerRefComponent != null) { + playerRefComponent.sendMessage(MESSAGE_MODULES_INTERACTION_FAILED_ADD_BACK_AFTER_FAILED_PLACE); } return; } } - blockSection.invalidateBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + blockSection.invalidateBlock(blockPosition.x(), blockPosition.y(), blockPosition.z()); } - private static void onPlaceBlockSuccess(@Nullable ItemStack itemStack, @Nonnull WorldChunk worldChunkComponent, @Nonnull Vector3i blockPosition) { + private static void onPlaceBlockSuccess( + @Nullable ItemStack itemStack, + @Nonnull WorldChunk worldChunkComponent, + @Nonnull Vector3i blockPosition, + BlockType blockTypeAsset, + RotationTuple targetRotation + ) { if (itemStack != null) { BsonDocument metadata = itemStack.getMetadata(); if (metadata != null) { - BsonValue bsonValue = metadata.get("BlockState"); + BsonValue bsonValue = metadata.get("BlockHolder"); if (bsonValue != null) { try { BsonDocument document = bsonValue.asDocument(); - BlockState blockState = BlockState.load(document, worldChunkComponent, blockPosition.clone()); - if (blockState != null) { - worldChunkComponent.setState(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockState); + Holder blockEntity = ChunkStore.REGISTRY.getEntityCodec().decode(document, EmptyExtraInfo.EMPTY); + if (blockEntity != null) { + worldChunkComponent.setState(blockPosition.x(), blockPosition.y(), blockPosition.z(), blockTypeAsset, targetRotation.index(), blockEntity); } else { - LOGGER.at(Level.WARNING).log("Failed to set BlockState from item metadata: %s, %s", itemStack.getItemId(), document); + LOGGER.at(Level.WARNING).log("Failed to set Block Entity from item metadata: %s, %s", itemStack.getItemId(), document); } - } catch (Exception var7) { - throw SneakyThrow.sneakyThrow(var7); + } catch (Exception var9) { + throw SneakyThrow.sneakyThrow(var9); } } } @@ -288,7 +297,6 @@ public class BlockPlaceUtils { Store store = world.getEntityStore().getStore(); PrefabBuffer.PrefabBufferAccessor prefabBufferAccessor = prefabBuffer.newAccess(); PrefabUtil.paste(prefabBufferAccessor, world, blockPosition, Rotation.None, true, new Random(), store); - prefabBufferAccessor.release(); }); return true; } @@ -305,7 +313,8 @@ public class BlockPlaceUtils { @Nonnull BlockChunk blockChunkComponent, @Nonnull Ref chunkReference, @Nonnull ComponentAccessor chunkStore, - @Nonnull ComponentAccessor entityStore + @Nonnull ComponentAccessor entityStore, + boolean quickReplace ) { WorldConfig worldConfig = entityStore.getExternalData().getWorld().getGameplayConfig().getWorldConfig(); if (!worldConfig.isBlockPlacementAllowed()) { @@ -328,14 +337,14 @@ public class BlockPlaceUtils { BlockType blockType = BlockType.getAssetMap().getAsset(blockTypeKey); int rotationIndex = rotation.index(); - if (blockType != null - && worldChunkComponent.testPlaceBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockType, rotationIndex)) { + if (quickReplace + || blockType != null && worldChunkComponent.testPlaceBlock(blockPosition.x(), blockPosition.y(), blockPosition.z(), blockType, rotationIndex)) { BlockBoundingBoxes hitBoxType = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); if (hitBoxType != null) { FillerBlockUtil.forEachFillerBlock( hitBoxType.get(rotationIndex), (x1, y1, z1) -> breakAndDropReplacedBlock( - blockPosition.clone().add(x1, y1, z1), worldChunkComponent, chunkReference, ref, chunkStore, entityStore + new Vector3i(blockPosition).add(x1, y1, z1), worldChunkComponent, chunkReference, ref, chunkStore, entityStore ) ); } else { @@ -343,7 +352,7 @@ public class BlockPlaceUtils { } int placeBlockSettings = 10; - if (!worldChunkComponent.placeBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockTypeKey, rotation, 10, false)) { + if (!worldChunkComponent.placeBlock(blockPosition.x(), blockPosition.y(), blockPosition.z(), blockTypeKey, rotation, 10, false)) { return false; } else { if (playerComponent != null && !playerComponent.isOverrideBlockPlacementRestrictions() && blockType.canBePlacedAsDeco()) { @@ -357,11 +366,6 @@ public class BlockPlaceUtils { } } - BlockState blockState = worldChunkComponent.getState(blockPosition.x, blockPosition.y, blockPosition.z); - if (blockState instanceof PlacedByBlockState placedByBlockState) { - placedByBlockState.placedBy(ref, blockTypeKey, blockState, entityStore); - } - int blockIndexInChunk = ChunkUtil.indexBlockInColumn(blockPosition.x, blockPosition.y, blockPosition.z); BlockComponentChunk blockComponentChunk = worldChunkComponent.getBlockComponentChunk(); Ref blockRef = blockComponentChunk == null ? null : blockComponentChunk.getEntityReference(blockIndexInChunk); @@ -393,39 +397,42 @@ public class BlockPlaceUtils { @Nonnull ComponentAccessor chunkStore, @Nonnull ComponentAccessor entityStore ) { - BlockType existingBlock = worldChunkComponent.getBlockType(blockPosition); - if (existingBlock != null) { - if (existingBlock.getMaterial() != BlockMaterial.Empty) { - return; - } - - BlockGathering gathering = existingBlock.getGathering(); - int dropQuantity = 1; - String itemId = null; - String dropListId = null; - if (gathering != null) { - SoftBlockDropType softGathering = gathering.getSoft(); - if (softGathering != null) { - itemId = softGathering.getItemId(); - dropListId = softGathering.getDropListId(); + int targetBlockId = worldChunkComponent.getBlock(blockPosition); + if (targetBlockId != 0) { + BlockType targetBlockType = BlockType.getAssetMap().getAsset(targetBlockId); + if (targetBlockType != null) { + if (targetBlockType.getMaterial() != BlockMaterial.Empty) { + return; } - } - int setBlockSettings = 288; - BlockHarvestUtils.performBlockBreak( - chunkStore.getExternalData().getWorld(), - blockPosition, - existingBlock, - null, - dropQuantity, - itemId, - dropListId, - 288, - ref, - chunkReference, - entityStore, - chunkStore - ); + BlockGathering gathering = targetBlockType.getGathering(); + int dropQuantity = 1; + String itemId = null; + String dropListId = null; + if (gathering != null) { + SoftBlockDropType softGathering = gathering.getSoft(); + if (softGathering != null) { + itemId = softGathering.getItemId(); + dropListId = softGathering.getDropListId(); + } + } + + int setBlockSettings = 288; + BlockHarvestUtils.performBlockBreak( + chunkStore.getExternalData().getWorld(), + blockPosition, + targetBlockType, + null, + dropQuantity, + itemId, + dropListId, + 288, + ref, + chunkReference, + entityStore, + chunkStore + ); + } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/InteractionModule.java b/src/com/hypixel/hytale/server/core/modules/interaction/InteractionModule.java index 4775a2f3..575e2d5e 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/InteractionModule.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/InteractionModule.java @@ -15,8 +15,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.event.IEventDispatcher; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.MouseButtonType; @@ -41,7 +39,7 @@ import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.CameraManager; import com.hypixel.hytale.server.core.event.events.player.PlayerMouseButtonEvent; import com.hypixel.hytale.server.core.event.events.player.PlayerMouseMotionEvent; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.hitboxcollision.HitboxCollisionConfig; @@ -98,6 +96,7 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.config.non import com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.StatsConditionInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.StatsConditionWithModifierInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.simple.ApplyEffectInteraction; +import com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.simple.CommandInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.simple.RemoveEntityInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.simple.SendMessageInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.selector.AOECircleSelector; @@ -113,7 +112,6 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.config.ser import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.CheckUniqueItemUsageInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.ClearEntityEffectInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.DamageEntityInteraction; -import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.DestroyConditionInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.DoorInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.EquipItemInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.IncreaseBackpackCapacityInteraction; @@ -145,6 +143,8 @@ import java.util.EnumSet; import java.util.List; import java.util.Map.Entry; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector3i; public class InteractionModule extends JavaPlugin { @Nonnull @@ -267,6 +267,7 @@ public class InteractionModule extends JavaPlugin { Interaction.CODEC.register("StatsConditionWithModifier", StatsConditionWithModifierInteraction.class, StatsConditionWithModifierInteraction.CODEC); Interaction.CODEC.register("SpawnPrefab", SpawnPrefabInteraction.class, SpawnPrefabInteraction.CODEC); Interaction.CODEC.register("SendMessage", SendMessageInteraction.class, SendMessageInteraction.CODEC); + Interaction.CODEC.register("Command", CommandInteraction.class, CommandInteraction.CODEC); Interaction.CODEC.register("EquipItem", EquipItemInteraction.class, EquipItemInteraction.CODEC); Interaction.CODEC.register("RefillContainer", RefillContainerInteraction.class, RefillContainerInteraction.CODEC); Interaction.CODEC.register("Door", DoorInteraction.class, DoorInteraction.CODEC); @@ -275,7 +276,6 @@ public class InteractionModule extends JavaPlugin { Interaction.CODEC.register("LaunchPad", LaunchPadInteraction.class, LaunchPadInteraction.CODEC); Interaction.CODEC.register("OpenContainer", OpenContainerInteraction.class, OpenContainerInteraction.CODEC); Interaction.CODEC.register("OpenItemStackContainer", OpenItemStackContainerInteraction.class, OpenItemStackContainerInteraction.CODEC); - Interaction.CODEC.register("DestroyCondition", DestroyConditionInteraction.class, DestroyConditionInteraction.CODEC); Interaction.CODEC.register("OpenCustomUI", OpenCustomUIInteraction.class, OpenCustomUIInteraction.CODEC); Interaction.CODEC.register("OpenPage", OpenPageInteraction.class, OpenPageInteraction.CODEC); Interaction.CODEC.register("ApplyEffect", ApplyEffectInteraction.class, ApplyEffectInteraction.CODEC); @@ -356,80 +356,85 @@ public class InteractionModule extends JavaPlugin { @Nonnull PlayerRef playerRefComponent ) { if (!this.isDisabled()) { - byte activeHotbarSlot = playerComponent.getInventory().getActiveHotbarSlot(); - if (activeHotbarSlot != packet.activeSlot) { - playerComponent.sendMessage( - Message.translation("server.modules.interaction.failedGetActiveSlot").param("server", (int)activeHotbarSlot).param("packet", packet.activeSlot) - ); - } else { - MouseButtonType mouseButtonType = packet.mouseButton != null ? packet.mouseButton.mouseButtonType : MouseButtonType.Left; - Inventory inventory = playerComponent.getInventory(); - ItemStack itemInHand = inventory.getItemInHand(); - ItemStack itemInOffHand = inventory.getUtilityItem(); - Item primaryItem = itemInHand != null && !itemInHand.isEmpty() ? itemInHand.getItem() : null; - Item secondaryItem = itemInOffHand != null && !itemInOffHand.isEmpty() ? itemInOffHand.getItem() : null; - Item item; - if (mouseButtonType == MouseButtonType.Left) { - item = primaryItem; - } else if (mouseButtonType == MouseButtonType.Right && secondaryItem != null) { - item = secondaryItem; + InventoryComponent.Hotbar hotbarComponent = componentAccessor.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbarComponent != null) { + byte activeHotbarSlot = hotbarComponent.getActiveSlot(); + if (activeHotbarSlot != packet.activeSlot) { + playerRefComponent.sendMessage( + Message.translation("server.modules.interaction.failedGetActiveSlot") + .param("server", (int)activeHotbarSlot) + .param("packet", packet.activeSlot) + ); } else { - item = primaryItem; - } - - WorldInteraction worldInteraction_ = packet.worldInteraction; - BlockPosition blockPositionPacket = worldInteraction_.blockPosition; - if (ref.isValid()) { - EntityStore entityComponentStore = componentAccessor.getExternalData(); - Vector3i targetBlock = blockPositionPacket == null ? null : new Vector3i(blockPositionPacket.x, blockPositionPacket.y, blockPositionPacket.z); - Entity targetEntity; - if (worldInteraction_.entityId < 0) { - targetEntity = null; + MouseButtonType mouseButtonType = packet.mouseButton != null ? packet.mouseButton.mouseButtonType : MouseButtonType.Left; + ItemStack itemInHand = InventoryComponent.getItemInHand(componentAccessor, ref); + InventoryComponent.Utility utilityComponent = componentAccessor.getComponent(ref, InventoryComponent.Utility.getComponentType()); + ItemStack itemInOffHand = utilityComponent != null ? utilityComponent.getActiveItem() : null; + Item primaryItem = itemInHand != null && !itemInHand.isEmpty() ? itemInHand.getItem() : null; + Item secondaryItem = itemInOffHand != null && !itemInOffHand.isEmpty() ? itemInOffHand.getItem() : null; + Item item; + if (mouseButtonType == MouseButtonType.Left) { + item = primaryItem; + } else if (mouseButtonType == MouseButtonType.Right && secondaryItem != null) { + item = secondaryItem; } else { - Ref entityReference = entityComponentStore.getRefFromNetworkId(worldInteraction_.entityId); - targetEntity = EntityUtils.getEntity(entityReference, componentAccessor); + item = primaryItem; } - CameraManager cameraManagerComponent = componentAccessor.getComponent(ref, CameraManager.getComponentType()); - - assert cameraManagerComponent != null; - - if (packet.mouseButton != null) { - IEventDispatcher dispatcher = HytaleServer.get() - .getEventBus() - .dispatchFor(PlayerMouseButtonEvent.class); - if (dispatcher.hasListener()) { - dispatcher.dispatch( - new PlayerMouseButtonEvent( - ref, - playerComponent, - playerRefComponent, - packet.clientTimestamp, - item, - targetBlock, - targetEntity, - packet.screenPoint, - packet.mouseButton - ) - ); + WorldInteraction worldInteraction_ = packet.worldInteraction; + BlockPosition blockPositionPacket = worldInteraction_.blockPosition; + if (ref.isValid()) { + EntityStore entityComponentStore = componentAccessor.getExternalData(); + Vector3i targetBlock = blockPositionPacket == null ? null : new Vector3i(blockPositionPacket.x, blockPositionPacket.y, blockPositionPacket.z); + Entity targetEntity; + if (worldInteraction_.entityId < 0) { + targetEntity = null; + } else { + Ref entityReference = entityComponentStore.getRefFromNetworkId(worldInteraction_.entityId); + targetEntity = EntityUtils.getEntity(entityReference, componentAccessor); } - cameraManagerComponent.handleMouseButtonState(packet.mouseButton.mouseButtonType, packet.mouseButton.state, targetBlock); - } else { - IEventDispatcher dispatcher = HytaleServer.get() - .getEventBus() - .dispatchFor(PlayerMouseMotionEvent.class); - if (dispatcher.hasListener()) { - dispatcher.dispatch( - new PlayerMouseMotionEvent( - ref, playerComponent, packet.clientTimestamp, item, targetBlock, targetEntity, packet.screenPoint, packet.mouseMotion - ) - ); + CameraManager cameraManagerComponent = componentAccessor.getComponent(ref, CameraManager.getComponentType()); + + assert cameraManagerComponent != null; + + if (packet.mouseButton != null) { + IEventDispatcher dispatcher = HytaleServer.get() + .getEventBus() + .dispatchFor(PlayerMouseButtonEvent.class); + if (dispatcher.hasListener()) { + dispatcher.dispatch( + new PlayerMouseButtonEvent( + ref, + playerComponent, + playerRefComponent, + packet.clientTimestamp, + item, + targetBlock, + targetEntity, + packet.screenPoint, + packet.mouseButton + ) + ); + } + + cameraManagerComponent.handleMouseButtonState(packet.mouseButton.mouseButtonType, packet.mouseButton.state, targetBlock); + } else { + IEventDispatcher dispatcher = HytaleServer.get() + .getEventBus() + .dispatchFor(PlayerMouseMotionEvent.class); + if (dispatcher.hasListener()) { + dispatcher.dispatch( + new PlayerMouseMotionEvent( + ref, playerComponent, packet.clientTimestamp, item, targetBlock, targetEntity, packet.screenPoint, packet.mouseMotion + ) + ); + } } + + cameraManagerComponent.setLastScreenPoint(new Vector2d(packet.screenPoint.x(), packet.screenPoint.y())); + cameraManagerComponent.setLastBlockPosition(targetBlock); } - - cameraManagerComponent.setLastScreenPoint(new Vector2d(packet.screenPoint.x, packet.screenPoint.y)); - cameraManagerComponent.setLastBlockPosition(targetBlock); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/Interaction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/Interaction.java index 486d8412..aa98cb57 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/Interaction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/Interaction.java @@ -21,8 +21,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.ForkedChainId; import com.hypixel.hytale.protocol.GameMode; @@ -58,7 +56,6 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.operation. import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.UUIDUtil; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.lang.ref.SoftReference; import java.util.Collections; import java.util.List; @@ -66,6 +63,8 @@ import java.util.Map; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector4d; public abstract class Interaction implements Operation, @@ -447,13 +446,13 @@ public abstract class Interaction Vector3d position = transformComponent.getPosition(); SpatialResource, EntityStore> playerSpatialResource = commandBuffer.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, this.viewDistance, results); Ref owningEntityRef = context.getOwningEntity(); ComponentType playerRefComponentType = PlayerRef.getComponentType(); for (Ref playerRef : results) { - if (!chain.requiresClient() || !playerRef.equals(owningEntityRef)) { + if (playerRef.isValid() && (!chain.requiresClient() || !playerRef.equals(owningEntityRef))) { PlayerRef playerPlayerRefComponent = commandBuffer.getComponent(playerRef, playerRefComponentType); assert playerPlayerRefComponent != null; diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/InteractionCameraSettings.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/InteractionCameraSettings.java index 3eac82a4..2ad15471 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/InteractionCameraSettings.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/InteractionCameraSettings.java @@ -6,12 +6,13 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.validation.LegacyValidator; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.protocol.Direction; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.io.NetworkSerializable; import java.util.Arrays; import javax.annotation.Nonnull; +import org.joml.Vector3f; public class InteractionCameraSettings implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(InteractionCameraSettings.class, InteractionCameraSettings::new) @@ -96,7 +97,7 @@ public class InteractionCameraSettings implements NetworkSerializableappendInherited( - new KeyedCodec<>("Position", ProtocolCodecs.VECTOR3F), (o, i) -> o.position = i, o -> o.position, (o, p) -> o.position = p.position + new KeyedCodec<>("Position", Vector3fUtil.CODEC), (o, i) -> o.position = i, o -> o.position, (o, p) -> o.position = p.position ) .addValidator(Validators.nonNull()) .add() diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/AddItemInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/AddItemInteraction.java index 2fd2e357..18977ebc 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/AddItemInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/AddItemInteraction.java @@ -6,18 +6,18 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class AddItemInteraction extends SimpleBlockInteraction { @Nonnull @@ -49,9 +49,8 @@ public class AddItemInteraction extends SimpleBlockInteraction { ) { if (this.quantity > 0 && this.itemId != null) { Ref ref = context.getEntity(); - if (EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity) { - livingEntity.getInventory().getCombinedHotbarFirst().addItemStack(new ItemStack(this.itemId, this.quantity)); - } + CombinedItemContainer hotbarFirstCombinedContainer = InventoryComponent.getCombined(commandBuffer, ref, InventoryComponent.HOTBAR_FIRST); + hotbarFirstCombinedContainer.addItemStack(new ItemStack(this.itemId, this.quantity)); } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ApplyForceInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ApplyForceInteraction.java index c9413540..98183180 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ApplyForceInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ApplyForceInteraction.java @@ -13,8 +13,8 @@ import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialStructure; import com.hypixel.hytale.math.range.FloatRange; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.AppliedForce; import com.hypixel.hytale.protocol.ApplyForceState; import com.hypixel.hytale.protocol.ChangeVelocityType; @@ -39,10 +39,12 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.operation. import com.hypixel.hytale.server.core.modules.physics.component.Velocity; import com.hypixel.hytale.server.core.modules.splitvelocity.VelocityConfig; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.Arrays; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class ApplyForceInteraction extends SimpleInteraction { @Nonnull @@ -50,7 +52,7 @@ public class ApplyForceInteraction extends SimpleInteraction { ApplyForceInteraction.class, ApplyForceInteraction::new, SimpleInteraction.CODEC ) .documentation("Applies a force to the user, optionally waiting for a condition to met before continuing.") - .appendInherited(new KeyedCodec<>("Direction", Vector3d.CODEC), (o, i) -> o.forces[0].direction = i.normalize(), o -> null, (o, p) -> {}) + .appendInherited(new KeyedCodec<>("Direction", Vector3dUtil.CODEC), (o, i) -> o.forces[0].direction.set(i.normalize()), o -> null, (o, p) -> {}) .documentation("The direction of the force to apply.") .add() .appendInherited(new KeyedCodec<>("AdjustVertical", Codec.BOOLEAN), (o, i) -> o.forces[0].adjustVertical = i, o -> null, (o, p) -> {}) @@ -256,7 +258,7 @@ public class ApplyForceInteraction extends SimpleInteraction { EntityModule.get().getNetworkSendableSpatialResourceType() ); SpatialStructure> spatialStructure = networkSendableSpatialComponent.getSpatialStructure(); - ObjectList> entities = SpatialResource.getThreadLocalReferenceList(); + List> entities = SpatialResource.getThreadLocalReferenceList(); spatialStructure.collect(transformComponent.getPosition(), 1.5, entities); boolean checkGround = time >= this.groundCheckDelay; boolean onGround = checkGround @@ -293,11 +295,11 @@ public class ApplyForceInteraction extends SimpleInteraction { assert velocityComponent != null; - Vector3f entityHeadRotation = headRotationComponent.getRotation(); + Rotation3f entityHeadRotation = headRotationComponent.getRotation(); ChangeVelocityType velocityType = this.changeVelocityType; for (ApplyForceInteraction.Force force : this.forces) { - Vector3d forceDirection = force.direction.clone(); + Vector3d forceDirection = new Vector3d(force.direction); if (force.adjustVertical) { float lookX = entityHeadRotation.x; if (this.verticalClamp != null) { @@ -309,7 +311,7 @@ public class ApplyForceInteraction extends SimpleInteraction { forceDirection.rotateX(lookX); } - forceDirection.scale(force.force); + forceDirection.mul(force.force); forceDirection.rotateY(entityHeadRotation.y); switch (velocityType) { case Add: @@ -398,7 +400,9 @@ public class ApplyForceInteraction extends SimpleInteraction { public static final BuilderCodec CODEC = BuilderCodec.builder( ApplyForceInteraction.Force.class, ApplyForceInteraction.Force::new ) - .appendInherited(new KeyedCodec<>("Direction", Vector3d.CODEC), (o, i) -> o.direction = i, o -> o.direction, (o, p) -> o.direction = p.direction) + .appendInherited( + new KeyedCodec<>("Direction", Vector3dUtil.CODEC), (o, i) -> o.direction.set(i), o -> o.direction, (o, p) -> o.direction.set(p.direction) + ) .documentation("The direction of the force to apply.") .addValidator(Validators.nonNull()) .add() @@ -416,16 +420,14 @@ public class ApplyForceInteraction extends SimpleInteraction { .afterDecode(o -> o.direction.normalize()) .build(); @Nonnull - private Vector3d direction = Vector3d.UP; + private final Vector3d direction = new Vector3d(Vector3dUtil.UP); private boolean adjustVertical = false; private double force = 1.0; @Nonnull public AppliedForce toPacket() { return new AppliedForce( - new com.hypixel.hytale.protocol.Vector3f((float)this.direction.x, (float)this.direction.y, (float)this.direction.z), - this.adjustVertical, - (float)this.force + new Vector3f((float)this.direction.x, (float)this.direction.y, (float)this.direction.z), this.adjustVertical, (float)this.force ); } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BlockConditionInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BlockConditionInteraction.java index e147a636..498144d0 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BlockConditionInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BlockConditionInteraction.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockFace; import com.hypixel.hytale.protocol.Interaction; import com.hypixel.hytale.protocol.InteractionState; @@ -29,6 +28,7 @@ import it.unimi.dsi.fastutil.ints.IntSet; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class BlockConditionInteraction extends SimpleBlockInteraction { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.java index c84c520b..9b539cad 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.java @@ -4,16 +4,32 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.protocol.Interaction; +import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; +import com.hypixel.hytale.server.core.asset.type.gameplay.WorldConfig; import com.hypixel.hytale.server.core.entity.InteractionContext; +import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.interaction.BlockHarvestUtils; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class BreakBlockInteraction extends SimpleBlockInteraction { @Nonnull @@ -58,218 +74,91 @@ public class BreakBlockInteraction extends SimpleBlockInteraction { @Override protected void interactWithBlock( - @Nonnull World param1, - @Nonnull CommandBuffer param2, - @Nonnull InteractionType param3, - @Nonnull InteractionContext param4, - @Nullable ItemStack param5, - @Nonnull Vector3i param6, - @Nonnull CooldownHandler param7 + @Nonnull World world, + @Nonnull CommandBuffer commandBuffer, + @Nonnull InteractionType type, + @Nonnull InteractionContext context, + @Nullable ItemStack heldItemStack, + @Nonnull Vector3i targetBlock, + @Nonnull CooldownHandler cooldownHandler ) { - // $VF: Couldn't be decompiled - // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) - // java.lang.IllegalStateException: Invalid switch case set: [[const(0)], [const(1)], [const(null), null]] for selector of type Lcom/hypixel/hytale/protocol/GameMode; - // at org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchHeadExprent.checkExprTypeBounds(SwitchHeadExprent.java:66) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.checkTypeExpr(VarTypeProcessor.java:140) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.checkTypeExprent(VarTypeProcessor.java:126) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.lambda$processVarTypes$2(VarTypeProcessor.java:114) - // at org.jetbrains.java.decompiler.modules.decompiler.flow.DirectGraph.iterateExprents(DirectGraph.java:107) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.processVarTypes(VarTypeProcessor.java:114) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.calculateVarTypes(VarTypeProcessor.java:44) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsProcessor.setVarVersions(VarVersionsProcessor.java:68) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor.setVarVersions(VarProcessor.java:47) - // at org.jetbrains.java.decompiler.main.rels.MethodProcessor.codeToJava(MethodProcessor.java:302) - // - // Bytecode: - // 000: aload 4 - // 002: invokevirtual com/hypixel/hytale/server/core/entity/InteractionContext.getEntity ()Lcom/hypixel/hytale/component/Ref; - // 005: astore 8 - // 007: aload 2 - // 008: aload 8 - // 00a: invokestatic com/hypixel/hytale/server/core/entity/entities/Player.getComponentType ()Lcom/hypixel/hytale/component/ComponentType; - // 00d: invokevirtual com/hypixel/hytale/component/CommandBuffer.getComponent (Lcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/component/ComponentType;)Lcom/hypixel/hytale/component/Component; - // 010: checkcast com/hypixel/hytale/server/core/entity/entities/Player - // 013: astore 9 - // 015: aload 9 - // 017: ifnonnull 039 - // 01a: invokestatic com/hypixel/hytale/logger/HytaleLogger.getLogger ()Lcom/hypixel/hytale/logger/HytaleLogger; - // 01d: getstatic java/util/logging/Level.INFO Ljava/util/logging/Level; - // 020: invokevirtual com/hypixel/hytale/logger/HytaleLogger.at (Ljava/util/logging/Level;)Lcom/hypixel/hytale/logger/HytaleLogger$Api; - // 023: bipush 5 - // 024: getstatic java/util/concurrent/TimeUnit.MINUTES Ljava/util/concurrent/TimeUnit; - // 027: invokeinterface com/hypixel/hytale/logger/HytaleLogger$Api.atMostEvery (ILjava/util/concurrent/TimeUnit;)Lcom/google/common/flogger/LoggingApi; 3 - // 02c: checkcast com/hypixel/hytale/logger/HytaleLogger$Api - // 02f: ldc "BreakBlockInteraction requires a Player but was used for: %s" - // 031: aload 8 - // 033: invokeinterface com/hypixel/hytale/logger/HytaleLogger$Api.log (Ljava/lang/String;Ljava/lang/Object;)V 3 - // 038: return - // 039: aload 1 - // 03a: invokevirtual com/hypixel/hytale/server/core/universe/world/World.getChunkStore ()Lcom/hypixel/hytale/server/core/universe/world/storage/ChunkStore; - // 03d: astore 10 - // 03f: aload 10 - // 041: invokevirtual com/hypixel/hytale/server/core/universe/world/storage/ChunkStore.getStore ()Lcom/hypixel/hytale/component/Store; - // 044: astore 11 - // 046: aload 6 - // 048: getfield com/hypixel/hytale/math/vector/Vector3i.x I - // 04b: aload 6 - // 04d: getfield com/hypixel/hytale/math/vector/Vector3i.z I - // 050: invokestatic com/hypixel/hytale/math/util/ChunkUtil.indexChunkFromBlock (II)J - // 053: lstore 12 - // 055: aload 10 - // 057: lload 12 - // 059: invokevirtual com/hypixel/hytale/server/core/universe/world/storage/ChunkStore.getChunkReference (J)Lcom/hypixel/hytale/component/Ref; - // 05c: astore 14 - // 05e: aload 14 - // 060: ifnull 06b - // 063: aload 14 - // 065: invokevirtual com/hypixel/hytale/component/Ref.isValid ()Z - // 068: ifne 06c - // 06b: return - // 06c: aload 11 - // 06e: aload 14 - // 070: invokestatic com/hypixel/hytale/server/core/universe/world/chunk/WorldChunk.getComponentType ()Lcom/hypixel/hytale/component/ComponentType; - // 073: invokevirtual com/hypixel/hytale/component/Store.getComponent (Lcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/component/ComponentType;)Lcom/hypixel/hytale/component/Component; - // 076: checkcast com/hypixel/hytale/server/core/universe/world/chunk/WorldChunk - // 079: astore 15 - // 07b: getstatic com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.$assertionsDisabled Z - // 07e: ifne 08e - // 081: aload 15 - // 083: ifnonnull 08e - // 086: new java/lang/AssertionError - // 089: dup - // 08a: invokespecial java/lang/AssertionError. ()V - // 08d: athrow - // 08e: aload 11 - // 090: aload 14 - // 092: invokestatic com/hypixel/hytale/server/core/universe/world/chunk/BlockChunk.getComponentType ()Lcom/hypixel/hytale/component/ComponentType; - // 095: invokevirtual com/hypixel/hytale/component/Store.getComponent (Lcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/component/ComponentType;)Lcom/hypixel/hytale/component/Component; - // 098: checkcast com/hypixel/hytale/server/core/universe/world/chunk/BlockChunk - // 09b: astore 16 - // 09d: getstatic com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.$assertionsDisabled Z - // 0a0: ifne 0b0 - // 0a3: aload 16 - // 0a5: ifnonnull 0b0 - // 0a8: new java/lang/AssertionError - // 0ab: dup - // 0ac: invokespecial java/lang/AssertionError. ()V - // 0af: athrow - // 0b0: aload 16 - // 0b2: aload 6 - // 0b4: invokevirtual com/hypixel/hytale/math/vector/Vector3i.getY ()I - // 0b7: invokevirtual com/hypixel/hytale/server/core/universe/world/chunk/BlockChunk.getSectionAtBlockY (I)Lcom/hypixel/hytale/server/core/universe/world/chunk/section/BlockSection; - // 0ba: astore 17 - // 0bc: aload 1 - // 0bd: invokevirtual com/hypixel/hytale/server/core/universe/world/World.getGameplayConfig ()Lcom/hypixel/hytale/server/core/asset/type/gameplay/GameplayConfig; - // 0c0: astore 18 - // 0c2: aload 18 - // 0c4: invokevirtual com/hypixel/hytale/server/core/asset/type/gameplay/GameplayConfig.getWorldConfig ()Lcom/hypixel/hytale/server/core/asset/type/gameplay/WorldConfig; - // 0c7: astore 19 - // 0c9: aload 0 - // 0ca: getfield com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.harvest Z - // 0cd: ifeq 14b - // 0d0: aload 6 - // 0d2: invokevirtual com/hypixel/hytale/math/vector/Vector3i.getX ()I - // 0d5: istore 20 - // 0d7: aload 6 - // 0d9: invokevirtual com/hypixel/hytale/math/vector/Vector3i.getY ()I - // 0dc: istore 21 - // 0de: aload 6 - // 0e0: invokevirtual com/hypixel/hytale/math/vector/Vector3i.getZ ()I - // 0e3: istore 22 - // 0e5: aload 15 - // 0e7: iload 20 - // 0e9: iload 21 - // 0eb: iload 22 - // 0ed: invokevirtual com/hypixel/hytale/server/core/universe/world/chunk/WorldChunk.getBlockType (III)Lcom/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType; - // 0f0: astore 23 - // 0f2: aload 23 - // 0f4: ifnonnull 103 - // 0f7: aload 4 - // 0f9: invokevirtual com/hypixel/hytale/server/core/entity/InteractionContext.getState ()Lcom/hypixel/hytale/protocol/InteractionSyncData; - // 0fc: getstatic com/hypixel/hytale/protocol/InteractionState.Failed Lcom/hypixel/hytale/protocol/InteractionState; - // 0ff: putfield com/hypixel/hytale/protocol/InteractionSyncData.state Lcom/hypixel/hytale/protocol/InteractionState; - // 102: return - // 103: aload 19 - // 105: invokevirtual com/hypixel/hytale/server/core/asset/type/gameplay/WorldConfig.isBlockGatheringAllowed ()Z - // 108: ifne 117 - // 10b: aload 4 - // 10d: invokevirtual com/hypixel/hytale/server/core/entity/InteractionContext.getState ()Lcom/hypixel/hytale/protocol/InteractionSyncData; - // 110: getstatic com/hypixel/hytale/protocol/InteractionState.Failed Lcom/hypixel/hytale/protocol/InteractionState; - // 113: putfield com/hypixel/hytale/protocol/InteractionSyncData.state Lcom/hypixel/hytale/protocol/InteractionState; - // 116: return - // 117: aload 23 - // 119: invokestatic com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.shouldPickupByInteraction (Lcom/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType;)Z - // 11c: ifne 12b - // 11f: aload 4 - // 121: invokevirtual com/hypixel/hytale/server/core/entity/InteractionContext.getState ()Lcom/hypixel/hytale/protocol/InteractionSyncData; - // 124: getstatic com/hypixel/hytale/protocol/InteractionState.Failed Lcom/hypixel/hytale/protocol/InteractionState; - // 127: putfield com/hypixel/hytale/protocol/InteractionSyncData.state Lcom/hypixel/hytale/protocol/InteractionState; - // 12a: return - // 12b: aload 17 - // 12d: iload 20 - // 12f: iload 21 - // 131: iload 22 - // 133: invokevirtual com/hypixel/hytale/server/core/universe/world/chunk/section/BlockSection.getFiller (III)I - // 136: istore 24 - // 138: aload 8 - // 13a: aload 6 - // 13c: aload 23 - // 13e: iload 24 - // 140: aload 14 - // 142: aload 2 - // 143: aload 11 - // 145: invokestatic com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.performPickupByInteraction (Lcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/math/vector/Vector3i;Lcom/hypixel/hytale/server/core/asset/type/blocktype/config/BlockType;ILcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/component/ComponentAccessor;Lcom/hypixel/hytale/component/ComponentAccessor;)V - // 148: goto 1ca - // 14b: aload 19 - // 14d: invokevirtual com/hypixel/hytale/server/core/asset/type/gameplay/WorldConfig.isBlockBreakingAllowed ()Z - // 150: istore 20 - // 152: iload 20 - // 154: ifne 163 - // 157: aload 4 - // 159: invokevirtual com/hypixel/hytale/server/core/entity/InteractionContext.getState ()Lcom/hypixel/hytale/protocol/InteractionSyncData; - // 15c: getstatic com/hypixel/hytale/protocol/InteractionState.Failed Lcom/hypixel/hytale/protocol/InteractionState; - // 15f: putfield com/hypixel/hytale/protocol/InteractionSyncData.state Lcom/hypixel/hytale/protocol/InteractionState; - // 162: return - // 163: aload 9 - // 165: invokevirtual com/hypixel/hytale/server/core/entity/entities/Player.getGameMode ()Lcom/hypixel/hytale/protocol/GameMode; - // 168: astore 21 - // 16a: bipush 0 - // 16b: istore 22 - // 16d: aload 21 - // 16f: iload 22 - // 171: invokedynamic typeSwitch (Ljava/lang/Object;I)I bsm=java/lang/runtime/SwitchBootstraps.typeSwitch (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc; ] - // 176: tableswitch 74 -1 1 74 26 57 - // 190: aload 9 - // 192: aload 8 - // 194: aload 6 - // 196: aload 5 - // 198: aconst_null - // 199: aload 0 - // 19a: getfield com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.toolId Ljava/lang/String; - // 19d: aload 0 - // 19e: getfield com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/BreakBlockInteraction.matchTool Z - // 1a1: fconst_1 - // 1a2: bipush 0 - // 1a3: aload 14 - // 1a5: aload 2 - // 1a6: aload 11 - // 1a8: invokestatic com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.performBlockDamage (Lcom/hypixel/hytale/server/core/entity/LivingEntity;Lcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/math/vector/Vector3i;Lcom/hypixel/hytale/server/core/inventory/ItemStack;Lcom/hypixel/hytale/server/core/asset/type/item/config/ItemTool;Ljava/lang/String;ZFILcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/component/ComponentAccessor;Lcom/hypixel/hytale/component/ComponentAccessor;)Z - // 1ab: pop - // 1ac: goto 1ca - // 1af: aload 8 - // 1b1: aload 5 - // 1b3: aload 6 - // 1b5: aload 14 - // 1b7: aload 2 - // 1b8: aload 11 - // 1ba: invokestatic com/hypixel/hytale/server/core/modules/interaction/BlockHarvestUtils.performBlockBreak (Lcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/server/core/inventory/ItemStack;Lcom/hypixel/hytale/math/vector/Vector3i;Lcom/hypixel/hytale/component/Ref;Lcom/hypixel/hytale/component/ComponentAccessor;Lcom/hypixel/hytale/component/ComponentAccessor;)V - // 1bd: goto 1ca - // 1c0: new java/lang/UnsupportedOperationException - // 1c3: dup - // 1c4: ldc "GameMode is not supported" - // 1c6: invokespecial java/lang/UnsupportedOperationException. (Ljava/lang/String;)V - // 1c9: athrow - // 1ca: return + Ref ref = context.getEntity(); + Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); + if (playerComponent == null) { + HytaleLogger.getLogger().at(Level.INFO).atMostEvery(5, TimeUnit.MINUTES).log("BreakBlockInteraction requires a Player but was used for: %s", ref); + } else { + ChunkStore chunkStore = world.getChunkStore(); + Store chunkStoreStore = chunkStore.getStore(); + long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z); + Ref chunkReference = chunkStore.getChunkReference(chunkIndex); + if (chunkReference != null && chunkReference.isValid()) { + WorldChunk worldChunkComponent = chunkStoreStore.getComponent(chunkReference, WorldChunk.getComponentType()); + + assert worldChunkComponent != null; + + BlockChunk blockChunkComponent = chunkStoreStore.getComponent(chunkReference, BlockChunk.getComponentType()); + + assert blockChunkComponent != null; + + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(targetBlock.y()); + GameplayConfig gameplayConfig = world.getGameplayConfig(); + WorldConfig worldConfig = gameplayConfig.getWorldConfig(); + if (this.harvest) { + int x = targetBlock.x(); + int y = targetBlock.y(); + int z = targetBlock.z(); + BlockType blockType = worldChunkComponent.getBlockType(x, y, z); + if (blockType == null) { + context.getState().state = InteractionState.Failed; + return; + } + + if (!worldConfig.isBlockGatheringAllowed()) { + context.getState().state = InteractionState.Failed; + return; + } + + if (!BlockHarvestUtils.shouldPickupByInteraction(blockType)) { + context.getState().state = InteractionState.Failed; + return; + } + + int filler = blockSection.getFiller(x, y, z); + BlockHarvestUtils.performPickupByInteraction(ref, targetBlock, blockType, filler, chunkReference, commandBuffer, chunkStoreStore); + } else { + boolean blockBreakingAllowed = worldConfig.isBlockBreakingAllowed(); + if (!blockBreakingAllowed) { + context.getState().state = InteractionState.Failed; + return; + } + + switch (playerComponent.getGameMode()) { + case Adventure: + BlockHarvestUtils.performBlockDamage( + playerComponent, + ref, + targetBlock, + heldItemStack, + null, + this.toolId, + this.matchTool, + 1.0F, + 0, + chunkReference, + commandBuffer, + chunkStoreStore + ); + break; + case Creative: + BlockHarvestUtils.performBlockBreak(ref, heldItemStack, targetBlock, chunkReference, commandBuffer, chunkStoreStore); + break; + case null: + default: + throw new UnsupportedOperationException("GameMode is not supported"); + } + } + } + } } @Override diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ChangeBlockInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ChangeBlockInteraction.java index 681e834e..295c5875 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ChangeBlockInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ChangeBlockInteraction.java @@ -8,8 +8,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.BlockRotation; import com.hypixel.hytale.protocol.Interaction; @@ -33,6 +31,8 @@ import java.util.Map; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class ChangeBlockInteraction extends SimpleBlockInteraction { @Nonnull @@ -99,9 +99,9 @@ public class ChangeBlockInteraction extends SimpleBlockInteraction { if (this.requireNotBroken && itemInHand != null && itemInHand.isBroken()) { context.getState().state = InteractionState.Failed; } else { - int x = targetBlock.getX(); - int y = targetBlock.getY(); - int z = targetBlock.getZ(); + int x = targetBlock.x(); + int y = targetBlock.y(); + int z = targetBlock.z(); WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)); int current = chunk.getBlock(x, y, z); int to = this.getChangeMapIds().get(current); diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ChangeStateInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ChangeStateInteraction.java index 3a7a2484..08d4f2c0 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ChangeStateInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ChangeStateInteraction.java @@ -7,7 +7,6 @@ import com.hypixel.hytale.codec.codecs.map.MapCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.Interaction; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; @@ -23,6 +22,7 @@ import java.util.HashMap; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class ChangeStateInteraction extends SimpleBlockInteraction { @Nonnull @@ -85,7 +85,7 @@ public class ChangeStateInteraction extends SimpleBlockInteraction { settings |= 2; } - chunk.setBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ(), newBlockId, newBlockType, rotation, 0, settings); + chunk.setBlock(targetBlock.x(), targetBlock.y(), targetBlock.z(), newBlockId, newBlockType, rotation, 0, settings); BlockType interactionStateBlock = current.getBlockForState(newState); if (interactionStateBlock == null) { return; diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/CycleBlockGroupInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/CycleBlockGroupInteraction.java index 970283bf..57f9e248 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/CycleBlockGroupInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/CycleBlockGroupInteraction.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockSoundEvent; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionSyncData; @@ -18,7 +17,9 @@ import com.hypixel.hytale.server.core.asset.type.gameplay.WorldConfig; import com.hypixel.hytale.server.core.asset.type.item.config.BlockGroup; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.InteractionContext; +import com.hypixel.hytale.server.core.entity.ItemUtils; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.universe.world.SoundUtil; @@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class CycleBlockGroupInteraction extends SimpleBlockInteraction { private static final int SET_SETTINGS = 256; @@ -73,7 +75,7 @@ public class CycleBlockGroupInteraction extends SimpleBlockInteraction { assert blockChunkComponent != null; - BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(targetBlock.getY()); + BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(targetBlock.y()); GameplayConfig gameplayConfig = world.getGameplayConfig(); WorldConfig worldConfig = gameplayConfig.getWorldConfig(); boolean blockBreakingAllowed = worldConfig.isBlockBreakingAllowed(); @@ -90,11 +92,15 @@ public class CycleBlockGroupInteraction extends SimpleBlockInteraction { BlockType nextBlockType = BlockType.getAssetMap().getAsset(nextBlockKey); if (nextBlockType != null) { ItemStack heldItem = context.getHeldItem(); - if (heldItem != null && playerComponent.canDecreaseItemStackDurability(ref, store) && !heldItem.isUnbreakable()) { + InventoryComponent.Hotbar hotbarComponent = commandBuffer.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (heldItem != null + && hotbarComponent != null + && ItemUtils.canDecreaseItemStackDurability(ref, commandBuffer) + && !heldItem.isUnbreakable()) { playerComponent.updateItemStackDurability( ref, heldItem, - playerComponent.getInventory().getHotbar(), + hotbarComponent.getInventory(), context.getHeldItemSlot(), -heldItem.getItem().getDurabilityLossOnHit(), commandBuffer @@ -103,7 +109,7 @@ public class CycleBlockGroupInteraction extends SimpleBlockInteraction { int newBlockId = BlockType.getAssetMap().getIndex(nextBlockType.getId()); int rotation = worldChunkComponent.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z); - worldChunkComponent.setBlock(targetBlock.getX(), targetBlock.getY(), targetBlock.getZ(), newBlockId, nextBlockType, rotation, 0, 256); + worldChunkComponent.setBlock(targetBlock.x(), targetBlock.y(), targetBlock.z(), newBlockId, nextBlockType, rotation, 0, 256); state.state = InteractionState.NotFinished; BlockSoundSet soundSet = BlockSoundSet.getAssetMap().getAsset(nextBlockType.getBlockSoundSetIndex()); if (soundSet != null) { diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/DestroyBlockInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/DestroyBlockInteraction.java index 112f7a92..4f39c934 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/DestroyBlockInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/DestroyBlockInteraction.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.entity.InteractionContext; @@ -16,6 +15,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class DestroyBlockInteraction extends SimpleInstantInteraction { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ExplodeInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ExplodeInteraction.java index ff9db631..c97e36b2 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ExplodeInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/ExplodeInteraction.java @@ -8,8 +8,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -33,6 +31,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector4d; public class ExplodeInteraction extends SimpleInstantInteraction { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PickBlockInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PickBlockInteraction.java index b1fda724..8b49e46e 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PickBlockInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PickBlockInteraction.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.core.modules.interaction.interaction.config.cl import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.Interaction; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -13,6 +12,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PickBlockInteraction extends SimpleBlockInteraction { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PlaceBlockInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PlaceBlockInteraction.java index aeb853e2..62468edc 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PlaceBlockInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PlaceBlockInteraction.java @@ -7,8 +7,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.BlockRotation; import com.hypixel.hytale.protocol.GameMode; @@ -39,6 +37,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PlaceBlockInteraction extends SimpleInteraction { public static final int TEMP_MAX_ADVENTURE_PLACEMENT_RANGE_SQUARED = 49; @@ -127,7 +127,7 @@ public class PlaceBlockInteraction extends SimpleInteraction { if (transformComponent != null && playerComponent != null && playerComponent.getGameMode() != GameMode.Creative) { Vector3d position = transformComponent.getPosition(); Vector3d blockCenter = new Vector3d(blockPosition.x + 0.5, blockPosition.y + 0.5, blockPosition.z + 0.5); - if (position.distanceSquaredTo(blockCenter) > 49.0) { + if (position.distanceSquared(blockCenter) > 49.0) { context.getState().state = InteractionState.Failed; return; } @@ -163,7 +163,7 @@ public class PlaceBlockInteraction extends SimpleInteraction { heldItemStack, clientPlacedBlockTypeKey != null ? clientPlacedBlockTypeKey : this.blockTypeKey, heldItemContainer, - BlockFace.fromProtocolFace(context.getClientState().blockFace).getDirection(), + new Vector3i(BlockFace.fromProtocolFace(context.getClientState().blockFace).getDirection()), targetBlockPosition, blockRotation, inventory, @@ -171,7 +171,8 @@ public class PlaceBlockInteraction extends SimpleInteraction { this.removeItemInHand, chunkReference, chunkStore, - commandBuffer + commandBuffer, + false ); boolean isAdventure = playerComponent == null || playerComponent.getGameMode() == GameMode.Adventure; if (isAdventure && heldItemStack.getQuantity() == 1 && this.removeItemInHand) { diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PlaceFluidInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PlaceFluidInteraction.java index 4dd9a4e5..04f5c86a 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PlaceFluidInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/PlaceFluidInteraction.java @@ -7,7 +7,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; @@ -28,6 +27,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PlaceFluidInteraction extends SimpleBlockInteraction { @Nonnull @@ -81,13 +81,12 @@ public class PlaceFluidInteraction extends SimpleBlockInteraction { BlockType targetBlockType = world.getBlockType(targetBlock); FluidTicker ticker = fluid.getTicker(); if (!ticker.canOccupySolidBlocks() && FluidTicker.isSolid(targetBlockType)) { - target = targetBlock.clone(); + target = new Vector3i(targetBlock); BlockFace face = BlockFace.fromProtocolFace(context.getClientState().blockFace); target.add(face.getDirection()); } - Ref section = world.getChunkStore() - .getChunkSectionReference(ChunkUtil.chunkCoordinate(target.x), ChunkUtil.chunkCoordinate(target.y), ChunkUtil.chunkCoordinate(target.z)); + Ref section = world.getChunkStore().getChunkSectionReferenceAtBlock(target.x, target.y, target.z); if (section != null) { FluidSection fluidSectionComponent = store.getComponent(section, FluidSection.getComponentType()); if (fluidSectionComponent != null) { diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/SimpleBlockInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/SimpleBlockInteraction.java index 74ca8595..c2cce8f1 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/SimpleBlockInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/SimpleBlockInteraction.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockFace; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.BlockRotation; @@ -15,10 +14,8 @@ import com.hypixel.hytale.protocol.InteractionSyncData; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; @@ -33,6 +30,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public abstract class SimpleBlockInteraction extends SimpleInteraction { @Nonnull @@ -85,7 +83,7 @@ public abstract class SimpleBlockInteraction extends SimpleInteraction { assert transformComponent != null; - double distanceSquared = transformComponent.getPosition().distanceSquaredTo(latestBlockPos.x + 0.5, latestBlockPos.y + 0.5, latestBlockPos.z + 0.5); + double distanceSquared = transformComponent.getPosition().distanceSquared(latestBlockPos.x + 0.5, latestBlockPos.y + 0.5, latestBlockPos.z + 0.5); BlockPosition baseBlock = world.getBaseBlock(latestBlockPos); context.getMetaStore().putMetaObject(Interaction.TARGET_BLOCK, baseBlock); context.getMetaStore().putMetaObject(Interaction.TARGET_BLOCK_RAW, latestBlockPos); @@ -95,18 +93,17 @@ public abstract class SimpleBlockInteraction extends SimpleInteraction { if (targetBlockPos == null) { context.getState().state = InteractionState.Failed; super.tick0(firstRun, time, type, context, cooldownHandler); - } else if (EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity) { - Inventory inventory = livingEntity.getInventory(); - ItemStack itemInHand = inventory.getItemInHand(); - Vector3i var21 = new Vector3i(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); - WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(var21.x, var21.z)); + } else { + ItemStack itemInHand = InventoryComponent.getItemInHand(commandBuffer, ref); + Vector3i targetBlock = new Vector3i(targetBlockPos.x, targetBlockPos.y, targetBlockPos.z); + WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z)); if (chunk == null) { context.getState().state = InteractionState.Failed; super.tick0(firstRun, time, type, context, cooldownHandler); } else { - int blockId = chunk.getBlock(var21); + int blockId = chunk.getBlock(targetBlock); if (blockId != 1 && blockId != 0) { - this.interactWithBlock(world, commandBuffer, type, context, itemInHand, var21, cooldownHandler); + this.interactWithBlock(world, commandBuffer, type, context, itemInHand, targetBlock, cooldownHandler); super.tick0(firstRun, time, type, context, cooldownHandler); } else { context.getState().state = InteractionState.Failed; @@ -138,39 +135,36 @@ public abstract class SimpleBlockInteraction extends SimpleInteraction { assert commandBuffer != null; World world = commandBuffer.getExternalData().getWorld(); - if (EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity) { - Inventory inventory = livingEntity.getInventory(); - ItemStack itemInHand = inventory.getItemInHand(); - context.getState().blockFace = BlockFace.Up; - BlockPosition contextTargetBlock = context.getTargetBlock(); - Vector3i targetBlock; - if (contextTargetBlock == null) { - targetBlock = TargetUtil.getTargetBlock(ref, 8.0, commandBuffer); - if (targetBlock == null) { - context.getState().state = InteractionState.Failed; - super.tick0(firstRun, time, type, context, cooldownHandler); - return; - } - - context.getState().blockPosition = new BlockPosition(targetBlock.x, targetBlock.y, targetBlock.z); - } else { - context.getState().blockPosition = contextTargetBlock; - targetBlock = new Vector3i(contextTargetBlock.x, contextTargetBlock.y, contextTargetBlock.z); - } - - WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z)); - if (chunk == null) { + ItemStack itemInHand = InventoryComponent.getItemInHand(commandBuffer, ref); + context.getState().blockFace = BlockFace.Up; + BlockPosition contextTargetBlock = context.getTargetBlock(); + Vector3i targetBlock; + if (contextTargetBlock == null) { + targetBlock = TargetUtil.getTargetBlock(ref, 8.0, commandBuffer); + if (targetBlock == null) { context.getState().state = InteractionState.Failed; super.tick0(firstRun, time, type, context, cooldownHandler); + return; + } + + context.getState().blockPosition = new BlockPosition(targetBlock.x, targetBlock.y, targetBlock.z); + } else { + context.getState().blockPosition = contextTargetBlock; + targetBlock = new Vector3i(contextTargetBlock.x, contextTargetBlock.y, contextTargetBlock.z); + } + + WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z)); + if (chunk == null) { + context.getState().state = InteractionState.Failed; + super.tick0(firstRun, time, type, context, cooldownHandler); + } else { + int blockId = chunk.getBlock(targetBlock); + if (blockId != 1 && blockId != 0) { + this.simulateInteractWithBlock(type, context, itemInHand, world, targetBlock); + super.tick0(firstRun, time, type, context, cooldownHandler); } else { - int blockId = chunk.getBlock(targetBlock); - if (blockId != 1 && blockId != 0) { - this.simulateInteractWithBlock(type, context, itemInHand, world, targetBlock); - super.tick0(firstRun, time, type, context, cooldownHandler); - } else { - context.getState().state = InteractionState.Failed; - super.tick0(firstRun, time, type, context, cooldownHandler); - } + context.getState().state = InteractionState.Failed; + super.tick0(firstRun, time, type, context, cooldownHandler); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/UseBlockInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/UseBlockInteraction.java index 988fc986..938c8bc4 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/UseBlockInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/client/UseBlockInteraction.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.modules.interaction.interaction.config.cl import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.Interaction; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class UseBlockInteraction extends SimpleBlockInteraction { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/ChangeActiveSlotInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/ChangeActiveSlotInteraction.java index 14463235..6649e553 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/ChangeActiveSlotInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/ChangeActiveSlotInteraction.java @@ -93,7 +93,7 @@ public class ChangeActiveSlotInteraction extends Interaction { var15.putMetaObject(TARGET_SLOT, Integer.valueOf(slot)); } - livingEntity.getInventory().setActiveHotbarSlot(slot); + livingEntity.getInventory().setActiveHotbarSlot(ref, slot, commandBuffer); Runnable action = var15.removeMetaObject(PLACE_MOVED_ITEM); if (action != null) { action.run(); diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/StatsConditionWithModifierInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/StatsConditionWithModifierInteraction.java index 88945210..8434aa33 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/StatsConditionWithModifierInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/StatsConditionWithModifierInteraction.java @@ -12,10 +12,8 @@ import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.ValueType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.item.config.ItemArmor; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; @@ -71,12 +69,9 @@ public class StatsConditionWithModifierInteraction extends StatsConditionBaseInt } float statValue = this.valueType == ValueType.Absolute ? stat.get() : stat.asPercentage() * 100.0F; - Inventory inventory = null; - if (EntityUtils.getEntity(ref, componentAccessor) instanceof LivingEntity livingEntity) { - inventory = livingEntity.getInventory(); - } - - float modifiedCost = this.calculateDiscount(inventory, cost.getIntKey(), cost.getFloatValue()); + InventoryComponent.Armor armorComponent = componentAccessor.getComponent(ref, InventoryComponent.Armor.getComponentType()); + ItemContainer armorContainer = armorComponent != null ? armorComponent.getInventory() : null; + float modifiedCost = this.calculateDiscount(armorContainer, cost.getIntKey(), cost.getFloatValue()); if (this.lessThan) { if (statValue >= modifiedCost) { return false; @@ -90,14 +85,9 @@ public class StatsConditionWithModifierInteraction extends StatsConditionBaseInt } } - private float calculateDiscount(@Nullable Inventory inventory, int statIndex, float baseCost) { + private float calculateDiscount(@Nullable ItemContainer armorContainer, int statIndex, float baseCost) { float flatModifier = 0.0F; float multiplierModifier = 0.0F; - ItemContainer armorContainer = null; - if (inventory != null) { - armorContainer = inventory.getArmor(); - } - if (armorContainer != null) { for (short i = 0; i < armorContainer.getCapacity(); i++) { ItemStack itemStack = armorContainer.getItemStack(i); diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/simple/CommandInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/simple/CommandInteraction.java new file mode 100644 index 00000000..ed6aa029 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/none/simple/CommandInteraction.java @@ -0,0 +1,46 @@ +package com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.simple; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.protocol.InteractionType; +import com.hypixel.hytale.server.core.command.system.CommandManager; +import com.hypixel.hytale.server.core.entity.InteractionContext; +import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; +import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import javax.annotation.Nonnull; + +public class CommandInteraction extends SimpleInstantInteraction { + @Nonnull + public static final BuilderCodec CODEC = BuilderCodec.builder( + CommandInteraction.class, CommandInteraction::new, SimpleInstantInteraction.CODEC + ) + .documentation("Interaction that executes a server command as the owning player.") + .appendInherited( + new KeyedCodec<>("Command", Codec.STRING), + (interaction, s) -> interaction.command = s, + interaction -> interaction.command, + (interaction, parent) -> interaction.command = parent.command + ) + .add() + .build(); + private String command; + + @Override + protected void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) { + Ref ref = context.getOwningEntity(); + PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType()); + if (playerRef != null) { + CommandManager.get().handleCommand(playerRef, this.command); + } + } + + @Nonnull + @Override + public String toString() { + return "CommandInteraction{command=" + this.command + "} " + super.toString(); + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/AOECircleSelector.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/AOECircleSelector.java index 264cb1b2..75cb14fd 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/AOECircleSelector.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/AOECircleSelector.java @@ -8,18 +8,20 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.function.consumer.TriIntConsumer; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.math.vector.Vector4d; -import com.hypixel.hytale.protocol.Vector3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.SelectInteraction; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Predicate; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector3i; +import org.joml.Vector4d; public class AOECircleSelector extends SelectorType { @Nonnull @@ -35,8 +37,8 @@ public class AOECircleSelector extends SelectorType { .documentation("The range of the area to search for targets in.") .add() .append( - new KeyedCodec<>("Offset", Vector3d.CODEC), - (aoeCircleEntitySelector, i) -> aoeCircleEntitySelector.offset = i, + new KeyedCodec<>("Offset", Vector3dUtil.CODEC), + (aoeCircleEntitySelector, i) -> aoeCircleEntitySelector.offset.set(i), aoeCircleEntitySelector -> aoeCircleEntitySelector.offset ) .documentation("The offset of the area to search for targets in.") @@ -46,7 +48,7 @@ public class AOECircleSelector extends SelectorType { private final AOECircleSelector.RuntimeSelector instance = new AOECircleSelector.RuntimeSelector(); protected float range; @Nonnull - protected Vector3d offset = Vector3d.ZERO; + protected final Vector3d offset = new Vector3d(); @Nonnull @Override @@ -75,8 +77,8 @@ public class AOECircleSelector extends SelectorType { assert headRotationComponent != null; - position = this.offset.clone(); - position.rotateY(headRotationComponent.getRotation().getYaw()); + position = new Vector3d(this.offset); + position.rotateY(headRotationComponent.getRotation().yaw()); position.add(transformComponent.getPosition()); } @@ -84,11 +86,16 @@ public class AOECircleSelector extends SelectorType { } private class RuntimeSelector implements Selector { + private RuntimeSelector() { + Objects.requireNonNull(AOECircleSelector.this); + super(); + } + @Override public void tick(@Nonnull CommandBuffer commandBuffer, @Nonnull Ref ref, float time, float runTime) { if (SelectInteraction.SHOW_VISUAL_DEBUG) { Vector3d position = AOECircleSelector.this.selectTargetPosition(commandBuffer, ref); - com.hypixel.hytale.math.vector.Vector3f color = new com.hypixel.hytale.math.vector.Vector3f( + Vector3f color = new Vector3f( (float)HashUtil.random(ref.getIndex(), this.hashCode(), 10L), (float)HashUtil.random(ref.getIndex(), this.hashCode(), 11L), (float)HashUtil.random(ref.getIndex(), this.hashCode(), 12L) @@ -106,7 +113,11 @@ public class AOECircleSelector extends SelectorType { ) { Vector3d position = AOECircleSelector.this.selectTargetPosition(commandBuffer, attacker); Selector.selectNearbyEntities( - commandBuffer, position, (double)AOECircleSelector.this.range, entity -> consumer.accept(entity, Vector4d.newPosition(position)), filter + commandBuffer, + position, + (double)AOECircleSelector.this.range, + entity -> consumer.accept(entity, new Vector4d(position.x, position.y, position.z, 1.0)), + filter ); } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/AOECylinderSelector.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/AOECylinderSelector.java index aeb58785..a9cc6e91 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/AOECylinderSelector.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/AOECylinderSelector.java @@ -9,9 +9,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.function.consumer.TriIntConsumer; import com.hypixel.hytale.math.util.HashUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.Invulnerable; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -19,10 +16,14 @@ import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.none.SelectInteraction; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector4d; public class AOECylinderSelector extends AOECircleSelector { @Nonnull @@ -53,6 +54,11 @@ public class AOECylinderSelector extends AOECircleSelector { } private class RuntimeSelector implements Selector { + private RuntimeSelector() { + Objects.requireNonNull(AOECylinderSelector.this); + super(); + } + @Override public void tick(@Nonnull CommandBuffer commandBuffer, @Nonnull Ref ref, float time, float runTime) { if (SelectInteraction.SHOW_VISUAL_DEBUG) { diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/ClientSourcedSelector.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/ClientSourcedSelector.java index 7697b996..a017371d 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/ClientSourcedSelector.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/ClientSourcedSelector.java @@ -3,13 +3,13 @@ package com.hypixel.hytale.server.core.modules.interaction.interaction.config.se import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.function.consumer.TriIntConsumer; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.protocol.SelectedHitEntity; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.function.BiConsumer; import java.util.function.Predicate; import javax.annotation.Nonnull; +import org.joml.Vector4d; @Deprecated public class ClientSourcedSelector implements Selector { @@ -40,7 +40,7 @@ public class ClientSourcedSelector implements Selector { for (SelectedHitEntity info : hits) { Ref targetRef = store.getRefFromNetworkId(info.networkId); if (targetRef != null) { - consumer.accept(targetRef, new Vector4d(info.hitLocation.x, info.hitLocation.y, info.hitLocation.z, 0.0)); + consumer.accept(targetRef, new Vector4d(info.hitLocation.x(), info.hitLocation.y(), info.hitLocation.z(), 0.0)); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/HorizontalSelector.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/HorizontalSelector.java index b6d9d139..66ce2a90 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/HorizontalSelector.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/HorizontalSelector.java @@ -13,13 +13,9 @@ import com.hypixel.hytale.math.hitdetection.LineOfSightProvider; import com.hypixel.hytale.math.hitdetection.projection.FrustumProjectionProvider; import com.hypixel.hytale.math.hitdetection.view.DirectionViewProvider; import com.hypixel.hytale.math.iterator.BlockIterator; -import com.hypixel.hytale.math.matrix.Matrix4d; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.HorizontalSelectorDirection; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; @@ -34,9 +30,14 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Predicate; import javax.annotation.Nonnull; +import org.joml.Matrix4d; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector4d; public class HorizontalSelector extends SelectorType { @Nonnull @@ -177,16 +178,25 @@ public class HorizontalSelector extends SelectorType { private class RuntimeSelector implements Selector { @Nonnull - protected HitDetectionExecutor executor = new HitDetectionExecutor(); + protected HitDetectionExecutor executor; @Nonnull - protected Matrix4d modelMatrix = new Matrix4d(); + protected Matrix4d modelMatrix; @Nonnull - protected FrustumProjectionProvider projectionProvider = new FrustumProjectionProvider(); + protected FrustumProjectionProvider projectionProvider; @Nonnull - protected DirectionViewProvider viewProvider = new DirectionViewProvider(); + protected DirectionViewProvider viewProvider; protected float lastTime; protected double runTimeDeltaPercentageSum; + private RuntimeSelector() { + Objects.requireNonNull(HorizontalSelector.this); + super(); + this.executor = new HitDetectionExecutor(); + this.modelMatrix = new Matrix4d(); + this.projectionProvider = new FrustumProjectionProvider(); + this.viewProvider = new DirectionViewProvider(); + } + @Override public void tick(@Nonnull CommandBuffer commandBuffer, @Nonnull Ref attacker, float time, float runTime) { ModelComponent modelComponent = commandBuffer.getComponent(attacker, ModelComponent.getComponentType()); @@ -203,9 +213,9 @@ public class HorizontalSelector extends SelectorType { assert headRotationComponent != null; - double posX = position.getX(); - double posY = position.getY() + yOffset; - double posZ = position.getZ(); + double posX = position.x(); + double posY = position.y() + yOffset; + double posZ = position.z(); float delta = time - this.lastTime; this.lastTime = time; float runTimeDeltaPercentage = delta / runTime; @@ -223,9 +233,7 @@ public class HorizontalSelector extends SelectorType { .setBottom(HorizontalSelector.this.extendBottom * stretchFactor) .setRotation(yawOffset, HorizontalSelector.this.pitchOffset, HorizontalSelector.this.rollOffset) .setTop(HorizontalSelector.this.extendTop * stretchFactor); - this.viewProvider - .setPosition(posX, posY, posZ) - .setDirection(headRotationComponent.getRotation().getYaw(), headRotationComponent.getRotation().getPitch()); + this.viewProvider.setPosition(posX, posY, posZ).setDirection(headRotationComponent.getRotation().yaw(), headRotationComponent.getRotation().pitch()); this.executor.setOrigin(posX, posY, posZ).setProjectionProvider(this.projectionProvider).setViewProvider(this.viewProvider); if (HorizontalSelector.this.testLineOfSight) { World world = commandBuffer.getStore().getExternalData().getWorld(); @@ -273,18 +281,17 @@ public class HorizontalSelector extends SelectorType { } if (SelectInteraction.SHOW_VISUAL_DEBUG) { - Matrix4d tmp = new Matrix4d(); Matrix4d matrix = new Matrix4d(); matrix.identity() .translate(posX, posY, posZ) - .rotateAxis(-headRotationComponent.getRotation().getYaw(), 0.0, 1.0, 0.0, tmp) - .rotateAxis(-headRotationComponent.getRotation().getPitch(), 1.0, 0.0, 0.0, tmp); + .rotate(headRotationComponent.getRotation().yaw(), 0.0, 1.0, 0.0) + .rotate(headRotationComponent.getRotation().pitch(), 1.0, 0.0, 0.0); Vector3f color = new Vector3f( (float)HashUtil.random(attacker.getIndex(), this.hashCode(), 10L), (float)HashUtil.random(attacker.getIndex(), this.hashCode(), 11L), (float)HashUtil.random(attacker.getIndex(), this.hashCode(), 12L) ); - DebugUtils.addFrustum(commandBuffer.getExternalData().getWorld(), matrix, this.projectionProvider.getMatrix(), color, 5.0F, true); + DebugUtils.addFrustum(commandBuffer.getExternalData().getWorld(), matrix, this.projectionProvider.getMatrix(), color, 5.0F, DebugUtils.FLAG_FADE); } this.runTimeDeltaPercentageSum += runTimeDeltaPercentage; diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/RaycastSelector.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/RaycastSelector.java index b40610f0..586f9d81 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/RaycastSelector.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/RaycastSelector.java @@ -8,12 +8,8 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.function.consumer.TriIntConsumer; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.math.vector.Vector4d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockMaterial; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.modules.collision.CollisionMath; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; @@ -24,14 +20,20 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.config.non import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import it.unimi.dsi.fastutil.ints.IntSet; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector3i; +import org.joml.Vector4d; public class RaycastSelector extends SelectorType { public static final BuilderCodec CODEC = BuilderCodec.builder(RaycastSelector.class, RaycastSelector::new, BASE_CODEC) - .appendInherited(new KeyedCodec<>("Offset", Vector3d.CODEC), (o, i) -> o.offset = i, o -> o.offset, (o, p) -> o.offset = p.offset) + .appendInherited(new KeyedCodec<>("Offset", Vector3dUtil.CODEC), (o, i) -> o.offset.set(i), o -> o.offset, (o, p) -> o.offset.set(p.offset)) .documentation("The offset of the area to search for targets in.") .add() .appendInherited(new KeyedCodec<>("Distance", Codec.INTEGER), (o, i) -> o.distance = i, o -> o.distance, (o, p) -> o.distance = p.distance) @@ -57,7 +59,7 @@ public class RaycastSelector extends SelectorType { } }) .build(); - protected Vector3d offset = Vector3d.ZERO; + protected final Vector3d offset = new Vector3d(); protected int distance = 30; protected boolean ignoreFluids = false; protected boolean ignoreEmptyCollisionMaterial = false; @@ -81,9 +83,9 @@ public class RaycastSelector extends SelectorType { TransformComponent transformComponent = commandBuffer.getComponent(attacker, TransformComponent.getComponentType()); Vector3d position = transformComponent.getPosition(); if (this.offset.x != 0.0 || this.offset.y != 0.0 || this.offset.z != 0.0) { - position = this.offset.clone(); + position = new Vector3d(this.offset); HeadRotation headRotation = commandBuffer.getComponent(attacker, HeadRotation.getComponentType()); - position.rotateY(headRotation.getRotation().getYaw()); + position.rotateY(headRotation.getRotation().yaw()); position.add(transformComponent.getPosition()); } @@ -100,29 +102,36 @@ public class RaycastSelector extends SelectorType { public Ref match; public double distance = Double.MAX_VALUE; @Nonnull - public Vector4d hitPosition = new Vector4d(); + public Vector4d hitPosition = new Vector4d().zero(); } private class RuntimeSelector implements Selector { - private final RaycastSelector.Result bestMatch = new RaycastSelector.Result(); - private final Vector2d minMax = new Vector2d(); + private final RaycastSelector.Result bestMatch; + private final Vector2d minMax; @Nullable private Vector3i blockPosition; + private RuntimeSelector() { + Objects.requireNonNull(RaycastSelector.this); + super(); + this.bestMatch = new RaycastSelector.Result(); + this.minMax = new Vector2d(); + } + @Override public void tick(@Nonnull CommandBuffer commandBuffer, @Nonnull Ref ref, float time, float runTime) { Vector3d position = RaycastSelector.this.selectTargetPosition(commandBuffer, ref); HeadRotation headRotation = commandBuffer.getComponent(ref, HeadRotation.getComponentType()); - Vector3d direction = new Vector3d(headRotation.getRotation().getYaw(), headRotation.getRotation().getPitch()); + Vector3d direction = Vector3dUtil.setYawPitch(headRotation.getRotation().yaw(), headRotation.getRotation().pitch(), new Vector3d()); IntSet blockTags = RaycastSelector.this.blockTag == null ? null : BlockType.getAssetMap().getIndexesForTag(RaycastSelector.this.blockTagIndex); if (SelectInteraction.SHOW_VISUAL_DEBUG) { - Vector3d dir = direction.clone().scale(RaycastSelector.this.distance); - com.hypixel.hytale.math.vector.Vector3f color = new com.hypixel.hytale.math.vector.Vector3f( + Vector3d dir = new Vector3d(direction).mul(RaycastSelector.this.distance); + Vector3f color = new Vector3f( (float)HashUtil.random(ref.getIndex(), this.hashCode(), 10L), (float)HashUtil.random(ref.getIndex(), this.hashCode(), 11L), (float)HashUtil.random(ref.getIndex(), this.hashCode(), 12L) ); - DebugUtils.addArrow(commandBuffer.getExternalData().getWorld(), position, dir, color, 5.0F, true); + DebugUtils.addArrow(commandBuffer.getExternalData().getWorld(), position, dir, color, 5.0F, DebugUtils.FLAG_FADE); } this.blockPosition = TargetUtil.getTargetBlock(commandBuffer.getExternalData().getWorld(), (id, fluidId) -> { @@ -153,21 +162,21 @@ public class RaycastSelector extends SelectorType { if (boundingBox != null) { TransformComponent transform = commandBuffer.getComponent(entityRef, TransformComponent.getComponentType()); Vector3d ePos = transform.getPosition(); - if (CollisionMath.intersectRayAABB(position, direction, ePos.getX(), ePos.getY(), ePos.getZ(), boundingBox.getBoundingBox(), this.minMax)) { + if (CollisionMath.intersectRayAABB(position, direction, ePos.x(), ePos.y(), ePos.z(), boundingBox.getBoundingBox(), this.minMax)) { double hitPosX = position.x + direction.x * this.minMax.x; double hitPosY = position.y + direction.y * this.minMax.x; double hitPosZ = position.z + direction.z * this.minMax.x; - double matchDistance = position.distanceSquaredTo(hitPosX, hitPosY, hitPosZ); + double matchDistance = position.distanceSquared(hitPosX, hitPosY, hitPosZ); if (!(matchDistance >= this.bestMatch.distance)) { this.bestMatch.match = entityRef; this.bestMatch.distance = matchDistance; - this.bestMatch.hitPosition.assign(hitPosX, hitPosY, hitPosZ, 0.0); + this.bestMatch.hitPosition.set(hitPosX, hitPosY, hitPosZ, 0.0); } } } }, e -> !e.equals(ref)); if (this.bestMatch.match != null && this.blockPosition != null) { - double blockDistance = position.distanceSquaredTo(this.blockPosition.x + 0.5, this.blockPosition.y + 0.5, this.blockPosition.z + 0.5); + double blockDistance = position.distanceSquared(this.blockPosition.x + 0.5, this.blockPosition.y + 0.5, this.blockPosition.z + 0.5); if (!(blockDistance < this.bestMatch.distance)) { this.blockPosition = null; } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/Selector.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/Selector.java index 32dd9e2c..dc41c1eb 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/Selector.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/Selector.java @@ -6,19 +6,19 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.function.consumer.TriIntConsumer; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector4d; public interface Selector { static void selectNearbyBlocks( @@ -77,7 +77,7 @@ public interface Selector { } else { Vector3d attackerPosition = transformComponent.getPosition(); Model model = modelComponent.getModel(); - Vector3d position = attackerPosition.clone().add(0.0, model.getEyeHeight(attacker, commandBuffer), 0.0); + Vector3d position = new Vector3d(attackerPosition).add(0.0, model.getEyeHeight(attacker, commandBuffer), 0.0); selectNearbyEntities(commandBuffer, position, range, consumer, filter); } } @@ -90,7 +90,7 @@ public interface Selector { @Nonnull Consumer> consumer, @Nullable Predicate> filter ) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); playerSpatialResource.getSpatialStructure().collect(position, range, results); SpatialResource, EntityStore> entitySpatialResource = componentAccessor.getResource(EntityModule.get().getEntitySpatialResourceType()); diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/StabSelector.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/StabSelector.java index 8cf707e5..fb20e30c 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/StabSelector.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/selector/StabSelector.java @@ -11,13 +11,9 @@ import com.hypixel.hytale.math.hitdetection.LineOfSightProvider; import com.hypixel.hytale.math.hitdetection.projection.OrthogonalProjectionProvider; import com.hypixel.hytale.math.hitdetection.view.DirectionViewProvider; import com.hypixel.hytale.math.iterator.BlockIterator; -import com.hypixel.hytale.math.matrix.Matrix4d; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector4d; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -31,9 +27,14 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Predicate; import javax.annotation.Nonnull; +import org.joml.Matrix4d; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector4d; public class StabSelector extends SelectorType { @Nonnull @@ -154,16 +155,25 @@ public class StabSelector extends SelectorType { private class RuntimeSelector implements Selector { @Nonnull - protected HitDetectionExecutor executor = new HitDetectionExecutor(); + protected HitDetectionExecutor executor; @Nonnull - protected Matrix4d modelMatrix = new Matrix4d(); + protected Matrix4d modelMatrix; @Nonnull - protected OrthogonalProjectionProvider projectionProvider = new OrthogonalProjectionProvider(); + protected OrthogonalProjectionProvider projectionProvider; @Nonnull - protected DirectionViewProvider viewProvider = new DirectionViewProvider(); + protected DirectionViewProvider viewProvider; protected float lastTime; protected double runTimeDeltaPercentageSum; + private RuntimeSelector() { + Objects.requireNonNull(StabSelector.this); + super(); + this.executor = new HitDetectionExecutor(); + this.modelMatrix = new Matrix4d(); + this.projectionProvider = new OrthogonalProjectionProvider(); + this.viewProvider = new DirectionViewProvider(); + } + @Override public void tick(@Nonnull CommandBuffer commandBuffer, @Nonnull Ref attacker, float time, float runTime) { ModelComponent modelComponent = commandBuffer.getComponent(attacker, ModelComponent.getComponentType()); @@ -180,9 +190,9 @@ public class StabSelector extends SelectorType { assert headRotationComponent != null; - double posX = position.getX(); - double posY = position.getY() + yOffset; - double posZ = position.getZ(); + double posX = position.x(); + double posY = position.y() + yOffset; + double posZ = position.z(); float delta = time - this.lastTime; this.lastTime = time; float runTimeDeltaPercentage = delta / runTime; @@ -197,9 +207,7 @@ public class StabSelector extends SelectorType { .setBottom(StabSelector.this.extendBottom) .setTop(StabSelector.this.extendTop) .setRotation(StabSelector.this.yawOffset, StabSelector.this.pitchOffset, StabSelector.this.rollOffset); - this.viewProvider - .setPosition(posX, posY, posZ) - .setDirection(headRotationComponent.getRotation().getYaw(), headRotationComponent.getRotation().getPitch()); + this.viewProvider.setPosition(posX, posY, posZ).setDirection(headRotationComponent.getRotation().yaw(), headRotationComponent.getRotation().pitch()); this.executor.setOrigin(posX, posY, posZ).setProjectionProvider(this.projectionProvider).setViewProvider(this.viewProvider); if (StabSelector.this.testLineOfSight) { World world = commandBuffer.getStore().getExternalData().getWorld(); @@ -247,18 +255,17 @@ public class StabSelector extends SelectorType { } if (SelectInteraction.SHOW_VISUAL_DEBUG) { - Matrix4d tmp = new Matrix4d(); Matrix4d matrix = new Matrix4d(); matrix.identity() .translate(posX, posY, posZ) - .rotateAxis(-headRotationComponent.getRotation().getYaw(), 0.0, 1.0, 0.0, tmp) - .rotateAxis(-headRotationComponent.getRotation().getPitch(), 1.0, 0.0, 0.0, tmp); + .rotate(headRotationComponent.getRotation().yaw(), 0.0, 1.0, 0.0) + .rotate(headRotationComponent.getRotation().pitch(), 1.0, 0.0, 0.0); Vector3f color = new Vector3f( (float)HashUtil.random(attacker.getIndex(), this.hashCode(), 10L), (float)HashUtil.random(attacker.getIndex(), this.hashCode(), 11L), (float)HashUtil.random(attacker.getIndex(), this.hashCode(), 12L) ); - DebugUtils.addFrustum(commandBuffer.getExternalData().getWorld(), matrix, this.projectionProvider.getMatrix(), color, 5.0F, true); + DebugUtils.addFrustum(commandBuffer.getExternalData().getWorld(), matrix, this.projectionProvider.getMatrix(), color, 5.0F, DebugUtils.FLAG_FADE); } this.runTimeDeltaPercentageSum += runTimeDeltaPercentage; diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/ChangeStatWithModifierInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/ChangeStatWithModifierInteraction.java index edda67b4..e878d2c6 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/ChangeStatWithModifierInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/ChangeStatWithModifierInteraction.java @@ -9,10 +9,8 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.item.config.ItemArmor; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; @@ -51,43 +49,38 @@ public class ChangeStatWithModifierInteraction extends ChangeStatBaseInteraction assert entityStatMapComponent != null; Int2FloatMap adjustedEntityStats = new Int2FloatOpenHashMap(this.entityStats); - Inventory inventory = null; - if (EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity) { - inventory = livingEntity.getInventory(); - } + InventoryComponent.Armor armorComponent = commandBuffer.getComponent(ref, InventoryComponent.Armor.getComponentType()); + if (armorComponent != null) { + ItemContainer armorContainer = armorComponent.getInventory(); - for (int index : adjustedEntityStats.keySet()) { - if (inventory != null) { - ItemContainer armorContainer = inventory.getArmor(); - if (armorContainer != null) { - float flatModifier = 0.0F; - float multiplierModifier = 0.0F; + for (int index : adjustedEntityStats.keySet()) { + float flatModifier = 0.0F; + float multiplierModifier = 0.0F; - for (short i = 0; i < armorContainer.getCapacity(); i++) { - ItemStack itemStack = armorContainer.getItemStack(i); - if (itemStack != null && !itemStack.isEmpty()) { - Item item = itemStack.getItem(); - if (item != null && item.getArmor() != null) { - Int2ObjectMap statModifierMap = item.getArmor().getInteractionModifier(this.interactionModifierId.toString()); - if (statModifierMap != null) { - StaticModifier statModifier = statModifierMap.get(index); - if (statModifier != null) { - if (statModifier.getCalculationType() == StaticModifier.CalculationType.ADDITIVE) { - flatModifier += statModifier.getAmount(); - } else { - multiplierModifier = statModifier.getAmount(); - } + for (short i = 0; i < armorContainer.getCapacity(); i++) { + ItemStack itemStack = armorContainer.getItemStack(i); + if (itemStack != null && !itemStack.isEmpty()) { + Item item = itemStack.getItem(); + if (item != null && item.getArmor() != null) { + Int2ObjectMap statModifierMap = item.getArmor().getInteractionModifier(this.interactionModifierId.toString()); + if (statModifierMap != null) { + StaticModifier statModifier = statModifierMap.get(index); + if (statModifier != null) { + if (statModifier.getCalculationType() == StaticModifier.CalculationType.ADDITIVE) { + flatModifier += statModifier.getAmount(); + } else { + multiplierModifier = statModifier.getAmount(); } } } } } - - float cost = this.entityStats.get(index); - cost += flatModifier; - cost *= Math.max(0.0F, 1.0F - multiplierModifier); - adjustedEntityStats.replace(index, cost); } + + float cost = this.entityStats.get(index); + cost += flatModifier; + cost *= Math.max(0.0F, 1.0F - multiplierModifier); + adjustedEntityStats.replace(index, cost); } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DamageEntityInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DamageEntityInteraction.java index 6087f031..fbf3158e 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DamageEntityInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DamageEntityInteraction.java @@ -12,22 +12,19 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector4d; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.EntitySnapshot; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.entity.ItemUtils; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.knockback.KnockbackComponent; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.io.NetworkSerializable; @@ -63,6 +60,9 @@ import java.util.Map.Entry; import java.util.stream.IntStream; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; +import org.joml.Vector4d; public class DamageEntityInteraction extends Interaction { @Nonnull @@ -372,7 +372,7 @@ public class DamageEntityInteraction extends Interaction { float angleBetween = TrigMathUtil.atan2(attackerPos.x - targetPos.x, attackerPos.z - targetPos.z); int nextLabel = 1; if (this.angledDamage != null) { - float angleBetweenRotation = MathUtil.wrapAngle(angleBetween + (float) Math.PI - targetSnapshot.getBodyRotation().getYaw()); + float angleBetweenRotation = MathUtil.wrapAngle(angleBetween + (float) Math.PI - targetSnapshot.getBodyRotation().yaw()); for (int i = 0; i < this.angledDamage.length; i++) { DamageEntityInteraction.AngledDamage angledDamage = this.angledDamage[i]; @@ -403,11 +403,11 @@ public class DamageEntityInteraction extends Interaction { : metaStore.getMetaObject(SEQUENTIAL_HITS); Object2FloatMap damage = damageCalculator.calculateDamage(this.getRunTime()); HeadRotation attackerHeadRotationComponent = commandBuffer.getComponent(attackerRef, HeadRotation.getComponentType()); - Vector3f attackerDirection; + Rotation3fc attackerDirection; if (attackerHeadRotationComponent != null) { attackerDirection = attackerHeadRotationComponent.getRotation(); } else { - attackerDirection = Vector3f.ZERO; + attackerDirection = Rotation3f.IDENTITY; } if (damage != null && !damage.isEmpty()) { @@ -425,16 +425,13 @@ public class DamageEntityInteraction extends Interaction { } Knockback knockback = damageEffects.getKnockback(); - knockbackComponent.setVelocity(knockback.calculateVector(attackerPos, attackerDirection.getYaw(), targetPos).scale(knockbackMultiplier[0])); + knockbackComponent.setVelocity(knockback.calculateVector(attackerPos, attackerDirection.yaw(), targetPos).mul(knockbackMultiplier[0])); knockbackComponent.setVelocityType(knockback.getVelocityType()); knockbackComponent.setVelocityConfig(knockback.getVelocityConfig()); knockbackComponent.setDuration(knockback.getDuration()); } - Player attackerPlayerComponent = commandBuffer.getComponent(attackerRef, Player.getComponentType()); - ItemStack itemInHand = attackerPlayerComponent != null && !attackerPlayerComponent.canApplyItemStackPenalties(attackerRef, commandBuffer) - ? null - : context.getHeldItem(); + ItemStack itemInHand = ItemUtils.canApplyItemStackPenalties(attackerRef, commandBuffer) ? context.getHeldItem() : null; Damage[] hits = DamageCalculatorSystems.queueDamageCalculator( commandBuffer.getExternalData().getWorld(), damage, targetRef, context.getCommandBuffer(), source, itemInHand ); @@ -458,7 +455,7 @@ public class DamageEntityInteraction extends Interaction { if (hit != null) { damageEvent.putMetaObject(Damage.HIT_LOCATION, hit); float hitAngleRad = TrigMathUtil.atan2(attackerPos.x - hit.x, attackerPos.z - hit.z); - hitAngleRad = MathUtil.wrapAngle(hitAngleRad - attackerDirection.getYaw()); + hitAngleRad = MathUtil.wrapAngle(hitAngleRad - attackerDirection.yaw()); float hitAngleDeg = hitAngleRad * (180.0F / (float)Math.PI); damageEvent.putMetaObject(Damage.HIT_ANGLE, hitAngleDeg); } @@ -492,57 +489,53 @@ public class DamageEntityInteraction extends Interaction { .reduce(1.0, (a, b) -> a * b); } - if (EntityUtils.getEntity(attackerRef, componentAccessor) instanceof LivingEntity livingEntity) { - Inventory inventory = livingEntity.getInventory(); - if (inventory != null) { - ItemContainer armorContainer = inventory.getArmor(); - if (armorContainer != null) { - float knockbackEnhancementModifier = 1.0F; + InventoryComponent.Armor armorComponent = componentAccessor.getComponent(attackerRef, InventoryComponent.Armor.getComponentType()); + if (armorComponent != null) { + ItemContainer armorContainer = armorComponent.getInventory(); + float knockbackEnhancementModifier = 1.0F; - for (short i = 0; i < armorContainer.getCapacity(); i++) { - ItemStack itemStack = armorContainer.getItemStack(i); - if (itemStack != null && !itemStack.isEmpty()) { - Item item = itemStack.getItem(); - if (item.getArmor() != null) { - Map armorDamageEnhancementMap = item.getArmor().getDamageEnhancementValues(); + for (short i = 0; i < armorContainer.getCapacity(); i++) { + ItemStack itemStack = armorContainer.getItemStack(i); + if (itemStack != null && !itemStack.isEmpty()) { + Item item = itemStack.getItem(); + if (item.getArmor() != null) { + Map armorDamageEnhancementMap = item.getArmor().getDamageEnhancementValues(); - for (DamageCause damageCause : damage.keySet()) { - if (armorDamageEnhancementMap != null) { - StaticModifier[] armorDamageEnhancementValue = armorDamageEnhancementMap.get(damageCause); - if (armorDamageEnhancementValue != null) { - for (StaticModifier staticModifier : armorDamageEnhancementValue) { - if (staticModifier.getCalculationType() == StaticModifier.CalculationType.ADDITIVE) { - armorDamageModifiers[0] += staticModifier.getAmount(); - } else { - armorDamageModifiers[1] += staticModifier.getAmount(); - } - } - } - } - - Map knockbackEnhancements = item.getArmor().getKnockbackEnhancements(); - if (knockbackEnhancements != null) { - knockbackEnhancementModifier += knockbackEnhancements.get(damageCause); - } - } - - StaticModifier[] damageClassModifier = item.getArmor().getDamageClassEnhancement().get(damageClass); - if (damageClassModifier != null) { - for (StaticModifier modifier : damageClassModifier) { - if (modifier.getCalculationType() == StaticModifier.CalculationType.ADDITIVE) { - armorDamageModifiers[0] += modifier.getAmount(); + for (DamageCause damageCause : damage.keySet()) { + if (armorDamageEnhancementMap != null) { + StaticModifier[] armorDamageEnhancementValue = armorDamageEnhancementMap.get(damageCause); + if (armorDamageEnhancementValue != null) { + for (StaticModifier staticModifier : armorDamageEnhancementValue) { + if (staticModifier.getCalculationType() == StaticModifier.CalculationType.ADDITIVE) { + armorDamageModifiers[0] += staticModifier.getAmount(); } else { - armorDamageModifiers[1] += modifier.getAmount(); + armorDamageModifiers[1] += staticModifier.getAmount(); } } } } + + Map knockbackEnhancements = item.getArmor().getKnockbackEnhancements(); + if (knockbackEnhancements != null) { + knockbackEnhancementModifier += knockbackEnhancements.get(damageCause); + } + } + + StaticModifier[] damageClassModifier = item.getArmor().getDamageClassEnhancement().get(damageClass); + if (damageClassModifier != null) { + for (StaticModifier modifier : damageClassModifier) { + if (modifier.getCalculationType() == StaticModifier.CalculationType.ADDITIVE) { + armorDamageModifiers[0] += modifier.getAmount(); + } else { + armorDamageModifiers[1] += modifier.getAmount(); + } + } } } - - knockbackMultiplier[0] *= knockbackEnhancementModifier; } } + + knockbackMultiplier[0] *= knockbackEnhancementModifier; } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DestroyConditionInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DestroyConditionInteraction.java deleted file mode 100644 index b2dbfec1..00000000 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DestroyConditionInteraction.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.hypixel.hytale.server.core.modules.interaction.interaction.config.server; - -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.component.CommandBuffer; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.protocol.InteractionState; -import com.hypixel.hytale.protocol.InteractionType; -import com.hypixel.hytale.protocol.WaitForDataFrom; -import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.inventory.ItemStack; -import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; -import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.BreakValidatedBlockState; -import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -@Deprecated -public class DestroyConditionInteraction extends SimpleBlockInteraction { - public static final BuilderCodec CODEC = BuilderCodec.builder( - DestroyConditionInteraction.class, DestroyConditionInteraction::new, SimpleBlockInteraction.CODEC - ) - .documentation("Checks if the target block is destroyable") - .build(); - - @Nonnull - @Override - public WaitForDataFrom getWaitForDataFrom() { - return WaitForDataFrom.Server; - } - - @Override - protected void interactWithBlock( - @Nonnull World world, - @Nonnull CommandBuffer commandBuffer, - @Nonnull InteractionType type, - @Nonnull InteractionContext context, - @Nullable ItemStack itemInHand, - @Nonnull Vector3i pos, - @Nonnull CooldownHandler cooldownHandler - ) { - Ref ref = context.getEntity(); - BlockState state = world.getChunk(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)).getState(pos.x, pos.y, pos.z); - if (state instanceof BreakValidatedBlockState && !((BreakValidatedBlockState)state).canDestroy(ref, commandBuffer)) { - context.getState().state = InteractionState.Failed; - } else { - context.getState().state = InteractionState.Finished; - } - } - - @Override - protected void simulateInteractWithBlock( - @Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock - ) { - } -} diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DoorInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DoorInteraction.java index a30ed8c8..5e2d1002 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DoorInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/DoorInteraction.java @@ -3,24 +3,32 @@ package com.hypixel.hytale.server.core.modules.interaction.interaction.config.se import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockGathering; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.PhysicsDropType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.SoftBlockDropType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; +import com.hypixel.hytale.server.core.modules.interaction.BlockHarvestUtils; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; @@ -31,8 +39,11 @@ import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class DoorInteraction extends SimpleBlockInteraction { private static final String OPEN_DOOR_IN = "OpenDoorIn"; @@ -88,8 +99,8 @@ public class DoorInteraction extends SimpleBlockInteraction { context.getState().state = InteractionState.Failed; } else { DoorInteraction.DoorState stateDoubleDoor = getOppositeDoorState(doorState); - BlockType interactionBlockState = activateDoor(world, blockType, targetBlock, doorState, checkResult); - boolean doubleDoor = this.checkForDoubleDoor(world, targetBlock, blockType, rotation, checkResult, stateDoubleDoor); + BlockType interactionBlockState = activateDoor(world, commandBuffer, blockType, targetBlock, doorState, checkResult); + boolean doubleDoor = this.checkForDoubleDoor(world, commandBuffer, targetBlock, blockType, rotation, checkResult, stateDoubleDoor); if (interactionBlockState != null) { Vector3d pos = new Vector3d(); int hitboxTypeIndex = BlockType.getAssetMap().getAsset(blockType.getItem().getId()).getHitboxTypeIndex(); @@ -105,7 +116,7 @@ public class DoorInteraction extends SimpleBlockInteraction { pos.add(hitbox.middleX(), hitbox.middleY(), hitbox.middleZ()); } - pos.add(targetBlock); + pos.add(targetBlock.x, targetBlock.y, targetBlock.z); SoundUtil.playSoundEvent3d(ref, interactionBlockState.getInteractionSoundEventIndex(), pos, commandBuffer); } } @@ -120,6 +131,7 @@ public class DoorInteraction extends SimpleBlockInteraction { private boolean checkForDoubleDoor( @Nonnull World world, + @Nonnull CommandBuffer commandBuffer, @Nonnull Vector3i blockPosition, @Nonnull BlockType blockType, int rotation, @@ -132,7 +144,7 @@ public class DoorInteraction extends SimpleBlockInteraction { } else { boolean otherDoorIsHorizontal = isHorizontalDoor(doorToOpen.blockType); DoorInteraction.DoorState stateForDoubleDoor = otherDoorIsHorizontal ? fromState : getOppositeDoorState(fromState); - activateDoor(world, doorToOpen.blockType, doorToOpen.blockPosition, doorToOpen.doorState, stateForDoubleDoor); + activateDoor(world, commandBuffer, doorToOpen.blockType, doorToOpen.blockPosition, doorToOpen.doorState, stateForDoubleDoor); return true; } } @@ -200,6 +212,7 @@ public class DoorInteraction extends SimpleBlockInteraction { @Nullable private static BlockType activateDoor( @Nonnull World world, + @Nonnull CommandBuffer commandBuffer, @Nonnull BlockType blockType, @Nonnull Vector3i blockPosition, @Nonnull DoorInteraction.DoorState fromState, @@ -209,6 +222,14 @@ public class DoorInteraction extends SimpleBlockInteraction { int rotationIndex = chunk.getRotationIndex(blockPosition.x, blockPosition.y, blockPosition.z); BlockBoundingBoxes oldHitbox = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); String interactionStateToSend = getInteractionState(fromState, doorState); + BlockType newState = blockType.getBlockForState(interactionStateToSend); + if (newState != null) { + BlockBoundingBoxes newHitbox = BlockBoundingBoxes.getAssetMap().getAsset(newState.getHitboxTypeIndex()); + if (newHitbox != null) { + breakSoftBlocksInHitbox(world, commandBuffer, blockPosition, newHitbox.get(rotationIndex)); + } + } + world.setBlockInteractionState(blockPosition, blockType, interactionStateToSend); BlockType currentBlockType = world.getBlockType(blockPosition); if (currentBlockType == null) { @@ -234,6 +255,50 @@ public class DoorInteraction extends SimpleBlockInteraction { } } + private static void breakSoftBlocksInHitbox( + @Nonnull World world, + @Nonnull CommandBuffer commandBuffer, + @Nonnull Vector3i basePosition, + @Nonnull BlockBoundingBoxes.RotatedVariantBoxes hitbox + ) { + FillerBlockUtil.forEachFillerBlock(hitbox, (x, y, z) -> { + if (x != 0 || y != 0 || z != 0) { + int worldX = basePosition.x + x; + int worldY = basePosition.y + y; + int worldZ = basePosition.z + z; + WorldChunk existingChunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(worldX, worldZ)); + if (existingChunk != null) { + BlockType existingType = existingChunk.getBlockType(worldX, worldY, worldZ); + if (existingType != null && existingType != BlockType.EMPTY && existingType.getMaterial() == BlockMaterial.Empty) { + int settings = 1312; + existingChunk.breakBlock(worldX, worldY, worldZ, settings); + String itemId = null; + String dropListId = null; + BlockGathering gathering = existingType.getGathering(); + if (gathering != null) { + PhysicsDropType physics = gathering.getPhysics(); + SoftBlockDropType soft = gathering.getSoft(); + if (physics != null) { + itemId = physics.getItemId(); + dropListId = physics.getDropListId(); + } else if (soft != null) { + itemId = soft.getItemId(); + dropListId = soft.getDropListId(); + } + } + + List itemStacks = BlockHarvestUtils.getDrops(existingType, 1, itemId, dropListId); + if (!itemStacks.isEmpty()) { + Vector3d dropPosition = new Vector3d(worldX + 0.5, worldY, worldZ + 0.5); + Holder[] itemEntityHolders = ItemComponent.generateItemDrops(commandBuffer, itemStacks, dropPosition, Rotation3f.IDENTITY); + commandBuffer.addEntities(itemEntityHolders, AddReason.SPAWN); + } + } + } + } + }); + } + @Nullable private static DoorInteraction.DoorInfo getDoubleDoor( @Nonnull ChunkAccessor chunkAccessor, @@ -258,7 +323,7 @@ public class DoorInteraction extends SimpleBlockInteraction { BlockBoundingBoxes.RotatedVariantBoxes baseBoxes = blockBoundingBoxes.get(Rotation.None, Rotation.None, Rotation.None); Vector3i offset = new Vector3i((int)baseBoxes.getBoundingBox().getMax().x * 2 - 1, 0, 0); Rotation rotationToCheck = RotationTuple.get(rotation).yaw(); - Vector3i blockPosition = worldPosition.clone().add(MathUtil.rotateVectorYAxis(offset, rotationToCheck.getDegrees(), false)); + Vector3i blockPosition = new Vector3i(worldPosition).add(MathUtil.rotateVectorYAxis(offset, rotationToCheck.getDegrees(), false)); DoorInteraction.DoorInfo matchingDoor = getDoorAtPosition( chunkAccessor, blockPosition.x, blockPosition.y, blockPosition.z, rotationToCheck.flip() ); @@ -334,7 +399,7 @@ public class DoorInteraction extends SimpleBlockInteraction { private static boolean isInFrontOfDoor(@Nonnull Vector3i blockPosition, @Nullable Rotation doorRotationYaw, @Nonnull Vector3d playerPosition) { double doorRotationRad = Math.toRadians(doorRotationYaw != null ? doorRotationYaw.getDegrees() : 0.0); Vector3d doorRotationVector = new Vector3d(TrigMathUtil.sin(doorRotationRad), 0.0, TrigMathUtil.cos(doorRotationRad)); - Vector3d direction = Vector3d.directionTo(blockPosition, playerPosition); + Vector3d direction = Vector3dUtil.directionTo(new Vector3d(blockPosition).add(0.5, 0.5, 0.5), playerPosition); return direction.dot(doorRotationVector) < 0.0; } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/EquipItemInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/EquipItemInteraction.java index ab309ac1..c5ae8080 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/EquipItemInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/EquipItemInteraction.java @@ -8,10 +8,8 @@ import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.item.config.ItemArmor; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction; @@ -41,17 +39,17 @@ public class EquipItemInteraction extends SimpleInstantInteraction { assert commandBuffer != null; Ref ref = context.getEntity(); - if (EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity) { - Inventory var15 = livingEntity.getInventory(); - byte activeSlot = context.getHeldItemSlot(); - ItemStack itemInHand = context.getHeldItem(); - if (itemInHand != null) { - Item item = itemInHand.getItem(); - if (item != null) { - ItemArmor armor = item.getArmor(); - if (armor != null) { + byte activeSlot = context.getHeldItemSlot(); + ItemStack itemInHand = context.getHeldItem(); + if (itemInHand != null) { + Item item = itemInHand.getItem(); + if (item != null) { + ItemArmor armor = item.getArmor(); + if (armor != null) { + InventoryComponent.Armor armorComponent = commandBuffer.getComponent(ref, InventoryComponent.Armor.getComponentType()); + if (armorComponent != null) { short slotId = (short)armor.getArmorSlot().ordinal(); - ItemContainer armorContainer = var15.getArmor(); + ItemContainer armorContainer = armorComponent.getInventory(); if (slotId <= armorContainer.getCapacity()) { MoveTransaction stackTransaction = context.getHeldItemContainer() .moveItemStackFromSlot(activeSlot, itemInHand.getQuantity(), armorContainer); diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/IncreaseBackpackCapacityInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/IncreaseBackpackCapacityInteraction.java index 0d25322d..5050a0e6 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/IncreaseBackpackCapacityInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/IncreaseBackpackCapacityInteraction.java @@ -4,15 +4,16 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.WaitForDataFrom; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; @@ -37,12 +38,21 @@ public class IncreaseBackpackCapacityInteraction extends SimpleInstantInteractio @Override protected void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) { Ref ref = context.getEntity(); - Player playerComponent = context.getCommandBuffer().getComponent(ref, Player.getComponentType()); - if (playerComponent != null) { - Inventory inventory = playerComponent.getInventory(); - short newBackpackCapacity = (short)(inventory.getBackpack().getCapacity() + this.capacity); - inventory.resizeBackpack(newBackpackCapacity, null); - playerComponent.sendMessage(Message.translation("server.commands.inventory.backpack.size").param("capacity", inventory.getBackpack().getCapacity())); + CommandBuffer commandBuffer = context.getCommandBuffer(); + + assert commandBuffer != null; + + InventoryComponent.Backpack backpackInventoryComponent = commandBuffer.getComponent(ref, InventoryComponent.Backpack.getComponentType()); + if (backpackInventoryComponent != null) { + short newBackpackCapacity = (short)(backpackInventoryComponent.getInventory().getCapacity() + this.capacity); + backpackInventoryComponent.resize(newBackpackCapacity, null); + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent != null) { + playerRefComponent.sendMessage( + Message.translation("server.commands.inventory.backpack.size").param("capacity", backpackInventoryComponent.getInventory().getCapacity()) + ); + } + context.getHeldItemContainer().removeItemStackFromSlot(context.getHeldItemSlot(), context.getHeldItem(), 1); } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/LaunchPadInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/LaunchPadInteraction.java index 84173b11..ae03a3b3 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/LaunchPadInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/LaunchPadInteraction.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.ChangeVelocityType; import com.hypixel.hytale.protocol.InteractionType; @@ -24,9 +23,11 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.meta.state.LaunchPad; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class LaunchPadInteraction extends SimpleBlockInteraction { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -68,11 +69,11 @@ public class LaunchPadInteraction extends SimpleBlockInteraction { velocityComponent.addInstruction( new Vector3d(launchPadState.getVelocityX(), launchPadState.getVelocityY(), launchPadState.getVelocityZ()), null, ChangeVelocityType.Set ); - Vector3d particlePos = targetBlock.toVector3d().add(0.5, 0.5, 0.5); + Vector3d particlePos = Vector3iUtil.toVector3d(targetBlock).add(0.5, 0.5, 0.5); SpatialResource, EntityStore> playerSpatialResource = commandBuffer.getResource( EntityModule.get().getPlayerSpatialResourceType() ); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(particlePos, 75.0, results); ParticleUtil.spawnParticleEffect("Splash", particlePos, results, commandBuffer); } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/LaunchProjectileInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/LaunchProjectileInteraction.java index a8a010af..75264842 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/LaunchProjectileInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/LaunchProjectileInteraction.java @@ -8,15 +8,15 @@ import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.gameplay.BrokenPenalties; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.projectile.config.Projectile; import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionContext; +import com.hypixel.hytale.server.core.entity.ItemUtils; import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.ProjectileComponent; @@ -35,6 +35,7 @@ import com.hypixel.hytale.server.core.util.TargetUtil; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; @Deprecated(forRemoval = true) public class LaunchProjectileInteraction extends SimpleInstantInteraction implements BallisticDataProvider { @@ -73,7 +74,7 @@ public class LaunchProjectileInteraction extends SimpleInstantInteraction implem if (EntityUtils.getEntity(sourceRef, commandBuffer) instanceof LivingEntity attackerLivingEntity) { Transform lookVec = TargetUtil.getLook(sourceRef, commandBuffer); Vector3d lookPosition = lookVec.getPosition(); - Vector3f lookRotation = lookVec.getRotation(); + Rotation3f lookRotation = lookVec.getRotation(); UUIDComponent sourceUuidComponent = commandBuffer.getComponent(sourceRef, UUIDComponent.getComponentType()); if (sourceUuidComponent != null) { UUID sourceUuid = sourceUuidComponent.getUuid(); @@ -91,14 +92,12 @@ public class LaunchProjectileInteraction extends SimpleInstantInteraction implem } } - projectileComponent.shoot( - holder, sourceUuid, lookPosition.getX(), lookPosition.getY(), lookPosition.getZ(), lookRotation.getYaw(), lookRotation.getPitch() - ); + projectileComponent.shoot(holder, sourceUuid, lookPosition.x(), lookPosition.y(), lookPosition.z(), lookRotation.yaw(), lookRotation.pitch()); commandBuffer.addEntity(holder, AddReason.SPAWN); ItemStack itemInHand = context.getHeldItem(); if (itemInHand != null && !itemInHand.isEmpty()) { Item item = itemInHand.getItem(); - if (attackerLivingEntity.canDecreaseItemStackDurability(sourceRef, commandBuffer) && !itemInHand.isUnbreakable() && item.getWeapon() != null) { + if (ItemUtils.canDecreaseItemStackDurability(sourceRef, commandBuffer) && !itemInHand.isUnbreakable() && item.getWeapon() != null) { Inventory inventory = attackerLivingEntity.getInventory(); ItemContainer section = inventory.getSectionById(context.getHeldItemSectionId()); if (section != null) { diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/ModifyInventoryInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/ModifyInventoryInteraction.java index ee60cbd9..afbd92b0 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/ModifyInventoryInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/ModifyInventoryInteraction.java @@ -15,6 +15,7 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; @@ -121,7 +122,7 @@ public class ModifyInventoryInteraction extends SimpleInstantInteraction { } else { boolean hasRequiredGameMode = this.requiredGameMode == null || playerComponent.getGameMode() == this.requiredGameMode; if (hasRequiredGameMode) { - CombinedItemContainer combinedHotbarFirst = playerComponent.getInventory().getCombinedBackpackStorageHotbarFirst(); + CombinedItemContainer combinedHotbarFirst = InventoryComponent.getCombined(commandBuffer, ref, InventoryComponent.HOTBAR_STORAGE_BACKPACK); if (this.itemToRemove != null) { ItemStackTransaction removeItemStack = combinedHotbarFirst.removeItemStack(this.itemToRemove, true, true); if (!removeItemStack.succeeded()) { @@ -166,13 +167,13 @@ public class ModifyInventoryInteraction extends SimpleInstantInteraction { boolean isTransformation = this.brokenItem != null && !this.brokenItem.equals(item.getItemId()); boolean shouldNotify = this.notifyOnBreak != null ? this.notifyOnBreak : !isTransformation; if (justBroke && shouldNotify) { - Message itemNameMessage = Message.translation(item.getItem().getTranslationKey()); - String messageKey = this.notifyOnBreakMessage != null ? this.notifyOnBreakMessage : "server.general.repair.itemBroken"; - playerComponent.sendMessage(Message.translation(messageKey).param("itemName", itemNameMessage).color("#ff5555")); PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); if (playerRefComponent != null) { + Message itemNameMessage = Message.translation(item.getItem().getTranslationKey()); + String messageKey = this.notifyOnBreakMessage != null ? this.notifyOnBreakMessage : "server.general.repair.itemBroken"; + playerRefComponent.sendMessage(Message.translation(messageKey).param("itemName", itemNameMessage).color("#ff5555")); int soundEventIndex = TempAssetIdUtil.getSoundEventIndex("SFX_Item_Break"); - SoundUtil.playSoundEvent2dToPlayer(playerRefComponent, soundEventIndex, SoundCategory.SFX); + SoundUtil.playSoundEvent2dToPlayer(playerRefComponent, soundEventIndex, SoundCategory.UI); } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/OpenContainerInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/OpenContainerInteraction.java index 66167f3a..e7b54f33 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/OpenContainerInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/OpenContainerInteraction.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.packets.interface_.Page; import com.hypixel.hytale.server.core.Message; @@ -16,18 +14,22 @@ import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerBlockWindow; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.block.components.ItemContainerBlock; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerState; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Map; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class OpenContainerInteraction extends SimpleBlockInteraction { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -35,6 +37,8 @@ public class OpenContainerInteraction extends SimpleBlockInteraction { ) .documentation("Opens the container of the block currently being interacted with.") .build(); + public static final String OPEN_WINDOW = "OpenWindow"; + public static final String CLOSE_WINDOW = "CloseWindow"; @Override protected void interactWithBlock( @@ -50,73 +54,82 @@ public class OpenContainerInteraction extends SimpleBlockInteraction { Store store = ref.getStore(); Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); if (playerComponent != null) { - BlockState container = world.getState(pos.x, pos.y, pos.z, true); - if (container instanceof ItemContainerState itemContainerState) { - BlockType blockType = world.getBlockType(pos.x, pos.y, pos.z); - if (itemContainerState.isAllowViewing() && itemContainerState.canOpen(ref, commandBuffer)) { - UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent != null) { + ChunkStore chunkStore = world.getChunkStore(); + Ref chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); + if (chunkRef != null) { + BlockComponentChunk blockComponentChunk = chunkStore.getStore().getComponent(chunkRef, BlockComponentChunk.getComponentType()); + if (blockComponentChunk != null) { + Ref blockRef = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(pos.x, pos.y, pos.z)); + if (blockRef != null) { + ItemContainerBlock itemContainerBlock = chunkStore.getStore().getComponent(blockRef, ItemContainerBlock.getComponentType()); + if (itemContainerBlock == null) { + playerRefComponent.sendMessage( + Message.translation("server.interactions.invalidBlockState") + .param("interaction", this.getClass().getSimpleName()) + .param("blockState", chunkStore.getStore().getArchetype(blockRef).toString()) + ); + } else { + BlockType blockType = world.getBlockType(pos.x, pos.y, pos.z); + UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType()); - assert uuidComponent != null; + assert uuidComponent != null; - UUID uuid = uuidComponent.getUuid(); - WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); - ContainerBlockWindow window = new ContainerBlockWindow( - pos.x, pos.y, pos.z, chunk.getRotationIndex(pos.x, pos.y, pos.z), blockType, itemContainerState.getItemContainer() - ); - Map windows = itemContainerState.getWindows(); - if (windows.putIfAbsent(uuid, window) == null) { - if (playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, window)) { - window.registerCloseEvent(event -> { - windows.remove(uuid, window); - BlockType currentBlockType = world.getBlockType(pos); - if (windows.isEmpty()) { - world.setBlockInteractionState(pos, currentBlockType, "CloseWindow"); - } + UUID uuid = uuidComponent.getUuid(); + WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); + ContainerBlockWindow window = new ContainerBlockWindow( + pos.x, pos.y, pos.z, chunk.getRotationIndex(pos.x, pos.y, pos.z), blockType, itemContainerBlock.getItemContainer() + ); + Map windows = itemContainerBlock.getWindows(); + if (windows.putIfAbsent(uuid, window) == null) { + if (playerComponent.getPageManager().setPageWithWindows(ref, store, Page.Bench, true, window)) { + window.registerCloseEvent(event -> { + windows.remove(uuid, window); + BlockType currentBlockType = world.getBlockType(pos); + if (windows.isEmpty()) { + world.setBlockInteractionState(pos, currentBlockType, "CloseWindow"); + } - BlockType interactionStatex = currentBlockType.getBlockForState("CloseWindow"); - if (interactionStatex != null) { - int soundEventIndexx = interactionStatex.getInteractionSoundEventIndex(); - if (soundEventIndexx != 0) { - int rotationIndexx = chunk.getRotationIndex(pos.x, pos.y, pos.z); - Vector3d soundPosx = new Vector3d(); - blockType.getBlockCenter(rotationIndexx, soundPosx); - soundPosx.add(pos); - SoundUtil.playSoundEvent3d(ref, soundEventIndexx, soundPosx, commandBuffer); + BlockType interactionStatex = currentBlockType.getBlockForState("CloseWindow"); + if (interactionStatex != null) { + int soundEventIndexx = interactionStatex.getInteractionSoundEventIndex(); + if (soundEventIndexx != 0) { + int rotationIndexx = chunk.getRotationIndex(pos.x, pos.y, pos.z); + Vector3d soundPosx = new Vector3d(); + blockType.getBlockCenter(rotationIndexx, soundPosx); + soundPosx.add(pos.x, pos.y, pos.z); + SoundUtil.playSoundEvent3d(ref, soundEventIndexx, soundPosx, commandBuffer); + } + } + }); + if (windows.size() == 1) { + world.setBlockInteractionState(pos, blockType, "OpenWindow"); + } + + BlockType interactionState = blockType.getBlockForState("OpenWindow"); + if (interactionState == null) { + return; + } + + int soundEventIndex = interactionState.getInteractionSoundEventIndex(); + if (soundEventIndex == 0) { + return; + } + + int rotationIndex = chunk.getRotationIndex(pos.x, pos.y, pos.z); + Vector3d soundPos = new Vector3d(); + blockType.getBlockCenter(rotationIndex, soundPos); + soundPos.add(pos.x, pos.y, pos.z); + SoundUtil.playSoundEvent3d(ref, soundEventIndex, soundPos, commandBuffer); + } else { + windows.remove(uuid, window); } } - }); - if (windows.size() == 1) { - world.setBlockInteractionState(pos, blockType, "OpenWindow"); } - - BlockType interactionState = blockType.getBlockForState("OpenWindow"); - if (interactionState == null) { - return; - } - - int soundEventIndex = interactionState.getInteractionSoundEventIndex(); - if (soundEventIndex == 0) { - return; - } - - int rotationIndex = chunk.getRotationIndex(pos.x, pos.y, pos.z); - Vector3d soundPos = new Vector3d(); - blockType.getBlockCenter(rotationIndex, soundPos); - soundPos.add(pos); - SoundUtil.playSoundEvent3d(ref, soundEventIndex, soundPos, commandBuffer); - } else { - windows.remove(uuid, window); } } - - itemContainerState.onOpen(ref, world, store); } - } else { - playerComponent.sendMessage( - Message.translation("server.interactions.invalidBlockState") - .param("interaction", this.getClass().getSimpleName()) - .param("blockState", container != null ? container.getClass().getSimpleName() : "null") - ); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/OpenCustomUIInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/OpenCustomUIInteraction.java index c7f09bfd..da5efa03 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/OpenCustomUIInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/OpenCustomUIInteraction.java @@ -12,7 +12,6 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.entity.InteractionContext; @@ -27,8 +26,6 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.function.Function; @@ -89,58 +86,6 @@ public class OpenCustomUIInteraction extends SimpleInstantInteraction { registerCustomPageSupplier(plugin, tClass, id, (ref, componentAccessor, playerRef, context) -> supplier.apply(playerRef)); } - @Deprecated - public static void registerBlockCustomPage( - @Nonnull PluginBase plugin, - Class tClass, - String id, - @Nonnull Class stateClass, - @Nonnull OpenCustomUIInteraction.BlockCustomPageSupplier blockSupplier - ) { - registerBlockCustomPage(plugin, tClass, id, stateClass, blockSupplier, false); - } - - @Deprecated - public static void registerBlockCustomPage( - @Nonnull PluginBase plugin, - Class tClass, - String id, - @Nonnull Class stateClass, - @Nonnull OpenCustomUIInteraction.BlockCustomPageSupplier blockSupplier, - boolean createState - ) { - OpenCustomUIInteraction.CustomPageSupplier supplier = (ref, componentAccessor, playerRef, context) -> { - BlockPosition targetBlock = context.getTargetBlock(); - if (targetBlock == null) { - return null; - } else { - Store store = ref.getStore(); - World world = store.getExternalData().getWorld(); - BlockState state = world.getState(targetBlock.x, targetBlock.y, targetBlock.z, true); - if (state == null) { - if (createState) { - WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z)); - state = BlockStateModule.get() - .createBlockState( - stateClass, - chunk, - new Vector3i(targetBlock.x, targetBlock.y, targetBlock.z), - chunk.getBlockType(targetBlock.x, targetBlock.y, targetBlock.z) - ); - chunk.setState(targetBlock.x, targetBlock.y, targetBlock.z, state); - } - - if (state == null) { - return null; - } - } - - return stateClass.isInstance(state) ? blockSupplier.tryCreate(playerRef, stateClass.cast(state)) : null; - } - }; - registerCustomPageSupplier(plugin, tClass, id, supplier); - } - public static void registerBlockEntityCustomPage( @Nonnull PluginBase plugin, Class tClass, String id, @Nonnull OpenCustomUIInteraction.BlockEntityCustomPageSupplier blockSupplier ) { @@ -200,11 +145,6 @@ public class OpenCustomUIInteraction extends SimpleInstantInteraction { registerCustomPageSupplier(plugin, tClass, id, supplier); } - @FunctionalInterface - public interface BlockCustomPageSupplier { - CustomUIPage tryCreate(@Nonnull PlayerRef var1, @Nonnull T var2); - } - @FunctionalInterface public interface BlockEntityCustomPageSupplier { CustomUIPage tryCreate(@Nonnull PlayerRef var1, @Nonnull Ref var2); diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/RefillContainerInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/RefillContainerInteraction.java index a487f57c..5c746b91 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/RefillContainerInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/RefillContainerInteraction.java @@ -9,9 +9,7 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.iterator.BlockIterator; -import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionSyncData; import com.hypixel.hytale.protocol.InteractionType; @@ -21,8 +19,9 @@ import com.hypixel.hytale.server.core.asset.type.fluid.FluidTicker; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; import com.hypixel.hytale.server.core.inventory.transaction.ItemStackSlotTransaction; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; @@ -45,6 +44,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; public class RefillContainerInteraction extends SimpleInstantInteraction { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -117,147 +117,142 @@ public class RefillContainerInteraction extends SimpleInstantInteraction { if (playerComponent == null) { state.state = InteractionState.Failed; } else { - Inventory inventory = playerComponent.getInventory(); - if (inventory == null) { + TransformComponent transformComponent = commandBuffer.getComponent(ref, TransformComponent.getComponentType()); + if (transformComponent == null) { state.state = InteractionState.Failed; } else { - TransformComponent transformComponent = commandBuffer.getComponent(ref, TransformComponent.getComponentType()); - if (transformComponent == null) { + HeadRotation headRotationComponent = commandBuffer.getComponent(ref, HeadRotation.getComponentType()); + if (headRotationComponent == null) { state.state = InteractionState.Failed; } else { - HeadRotation headRotationComponent = commandBuffer.getComponent(ref, HeadRotation.getComponentType()); - if (headRotationComponent == null) { + ModelComponent modelComponent = commandBuffer.getComponent(ref, ModelComponent.getComponentType()); + if (modelComponent == null) { state.state = InteractionState.Failed; } else { - ModelComponent modelComponent = commandBuffer.getComponent(ref, ModelComponent.getComponentType()); - if (modelComponent == null) { + ItemStack heldItem = context.getHeldItem(); + if (heldItem == null) { state.state = InteractionState.Failed; } else { - ItemStack heldItem = context.getHeldItem(); - if (heldItem == null) { - state.state = InteractionState.Failed; - } else { - InteractionConfiguration heldItemInteractionConfig = heldItem.getItem().getInteractionConfig(); - float distance = heldItemInteractionConfig.getUseDistance(playerComponent.getGameMode()); - Vector3d fromPos = transformComponent.getPosition().clone(); - fromPos.y = fromPos.y + modelComponent.getModel().getEyeHeight(ref, commandBuffer); - Vector3d lookDir = headRotationComponent.getDirection(); - Vector3d toPos = fromPos.clone().add(lookDir.scale(distance)); - AtomicBoolean refilled = new AtomicBoolean(false); - BlockIterator.iterateFromTo( - fromPos, - toPos, - (x, y, z, px, py, pz, qx, qy, qz) -> { - Ref section = world.getChunkStore() - .getChunkSectionReference(ChunkUtil.chunkCoordinate(x), ChunkUtil.chunkCoordinate(y), ChunkUtil.chunkCoordinate(z)); - if (section == null) { + CombinedItemContainer combinedInventoryHotbarFirst = InventoryComponent.getCombined(commandBuffer, ref, InventoryComponent.HOTBAR_FIRST); + InteractionConfiguration heldItemInteractionConfig = heldItem.getItem().getInteractionConfig(); + float distance = heldItemInteractionConfig.getUseDistance(playerComponent.getGameMode()); + Vector3d fromPos = new Vector3d(transformComponent.getPosition()); + fromPos.y = fromPos.y + modelComponent.getModel().getEyeHeight(ref, commandBuffer); + Vector3d lookDir = headRotationComponent.getDirection(); + Vector3d toPos = new Vector3d(fromPos).add(lookDir.mul(distance)); + AtomicBoolean refilled = new AtomicBoolean(false); + BlockIterator.iterateFromTo( + fromPos, + toPos, + (x, y, z, px, py, pz, qx, qy, qz) -> { + Ref section = world.getChunkStore().getChunkSectionReferenceAtBlock(x, y, z); + if (section == null) { + return true; + } else { + BlockSection blockSection = section.getStore().getComponent(section, BlockSection.getComponentType()); + if (blockSection == null) { return true; + } else if (FluidTicker.isSolid(BlockType.getAssetMap().getAsset(blockSection.get(x, y, z)))) { + state.state = InteractionState.Failed; + return false; } else { - BlockSection blockSection = section.getStore().getComponent(section, BlockSection.getComponentType()); - if (blockSection == null) { + FluidSection fluidSection = section.getStore().getComponent(section, FluidSection.getComponentType()); + if (fluidSection == null) { return true; - } else if (FluidTicker.isSolid(BlockType.getAssetMap().getAsset(blockSection.get(x, y, z)))) { - state.state = InteractionState.Failed; - return false; } else { - FluidSection fluidSection = section.getStore().getComponent(section, FluidSection.getComponentType()); - if (fluidSection == null) { + int fluidId = fluidSection.getFluidId(x, y, z); + int[] allowedBlockIds = this.getAllowedFluidIds(); + if (allowedBlockIds != null && Arrays.binarySearch(allowedBlockIds, fluidId) < 0) { + state.state = InteractionState.Failed; return true; } else { - int fluidId = fluidSection.getFluidId(x, y, z); - int[] allowedBlockIds = this.getAllowedFluidIds(); - if (allowedBlockIds != null && Arrays.binarySearch(allowedBlockIds, fluidId) < 0) { + String newState = this.getFluidToState().get(fluidId); + if (newState == null) { state.state = InteractionState.Failed; - return true; + return false; } else { - String newState = this.getFluidToState().get(fluidId); - if (newState == null) { + ItemStack current = context.getHeldItem(); + Item newItemAsset = current.getItem().getItemForState(newState); + if (newItemAsset == null) { state.state = InteractionState.Failed; return false; } else { - ItemStack current = context.getHeldItem(); - Item newItemAsset = current.getItem().getItemForState(newState); - if (newItemAsset == null) { - state.state = InteractionState.Failed; - return false; - } else { - RefillContainerInteraction.RefillState refillState = this.refillStateMap.get(newState); - if (newItemAsset.getId().equals(current.getItemId())) { - if (refillState != null) { - double newDurability = MathUtil.maxValue(refillState.durability, current.getMaxDurability()); - if (newDurability <= current.getDurability()) { - state.state = InteractionState.Failed; - return false; - } - - ItemStack newItem = current.withIncreasedDurability(newDurability); - ItemStackSlotTransaction transaction = context.getHeldItemContainer() - .setItemStackForSlot(context.getHeldItemSlot(), newItem); - if (!transaction.succeeded()) { - state.state = InteractionState.Failed; - return false; - } - - context.setHeldItem(newItem); - refilled.set(true); - } - } else { - ItemStackSlotTransaction removeEmptyTransaction = context.getHeldItemContainer() - .removeItemStackFromSlot(context.getHeldItemSlot(), current, 1); - if (!removeEmptyTransaction.succeeded()) { + RefillContainerInteraction.RefillState refillState = this.refillStateMap.get(newState); + if (newItemAsset.getId().equals(current.getItemId())) { + if (refillState != null) { + double newDurability = MathUtil.maxValue(refillState.durability, current.getMaxDurability()); + if (newDurability <= current.getDurability()) { state.state = InteractionState.Failed; return false; } - ItemStack refilledContainer = new ItemStack(newItemAsset.getId(), 1); - if (refillState != null && refillState.durability > 0.0) { - refilledContainer = refilledContainer.withDurability(refillState.durability); - } - - if (current.getQuantity() == 1) { - ItemStackSlotTransaction addFilledTransaction = context.getHeldItemContainer() - .setItemStackForSlot(context.getHeldItemSlot(), refilledContainer); - if (!addFilledTransaction.succeeded()) { - state.state = InteractionState.Failed; - return false; - } - - context.setHeldItem(refilledContainer); - } else { - SimpleItemContainer.addOrDropItemStack( - commandBuffer, ref, inventory.getCombinedHotbarFirst(), refilledContainer - ); - context.setHeldItem(context.getHeldItemContainer().getItemStack(context.getHeldItemSlot())); - } - } - - if (refillState != null && refillState.getTransformFluid() != null) { - int transformedFluid = Fluid.getFluidIdOrUnknown( - refillState.getTransformFluid(), "Unknown fluid %s", refillState.getTransformFluid() - ); - boolean placed = fluidSection.setFluid( - x, y, z, transformedFluid, (byte)Fluid.getAssetMap().getAsset(transformedFluid).getMaxFluidLevel() - ); - if (!placed) { + ItemStack newItem = current.withIncreasedDurability(newDurability); + ItemStackSlotTransaction transaction = context.getHeldItemContainer() + .setItemStackForSlot(context.getHeldItemSlot(), newItem); + if (!transaction.succeeded()) { state.state = InteractionState.Failed; + return false; } - world.performBlockUpdate(x, y, z); + context.setHeldItem(newItem); refilled.set(true); } + } else { + ItemStackSlotTransaction removeEmptyTransaction = context.getHeldItemContainer() + .removeItemStackFromSlot(context.getHeldItemSlot(), current, 1); + if (!removeEmptyTransaction.succeeded()) { + state.state = InteractionState.Failed; + return false; + } - return false; + ItemStack refilledContainer = new ItemStack(newItemAsset.getId(), 1); + if (refillState != null && refillState.durability > 0.0) { + refilledContainer = refilledContainer.withDurability(refillState.durability); + } + + if (current.getQuantity() == 1) { + ItemStackSlotTransaction addFilledTransaction = context.getHeldItemContainer() + .setItemStackForSlot(context.getHeldItemSlot(), refilledContainer); + if (!addFilledTransaction.succeeded()) { + state.state = InteractionState.Failed; + return false; + } + + context.setHeldItem(refilledContainer); + } else { + SimpleItemContainer.addOrDropItemStack( + commandBuffer, ref, combinedInventoryHotbarFirst, refilledContainer + ); + context.setHeldItem(context.getHeldItemContainer().getItemStack(context.getHeldItemSlot())); + } } + + if (refillState != null && refillState.getTransformFluid() != null) { + int transformedFluid = Fluid.getFluidIdOrUnknown( + refillState.getTransformFluid(), "Unknown fluid %s", refillState.getTransformFluid() + ); + boolean placed = fluidSection.setFluid( + x, y, z, transformedFluid, (byte)Fluid.getAssetMap().getAsset(transformedFluid).getMaxFluidLevel() + ); + if (!placed) { + state.state = InteractionState.Failed; + } + + world.performBlockUpdate(x, y, z); + refilled.set(true); + } + + return false; } } } } } } - ); - if (!refilled.get()) { - context.getState().state = InteractionState.Failed; } + ); + if (!refilled.get()) { + context.getState().state = InteractionState.Failed; } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/RunOnBlockTypesInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/RunOnBlockTypesInteraction.java index c0128219..9326fd37 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/RunOnBlockTypesInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/RunOnBlockTypesInteraction.java @@ -9,8 +9,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; @@ -37,7 +35,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntConsumer; import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; @@ -45,6 +42,8 @@ import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; +import org.joml.Vector3d; +import org.joml.Vector3i; public class RunOnBlockTypesInteraction extends SimpleInteraction { @Nonnull @@ -236,7 +235,6 @@ public class RunOnBlockTypesInteraction extends SimpleInteraction { RunOnBlockTypesInteraction.BlockSearchConsumer consumer = new RunOnBlockTypesInteraction.BlockSearchConsumer( originX, originY, originZ, radiusSquared, this.maxCount ); - IntOpenHashSet internalIdHolder = new IntOpenHashSet(); int minY = Math.max(0, originY - this.range); int maxY = Math.min(319, originY + this.range); @@ -252,8 +250,7 @@ public class RunOnBlockTypesInteraction extends SimpleInteraction { BlockSection section = blockChunk.getSectionAtIndex(sectionIndex); if (!section.isSolidAir() && section.containsAny(blockIds)) { consumer.setSection(x, z, sectionIndex); - section.find(blockIds, internalIdHolder, consumer); - internalIdHolder.clear(); + section.find(blockIds, consumer); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/SpawnPrefabInteraction.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/SpawnPrefabInteraction.java index 282ab664..9daeb437 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/SpawnPrefabInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/SpawnPrefabInteraction.java @@ -9,8 +9,8 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; @@ -27,6 +27,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.PrefabUtil; import java.nio.file.Path; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class SpawnPrefabInteraction extends SimpleInstantInteraction { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -35,7 +37,7 @@ public class SpawnPrefabInteraction extends SimpleInstantInteraction { .documentation("Spawns a prefab at the current location.") .appendInherited(new KeyedCodec<>("PrefabPath", Codec.STRING), (o, i) -> o.prefabPath = i, o -> o.prefabPath, (o, p) -> o.prefabPath = p.prefabPath) .add() - .appendInherited(new KeyedCodec<>("Offset", Vector3i.CODEC), (o, i) -> o.offset = i, o -> o.offset, (o, p) -> o.offset = p.offset) + .appendInherited(new KeyedCodec<>("Offset", Vector3iUtil.CODEC), (o, i) -> o.offset = i, o -> o.offset, (o, p) -> o.offset = p.offset) .addValidator(Validators.nonNull()) .add() .appendInherited( @@ -55,7 +57,7 @@ public class SpawnPrefabInteraction extends SimpleInstantInteraction { .add() .build(); private String prefabPath; - private Vector3i offset = Vector3i.ZERO; + private Vector3i offset = new Vector3i(); private Rotation rotationYaw = Rotation.None; @Nonnull private SpawnPrefabInteraction.OriginSource originSource = SpawnPrefabInteraction.OriginSource.ENTITY; @@ -79,7 +81,7 @@ public class SpawnPrefabInteraction extends SimpleInstantInteraction { Vector3i target; switch (this.originSource) { case ENTITY: - target = entityPosition.toVector3i(); + target = Vector3dUtil.toVector3i(entityPosition); target.add(this.offset); break; case BLOCK: diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/DamageEffects.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/DamageEffects.java index 86dd0f4d..b00d3681 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/DamageEffects.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/DamageEffects.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.asset.type.camera.CameraEffect; import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle; @@ -21,10 +20,11 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.ParticleUtil; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.Arrays; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class DamageEffects implements NetworkSerializable { public static final BuilderCodec CODEC = BuilderCodec.builder(DamageEffects.class, DamageEffects::new) @@ -221,20 +221,19 @@ public class DamageEffects implements NetworkSerializable, EntityStore> playerSpatialResource = commandBuffer.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> playerRefs = SpatialResource.getThreadLocalReferenceList(); + List> playerRefs = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, this.viewDistance, playerRefs); ParticleUtil.spawnParticleEffects(this.worldParticles, position, null, playerRefs, commandBuffer); } + PlayerRef playerRef = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); if (this.worldSoundEventIndex != 0) { - SoundUtil.playSoundEvent3d(ref, this.worldSoundEventIndex, position, commandBuffer); + boolean ignoreSource = playerRef != null; + SoundUtil.playSoundEvent3d(ref, this.worldSoundEventIndex, position, ignoreSource, commandBuffer); } - if (this.playerSoundEventIndex != 0) { - PlayerRef playerRef = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); - if (playerRef != null) { - SoundUtil.playSoundEvent2dToPlayer(playerRef, this.playerSoundEventIndex, SoundCategory.SFX); - } + if (playerRef != null && (this.playerSoundEventIndex != 0 || this.worldSoundEventIndex != 0)) { + SoundUtil.playLocalPlayerSoundEvent(playerRef, this.playerSoundEventIndex, this.worldSoundEventIndex, SoundCategory.SFX); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/DirectionalKnockback.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/DirectionalKnockback.java index 1783f6bb..812286d6 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/DirectionalKnockback.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/DirectionalKnockback.java @@ -3,8 +3,8 @@ package com.hypixel.hytale.server.core.modules.interaction.interaction.config.se import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DirectionalKnockback extends Knockback { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -36,11 +36,11 @@ public class DirectionalKnockback extends Knockback { @Nonnull @Override public Vector3d calculateVector(@Nonnull Vector3d source, float yaw, @Nonnull Vector3d target) { - Vector3d vector = source.clone().subtract(target); - if (vector.squaredLength() <= 1.0E-8) { + Vector3d vector = new Vector3d(source).sub(target); + if (vector.lengthSquared() <= 1.0E-8) { Vector3d lookVector = new Vector3d(0.0, 0.0, -1.0); lookVector.rotateY(yaw); - vector.assign(lookVector); + vector.set(lookVector); } else { vector.normalize(); } @@ -51,8 +51,8 @@ public class DirectionalKnockback extends Knockback { vector.add(rotation); } - double x = vector.getX() * this.force; - double z = vector.getZ() * this.force; + double x = vector.x() * this.force; + double z = vector.z() * this.force; double y = this.velocityY; return new Vector3d(x, y, z); } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/ForceKnockback.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/ForceKnockback.java index 5102e11f..7680bd5f 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/ForceKnockback.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/ForceKnockback.java @@ -3,24 +3,27 @@ package com.hypixel.hytale.server.core.modules.interaction.interaction.config.se import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ForceKnockback extends Knockback { public static final BuilderCodec CODEC = BuilderCodec.builder(ForceKnockback.class, ForceKnockback::new, Knockback.BASE_CODEC) - .appendInherited(new KeyedCodec<>("Direction", Vector3d.CODEC), (o, i) -> o.direction = i, o -> o.direction, (o, p) -> o.direction = p.direction) + .appendInherited( + new KeyedCodec<>("Direction", Vector3dUtil.CODEC), (o, i) -> o.direction.set(i), o -> o.direction, (o, p) -> o.direction.set(p.direction) + ) .addValidator(Validators.nonNull()) .add() .afterDecode(i -> i.direction.normalize()) .build(); - private Vector3d direction = Vector3d.UP; + private final Vector3d direction = new Vector3d(Vector3dUtil.UP); @Nonnull @Override public Vector3d calculateVector(Vector3d source, float yaw, Vector3d target) { - Vector3d vel = this.direction.clone(); + Vector3d vel = new Vector3d(this.direction); vel.rotateY(yaw); - vel.scale(this.force); + vel.mul(this.force); return vel; } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/Knockback.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/Knockback.java index 598e2491..b799086b 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/Knockback.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/Knockback.java @@ -5,11 +5,11 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.lookup.CodecMapCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ChangeVelocityType; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.modules.splitvelocity.VelocityConfig; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class Knockback { public static final CodecMapCodec CODEC = new CodecMapCodec<>("Type", true); diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/PointKnockback.java b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/PointKnockback.java index 26099ff9..fa0a6a67 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/PointKnockback.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/interaction/config/server/combat/PointKnockback.java @@ -3,8 +3,9 @@ package com.hypixel.hytale.server.core.modules.interaction.interaction.config.se import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PointKnockback extends Knockback { public static final BuilderCodec CODEC = BuilderCodec.builder(PointKnockback.class, PointKnockback::new, Knockback.BASE_CODEC) @@ -48,13 +49,13 @@ public class PointKnockback extends Knockback { from.add(source); } - Vector3d vector = Vector3d.directionTo(from, target).normalize(); + Vector3d vector = Vector3dUtil.directionTo(from, target).normalize(); if (this.rotateY != 0) { vector.rotateY(this.rotateY); } - double x = vector.getX() * this.force; - double z = vector.getZ() * this.force; + double x = vector.x() * this.force; + double z = vector.z() * this.force; double y = this.velocityY; return new Vector3d(x, y, z); } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/suppliers/ItemRepairPageSupplier.java b/src/com/hypixel/hytale/server/core/modules/interaction/suppliers/ItemRepairPageSupplier.java index c44d3ca9..a5b58424 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/suppliers/ItemRepairPageSupplier.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/suppliers/ItemRepairPageSupplier.java @@ -6,10 +6,11 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.CustomUIPage; import com.hypixel.hytale.server.core.entity.entities.player.pages.itemrepair.ItemRepairPage; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemContext; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -34,13 +35,14 @@ public class ItemRepairPageSupplier implements OpenCustomUIInteraction.CustomPag @Nonnull PlayerRef playerRef, @Nonnull InteractionContext context ) { - Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType()); - - assert playerComponent != null; - ItemContext itemContext = context.createHeldItemContext(); - return itemContext == null - ? null - : new ItemRepairPage(playerRef, playerComponent.getInventory().getCombinedArmorHotbarUtilityStorage(), this.repairPenalty, itemContext); + if (itemContext == null) { + return null; + } else { + CombinedItemContainer hotbarUtilityCombinedContainer = InventoryComponent.getCombined( + componentAccessor, ref, InventoryComponent.ARMOR_HOTBAR_UTILITY_STORAGE + ); + return new ItemRepairPage(playerRef, hotbarUtilityCombinedContainer, this.repairPenalty, itemContext); + } } } diff --git a/src/com/hypixel/hytale/server/core/modules/interaction/system/InteractionSystems.java b/src/com/hypixel/hytale/server/core/modules/interaction/system/InteractionSystems.java index 68c2756e..8fdbaf9f 100644 --- a/src/com/hypixel/hytale/server/core/modules/interaction/system/InteractionSystems.java +++ b/src/com/hypixel/hytale/server/core/modules/interaction/system/InteractionSystems.java @@ -175,6 +175,17 @@ public class InteractionSystems { interactionManager.tick(ref, commandBuffer, dt); ObjectList syncPackets = interactionManager.getSyncPackets(); if (playerRef != null && !syncPackets.isEmpty()) { + if (syncPackets.size() > 128) { + LOGGER.at(Level.WARNING) + .log( + "Dropping %d excess interaction sync packets for %s (had %d, limit 128)", + syncPackets.size() - 128, + playerRef.getUuid(), + syncPackets.size() + ); + syncPackets.removeElements(128, syncPackets.size()); + } + playerRef.getPacketHandler().writeNoCache(new SyncInteractionChains(syncPackets.toArray(SyncInteractionChain[]::new))); syncPackets.clear(); } diff --git a/src/com/hypixel/hytale/server/core/modules/item/commands/SpawnItemCommand.java b/src/com/hypixel/hytale/server/core/modules/item/commands/SpawnItemCommand.java index f5653e22..bc2c46b2 100644 --- a/src/com/hypixel/hytale/server/core/modules/item/commands/SpawnItemCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/item/commands/SpawnItemCommand.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.model.config.Model; @@ -27,6 +26,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpawnItemCommand extends AbstractPlayerCommand { @Nonnull @@ -61,7 +61,7 @@ public class SpawnItemCommand extends AbstractPlayerCommand { float throwSpeed = 6.0F * force; if (this.countArg.provided(context)) { int count = this.countArg.get(context); - Vector3d throwPosition = playerPosition.clone(); + Vector3d throwPosition = new Vector3d(playerPosition); throwPosition.add(0.0, playerModel.getEyeHeight(ref, store), 0.0); ThreadLocalRandom random = ThreadLocalRandom.current(); @@ -70,7 +70,7 @@ public class SpawnItemCommand extends AbstractPlayerCommand { store, new ItemStack(itemId, quantity), throwPosition, - Vector3f.ZERO, + Rotation3f.IDENTITY, (float)random.nextGaussian() * throwSpeed, 0.5F, (float)random.nextGaussian() * throwSpeed diff --git a/src/com/hypixel/hytale/server/core/modules/migrations/EntityMigration.java b/src/com/hypixel/hytale/server/core/modules/migrations/EntityMigration.java deleted file mode 100644 index 1b4435b4..00000000 --- a/src/com/hypixel/hytale/server/core/modules/migrations/EntityMigration.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.hypixel.hytale.server.core.modules.migrations; - -import com.hypixel.hytale.codec.ExtraInfo; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import java.util.function.IntFunction; - -public abstract class EntityMigration implements Migration { - private Class tClass; - private IntFunction extraInfoSupplier; - - public EntityMigration(Class tClass, IntFunction extraInfoSupplier) { - this.tClass = tClass; - this.extraInfoSupplier = extraInfoSupplier; - } - - @Override - public final void run(WorldChunk chunk) { - throw new UnsupportedOperationException("Not implemented!"); - } - - protected abstract boolean migrate(T var1); -} diff --git a/src/com/hypixel/hytale/server/core/modules/migrations/MigrationModule.java b/src/com/hypixel/hytale/server/core/modules/migrations/MigrationModule.java index 401a59da..7eb4e2d0 100644 --- a/src/com/hypixel/hytale/server/core/modules/migrations/MigrationModule.java +++ b/src/com/hypixel/hytale/server/core/modules/migrations/MigrationModule.java @@ -96,8 +96,8 @@ public class MigrationModule extends JavaPlugin { IChunkLoader loader = chunkComponentStore.getLoader(); world.execute( () -> { + world.lockSaving(); ChunkSavingSystems.Data data = chunkComponentStore.getStore().getResource(ChunkStore.SAVE_RESOURCE); - data.isSaving = false; data.waitForSavingChunks() .whenComplete( (aVoid, throwable) -> { @@ -145,7 +145,7 @@ public class MigrationModule extends JavaPlugin { } catch (Throwable var21) { this.getLogger().at(Level.SEVERE).withCause(var21).log("Failed to migrate chunks!"); } finally { - data.isSaving = true; + world.unlockSaving(); this.getLogger().at(Level.INFO).log("%d world(s) left to migrate.", worldsCount.decrementAndGet()); } } diff --git a/src/com/hypixel/hytale/server/core/modules/physics/RestingSupport.java b/src/com/hypixel/hytale/server/core/modules/physics/RestingSupport.java index 34f38801..e262cfbd 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/RestingSupport.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/RestingSupport.java @@ -3,11 +3,11 @@ package com.hypixel.hytale.server.core.modules.physics; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class RestingSupport { protected int supportMinX; diff --git a/src/com/hypixel/hytale/server/core/modules/physics/SimplePhysicsProvider.java b/src/com/hypixel/hytale/server/core/modules/physics/SimplePhysicsProvider.java index a49b7faa..dbe87866 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/SimplePhysicsProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/SimplePhysicsProvider.java @@ -7,9 +7,8 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.NearestBlockUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.projectile.config.Projectile; import com.hypixel.hytale.server.core.entity.Entity; @@ -38,6 +37,8 @@ import java.util.UUID; import java.util.function.BiConsumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; @Deprecated public class SimplePhysicsProvider implements IBlockCollisionConsumer { @@ -166,16 +167,16 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { BlockMaterial blockMaterial = blockData.getBlockType().getMaterial(); if (this.moveOutOfSolidSpeed > 0.0 && contactData.isOverlapping() && blockMaterial == BlockMaterial.Solid) { Vector3i nearestBlock = NearestBlockUtil.findNearestBlock(this.position, (block, w) -> { - WorldChunk worldChunk = w.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(block.getX(), block.getZ())); + WorldChunk worldChunk = w.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(block.x(), block.z())); return worldChunk != null && worldChunk.getBlockType(block).getMaterial() != BlockMaterial.Solid; }, this.world); if (nearestBlock != null) { - this.tempVector.assign(nearestBlock.x, nearestBlock.y, nearestBlock.z); + this.tempVector.set(nearestBlock.x, nearestBlock.y, nearestBlock.z); this.tempVector.add(0.5, 0.5, 0.5); - this.tempVector.subtract(this.position); - this.tempVector.setLength(this.moveOutOfSolidSpeed); + this.tempVector.sub(this.position); + this.tempVector.normalize(this.moveOutOfSolidSpeed); } else { - this.tempVector.assign(0.0, this.moveOutOfSolidSpeed, 0.0); + this.tempVector.set(0.0, this.moveOutOfSolidSpeed, 0.0); } this.moveOutOfSolidVelocity.add(this.tempVector); @@ -212,8 +213,8 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { if (surfaceAlignment >= 0.0) { return IBlockCollisionConsumer.Result.CONTINUE; } else { - this.contactPosition.assign(contactData.getCollisionPoint()); - this.contactNormal.assign(contactData.getCollisionNormal()); + this.contactPosition.set(contactData.getCollisionPoint()); + this.contactNormal.set(contactData.getCollisionNormal()); this.collisionStart = contactData.getCollisionStart(); this.bounced = true; return IBlockCollisionConsumer.Result.STOP; @@ -257,7 +258,7 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { return null; } else { if (this.state == SimplePhysicsProvider.STATE.Resting) { - if (this.forceProviderStandardState.externalForce.squaredLength() == 0.0 && !this.restingSupport.hasChanged(entityWorld)) { + if (this.forceProviderStandardState.externalForce.lengthSquared() == 0.0 && !this.restingSupport.hasChanged(entityWorld)) { return null; } @@ -265,13 +266,13 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { } this.world = entityWorld; - this.position.assign(entityTransform.getPosition()); + this.position.set(entityTransform.getPosition()); entityVelocity.assignVelocityTo(this.velocity); double mass = this.forceProviderEntity.getMass(this.boundingBox.getBoundingBox().getVolume()); this.forceProviderStandardState.convertToForces(dt, mass); this.forceProviderStandardState.updateVelocity(this.velocity); - if (!(this.velocity.squaredLength() * dt * dt >= 1.0000000000000002E-10) && !(this.forceProviderStandardState.externalForce.squaredLength() >= 0.0)) { - this.velocity.assign(Vector3d.ZERO); + if (!(this.velocity.lengthSquared() * dt * dt >= 1.0000000000000002E-10) && !(this.forceProviderStandardState.externalForce.lengthSquared() >= 0.0)) { + this.velocity.zero(); } else { this.state = SimplePhysicsProvider.STATE.Active; } @@ -280,17 +281,17 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { this.state = SimplePhysicsProvider.STATE.Active; } - this.stateBefore.position.assign(this.position); - this.stateBefore.velocity.assign(this.velocity); + this.stateBefore.position.set(this.position); + this.stateBefore.velocity.set(this.velocity); this.forceProviderEntity.setForceProviderStandardState(this.forceProviderStandardState); this.stateUpdater.update(this.stateBefore, this.stateAfter, mass, dt, this.onGround, this.forceProviders); - this.velocity.assign(this.stateAfter.velocity); - this.movement.assign(this.velocity).scale(dt); + this.velocity.set(this.stateAfter.velocity); + this.movement.set(this.velocity).mul(dt); this.forceProviderStandardState.clear(); - if (this.velocity.squaredLength() * dt * dt >= 1.0000000000000002E-10) { + if (this.velocity.lengthSquared() * dt * dt >= 1.0000000000000002E-10) { this.state = SimplePhysicsProvider.STATE.Active; } else { - this.velocity.assign(Vector3d.ZERO); + this.velocity.zero(); } double maxRelativeDistance = 1.0; @@ -309,22 +310,22 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { this.bounced = false; this.onGround = false; - this.moveOutOfSolidVelocity.assign(Vector3d.ZERO); + this.moveOutOfSolidVelocity.zero(); this.movedInsideSolid = false; this.displacedMass = 0.0; this.subSurfaceVolume = 0.0; this.enterFluid = Double.MAX_VALUE; this.leaveFluid = -Double.MAX_VALUE; this.collisionStart = maxRelativeDistance; - this.contactPosition.assign(this.position).addScaled(this.movement, this.collisionStart); - this.contactNormal.assign(Vector3d.ZERO); + this.contactPosition.set(this.position).fma(this.collisionStart, this.movement); + this.contactNormal.zero(); this.blockCollisionProvider .cast(entityWorld, this.boundingBox.getBoundingBox(), this.position, this.movement, this, this.triggerTracker, maxRelativeDistance); this.fluidTracker.reset(); double density = this.displacedMass > 0.0 ? this.displacedMass / this.subSurfaceVolume : 1.2; if (this.movedInsideSolid) { - this.position.addScaled(this.moveOutOfSolidVelocity, dt); - this.velocity.assign(this.moveOutOfSolidVelocity); + this.position.fma(dt, this.moveOutOfSolidVelocity); + this.velocity.set(this.moveOutOfSolidVelocity); this.forceProviderStandardState.dragCoefficient = this.getDragCoefficient(density); this.forceProviderStandardState.displacedMass = this.displacedMass; this.forceProviderStandardState.gravity = this.gravity; @@ -346,7 +347,7 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { if (velocityClip > 0.0 && velocityClip < 1.0) { this.stateUpdater.update(this.stateBefore, this.stateAfter, mass, dt * velocityClip, this.onGround, this.forceProviders); - this.velocity.assign(this.stateAfter.velocity); + this.velocity.set(this.stateAfter.velocity); } if (this.inFluid && this.subSurfaceVolume < this.boundingBox.getBoundingBox().getVolume() && this.velocityExtremaCount > 0) { @@ -363,7 +364,7 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { } if (enteringWater) { - this.forceProviderStandardState.externalImpulse.addScaled(this.stateAfter.velocity, -this.hitWaterImpulseLoss * mass); + this.forceProviderStandardState.externalImpulse.fma(-this.hitWaterImpulseLoss * mass, this.stateAfter.velocity); } this.forceProviderStandardState.displacedMass = this.displacedMass; @@ -374,7 +375,7 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { EntityContactData contact = this.entityCollisionProvider.getContact(0); Ref contactRef = contact.getEntityReference(); Entity target = EntityUtils.getEntity(contactRef, componentAccessor); - this.position.assign(contact.getCollisionPoint()); + this.position.set(contact.getCollisionPoint()); this.state = SimplePhysicsProvider.STATE.Inactive; if (this.impactConsumer != null) { this.impactConsumer.accept(selfRef, this.position, contactRef, componentAccessor); @@ -390,11 +391,11 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { return null; } } else { - this.position.assign(this.contactPosition); + this.position.set(this.contactPosition); computeReflectedVector(this.velocity, this.contactNormal, this.velocity); - this.velocity.scale(this.bounciness); - if (this.velocity.squaredLength() * dt * dt < 0.16000000000000003) { - boolean hitGround = this.contactNormal.equals(Vector3d.UP); + this.velocity.mul(this.bounciness); + if (this.velocity.lengthSquared() * dt * dt < 0.16000000000000003) { + boolean hitGround = this.contactNormal.equals(Vector3dUtil.UP); if (this.sticksVertically || hitGround) { this.state = SimplePhysicsProvider.STATE.Resting; this.restingSupport.rest(entityWorld, this.boundingBox.getBoundingBox(), this.position); @@ -404,7 +405,7 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { } } - this.velocity.assign(Vector3d.ZERO); + this.velocity.zero(); } else if (this.bounceConsumer != null) { this.bounceConsumer.accept(this.position, componentAccessor); } @@ -424,7 +425,7 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { this.entityCollisionProvider.clear(); } - protected void rotateBody(double dt, @Nonnull Vector3f bodyRotation) { + protected void rotateBody(double dt, @Nonnull Rotation3f bodyRotation) { if (this.isComputeYaw() || this.isComputePitch()) { double vx = this.stateAfter.velocity.x; double vz = this.stateAfter.velocity.z; @@ -448,10 +449,10 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { } if (this.isComputePitch()) { - float pitch = bodyRotation.getPitch(); + float pitch = bodyRotation.pitch(); float targetPitch = PhysicsMath.pitchFromDirection(vx, this.velocity.y, vz); float delta = PhysicsMath.normalizeTurnAngle(targetPitch - pitch); - float maxDelta = (float)(this.velocity.squaredLength() * dt * this.speedRotationFactor); + float maxDelta = (float)(this.velocity.lengthSquared() * dt * this.speedRotationFactor); if (delta > maxDelta) { targetPitch = pitch + maxDelta; delta = maxDelta; @@ -461,7 +462,7 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { } bodyRotation.setPitch(targetPitch); - this.forceProviderStandardState.externalForce.addScaled(this.stateAfter.velocity, delta * -this.rotationForce); + this.forceProviderStandardState.externalForce.fma(delta * -this.rotationForce, this.stateAfter.velocity); } } } @@ -477,11 +478,11 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { } public static void computeReflectedVector(@Nonnull Vector3d vec, @Nonnull Vector3d normal, @Nonnull Vector3d result) { - result.assign(vec); - double squaredLength = normal.squaredLength(); + result.set(vec); + double squaredLength = normal.lengthSquared(); if (squaredLength != 0.0) { double proj = vec.dot(normal) / squaredLength; - result.addScaled(normal, -2.0 * proj); + result.fma(-2.0 * proj, normal); } } @@ -576,7 +577,7 @@ public class SimplePhysicsProvider implements IBlockCollisionConsumer { } public void setVelocity(@Nonnull Vector3d velocity) { - this.forceProviderStandardState.nextTickVelocity.assign(velocity); + this.forceProviderStandardState.nextTickVelocity.set(velocity); } public void setMoveOutOfSolid(boolean moveOutOfSolid) { diff --git a/src/com/hypixel/hytale/server/core/modules/physics/component/Velocity.java b/src/com/hypixel/hytale/server/core/modules/physics/component/Velocity.java index f9974918..f9c0c861 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/component/Velocity.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/component/Velocity.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.ChangeVelocityType; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.splitvelocity.VelocityConfig; @@ -13,11 +13,12 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Velocity implements Component { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder(Velocity.class, Velocity::new) - .append(new KeyedCodec<>("Velocity", Vector3d.CODEC), (entity, o) -> entity.velocity.assign(o), entity -> entity.velocity) + .append(new KeyedCodec<>("Velocity", Vector3dUtil.CODEC), (entity, o) -> entity.velocity.set(o), entity -> entity.velocity) .add() .build(); @Nonnull @@ -36,11 +37,11 @@ public class Velocity implements Component { } public Velocity(@Nonnull Velocity other) { - this(other.velocity.clone()); + this(new Vector3d(other.velocity)); } public Velocity(@Nonnull Vector3d initialVelocity) { - this.velocity.assign(initialVelocity); + this.velocity.set(initialVelocity); } public void setZero() { @@ -56,43 +57,43 @@ public class Velocity implements Component { } public void set(@Nonnull Vector3d newVelocity) { - this.set(newVelocity.getX(), newVelocity.getY(), newVelocity.getZ()); + this.set(newVelocity.x(), newVelocity.y(), newVelocity.z()); } public void set(double x, double y, double z) { - this.velocity.assign(x, y, z); + this.velocity.set(x, y, z); } public void setClient(@Nonnull Vector3d newVelocity) { - this.setClient(newVelocity.getX(), newVelocity.getY(), newVelocity.getZ()); + this.setClient(newVelocity.x(), newVelocity.y(), newVelocity.z()); } public void setClient(double x, double y, double z) { - this.clientVelocity.assign(x, y, z); + this.clientVelocity.set(x, y, z); } public void setX(double x) { - this.velocity.setX(x); + this.velocity.x = x; } public void setY(double y) { - this.velocity.setY(y); + this.velocity.y = y; } public void setZ(double z) { - this.velocity.setZ(z); + this.velocity.z = z; } public double getX() { - return this.velocity.getX(); + return this.velocity.x(); } public double getY() { - return this.velocity.getY(); + return this.velocity.y(); } public double getZ() { - return this.velocity.getZ(); + return this.velocity.z(); } public double getSpeed() { @@ -120,7 +121,7 @@ public class Velocity implements Component { @Nonnull public Vector3d assignVelocityTo(@Nonnull Vector3d vector) { - return vector.assign(this.velocity); + return vector.set(this.velocity); } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/physics/util/ForceAccumulator.java b/src/com/hypixel/hytale/server/core/modules/physics/util/ForceAccumulator.java index a90e9d55..d04b0372 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/util/ForceAccumulator.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/util/ForceAccumulator.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.server.core.modules.physics.util; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ForceAccumulator { public double speed; @@ -9,9 +9,9 @@ public class ForceAccumulator { public final Vector3d resistanceForceLimit = new Vector3d(); public void initialize(@Nonnull PhysicsBodyState state, double mass, double timeStep) { - this.force.assign(Vector3d.ZERO); + this.force.zero(); this.speed = state.velocity.length(); - this.resistanceForceLimit.assign(state.velocity).scale(-mass / timeStep); + this.resistanceForceLimit.set(state.velocity).mul(-mass / timeStep); } protected void computeResultingForce( diff --git a/src/com/hypixel/hytale/server/core/modules/physics/util/ForceProviderStandard.java b/src/com/hypixel/hytale/server/core/modules/physics/util/ForceProviderStandard.java index a742c47b..81601b4a 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/util/ForceProviderStandard.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/util/ForceProviderStandard.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.server.core.modules.physics.util; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class ForceProviderStandard implements ForceProvider { @Nonnull @@ -30,7 +30,7 @@ public abstract class ForceProviderStandard implements ForceProvider { accumulator.force.add(extForce); double speed = accumulator.speed; double dragForceDivSpeed = standardState.dragCoefficient * this.getProjectedArea(bodyState, speed) * speed; - this.dragForce.assign(bodyState.velocity).scale(-dragForceDivSpeed); + this.dragForce.set(bodyState.velocity).mul(-dragForceDivSpeed); this.clipForce(this.dragForce, accumulator.resistanceForceLimit); accumulator.force.add(this.dragForce); double gravityForce = -standardState.gravity * this.getMass(this.getVolume()); diff --git a/src/com/hypixel/hytale/server/core/modules/physics/util/ForceProviderStandardState.java b/src/com/hypixel/hytale/server/core/modules/physics/util/ForceProviderStandardState.java index b739a325..72ea6dfc 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/util/ForceProviderStandardState.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/util/ForceProviderStandardState.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.server.core.modules.physics.util; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ForceProviderStandardState { public double displacedMass; @@ -14,27 +14,27 @@ public class ForceProviderStandardState { public final Vector3d externalImpulse = new Vector3d(); public ForceProviderStandardState() { - this.nextTickVelocity.assign(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + this.nextTickVelocity.set(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); } public void convertToForces(double dt, double mass) { - this.externalForce.addScaled(this.externalAcceleration, 1.0 / mass); - this.externalForce.addScaled(this.externalImpulse, 1.0 / dt); - this.externalAcceleration.assign(Vector3d.ZERO); - this.externalImpulse.assign(Vector3d.ZERO); + this.externalForce.fma(1.0 / mass, this.externalAcceleration); + this.externalForce.fma(1.0 / dt, this.externalImpulse); + this.externalAcceleration.zero(); + this.externalImpulse.zero(); } public void updateVelocity(@Nonnull Vector3d velocity) { if (this.nextTickVelocity.x < Double.MAX_VALUE) { - velocity.assign(this.nextTickVelocity); - this.nextTickVelocity.assign(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + velocity.set(this.nextTickVelocity); + this.nextTickVelocity.set(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); } velocity.add(this.externalVelocity); - this.externalVelocity.assign(Vector3d.ZERO); + this.externalVelocity.zero(); } public void clear() { - this.externalForce.assign(Vector3d.ZERO); + this.externalForce.zero(); } } diff --git a/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsBodyState.java b/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsBodyState.java index 5e8b864c..1bb09126 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsBodyState.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsBodyState.java @@ -1,6 +1,6 @@ package com.hypixel.hytale.server.core.modules.physics.util; -import com.hypixel.hytale.math.vector.Vector3d; +import org.joml.Vector3d; public class PhysicsBodyState { public final Vector3d position = new Vector3d(); diff --git a/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsBodyStateUpdater.java b/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsBodyStateUpdater.java index 2b2bbeed..4e31114a 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsBodyStateUpdater.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsBodyStateUpdater.java @@ -1,7 +1,8 @@ package com.hypixel.hytale.server.core.modules.physics.util; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PhysicsBodyStateUpdater { protected static double MIN_VELOCITY = 1.0E-6; @@ -18,24 +19,24 @@ public class PhysicsBodyStateUpdater { } protected static void updatePositionBeforeVelocity(@Nonnull PhysicsBodyState before, @Nonnull PhysicsBodyState after, double dt) { - after.position.assign(before.position).addScaled(before.velocity, dt); + after.position.set(before.position).fma(dt, before.velocity); } protected static void updatePositionAfterVelocity(@Nonnull PhysicsBodyState before, @Nonnull PhysicsBodyState after, double dt) { - after.position.assign(before.position).addScaled(after.velocity, dt); + after.position.set(before.position).fma(dt, after.velocity); } protected void updateAndClampVelocity(@Nonnull PhysicsBodyState before, @Nonnull PhysicsBodyState after, double dt) { this.updateVelocity(before, after, dt); - after.velocity.clipToZero(MIN_VELOCITY); + Vector3dUtil.clipToZero(after.velocity, MIN_VELOCITY); } protected void updateVelocity(@Nonnull PhysicsBodyState before, @Nonnull PhysicsBodyState after, double dt) { - after.velocity.assign(before.velocity).addScaled(this.acceleration, dt); + after.velocity.set(before.velocity).fma(dt, this.acceleration); } protected void computeAcceleration(double mass) { - this.acceleration.assign(this.accumulator.force).scale(1.0 / mass); + this.acceleration.set(this.accumulator.force).mul(1.0 / mass); } protected void computeAcceleration(@Nonnull PhysicsBodyState state, boolean onGround, @Nonnull ForceProvider[] forceProviders, double mass, double timeStep) { @@ -44,11 +45,11 @@ public class PhysicsBodyStateUpdater { } protected void assignAcceleration(@Nonnull PhysicsBodyState state) { - state.velocity.assign(this.acceleration); + state.velocity.set(this.acceleration); } protected void addAcceleration(@Nonnull PhysicsBodyState state, double scale) { - state.velocity.addScaled(this.acceleration, scale); + state.velocity.fma(scale, this.acceleration); } protected void addAcceleration(@Nonnull PhysicsBodyState state) { @@ -56,6 +57,6 @@ public class PhysicsBodyStateUpdater { } protected void convertAccelerationToVelocity(@Nonnull PhysicsBodyState before, @Nonnull PhysicsBodyState after, double scale) { - after.velocity.scale(scale).add(before.velocity).clipToZero(MIN_VELOCITY); + Vector3dUtil.clipToZero(after.velocity.mul(scale).add(before.velocity), MIN_VELOCITY); } } diff --git a/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsMath.java b/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsMath.java index 074f09a8..eeecfe21 100644 --- a/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsMath.java +++ b/src/com/hypixel/hytale/server/core/modules/physics/util/PhysicsMath.java @@ -2,8 +2,9 @@ package com.hypixel.hytale.server.core.modules.physics.util; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class PhysicsMath { public static final double DENSITY_AIR = 1.2; @@ -55,14 +56,14 @@ public class PhysicsMath { return computeProjectedArea(direction.x, direction.y, direction.z, box); } - public static double volumeOfIntersection(@Nonnull Box a, @Nonnull Vector3d posA, @Nonnull Box b, @Nonnull Vector3d posB) { - return volumeOfIntersection(a, posA, b, posB.x, posB.y, posB.z); + public static double volumeOfIntersection(@Nonnull Box a, @Nonnull Vector3dc posA, @Nonnull Box b, @Nonnull Vector3dc posB) { + return volumeOfIntersection(a, posA, b, posB.x(), posB.y(), posB.z()); } - public static double volumeOfIntersection(@Nonnull Box a, @Nonnull Vector3d posA, @Nonnull Box b, double posBX, double posBY, double posBZ) { - posBX -= posA.x; - posBY -= posA.y; - posBZ -= posA.z; + public static double volumeOfIntersection(@Nonnull Box a, @Nonnull Vector3dc posA, @Nonnull Box b, double posBX, double posBY, double posBZ) { + posBX -= posA.x(); + posBY -= posA.y(); + posBZ -= posA.z(); return lengthOfIntersection(a.min.x, a.max.x, posBX + b.min.x, posBX + b.max.x) * lengthOfIntersection(a.min.y, a.max.y, posBY + b.min.y, posBY + b.max.y) * lengthOfIntersection(a.min.z, a.max.z, posBZ + b.min.z, posBZ + b.max.z); diff --git a/src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerState.java b/src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerBlock.java similarity index 77% rename from src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerState.java rename to src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerBlock.java index 72791678..e3217424 100644 --- a/src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerState.java +++ b/src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerBlock.java @@ -4,6 +4,8 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; +import com.hypixel.hytale.component.Component; +import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; @@ -11,25 +13,25 @@ import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.Page; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage; +import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.prefab.PrefabWeights; import com.hypixel.hytale.server.core.ui.builder.EventData; import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder; import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder; import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class PrefabSpawnerState extends BlockState { - public static final String PREFAB_SPAWNER_TYPE = "prefabspawner"; +public class PrefabSpawnerBlock implements Component { public static final KeyedCodec FIT_HEIGHTMAP_CODEC = new KeyedCodec<>("FitHeightmap", Codec.BOOLEAN); public static final KeyedCodec INHERIT_SEED_CODEC = new KeyedCodec<>("InheritSeed", Codec.BOOLEAN); public static final KeyedCodec INHERIT_HEIGHT_CONDITION_CODEC = new KeyedCodec<>("InheritHeightCondition", Codec.BOOLEAN); public static final KeyedCodec PREFAB_WEIGHTS_CODEC = new KeyedCodec<>("PrefabWeights", PrefabWeights.CODEC); @Nonnull - public static final Codec CODEC = BuilderCodec.builder(PrefabSpawnerState.class, PrefabSpawnerState::new, BlockState.BASE_CODEC) + public static final BuilderCodec CODEC = BuilderCodec.builder(PrefabSpawnerBlock.class, PrefabSpawnerBlock::new) .append(new KeyedCodec<>("PrefabPath", Codec.STRING), (state, s) -> state.prefabPath = s, state -> state.prefabPath) .documentation("The prefab path where the prefab is located. This uses the dot-notation. 'folder.folder.folder.filename'") .add() @@ -44,17 +46,36 @@ public class PrefabSpawnerState extends BlockState { "Determines if the child prefab should inherit the HeightCondition from the parent. Setting to false allows child prefabs to bypass the height condition check." ) .add() - .append(PREFAB_WEIGHTS_CODEC, PrefabSpawnerState::setPrefabWeights, PrefabSpawnerState::getPrefabWeightsNullable) + .append(PREFAB_WEIGHTS_CODEC, PrefabSpawnerBlock::setPrefabWeights, PrefabSpawnerBlock::getPrefabWeightsNullable) .documentation( "Determines the probability of each individual prefab file being selected to generate when the PrefabPath points to a folder containing multiple prefabs." ) .add() .build(); private String prefabPath; - private boolean fitHeightmap = false; - private boolean inheritSeed = true; - private boolean inheritHeightCondition = true; - private PrefabWeights prefabWeights = PrefabWeights.NONE; + private boolean fitHeightmap; + private boolean inheritSeed; + private boolean inheritHeightCondition; + private PrefabWeights prefabWeights; + + public static ComponentType getComponentType() { + return PrefabSpawnerModule.get().getPrefabSpawnerBlockType(); + } + + public PrefabSpawnerBlock() { + this.fitHeightmap = false; + this.inheritSeed = true; + this.inheritHeightCondition = true; + this.prefabWeights = PrefabWeights.NONE; + } + + public PrefabSpawnerBlock(String prefabPath, boolean fitHeightmap, boolean inheritSeed, boolean inheritHeightCondition, PrefabWeights prefabWeights) { + this.prefabPath = prefabPath; + this.fitHeightmap = fitHeightmap; + this.inheritSeed = inheritSeed; + this.inheritHeightCondition = inheritHeightCondition; + this.prefabWeights = prefabWeights; + } public String getPrefabPath() { return this.prefabPath; @@ -101,11 +122,21 @@ public class PrefabSpawnerState extends BlockState { return this.prefabWeights.size() == 0 ? null : this.prefabWeights; } - public static class PrefabSpawnerSettingsPage extends InteractiveCustomUIPage { - private final PrefabSpawnerState state; + @Nullable + @Override + public Component clone() { + return new PrefabSpawnerBlock(this.prefabPath, this.fitHeightmap, this.inheritSeed, this.inheritHeightCondition, this.prefabWeights); + } - public PrefabSpawnerSettingsPage(@Nonnull PlayerRef playerRef, PrefabSpawnerState state, @Nonnull CustomPageLifetime lifetime) { - super(playerRef, lifetime, PrefabSpawnerState.PrefabSpawnerSettingsPageEventData.CODEC); + public static class PrefabSpawnerSettingsPage extends InteractiveCustomUIPage { + private final BlockModule.BlockStateInfo info; + private final PrefabSpawnerBlock state; + + public PrefabSpawnerSettingsPage( + @Nonnull PlayerRef playerRef, BlockModule.BlockStateInfo info, PrefabSpawnerBlock state, @Nonnull CustomPageLifetime lifetime + ) { + super(playerRef, lifetime, PrefabSpawnerBlock.PrefabSpawnerSettingsPageEventData.CODEC); + this.info = info; this.state = state; } @@ -134,7 +165,7 @@ public class PrefabSpawnerState extends BlockState { } public void handleDataEvent( - @Nonnull Ref ref, @Nonnull Store store, @Nonnull PrefabSpawnerState.PrefabSpawnerSettingsPageEventData data + @Nonnull Ref ref, @Nonnull Store store, @Nonnull PrefabSpawnerBlock.PrefabSpawnerSettingsPageEventData data ) { this.state.prefabPath = data.prefabPath; this.state.fitHeightmap = data.fitHeightmap; @@ -142,7 +173,7 @@ public class PrefabSpawnerState extends BlockState { this.state.inheritHeightCondition = data.inheritHeightCondition; this.state.prefabWeights = PrefabWeights.parse(data.prefabWeights); this.state.prefabWeights.setDefaultWeight(data.defaultWeight); - this.state.markNeedsSave(); + this.info.markNeedsSaving(); Player playerComponent = store.getComponent(ref, Player.getComponentType()); playerComponent.getPageManager().setPage(ref, store, Page.None); } @@ -155,8 +186,8 @@ public class PrefabSpawnerState extends BlockState { public static final String KEY_INHERIT_HEIGHT_CONDITION = "@InheritHeightCondition"; public static final String KEY_DEFAULT_WEIGHT = "@DefaultWeight"; public static final String KEY_PREFAB_WEIGHTS = "@PrefabWeights"; - public static final BuilderCodec CODEC = BuilderCodec.builder( - PrefabSpawnerState.PrefabSpawnerSettingsPageEventData.class, PrefabSpawnerState.PrefabSpawnerSettingsPageEventData::new + public static final BuilderCodec CODEC = BuilderCodec.builder( + PrefabSpawnerBlock.PrefabSpawnerSettingsPageEventData.class, PrefabSpawnerBlock.PrefabSpawnerSettingsPageEventData::new ) .append(new KeyedCodec<>("@PrefabPath", Codec.STRING), (entry, s) -> entry.prefabPath = s, entry -> entry.prefabPath) .add() diff --git a/src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerModule.java b/src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerModule.java index e84f5b99..c8e0a2d0 100644 --- a/src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerModule.java +++ b/src/com/hypixel/hytale/server/core/modules/prefabspawner/PrefabSpawnerModule.java @@ -1,15 +1,30 @@ package com.hypixel.hytale.server.core.modules.prefabspawner; import com.hypixel.hytale.common.plugin.PluginManifest; +import com.hypixel.hytale.component.AddReason; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.RemoveReason; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.data.unknown.UnknownComponents; +import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.prefabspawner.commands.PrefabSpawnerCommand; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class PrefabSpawnerModule extends JavaPlugin { @Nonnull - public static final PluginManifest MANIFEST = PluginManifest.corePlugin(PrefabSpawnerModule.class).depends(BlockStateModule.class).build(); + public static final PluginManifest MANIFEST = PluginManifest.corePlugin(PrefabSpawnerModule.class).depends(BlockModule.class).build(); + private static PrefabSpawnerModule INSTANCE; + private ComponentType prefabSpawnerBlockType; + + public static PrefabSpawnerModule get() { + return INSTANCE; + } public PrefabSpawnerModule(@Nonnull JavaPluginInit init) { super(init); @@ -17,7 +32,37 @@ public class PrefabSpawnerModule extends JavaPlugin { @Override protected void setup() { - this.getBlockStateRegistry().registerBlockState(PrefabSpawnerState.class, "prefabspawner", PrefabSpawnerState.CODEC); + INSTANCE = this; this.getCommandRegistry().registerCommand(new PrefabSpawnerCommand()); + this.prefabSpawnerBlockType = this.getChunkStoreRegistry().registerComponent(PrefabSpawnerBlock.class, "PrefabSpawner", PrefabSpawnerBlock.CODEC); + this.getChunkStoreRegistry().registerSystem(new PrefabSpawnerModule.MigratePrefabSpawn()); + } + + public ComponentType getPrefabSpawnerBlockType() { + return this.prefabSpawnerBlockType; + } + + public static class MigratePrefabSpawn extends BlockModule.MigrationSystem { + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + UnknownComponents unknownComponents = holder.getComponent(ChunkStore.REGISTRY.getUnknownComponentType()); + + assert unknownComponents != null; + + PrefabSpawnerBlock prefabSpawnerBlock = unknownComponents.removeComponent("prefabspawner", PrefabSpawnerBlock.CODEC); + if (prefabSpawnerBlock != null) { + holder.putComponent(PrefabSpawnerBlock.getComponentType(), prefabSpawnerBlock); + } + } + + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } + + @Nullable + @Override + public Query getQuery() { + return ChunkStore.REGISTRY.getUnknownComponentType(); + } } } diff --git a/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerGetCommand.java b/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerGetCommand.java index 2b09f782..a2b62745 100644 --- a/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerGetCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerGetCommand.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.server.core.modules.prefabspawner.commands; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; -import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerState; +import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerBlock; import com.hypixel.hytale.server.core.prefab.PrefabWeights; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import java.util.Objects; @@ -14,7 +14,7 @@ public class PrefabSpawnerGetCommand extends TargetPrefabSpawnerCommand { } @Override - protected void execute(@Nonnull CommandContext context, @Nonnull WorldChunk chunk, @Nonnull PrefabSpawnerState prefabSpawner) { + protected void execute(@Nonnull CommandContext context, @Nonnull WorldChunk chunk, @Nonnull PrefabSpawnerBlock prefabSpawner) { String prefab = Objects.requireNonNullElse(prefabSpawner.getPrefabPath(), ""); context.sendMessage(Message.translation("server.commands.prefabspawner.get.path").param("prefab", prefab)); context.sendMessage(Message.translation("server.commands.prefabspawner.get.fitsHeightmap").param("fitHeightmap", prefabSpawner.isFitHeightmap())); diff --git a/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerSetCommand.java b/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerSetCommand.java index 994f4862..1d4a2837 100644 --- a/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerSetCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerSetCommand.java @@ -5,7 +5,7 @@ import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; -import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerState; +import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerBlock; import com.hypixel.hytale.server.core.prefab.PrefabWeights; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import javax.annotation.Nonnull; @@ -37,7 +37,7 @@ public class PrefabSpawnerSetCommand extends TargetPrefabSpawnerCommand { } @Override - protected void execute(@Nonnull CommandContext context, @Nonnull WorldChunk chunk, @Nonnull PrefabSpawnerState prefabSpawner) { + protected void execute(@Nonnull CommandContext context, @Nonnull WorldChunk chunk, @Nonnull PrefabSpawnerBlock prefabSpawner) { String prefabPath = this.prefabPathArg.get(context); prefabSpawner.setPrefabPath(prefabPath); if (this.fitHeightmapArg.provided(context)) { diff --git a/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerWeightCommand.java b/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerWeightCommand.java index 8d71fb73..4111609b 100644 --- a/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerWeightCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/PrefabSpawnerWeightCommand.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; -import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerState; +import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerBlock; import com.hypixel.hytale.server.core.prefab.PrefabWeights; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import javax.annotation.Nonnull; @@ -20,7 +20,7 @@ public class PrefabSpawnerWeightCommand extends TargetPrefabSpawnerCommand { } @Override - protected void execute(@Nonnull CommandContext context, @Nonnull WorldChunk chunk, @Nonnull PrefabSpawnerState prefabSpawner) { + protected void execute(@Nonnull CommandContext context, @Nonnull WorldChunk chunk, @Nonnull PrefabSpawnerBlock prefabSpawner) { String prefab = this.prefabArg.get(context); Float weight = this.weightArg.get(context); PrefabWeights prefabWeights = prefabSpawner.getPrefabWeights(); diff --git a/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/TargetPrefabSpawnerCommand.java b/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/TargetPrefabSpawnerCommand.java index 7c28983d..03dc0c32 100644 --- a/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/TargetPrefabSpawnerCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/prefabspawner/commands/TargetPrefabSpawnerCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.modules.prefabspawner.commands; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -11,13 +10,14 @@ import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeIntPosition; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand; import com.hypixel.hytale.server.core.command.system.exceptions.GeneralCommandException; -import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerState; +import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerBlock; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import javax.annotation.Nonnull; +import org.joml.Vector3i; public abstract class TargetPrefabSpawnerCommand extends AbstractWorldCommand { @Nonnull @@ -54,13 +54,18 @@ public abstract class TargetPrefabSpawnerCommand extends AbstractWorldCommand { } WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(target.x, target.z)); - BlockState state = chunk.getState(target.x, target.y, target.z); - if (!(state instanceof PrefabSpawnerState)) { + Ref blockEntityRef = chunk.getBlockComponentEntity(target.x, target.y, target.z); + if (blockEntityRef == null) { context.sendMessage(Message.translation("server.commands.prefabspawner.spawnerNotFoundAtTarget").param("pos", target.toString())); } else { - this.execute(context, chunk, (PrefabSpawnerState)state); + PrefabSpawnerBlock prefabSpawnerBlock = blockEntityRef.getStore().getComponent(blockEntityRef, PrefabSpawnerBlock.getComponentType()); + if (prefabSpawnerBlock == null) { + context.sendMessage(Message.translation("server.commands.prefabspawner.spawnerNotFoundAtTarget").param("pos", target.toString())); + } else { + this.execute(context, chunk, prefabSpawnerBlock); + } } } - protected abstract void execute(@Nonnull CommandContext var1, @Nonnull WorldChunk var2, @Nonnull PrefabSpawnerState var3); + protected abstract void execute(@Nonnull CommandContext var1, @Nonnull WorldChunk var2, @Nonnull PrefabSpawnerBlock var3); } diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/ProjectileModule.java b/src/com/hypixel/hytale/server/core/modules/projectile/ProjectileModule.java index 16732f4d..0389befb 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/ProjectileModule.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/ProjectileModule.java @@ -8,17 +8,14 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.server.core.asset.type.model.config.Model; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.InteractionManager; -import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.modules.collision.CollisionModule; import com.hypixel.hytale.server.core.modules.entity.DespawnComponent; @@ -54,6 +51,7 @@ import java.time.Duration; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ProjectileModule extends JavaPlugin { @Nonnull @@ -115,13 +113,13 @@ public class ProjectileModule extends JavaPlugin { @Nonnull Vector3d direction ) { Holder holder = EntityStore.REGISTRY.newHolder(); - Vector3f rotation = new Vector3f(); + Rotation3f rotation = new Rotation3f(); Direction rotationOffset = config.getSpawnRotationOffset(); rotation.setYaw(PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(direction.x, direction.z))); rotation.setPitch(PhysicsMath.pitchFromDirection(direction.x, direction.y, direction.z)); rotation.add(rotationOffset.pitch, rotationOffset.yaw, rotationOffset.roll); - PhysicsMath.vectorFromAngles(rotation.getYaw(), rotation.getPitch(), direction); - Vector3d offset = config.getCalculatedOffset(rotation.getPitch(), rotation.getYaw()); + PhysicsMath.vectorFromAngles(rotation.yaw(), rotation.pitch(), direction); + Vector3d offset = config.getCalculatedOffset(rotation.pitch(), rotation.yaw()); position.add(offset); holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(position, rotation)); holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(rotation)); @@ -141,7 +139,7 @@ public class ProjectileModule extends JavaPlugin { } holder.addComponent(Velocity.getComponentType(), new Velocity()); - config.getPhysicsConfig().apply(holder, creatorRef, direction.clone().scale(config.getLaunchForce()), commandBuffer, predictionId != null); + config.getPhysicsConfig().apply(holder, creatorRef, new Vector3d(direction).mul(config.getLaunchForce()), commandBuffer, predictionId != null); holder.ensureComponent(EntityStore.REGISTRY.getNonSerializedComponentType()); holder.addComponent( DespawnComponent.getComponentType(), @@ -172,15 +170,13 @@ public class ProjectileModule extends JavaPlugin { private static void onProjectileSpawnInteraction(@Nonnull Ref ref, @Nonnull Ref creatorRef, @Nonnull Store store) { InteractionManager interactionManagerComponent = store.getComponent(creatorRef, InteractionModule.get().getInteractionManagerComponent()); if (interactionManagerComponent != null) { - if (EntityUtils.getEntity(creatorRef, store) instanceof LivingEntity livingEntity) { - InteractionContext context = InteractionContext.forProxyEntity(interactionManagerComponent, livingEntity, ref); - String rootInteractionId = context.getRootInteractionId(InteractionType.ProjectileSpawn); - if (rootInteractionId != null) { - RootInteraction rootInteraction = RootInteraction.getRootInteractionOrUnknown(rootInteractionId); - if (rootInteraction != null) { - InteractionChain chain = interactionManagerComponent.initChain(InteractionType.ProjectileSpawn, context, rootInteraction, true); - interactionManagerComponent.queueExecuteChain(chain); - } + InteractionContext context = InteractionContext.forProxyEntity(interactionManagerComponent, creatorRef, ref, store); + String rootInteractionId = context.getRootInteractionId(InteractionType.ProjectileSpawn); + if (rootInteractionId != null) { + RootInteraction rootInteraction = RootInteraction.getRootInteractionOrUnknown(rootInteractionId); + if (rootInteraction != null) { + InteractionChain chain = interactionManagerComponent.initChain(InteractionType.ProjectileSpawn, context, rootInteraction, true); + interactionManagerComponent.queueExecuteChain(chain); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/config/BounceConsumer.java b/src/com/hypixel/hytale/server/core/modules/projectile/config/BounceConsumer.java index 597400dc..ce7130ef 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/config/BounceConsumer.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/config/BounceConsumer.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.server.core.modules.projectile.config; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; @FunctionalInterface public interface BounceConsumer { diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/config/ImpactConsumer.java b/src/com/hypixel/hytale/server/core/modules/projectile/config/ImpactConsumer.java index c4a3ae3e..d9999fcb 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/config/ImpactConsumer.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/config/ImpactConsumer.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.server.core.modules.projectile.config; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; @FunctionalInterface public interface ImpactConsumer { diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/config/PhysicsConfig.java b/src/com/hypixel/hytale/server/core/modules/projectile/config/PhysicsConfig.java index 035e6b26..721d5788 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/config/PhysicsConfig.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/config/PhysicsConfig.java @@ -4,11 +4,11 @@ import com.hypixel.hytale.codec.lookup.CodecMapCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.io.NetworkSerializable; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public interface PhysicsConfig extends NetworkSerializable { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/config/ProjectileConfig.java b/src/com/hypixel/hytale/server/core/modules/projectile/config/ProjectileConfig.java index 1f555f9b..2ba34dad 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/config/ProjectileConfig.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/config/ProjectileConfig.java @@ -12,10 +12,9 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.codecs.map.EnumMapCodec; import com.hypixel.hytale.codec.validation.ValidatorCache; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3fUtil; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.InteractionType; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; @@ -29,6 +28,8 @@ import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class ProjectileConfig implements JsonAssetWithMap>, @@ -55,7 +56,7 @@ public class ProjectileConfig .appendInherited(new KeyedCodec<>("LaunchForce", Codec.DOUBLE), (o, i) -> o.launchForce = i, o -> o.launchForce, (o, p) -> o.launchForce = p.launchForce) .add() .appendInherited( - new KeyedCodec<>("SpawnOffset", ProtocolCodecs.VECTOR3F), (o, i) -> o.spawnOffset = i, o -> o.spawnOffset, (o, p) -> o.spawnOffset = p.spawnOffset + new KeyedCodec<>("SpawnOffset", Vector3fUtil.CODEC), (o, i) -> o.spawnOffset = i, o -> o.spawnOffset, (o, p) -> o.spawnOffset = p.spawnOffset ) .addValidator(Validators.nonNull()) .add() @@ -255,6 +256,7 @@ public class ProjectileConfig config.spawnOffset = this.spawnOffset; config.rotationOffset = this.spawnRotationOffset; config.launchLocalSoundEventIndex = this.launchLocalSoundEventIndex; + config.launchWorldSoundEventIndex = this.launchWorldSoundEventIndex; config.projectileSoundEventIndex = this.projectileSoundEventIndex; config.interactions = new EnumMap<>(InteractionType.class); diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/config/StandardPhysicsConfig.java b/src/com/hypixel/hytale/server/core/modules/projectile/config/StandardPhysicsConfig.java index 26abb746..21c6ea1d 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/config/StandardPhysicsConfig.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/config/StandardPhysicsConfig.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.PhysicsType; import com.hypixel.hytale.protocol.RotationMode; import com.hypixel.hytale.server.core.entity.UUIDComponent; @@ -17,6 +16,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class StandardPhysicsConfig implements PhysicsConfig { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/config/StandardPhysicsProvider.java b/src/com/hypixel/hytale/server/core/modules/projectile/config/StandardPhysicsProvider.java index 0ac67492..669c28ff 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/config/StandardPhysicsProvider.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/config/StandardPhysicsProvider.java @@ -8,17 +8,13 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.NearestBlockUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.math.vector.Vector4d; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.InteractionType; -import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.InteractionManager; -import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.meta.DynamicMetaStore; import com.hypixel.hytale.server.core.modules.collision.BlockCollisionProvider; import com.hypixel.hytale.server.core.modules.collision.BlockContactData; @@ -47,6 +43,9 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.joml.Vector4d; public class StandardPhysicsProvider implements IBlockCollisionConsumer, Component { public static final int WATER_DETECTION_EXTREMA_COUNT = 2; @@ -137,20 +136,20 @@ public class StandardPhysicsProvider implements IBlockCollisionConsumer, Compone this.forceProviderEntity = new ForceProviderEntity(boundingBox); this.forceProviderEntity.setDensity(physicsConfig.density); this.forceProviders = new ForceProvider[]{this.forceProviderEntity}; - this.forceProviderStandardState.nextTickVelocity.assign(initialForce); + this.forceProviderStandardState.nextTickVelocity.set(initialForce); this.recomputeDragFactors(boundingBox); if (!predicted) { this.impactConsumer = (ref, position, targetRef, collisionDetailName, commandBuffer) -> { if (creatorUuid != null) { Ref creatorRef = commandBuffer.getExternalData().getRefFromUUID(creatorUuid); - if (creatorRef != null && creatorRef.isValid() && EntityUtils.getEntity(creatorRef, commandBuffer) instanceof LivingEntity livingEntity) { + if (creatorRef != null && creatorRef.isValid()) { InteractionManager interactionManagerComponent = commandBuffer.getComponent( creatorRef, InteractionModule.get().getInteractionManagerComponent() ); if (interactionManagerComponent == null) { commandBuffer.removeEntity(ref, RemoveReason.REMOVE); } else { - InteractionContext context = InteractionContext.forProxyEntity(interactionManagerComponent, livingEntity, ref); + InteractionContext context = InteractionContext.forProxyEntity(interactionManagerComponent, creatorRef, ref, commandBuffer); DynamicMetaStore metaStore = context.getMetaStore(); metaStore.putMetaObject(Interaction.TARGET_ENTITY, targetRef); metaStore.putMetaObject(Interaction.HIT_LOCATION, new Vector4d(position.x, position.y, position.z, 1.0)); @@ -173,14 +172,14 @@ public class StandardPhysicsProvider implements IBlockCollisionConsumer, Compone this.bounceConsumer = (ref, position, commandBuffer) -> { if (creatorUuid != null) { Ref creatorRef = commandBuffer.getExternalData().getRefFromUUID(creatorUuid); - if (creatorRef != null && creatorRef.isValid() && EntityUtils.getEntity(creatorRef, commandBuffer) instanceof LivingEntity livingEntity) { + if (creatorRef != null && creatorRef.isValid()) { InteractionManager interactionManagerComponent = commandBuffer.getComponent( creatorRef, InteractionModule.get().getInteractionManagerComponent() ); if (interactionManagerComponent == null) { commandBuffer.removeEntity(ref, RemoveReason.REMOVE); } else { - InteractionContext context = InteractionContext.forProxyEntity(interactionManagerComponent, livingEntity, ref); + InteractionContext context = InteractionContext.forProxyEntity(interactionManagerComponent, creatorRef, ref, commandBuffer); context.getMetaStore().putMetaObject(Interaction.HIT_LOCATION, new Vector4d(position.x, position.y, position.z, 1.0)); InteractionType interactionType = InteractionType.ProjectileBounce; String rootInteractionId = context.getRootInteractionId(interactionType); @@ -214,16 +213,16 @@ public class StandardPhysicsProvider implements IBlockCollisionConsumer, Compone BlockMaterial blockMaterial = blockData.getBlockType().getMaterial(); if (this.physicsConfig.moveOutOfSolidSpeed > 0.0 && contactData.isOverlapping() && blockMaterial == BlockMaterial.Solid) { Vector3i nearestBlock = NearestBlockUtil.findNearestBlock(this.position, (block, w) -> { - WorldChunk worldChunk = w.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(block.getX(), block.getZ())); + WorldChunk worldChunk = w.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(block.x(), block.z())); return worldChunk != null && worldChunk.getBlockType(block).getMaterial() != BlockMaterial.Solid; }, this.world); if (nearestBlock != null) { - this.tempVector.assign(nearestBlock.x, nearestBlock.y, nearestBlock.z); + this.tempVector.set(nearestBlock.x, nearestBlock.y, nearestBlock.z); this.tempVector.add(0.5, 0.5, 0.5); - this.tempVector.subtract(this.position); - this.tempVector.setLength(this.physicsConfig.moveOutOfSolidSpeed); + this.tempVector.sub(this.position); + this.tempVector.normalize(this.physicsConfig.moveOutOfSolidSpeed); } else { - this.tempVector.assign(0.0, this.physicsConfig.moveOutOfSolidSpeed, 0.0); + this.tempVector.set(0.0, this.physicsConfig.moveOutOfSolidSpeed, 0.0); } this.moveOutOfSolidVelocity.add(this.tempVector); @@ -260,14 +259,14 @@ public class StandardPhysicsProvider implements IBlockCollisionConsumer, Compone if (surfaceAlignment >= 0.0) { return IBlockCollisionConsumer.Result.CONTINUE; } else { - this.contactPosition.assign(contactData.getCollisionPoint()); - this.contactNormal.assign(contactData.getCollisionNormal()); + this.contactPosition.set(contactData.getCollisionPoint()); + this.contactNormal.set(contactData.getCollisionNormal()); if (this.physicsConfig.allowRolling) { - Vector3d remaining = this.stateBefore.position.clone().add(this.movement).subtract(this.contactPosition); - if (!remaining.equals(Vector3d.ZERO)) { + Vector3d remaining = new Vector3d(this.stateBefore.position).add(this.movement).sub(this.contactPosition); + if (!remaining.equals(Vector3dUtil.ZERO)) { double t = remaining.dot(this.contactNormal); - this.nextMovement.assign(remaining); - this.nextMovement.addScaled(this.contactNormal, -t); + this.nextMovement.set(remaining); + this.nextMovement.fma(-t, this.contactNormal); this.isSliding = true; } } @@ -308,7 +307,7 @@ public class StandardPhysicsProvider implements IBlockCollisionConsumer, Compone this.entityCollisionProvider.clear(); } - public void rotateBody(double dt, @Nonnull Vector3f bodyRotation) { + public void rotateBody(double dt, @Nonnull Rotation3f bodyRotation) { if (this.physicsConfig.computeYaw || this.physicsConfig.computePitch) { double vx = this.stateAfter.velocity.x; double vz = this.stateAfter.velocity.z; @@ -332,10 +331,10 @@ public class StandardPhysicsProvider implements IBlockCollisionConsumer, Compone } if (this.physicsConfig.computePitch) { - float pitch = bodyRotation.getPitch(); + float pitch = bodyRotation.pitch(); float targetPitch = PhysicsMath.pitchFromDirection(vx, this.velocity.y, vz); float delta = PhysicsMath.normalizeTurnAngle(targetPitch - pitch); - float maxDelta = (float)(this.velocity.squaredLength() * dt * this.physicsConfig.speedRotationFactor); + float maxDelta = (float)(this.velocity.lengthSquared() * dt * this.physicsConfig.speedRotationFactor); if (delta > maxDelta) { targetPitch = pitch + maxDelta; delta = maxDelta; @@ -345,12 +344,12 @@ public class StandardPhysicsProvider implements IBlockCollisionConsumer, Compone } bodyRotation.setPitch(targetPitch); - this.forceProviderStandardState.externalForce.addScaled(this.stateAfter.velocity, delta * -this.physicsConfig.rotationForce); + this.forceProviderStandardState.externalForce.fma(delta * -this.physicsConfig.rotationForce, this.stateAfter.velocity); } break; case VelocityRoll: bodyRotation.setYaw(PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(vx, vz))); - bodyRotation.setPitch(bodyRotation.getPitch() - (float)this.stateBefore.velocity.length() * this.physicsConfig.rollingSpeed); + bodyRotation.setPitch(bodyRotation.pitch() - (float)this.stateBefore.velocity.length() * this.physicsConfig.rollingSpeed); } } } diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/interaction/ProjectileInteraction.java b/src/com/hypixel/hytale/server/core/modules/projectile/interaction/ProjectileInteraction.java index 591dc8a2..02748cd8 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/interaction/ProjectileInteraction.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/interaction/ProjectileInteraction.java @@ -5,9 +5,9 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.Interaction; import com.hypixel.hytale.protocol.InteractionSyncData; @@ -26,6 +26,7 @@ import com.hypixel.hytale.server.core.util.TargetUtil; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ProjectileInteraction extends SimpleInstantInteraction implements BallisticDataProvider { @Nonnull @@ -73,18 +74,18 @@ public class ProjectileInteraction extends SimpleInstantInteraction implements B assert commandBuffer != null; boolean hasClientState = clientState != null && clientState.attackerPos != null && clientState.attackerRot != null; + Vector3d direction = new Vector3d(); Vector3d position; - Vector3d direction; UUID generatedUUID; if (hasClientState) { position = PositionUtil.toVector3d(clientState.attackerPos); - Vector3f lookVec = PositionUtil.toRotation(clientState.attackerRot); - direction = new Vector3d(lookVec.getYaw(), lookVec.getPitch()); + Rotation3f lookVec = PositionUtil.toRotation(clientState.attackerRot); + Vector3dUtil.setYawPitch(lookVec.yaw(), lookVec.pitch(), direction); generatedUUID = clientState.generatedUUID; } else { Transform lookVec = TargetUtil.getLook(ref, commandBuffer); position = lookVec.getPosition(); - direction = lookVec.getDirection(); + direction.set(lookVec.getDirection()); generatedUUID = null; } @@ -102,8 +103,8 @@ public class ProjectileInteraction extends SimpleInstantInteraction implements B Transform lookVec = TargetUtil.getLook(ref, commandBuffer); InteractionSyncData state = context.getState(); state.attackerPos = PositionUtil.toPositionPacket(lookVec.getPosition()); - Vector3f rotation = lookVec.getRotation(); - state.attackerRot = new Direction(rotation.getYaw(), rotation.getPitch(), rotation.getRoll()); + Rotation3f rotation = lookVec.getRotation(); + state.attackerRot = new Direction(rotation.yaw(), rotation.pitch(), rotation.roll()); } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/modules/projectile/system/StandardPhysicsTickSystem.java b/src/com/hypixel/hytale/server/core/modules/projectile/system/StandardPhysicsTickSystem.java index 27fee6e8..0801790f 100644 --- a/src/com/hypixel/hytale/server/core/modules/projectile/system/StandardPhysicsTickSystem.java +++ b/src/com/hypixel/hytale/server/core/modules/projectile/system/StandardPhysicsTickSystem.java @@ -11,7 +11,7 @@ import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.collision.BlockCollisionProvider; import com.hypixel.hytale.server.core.modules.collision.BlockTracker; import com.hypixel.hytale.server.core.modules.collision.EntityContactData; @@ -36,6 +36,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class StandardPhysicsTickSystem extends EntityTickingSystem { @Nonnull @@ -101,7 +102,7 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem ForceProviderStandardState forceState = physicsComponent.getForceProviderStandardState(); RestingSupport restingSupport = physicsComponent.getRestingSupport(); if (physicsComponent.getState() == StandardPhysicsProvider.STATE.RESTING) { - if (forceState.externalForce.squaredLength() == 0.0 && !restingSupport.hasChanged(world)) { + if (forceState.externalForce.lengthSquared() == 0.0 && !restingSupport.hasChanged(world)) { return; } @@ -122,13 +123,13 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem int bounceCount = physicsConfig.getBounceCount(); boolean allowRolling = physicsConfig.isAllowRolling(); physicsComponent.setWorld(world); - position.assign(transformComponent.getPosition()); + position.set(transformComponent.getPosition()); velocityComponent.assignVelocityTo(velocity); double mass = forceProviderEntity.getMass(boundingBox.getVolume()); forceState.convertToForces(dt, mass); forceState.updateVelocity(velocity); - if (!(velocity.squaredLength() * dt * dt >= 1.0000000000000002E-10) && !(forceState.externalForce.squaredLength() >= 0.0)) { - velocity.assign(Vector3d.ZERO); + if (!(velocity.lengthSquared() * dt * dt >= 1.0000000000000002E-10) && !(forceState.externalForce.lengthSquared() >= 0.0)) { + velocity.zero(); } else { physicsComponent.setState(StandardPhysicsProvider.STATE.ACTIVE); } @@ -137,17 +138,17 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem physicsComponent.setState(StandardPhysicsProvider.STATE.ACTIVE); } - stateBefore.position.assign(position); - stateBefore.velocity.assign(velocity); + stateBefore.position.set(position); + stateBefore.velocity.set(velocity); forceProviderEntity.setForceProviderStandardState(forceState); stateUpdater.update(stateBefore, stateAfter, mass, dt, physicsComponent.isOnGround(), forceProviders); - velocity.assign(stateAfter.velocity); - movement.assign(velocity).scale(dt); + velocity.set(stateAfter.velocity); + movement.set(velocity).mul(dt); forceState.clear(); - if (velocity.squaredLength() * dt * dt >= 1.0000000000000002E-10) { + if (velocity.lengthSquared() * dt * dt >= 1.0000000000000002E-10) { physicsComponent.setState(StandardPhysicsProvider.STATE.ACTIVE); } else { - velocity.assign(Vector3d.ZERO); + velocity.zero(); } EntityRefCollisionProvider entityCollisionProvider = physicsComponent.getEntityCollisionProvider(); @@ -171,33 +172,33 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem physicsComponent.setBounced(false); physicsComponent.setOnGround(false); - moveOutOfSolidVelocity.assign(Vector3d.ZERO); + moveOutOfSolidVelocity.zero(); physicsComponent.setMovedInsideSolid(false); physicsComponent.setDisplacedMass(0.0); physicsComponent.setSubSurfaceVolume(0.0); physicsComponent.setEnterFluid(Double.MAX_VALUE); physicsComponent.setLeaveFluid(-Double.MAX_VALUE); physicsComponent.setCollisionStart(maxRelativeDistance); - contactPosition.assign(position).addScaled(movement, physicsComponent.getCollisionStart()); - contactNormal.assign(Vector3d.ZERO); + contactPosition.set(position).fma(physicsComponent.getCollisionStart(), movement); + contactNormal.zero(); physicsComponent.setSliding(true); - Vector3d tmpPosition = position.clone(); - nextMovement.assign(Vector3d.ZERO); + Vector3d tmpPosition = new Vector3d(position); + nextMovement.zero(); - while (physicsComponent.isSliding() && !movement.equals(Vector3d.ZERO)) { - contactPosition.assign(tmpPosition).addScaled(movement, physicsComponent.getCollisionStart()); + while (physicsComponent.isSliding() && !movement.equals(Vector3dUtil.ZERO)) { + contactPosition.set(tmpPosition).fma(physicsComponent.getCollisionStart(), movement); physicsComponent.setSliding(false); blockCollisionProvider.cast(world, boundingBox, tmpPosition, movement, physicsComponent, triggerTracker, maxRelativeDistance); - movement.assign(nextMovement); - tmpPosition.assign(contactPosition); + movement.set(nextMovement); + tmpPosition.set(contactPosition); } - movement.assign(tmpPosition).add(nextMovement).subtract(position); + movement.set(tmpPosition).add(nextMovement).sub(position); physicsComponent.getFluidTracker().reset(); double density = physicsComponent.getDisplacedMass() > 0.0 ? physicsComponent.getDisplacedMass() / physicsComponent.getSubSurfaceVolume() : 1.2; if (physicsComponent.isMovedInsideSolid()) { - position.addScaled(moveOutOfSolidVelocity, dt); - velocity.assign(moveOutOfSolidVelocity); + position.fma((double)dt, moveOutOfSolidVelocity); + velocity.set(moveOutOfSolidVelocity); forceState.dragCoefficient = physicsComponent.getDragCoefficient(density); forceState.displacedMass = physicsComponent.getDisplacedMass(); forceState.gravity = gravity; @@ -218,7 +219,7 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem if (velocityClip > 0.0 && velocityClip < 1.0) { stateUpdater.update(stateBefore, stateAfter, mass, dt * velocityClip, physicsComponent.isOnGround(), forceProviders); - velocity.assign(stateAfter.velocity); + velocity.set(stateAfter.velocity); } if (physicsComponent.isInFluid() @@ -236,7 +237,7 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem } if (enteringWater) { - forceState.externalImpulse.addScaled(stateAfter.velocity, -physicsConfig.getHitWaterImpulseLoss() * mass); + forceState.externalImpulse.fma(-physicsConfig.getHitWaterImpulseLoss() * mass, stateAfter.velocity); } forceState.displacedMass = physicsComponent.getDisplacedMass(); @@ -245,7 +246,7 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem if (entityCollisionProvider.getCount() > 0) { EntityContactData contact = entityCollisionProvider.getContact(0); Ref contactRef = contact.getEntityReference(); - position.assign(contact.getCollisionPoint()); + position.set(contact.getCollisionPoint()); physicsComponent.setState(StandardPhysicsProvider.STATE.INACTIVE); if (physicsComponent.getImpactConsumer() != null) { physicsComponent.getImpactConsumer().onImpact(selfRef, position, contactRef, contact.getCollisionDetailName(), commandBuffer); @@ -258,20 +259,20 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem physicsComponent.rotateBody(dt, transformComponent.getRotation()); physicsComponent.finishTick(transformComponent, velocityComponent); } else { - position.assign(contactPosition); + position.set(contactPosition); physicsComponent.incrementBounces(); SimplePhysicsProvider.computeReflectedVector(velocity, contactNormal, velocity); if (bounceCount == -1 || physicsComponent.getBounces() <= bounceCount) { - velocity.scale(physicsConfig.getBounciness()); + velocity.mul(physicsConfig.getBounciness()); } if ((bounceCount == -1 || physicsComponent.getBounces() <= bounceCount) - && !(velocity.squaredLength() * dt * dt < physicsConfig.getBounceLimit() * physicsConfig.getBounceLimit())) { + && !(velocity.lengthSquared() * dt * dt < physicsConfig.getBounceLimit() * physicsConfig.getBounceLimit())) { if (physicsComponent.getBounceConsumer() != null) { physicsComponent.getBounceConsumer().onBounce(selfRef, position, commandBuffer); } } else { - boolean hitGround = contactNormal.equals(Vector3d.UP); + boolean hitGround = contactNormal.equals(Vector3dUtil.UP); if (!allowRolling && (physicsConfig.isSticksVertically() || hitGround)) { physicsComponent.setState(StandardPhysicsProvider.STATE.RESTING); restingSupport.rest(world, boundingBox, position); @@ -283,10 +284,10 @@ public class StandardPhysicsTickSystem extends EntityTickingSystem if (allowRolling) { velocity.y = 0.0; - velocity.scale(physicsConfig.getRollingFrictionFactor()); + velocity.mul(physicsConfig.getRollingFrictionFactor()); physicsComponent.setOnGround(hitGround); } else { - velocity.assign(Vector3d.ZERO); + velocity.zero(); } } diff --git a/src/com/hypixel/hytale/server/core/modules/serverplayerlist/ServerPlayerListModule.java b/src/com/hypixel/hytale/server/core/modules/serverplayerlist/ServerPlayerListModule.java index a46ea238..3b7009af 100644 --- a/src/com/hypixel/hytale/server/core/modules/serverplayerlist/ServerPlayerListModule.java +++ b/src/com/hypixel/hytale/server/core/modules/serverplayerlist/ServerPlayerListModule.java @@ -23,7 +23,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import java.util.List; +import java.util.Collection; import java.util.UUID; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; @@ -56,7 +56,7 @@ public class ServerPlayerListModule extends JavaPlugin { private void onPlayerConnect(@Nonnull PlayerConnectEvent event) { PlayerRef joiningPlayerRef = event.getPlayerRef(); UUID joiningPlayerUuid = joiningPlayerRef.getUuid(); - List allPlayers = Universe.get().getPlayers(); + Collection allPlayers = Universe.get().getPlayers(); ServerPlayerListPlayer[] serverListPlayers = new ServerPlayerListPlayer[allPlayers.size()]; int index = 0; @@ -102,7 +102,7 @@ public class ServerPlayerListModule extends JavaPlugin { } private void broadcastPingUpdates() { - List allPlayers = Universe.get().getPlayers(); + Collection allPlayers = Universe.get().getPlayers(); if (!allPlayers.isEmpty()) { Object2IntOpenHashMap pingMap = new Object2IntOpenHashMap<>(allPlayers.size()); diff --git a/src/com/hypixel/hytale/server/core/modules/splitvelocity/VelocityConfig.java b/src/com/hypixel/hytale/server/core/modules/splitvelocity/VelocityConfig.java index 83eedd5e..382acf6f 100644 --- a/src/com/hypixel/hytale/server/core/modules/splitvelocity/VelocityConfig.java +++ b/src/com/hypixel/hytale/server/core/modules/splitvelocity/VelocityConfig.java @@ -61,26 +61,50 @@ public class VelocityConfig implements NetworkSerializable { public static final long NANOS_PER_DAY = ChronoUnit.DAYS.getDuration().toNanos(); @@ -191,13 +192,13 @@ public class WorldTimeResource implements Resource { return this.sunlightFactor; } - public void setGameTime(@Nonnull Instant gameTime, @Nonnull World world, @Nonnull Store store) { + public void setGameTime(@Nonnull Instant gameTime, @Nonnull World world, @Nonnull ComponentAccessor store) { this.setGameTime0(gameTime); this.updateMoonPhase(world, store); this.broadcastTimePacket(store); } - public void setDayTime(double dayTime, @Nonnull World world, @Nonnull Store store) { + public void setDayTime(double dayTime, @Nonnull World world, @Nonnull ComponentAccessor store) { if (!(dayTime < 0.0) && !(dayTime > 1.0)) { Instant oldGameTime = this.gameTime; Instant dayStart = oldGameTime.truncatedTo(ChronoUnit.DAYS); @@ -212,7 +213,7 @@ public class WorldTimeResource implements Resource { } } - public void broadcastTimePacket(@Nonnull Store store) { + public void broadcastTimePacket(@Nonnull ComponentAccessor store) { PlayerUtil.broadcastPacketToPlayers(store, this.currentTimePacket); } @@ -266,7 +267,7 @@ public class WorldTimeResource implements Resource { } @Nonnull - public Vector3f getSunDirection() { + public Vector3d getSunDirection() { float dayTime = this.getDayProgress() * HOURS_PER_DAY; float daylightDuration = 0.6F * HOURS_PER_DAY; float nightDuration = HOURS_PER_DAY - daylightDuration; @@ -286,16 +287,16 @@ public class WorldTimeResource implements Resource { ); } - Vector3f sunPosition = new Vector3f(TrigMathUtil.cos(sunAngle), TrigMathUtil.sin(sunAngle) * 2.0F, TrigMathUtil.sin(sunAngle)); + Vector3d sunPosition = new Vector3d(TrigMathUtil.cos(sunAngle), TrigMathUtil.sin(sunAngle) * 2.0F, TrigMathUtil.sin(sunAngle)); sunPosition.normalize(); - float tweakedSunHeight = sunPosition.y + 0.2F; - if (tweakedSunHeight > 0.0F) { - sunPosition.scale(-1.0F); + double tweakedSunHeight = sunPosition.y + 0.2F; + if (tweakedSunHeight > 0.0) { + sunPosition.negate(); } - sunPosition.x = MathUtil.lerp(sunPosition.x, Vector3f.DOWN.x, 0.35F); - sunPosition.y = MathUtil.lerp(sunPosition.y, Vector3f.DOWN.y, 0.35F); - sunPosition.z = MathUtil.lerp(sunPosition.z, Vector3f.DOWN.z, 0.35F); + sunPosition.x = MathUtil.lerp(sunPosition.x, Vector3dUtil.DOWN.x(), 0.35F); + sunPosition.y = MathUtil.lerp(sunPosition.y, Vector3dUtil.DOWN.y(), 0.35F); + sunPosition.z = MathUtil.lerp(sunPosition.z, Vector3dUtil.DOWN.z(), 0.35F); return sunPosition; } diff --git a/src/com/hypixel/hytale/server/core/modules/time/commands/TimeCommand.java b/src/com/hypixel/hytale/server/core/modules/time/commands/TimeCommand.java index 4b76b082..84f549ba 100644 --- a/src/com/hypixel/hytale/server/core/modules/time/commands/TimeCommand.java +++ b/src/com/hypixel/hytale/server/core/modules/time/commands/TimeCommand.java @@ -39,8 +39,8 @@ public class TimeCommand extends AbstractWorldCommand { public void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store store) { WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType()); LocalDateTime gameDateTime = worldTimeResource.getGameDateTime(); - Message pausedMessage = Message.translation(world.getWorldConfig().isGameTimePaused() ? "server.commands.time.paused" : "server.commands.time.unpaused"); - Message message = Message.translation("server.commands.time.info").param("worldName", world.getName()).param("timePaused", pausedMessage); + String pausedMessageKey = world.getWorldConfig().isGameTimePaused() ? "server.commands.time.info" : "server.commands.time.infoUnpaused"; + Message message = Message.translation(pausedMessageKey).param("worldName", world.getName()); context.sendMessage( message.param("time", worldTimeResource.getGameTime().toString()) .param("dayOfWeek", FormatUtil.addNumberSuffix(gameDateTime.get(ChronoField.DAY_OF_WEEK))) @@ -90,9 +90,9 @@ public class TimeCommand extends AbstractWorldCommand { WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType()); worldTimeResource.setDayTime(periodTime / WorldTimeResource.HOURS_PER_DAY, world, store); context.sendMessage( - Message.translation("server.commands.time.set") + Message.translation("server.commands.time.setPeriod." + this.timeOfDay.name().toLowerCase()) .param("worldName", world.getName()) - .param("time", String.format("%s (%s)", worldTimeResource.getGameTime().toString(), this.timeOfDay.name())) + .param("time", worldTimeResource.getGameTime().toString()) ); } } diff --git a/src/com/hypixel/hytale/server/core/modules/voice/VoiceModule.java b/src/com/hypixel/hytale/server/core/modules/voice/VoiceModule.java new file mode 100644 index 00000000..2263a09b --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/voice/VoiceModule.java @@ -0,0 +1,466 @@ +package com.hypixel.hytale.server.core.modules.voice; + +import com.hypixel.hytale.common.plugin.PluginManifest; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.protocol.packets.serveraccess.Access; +import com.hypixel.hytale.protocol.packets.stream.StreamType; +import com.hypixel.hytale.server.core.Constants; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent; +import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent; +import com.hypixel.hytale.server.core.io.ServerManager; +import com.hypixel.hytale.server.core.io.stream.StreamManager; +import com.hypixel.hytale.server.core.modules.entity.EntityModule; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; +import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; +import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerRequestAccessEvent; +import com.hypixel.hytale.server.core.modules.voice.commands.VoiceCommand; +import com.hypixel.hytale.server.core.plugin.JavaPlugin; +import com.hypixel.hytale.server.core.plugin.JavaPluginInit; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.core.util.Config; +import com.hypixel.hytale.server.core.util.concurrent.ThreadUtil; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import javax.annotation.Nonnull; +import org.joml.Vector3d; + +public class VoiceModule extends JavaPlugin { + private static final long POSITION_CACHE_UPDATE_INTERVAL_MS = 100L; + public static final PluginManifest MANIFEST = PluginManifest.corePlugin(VoiceModule.class).depends(Universe.class).depends(EntityModule.class).build(); + private static VoiceModule instance; + @Nonnull + private final Config config = this.withConfig("VoiceModule", VoiceModuleConfig.CODEC); + private static final int MAX_PACKETS_PER_SECOND = 60; + private static final int BURST_CAPACITY = 25; + private static final int MAX_PACKET_SIZE = 1024; + private static final double PLAYER_EYE_HEIGHT_OFFSET = 1.62; + private final Map playerStates = new ConcurrentHashMap<>(); + private VoiceRouter voiceRouter; + private static final int VOICE_THREAD_POOL_SIZE = 4; + private final ExecutorService[] voiceExecutors; + private volatile boolean isShutdown = false; + private final ConcurrentHashMap positionCache = new ConcurrentHashMap<>(); + private ScheduledFuture positionUpdateTask; + + public static VoiceModule get() { + return instance; + } + + public VoiceModule(@Nonnull JavaPluginInit init) { + super(init); + instance = this; + this.voiceExecutors = new ExecutorService[4]; + + for (int i = 0; i < 4; i++) { + this.voiceExecutors[i] = Executors.newSingleThreadExecutor(ThreadUtil.daemon("VoiceRouter-" + i)); + } + } + + @Override + protected void setup() { + if (Constants.SINGLEPLAYER) { + this.config.get().setVoiceEnabled(false); + } + + this.voiceRouter = new VoiceRouter(this); + this.getCommandRegistry().registerCommand(new VoiceCommand()); + ServerManager.get().registerSubPacketHandlers(VoicePacketHandler::new); + StreamManager.getInstance().registerHandler(StreamType.Voice, VoiceStreamHandler::new); + this.getLogger().at(Level.INFO).log("[Voice] Registered voice stream handler"); + this.getEventRegistry().register(PlayerConnectEvent.class, this::onPlayerConnect); + this.getEventRegistry().register(PlayerDisconnectEvent.class, this::onPlayerDisconnect); + if (Constants.SINGLEPLAYER) { + this.getEventRegistry().register(SingleplayerRequestAccessEvent.class, this::onServerAccessChanged); + } + + this.getLogger() + .at(Level.INFO) + .log( + "[Voice] VoiceModule initialized (maxDistance=%.1f, refDistance=%.1f, enabled=%s, voiceStreamSupported=true)", + this.getMaxHearingDistance(), + this.getReferenceDistance(), + this.isVoiceEnabled() + ); + } + + @Override + protected void start() { + this.positionUpdateTask = HytaleServer.SCHEDULED_EXECUTOR.scheduleAtFixedRate(this::updateAllPlayerPositions, 100L, 100L, TimeUnit.MILLISECONDS); + this.getLogger().at(Level.INFO).log("[Voice] Started position cache update task (%dms interval)", 100L); + } + + private void updateAllPlayerPositions() { + if (!this.isShutdown) { + HashMap> playersByWorld = new HashMap<>(); + + for (Entry entry : this.playerStates.entrySet()) { + UUID playerId = entry.getKey(); + PlayerRef playerRef = Universe.get().getPlayer(playerId); + if (playerRef != null) { + Ref ref = playerRef.getReference(); + if (ref != null) { + Store store = ref.getStore(); + if (store != null) { + EntityStore externalData = store.getExternalData(); + if (externalData != null) { + World world = externalData.getWorld(); + if (world != null) { + playersByWorld.computeIfAbsent(world, k -> new ArrayList<>()).add(playerId); + } + } + } + } + } + } + + for (Entry> worldEntry : playersByWorld.entrySet()) { + World world = worldEntry.getKey(); + List playerIds = worldEntry.getValue(); + world.execute( + () -> { + for (UUID playerId : playerIds) { + PlayerRef freshPlayerRef = Universe.get().getPlayer(playerId); + if (freshPlayerRef != null) { + Ref freshRef = freshPlayerRef.getReference(); + if (freshRef != null && freshRef.isValid()) { + Store freshStore = freshRef.getStore(); + if (freshStore != null) { + EntityStore freshExternalData = freshStore.getExternalData(); + if (freshExternalData != null) { + World freshWorld = freshExternalData.getWorld(); + if (freshWorld != null) { + long currentWorldId = freshWorld.getWorldConfig().getUuid().getMostSignificantBits(); + TransformComponent transform = freshStore.getComponent(freshRef, EntityModule.get().getTransformComponentType()); + if (transform != null) { + boolean isUnderwater = this.isEyeInFluid(transform.getPosition(), freshWorld); + int networkId = 0; + NetworkId networkIdComp = freshStore.getComponent(freshRef, NetworkId.getComponentType()); + if (networkIdComp != null) { + networkId = networkIdComp.getId(); + } + + boolean isDead = freshStore.getComponent(freshRef, DeathComponent.getComponentType()) != null; + this.voiceRouter + .updateSpeakerPositionCache(freshPlayerRef, transform.getPosition(), isUnderwater, currentWorldId, networkId, isDead); + } + } + } + } + } + } + } + } + ); + } + } + } + + @Override + protected void shutdown() { + this.isShutdown = true; + if (this.positionUpdateTask != null) { + this.positionUpdateTask.cancel(false); + } + + for (ExecutorService executor : this.voiceExecutors) { + executor.shutdown(); + } + + try { + for (ExecutorService executor : this.voiceExecutors) { + if (!executor.awaitTermination(5L, TimeUnit.SECONDS)) { + this.getLogger().at(Level.WARNING).log("[Voice] VoiceExecutor did not terminate in time, forcing shutdown"); + executor.shutdownNow(); + } + } + } catch (InterruptedException var6) { + this.getLogger().at(Level.WARNING).log("[Voice] VoiceExecutor shutdown interrupted"); + + for (ExecutorService executorx : this.voiceExecutors) { + executorx.shutdownNow(); + } + + Thread.currentThread().interrupt(); + } + + this.playerStates.clear(); + this.positionCache.clear(); + this.getLogger().at(Level.INFO).log("[Voice] VoiceModule shutting down"); + } + + private void onServerAccessChanged(@Nonnull SingleplayerRequestAccessEvent event) { + if (event.getAccess() != Access.Private && !this.isVoiceEnabled()) { + this.setVoiceEnabled(true); + this.getLogger().at(Level.INFO).log("[Voice] Auto-enabled voice for %s play", event.getAccess()); + } else if (event.getAccess() == Access.Private && this.isVoiceEnabled()) { + this.setVoiceEnabled(false); + this.getLogger().at(Level.INFO).log("[Voice] Auto-disabled voice — returned to singleplayer"); + } + } + + private void onPlayerConnect(@Nonnull PlayerConnectEvent event) { + PlayerRef playerRef = event.getPlayerRef(); + VoicePlayerState state = new VoicePlayerState(playerRef.getUuid(), this.getLogger(), 25); + this.playerStates.put(playerRef.getUuid(), state); + this.getLogger() + .at(Level.FINE) + .log("[Voice] Player connected: %s (%s), totalPlayers=%d", playerRef.getUsername(), playerRef.getUuid(), this.playerStates.size()); + this.voiceRouter.sendVoiceConfig(playerRef); + this.scheduleImmediatePositionUpdate(playerRef); + } + + public void scheduleImmediatePositionUpdate(@Nonnull PlayerRef playerRef) { + UUID playerId = playerRef.getUuid(); + Ref ref = playerRef.getReference(); + if (ref != null) { + Store store = ref.getStore(); + if (store != null) { + EntityStore externalData = store.getExternalData(); + if (externalData != null) { + World world = externalData.getWorld(); + if (world != null) { + world.execute(() -> { + PlayerRef freshPlayerRef = Universe.get().getPlayer(playerId); + if (freshPlayerRef != null) { + Ref freshRef = freshPlayerRef.getReference(); + if (freshRef != null && freshRef.isValid()) { + Store freshStore = freshRef.getStore(); + if (freshStore != null) { + EntityStore freshExternalData = freshStore.getExternalData(); + if (freshExternalData != null) { + World freshWorld = freshExternalData.getWorld(); + if (freshWorld != null) { + long worldIdHash = freshWorld.getWorldConfig().getUuid().getMostSignificantBits(); + TransformComponent transform = freshStore.getComponent(freshRef, EntityModule.get().getTransformComponentType()); + if (transform != null) { + boolean isUnderwater = this.isEyeInFluid(transform.getPosition(), freshWorld); + int networkId = 0; + NetworkId networkIdComp = freshStore.getComponent(freshRef, NetworkId.getComponentType()); + if (networkIdComp != null) { + networkId = networkIdComp.getId(); + } + + boolean isDead = freshStore.getComponent(freshRef, DeathComponent.getComponentType()) != null; + this.updatePositionCache(playerId, transform.getPosition(), isUnderwater, worldIdHash, networkId, isDead); + this.getLogger().at(Level.FINE).log("[Voice] Immediate position cache populated for %s", freshPlayerRef.getUsername()); + } + } + } + } + } + } + }); + } + } + } + } + } + + private void onPlayerDisconnect(@Nonnull PlayerDisconnectEvent event) { + PlayerRef playerRef = event.getPlayerRef(); + VoicePlayerState state = this.playerStates.remove(playerRef.getUuid()); + this.positionCache.remove(playerRef.getUuid()); + this.voiceRouter.removePlayerFromWorldSets(playerRef.getUuid()); + String stats = state != null ? state.getStatsString() : "no state"; + this.getLogger() + .at(Level.FINE) + .log( + "[Voice] Player disconnected: %s (%s), stats=[%s], remainingPlayers=%d", + playerRef.getUsername(), + playerRef.getUuid(), + stats, + this.playerStates.size() + ); + } + + public VoicePlayerState getPlayerState(@Nonnull UUID playerId) { + return this.playerStates.get(playerId); + } + + public VoiceRouter getVoiceRouter() { + return this.voiceRouter; + } + + public boolean isVoiceEnabled() { + return this.config.get().isVoiceEnabled(); + } + + public void setVoiceEnabled(boolean enabled) { + boolean wasEnabled = this.config.get().isVoiceEnabled(); + this.config.get().setVoiceEnabled(enabled); + if (wasEnabled != enabled) { + this.getLogger().at(Level.INFO).log("[Voice] Voice enabled changed: %s -> %s", wasEnabled, enabled); + this.broadcastConfigToAllPlayers(); + this.config.save(); + } + } + + public boolean isDeadPlayersCanHear() { + return this.config.get().isDeadPlayersCanHear(); + } + + public float getMaxHearingDistance() { + return this.config.get().getMaxHearingDistance(); + } + + public void setMaxHearingDistance(float distance) { + float oldDistance = this.config.get().getMaxHearingDistance(); + this.config.get().setMaxHearingDistance(distance); + if (oldDistance != distance) { + this.getLogger().at(Level.INFO).log("[Voice] Max hearing distance changed: %.1f -> %.1f", oldDistance, distance); + this.broadcastConfigToAllPlayers(); + this.config.save(); + } + } + + public float getReferenceDistance() { + return this.config.get().getFullVolumeDistance(); + } + + public void setReferenceDistance(float distance) { + float oldDistance = this.config.get().getFullVolumeDistance(); + this.config.get().setFullVolumeDistance(distance); + if (oldDistance != distance) { + this.getLogger().at(Level.INFO).log("[Voice] Full volume distance changed: %.1f -> %.1f", oldDistance, distance); + this.broadcastConfigToAllPlayers(); + this.config.save(); + } + } + + private void broadcastConfigToAllPlayers() { + for (Entry entry : this.playerStates.entrySet()) { + PlayerRef playerRef = Universe.get().getPlayer(entry.getKey()); + if (playerRef != null) { + this.voiceRouter.sendVoiceConfig(playerRef); + } + } + } + + private void broadcastMuteUpdate(@Nonnull UUID playerId, boolean isMuted) { + this.broadcastConfigToAllPlayers(); + this.getLogger().at(Level.INFO).log("[Voice] Broadcast config update for mute change: playerId=%s, isMuted=%s", playerId, isMuted); + } + + public boolean isPlayerMuted(@Nonnull UUID playerId) { + return this.config.get().isPlayerMuted(playerId); + } + + public boolean mutePlayer(@Nonnull UUID playerId) { + boolean added = this.config.get().mutePlayer(playerId); + if (added) { + this.getLogger().at(Level.INFO).log("[Voice] Player globally muted: %s", playerId); + this.broadcastMuteUpdate(playerId, true); + this.config.save(); + } + + return added; + } + + public boolean unmutePlayer(@Nonnull UUID playerId) { + boolean removed = this.config.get().unmutePlayer(playerId); + if (removed) { + this.getLogger().at(Level.INFO).log("[Voice] Player globally unmuted: %s", playerId); + this.broadcastMuteUpdate(playerId, false); + this.config.save(); + } + + return removed; + } + + public Set getGloballyMutedPlayers() { + return this.config.get().getMutedPlayers(); + } + + public int getMaxPacketsPerSecond() { + return 60; + } + + public int getBurstCapacity() { + return 25; + } + + public int getMaxPacketSize() { + return 1024; + } + + private boolean isEyeInFluid(@Nonnull Vector3d position, @Nonnull World world) { + int blockX = MathUtil.floor(position.x()); + int blockY = MathUtil.floor(position.y() + 1.62); + int blockZ = MathUtil.floor(position.z()); + ChunkStore chunkStore = world.getChunkStore(); + long chunkIndex = ChunkUtil.indexChunkFromBlock(blockX, blockZ); + Ref chunkRef = chunkStore.getChunkReference(chunkIndex); + if (chunkRef != null && chunkRef.isValid()) { + WorldChunk worldChunk = chunkStore.getStore().getComponent(chunkRef, WorldChunk.getComponentType()); + return worldChunk == null ? false : worldChunk.getFluidId(blockX, blockY, blockZ) != 0; + } else { + return false; + } + } + + @Deprecated + public void updatePositionCache(@Nonnull UUID playerId, @Nonnull Vector3d position) { + this.updatePositionCache(playerId, position, false, 0L, 0, false); + } + + @Deprecated + public void updatePositionCache(@Nonnull UUID playerId, @Nonnull Vector3d position, boolean isUnderwater) { + this.updatePositionCache(playerId, position, isUnderwater, 0L, 0, false); + } + + @Deprecated + public void updatePositionCache(@Nonnull UUID playerId, @Nonnull Vector3d position, boolean isUnderwater, long worldId, int networkId) { + this.updatePositionCache(playerId, position, isUnderwater, worldId, networkId, false); + } + + public void updatePositionCache(@Nonnull UUID playerId, @Nonnull Vector3d position, boolean isUnderwater, long worldId, int networkId, boolean isDead) { + this.positionCache + .put( + playerId, + new VoiceModule.PositionSnapshot( + position.x(), position.y() + 1.62, position.z(), isUnderwater, worldId, networkId, isDead, System.currentTimeMillis() + ) + ); + } + + public VoiceModule.PositionSnapshot getCachedPosition(@Nonnull UUID playerId) { + return this.positionCache.get(playerId); + } + + public ExecutorService getVoiceExecutor(@Nonnull UUID speakerId) { + int index = Math.floorMod(speakerId.hashCode(), 4); + return this.voiceExecutors[index]; + } + + public boolean isShutdown() { + return this.isShutdown; + } + + public Map getPlayerStates() { + return this.playerStates; + } + + public record PositionSnapshot(double x, double y, double z, boolean isUnderwater, long worldId, int networkId, boolean isDead, long timestamp) { + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/voice/VoiceModuleConfig.java b/src/com/hypixel/hytale/server/core/modules/voice/VoiceModuleConfig.java new file mode 100644 index 00000000..4443e0e8 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/voice/VoiceModuleConfig.java @@ -0,0 +1,77 @@ +package com.hypixel.hytale.server.core.modules.voice; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.codecs.set.SetCodec; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class VoiceModuleConfig { + public static final BuilderCodec CODEC = BuilderCodec.builder(VoiceModuleConfig.class, VoiceModuleConfig::new) + .addField(new KeyedCodec<>("VoiceEnabled", Codec.BOOLEAN), (config, b) -> config.voiceEnabled = b, config -> config.voiceEnabled) + .addField(new KeyedCodec<>("MaxHearingDistance", Codec.FLOAT), (config, f) -> config.maxHearingDistance = f, config -> config.maxHearingDistance) + .addField(new KeyedCodec<>("FullVolumeDistance", Codec.FLOAT), (config, f) -> config.fullVolumeDistance = f, config -> config.fullVolumeDistance) + .addField(new KeyedCodec<>("DeadPlayersCanHear", Codec.BOOLEAN), (config, b) -> config.deadPlayersCanHear = b, config -> config.deadPlayersCanHear) + .addField(new KeyedCodec<>("MutedPlayers", new SetCodec<>(Codec.UUID_STRING, HashSet::new, false)), (config, s) -> { + config.mutedPlayers.clear(); + config.mutedPlayers.addAll(s); + }, config -> config.mutedPlayers) + .build(); + private boolean voiceEnabled = true; + private float maxHearingDistance = 32.0F; + private float fullVolumeDistance = 4.0F; + private boolean deadPlayersCanHear = false; + private final Set mutedPlayers = ConcurrentHashMap.newKeySet(); + + public boolean isVoiceEnabled() { + return this.voiceEnabled; + } + + public void setVoiceEnabled(boolean voiceEnabled) { + this.voiceEnabled = voiceEnabled; + } + + public float getMaxHearingDistance() { + return this.maxHearingDistance; + } + + public void setMaxHearingDistance(float maxHearingDistance) { + this.maxHearingDistance = maxHearingDistance; + } + + public float getFullVolumeDistance() { + return this.fullVolumeDistance; + } + + public void setFullVolumeDistance(float fullVolumeDistance) { + this.fullVolumeDistance = fullVolumeDistance; + } + + public Set getMutedPlayers() { + return Collections.unmodifiableSet(new HashSet<>(this.mutedPlayers)); + } + + public boolean isPlayerMuted(UUID playerId) { + return this.mutedPlayers.contains(playerId); + } + + public boolean mutePlayer(UUID playerId) { + return this.mutedPlayers.add(playerId); + } + + public boolean unmutePlayer(UUID playerId) { + return this.mutedPlayers.remove(playerId); + } + + public boolean isDeadPlayersCanHear() { + return this.deadPlayersCanHear; + } + + public void setDeadPlayersCanHear(boolean deadPlayersCanHear) { + this.deadPlayersCanHear = deadPlayersCanHear; + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/voice/VoicePacketHandler.java b/src/com/hypixel/hytale/server/core/modules/voice/VoicePacketHandler.java new file mode 100644 index 00000000..58940ee7 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/voice/VoicePacketHandler.java @@ -0,0 +1,38 @@ +package com.hypixel.hytale.server.core.modules.voice; + +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.protocol.packets.voice.VoiceData; +import com.hypixel.hytale.server.core.io.handlers.IPacketHandler; +import com.hypixel.hytale.server.core.io.handlers.SubPacketHandler; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import java.util.logging.Level; +import javax.annotation.Nonnull; + +public class VoicePacketHandler implements SubPacketHandler { + private final HytaleLogger logger; + private final IPacketHandler parent; + private boolean loggedGameStreamRejection = false; + + public VoicePacketHandler(@Nonnull IPacketHandler parent) { + this.parent = parent; + this.logger = VoiceModule.get().getLogger(); + } + + @Override + public void registerHandlers() { + this.parent.registerHandler(450, p -> this.handleVoiceData((VoiceData)p)); + } + + private void handleVoiceData(@Nonnull VoiceData packet) { + if (!this.loggedGameStreamRejection) { + this.loggedGameStreamRejection = true; + PlayerRef playerRef = this.parent.getPlayerRef(); + this.logger + .at(Level.WARNING) + .log( + "[VoicePacket] REJECTED: Voice data received on game stream from %s - client should use dedicated voice stream", + playerRef != null ? playerRef.getUsername() : "unknown" + ); + } + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/voice/VoicePlayerState.java b/src/com/hypixel/hytale/server/core/modules/voice/VoicePlayerState.java new file mode 100644 index 00000000..46ca69b2 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/voice/VoicePlayerState.java @@ -0,0 +1,148 @@ +package com.hypixel.hytale.server.core.modules.voice; + +import com.hypixel.hytale.logger.HytaleLogger; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import javax.annotation.Nonnull; + +public class VoicePlayerState { + private static final long MILLISECONDS_PER_SECOND = 1000L; + private static final long RATE_LIMIT_LOG_THROTTLE_MS = 10000L; + static final int MAX_CONSECUTIVE_ERRORS = 10; + private final UUID playerId; + private final HytaleLogger logger; + private volatile boolean isSpeaking; + private volatile boolean silenced = false; + private volatile long lastPacketTime; + private volatile double tokenBucket; + private volatile long lastTokenRefillTime; + private volatile long lastRateLimitLogTime; + private final AtomicLong totalPacketsReceived = new AtomicLong(); + private final AtomicLong totalPacketsRateLimited = new AtomicLong(); + private final AtomicInteger speakingStateChanges = new AtomicInteger(); + private final AtomicInteger consecutiveErrors = new AtomicInteger(); + private volatile boolean routingDisabled = false; + + public VoicePlayerState(@Nonnull UUID playerId, @Nonnull HytaleLogger logger, int burstCapacity) { + this.playerId = playerId; + this.logger = logger; + long now = System.currentTimeMillis(); + this.lastTokenRefillTime = now; + this.tokenBucket = burstCapacity; + } + + @Nonnull + public UUID getPlayerId() { + return this.playerId; + } + + public boolean isSpeaking() { + return this.isSpeaking; + } + + public void setSpeaking(boolean speaking) { + if (this.isSpeaking != speaking) { + int changes = this.speakingStateChanges.incrementAndGet(); + this.logger + .at(Level.FINE) + .log("[PlayerState] Speaking changed: player=%s, speaking=%s -> %s, totalChanges=%d", this.playerId, this.isSpeaking, speaking, changes); + } + + this.isSpeaking = speaking; + } + + public boolean isSilenced() { + return this.silenced; + } + + public void setSilenced(boolean silenced) { + this.silenced = silenced; + } + + public boolean isRoutingDisabled() { + return this.routingDisabled; + } + + public void setRoutingDisabled(boolean disabled) { + this.routingDisabled = disabled; + } + + public int incrementConsecutiveErrors() { + return this.consecutiveErrors.incrementAndGet(); + } + + public void resetConsecutiveErrors() { + this.consecutiveErrors.set(0); + } + + public int getConsecutiveErrors() { + return this.consecutiveErrors.get(); + } + + public synchronized boolean checkRateLimit(int maxPacketsPerSecond, int burstCapacity) { + long now = System.currentTimeMillis(); + long elapsed = now - this.lastTokenRefillTime; + if (elapsed > 0L) { + double tokensToAdd = elapsed / 1000.0 * maxPacketsPerSecond; + this.tokenBucket = Math.min((double)burstCapacity, this.tokenBucket + tokensToAdd); + this.lastTokenRefillTime = now; + if (tokensToAdd >= 1.0) { + this.logger + .at(Level.FINEST) + .log("[PlayerState] TokenRefill: player=%s, tokensAdded=%.2f, bucket=%.2f", this.playerId, tokensToAdd, this.tokenBucket); + } + } + + this.totalPacketsReceived.incrementAndGet(); + if (this.tokenBucket < 1.0) { + this.totalPacketsRateLimited.incrementAndGet(); + return false; + } else { + this.tokenBucket--; + this.lastPacketTime = now; + return true; + } + } + + public synchronized boolean shouldLogRateLimit() { + long now = System.currentTimeMillis(); + if (now - this.lastRateLimitLogTime >= 10000L) { + this.lastRateLimitLogTime = now; + return true; + } else { + return false; + } + } + + public long getLastPacketTime() { + return this.lastPacketTime; + } + + public double getTokenBucket() { + return this.tokenBucket; + } + + public long getTotalPacketsReceived() { + return this.totalPacketsReceived.get(); + } + + public long getTotalPacketsRateLimited() { + return this.totalPacketsRateLimited.get(); + } + + public int getSpeakingStateChanges() { + return this.speakingStateChanges.get(); + } + + public String getStatsString() { + return String.format( + "packets=%d, rateLimited=%d, stateChanges=%d, routingErrors=%d", + this.totalPacketsReceived.get(), + this.totalPacketsRateLimited.get(), + this.speakingStateChanges.get(), + this.consecutiveErrors.get() + ); + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/voice/VoiceRouter.java b/src/com/hypixel/hytale/server/core/modules/voice/VoiceRouter.java new file mode 100644 index 00000000..c913c73d --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/voice/VoiceRouter.java @@ -0,0 +1,233 @@ +package com.hypixel.hytale.server.core.modules.voice; + +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.protocol.Position; +import com.hypixel.hytale.protocol.packets.stream.StreamType; +import com.hypixel.hytale.protocol.packets.voice.RelayedVoiceData; +import com.hypixel.hytale.protocol.packets.voice.VoiceCodec; +import com.hypixel.hytale.protocol.packets.voice.VoiceConfig; +import com.hypixel.hytale.protocol.packets.voice.VoiceData; +import com.hypixel.hytale.server.core.io.PacketHandler; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; +import io.netty.channel.Channel; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import javax.annotation.Nonnull; +import org.joml.Vector3d; + +public class VoiceRouter { + private static final int VERBOSE_LOG_PACKET_FREQUENCY = 50; + private static final int MAX_SPEAKERS_PER_LISTENER = 12; + private final VoiceModule voiceModule; + private final HytaleLogger logger; + private final ConcurrentHashMap> worldPlayerSets = new ConcurrentHashMap<>(); + private volatile boolean loggedFirstCacheRoute = false; + + public VoiceRouter(@Nonnull VoiceModule voiceModule) { + this.voiceModule = voiceModule; + this.logger = voiceModule.getLogger(); + } + + public void updateSpeakerPositionCache( + @Nonnull PlayerRef speaker, @Nonnull Vector3d position, boolean isUnderwater, long worldId, int networkId, boolean isDead + ) { + UUID playerId = speaker.getUuid(); + VoiceModule.PositionSnapshot oldSnapshot = this.voiceModule.getCachedPosition(playerId); + if (oldSnapshot != null && oldSnapshot.worldId() != worldId) { + Set oldWorldSet = this.worldPlayerSets.get(oldSnapshot.worldId()); + if (oldWorldSet != null) { + oldWorldSet.remove(playerId); + } + } + + this.worldPlayerSets.computeIfAbsent(worldId, k -> ConcurrentHashMap.newKeySet()).add(playerId); + this.voiceModule.updatePositionCache(playerId, position, isUnderwater, worldId, networkId, isDead); + } + + public void removePlayerFromWorldSets(@Nonnull UUID playerId) { + for (Set worldSet : this.worldPlayerSets.values()) { + worldSet.remove(playerId); + } + } + + public void sendVoiceConfig(@Nonnull PlayerRef player) { + VoiceConfig config = new VoiceConfig(); + config.voiceEnabled = this.voiceModule.isVoiceEnabled(); + config.codec = VoiceCodec.Opus; + config.sampleRate = 48000; + config.channels = 1; + config.maxHearingDistance = this.voiceModule.getMaxHearingDistance(); + config.referenceDistance = this.voiceModule.getReferenceDistance(); + config.supportsVoiceStream = true; + config.maxPacketsPerSecond = (byte)this.voiceModule.getMaxPacketsPerSecond(); + player.getPacketHandler().writeNoCache(config); + this.logger + .at(Level.FINE) + .log( + "[VoiceConfig] Sent to %s: enabled=%s, codec=%s, sampleRate=%d, maxDistance=%.1f, refDistance=%.1f, supportsVoiceStream=true, maxPacketsPerSecond=%d", + player.getUsername(), + config.voiceEnabled, + config.codec, + config.sampleRate, + config.maxHearingDistance, + config.referenceDistance, + config.maxPacketsPerSecond + ); + } + + public void routeVoiceFromCache(@Nonnull PlayerRef speaker, @Nonnull VoiceData packet) { + if (this.voiceModule.isVoiceEnabled()) { + if (!this.voiceModule.isPlayerMuted(speaker.getUuid())) { + if (packet.opusData != null && packet.opusData.length <= this.voiceModule.getMaxPacketSize()) { + VoiceModule.PositionSnapshot speakerPos = this.voiceModule.getCachedPosition(speaker.getUuid()); + if (speakerPos != null) { + VoicePlayerState speakerState = this.voiceModule.getPlayerState(speaker.getUuid()); + if (speakerState == null || !speakerState.isSilenced()) { + long now = System.currentTimeMillis(); + double qx = Math.round(speakerPos.x() * 2.0) / 2.0; + double qy = Math.round(speakerPos.y() * 2.0) / 2.0; + double qz = Math.round(speakerPos.z() * 2.0) / 2.0; + RelayedVoiceData relay = new RelayedVoiceData(); + relay.speakerId = speaker.getUuid(); + relay.entityId = speakerPos.networkId(); + relay.sequenceNumber = packet.sequenceNumber; + relay.timestamp = packet.timestamp; + relay.speakerPosition = new Position(qx, qy, qz); + relay.speakerIsUnderwater = speakerPos.isUnderwater(); + relay.opusData = packet.opusData; + float maxDistSq = this.voiceModule.getMaxHearingDistance() * this.voiceModule.getMaxHearingDistance(); + long speakerWorldId = speakerPos.worldId(); + if (packet.sequenceNumber % 50 == 0) { + this.logger + .at(Level.FINE) + .log( + "[VoiceRouter] SPEAKER_WORLD: speaker=%s, worldId=%d, pos=(%.1f,%.1f,%.1f), seq=%d", + speaker.getUsername(), + speakerWorldId, + speakerPos.x(), + speakerPos.y(), + speakerPos.z(), + packet.sequenceNumber + ); + } + + int recipientCount = 0; + int skippedCount = 0; + Set sameWorldPlayers = this.worldPlayerSets.get(speakerWorldId); + if (sameWorldPlayers != null && !sameWorldPlayers.isEmpty()) { + List candidates = null; + + for (UUID listenerId : sameWorldPlayers) { + if (!listenerId.equals(speaker.getUuid())) { + VoiceModule.PositionSnapshot listenerPos = this.voiceModule.getCachedPosition(listenerId); + if (listenerPos != null) { + if (listenerPos.worldId() != speakerWorldId) { + this.logger + .at(Level.FINE) + .log( + "[VoiceRouter] WORLD_ISOLATION: speakerWorldId=%d, listenerWorldId=%d, speaker=%s, listener=%s - BLOCKING", + speakerWorldId, + listenerPos.worldId(), + speaker.getUsername(), + listenerId.toString().substring(0, 8) + ); + } else if (!this.voiceModule.isDeadPlayersCanHear() && listenerPos.isDead()) { + skippedCount++; + } else { + double dx = speakerPos.x() - listenerPos.x(); + double dy = speakerPos.y() - listenerPos.y(); + double dz = speakerPos.z() - listenerPos.z(); + double distSq = dx * dx + dy * dy + dz * dz; + if (distSq <= maxDistSq) { + PacketHandler handler = this.getPlayerHandler(listenerId); + if (handler != null) { + Channel voiceChannel = handler.getChannel(StreamType.Voice); + if (voiceChannel != null && voiceChannel.isActive()) { + if (candidates == null) { + candidates = new ArrayList<>(); + } + + candidates.add(new VoiceRouter.ListenerCandidate(listenerId, distSq, handler)); + } else { + skippedCount++; + } + } + } + } + } + } + } + + if (candidates != null) { + if (candidates.size() > 12) { + candidates.sort((a, b) -> Double.compare(a.distSq, b.distSq)); + candidates = candidates.subList(0, 12); + } + + for (VoiceRouter.ListenerCandidate candidate : candidates) { + RelayedVoiceData recipientRelay = createPerRecipientRelay(relay); + Channel voiceChannel = candidate.handler.getChannel(StreamType.Voice); + if (voiceChannel != null && voiceChannel.isActive()) { + voiceChannel.writeAndFlush(recipientRelay); + recipientCount++; + } + } + } + + if (!this.loggedFirstCacheRoute && recipientCount > 0) { + this.loggedFirstCacheRoute = true; + this.logger + .at(Level.INFO) + .log( + "[VoiceRouter] First voice packet routed via cache: speaker=%s, seq=%d, recipientCount=%d, skipped=%d", + speaker.getUsername(), + packet.sequenceNumber, + recipientCount, + skippedCount + ); + } + + if (packet.sequenceNumber % 50 == 0) { + this.logger + .at(Level.FINE) + .log( + "[VoiceRouter] CacheRoute seq=%d, recipients=%d, skipped=%d, speakerCacheAge=%dms", + packet.sequenceNumber, + recipientCount, + skippedCount, + now - speakerPos.timestamp() + ); + } + } + } + } + } + } + } + } + + private PacketHandler getPlayerHandler(UUID playerId) { + PlayerRef playerRef = Universe.get().getPlayer(playerId); + return playerRef != null ? playerRef.getPacketHandler() : null; + } + + private static RelayedVoiceData createPerRecipientRelay(@Nonnull RelayedVoiceData source) { + RelayedVoiceData relay = new RelayedVoiceData(); + relay.speakerId = source.speakerId; + relay.entityId = source.entityId; + relay.sequenceNumber = source.sequenceNumber; + relay.timestamp = source.timestamp; + relay.speakerPosition = source.speakerPosition != null ? new Position(source.speakerPosition) : null; + relay.speakerIsUnderwater = source.speakerIsUnderwater; + relay.opusData = source.opusData; + return relay; + } + + private record ListenerCandidate(UUID listenerId, double distSq, PacketHandler handler) { + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/voice/VoiceStreamHandler.java b/src/com/hypixel/hytale/server/core/modules/voice/VoiceStreamHandler.java new file mode 100644 index 00000000..0a962696 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/voice/VoiceStreamHandler.java @@ -0,0 +1,171 @@ +package com.hypixel.hytale.server.core.modules.voice; + +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.protocol.Packet; +import com.hypixel.hytale.protocol.packets.stream.StreamType; +import com.hypixel.hytale.protocol.packets.voice.VoiceData; +import com.hypixel.hytale.server.core.io.PacketHandler; +import com.hypixel.hytale.server.core.io.handlers.game.GamePacketHandler; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import java.util.logging.Level; +import javax.annotation.Nonnull; + +public class VoiceStreamHandler extends SimpleChannelInboundHandler { + private final PacketHandler packetHandler; + private final VoiceModule voiceModule; + private final HytaleLogger logger; + private volatile PlayerRef cachedPlayerRef; + private volatile boolean loggedFirstPacket = false; + private volatile boolean loggedFirstVoiceData = false; + + public VoiceStreamHandler(@Nonnull PacketHandler packetHandler) { + this.packetHandler = packetHandler; + this.voiceModule = VoiceModule.get(); + this.logger = this.voiceModule.getLogger(); + } + + @Override + public void handlerAdded(@Nonnull ChannelHandlerContext ctx) throws Exception { + this.packetHandler.setChannel(StreamType.Voice, ctx.channel()); + if (this.packetHandler instanceof GamePacketHandler gameHandler) { + this.cachedPlayerRef = gameHandler.getPlayerRef(); + } + + this.logger + .at(Level.FINE) + .log( + "[VoiceStream] Voice stream registered for %s (channel active=%s, playerRef=%s)", + this.packetHandler.getIdentifier(), + ctx.channel().isActive(), + this.cachedPlayerRef != null ? this.cachedPlayerRef.getUsername() : "null" + ); + super.handlerAdded(ctx); + } + + protected void channelRead0(@Nonnull ChannelHandlerContext ctx, @Nonnull Packet packet) { + if (!this.loggedFirstPacket) { + this.loggedFirstPacket = true; + this.logger + .at(Level.FINE) + .log("[VoiceStream] First packet received from %s: %s", this.packetHandler.getIdentifier(), packet.getClass().getSimpleName()); + } + + PlayerRef playerRef = this.getPlayerRef(); + if (playerRef == null) { + this.logger.at(Level.WARNING).log("[VoiceStream] No player ref for voice packet from %s", this.packetHandler.getIdentifier()); + } else { + if (packet instanceof VoiceData voiceData) { + this.handleVoiceData(playerRef, voiceData); + } else { + this.logger + .at(Level.WARNING) + .log("[VoiceStream] Unexpected packet type %s from %s", packet.getClass().getSimpleName(), this.packetHandler.getIdentifier()); + } + } + } + + private void handleVoiceData(@Nonnull PlayerRef playerRef, @Nonnull VoiceData data) { + if (this.voiceModule.isVoiceEnabled()) { + if (!this.loggedFirstVoiceData) { + this.loggedFirstVoiceData = true; + this.logger + .at(Level.FINE) + .log( + "[VoiceStream] Routing first VoiceData from %s: seq=%d, dataSize=%d", + playerRef.getUsername(), + data.sequenceNumber, + data.opusData != null ? data.opusData.length : 0 + ); + } + + if (!this.voiceModule.isShutdown()) { + VoicePlayerState state = this.voiceModule.getPlayerState(playerRef.getUuid()); + if (state != null) { + if (!state.isRoutingDisabled()) { + if (!state.isSilenced()) { + if (!this.voiceModule.isPlayerMuted(playerRef.getUuid())) { + if (!state.checkRateLimit(this.voiceModule.getMaxPacketsPerSecond(), this.voiceModule.getBurstCapacity())) { + if (state.shouldLogRateLimit()) { + this.logger + .at(Level.WARNING) + .log( + "[VoiceStream] RATE_LIMITED: player=%s, tokens=%.2f, maxPps=%d, burstCapacity=%d", + playerRef.getUsername(), + state.getTokenBucket(), + this.voiceModule.getMaxPacketsPerSecond(), + this.voiceModule.getBurstCapacity() + ); + } + } else if (data.opusData != null && data.opusData.length != 0) { + if (data.opusData.length > this.voiceModule.getMaxPacketSize()) { + this.logger + .at(Level.WARNING) + .log( + "[VoiceStream] REJECTED_OVERSIZE: player=%s, size=%d, maxSize=%d", + playerRef.getUsername(), + data.opusData.length, + this.voiceModule.getMaxPacketSize() + ); + } else { + this.voiceModule + .getVoiceExecutor(playerRef.getUuid()) + .execute( + () -> { + try { + this.voiceModule.getVoiceRouter().routeVoiceFromCache(playerRef, data); + state.resetConsecutiveErrors(); + } catch (Exception var6) { + int failures = state.incrementConsecutiveErrors(); + if (failures >= 10) { + this.logger + .at(Level.WARNING) + .log("[VoiceStream] Disabled voice routing for %s after %d consecutive errors", playerRef.getUuid(), failures); + state.setRoutingDisabled(true); + } else { + this.logger + .at(Level.SEVERE) + .withCause(var6) + .log("[VoiceStream] Exception in routeVoiceFromCache for %s (failure %d/%d)", playerRef.getUuid(), failures, 10); + } + } + } + ); + } + } else { + this.logger.at(Level.FINE).log("[VoiceStream] REJECTED_EMPTY: player=%s, seq=%d", playerRef.getUsername(), data.sequenceNumber); + } + } + } + } + } + } + } + } + + private PlayerRef getPlayerRef() { + if (this.cachedPlayerRef != null) { + return this.cachedPlayerRef; + } else { + if (this.packetHandler instanceof GamePacketHandler gameHandler) { + this.cachedPlayerRef = gameHandler.getPlayerRef(); + } + + return this.cachedPlayerRef; + } + } + + @Override + public void channelInactive(@Nonnull ChannelHandlerContext ctx) throws Exception { + this.packetHandler.compareAndSetChannel(StreamType.Voice, ctx.channel(), null); + this.logger.at(Level.FINE).log("[VoiceStream] Voice stream closed for %s", this.packetHandler.getIdentifier()); + super.channelInactive(ctx); + } + + @Override + public void exceptionCaught(@Nonnull ChannelHandlerContext ctx, @Nonnull Throwable cause) { + this.logger.at(Level.WARNING).withCause(cause).log("[VoiceStream] Exception in voice stream for %s", this.packetHandler.getIdentifier()); + ctx.close(); + } +} diff --git a/src/com/hypixel/hytale/server/core/modules/voice/commands/VoiceCommand.java b/src/com/hypixel/hytale/server/core/modules/voice/commands/VoiceCommand.java new file mode 100644 index 00000000..52f0f947 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/modules/voice/commands/VoiceCommand.java @@ -0,0 +1,219 @@ +package com.hypixel.hytale.server.core.modules.voice.commands; + +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.auth.ProfileServiceClient; +import com.hypixel.hytale.server.core.command.system.AbstractCommand; +import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; +import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes; +import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncCommand; +import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; +import com.hypixel.hytale.server.core.modules.voice.VoiceModule; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class VoiceCommand extends AbstractCommandCollection { + public VoiceCommand() { + super("voice", "server.commands.voice.desc"); + this.addSubCommand(new VoiceCommand.VoiceEnabledCommand()); + this.addSubCommand(new VoiceCommand.VoiceMaxDistanceCommand()); + this.addSubCommand(new VoiceCommand.VoiceFullVolumeDistanceCommand()); + this.addSubCommand(new VoiceCommand.VoiceMuteCommand()); + this.addSubCommand(new VoiceCommand.VoiceUnmuteCommand()); + this.addSubCommand(new VoiceCommand.VoiceMutedListCommand()); + this.addSubCommand(new VoiceCommand.VoiceStatusCommand()); + } + + private class VoiceEnabledCommand extends AbstractCommand { + @Nonnull + private final RequiredArg enabledArg; + + public VoiceEnabledCommand() { + Objects.requireNonNull(VoiceCommand.this); + super("enabled", "server.commands.voice.enabled.desc"); + this.enabledArg = this.withRequiredArg("enabled", "server.commands.voice.enabled.arg.desc", ArgTypes.BOOLEAN); + this.setPermissionGroup(null); + } + + @Nullable + @Override + protected CompletableFuture execute(@Nonnull CommandContext context) { + boolean enabled = this.enabledArg.get(context); + VoiceModule.get().setVoiceEnabled(enabled); + String messageId = enabled ? "server.commands.voice.status.enabled" : "server.commands.voice.status.disabled"; + context.sendMessage(Message.translation(messageId)); + return null; + } + } + + private class VoiceFullVolumeDistanceCommand extends AbstractCommand { + @Nonnull + private final RequiredArg distanceArg; + + public VoiceFullVolumeDistanceCommand() { + Objects.requireNonNull(VoiceCommand.this); + super("fullvolumedistance", "server.commands.voice.fullvolumedistance.desc"); + this.distanceArg = this.withRequiredArg("blocks", "server.commands.voice.fullvolumedistance.arg.desc", ArgTypes.FLOAT); + this.setPermissionGroup(null); + } + + @Nullable + @Override + protected CompletableFuture execute(@Nonnull CommandContext context) { + float distance = this.distanceArg.get(context); + if (distance <= 0.0F) { + context.sendMessage(Message.translation("server.commands.voice.fullvolumedistance.invalid")); + return null; + } else { + VoiceModule.get().setReferenceDistance(distance); + context.sendMessage(Message.translation("server.commands.voice.fullvolumedistance.set").param("distance", distance)); + return null; + } + } + } + + private class VoiceMaxDistanceCommand extends AbstractCommand { + @Nonnull + private final RequiredArg distanceArg; + + public VoiceMaxDistanceCommand() { + Objects.requireNonNull(VoiceCommand.this); + super("maxdistance", "server.commands.voice.maxdistance.desc"); + this.distanceArg = this.withRequiredArg("blocks", "server.commands.voice.maxdistance.arg.desc", ArgTypes.FLOAT); + this.setPermissionGroup(null); + } + + @Nullable + @Override + protected CompletableFuture execute(@Nonnull CommandContext context) { + float distance = this.distanceArg.get(context); + if (distance <= 0.0F) { + context.sendMessage(Message.translation("server.commands.voice.maxdistance.invalid")); + return null; + } else { + VoiceModule.get().setMaxHearingDistance(distance); + context.sendMessage(Message.translation("server.commands.voice.maxdistance.set").param("distance", distance)); + return null; + } + } + } + + private class VoiceMuteCommand extends AbstractAsyncCommand { + @Nonnull + private final RequiredArg playerArg; + + public VoiceMuteCommand() { + Objects.requireNonNull(VoiceCommand.this); + super("mute", "server.commands.voice.mute.desc"); + this.playerArg = this.withRequiredArg("player", "server.commands.voice.mute.arg.desc", ArgTypes.GAME_PROFILE_LOOKUP); + this.setPermissionGroup(null); + } + + @Nonnull + @Override + protected CompletableFuture executeAsync(@Nonnull CommandContext context) { + ProfileServiceClient.PublicGameProfile profile = this.playerArg.get(context); + if (profile == null) { + return CompletableFuture.completedFuture(null); + } else { + UUID uuid = profile.getUuid(); + Message displayName = Message.raw(profile.getUsername()).bold(true); + if (VoiceModule.get().isPlayerMuted(uuid)) { + context.sendMessage(Message.translation("server.commands.voice.mute.already").param("player", displayName)); + return CompletableFuture.completedFuture(null); + } else { + VoiceModule.get().mutePlayer(uuid); + context.sendMessage(Message.translation("server.commands.voice.mute.success").param("player", displayName)); + return CompletableFuture.completedFuture(null); + } + } + } + } + + private class VoiceMutedListCommand extends AbstractCommand { + public VoiceMutedListCommand() { + Objects.requireNonNull(VoiceCommand.this); + super("muted", "server.commands.voice.muted.desc"); + this.setPermissionGroup(null); + } + + @Nullable + @Override + protected CompletableFuture execute(@Nonnull CommandContext context) { + Set mutedPlayers = VoiceModule.get().getGloballyMutedPlayers(); + if (mutedPlayers.isEmpty()) { + context.sendMessage(Message.translation("server.commands.voice.muted.empty")); + return null; + } else { + String playerList = mutedPlayers.stream().map(uuid -> { + PlayerRef playerRef = Universe.get().getPlayer(uuid); + return playerRef != null ? playerRef.getUsername() + " (" + uuid + ")" : uuid.toString(); + }).collect(Collectors.joining(", ")); + context.sendMessage(Message.translation("server.commands.voice.muted.list").param("count", mutedPlayers.size()).param("players", playerList)); + return null; + } + } + } + + private class VoiceStatusCommand extends AbstractCommand { + public VoiceStatusCommand() { + Objects.requireNonNull(VoiceCommand.this); + super("status", "server.commands.voice.status.desc"); + this.setPermissionGroup(null); + } + + @Nullable + @Override + protected CompletableFuture execute(@Nonnull CommandContext context) { + VoiceModule voiceModule = VoiceModule.get(); + int mutedCount = voiceModule.getGloballyMutedPlayers().size(); + String messageId = voiceModule.isVoiceEnabled() ? "server.commands.voice.status.enabledInfo" : "server.commands.voice.status.disabledInfo"; + context.sendMessage( + Message.translation(messageId) + .param("maxDistance", voiceModule.getMaxHearingDistance()) + .param("fullVolumeDistance", voiceModule.getReferenceDistance()) + .param("mutedCount", mutedCount) + ); + return null; + } + } + + private class VoiceUnmuteCommand extends AbstractAsyncCommand { + @Nonnull + private final RequiredArg playerArg; + + public VoiceUnmuteCommand() { + Objects.requireNonNull(VoiceCommand.this); + super("unmute", "server.commands.voice.unmute.desc"); + this.playerArg = this.withRequiredArg("player", "server.commands.voice.unmute.arg.desc", ArgTypes.GAME_PROFILE_LOOKUP); + this.setPermissionGroup(null); + } + + @Nonnull + @Override + protected CompletableFuture executeAsync(@Nonnull CommandContext context) { + ProfileServiceClient.PublicGameProfile profile = this.playerArg.get(context); + if (profile == null) { + return CompletableFuture.completedFuture(null); + } else { + UUID uuid = profile.getUuid(); + Message displayName = Message.raw(profile.getUsername()).bold(true); + if (!VoiceModule.get().isPlayerMuted(uuid)) { + context.sendMessage(Message.translation("server.commands.voice.unmute.notmuted").param("player", displayName)); + return CompletableFuture.completedFuture(null); + } else { + VoiceModule.get().unmutePlayer(uuid); + context.sendMessage(Message.translation("server.commands.voice.unmute.success").param("player", displayName)); + return CompletableFuture.completedFuture(null); + } + } + } + } +} diff --git a/src/com/hypixel/hytale/server/core/permissions/PermissionsModule.java b/src/com/hypixel/hytale/server/core/permissions/PermissionsModule.java index c791dc5a..297b8005 100644 --- a/src/com/hypixel/hytale/server/core/permissions/PermissionsModule.java +++ b/src/com/hypixel/hytale/server/core/permissions/PermissionsModule.java @@ -8,18 +8,22 @@ import com.hypixel.hytale.server.core.command.system.CommandRegistry; import com.hypixel.hytale.server.core.event.events.permissions.GroupPermissionChangeEvent; import com.hypixel.hytale.server.core.event.events.permissions.PlayerGroupEvent; import com.hypixel.hytale.server.core.event.events.permissions.PlayerPermissionChangeEvent; +import com.hypixel.hytale.server.core.io.handlers.game.GamePacketHandler; import com.hypixel.hytale.server.core.permissions.commands.PermCommand; import com.hypixel.hytale.server.core.permissions.commands.op.OpCommand; import com.hypixel.hytale.server.core.permissions.provider.HytalePermissionsProvider; import com.hypixel.hytale.server.core.permissions.provider.PermissionProvider; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; @@ -37,6 +41,7 @@ public class PermissionsModule extends JavaPlugin { @Nonnull private final List providers = new CopyOnWriteArrayList() { { + Objects.requireNonNull(PermissionsModule.this); this.add(PermissionsModule.this.standardProvider); } }; @@ -124,6 +129,7 @@ public class PermissionsModule extends JavaPlugin { .getEventBus() .dispatchFor(PlayerGroupEvent.Added.class) .dispatch(new PlayerGroupEvent.Added(uuid, group)); + resendCommandTreeForPlayer(uuid); } public void removeUserFromGroup(@Nonnull UUID uuid, @Nonnull String group) { @@ -132,6 +138,14 @@ public class PermissionsModule extends JavaPlugin { .getEventBus() .dispatchFor(PlayerGroupEvent.Removed.class) .dispatch(new PlayerGroupEvent.Removed(uuid, group)); + resendCommandTreeForPlayer(uuid); + } + + private static void resendCommandTreeForPlayer(@Nonnull UUID uuid) { + PlayerRef playerRef = Universe.get().getPlayer(uuid); + if (playerRef != null && playerRef.getPacketHandler() instanceof GamePacketHandler gameHandler) { + gameHandler.sendCommandTree(); + } } public void setVirtualGroups(@Nonnull Map> virtualGroups) { diff --git a/src/com/hypixel/hytale/server/core/plugin/PluginBase.java b/src/com/hypixel/hytale/server/core/plugin/PluginBase.java index 0b25e750..6a13e89b 100644 --- a/src/com/hypixel/hytale/server/core/plugin/PluginBase.java +++ b/src/com/hypixel/hytale/server/core/plugin/PluginBase.java @@ -24,8 +24,8 @@ import com.hypixel.hytale.server.core.plugin.registry.CodecMapRegistry; import com.hypixel.hytale.server.core.plugin.registry.IRegistry; import com.hypixel.hytale.server.core.plugin.registry.MapKeyMapRegistry; import com.hypixel.hytale.server.core.registry.ClientFeatureRegistry; +import com.hypixel.hytale.server.core.schema.SchemaGenerator; import com.hypixel.hytale.server.core.task.TaskRegistry; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateRegistry; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.Config; @@ -72,9 +72,6 @@ public abstract class PluginBase implements CommandOwner { private final EventRegistry eventRegistry = new EventRegistry( this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString, HytaleServer.get().getEventBus() ); - private final BlockStateRegistry blockStateRegistry = new BlockStateRegistry( - this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString - ); private final EntityRegistry entityRegistry = new EntityRegistry( this.shutdownTasks, () -> this.state != PluginState.NONE && this.state != PluginState.DISABLED, this.notEnabledString ); @@ -116,6 +113,9 @@ public abstract class PluginBase implements CommandOwner { } else { Config config = new Config<>(this.dataDirectory, name, configCodec); this.configs.add(config); + PluginIdentifier id = this.getIdentifier(); + String schemaName = "Plugin." + id.getGroup() + "." + id.getName() + "." + name; + SchemaGenerator.registerConfig(schemaName, configCodec, "Config/Plugin/" + id.getGroup() + "/" + id.getName(), null); return config; } } @@ -181,11 +181,6 @@ public abstract class PluginBase implements CommandOwner { return this.eventRegistry; } - @Nonnull - public BlockStateRegistry getBlockStateRegistry() { - return this.blockStateRegistry; - } - @Nonnull public EntityRegistry getEntityRegistry() { return this.entityRegistry; @@ -305,7 +300,6 @@ public abstract class PluginBase implements CommandOwner { this.commandRegistry.shutdown(); this.eventRegistry.shutdown(); this.clientFeatureRegistry.shutdown(); - this.blockStateRegistry.shutdown(); this.taskRegistry.shutdown(); this.entityStoreRegistry.shutdown(); this.chunkStoreRegistry.shutdown(); diff --git a/src/com/hypixel/hytale/server/core/plugin/PluginClassLoader.java b/src/com/hypixel/hytale/server/core/plugin/PluginClassLoader.java index 2595dc01..8d6072f3 100644 --- a/src/com/hypixel/hytale/server/core/plugin/PluginClassLoader.java +++ b/src/com/hypixel/hytale/server/core/plugin/PluginClassLoader.java @@ -14,12 +14,20 @@ public class PluginClassLoader extends URLClassLoader { @Nonnull private final PluginManager pluginManager; private final boolean inServerClassPath; + private final boolean childFirst; @Nullable private JavaPlugin plugin; public PluginClassLoader(@Nonnull PluginManager pluginManager, @Nullable PluginIdentifier identifier, boolean inServerClassPath, @Nonnull URL... urls) { + this(pluginManager, identifier, inServerClassPath, false, urls); + } + + public PluginClassLoader( + @Nonnull PluginManager pluginManager, @Nullable PluginIdentifier identifier, boolean inServerClassPath, boolean childFirst, @Nonnull URL... urls + ) { super((inServerClassPath ? "BuiltinPlugin" : "ThirdParty") + (identifier != null ? "(" + identifier + ")" : ""), urls, null); this.inServerClassPath = inServerClassPath; + this.childFirst = childFirst; this.pluginManager = pluginManager; } @@ -39,6 +47,16 @@ public class PluginClassLoader extends URLClassLoader { @Nonnull private Class loadClass0(@Nonnull String name, boolean useBridge) throws ClassNotFoundException { + if (this.childFirst) { + try { + Class loadClass = super.loadClass(name, false); + if (loadClass != null) { + return loadClass; + } + } catch (ClassNotFoundException var8) { + } + } + try { Class loadClass = PluginManager.class.getClassLoader().loadClass(name); if (loadClass != null) { @@ -47,12 +65,14 @@ public class PluginClassLoader extends URLClassLoader { } catch (ClassNotFoundException var7) { } - try { - Class loadClass = super.loadClass(name, false); - if (loadClass != null) { - return loadClass; + if (!this.childFirst) { + try { + Class loadClass = super.loadClass(name, false); + if (loadClass != null) { + return loadClass; + } + } catch (ClassNotFoundException var6) { } - } catch (ClassNotFoundException var6) { } if (useBridge) { @@ -157,7 +177,7 @@ public class PluginClassLoader extends URLClassLoader { public static boolean isFromThirdPartyPlugin(@Nullable Throwable throwable) { while (throwable != null) { for (StackTraceElement element : throwable.getStackTrace()) { - if ("ThirdParty".equals(element.getClassLoaderName())) { + if (element.getClassLoaderName() != null && element.getClassLoaderName().startsWith("ThirdParty")) { return true; } } diff --git a/src/com/hypixel/hytale/server/core/plugin/PluginManager.java b/src/com/hypixel/hytale/server/core/plugin/PluginManager.java index 1ce2d175..aed0068f 100644 --- a/src/com/hypixel/hytale/server/core/plugin/PluginManager.java +++ b/src/com/hypixel/hytale/server/core/plugin/PluginManager.java @@ -5,6 +5,8 @@ import com.hypixel.hytale.assetstore.AssetStore; import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.util.RawJsonReader; +import com.hypixel.hytale.common.plugin.Mod; +import com.hypixel.hytale.common.plugin.ModLoadOrderException; import com.hypixel.hytale.common.plugin.PluginIdentifier; import com.hypixel.hytale.common.plugin.PluginManifest; import com.hypixel.hytale.common.semver.SemverRange; @@ -13,7 +15,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.event.IEventDispatcher; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.metrics.MetricsRegistry; -import com.hypixel.hytale.server.core.Constants; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.HytaleServerConfig; import com.hypixel.hytale.server.core.Message; @@ -22,7 +23,6 @@ import com.hypixel.hytale.server.core.ShutdownReason; import com.hypixel.hytale.server.core.asset.AssetModule; import com.hypixel.hytale.server.core.command.system.CommandManager; import com.hypixel.hytale.server.core.config.ModConfig; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.player.AddPlayerToWorldEvent; import com.hypixel.hytale.server.core.plugin.commands.PluginCommand; import com.hypixel.hytale.server.core.plugin.event.PluginSetupEvent; @@ -51,7 +51,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.time.Duration; +import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -86,7 +86,7 @@ public class PluginManager { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Map plugins = new Object2ObjectLinkedOpenHashMap<>(); private final Map classLoaders = new ConcurrentHashMap<>(); - private boolean hasOutdatedPlugins = false; + private final List outdatedPlugins = new ArrayList<>(); private final boolean loadExternalPlugins = true; @Nonnull private PluginState state = PluginState.NONE; @@ -121,7 +121,7 @@ public class PluginManager { enabled = modConfig.getEnabled(); } else { HytaleServerConfig serverConfig = HytaleServer.get().getConfig(); - enabled = !manifest.isDisabledByDefault() && (plugin.isInServerClassPath() || serverConfig.getDefaultModsEnabled()); + enabled = !manifest.isDisabledByDefault() && (plugin.isCoreMod() || serverConfig.getDefaultModsEnabled()); } if (enabled) { @@ -132,7 +132,7 @@ public class PluginManager { } } - public void setup() { + public void setup() throws ModLoadOrderException { if (this.state != PluginState.NONE) { throw new IllegalStateException("Expected PluginState.NONE but found " + this.state); } else { @@ -154,14 +154,6 @@ public class PluginManager { } } - Path self; - try { - self = Paths.get(PluginManager.class.getProtectionDomain().getCodeSource().getLocation().toURI()); - } catch (URISyntaxException var30) { - throw new RuntimeException(var30); - } - - this.loadPluginsFromDirectory(pending, self.getParent().resolve("builtin"), false, this.availablePlugins); this.loadPluginsInClasspath(pending, this.availablePlugins); this.loadPluginsFromDirectory(pending, MODS_PATH, !Options.getOptionSet().has(Options.BARE), this.availablePlugins); @@ -183,8 +175,8 @@ public class PluginManager { try { this.validatePluginDeps(pendingLoadPlugin, pending); - } catch (MissingPluginDependencyException var29) { - LOGGER.at(Level.SEVERE).log(var29.getMessage()); + } catch (MissingPluginDependencyException var25) { + LOGGER.at(Level.SEVERE).log(var25.getMessage()); iterator.remove(); } } @@ -192,30 +184,49 @@ public class PluginManager { this.lock.readLock().unlock(); } - if (this.hasOutdatedPlugins && System.getProperty("hytale.allow_outdated_mods") == null) { + if (!this.outdatedPlugins.isEmpty() && System.getProperty("hytale.allow_outdated_mods") == null) { LOGGER.at(Level.SEVERE) .log("One or more plugins are targeting a different server version. It is recommended to update these plugins to ensure compatibility."); + HytaleServer.get() + .getEventBus() + .registerGlobal( + AddPlayerToWorldEvent.class, + event -> { + PlayerRef playerRef = event.getHolder().getComponent(PlayerRef.getComponentType()); + if (playerRef != null) { + if (playerRef.hasPermission("hytale.mods.outdated.notify")) { + StringBuilder modsList = new StringBuilder(); - try { - if (!Constants.SINGLEPLAYER) { - Thread.sleep(Duration.ofSeconds(2L)); - } - } catch (InterruptedException var32) { - throw new RuntimeException(var32); - } + for (PluginIdentifier id : this.outdatedPlugins) { + modsList.append("\n - ").append(id); + } - HytaleServer.get().getEventBus().registerGlobal(AddPlayerToWorldEvent.class, event -> { - PlayerRef playerRef = event.getHolder().getComponent(PlayerRef.getComponentType()); - Player player = event.getHolder().getComponent(Player.getComponentType()); - if (playerRef != null && player != null) { - if (player.hasPermission("hytale.mods.outdated.notify")) { - playerRef.sendMessage(Message.translation("server.pluginManager.outOfDatePlugins").color(Color.RED)); + playerRef.sendMessage( + Message.translation("server.pluginManager.outOfDatePlugins") + .param("count", this.outdatedPlugins.size()) + .param("mods", modsList.toString()) + .color(Color.RED) + ); + } + } } - } - }); + ); + } + + List modOrder = List.of(HytaleServer.get().getConfig().getModLoadOrder()); + this.loadOrder = Mod.calculateLoadOrder(pending, pluginIdentifier -> AssetModule.get().getAssetPack(pluginIdentifier.toString()) != null, modOrder); + + for (PendingLoadPlugin pendingLoadPlugin : this.loadOrder) { + if (pendingLoadPlugin instanceof PendingLoadJavaPlugin javaPlugin + && javaPlugin.isCoreMod() + && !"Hytale".equals(javaPlugin.getIdentifier().getGroup())) { + PluginClassLoader oldClassLoader = javaPlugin.getClassLoader(); + PluginClassLoader newClassLoader = new PluginClassLoader(this, javaPlugin.getIdentifier(), true, true, oldClassLoader.getURLs()); + javaPlugin.setClassLoader(newClassLoader); + LOGGER.at(Level.FINE).log("Using child-first classloader for classpath plugin %s", javaPlugin.getIdentifier()); + } } - this.loadOrder = PendingLoadPlugin.calculateLoadOrder(pending); this.loading = new Object2ObjectOpenHashMap<>(); pending.forEach((identifier, pendingLoad) -> this.availablePlugins.put(identifier, pendingLoad.getManifest())); ObjectArrayList> preLoadFutures = new ObjectArrayList<>(); @@ -225,55 +236,65 @@ public class PluginManager { try { LOGGER.at(Level.FINE).log("Loading plugins!"); - for (PendingLoadPlugin pendingLoadPlugin : this.loadOrder) { - LOGGER.at(Level.FINE).log("- %s", pendingLoadPlugin.getIdentifier()); + for (PendingLoadPlugin pendingLoadPluginx : this.loadOrder) { + LOGGER.at(Level.FINE).log("- %s", pendingLoadPluginx.getIdentifier()); try { - PluginBase plugin = pendingLoadPlugin.load(); + PluginBase plugin = pendingLoadPluginx.load(); this.plugins.put(plugin.getIdentifier(), plugin); this.loading.put(plugin.getIdentifier(), plugin); CompletableFuture future = plugin.preLoad(); if (future != null) { preLoadFutures.add(future); } - } catch (ClassNotFoundException var26) { - LOGGER.at(Level.SEVERE).withCause(var26).log("Failed to load plugin %s. Failed to find main class!", pendingLoadPlugin.getPath()); - failedBootPlugins.add(pendingLoadPlugin.getIdentifier()); - } catch (NoSuchMethodException var27) { - LOGGER.at(Level.SEVERE).withCause(var27).log("Failed to load plugin %s. Requires default constructor!", pendingLoadPlugin.getPath()); - failedBootPlugins.add(pendingLoadPlugin.getIdentifier()); - } catch (Throwable var28) { - LOGGER.at(Level.SEVERE).withCause(var28).log("Failed to load plugin %s", pendingLoadPlugin.getPath()); - failedBootPlugins.add(pendingLoadPlugin.getIdentifier()); + } catch (ClassNotFoundException var22) { + LOGGER.at(Level.SEVERE).withCause(var22).log("Failed to load plugin %s. Failed to find main class!", pendingLoadPluginx.getPath()); + failedBootPlugins.add(pendingLoadPluginx.getIdentifier()); + } catch (NoSuchMethodException var23) { + LOGGER.at(Level.SEVERE).withCause(var23).log("Failed to load plugin %s. Requires default constructor!", pendingLoadPluginx.getPath()); + failedBootPlugins.add(pendingLoadPluginx.getIdentifier()); + } catch (Throwable var24) { + LOGGER.at(Level.SEVERE).withCause(var24).log("Failed to load plugin %s", pendingLoadPluginx.getPath()); + failedBootPlugins.add(pendingLoadPluginx.getIdentifier()); } } } finally { this.lock.writeLock().unlock(); } - if (!failedBootPlugins.isEmpty() && !Constants.shouldSkipModValidation()) { - StringBuilder sb = new StringBuilder("Failed to boot the following plugins:\n"); + if (!failedBootPlugins.isEmpty() && !Options.getOptionSet().has(Options.IGNORE_BROKEN_MODS)) { + StringBuilder sb = new StringBuilder(); for (PluginIdentifier failed : failedBootPlugins) { sb.append(" - ").append(failed).append('\n'); } - HytaleServer.get().shutdownServer(ShutdownReason.MOD_ERROR.withMessage(sb.toString().trim())); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.pluginError.detail") + .param("count", failedBootPlugins.size()) + .param("detail", sb.toString().trim()); + HytaleServer.get().shutdownServer(ShutdownReason.MOD_ERROR.withMessage(reasonMessage)); } else { CompletableFuture.allOf(preLoadFutures.toArray(CompletableFuture[]::new)).join(); boolean hasFailed = false; for (PendingLoadPlugin pendingPlugin : this.loadOrder) { + if (HytaleServer.get().isShuttingDown()) { + break; + } + PluginBase plugin = this.loading.get(pendingPlugin.getIdentifier()); if (plugin != null && !this.setup(plugin)) { hasFailed = true; } } - if (!Constants.shouldSkipModValidation() && hasFailed) { - StringBuilder sb = new StringBuilder("Failed to setup the following plugins:\n"); - this.collectFailedPlugins(sb); - HytaleServer.get().shutdownServer(ShutdownReason.MOD_ERROR.withMessage(sb.toString().trim())); + if (!Options.getOptionSet().has(Options.IGNORE_BROKEN_MODS) && hasFailed) { + StringBuilder sb = new StringBuilder(); + int failedPluginCount = this.collectFailedPlugins(sb); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.pluginError.detail") + .param("count", failedPluginCount) + .param("detail", sb.toString().trim()); + HytaleServer.get().shutdownServer(ShutdownReason.MOD_ERROR.withMessage(reasonMessage)); } else { this.loading.values().removeIf(v -> v.getState().isInactive()); } @@ -309,11 +330,15 @@ public class PluginManager { if (!sb.isEmpty()) { String msg = "Failed to start server! Missing Mods:\n" + sb; LOGGER.at(Level.SEVERE).log(msg); - HytaleServer.get().shutdownServer(ShutdownReason.MISSING_REQUIRED_PLUGIN.withMessage(msg)); - } else if (hasFailed && !Constants.shouldSkipModValidation()) { - sb = new StringBuilder("Failed to start the following plugins:\n"); - this.collectFailedPlugins(sb); - HytaleServer.get().shutdownServer(ShutdownReason.MOD_ERROR.withMessage(sb.toString().trim())); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.missingRequiredPlugin.detail").param("detail", sb.toString()); + HytaleServer.get().shutdownServer(ShutdownReason.MISSING_REQUIRED_PLUGIN.withMessage(reasonMessage)); + } else if (hasFailed && !Options.getOptionSet().has(Options.IGNORE_BROKEN_MODS)) { + sb = new StringBuilder(); + int failedPluginCount = this.collectFailedPlugins(sb); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.pluginError.detail") + .param("count", failedPluginCount) + .param("detail", sb.toString().trim()); + HytaleServer.get().shutdownServer(ShutdownReason.MOD_ERROR.withMessage(reasonMessage)); } else { this.loadOrder = null; this.loading = null; @@ -321,15 +346,21 @@ public class PluginManager { } } - private void collectFailedPlugins(StringBuilder sb) { - if (this.loading != null) { + private int collectFailedPlugins(StringBuilder sb) { + int count = 0; + if (this.loading == null) { + return count; + } else { for (Entry failed : this.loading.entrySet()) { if (failed.getValue().getState() == PluginState.FAILED) { Throwable reasonThrowable = failed.getValue().getFailureCause(); String reason = reasonThrowable != null ? reasonThrowable.toString() : "Unknown"; sb.append(" - ").append(failed.getKey()).append(": ").append(reason).append('\n'); + count++; } } + + return count; } } @@ -387,7 +418,7 @@ public class PluginManager { ); } - this.hasOutdatedPlugins = true; + this.outdatedPlugins.add(pendingLoadPlugin.getIdentifier()); } } @@ -753,28 +784,8 @@ public class PluginManager { } } } - - Path path = Paths.get(uri).getParent().resolve("builtin"); - if (Files.exists(path)) { - try (DirectoryStream stream = Files.newDirectoryStream(path)) { - for (Path file : stream) { - if (Files.isRegularFile(file) && file.getFileName().toString().toLowerCase().endsWith(".jar")) { - PluginManifest manifestx = loadManifest(file); - if (manifestx != null && new PluginIdentifier(manifestx).equals(identifier)) { - PendingLoadJavaPlugin pendingLoadJavaPlugin = this.loadPendingJavaPlugin(file); - if (pendingLoadJavaPlugin != null) { - return this.load(pendingLoadJavaPlugin); - } - break; - } - } - } - } catch (IOException var29) { - LOGGER.at(Level.SEVERE).withCause(var29).log("Failed to find plugins!"); - } - } - } catch (URISyntaxException | IOException var30) { - LOGGER.at(Level.SEVERE).withCause(var30).log("Failed to load pending classpath plugin. Failed to load manifest file!"); + } catch (URISyntaxException | IOException var27) { + LOGGER.at(Level.SEVERE).withCause(var27).log("Failed to load pending classpath plugin. Failed to load manifest file!"); } Boolean result = this.findPluginInDirectory(identifier, MODS_PATH); @@ -888,7 +899,8 @@ public class PluginManager { } private boolean setup(@Nonnull PluginBase plugin) { - if (plugin.getState() == PluginState.NONE && this.dependenciesMatchState(plugin, PluginState.SETUP, PluginState.SETUP)) { + PluginState requiredDepState = this.state == PluginState.SETUP ? PluginState.SETUP : PluginState.ENABLED; + if (plugin.getState() == PluginState.NONE && this.dependenciesMatchState(plugin, requiredDepState, PluginState.SETUP)) { LOGGER.at(Level.FINE).log("Setting up plugin %s", plugin.getIdentifier()); boolean prev = AssetStore.DISABLE_DYNAMIC_DEPENDENCIES; AssetStore.DISABLE_DYNAMIC_DEPENDENCIES = false; @@ -958,7 +970,10 @@ public class PluginManager { private static void loadPendingPlugin(@Nonnull Map pending, @Nonnull PendingLoadPlugin plugin) { if (pending.putIfAbsent(plugin.getIdentifier(), plugin) != null) { - throw new IllegalArgumentException("Tried to load duplicate plugin: " + plugin.getIdentifier()); + String detail = "Tried to load duplicate plugin: " + plugin.getIdentifier(); + LOGGER.at(Level.SEVERE).log(detail); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.pluginDuplicate").param("plugin", plugin.getIdentifier().toString()); + HytaleServer.get().shutdownServer(ShutdownReason.MOD_ERROR.withMessage(reasonMessage)); } else { for (PendingLoadPlugin subPlugin : plugin.createSubPendingLoadPlugins()) { loadPendingPlugin(pending, subPlugin); diff --git a/src/com/hypixel/hytale/server/core/plugin/pending/PendingLoadJavaPlugin.java b/src/com/hypixel/hytale/server/core/plugin/pending/PendingLoadJavaPlugin.java index 9ccea665..da6263b4 100644 --- a/src/com/hypixel/hytale/server/core/plugin/pending/PendingLoadJavaPlugin.java +++ b/src/com/hypixel/hytale/server/core/plugin/pending/PendingLoadJavaPlugin.java @@ -14,7 +14,7 @@ import javax.annotation.Nullable; public class PendingLoadJavaPlugin extends PendingLoadPlugin { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @Nonnull - private final PluginClassLoader urlClassLoader; + private PluginClassLoader urlClassLoader; public PendingLoadJavaPlugin(@Nullable Path path, @Nonnull PluginManifest manifest, @Nonnull PluginClassLoader urlClassLoader) { super(path, manifest); @@ -28,10 +28,19 @@ public class PendingLoadJavaPlugin extends PendingLoadPlugin { } @Override - public boolean isInServerClassPath() { + public boolean isCoreMod() { return this.urlClassLoader.isInServerClassPath(); } + @Nonnull + public PluginClassLoader getClassLoader() { + return this.urlClassLoader; + } + + public void setClassLoader(@Nonnull PluginClassLoader classLoader) { + this.urlClassLoader = classLoader; + } + @Nonnull public JavaPlugin load() throws Exception { PluginManifest manifest = this.getManifest(); diff --git a/src/com/hypixel/hytale/server/core/plugin/pending/PendingLoadPlugin.java b/src/com/hypixel/hytale/server/core/plugin/pending/PendingLoadPlugin.java index 1398cd43..16255e30 100644 --- a/src/com/hypixel/hytale/server/core/plugin/pending/PendingLoadPlugin.java +++ b/src/com/hypixel/hytale/server/core/plugin/pending/PendingLoadPlugin.java @@ -1,23 +1,18 @@ package com.hypixel.hytale.server.core.plugin.pending; +import com.hypixel.hytale.common.plugin.Mod; import com.hypixel.hytale.common.plugin.PluginIdentifier; import com.hypixel.hytale.common.plugin.PluginManifest; import com.hypixel.hytale.server.core.plugin.PluginBase; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.nio.file.Path; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.Set; -import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public abstract class PendingLoadPlugin { +public abstract class PendingLoadPlugin implements Mod { @Nonnull private final PluginIdentifier identifier; @Nonnull @@ -37,6 +32,7 @@ public abstract class PendingLoadPlugin { } @Nonnull + @Override public PluginManifest getManifest() { return this.manifest; } @@ -72,7 +68,8 @@ public abstract class PendingLoadPlugin { return this.manifest.getDependencies().containsKey(identifier) || this.manifest.getOptionalDependencies().containsKey(identifier); } - public abstract boolean isInServerClassPath(); + @Override + public abstract boolean isCoreMod(); @Override public boolean equals(@Nullable Object o) { @@ -102,131 +99,4 @@ public abstract class PendingLoadPlugin { public String toString() { return "PendingLoadPlugin{identifier=" + this.identifier + ", manifest=" + this.manifest + ", path=" + this.path + "}"; } - - @Nonnull - public static List calculateLoadOrder(@Nonnull Map pending) { - HashMap nodes = new HashMap<>(pending.size()); - - for (Entry entry : pending.entrySet()) { - nodes.put(entry.getKey(), new PendingLoadPlugin.EntryNode(entry.getValue())); - } - - HashSet classpathPlugins = new HashSet<>(); - - for (Entry entry : pending.entrySet()) { - if (entry.getValue().isInServerClassPath() && "Hytale".equals(entry.getKey().getGroup())) { - classpathPlugins.add(entry.getKey()); - } - } - - HashMap> missingDependencies = new HashMap<>(); - - for (PendingLoadPlugin.EntryNode node : nodes.values()) { - PluginManifest manifest = node.plugin.manifest; - - for (PluginIdentifier depId : manifest.getDependencies().keySet()) { - if (nodes.containsKey(depId)) { - node.edge.add(depId); - } else { - missingDependencies.computeIfAbsent(node.plugin.identifier, k -> new HashSet<>()).add(depId); - } - } - - for (PluginIdentifier identifier : manifest.getOptionalDependencies().keySet()) { - PendingLoadPlugin.EntryNode dep = nodes.get(identifier); - if (dep != null) { - node.edge.add(identifier); - } - } - - if (!node.plugin.isInServerClassPath()) { - node.edge.addAll(classpathPlugins); - } - } - - HashMap> missingLoadBefore = new HashMap<>(); - - for (Entry entryx : pending.entrySet()) { - PluginManifest manifest = entryx.getValue().manifest; - - for (PluginIdentifier targetId : manifest.getLoadBefore().keySet()) { - PendingLoadPlugin.EntryNode targetNode = nodes.get(targetId); - if (targetNode != null) { - targetNode.edge.add(entryx.getKey()); - } else { - missingLoadBefore.computeIfAbsent(entryx.getKey(), k -> new HashSet<>()).add(targetId); - } - } - } - - if (missingDependencies.isEmpty() && missingLoadBefore.isEmpty()) { - ObjectArrayList loadOrder = new ObjectArrayList<>(nodes.size()); - - while (!nodes.isEmpty()) { - boolean didWork = false; - Iterator> iterator = nodes.entrySet().iterator(); - - while (iterator.hasNext()) { - Entry entryx = iterator.next(); - PendingLoadPlugin.EntryNode node = entryx.getValue(); - if (node.edge.isEmpty()) { - didWork = true; - iterator.remove(); - loadOrder.add(node.plugin); - PluginIdentifier identifierx = entryx.getKey(); - - for (PendingLoadPlugin.EntryNode otherNode : nodes.values()) { - otherNode.edge.remove(identifierx); - } - } - } - - if (!didWork) { - StringBuilder sb = new StringBuilder("Found cyclic dependency between plugins:\n"); - - for (Entry entryx : nodes.entrySet()) { - sb.append(" ").append(entryx.getKey()).append(" waiting on: ").append(entryx.getValue().edge).append("\n"); - } - - throw new IllegalArgumentException(sb.toString()); - } - } - - return loadOrder; - } else { - StringBuilder sb = new StringBuilder(); - if (!missingDependencies.isEmpty()) { - sb.append("Missing required dependencies:\n"); - - for (Entry> entryx : missingDependencies.entrySet()) { - sb.append(" ").append(entryx.getKey()).append(" requires: ").append(entryx.getValue()).append("\n"); - } - } - - if (!missingLoadBefore.isEmpty()) { - sb.append("Missing loadBefore targets:\n"); - - for (Entry> entryx : missingLoadBefore.entrySet()) { - sb.append(" ").append(entryx.getKey()).append(" loadBefore: ").append(entryx.getValue()).append("\n"); - } - } - - throw new IllegalArgumentException(sb.toString()); - } - } - - private static final class EntryNode { - private final Set edge = new HashSet<>(); - private final PendingLoadPlugin plugin; - - private EntryNode(PendingLoadPlugin plugin) { - this.plugin = plugin; - } - - @Nonnull - @Override - public String toString() { - return "EntryNode{plugin=" + this.plugin + ", dependencies=" + this.edge + "}"; - } - } } diff --git a/src/com/hypixel/hytale/server/core/prefab/PrefabRotation.java b/src/com/hypixel/hytale/server/core/prefab/PrefabRotation.java index 199fd050..b9fdd9e2 100644 --- a/src/com/hypixel/hytale/server/core/prefab/PrefabRotation.java +++ b/src/com/hypixel/hytale/server/core/prefab/PrefabRotation.java @@ -1,13 +1,13 @@ package com.hypixel.hytale.server.core.prefab; import com.hypixel.hytale.math.Axis; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.math.vector.Vector3l; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.universe.world.chunk.BlockRotationUtil; import javax.annotation.Nonnull; +import org.joml.Vector3L; +import org.joml.Vector3d; +import org.joml.Vector3i; public enum PrefabRotation { ROTATION_0(Rotation.None, new PrefabRotation.RotationExecutor_0()), @@ -40,6 +40,11 @@ public enum PrefabRotation { this.executor = executor; } + @Nonnull + public Rotation getRotation() { + return this.rotation; + } + public PrefabRotation add(@Nonnull PrefabRotation other) { int val = this.rotation.getDegrees() + other.rotation.getDegrees(); return VALUES[val % 360 / 90]; @@ -59,7 +64,7 @@ public enum PrefabRotation { v.z = this.executor.rotateIntZ(x, z); } - public void rotate(@Nonnull Vector3l v) { + public void rotate(@Nonnull Vector3L v) { long x = v.x; long z = v.z; v.x = this.executor.rotateLongX(x, z); diff --git a/src/com/hypixel/hytale/server/core/prefab/PrefabStore.java b/src/com/hypixel/hytale/server/core/prefab/PrefabStore.java index c73f9095..11cdafa6 100644 --- a/src/com/hypixel/hytale/server/core/prefab/PrefabStore.java +++ b/src/com/hypixel/hytale/server/core/prefab/PrefabStore.java @@ -200,6 +200,19 @@ public class PrefabStore { return null; } + @Nonnull + public Path getRelativePrefabPath(@Nonnull Path absolutePrefabPath) { + Path normalized = absolutePrefabPath.toAbsolutePath().normalize(); + AssetPack sourcePack = this.findAssetPackForPrefabPath(normalized); + if (sourcePack != null) { + Path sourcePrefabsPath = this.getAssetPrefabsPathForPack(sourcePack).toAbsolutePath().normalize(); + return sourcePrefabsPath.relativize(normalized); + } else { + Path serverPrefabsPath = this.getServerPrefabsPath().toAbsolutePath().normalize(); + return normalized.startsWith(serverPrefabsPath) ? serverPrefabsPath.relativize(normalized) : normalized.getFileName(); + } + } + @Nonnull public BlockSelection getAssetPrefab(@Nonnull String key) { return this.getPrefab(resolvePrefabKey(this.getAssetPrefabsPath(), key)); @@ -218,6 +231,21 @@ public class PrefabStore { this.savePrefab(resolvePrefabKey(this.getAssetPrefabsPath(), key), prefab, overwrite); } + public void savePrefabToPack(@Nonnull AssetPack pack, @Nonnull String key, @Nonnull BlockSelection prefab, boolean overwrite) { + if (!AssetModule.get().validatePackExistsOnDisk(pack)) { + throw new PrefabSaveException( + PrefabSaveException.Type.ERROR, new IllegalStateException("Asset pack '" + pack.getName() + "' no longer exists on disk") + ); + } else { + this.savePrefab(resolvePrefabKey(this.getAssetPrefabsPathForPack(pack), key), prefab, overwrite); + } + } + + @Nonnull + public Path getPrefabsPathForPack(@Nullable AssetPack pack) { + return pack != null ? this.getAssetPrefabsPathForPack(pack) : this.getServerPrefabsPath(); + } + @Nonnull public BlockSelection getWorldGenPrefab(@Nonnull String key) { return this.getWorldGenPrefab(this.getWorldGenPrefabsPath(), key); diff --git a/src/com/hypixel/hytale/server/core/prefab/config/SelectionPrefabSerializer.java b/src/com/hypixel/hytale/server/core/prefab/config/SelectionPrefabSerializer.java index 80a90564..098a293b 100644 --- a/src/com/hypixel/hytale/server/core/prefab/config/SelectionPrefabSerializer.java +++ b/src/com/hypixel/hytale/server/core/prefab/config/SelectionPrefabSerializer.java @@ -2,40 +2,21 @@ package com.hypixel.hytale.server.core.prefab.config; import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; -import com.hypixel.hytale.codec.DirectDecodeCodec; -import com.hypixel.hytale.codec.ExtraInfo; -import com.hypixel.hytale.codec.lookup.ACodecMapCodec; -import com.hypixel.hytale.component.Archetype; -import com.hypixel.hytale.component.Component; -import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.component.data.unknown.TempUnknownComponent; -import com.hypixel.hytale.component.data.unknown.UnknownComponents; -import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockMigration; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; -import com.hypixel.hytale.server.core.entity.Entity; -import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.prefab.selection.buffer.BsonPrefabBufferDeserializer; import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import com.hypixel.hytale.server.core.util.FillerBlockUtil; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Function; -import java.util.logging.Level; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import org.bson.BsonArray; import org.bson.BsonDocument; import org.bson.BsonInt32; @@ -58,14 +39,11 @@ public class SelectionPrefabSerializer { public static BlockSelection deserialize(@Nonnull BsonDocument doc) { BsonValue versionValue = doc.get("version"); int version = versionValue != null ? versionValue.asInt32().getValue() : -1; - if (version <= 0) { + if (version < 8) { throw new IllegalArgumentException("Prefab version is too old: " + version); } else if (version > 8) { throw new IllegalArgumentException("Prefab version is too new: " + version + " by expected 8"); } else { - int worldVersion = version < 4 ? readWorldVersion(doc) : 0; - BsonValue entityVersionValue = doc.get("entityVersion"); - int entityVersion = entityVersionValue != null ? entityVersionValue.asInt32().getValue() : 0; int anchorX = doc.getInt32("anchorX").getValue(); int anchorY = doc.getInt32("anchorY").getValue(); int anchorZ = doc.getInt32("anchorZ").getValue(); @@ -95,116 +73,9 @@ public class SelectionPrefabSerializer { int y = innerObj.getInt32("y").getValue(); int z = innerObj.getInt32("z").getValue(); String blockTypeStr = innerObj.getString("name").getValue(); - boolean legacyStripName = false; - if (version <= 4) { - Fluid.ConversionResult result = Fluid.convertBlockToFluid(blockTypeStr); - if (result != null) { - legacyStripName = true; - selection.addFluidAtLocalPos(x, y, z, result.fluidId, result.fluidLevel); - if (result.blockTypeStr == null) { - continue; - } - } - } - - int support = 0; - if (version >= 6) { - support = innerObj.getInt32("support", DEFAULT_SUPPORT_VALUE).getValue(); - } else if (blockTypeStr.contains("|Deco")) { - legacyStripName = true; - support = 15; - } else if (blockTypeStr.contains("|Support=")) { - legacyStripName = true; - int start = blockTypeStr.indexOf("|Support=") + "|Support=".length(); - int end = blockTypeStr.indexOf(124, start); - if (end == -1) { - end = blockTypeStr.length(); - } - - support = Integer.parseInt(blockTypeStr, start, end, 10); - } else { - support = 0; - } - - int filler = 0; - if (version >= 7) { - filler = innerObj.getInt32("filler", DEFAULT_FILLER_VALUE).getValue(); - } else if (blockTypeStr.contains("|Filler=")) { - legacyStripName = true; - int start = blockTypeStr.indexOf("|Filler=") + "|Filler=".length(); - int firstComma = blockTypeStr.indexOf(44, start); - if (firstComma == -1) { - throw new IllegalArgumentException("Invalid filler metadata! Missing comma"); - } - - int secondComma = blockTypeStr.indexOf(44, firstComma + 1); - if (secondComma == -1) { - throw new IllegalArgumentException("Invalid filler metadata! Missing second comma"); - } - - int end = blockTypeStr.indexOf(124, start); - if (end == -1) { - end = blockTypeStr.length(); - } - - int fillerX = Integer.parseInt(blockTypeStr, start, firstComma, 10); - int fillerY = Integer.parseInt(blockTypeStr, firstComma + 1, secondComma, 10); - int fillerZ = Integer.parseInt(blockTypeStr, secondComma + 1, end, 10); - filler = FillerBlockUtil.pack(fillerX, fillerY, fillerZ); - } else { - filler = 0; - } - - int rotation = 0; - if (version >= 8) { - rotation = innerObj.getInt32("rotation", DEFAULT_ROTATION_VALUE).getValue(); - } else { - Rotation yaw = Rotation.None; - Rotation pitch = Rotation.None; - Rotation roll = Rotation.None; - if (blockTypeStr.contains("|Yaw=")) { - legacyStripName = true; - int startx = blockTypeStr.indexOf("|Yaw=") + "|Yaw=".length(); - int end = blockTypeStr.indexOf(124, startx); - if (end == -1) { - end = blockTypeStr.length(); - } - - yaw = Rotation.ofDegrees(Integer.parseInt(blockTypeStr, startx, end, 10)); - } - - if (blockTypeStr.contains("|Pitch=")) { - legacyStripName = true; - int startx = blockTypeStr.indexOf("|Pitch=") + "|Pitch=".length(); - int end = blockTypeStr.indexOf(124, startx); - if (end == -1) { - end = blockTypeStr.length(); - } - - pitch = Rotation.ofDegrees(Integer.parseInt(blockTypeStr, startx, end, 10)); - } - - if (blockTypeStr.contains("|Roll=")) { - legacyStripName = true; - int startx = blockTypeStr.indexOf("|Roll=") + "|Roll=".length(); - int end = blockTypeStr.indexOf(124, startx); - if (end == -1) { - end = blockTypeStr.length(); - } - - pitch = Rotation.ofDegrees(Integer.parseInt(blockTypeStr, startx, end, 10)); - } - - rotation = RotationTuple.index(yaw, pitch, roll); - } - - if (legacyStripName) { - int endOfName = blockTypeStr.indexOf(124); - if (endOfName != -1) { - blockTypeStr = blockTypeStr.substring(0, endOfName); - } - } - + int support = innerObj.getInt32("support", DEFAULT_SUPPORT_VALUE).getValue(); + int filler = innerObj.getInt32("filler", DEFAULT_FILLER_VALUE).getValue(); + int rotation = innerObj.getInt32("rotation", DEFAULT_ROTATION_VALUE).getValue(); String blockTypeKey = blockTypeStr; if (blockMigration != null) { blockTypeKey = blockMigration.apply(blockTypeStr); @@ -212,20 +83,9 @@ public class SelectionPrefabSerializer { int blockId = BlockType.getBlockIdOrUnknown(assetMap, blockTypeKey, "Failed to find block '%s' in unknown legacy prefab!", blockTypeStr); Holder wrapper = null; - if (version <= 2) { - BsonValue stateValue = innerObj.get("state"); - if (stateValue != null) { - wrapper = legacyStateDecode(stateValue.asDocument()); - } - } else { - BsonValue stateValue = innerObj.get("components"); - if (stateValue != null) { - if (version < 4) { - wrapper = ChunkStore.REGISTRY.deserialize(stateValue.asDocument(), worldVersion); - } else { - wrapper = ChunkStore.REGISTRY.deserialize(stateValue.asDocument()); - } - } + BsonValue stateValue = innerObj.get("components"); + if (stateValue != null) { + wrapper = ChunkStore.REGISTRY.deserialize(stateValue.asDocument()); } selection.addBlockAtLocalPos(x, y, z, blockId, rotation, filler, support, wrapper); @@ -255,15 +115,7 @@ public class SelectionPrefabSerializer { for (int i = 0; i < entities.size(); i++) { BsonDocument bsonDocument = entities.get(i).asDocument(); - if (version <= 1) { - try { - selection.addEntityHolderRaw(legacyEntityDecode(bsonDocument, entityVersion)); - } catch (Throwable var34) { - HytaleLogger.getLogger().at(Level.WARNING).withCause(var34).log("Exception when loading entity state %s", bsonDocument); - } - } else { - selection.addEntityHolderRaw(EntityStore.REGISTRY.deserialize(bsonDocument)); - } + selection.addEntityHolderRaw(EntityStore.REGISTRY.deserialize(bsonDocument)); } } @@ -342,72 +194,4 @@ public class SelectionPrefabSerializer { return out; } - - public static int readWorldVersion(@Nonnull BsonDocument document) { - int worldVersion; - if (document.containsKey("worldVersion")) { - worldVersion = document.getInt32("worldVersion").getValue(); - } else if (document.containsKey("worldver")) { - worldVersion = document.getInt32("worldver").getValue(); - } else { - worldVersion = 5; - } - - if (worldVersion == 18553) { - throw new IllegalArgumentException("WorldChunk version old format! Update!"); - } else if (worldVersion > 23) { - throw new IllegalArgumentException("WorldChunk version is newer than we understand! Version: " + worldVersion + ", Latest Version: 23"); - } else { - return worldVersion; - } - } - - @Nullable - public static Holder legacyEntityDecode(@Nonnull BsonDocument document, int version) { - String entityTypeStr = document.getString("EntityType").getValue(); - Class entityType = EntityModule.get().getClass(entityTypeStr); - if (entityType == null) { - UnknownComponents unknownComponents = new UnknownComponents(); - unknownComponents.addComponent(entityTypeStr, new TempUnknownComponent(document)); - return EntityStore.REGISTRY.newHolder(Archetype.of(EntityStore.REGISTRY.getUnknownComponentType()), new Component[]{unknownComponents}); - } else { - Function constructor = EntityModule.get().getConstructor(entityType); - if (constructor == null) { - return null; - } else { - DirectDecodeCodec codec = EntityModule.get().getCodec(entityType); - Objects.requireNonNull(codec, "Unable to create entity because there is no associated codec"); - Entity entity = constructor.apply(null); - codec.decode(document, entity, new ExtraInfo(version)); - return entity.toHolder(); - } - } - } - - @Nonnull - public static Holder legacyStateDecode(@Nonnull BsonDocument document) { - ExtraInfo extraInto = ExtraInfo.THREAD_LOCAL.get(); - String type = BlockState.TYPE_STRUCTURE.getNow(document, extraInto); - Class blockStateClass = BlockState.CODEC.getClassFor(type); - if (blockStateClass != null) { - try { - BlockState t = BlockState.CODEC.decode(document, extraInto); - Holder holder = ChunkStore.REGISTRY.newHolder(); - ComponentType componentType = BlockStateModule.get().getComponentType(blockStateClass); - if (componentType == null) { - throw new IllegalArgumentException("Unable to find component type for: " + blockStateClass); - } - - holder.addComponent(componentType, t); - return holder; - } catch (ACodecMapCodec.UnknownIdException var7) { - } - } - - Holder holder = ChunkStore.REGISTRY.newHolder(); - UnknownComponents unknownComponents = new UnknownComponents<>(); - unknownComponents.addComponent(type, new TempUnknownComponent<>(document)); - holder.addComponent(ChunkStore.REGISTRY.getUnknownComponentType(), unknownComponents); - return holder; - } } diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/BinaryPrefabBufferCodec.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/BinaryPrefabBufferCodec.java index 9275eae4..29000e0b 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/BinaryPrefabBufferCodec.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/BinaryPrefabBufferCodec.java @@ -5,28 +5,28 @@ import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockMigration; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; -import com.hypixel.hytale.server.core.prefab.config.SelectionPrefabSerializer; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBufferBlockEntry; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.BsonUtil; import com.hypixel.hytale.server.core.util.io.ByteBufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import com.hypixel.hytale.sneakythrow.SneakyThrow; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import java.nio.file.Path; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; import java.util.Map; import java.util.function.Function; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bson.BsonDocument; +import org.joml.Vector3i; -public class BinaryPrefabBufferCodec implements PrefabBufferCodec { +public class BinaryPrefabBufferCodec { public static final BinaryPrefabBufferCodec INSTANCE = new BinaryPrefabBufferCodec(); public static final int VERSION = 21; private static final int MASK_CHANCE = 1; @@ -37,169 +37,139 @@ public class BinaryPrefabBufferCodec implements PrefabBufferCodec { private static final int MASK_ROTATION = 32; @Nonnull - public PrefabBuffer deserialize(Path path, @Nonnull ByteBuf buffer) { - int version = buffer.readUnsignedShort(); - if (version == 18553) { - throw new UpdateBinaryPrefabException("Old prefab format!"); - } else if (21 < version) { - throw new IllegalStateException("Prefab version is newer than supported. Given: " + version); - } else { - int worldVersion = version < 17 ? buffer.readUnsignedShort() : 0; - if (version == 11) { - buffer.readUnsignedShort(); - } + public PrefabBuffer deserialize(@Nonnull ByteBuffer buffer) { + int version = Short.toUnsignedInt(buffer.getShort()); + if (version != 18553 && version >= 21) { + if (21 < version) { + throw new IllegalStateException("Prefab version is newer than supported. Given: " + version); + } else { + BlockTypeAssetMap assetMap = BlockType.getAssetMap(); + int blockIdVersion = buffer.getShort(); + long packedAnchor = buffer.getLong(); + Vector3i anchor = new Vector3i(BlockUtil.unpackX(packedAnchor), BlockUtil.unpackY(packedAnchor), BlockUtil.unpackZ(packedAnchor)); + Function blockMigration = null; + Map blockMigrationMap = BlockMigration.getAssetMap().getAssetMap(); + int v = blockIdVersion; - int entityVersion = version >= 14 && version < 17 ? buffer.readUnsignedShort() : 0; - BlockTypeAssetMap assetMap = BlockType.getAssetMap(); - int blockIdVersion = 8; - if (version >= 13) { - blockIdVersion = buffer.readShort(); - } - - Vector3i anchor = Vector3i.ZERO; - if (version >= 16) { - long packedAnchor = buffer.readLong(); - anchor = new Vector3i(BlockUtil.unpackX(packedAnchor), BlockUtil.unpackY(packedAnchor), BlockUtil.unpackZ(packedAnchor)); - } - - Function blockMigration = null; - Map blockMigrationMap = BlockMigration.getAssetMap().getAssetMap(); - int v = blockIdVersion; - - for (BlockMigration migration = blockMigrationMap.get(blockIdVersion); migration != null; migration = blockMigrationMap.get(++v)) { - if (blockMigration == null) { - blockMigration = migration::getMigration; - } else { - blockMigration = blockMigration.andThen(migration::getMigration); + for (BlockMigration migration = blockMigrationMap.get(blockIdVersion); migration != null; migration = blockMigrationMap.get(++v)) { + if (blockMigration == null) { + blockMigration = migration::getMigration; + } else { + blockMigration = blockMigration.andThen(migration::getMigration); + } } - } - int blockNameCount = buffer.readInt(); - Int2ObjectOpenHashMap blockIdMapping = new Int2ObjectOpenHashMap<>(blockNameCount); + int blockNameCount = buffer.getInt(); + Int2ObjectOpenHashMap blockIdMapping = new Int2ObjectOpenHashMap<>(blockNameCount); - for (int i = 0; i < blockNameCount; i++) { - try { - int readId = buffer.readInt(); - BinaryPrefabBufferCodec.BlockIdEntry block = this.deserializeBlock(buffer, assetMap, blockMigration); - blockIdMapping.put(readId, block); - } catch (Exception var44) { - throw new IllegalStateException("Failed to deserialize block name #" + i, var44); + for (int i = 0; i < blockNameCount; i++) { + try { + int readId = buffer.getInt(); + BinaryPrefabBufferCodec.BlockIdEntry block = this.deserializeBlock(buffer, assetMap, blockMigration); + blockIdMapping.put(readId, block); + } catch (Exception var44) { + throw new IllegalStateException("Failed to deserialize block name #" + i, var44); + } } - } - IndexedLookupTableAssetMap fluidMap = Fluid.getAssetMap(); - int fluidNameCount = version >= 18 ? buffer.readInt() : 0; - Int2ObjectOpenHashMap fluidIdMapping = new Int2ObjectOpenHashMap<>(fluidNameCount); + IndexedLookupTableAssetMap fluidMap = Fluid.getAssetMap(); + int fluidNameCount = buffer.getInt(); + Int2ObjectOpenHashMap fluidIdMapping = new Int2ObjectOpenHashMap<>(fluidNameCount); - for (int i = 0; i < fluidNameCount; i++) { - try { - int readId = buffer.readInt(); - BinaryPrefabBufferCodec.FluidIdEntry fluid = this.deserializeFluid(buffer, fluidMap); - fluidIdMapping.put(readId, fluid); - } catch (Exception var43) { - throw new IllegalStateException("Failed to deserialize block name #" + i, var43); + for (int i = 0; i < fluidNameCount; i++) { + try { + int readId = buffer.getInt(); + BinaryPrefabBufferCodec.FluidIdEntry fluid = this.deserializeFluid(buffer, fluidMap); + fluidIdMapping.put(readId, fluid); + } catch (Exception var43) { + throw new IllegalStateException("Failed to deserialize block name #" + i, var43); + } } - } - PrefabBuffer.Builder builder = PrefabBuffer.newBuilder(); - builder.setAnchor(anchor); - int columnCount = buffer.readInt(); + PrefabBuffer.Builder builder = PrefabBuffer.newBuilder(); + builder.setAnchor(anchor); + int columnCount = buffer.getInt(); - for (int i = 0; i < columnCount; i++) { - int columnIndex = buffer.readInt(); - int blocks = buffer.readInt(); - PrefabBufferBlockEntry[] blockEntries = new PrefabBufferBlockEntry[blocks]; + for (int i = 0; i < columnCount; i++) { + int columnIndex = buffer.getInt(); + int blocks = buffer.getInt(); + PrefabBufferBlockEntry[] blockEntries = new PrefabBufferBlockEntry[blocks]; - for (int j = 0; j < blocks; j++) { - int y = buffer.readShort(); - int readId = buffer.readInt(); - BinaryPrefabBufferCodec.BlockIdEntry block = blockIdMapping.get(readId); - int mask = buffer.readUnsignedByte(); - boolean hasChance = (mask & 1) == 1; - boolean hasState = (mask & 2) == 2; - boolean hasFluid = (mask & 4) == 4; - boolean hasSupportValue = (mask & 8) == 8; - boolean hasFiller = (mask & 16) == 16; - boolean hasRotation = (mask & 32) == 32; - float chance = hasChance ? buffer.readFloat() : 1.0F; - Holder holder = null; - if (hasState) { - BsonDocument doc = BsonUtil.readFromBinaryStream(buffer); - if (version < 15) { - holder = SelectionPrefabSerializer.legacyStateDecode(doc); - } else if (version < 17) { - holder = ChunkStore.REGISTRY.deserialize(doc, worldVersion); - } else { + for (int j = 0; j < blocks; j++) { + int y = buffer.getShort(); + int readId = buffer.getInt(); + BinaryPrefabBufferCodec.BlockIdEntry block = blockIdMapping.get(readId); + int mask = Byte.toUnsignedInt(buffer.get()); + boolean hasChance = (mask & 1) == 1; + boolean hasState = (mask & 2) == 2; + boolean hasFluid = (mask & 4) == 4; + boolean hasSupportValue = (mask & 8) == 8; + boolean hasFiller = (mask & 16) == 16; + boolean hasRotation = (mask & 32) == 32; + float chance = hasChance ? buffer.getFloat() : 1.0F; + Holder holder = null; + if (hasState) { + BsonDocument doc = BsonUtil.readFromBinaryStream(buffer); holder = ChunkStore.REGISTRY.deserialize(doc); } + + byte supportValue = 0; + if (hasSupportValue) { + supportValue = (byte)(buffer.get() & 15); + } + + int filler = 0; + if (hasFiller) { + filler = Short.toUnsignedInt(buffer.getShort()); + } + + int rotation = 0; + if (hasRotation) { + rotation = Byte.toUnsignedInt(buffer.get()); + } + + int fluidId = 0; + byte fluidLevel = 0; + if (hasFluid) { + int id = buffer.getInt(); + fluidId = fluidIdMapping.get(id).id; + fluidLevel = buffer.get(); + } + + blockEntries[j] = new PrefabBufferBlockEntry(y, block.id, block.key, chance, holder, fluidId, fluidLevel, supportValue, rotation, filler); } - byte supportValue = 0; - if (hasSupportValue) { - supportValue = (byte)(buffer.readByte() & 15); - } + int entityCount = Short.toUnsignedInt(buffer.getShort()); + Holder[] entityHolders = null; + if (entityCount > 0) { + entityHolders = new Holder[entityCount]; - int filler = 0; - if (hasFiller) { - filler = buffer.readUnsignedShort(); - } - - int rotation = 0; - if (hasRotation) { - rotation = buffer.readUnsignedByte(); - } - - int fluidId = 0; - byte fluidLevel = 0; - if (hasFluid) { - int id = buffer.readInt(); - fluidId = fluidIdMapping.get(id).id; - fluidLevel = buffer.readByte(); - } - - blockEntries[j] = new PrefabBufferBlockEntry(y, block.id, block.key, chance, holder, fluidId, fluidLevel, supportValue, rotation, filler); - } - - int entityCount = buffer.readUnsignedShort(); - Holder[] entityHolders = null; - if (entityCount > 0) { - entityHolders = new Holder[entityCount]; - - for (int j = 0; j < entityCount; j++) { - try { - if (version >= 12 && version < 14) { - entityVersion = buffer.readUnsignedShort(); + for (int j = 0; j < entityCount; j++) { + try { + BsonDocument entityDocument = BsonUtil.readFromBinaryStream(buffer); + Holder entityHolder = EntityStore.REGISTRY.deserialize(entityDocument); + entityHolders[j] = entityHolder; + } catch (Exception var42) { + throw new IllegalStateException("Failed to deserialize entity wrapper #" + i, var42); } - - BsonDocument entityDocument = BsonUtil.readFromBinaryStream(buffer); - Holder entityHolder; - if (version < 14) { - entityHolder = SelectionPrefabSerializer.legacyEntityDecode(entityDocument, entityVersion); - } else if (version < 17) { - entityHolder = EntityStore.REGISTRY.deserialize(entityDocument, entityVersion); - } else { - entityHolder = EntityStore.REGISTRY.deserialize(entityDocument); - } - - entityHolders[j] = entityHolder; - } catch (Exception var45) { - throw new IllegalStateException("Failed to deserialize entity wrapper #" + i, var45); } } + + int x = MathUtil.unpackLeft(columnIndex); + int z = MathUtil.unpackRight(columnIndex); + builder.addColumn(x, z, blockEntries, entityHolders); } - int x = MathUtil.unpackLeft(columnIndex); - int z = MathUtil.unpackRight(columnIndex); - builder.addColumn(x, z, blockEntries, entityHolders); + return builder.build(); } - - return builder.build(); + } else { + throw new UpdateBinaryPrefabException("Old prefab format!"); } } @Nonnull private BinaryPrefabBufferCodec.BlockIdEntry deserializeBlock( - @Nonnull ByteBuf buffer, @Nonnull BlockTypeAssetMap assetMap, @Nullable Function blockMigration + @Nonnull ByteBuffer buffer, @Nonnull BlockTypeAssetMap assetMap, @Nullable Function blockMigration ) { String blockTypeString = ByteBufUtil.readUTF(buffer); String blockTypeKey = blockTypeString; @@ -212,23 +182,17 @@ public class BinaryPrefabBufferCodec implements PrefabBufferCodec { } @Nonnull - private BinaryPrefabBufferCodec.FluidIdEntry deserializeFluid(@Nonnull ByteBuf buffer, @Nonnull IndexedLookupTableAssetMap assetMap) { + private BinaryPrefabBufferCodec.FluidIdEntry deserializeFluid(@Nonnull ByteBuffer buffer, @Nonnull IndexedLookupTableAssetMap assetMap) { String fluidName = ByteBufUtil.readUTF(buffer); int fluidId = Fluid.getFluidIdOrUnknown(assetMap, fluidName, "Failed to find fluid '%s'", fluidName); return new BinaryPrefabBufferCodec.FluidIdEntry(fluidId, fluidName); } - @Nonnull - public ByteBuf serialize(@Nonnull PrefabBuffer prefabBuffer) { + public void serialize(@Nonnull PrefabBuffer prefabBuffer, DataOutputStream out) throws IOException { PrefabBuffer.PrefabBufferAccessor access = prefabBuffer.newAccess(); Int2ObjectOpenHashMap blockNameMapping = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap fluidNameMapping = new Int2ObjectOpenHashMap<>(); - int[] counts = new int[3]; - access.forEachRaw((x, z, blocks, o) -> { - counts[0]++; - counts[1] += blocks; - return true; - }, (x, y, z, mask, blockId, chance, holder, support, rotation, filler, o) -> { + access.forEachRaw((x, z, blocks, o) -> true, (x, y, z, mask, blockId, chance, holder, support, rotation, filler, o) -> { if (!blockNameMapping.containsKey(blockId)) { BlockTypeAssetMap assetMap = BlockType.getAssetMap(); BlockType blockType = assetMap.getAsset(blockId); @@ -248,103 +212,113 @@ public class BinaryPrefabBufferCodec implements PrefabBufferCodec { fluidNameMapping.put(fluidId, fluidType.getId()); } - }, (x, z, entityHolders, o) -> { - if (entityHolders != null) { - counts[2] += entityHolders.length; - } - }, null); - ByteBuf buffer = Unpooled.buffer(4 + blockNameMapping.size() * 261 + counts[0] * 8 + counts[1] * 13 + counts[2] * 2048); - buffer.writeShort(21); - buffer.writeShort(BlockMigration.getAssetMap().getAssetCount()); - buffer.writeLong(BlockUtil.pack(prefabBuffer.getAnchorX(), prefabBuffer.getAnchorY(), prefabBuffer.getAnchorZ())); - buffer.writeInt(blockNameMapping.size()); - blockNameMapping.int2ObjectEntrySet().fastForEach(entry -> { - buffer.writeInt(entry.getIntKey()); - ByteBufUtil.writeUTF(buffer, entry.getValue()); - }); - buffer.writeInt(fluidNameMapping.size()); - fluidNameMapping.int2ObjectEntrySet().fastForEach(entry -> { - buffer.writeInt(entry.getIntKey()); - ByteBufUtil.writeUTF(buffer, entry.getValue()); - }); - buffer.writeInt(access.getColumnCount()); + }, (x, z, entityHolders, o) -> {}, null); + out.writeShort(21); + out.writeShort(BlockMigration.getAssetMap().getAssetCount()); + out.writeLong(BlockUtil.pack(prefabBuffer.getAnchorX(), prefabBuffer.getAnchorY(), prefabBuffer.getAnchorZ())); + out.writeInt(blockNameMapping.size()); + blockNameMapping.int2ObjectEntrySet().fastForEach(SneakyThrow.sneakyConsumer(entry -> { + out.writeInt(entry.getIntKey()); + ByteBufUtil.writeUTF(out, (String)entry.getValue()); + })); + out.writeInt(fluidNameMapping.size()); + fluidNameMapping.int2ObjectEntrySet().fastForEach(SneakyThrow.sneakyConsumer(entry -> { + out.writeInt(entry.getIntKey()); + ByteBufUtil.writeUTF(out, (String)entry.getValue()); + })); + out.writeInt(access.getColumnCount()); access.forEachRaw((x, z, blocks, o) -> { - buffer.writeInt(MathUtil.packInt(x, z)); - buffer.writeInt(blocks); - return true; + try { + out.writeInt(MathUtil.packInt(x, z)); + out.writeInt(blocks); + return true; + } catch (IOException var6) { + throw SneakyThrow.sneakyThrow(var6); + } }, (x, y, z, entryMask, blockId, chance, holder, supportValue, rotation, filler, o) -> { - buffer.writeShort((short)y); - buffer.writeInt(blockId); - boolean hasChance = chance < 1.0F; - boolean hasComponents = holder != null; - int mask = 0; - if (hasChance) { - mask |= 1; - } - - if (hasComponents) { - mask |= 2; - } - - if ((entryMask & 192) != 0) { - mask |= 4; - } - - if (supportValue != 0) { - mask |= 8; - } - - if (filler != 0) { - mask |= 16; - } - - if (rotation != 0) { - mask |= 32; - } - - buffer.writeByte(mask); - if (hasChance) { - buffer.writeFloat(chance); - } - - if (hasComponents) { - try { - BsonUtil.writeToBinaryStream(buffer, ChunkStore.REGISTRY.serialize(holder)); - } catch (Throwable var16) { - throw new IllegalStateException(String.format("Exception while writing %d, %d, %d state!", x, y, z), var16); + try { + out.writeShort((short)y); + out.writeInt(blockId); + boolean hasChance = chance < 1.0F; + boolean hasComponents = holder != null; + int mask = 0; + if (hasChance) { + mask |= 1; } - } - if (supportValue != 0) { - buffer.writeByte(supportValue); - } + if (hasComponents) { + mask |= 2; + } - if (filler != 0) { - buffer.writeShort(filler); - } + if ((entryMask & 192) != 0) { + mask |= 4; + } - if (rotation != 0) { - buffer.writeByte(rotation); + if (supportValue != 0) { + mask |= 8; + } + + if (filler != 0) { + mask |= 16; + } + + if (rotation != 0) { + mask |= 32; + } + + out.writeByte(mask); + if (hasChance) { + out.writeFloat(chance); + } + + if (hasComponents) { + try { + BsonUtil.writeToBinaryStream(out, ChunkStore.REGISTRY.serialize(holder)); + } catch (Throwable var16) { + throw new IllegalStateException(String.format("Exception while writing %d, %d, %d state!", x, y, z), var16); + } + } + + if (supportValue != 0) { + out.writeByte(supportValue); + } + + if (filler != 0) { + out.writeShort(filler); + } + + if (rotation != 0) { + out.writeByte(rotation); + } + } catch (IOException var17) { + throw SneakyThrow.sneakyThrow(var17); } }, (x, y, z, fluidId, level, o) -> { - buffer.writeInt(fluidId); - buffer.writeByte(level); + try { + out.writeInt(fluidId); + out.writeByte(level); + } catch (IOException var8) { + throw SneakyThrow.sneakyThrow(var8); + } }, (x, z, entityHolders, o) -> { - int entities = entityHolders != null ? entityHolders.length : 0; - buffer.writeShort(entities); + try { + int entities = entityHolders != null ? entityHolders.length : 0; + out.writeShort(entities); - for (int i = 0; i < entities; i++) { - Holder entityHolder = entityHolders[i]; + for (int i = 0; i < entities; i++) { + Holder entityHolder = entityHolders[i]; - try { - BsonDocument document = EntityStore.REGISTRY.serialize(entityHolder); - BsonUtil.writeToBinaryStream(buffer, document); - } catch (Exception var9) { - throw new IllegalStateException(String.format("Failed to write EntityWrapper at %d, %d #%d", x, z, i), var9); + try { + BsonDocument document = EntityStore.REGISTRY.serialize(entityHolder); + BsonUtil.writeToBinaryStream(out, document); + } catch (Exception var9) { + throw new IllegalStateException(String.format("Failed to write EntityWrapper at %d, %d #%d", x, z, i), var9); + } } + } catch (IOException var10) { + throw SneakyThrow.sneakyThrow(var10); } }, null); - return buffer; } private static class BlockIdEntry { diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/BsonPrefabBufferDeserializer.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/BsonPrefabBufferDeserializer.java index 90f3b2bd..afc702f1 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/BsonPrefabBufferDeserializer.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/BsonPrefabBufferDeserializer.java @@ -4,13 +4,10 @@ import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockMigration; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; -import com.hypixel.hytale.server.core.prefab.config.SelectionPrefabSerializer; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBufferBlockEntry; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; @@ -30,8 +27,10 @@ import org.bson.BsonArray; import org.bson.BsonDocument; import org.bson.BsonInt32; import org.bson.BsonValue; +import org.joml.Vector3d; +import org.joml.Vector3i; -public class BsonPrefabBufferDeserializer implements PrefabBufferDeserializer { +public class BsonPrefabBufferDeserializer { public static final BsonPrefabBufferDeserializer INSTANCE = new BsonPrefabBufferDeserializer(); public static final BsonInt32 LEGACY_BLOCK_ID_VERSION = new BsonInt32(8); private static final BsonInt32 DEFAULT_SUPPORT_VALUE = new BsonInt32(0); @@ -42,158 +41,153 @@ public class BsonPrefabBufferDeserializer implements PrefabBufferDeserializer 8) { + if (version < 8) { + throw new IllegalArgumentException("Prefab version is too old: " + version); + } else if (version > 8) { throw new IllegalArgumentException("Prefab version is too new: " + version + " by expected 8"); } else { - int worldVersion = version < 4 ? SelectionPrefabSerializer.readWorldVersion(document) : 0; - BsonValue entityVersionValue = document.get("entityVersion"); - int entityVersion = entityVersionValue != null ? entityVersionValue.asInt32().getValue() : 0; - if (version < 1) { - throw new IllegalArgumentException("Prefab version " + version + " is no longer supported. Please re-save the prefab."); - } else { - Vector3i anchor = new Vector3i(); - anchor.x = document.getInt32("anchorX").getValue(); - anchor.y = document.getInt32("anchorY").getValue(); - anchor.z = document.getInt32("anchorZ").getValue(); - int blockIdVersion = document.getInt32("blockIdVersion", LEGACY_BLOCK_ID_VERSION).getValue(); - Function blockMigration = null; - Map blockMigrationMap = BlockMigration.getAssetMap().getAssetMap(); - int v = blockIdVersion; + Vector3i anchor = new Vector3i(); + anchor.x = document.getInt32("anchorX").getValue(); + anchor.y = document.getInt32("anchorY").getValue(); + anchor.z = document.getInt32("anchorZ").getValue(); + int blockIdVersion = document.getInt32("blockIdVersion", LEGACY_BLOCK_ID_VERSION).getValue(); + Function blockMigration = null; + Map blockMigrationMap = BlockMigration.getAssetMap().getAssetMap(); + int v = blockIdVersion; - for (BlockMigration migration = blockMigrationMap.get(blockIdVersion); migration != null; migration = blockMigrationMap.get(++v)) { - if (blockMigration == null) { - blockMigration = migration::getMigration; - } else { - blockMigration = blockMigration.andThen(migration::getMigration); - } + for (BlockMigration migration = blockMigrationMap.get(blockIdVersion); migration != null; migration = blockMigrationMap.get(++v)) { + if (blockMigration == null) { + blockMigration = migration::getMigration; + } else { + blockMigration = blockMigration.andThen(migration::getMigration); } - - Int2ObjectOpenHashMap> columnMap = new Int2ObjectOpenHashMap<>(); - PrefabBuffer.Builder builder = PrefabBuffer.newBuilder(); - builder.setAnchor(anchor); - BsonValue blocksValue = document.get("blocks"); - if (blocksValue != null) { - BlockTypeAssetMap assetMap = BlockType.getAssetMap(); - - for (BsonValue blockValue : blocksValue.asArray()) { - BsonDocument blockDocument = blockValue.asDocument(); - int realX = blockDocument.getInt32("x").getValue(); - int realY = blockDocument.getInt32("y").getValue(); - int realZ = blockDocument.getInt32("z").getValue(); - int x = realX - anchor.x; - int y = realY - anchor.y; - int z = realZ - anchor.z; - if (-32768 > x || x > 32767) { - throw new IllegalArgumentException("Violation X: Short.MIN_VALUE < " + x + " < Short.MAX_VALUE"); - } - - if (-32768 > y || y > 32767) { - throw new IllegalArgumentException("Violation Y: Short.MIN_VALUE < " + y + " < Short.MAX_VALUE"); - } - - if (-32768 > z || z > 32767) { - throw new IllegalArgumentException("Violation Z: Short.MIN_VALUE < " + z + " < Short.MAX_VALUE"); - } - - PrefabBufferBlockEntry blockEntry = builder.newBlockEntry(y); - - try { - deserializeBlockType(blockEntry, blockDocument, assetMap, blockMigration); - } catch (Throwable var33) { - throw new IllegalStateException("Failed to load block type for " + path + " at " + realX + ", " + realY + ", " + realZ, var33); - } - - deserializeState(blockEntry, blockDocument, version, worldVersion); - blockEntry.supportValue = (byte)blockDocument.getInt32("support", DEFAULT_SUPPORT_VALUE).getValue(); - blockEntry.filler = blockDocument.getInt32("filler", DEFAULT_FILLER_VALUE).getValue(); - blockEntry.rotation = blockDocument.getInt32("rotation", DEFAULT_ROTATION_VALUE).getValue(); - int columnIndex = MathUtil.packInt(x, z); - Int2ObjectMap column = columnMap.get(columnIndex); - if (column == null) { - columnMap.put(columnIndex, column = new Int2ObjectOpenHashMap<>()); - } - - PrefabBufferBlockEntry existing = column.putIfAbsent(y, blockEntry); - if (existing != null) { - throw new IllegalStateException( - "Block is already present in column. Given: " - + realX - + ", " - + realY - + ", " - + realZ - + ", " - + blockEntry.blockTypeKey - + " - Existing: " - + existing.y - + ", " - + existing.blockTypeKey - ); - } - } - } - - BsonValue fluidsValue = document.get("fluids"); - if (fluidsValue != null) { - IndexedLookupTableAssetMap assetMap = Fluid.getAssetMap(); - - for (BsonValue fluidValue : fluidsValue.asArray()) { - BsonDocument fluidDocument = fluidValue.asDocument(); - int realXx = fluidDocument.getInt32("x").getValue(); - int realYx = fluidDocument.getInt32("y").getValue(); - int realZx = fluidDocument.getInt32("z").getValue(); - int xx = realXx - anchor.x; - int yx = realYx - anchor.y; - int zx = realZx - anchor.z; - if (-32768 > xx || xx > 32767) { - throw new IllegalArgumentException("Violation X: Short.MIN_VALUE < " + xx + " < Short.MAX_VALUE"); - } - - if (-32768 > yx || yx > 32767) { - throw new IllegalArgumentException("Violation Y: Short.MIN_VALUE < " + yx + " < Short.MAX_VALUE"); - } - - if (-32768 > zx || zx > 32767) { - throw new IllegalArgumentException("Violation Z: Short.MIN_VALUE < " + zx + " < Short.MAX_VALUE"); - } - - int columnIndexx = MathUtil.packInt(xx, zx); - Int2ObjectMap columnx = columnMap.get(columnIndexx); - if (columnx == null) { - columnMap.put(columnIndexx, columnx = new Int2ObjectOpenHashMap<>()); - } - - PrefabBufferBlockEntry entry = columnx.computeIfAbsent(yx, builder::newBlockEntry); - String fluidName = fluidDocument.getString("name").getValue(); - entry.fluidId = Fluid.getFluidIdOrUnknown(fluidName, "Unknown fluid '%s'", fluidName); - entry.fluidLevel = (byte)fluidDocument.getInt32("level").getValue(); - } - } - - Int2ObjectOpenHashMap>> entityMap = deserializeEntityHolders(document, anchor, version, entityVersion); - columnMap.int2ObjectEntrySet().fastForEach(entryx -> { - int columnIndexx = entryx.getIntKey(); - int xxx = MathUtil.unpackLeft(columnIndexx); - int zxx = MathUtil.unpackRight(columnIndexx); - Int2ObjectMap columnBlockMap = (Int2ObjectMap)entryx.getValue(); - PrefabBufferBlockEntry[] entries = columnBlockMap.values().toArray(PrefabBufferBlockEntry[]::new); - Arrays.sort(entries, Comparator.comparingInt(o -> o.y)); - List> entityColumn = entityMap.remove(columnIndexx); - Holder[] entityArray = entityColumn != null && !entityColumn.isEmpty() ? entityColumn.toArray(Holder[]::new) : null; - builder.addColumn(xxx, zxx, entries, entityArray); - }); - entityMap.int2ObjectEntrySet().fastForEach(entryx -> { - int columnIndexx = entryx.getIntKey(); - int xxx = MathUtil.unpackLeft(columnIndexx); - int zxx = MathUtil.unpackRight(columnIndexx); - List> entityColumn = (List>)entryx.getValue(); - Holder[] entityArray = !entityColumn.isEmpty() ? entityColumn.toArray(Holder[]::new) : null; - if (entityArray != null) { - builder.addColumn(xxx, zxx, PrefabBufferBlockEntry.EMPTY_ARRAY, entityArray); - } - }); - return builder.build(); } + + Int2ObjectOpenHashMap> columnMap = new Int2ObjectOpenHashMap<>(); + PrefabBuffer.Builder builder = PrefabBuffer.newBuilder(); + builder.setAnchor(anchor); + BsonValue blocksValue = document.get("blocks"); + if (blocksValue != null) { + BlockTypeAssetMap assetMap = BlockType.getAssetMap(); + + for (BsonValue blockValue : blocksValue.asArray()) { + BsonDocument blockDocument = blockValue.asDocument(); + int realX = blockDocument.getInt32("x").getValue(); + int realY = blockDocument.getInt32("y").getValue(); + int realZ = blockDocument.getInt32("z").getValue(); + int x = realX - anchor.x; + int y = realY - anchor.y; + int z = realZ - anchor.z; + if (-32768 > x || x > 32767) { + throw new IllegalArgumentException("Violation X: Short.MIN_VALUE < " + x + " < Short.MAX_VALUE"); + } + + if (-32768 > y || y > 32767) { + throw new IllegalArgumentException("Violation Y: Short.MIN_VALUE < " + y + " < Short.MAX_VALUE"); + } + + if (-32768 > z || z > 32767) { + throw new IllegalArgumentException("Violation Z: Short.MIN_VALUE < " + z + " < Short.MAX_VALUE"); + } + + PrefabBufferBlockEntry blockEntry = builder.newBlockEntry(y); + + try { + deserializeBlockType(blockEntry, blockDocument, assetMap, blockMigration); + } catch (Throwable var30) { + throw new IllegalStateException("Failed to load block type for " + path + " at " + realX + ", " + realY + ", " + realZ, var30); + } + + deserializeState(blockEntry, blockDocument); + blockEntry.supportValue = (byte)blockDocument.getInt32("support", DEFAULT_SUPPORT_VALUE).getValue(); + blockEntry.filler = blockDocument.getInt32("filler", DEFAULT_FILLER_VALUE).getValue(); + blockEntry.rotation = blockDocument.getInt32("rotation", DEFAULT_ROTATION_VALUE).getValue(); + int columnIndex = MathUtil.packInt(x, z); + Int2ObjectMap column = columnMap.get(columnIndex); + if (column == null) { + columnMap.put(columnIndex, column = new Int2ObjectOpenHashMap<>()); + } + + PrefabBufferBlockEntry existing = column.putIfAbsent(y, blockEntry); + if (existing != null) { + throw new IllegalStateException( + "Block is already present in column. Given: " + + realX + + ", " + + realY + + ", " + + realZ + + ", " + + blockEntry.blockTypeKey + + " - Existing: " + + existing.y + + ", " + + existing.blockTypeKey + ); + } + } + } + + BsonValue fluidsValue = document.get("fluids"); + if (fluidsValue != null) { + IndexedLookupTableAssetMap assetMap = Fluid.getAssetMap(); + + for (BsonValue fluidValue : fluidsValue.asArray()) { + BsonDocument fluidDocument = fluidValue.asDocument(); + int realXx = fluidDocument.getInt32("x").getValue(); + int realYx = fluidDocument.getInt32("y").getValue(); + int realZx = fluidDocument.getInt32("z").getValue(); + int xx = realXx - anchor.x; + int yx = realYx - anchor.y; + int zx = realZx - anchor.z; + if (-32768 > xx || xx > 32767) { + throw new IllegalArgumentException("Violation X: Short.MIN_VALUE < " + xx + " < Short.MAX_VALUE"); + } + + if (-32768 > yx || yx > 32767) { + throw new IllegalArgumentException("Violation Y: Short.MIN_VALUE < " + yx + " < Short.MAX_VALUE"); + } + + if (-32768 > zx || zx > 32767) { + throw new IllegalArgumentException("Violation Z: Short.MIN_VALUE < " + zx + " < Short.MAX_VALUE"); + } + + int columnIndexx = MathUtil.packInt(xx, zx); + Int2ObjectMap columnx = columnMap.get(columnIndexx); + if (columnx == null) { + columnMap.put(columnIndexx, columnx = new Int2ObjectOpenHashMap<>()); + } + + PrefabBufferBlockEntry entry = columnx.computeIfAbsent(yx, builder::newBlockEntry); + String fluidName = fluidDocument.getString("name").getValue(); + entry.fluidId = Fluid.getFluidIdOrUnknown(fluidName, "Unknown fluid '%s'", fluidName); + entry.fluidLevel = (byte)fluidDocument.getInt32("level").getValue(); + } + } + + Int2ObjectOpenHashMap>> entityMap = deserializeEntityHolders(document, anchor); + columnMap.int2ObjectEntrySet().fastForEach(entryx -> { + int columnIndexx = entryx.getIntKey(); + int xxx = MathUtil.unpackLeft(columnIndexx); + int zxx = MathUtil.unpackRight(columnIndexx); + Int2ObjectMap columnBlockMap = (Int2ObjectMap)entryx.getValue(); + PrefabBufferBlockEntry[] entries = columnBlockMap.values().toArray(PrefabBufferBlockEntry[]::new); + Arrays.sort(entries, Comparator.comparingInt(o -> o.y)); + List> entityColumn = entityMap.remove(columnIndexx); + Holder[] entityArray = entityColumn != null && !entityColumn.isEmpty() ? entityColumn.toArray(Holder[]::new) : null; + builder.addColumn(xxx, zxx, entries, entityArray); + }); + entityMap.int2ObjectEntrySet().fastForEach(entryx -> { + int columnIndexx = entryx.getIntKey(); + int xxx = MathUtil.unpackLeft(columnIndexx); + int zxx = MathUtil.unpackRight(columnIndexx); + List> entityColumn = (List>)entryx.getValue(); + Holder[] entityArray = !entityColumn.isEmpty() ? entityColumn.toArray(Holder[]::new) : null; + if (entityArray != null) { + builder.addColumn(xxx, zxx, PrefabBufferBlockEntry.EMPTY_ARRAY, entityArray); + } + }); + return builder.build(); } } @@ -233,28 +227,15 @@ public class BsonPrefabBufferDeserializer implements PrefabBufferDeserializer>> deserializeEntityHolders( - @Nonnull BsonDocument document, @Nonnull Vector3i anchor, int version, int entityVersion - ) { + private static Int2ObjectOpenHashMap>> deserializeEntityHolders(@Nonnull BsonDocument document, @Nonnull Vector3i anchor) { BsonValue entitiesValue = document.get("entities"); Int2ObjectOpenHashMap>> entityMap = new Int2ObjectOpenHashMap<>(); if (entitiesValue == null) { @@ -267,21 +248,15 @@ public class BsonPrefabBufferDeserializer implements PrefabBufferDeserializer entityHolder; - if (version <= 1) { - entityHolder = SelectionPrefabSerializer.legacyEntityDecode(entityDocument, entityVersion); - } else { - entityHolder = EntityStore.REGISTRY.deserialize(entityDocument); - } - + Holder entityHolder = EntityStore.REGISTRY.deserialize(entityDocument); TransformComponent transformComponent = entityHolder.getComponent(TransformComponent.getComponentType()); assert transformComponent != null; Vector3d position = transformComponent.getPosition(); position.add(-anchor.x, -anchor.y, -anchor.z); - int x = MathUtil.floor(position.getX()) & 65535; - int z = MathUtil.floor(position.getZ()) & 65535; + int x = MathUtil.floor(position.x()) & 65535; + int z = MathUtil.floor(position.z()) & 65535; int columnIndex = MathUtil.packInt(x, z); List> entityColumn = entityMap.get(columnIndex); if (entityColumn == null) { @@ -289,8 +264,8 @@ public class BsonPrefabBufferDeserializer implements PrefabBufferDeserializer extends PrefabBufferSerializer, PrefabBufferDeserializer { -} diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferDeserializer.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferDeserializer.java deleted file mode 100644 index 4ceca072..00000000 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferDeserializer.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.hypixel.hytale.server.core.prefab.selection.buffer; - -import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; -import java.nio.file.Path; - -public interface PrefabBufferDeserializer { - PrefabBuffer deserialize(Path var1, T var2); -} diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferSerializer.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferSerializer.java deleted file mode 100644 index 26d067ad..00000000 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferSerializer.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.hypixel.hytale.server.core.prefab.selection.buffer; - -import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; - -public interface PrefabBufferSerializer { - T serialize(PrefabBuffer var1); -} diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferUtil.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferUtil.java index 3c772308..1bfe9eab 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferUtil.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabBufferUtil.java @@ -10,16 +10,17 @@ import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer; import com.hypixel.hytale.server.core.util.BsonUtil; import com.hypixel.hytale.server.core.util.io.FileUtil; import com.hypixel.hytale.sneakythrow.SneakyThrow; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; import java.io.IOException; import java.lang.ref.WeakReference; -import java.nio.channels.SeekableByteChannel; +import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; @@ -38,6 +39,7 @@ public class PrefabBufferUtil { public static final Pattern FILE_SUFFIX_PATTERN = Pattern.compile("((!\\.prefab\\.json)\\.lpf|\\.prefab\\.json)$"); public static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); private static final Map> CACHE = new ConcurrentHashMap<>(); + private static final Set SAVING_PREFABS = ConcurrentHashMap.newKeySet(); @Nonnull public static IPrefabBuffer getCached(@Nonnull Path path) { @@ -124,9 +126,13 @@ public class PrefabBufferUtil { @Nonnull public static CompletableFuture writeToFileAsync(@Nonnull PrefabBuffer prefab, @Nonnull Path path) { return CompletableFuture.runAsync(SneakyThrow.sneakyRunnable(() -> { - try (SeekableByteChannel channel = Files.newByteChannel(path, FileUtil.DEFAULT_WRITE_OPTIONS)) { - channel.write(BinaryPrefabBufferCodec.INSTANCE.serialize(prefab).nioBuffer()); + Path tmp = path.resolveSibling(path.getFileName() + ".tmp"); + + try (DataOutputStream channel = new DataOutputStream(new BufferedOutputStream(Files.newOutputStream(tmp)))) { + BinaryPrefabBufferCodec.INSTANCE.serialize(prefab, channel); } + + FileUtil.atomicMove(tmp, path); })); } @@ -137,19 +143,8 @@ public class PrefabBufferUtil { @Nonnull public static CompletableFuture readFromFileAsync(@Nonnull Path path) { return CompletableFuture.supplyAsync(SneakyThrow.sneakySupplier(() -> { - PrefabBuffer var4; - try (SeekableByteChannel channel = Files.newByteChannel(path)) { - int size = (int)channel.size(); - ByteBuf buf = Unpooled.buffer(size); - buf.writerIndex(size); - if (channel.read(buf.internalNioBuffer(0, size)) != size) { - throw new IOException("Didn't read full file!"); - } - - var4 = BinaryPrefabBufferCodec.INSTANCE.deserialize(path, buf); - } - - return var4; + byte[] bytes = Files.readAllBytes(path); + return BinaryPrefabBufferCodec.INSTANCE.deserialize(ByteBuffer.wrap(bytes)); })); } @@ -168,7 +163,7 @@ public class PrefabBufferUtil { try { cachedAttr = Files.readAttributes(cachedLpfPath, BasicFileAttributes.class); - } catch (IOException var10) { + } catch (IOException var12) { } FileTime targetModifiedTime; @@ -181,12 +176,12 @@ public class PrefabBufferUtil { if (cachedAttr != null && targetModifiedTime.compareTo(cachedAttr.lastModifiedTime()) <= 0) { try { return readFromFile(cachedLpfPath); - } catch (CompletionException var11) { + } catch (CompletionException var13) { if (!Options.getOptionSet().has(Options.VALIDATE_PREFABS)) { - if (var11.getCause() instanceof UpdateBinaryPrefabException) { - LOGGER.at(Level.FINE).log("Ignoring LPF %s due to: %s", path, var11.getMessage()); + if (var13.getCause() instanceof UpdateBinaryPrefabException) { + LOGGER.at(Level.FINE).log("Ignoring LPF %s due to: %s", path, var13.getMessage()); } else { - LOGGER.at(Level.WARNING).withCause(new SkipSentryException(var11)).log("Failed to load %s", cachedLpfPath); + LOGGER.at(Level.WARNING).withCause(new SkipSentryException(var13)).log("Failed to load %s", cachedLpfPath); } } } @@ -194,7 +189,8 @@ public class PrefabBufferUtil { try { PrefabBuffer buffer = BsonPrefabBufferDeserializer.INSTANCE.deserialize(jsonPath, BsonUtil.readDocument(jsonPath, false).join()); - if (!Options.getOptionSet().has(Options.DISABLE_CPB_BUILD)) { + Path fullPath = path.normalize(); + if (!Options.getOptionSet().has(Options.DISABLE_CPB_BUILD) && SAVING_PREFABS.add(fullPath)) { try { Files.createDirectories(cachedLpfPath.getParent()); writeToFileAsync(buffer, cachedLpfPath).thenRun(() -> { @@ -205,15 +201,19 @@ public class PrefabBufferUtil { }).exceptionally(throwable -> { HytaleLogger.getLogger().at(Level.FINE).withCause(new SkipSentryException(throwable)).log("Failed to save prefab cache %s", cachedLpfPath); return null; - }); - } catch (IOException var8) { - LOGGER.at(Level.FINE).log("Cannot create cache directory for %s: %s", cachedLpfPath, var8.getMessage()); + }).whenComplete((unused, throwable) -> SAVING_PREFABS.remove(fullPath)); + } catch (IOException var9) { + LOGGER.at(Level.FINE).log("Cannot create cache directory for %s: %s", cachedLpfPath, var9.getMessage()); + SAVING_PREFABS.remove(fullPath); + } catch (Throwable var10) { + SAVING_PREFABS.remove(fullPath); + throw var10; } } return buffer; - } catch (Exception var9) { - throw new Error("Error while loading Prefab from " + jsonPath.toAbsolutePath(), var9); + } catch (Exception var11) { + throw new Error("Error while loading Prefab from " + jsonPath.toAbsolutePath(), var11); } } diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabLoader.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabLoader.java index 26319311..1d0886a5 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabLoader.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/PrefabLoader.java @@ -4,8 +4,10 @@ import com.hypixel.hytale.common.util.PathUtil; import com.hypixel.hytale.server.core.util.io.FileUtil; import java.io.File; import java.io.IOException; +import java.nio.file.AccessDeniedException; import java.nio.file.FileVisitResult; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.NotDirectoryException; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; @@ -56,19 +58,31 @@ public class PrefabLoader { if (!Files.isDirectory(directoryPath)) { throw new NotDirectoryException(directoryPath.toString()); } else { - Files.walkFileTree(directoryPath, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor() { - @Nonnull - public FileVisitResult visitFile(@Nonnull Path file, BasicFileAttributes attrs) { - String fileName = file.getFileName().toString(); - Matcher matcher = PrefabBufferUtil.FILE_SUFFIX_PATTERN.matcher(fileName); - if (matcher.find()) { - String fileNameNoExtension = matcher.replaceAll(""); - pathConsumer.accept(file.resolveSibling(fileNameNoExtension)); + Files.walkFileTree( + directoryPath, + FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, + Integer.MAX_VALUE, + new SimpleFileVisitor() { + @Nonnull + public FileVisitResult visitFile(@Nonnull Path file, BasicFileAttributes attrs) { + String fileName = file.getFileName().toString(); + Matcher matcher = PrefabBufferUtil.FILE_SUFFIX_PATTERN.matcher(fileName); + if (matcher.find()) { + String fileNameNoExtension = matcher.replaceAll(""); + pathConsumer.accept(file.resolveSibling(fileNameNoExtension)); + } + + return FileVisitResult.CONTINUE; } - return FileVisitResult.CONTINUE; + @Nonnull + public FileVisitResult visitFileFailed(@Nonnull Path file, @Nonnull IOException exc) throws IOException { + return !(exc instanceof NoSuchFileException) && !(exc instanceof AccessDeniedException) + ? super.visitFileFailed(file, exc) + : FileVisitResult.CONTINUE; + } } - }); + ); } } } diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/IPrefabBuffer.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/IPrefabBuffer.java index 2cc130bf..bc3c9c5e 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/IPrefabBuffer.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/IPrefabBuffer.java @@ -97,8 +97,6 @@ public interface IPrefabBuffer { @Nullable T var5 ); - void release(); - default boolean compare(@Nonnull IPrefabBuffer.BlockComparingPredicate blockComparingPredicate, @Nonnull T t) { return this.forEachRaw( iterateAllColumns(), diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/PrefabBuffer.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/PrefabBuffer.java index 2c3118ef..2383d31d 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/PrefabBuffer.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/PrefabBuffer.java @@ -1,33 +1,31 @@ package com.hypixel.hytale.server.core.prefab.selection.buffer.impl; -import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; -import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerState; +import com.hypixel.hytale.server.core.modules.prefabspawner.PrefabSpawnerBlock; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import com.hypixel.hytale.server.core.prefab.PrefabWeights; import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.io.ByteBufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.util.List; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PrefabBuffer { public static final float DEFAULT_CHANCE = 1.0F; @@ -41,18 +39,14 @@ public class PrefabBuffer { private final Int2ObjectMap columns; @Nonnull private final PrefabBuffer.ChildPrefab[] childPrefabs; - @Nullable - private ByteBuf buf; private PrefabBuffer( - @Nonnull ByteBuf buf, @Nonnull Vector3i anchor, @Nonnull Vector3i min, @Nonnull Vector3i max, @Nonnull Int2ObjectMap columns, @Nonnull PrefabBuffer.ChildPrefab[] childPrefabs ) { - this.buf = buf; this.anchor = anchor; this.min = min; this.max = max; @@ -66,35 +60,22 @@ public class PrefabBuffer { } public int getAnchorX() { - return this.anchor.getX(); + return this.anchor.x(); } public int getAnchorY() { - return this.anchor.getY(); + return this.anchor.y(); } public int getAnchorZ() { - return this.anchor.getZ(); + return this.anchor.z(); } @Nonnull public PrefabBuffer.PrefabBufferAccessor newAccess() { - this.checkReleased(); return new PrefabBuffer.PrefabBufferAccessor(this); } - public void release() { - this.checkReleased(); - this.buf.release(); - this.buf = null; - } - - private void checkReleased() { - if (this.buf == null) { - throw new IllegalStateException("PrefabBuffer has already been released!"); - } - } - public interface BlockMaskConstants { int ID_IS_BYTE = 1; int ID_IS_SHORT = 2; @@ -230,16 +211,16 @@ public class PrefabBuffer { } public static class Builder { - private final ByteBuf buf = Unpooled.buffer(); @Nonnull - private final Vector3i min = new Vector3i(Vector3i.MAX); + private final Vector3i min = new Vector3i(Vector3iUtil.MAX); @Nonnull - private final Vector3i max = new Vector3i(Vector3i.MIN); + private final Vector3i max = new Vector3i(Vector3iUtil.MIN); @Nonnull - private final Int2ObjectMap columns = new Int2ObjectOpenHashMap<>(); + private final Int2ObjectMap builderColumns = new Int2ObjectOpenHashMap<>(); @Nonnull private final List childPrefabs = new ObjectArrayList<>(0); - private Vector3i anchor = Vector3i.ZERO; + private Vector3i anchor = new Vector3i(); + private int requiredMemory; private Builder() { } @@ -257,112 +238,49 @@ public class PrefabBuffer { throw new IllegalArgumentException("z is smaller than -32768. Given: " + z); } else if (z > 32767) { throw new IllegalArgumentException("z is larger than 32767. Given: " + z); - } else { - int columnIndex = MathUtil.packInt((short)x, (short)z); - if (this.columns.containsKey(columnIndex)) { - throw new IllegalStateException("Column is already set! Given: " + x + ", " + z); + } else if (entries.length != 0) { + int columnIndex = MathUtil.packInt(x, z); + if (this.builderColumns.put(columnIndex, new PrefabBuffer.BuilderColumn(x, z, entries, entityHolders)) != null) { + throw new IllegalArgumentException("Duplicate column"); } else { - int blockCount = entries.length; - Int2ObjectOpenHashMap> holderMap = new Int2ObjectOpenHashMap<>(); - if (blockCount != 0 || entityHolders != null && entityHolders.length != 0) { - int readerIndex = this.buf.writerIndex(); - this.buf.writeInt(blockCount); - if (blockCount > 0) { - int offset = entries[0].y; - if (offset < this.min.y) { - this.min.y = offset; - } + int size = 4; + int lastY = Integer.MIN_VALUE; - this.buf.writeInt(offset - 1); - offset = Integer.MIN_VALUE; - - for (int i = 0; i < blockCount; i++) { - PrefabBufferBlockEntry entry = entries[i]; - int y = entry.y; - int blockId = entry.blockId; - float chance = entry.chance; - Holder holder = entry.state; - int fluidId = entry.fluidId; - byte fluidLevel = entry.fluidLevel; - if (y <= offset) { - throw new IllegalArgumentException("Y Values are not sequential. " + offset + " -> " + y); - } - - int offsetx = i == 0 ? 0 : y - offset; - if (offsetx > 65535) { - throw new IllegalArgumentException("Offset is larger than 65535. Given: " + offsetx); - } - - boolean hasChance = chance < 1.0F; - int blockBytes = MathUtil.byteCount(blockId); - int offsetBytes = offsetx == 1 ? 0 : MathUtil.byteCount(offsetx); - int fluidBytes = MathUtil.byteCount(fluidId); - int mask = PrefabBuffer.BlockMaskConstants.getBlockMask( - blockBytes, fluidBytes, hasChance, offsetBytes, holder, entry.supportValue, entry.rotation, entry.filler - ); - this.buf.writeShort(mask); - ByteBufUtil.writeNumber(this.buf, blockBytes, blockId); - ByteBufUtil.writeNumber(this.buf, offsetBytes, offsetx); - if (hasChance) { - this.buf.writeFloat(chance); - } - - if (entry.rotation != 0) { - this.buf.writeByte(entry.rotation); - } - - if (entry.filler != 0) { - this.buf.writeShort(entry.filler); - } - - if (fluidId != 0) { - ByteBufUtil.writeNumber(this.buf, fluidBytes, fluidId); - this.buf.writeByte(fluidLevel); - } - - if (holder != null) { - holderMap.put(y, holder); - this.handleBlockComponents(entry.rotation, x, y, z, holder); - } - - offset = y; - } - - if (offset > this.max.y) { - this.max.y = offset; - } + for (int i = 0; i < entries.length; i++) { + PrefabBufferBlockEntry entry = entries[i]; + int y = entry.y; + int blockId = entry.blockId; + float chance = entry.chance; + Holder holder = entry.state; + int fluidId = entry.fluidId; + if (y <= lastY) { + throw new IllegalArgumentException("Y Values are not sequential. " + lastY + " -> " + y); } - if (x < this.min.x) { - this.min.x = x; + int offset = i == 0 ? 0 : y - lastY; + if (offset > 65535) { + throw new IllegalArgumentException("Offset is larger than 65535. Given: " + offset); } - if (x > this.max.x) { - this.max.x = x; - } - - if (z < this.min.z) { - this.min.z = z; - } - - if (z > this.max.z) { - this.max.z = z; - } - - if (holderMap.isEmpty()) { - holderMap = null; - } - - PrefabBufferColumn column = new PrefabBufferColumn(readerIndex, entityHolders, holderMap); - this.columns.put(columnIndex, column); + boolean hasChance = chance < 1.0F; + int blockBytes = MathUtil.byteCount(blockId); + int offsetBytes = offset == 1 ? 0 : MathUtil.byteCount(offset); + int fluidBytes = MathUtil.byteCount(fluidId); + int mask = PrefabBuffer.BlockMaskConstants.getBlockMask( + blockBytes, fluidBytes, hasChance, offsetBytes, holder, entry.supportValue, entry.rotation, entry.filler + ); + size += 2 + PrefabBuffer.BlockMaskConstants.getSkipBytes(mask); + lastY = y; } + + this.requiredMemory += size; } } } private void handleBlockComponents(int blockRotation, int x, int y, int z, @Nonnull Holder holder) { - ComponentType componentType = BlockStateModule.get().getComponentType(PrefabSpawnerState.class); - PrefabSpawnerState spawnerState = holder.getComponent(componentType); + ComponentType componentType = PrefabSpawnerBlock.getComponentType(); + PrefabSpawnerBlock spawnerState = holder.getComponent(componentType); if (spawnerState != null) { String path = spawnerState.getPrefabPath(); if (path == null) { @@ -396,20 +314,131 @@ public class PrefabBuffer { return new PrefabBufferBlockEntry(y); } + private int buildColumn(Int2ObjectMap columns, PrefabBuffer.BuilderColumn column, MemorySegment data, int offset) { + PrefabBufferBlockEntry[] entries = column.entries; + int blockCount = entries.length; + Int2ObjectOpenHashMap> holderMap = new Int2ObjectOpenHashMap<>(); + if (blockCount != 0 || column.entityHolders != null && column.entityHolders.length != 0) { + int writeOffset = offset; + if (blockCount > 0) { + int initialY = entries[0].y; + if (initialY < this.min.y) { + this.min.y = initialY; + } + + data.set(ValueLayout.JAVA_INT_UNALIGNED, (long)offset, initialY - 1); + writeOffset = offset + 4; + initialY = Integer.MIN_VALUE; + + for (int i = 0; i < blockCount; i++) { + PrefabBufferBlockEntry entry = entries[i]; + int y = entry.y; + int blockId = entry.blockId; + float chance = entry.chance; + Holder holder = entry.state; + int fluidId = entry.fluidId; + byte fluidLevel = entry.fluidLevel; + int yOffset = i == 0 ? 0 : y - initialY; + boolean hasChance = chance < 1.0F; + int blockBytes = MathUtil.byteCount(blockId); + int offsetBytes = yOffset == 1 ? 0 : MathUtil.byteCount(yOffset); + int fluidBytes = MathUtil.byteCount(fluidId); + int mask = PrefabBuffer.BlockMaskConstants.getBlockMask( + blockBytes, fluidBytes, hasChance, offsetBytes, holder, entry.supportValue, entry.rotation, entry.filler + ); + data.set(ValueLayout.JAVA_SHORT_UNALIGNED, (long)writeOffset, (short)mask); + writeOffset += 2; + ByteBufUtil.writeNumber(data, writeOffset, blockBytes, blockId); + writeOffset += blockBytes; + ByteBufUtil.writeNumber(data, writeOffset, offsetBytes, yOffset); + writeOffset += offsetBytes; + if (hasChance) { + data.set(ValueLayout.JAVA_FLOAT_UNALIGNED, (long)writeOffset, chance); + writeOffset += 4; + } + + if (entry.rotation != 0) { + data.set(ValueLayout.JAVA_BYTE, (long)writeOffset, (byte)entry.rotation); + writeOffset++; + } + + if (entry.filler != 0) { + data.set(ValueLayout.JAVA_SHORT_UNALIGNED, (long)writeOffset, (short)entry.filler); + writeOffset += 2; + } + + if (fluidId != 0) { + ByteBufUtil.writeNumber(data, writeOffset, fluidBytes, fluidId); + writeOffset += fluidBytes; + data.set(ValueLayout.JAVA_BYTE, (long)writeOffset, fluidLevel); + writeOffset++; + } + + if (holder != null) { + holderMap.put(y, holder); + this.handleBlockComponents(entry.rotation, column.x, y, column.z, holder); + } + + initialY = y; + } + + if (initialY > this.max.y) { + this.max.y = initialY; + } + } + + if (column.x < this.min.x) { + this.min.x = column.x; + } + + if (column.x > this.max.x) { + this.max.x = column.x; + } + + if (column.z < this.min.z) { + this.min.z = column.z; + } + + if (column.z > this.max.z) { + this.max.z = column.z; + } + + if (holderMap.isEmpty()) { + holderMap = null; + } + + int size = writeOffset - offset; + PrefabBufferColumn newColumn = new PrefabBufferColumn(blockCount, data.asSlice(offset, size), column.entityHolders, holderMap); + columns.put(MathUtil.packInt(column.x, column.z), newColumn); + return size; + } else { + return 0; + } + } + @Nonnull public PrefabBuffer build() { - ByteBuf buffer = Unpooled.copiedBuffer(this.buf); - this.buf.release(); - PrefabBuffer.ChildPrefab[] childPrefabArray = this.childPrefabs.toArray(PrefabBuffer.ChildPrefab[]::new); - if (this.columns.isEmpty()) { - this.min.assign(0); - this.max.assign(0); + Int2ObjectOpenHashMap columns = new Int2ObjectOpenHashMap<>(); + MemorySegment memorySegment = MemorySegment.ofArray(new byte[this.requiredMemory]); + int offset = 0; + + for (PrefabBuffer.BuilderColumn e : this.builderColumns.values()) { + offset += this.buildColumn(columns, e, memorySegment, offset); } - return new PrefabBuffer(buffer, this.anchor, this.min, this.max, this.columns, childPrefabArray); + PrefabBuffer.ChildPrefab[] childPrefabArray = this.childPrefabs.toArray(PrefabBuffer.ChildPrefab[]::new); + if (this.builderColumns.isEmpty()) { + this.min.zero(); + this.max.zero(); + } + + return new PrefabBuffer(this.anchor, this.min, this.max, columns, childPrefabArray); } } + private record BuilderColumn(int x, int z, @Nonnull PrefabBufferBlockEntry[] entries, @Nullable Holder[] entityHolders) { + } + public static class ChildPrefab { private final int x; private final int y; @@ -489,11 +518,8 @@ public class PrefabBuffer { public static class PrefabBufferAccessor implements IPrefabBuffer { @Nonnull private final PrefabBuffer prefabBuffer; - @Nullable - private ByteBuf buffer; private PrefabBufferAccessor(@Nonnull PrefabBuffer prefabBuffer) { - this.buffer = prefabBuffer.buf.retainedDuplicate(); this.prefabBuffer = prefabBuffer; } @@ -514,49 +540,39 @@ public class PrefabBuffer { @Override public int getMinX(@Nonnull PrefabRotation rotation) { - this.prefabBuffer.checkReleased(); return Math.min( - rotation.getX(this.prefabBuffer.min.getX(), this.prefabBuffer.min.getZ()), - rotation.getX(this.prefabBuffer.max.getX(), this.prefabBuffer.max.getZ()) + rotation.getX(this.prefabBuffer.min.x(), this.prefabBuffer.min.z()), rotation.getX(this.prefabBuffer.max.x(), this.prefabBuffer.max.z()) ); } @Override public int getMinY() { - this.prefabBuffer.checkReleased(); - return this.prefabBuffer.min.getY(); + return this.prefabBuffer.min.y(); } @Override public int getMinZ(@Nonnull PrefabRotation rotation) { - this.prefabBuffer.checkReleased(); return Math.min( - rotation.getZ(this.prefabBuffer.min.getX(), this.prefabBuffer.min.getZ()), - rotation.getZ(this.prefabBuffer.max.getX(), this.prefabBuffer.max.getZ()) + rotation.getZ(this.prefabBuffer.min.x(), this.prefabBuffer.min.z()), rotation.getZ(this.prefabBuffer.max.x(), this.prefabBuffer.max.z()) ); } @Override public int getMaxX(@Nonnull PrefabRotation rotation) { - this.prefabBuffer.checkReleased(); return Math.max( - rotation.getX(this.prefabBuffer.min.getX(), this.prefabBuffer.min.getZ()), - rotation.getX(this.prefabBuffer.max.getX(), this.prefabBuffer.max.getZ()) + rotation.getX(this.prefabBuffer.min.x(), this.prefabBuffer.min.z()), rotation.getX(this.prefabBuffer.max.x(), this.prefabBuffer.max.z()) ); } @Override public int getMaxY() { - this.prefabBuffer.checkReleased(); - return this.prefabBuffer.max.getY(); + return this.prefabBuffer.max.y(); } @Override public int getMaxZ(@Nonnull PrefabRotation rotation) { - this.prefabBuffer.checkReleased(); return Math.max( - rotation.getZ(this.prefabBuffer.min.getX(), this.prefabBuffer.min.getZ()), - rotation.getZ(this.prefabBuffer.max.getX(), this.prefabBuffer.max.getZ()) + rotation.getZ(this.prefabBuffer.min.x(), this.prefabBuffer.min.z()), rotation.getZ(this.prefabBuffer.max.x(), this.prefabBuffer.max.z()) ); } @@ -573,65 +589,57 @@ public class PrefabBuffer { @Override public int getMinYAt(@Nonnull PrefabRotation rotation, int x, int z) { - this.prefabBuffer.checkReleased(); int rotatedX = rotation.getX(x, z); int rotatedZ = rotation.getZ(x, z); int columnIndex = MathUtil.packInt(rotatedX, rotatedZ); PrefabBufferColumn columnData = this.prefabBuffer.columns.get(columnIndex); - if (columnData != null) { - this.buffer.readerIndex(columnData.getReaderIndex()); - int blockCount = this.buffer.readInt(); - if (blockCount > 0) { - return this.buffer.readInt() + 1; - } - } - - return -1; + return columnData != null && columnData.getBlockCount() > 0 ? columnData.getMemorySegment().get(ValueLayout.JAVA_INT_UNALIGNED, 0L) + 1 : -1; } @Override public int getMaxYAt(@Nonnull PrefabRotation rotation, int x, int z) { - this.prefabBuffer.checkReleased(); int rotatedX = rotation.getX(x, z); int rotatedZ = rotation.getZ(x, z); int columnIndex = MathUtil.packInt(rotatedX, rotatedZ); PrefabBufferColumn column = this.prefabBuffer.columns.get(columnIndex); if (column == null) { return -1; - } else { - this.buffer.readerIndex(column.getReaderIndex()); - int blockCount = this.buffer.readInt(); - if (blockCount > 0) { - int y = this.buffer.readInt(); + } else if (column.getBlockCount() > 0) { + int offset = 0; + MemorySegment data = column.getMemorySegment(); + int y = data.get(ValueLayout.JAVA_INT_UNALIGNED, (long)offset); + offset += 4; - for (int i = 0; i < blockCount; i++) { - int mask = this.buffer.readUnsignedShort(); - if (PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask) > 0) { - this.buffer.skipBytes(PrefabBuffer.BlockMaskConstants.getBlockBytes(mask)); - y += ByteBufUtil.readNumber(this.buffer, PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask)); - if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { - this.buffer.skipBytes(4); - } - - if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { - this.buffer.skipBytes(1); - } - - if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { - this.buffer.skipBytes(2); - } - - this.buffer.skipBytes(PrefabBuffer.BlockMaskConstants.getFluidBytes(mask)); - } else { - this.buffer.skipBytes(PrefabBuffer.BlockMaskConstants.getSkipBytes(mask)); - y++; + for (int i = 0; i < column.getBlockCount(); i++) { + int mask = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; + if (PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask) > 0) { + offset += PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); + int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); + y += ByteBufUtil.readNumber(data, offset, offsetBytes); + offset += offsetBytes; + if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { + offset += 4; } - } - return y; - } else { - return -1; + if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { + offset++; + } + + if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { + offset += 2; + } + + offset += PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); + } else { + offset += PrefabBuffer.BlockMaskConstants.getSkipBytes(mask); + y++; + } } + + return y; + } else { + return -1; } } @@ -643,7 +651,6 @@ public class PrefabBuffer { @Nullable IPrefabBuffer.ChildConsumer childConsumer, @Nonnull T t ) { - this.prefabBuffer.checkReleased(); this.prefabBuffer.columns.int2ObjectEntrySet().forEach(entry -> { int columnIndex = entry.getIntKey(); int cx = MathUtil.unpackLeft(columnIndex); @@ -651,24 +658,29 @@ public class PrefabBuffer { int xx = t.rotation.getX(cx, cz); int zx = t.rotation.getZ(cx, cz); PrefabBufferColumn column = entry.getValue(); - this.buffer.readerIndex(column.getReaderIndex()); - int blockCount = this.buffer.readInt(); + MemorySegment data = column.getMemorySegment(); + int blockCount = column.getBlockCount(); if (columnPredicate.test(xx, zx, blockCount, t)) { - BlockTypeAssetMap assetMap = BlockType.getAssetMap(); if (blockCount > 0) { - int y = this.buffer.readInt(); + int offset = 0; + int y = data.get(ValueLayout.JAVA_INT_UNALIGNED, (long)offset); + offset += 4; for (int i = 0; i < blockCount; i++) { - int mask = this.buffer.readUnsignedShort(); + int mask = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; int blockBytes = PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); - int blockId = ByteBufUtil.readNumber(this.buffer, blockBytes); + int blockId = ByteBufUtil.readNumber(data, offset, blockBytes); + offset += blockBytes; int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); - y += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(this.buffer, offsetBytes); + y += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(data, offset, offsetBytes); + offset += offsetBytes; if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { - float chance = this.buffer.readFloat(); + float chance = data.get(ValueLayout.JAVA_FLOAT_UNALIGNED, (long)offset); + offset += 4; if (chance < t.random.nextFloat()) { - this.buffer.skipBytes(2); - this.buffer.skipBytes(PrefabBuffer.BlockMaskConstants.getFluidBytes(mask)); + offset += 2; + offset += PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); continue; } } @@ -677,21 +689,25 @@ public class PrefabBuffer { int supportValue = PrefabBuffer.BlockMaskConstants.getSupportValue(mask); int rotation = 0; if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { - rotation = this.buffer.readUnsignedByte(); + rotation = Byte.toUnsignedInt(data.get(ValueLayout.JAVA_BYTE, (long)offset)); + offset++; } rotation = t.rotation.getRotation(rotation); int filler = 0; if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { - filler = t.rotation.getFiller(this.buffer.readUnsignedShort()); + filler = t.rotation.getFiller(Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset))); + offset += 2; } int fluidBytes = PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); int fluidId = 0; int fluidLevel = 0; if (fluidBytes != 0) { - fluidId = ByteBufUtil.readNumber(this.buffer, fluidBytes - 1); - fluidLevel = this.buffer.readByte(); + fluidId = ByteBufUtil.readNumber(data, offset, fluidBytes - 1); + offset += fluidBytes - 1; + fluidLevel = data.get(ValueLayout.JAVA_BYTE, (long)offset); + offset++; } blockConsumer.accept(xx, y, zx, blockId, holder, supportValue, rotation, filler, t, fluidId, fluidLevel); @@ -732,47 +748,56 @@ public class PrefabBuffer { @Nullable IPrefabBuffer.EntityConsumer entityConsumer, @Nullable T t ) { - this.prefabBuffer.checkReleased(); this.prefabBuffer.columns.int2ObjectEntrySet().forEach(entry -> { int columnIndex = entry.getIntKey(); int x = MathUtil.unpackLeft(columnIndex); int z = MathUtil.unpackRight(columnIndex); PrefabBufferColumn column = entry.getValue(); - this.buffer.readerIndex(column.getReaderIndex()); - int blockCount = this.buffer.readInt(); + MemorySegment data = column.getMemorySegment(); + int blockCount = column.getBlockCount(); if (columnPredicate.test(x, z, blockCount, t)) { if (blockCount > 0) { - int y = this.buffer.readInt(); + int offset = 0; + int y = data.get(ValueLayout.JAVA_INT_UNALIGNED, (long)offset); + offset += 4; for (int i = 0; i < blockCount; i++) { - int mask = this.buffer.readUnsignedShort(); + int mask = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; int blockBytes = PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); - int blockId = ByteBufUtil.readNumber(this.buffer, blockBytes); + int blockId = ByteBufUtil.readNumber(data, offset, blockBytes); + offset += blockBytes; int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); - y += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(this.buffer, offsetBytes); - float chance = PrefabBuffer.BlockMaskConstants.hasChance(mask) ? this.buffer.readFloat() : 1.0F; + y += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(data, offset, offsetBytes); + offset += offsetBytes; + float chance = 1.0F; + if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { + chance = data.get(ValueLayout.JAVA_FLOAT_UNALIGNED, (long)offset); + offset += 4; + } + Holder holder = PrefabBuffer.BlockMaskConstants.hasComponents(mask) ? column.getBlockComponents().get(y) : null; int supportValue = PrefabBuffer.BlockMaskConstants.getSupportValue(mask); int rotation = 0; if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { - rotation = this.buffer.readUnsignedByte(); + rotation = Byte.toUnsignedInt(data.get(ValueLayout.JAVA_BYTE, (long)offset)); + offset++; } int filler = 0; if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { - filler = this.buffer.readUnsignedShort(); + filler = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; } - int position = this.buffer.readerIndex(); blockConsumer.accept(x, y, z, mask, blockId, chance, holder, supportValue, rotation, filler, t); - this.buffer.readerIndex(position); int fluidBytes = PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); if (fluidBytes != 0) { - int fluidId = ByteBufUtil.readNumber(this.buffer, fluidBytes - 1); - byte fluidLevel = this.buffer.readByte(); - position = this.buffer.readerIndex(); + int fluidId = ByteBufUtil.readNumber(data, offset, fluidBytes - 1); + offset += fluidBytes - 1; + byte fluidLevel = data.get(ValueLayout.JAVA_BYTE, (long)offset); + offset++; fluidConsumer.accept(x, y, z, fluidId, fluidLevel, t); - this.buffer.readerIndex(position); } } } @@ -793,49 +818,64 @@ public class PrefabBuffer { @Nullable IPrefabBuffer.EntityPredicate entityPredicate, @Nullable T t ) { - this.prefabBuffer.checkReleased(); - for (Entry entry : this.prefabBuffer.columns.int2ObjectEntrySet()) { int columnIndex = entry.getIntKey(); int x = MathUtil.unpackLeft(columnIndex); int z = MathUtil.unpackRight(columnIndex); PrefabBufferColumn column = entry.getValue(); - this.buffer.readerIndex(column.getReaderIndex()); - int blockCount = this.buffer.readInt(); + MemorySegment data = column.getMemorySegment(); + int blockCount = column.getBlockCount(); if (!columnPredicate.test(x, z, blockCount, t)) { return false; } if (blockCount > 0) { - int y = this.buffer.readInt(); + int offset = 0; + int y = data.get(ValueLayout.JAVA_INT_UNALIGNED, (long)offset); + offset += 4; for (int i = 0; i < blockCount; i++) { - int mask = this.buffer.readUnsignedShort(); + int mask = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; int blockBytes = PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); - int blockId = ByteBufUtil.readNumber(this.buffer, blockBytes); + int blockId = ByteBufUtil.readNumber(data, offset, blockBytes); + offset += blockBytes; int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); - y += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(this.buffer, offsetBytes); - float chance = PrefabBuffer.BlockMaskConstants.hasChance(mask) ? this.buffer.readFloat() : 1.0F; + y += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(data, offset, offsetBytes); + offset += offsetBytes; + float chance = 1.0F; + if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { + chance = data.get(ValueLayout.JAVA_FLOAT_UNALIGNED, (long)offset); + offset += 4; + } + Holder holder = PrefabBuffer.BlockMaskConstants.hasComponents(mask) ? column.getBlockComponents().get(y) : null; - short rotation = PrefabBuffer.BlockMaskConstants.hasRotation(mask) ? this.buffer.readUnsignedByte() : 0; - int filler = PrefabBuffer.BlockMaskConstants.hasFiller(mask) ? this.buffer.readUnsignedShort() : 0; + int rotation = 0; + if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { + rotation = Byte.toUnsignedInt(data.get(ValueLayout.JAVA_BYTE, (long)offset)); + offset++; + } + + int filler = 0; + if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { + filler = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; + } + int supportValue = PrefabBuffer.BlockMaskConstants.getSupportValue(mask); - int position = this.buffer.readerIndex(); if (!blockPredicate.test(x, y, z, blockId, chance, holder, supportValue, rotation, filler, t)) { return false; } - this.buffer.readerIndex(position); int fluidBytes = PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); if (fluidBytes != 0) { - int fluidId = ByteBufUtil.readNumber(this.buffer, fluidBytes - 1); - byte fluidLevel = this.buffer.readByte(); - position = this.buffer.readerIndex(); + int fluidId = ByteBufUtil.readNumber(data, offset, fluidBytes - 1); + offset += fluidBytes - 1; + byte fluidLevel = data.get(ValueLayout.JAVA_BYTE, (long)offset); + offset++; if (!fluidPredicate.test(x, y, z, fluidId, fluidLevel, t)) { return false; } - - this.buffer.readerIndex(position); } } } @@ -849,12 +889,6 @@ public class PrefabBuffer { return true; } - @Override - public void release() { - this.buffer.release(); - this.buffer = null; - } - @Override public boolean compare( @Nonnull IPrefabBuffer.BlockComparingPrefabPredicate blockComparingIterator, @Nonnull T t, @Nonnull IPrefabBuffer otherPrefab @@ -866,8 +900,6 @@ public class PrefabBuffer { IntOpenHashSet columnIndexes = new IntOpenHashSet(this.prefabBuffer.columns.size() + secondPrefabColumns.size()); columnIndexes.addAll(this.prefabBuffer.columns.keySet()); columnIndexes.addAll(secondPrefabColumns.keySet()); - this.prefabBuffer.checkReleased(); - BlockTypeAssetMap assetMap = BlockType.getAssetMap(); IntIterator columnIterator = columnIndexes.iterator(); while (columnIterator.hasNext()) { @@ -878,19 +910,29 @@ public class PrefabBuffer { int z = t.rotation.getZ(cx, cz); PrefabBufferColumn firstColumn = this.prefabBuffer.columns.get(columnIndex); PrefabBufferColumn secondColumn = (PrefabBufferColumn)secondPrefabColumns.get(columnIndex); - if (firstColumn != null) { - this.buffer.readerIndex(firstColumn.getReaderIndex()); - } - - if (secondColumn != null) { - secondPrefab.buffer.readerIndex(secondColumn.getReaderIndex()); - } - - int firstColumnBlockCount = firstColumn != null ? this.buffer.readInt() : 0; - int secondColumnBlockCount = secondColumn != null ? secondPrefab.buffer.readInt() : 0; + MemorySegment firstData = firstColumn != null ? firstColumn.getMemorySegment() : null; + MemorySegment secondData = secondColumn != null ? secondColumn.getMemorySegment() : null; + int firstColumnBlockCount = firstColumn != null ? firstColumn.getBlockCount() : 0; + int secondColumnBlockCount = secondColumn != null ? secondColumn.getBlockCount() : 0; if (firstColumnBlockCount != 0 || secondColumnBlockCount != 0) { - int firstColumnY = firstColumnBlockCount > 0 ? this.buffer.readInt() : Integer.MAX_VALUE; - int secondColumnY = secondColumnBlockCount > 0 ? secondPrefab.buffer.readInt() : Integer.MAX_VALUE; + int firstOffset = 0; + int firstColumnY; + if (firstColumnBlockCount > 0) { + firstColumnY = firstData.get(ValueLayout.JAVA_INT_UNALIGNED, (long)firstOffset); + firstOffset += 4; + } else { + firstColumnY = Integer.MAX_VALUE; + } + + int secondOffset = 0; + int secondColumnY; + if (secondColumnBlockCount > 0) { + secondColumnY = secondData.get(ValueLayout.JAVA_INT_UNALIGNED, (long)secondOffset); + secondOffset += 4; + } else { + secondColumnY = Integer.MAX_VALUE; + } + int firstColumnBlockId = Integer.MIN_VALUE; float firstColumnChance = 1.0F; int firstColumnRotation = 0; @@ -907,37 +949,76 @@ public class PrefabBuffer { while (firstColumnBlocksRead < firstColumnBlockCount || secondColumnBlocksRead < secondColumnBlockCount) { int oldFirstColumnY = firstColumnY; int oldSecondColumnY = secondColumnY; - int oldFirstColumnReaderIndex = firstColumnBlocksRead < firstColumnBlockCount ? this.buffer.readerIndex() : -1; - int oldSecondColumnReaderIndex = secondColumnBlocksRead < secondColumnBlockCount ? secondPrefab.buffer.readerIndex() : -1; + int oldFirstOffset = firstOffset; + int oldSecondOffset = secondOffset; if (firstColumnBlocksRead < firstColumnBlockCount) { - int mask = this.buffer.readUnsignedShort(); + int mask = Short.toUnsignedInt(firstData.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)firstOffset)); + firstOffset += 2; int blockBytes = PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); - firstColumnBlockId = ByteBufUtil.readNumber(this.buffer, blockBytes); + firstColumnBlockId = ByteBufUtil.readNumber(firstData, firstOffset, blockBytes); + firstOffset += blockBytes; int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); - firstColumnY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(this.buffer, offsetBytes); - firstColumnChance = PrefabBuffer.BlockMaskConstants.hasChance(mask) ? this.buffer.readFloat() : 1.0F; - firstColumnRotation = t.rotation.getRotation(PrefabBuffer.BlockMaskConstants.hasRotation(mask) ? this.buffer.readUnsignedByte() : 0); - firstColumnFiller = PrefabBuffer.BlockMaskConstants.hasFiller(mask) ? t.rotation.getFiller(this.buffer.readUnsignedShort()) : 0; + firstColumnY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(firstData, firstOffset, offsetBytes); + firstOffset += offsetBytes; + if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { + firstColumnChance = firstData.get(ValueLayout.JAVA_FLOAT_UNALIGNED, (long)firstOffset); + firstOffset += 4; + } else { + firstColumnChance = 1.0F; + } + + if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { + firstColumnRotation = t.rotation.getRotation(Byte.toUnsignedInt(firstData.get(ValueLayout.JAVA_BYTE, (long)firstOffset))); + firstOffset++; + } else { + firstColumnRotation = 0; + } + + if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { + firstColumnFiller = t.rotation.getFiller(Short.toUnsignedInt(firstData.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)firstOffset))); + firstOffset += 2; + } else { + firstColumnFiller = 0; + } + firstColumnComponents = PrefabBuffer.BlockMaskConstants.hasComponents(mask) ? firstColumn.getBlockComponents().get(firstColumnY) : null; - this.buffer.skipBytes(PrefabBuffer.BlockMaskConstants.getFluidBytes(mask)); + firstOffset += PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); } if (secondColumnBlocksRead < secondColumnBlockCount) { - int mask = secondPrefab.buffer.readUnsignedShort(); - int blockBytes = PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); - secondColumnBlockId = ByteBufUtil.readNumber(secondPrefab.buffer, blockBytes); - int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); - secondColumnY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(secondPrefab.buffer, offsetBytes); - secondColumnChance = PrefabBuffer.BlockMaskConstants.hasChance(mask) ? secondPrefab.buffer.readFloat() : 1.0F; - secondColumnRotation = t.rotation - .getRotation(PrefabBuffer.BlockMaskConstants.hasRotation(mask) ? secondPrefab.buffer.readUnsignedByte() : 0); - secondColumnFiller = PrefabBuffer.BlockMaskConstants.hasFiller(mask) - ? t.rotation.getFiller(secondPrefab.buffer.readUnsignedShort()) - : 0; - secondColumnComponents = PrefabBuffer.BlockMaskConstants.hasComponents(mask) + int maskx = Short.toUnsignedInt(secondData.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)secondOffset)); + secondOffset += 2; + int blockBytesx = PrefabBuffer.BlockMaskConstants.getBlockBytes(maskx); + secondColumnBlockId = ByteBufUtil.readNumber(secondData, secondOffset, blockBytesx); + secondOffset += blockBytesx; + int offsetBytesx = PrefabBuffer.BlockMaskConstants.getOffsetBytes(maskx); + secondColumnY += offsetBytesx == 0 ? 1 : ByteBufUtil.readNumber(secondData, secondOffset, offsetBytesx); + secondOffset += offsetBytesx; + if (PrefabBuffer.BlockMaskConstants.hasChance(maskx)) { + secondColumnChance = secondData.get(ValueLayout.JAVA_FLOAT_UNALIGNED, (long)secondOffset); + secondOffset += 4; + } else { + secondColumnChance = 1.0F; + } + + if (PrefabBuffer.BlockMaskConstants.hasRotation(maskx)) { + secondColumnRotation = t.rotation.getRotation(Byte.toUnsignedInt(secondData.get(ValueLayout.JAVA_BYTE, (long)secondOffset))); + secondOffset++; + } else { + secondColumnRotation = 0; + } + + if (PrefabBuffer.BlockMaskConstants.hasFiller(maskx)) { + secondColumnFiller = t.rotation.getFiller(Short.toUnsignedInt(secondData.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)secondOffset))); + secondOffset += 2; + } else { + secondColumnFiller = 0; + } + + secondColumnComponents = PrefabBuffer.BlockMaskConstants.hasComponents(maskx) ? secondColumn.getBlockComponents().get(secondColumnY) : null; - secondPrefab.buffer.skipBytes(PrefabBuffer.BlockMaskConstants.getFluidBytes(mask)); + secondOffset += PrefabBuffer.BlockMaskConstants.getFluidBytes(maskx); } if (firstColumnY == secondColumnY) { @@ -966,10 +1047,7 @@ public class PrefabBuffer { && secondColumnBlocksRead < secondColumnBlockCount) { secondColumnBlocksRead++; firstColumnY = oldFirstColumnY; - if (oldFirstColumnReaderIndex != -1) { - this.buffer.readerIndex(oldFirstColumnReaderIndex); - } - + firstOffset = oldFirstOffset; boolean test = blockComparingIterator.test( x, secondColumnY, @@ -992,10 +1070,7 @@ public class PrefabBuffer { } else { firstColumnBlocksRead++; secondColumnY = oldSecondColumnY; - if (oldSecondColumnReaderIndex != -1) { - secondPrefab.buffer.readerIndex(oldSecondColumnReaderIndex); - } - + secondOffset = oldSecondOffset; boolean test = blockComparingIterator.test( x, firstColumnY, @@ -1026,42 +1101,45 @@ public class PrefabBuffer { @Override public int getBlockId(int x, int y, int z) { - this.prefabBuffer.checkReleased(); PrefabBufferColumn column = this.prefabBuffer.columns.get(MathUtil.packInt(x, z)); if (column == null) { return 0; } else { - this.buffer.readerIndex(column.getReaderIndex()); - int blockCount = this.buffer.readInt(); + int blockCount = column.getBlockCount(); if (blockCount <= 0) { return 0; } else { - int blockY = this.buffer.readInt(); + MemorySegment data = column.getMemorySegment(); + int offset = 0; + int blockY = data.get(ValueLayout.JAVA_INT_UNALIGNED, (long)offset); + offset += 4; for (int i = 0; i < blockCount; i++) { - int mask = this.buffer.readUnsignedShort(); + int mask = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; int blockBytes = PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); - int blockId = ByteBufUtil.readNumber(this.buffer, blockBytes); + int blockId = ByteBufUtil.readNumber(data, offset, blockBytes); + offset += blockBytes; int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); - blockY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(this.buffer, offsetBytes); + blockY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(data, offset, offsetBytes); + offset += offsetBytes; if (blockY > y) { return 0; } if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { - this.buffer.readFloat(); + offset += 4; } if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { - this.buffer.readUnsignedByte(); + offset++; } if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { - this.buffer.readUnsignedShort(); + offset += 2; } - int fluidBytes = PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); - this.buffer.skipBytes(fluidBytes); + offset += PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); if (blockY == y) { if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { throw new UnsupportedOperationException("Unable to access block with chance!"); @@ -1078,43 +1156,47 @@ public class PrefabBuffer { @Override public int getFiller(int x, int y, int z) { - this.prefabBuffer.checkReleased(); PrefabBufferColumn column = this.prefabBuffer.columns.get(MathUtil.packInt(x, z)); if (column == null) { return 0; } else { - this.buffer.readerIndex(column.getReaderIndex()); - int blockCount = this.buffer.readInt(); + int blockCount = column.getBlockCount(); if (blockCount <= 0) { return 0; } else { - int blockY = this.buffer.readInt(); + MemorySegment data = column.getMemorySegment(); + int offset = 0; + int blockY = data.get(ValueLayout.JAVA_INT_UNALIGNED, (long)offset); + offset += 4; for (int i = 0; i < blockCount; i++) { - int mask = this.buffer.readUnsignedShort(); + int mask = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; int blockBytes = PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); - int blockId = ByteBufUtil.readNumber(this.buffer, blockBytes); + ByteBufUtil.readNumber(data, offset, blockBytes); + offset += blockBytes; int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); - blockY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(this.buffer, offsetBytes); + blockY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(data, offset, offsetBytes); + offset += offsetBytes; if (blockY > y) { return 0; } if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { - this.buffer.readFloat(); + offset += 4; } if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { - this.buffer.readUnsignedByte(); + offset++; } int filler = 0; if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { - filler = this.buffer.readUnsignedShort(); + filler = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; } - int fluidBytes = PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); - this.buffer.skipBytes(fluidBytes); + offset += PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); if (blockY == y) { if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { throw new UnsupportedOperationException("Unable to access block with chance!"); @@ -1131,43 +1213,47 @@ public class PrefabBuffer { @Override public int getRotationIndex(int x, int y, int z) { - this.prefabBuffer.checkReleased(); PrefabBufferColumn column = this.prefabBuffer.columns.get(MathUtil.packInt(x, z)); if (column == null) { return 0; } else { - this.buffer.readerIndex(column.getReaderIndex()); - int blockCount = this.buffer.readInt(); + int blockCount = column.getBlockCount(); if (blockCount <= 0) { return 0; } else { - int blockY = this.buffer.readInt(); + MemorySegment data = column.getMemorySegment(); + int offset = 0; + int blockY = data.get(ValueLayout.JAVA_INT_UNALIGNED, (long)offset); + offset += 4; for (int i = 0; i < blockCount; i++) { - int mask = this.buffer.readUnsignedShort(); + int mask = Short.toUnsignedInt(data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset)); + offset += 2; int blockBytes = PrefabBuffer.BlockMaskConstants.getBlockBytes(mask); - int blockId = ByteBufUtil.readNumber(this.buffer, blockBytes); + ByteBufUtil.readNumber(data, offset, blockBytes); + offset += blockBytes; int offsetBytes = PrefabBuffer.BlockMaskConstants.getOffsetBytes(mask); - blockY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(this.buffer, offsetBytes); + blockY += offsetBytes == 0 ? 1 : ByteBufUtil.readNumber(data, offset, offsetBytes); + offset += offsetBytes; if (blockY > y) { return 0; } if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { - this.buffer.readFloat(); + offset += 4; } int rotation = 0; if (PrefabBuffer.BlockMaskConstants.hasRotation(mask)) { - rotation = this.buffer.readUnsignedByte(); + rotation = Byte.toUnsignedInt(data.get(ValueLayout.JAVA_BYTE, (long)offset)); + offset++; } if (PrefabBuffer.BlockMaskConstants.hasFiller(mask)) { - this.buffer.readUnsignedShort(); + offset += 2; } - int fluidBytes = PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); - this.buffer.skipBytes(fluidBytes); + offset += PrefabBuffer.BlockMaskConstants.getFluidBytes(mask); if (blockY == y) { if (PrefabBuffer.BlockMaskConstants.hasChance(mask)) { throw new UnsupportedOperationException("Unable to access block with chance!"); diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/PrefabBufferColumn.java b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/PrefabBufferColumn.java index 2383ed5e..a608b363 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/PrefabBufferColumn.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/buffer/impl/PrefabBufferColumn.java @@ -4,21 +4,33 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import java.lang.foreign.MemorySegment; +import javax.annotation.Nonnull; import javax.annotation.Nullable; public class PrefabBufferColumn { - private final int readerIndex; + private final int blockCount; + @Nonnull + private final MemorySegment memorySegment; private final Holder[] entityHolders; private final Int2ObjectMap> blockComponents; - public PrefabBufferColumn(int readerIndex, Holder[] entityHolders, Int2ObjectMap> blockComponents) { - this.readerIndex = readerIndex; + public PrefabBufferColumn( + int blockCount, @Nonnull MemorySegment memorySegment, Holder[] entityHolders, Int2ObjectMap> blockComponents + ) { + this.blockCount = blockCount; + this.memorySegment = memorySegment; this.entityHolders = entityHolders; this.blockComponents = blockComponents; } - public int getReaderIndex() { - return this.readerIndex; + public int getBlockCount() { + return this.blockCount; + } + + @Nonnull + public MemorySegment getMemorySegment() { + return this.memorySegment; } @Nullable diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockFilter.java b/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockFilter.java index 906be7d8..47824cf7 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockFilter.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockFilter.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.prefab.selection.mask; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.function.FunctionCodec; import com.hypixel.hytale.common.util.ArrayUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.buildertool.config.BlockTypeListAsset; @@ -21,6 +20,7 @@ import java.util.Objects; import java.util.regex.Pattern; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class BlockFilter { public static final BlockFilter[] EMPTY_ARRAY = new BlockFilter[0]; @@ -36,6 +36,7 @@ public class BlockFilter { private final transient String toString0; private IntSet resolvedBlocks; private IntSet resolvedFluids; + private boolean hasInvalidBlocks; public BlockFilter(@Nonnull BlockFilter.FilterType blockFilterType, @Nonnull String[] blocks, boolean inverted) { Objects.requireNonNull(blockFilterType); @@ -51,9 +52,15 @@ public class BlockFilter { BlockFilter.BlocksAndFluids result = parseBlocksAndFluids(this.blocks); this.resolvedBlocks = result.blocks; this.resolvedFluids = result.fluids; + this.hasInvalidBlocks = result.hasInvalidBlocks; } } + public boolean hasInvalidBlocks() { + this.resolve(); + return this.hasInvalidBlocks; + } + @Nonnull public BlockFilter.FilterType getBlockFilterType() { return this.blockFilterType; @@ -209,6 +216,7 @@ public class BlockFilter { private static BlockFilter.BlocksAndFluids parseBlocksAndFluids(@Nonnull String[] blocksArgs) { IntSet blocks = new IntOpenHashSet(); IntSet fluids = new IntOpenHashSet(); + boolean invalid = false; for (String blockArg : blocksArgs) { Item item = Item.getAssetMap().getAsset(blockArg); @@ -221,15 +229,19 @@ public class BlockFilter { } int blockId = BlockPattern.parseBlock(blockArg); + if (blockId == 0 && !blockArg.equalsIgnoreCase("Empty")) { + invalid = true; + } + BlockType blockType = BlockType.getAssetMap().getAsset(blockId); if (blockType != null && blockType.getBlockListAssetId() != null) { BlockTypeListAsset blockTypeListAsset = BlockTypeListAsset.getAssetMap().getAsset(blockType.getBlockListAssetId()); if (blockTypeListAsset != null && blockTypeListAsset.getBlockPattern() != null) { - Integer[] var11 = blockTypeListAsset.getBlockPattern().getResolvedKeys(); - int var12 = var11.length; + Integer[] var12 = blockTypeListAsset.getBlockPattern().getResolvedKeys(); + int var13 = var12.length; - for (int var13 = 0; var13 < var12; var13++) { - int resolvedKey = var11[var13]; + for (int var14 = 0; var14 < var13; var14++) { + int resolvedKey = var12[var14]; blocks.add(resolvedKey); } continue; @@ -239,7 +251,7 @@ public class BlockFilter { blocks.add(blockId); } - return new BlockFilter.BlocksAndFluids(IntSets.unmodifiable(blocks), fluids.isEmpty() ? null : IntSets.unmodifiable(fluids)); + return new BlockFilter.BlocksAndFluids(IntSets.unmodifiable(blocks), fluids.isEmpty() ? null : IntSets.unmodifiable(fluids), invalid); } private static int getFluidIdFromItem(@Nonnull Item item) { @@ -273,10 +285,12 @@ public class BlockFilter { private static class BlocksAndFluids { final IntSet blocks; final IntSet fluids; + final boolean hasInvalidBlocks; - BlocksAndFluids(IntSet blocks, IntSet fluids) { + BlocksAndFluids(IntSet blocks, IntSet fluids, boolean hasInvalidBlocks) { this.blocks = blocks; this.fluids = fluids; + this.hasInvalidBlocks = hasInvalidBlocks; } } diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockMask.java b/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockMask.java index a8636e1f..1b790aba 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockMask.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockMask.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.core.prefab.selection.mask; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.function.FunctionCodec; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; @@ -10,6 +9,7 @@ import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class BlockMask { public static final BlockMask EMPTY = new BlockMask(BlockFilter.EMPTY_ARRAY); @@ -67,6 +67,16 @@ public class BlockMask { return this.inverted; } + public boolean hasInvalidBlocks() { + for (BlockFilter filter : this.filters) { + if (filter.hasInvalidBlocks()) { + return true; + } + } + + return false; + } + public boolean isExcluded(@Nonnull ChunkAccessor accessor, int x, int y, int z, Vector3i min, Vector3i max, int blockId) { return this.isExcluded(accessor, x, y, z, min, max, blockId, -1); } diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockPattern.java b/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockPattern.java index 7e049011..d1aae0fa 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockPattern.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/mask/BlockPattern.java @@ -33,6 +33,7 @@ public class BlockPattern { private final transient String toString0; private IWeightedMap resolvedWeightedMap; private IWeightedMap resolvedWeightedMapBtk; + private boolean hasInvalidBlocks; public BlockPattern(IWeightedMap weightedMap) { this.weightedMap = weightedMap; @@ -50,6 +51,13 @@ public class BlockPattern { WeightedMap.Builder mapBuilderKey = WeightedMap.builder(new BlockPattern.BlockEntry[0]); this.weightedMap.forEachEntry((blockName, weight) -> { int blockId = parseBlock(blockName); + if (blockId == 0) { + String baseName = blockName.contains("|") ? blockName.substring(0, blockName.indexOf(124)) : blockName; + if (!baseName.equalsIgnoreCase("Empty")) { + this.hasInvalidBlocks = true; + } + } + BlockPattern.BlockEntry key = tryParseBlockTypeKey(blockName); BlockType blockType = BlockType.getAssetMap().getAsset(blockId); if (blockType != null && blockType.getBlockListAssetId() != null) { @@ -82,6 +90,11 @@ public class BlockPattern { return this.weightedMap.size() == 0; } + public boolean hasInvalidBlocks() { + this.resolve(); + return this.hasInvalidBlocks; + } + public int nextBlock(Random random) { this.resolve(); return this.resolvedWeightedMap.get(random); diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/mask/MultiBlockMask.java b/src/com/hypixel/hytale/server/core/prefab/selection/mask/MultiBlockMask.java index 9f93556b..9e1a6ec2 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/mask/MultiBlockMask.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/mask/MultiBlockMask.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.server.core.prefab.selection.mask; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class MultiBlockMask extends BlockMask { private static final String BLOCK_MASK_SEPARATOR = ";"; diff --git a/src/com/hypixel/hytale/server/core/prefab/selection/standard/BlockSelection.java b/src/com/hypixel/hytale/server/core/prefab/selection/standard/BlockSelection.java index eb509c8c..6b893b97 100644 --- a/src/com/hypixel/hytale/server/core/prefab/selection/standard/BlockSelection.java +++ b/src/com/hypixel/hytale/server/core/prefab/selection/standard/BlockSelection.java @@ -15,16 +15,16 @@ import com.hypixel.hytale.component.SystemType; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.block.BlockUtil; -import com.hypixel.hytale.math.matrix.Matrix4d; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricResults; import com.hypixel.hytale.metrics.MetricsRegistry; +import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.Opacity; +import com.hypixel.hytale.protocol.packets.buildertools.ClipboardEntityChange; import com.hypixel.hytale.protocol.packets.interface_.BlockChange; import com.hypixel.hytale.protocol.packets.interface_.EditorBlocksChange; import com.hypixel.hytale.protocol.packets.interface_.EditorSelection; @@ -33,18 +33,20 @@ import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.VariantRotation; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.asset.type.fluid.FluidTicker; import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; import com.hypixel.hytale.server.core.command.system.CommandSender; +import com.hypixel.hytale.server.core.entity.entities.BlockEntity; import com.hypixel.hytale.server.core.io.NetworkSerializable; import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.entity.EntityModule; +import com.hypixel.hytale.server.core.modules.entity.component.EntityScaleComponent; import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; +import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; import com.hypixel.hytale.server.core.prefab.event.PrefabPlaceEntityEvent; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask; import com.hypixel.hytale.server.core.universe.world.World; @@ -54,11 +56,13 @@ import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.longs.Long2IntMap; +import it.unimi.dsi.fastutil.longs.Long2IntMaps; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -77,17 +81,20 @@ import java.util.function.IntUnaryOperator; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.bson.BsonDocument; +import org.joml.Quaterniond; +import org.joml.Vector3d; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class BlockSelection implements NetworkSerializable, MetricProvider { public static final Consumer> DEFAULT_ENTITY_CONSUMER = ref -> {}; public static final MetricsRegistry METRICS_REGISTRY = new MetricsRegistry() .register("BlocksLock", selection -> selection.blocksLock.toString(), Codec.STRING) .register("EntitiesLock", selection -> selection.entitiesLock.toString(), Codec.STRING) - .register("Position", selection -> new Vector3i(selection.x, selection.y, selection.z), Vector3i.CODEC) - .register("Anchor", selection -> new Vector3i(selection.anchorX, selection.anchorY, selection.anchorZ), Vector3i.CODEC) - .register("Min", BlockSelection::getSelectionMin, Vector3i.CODEC) - .register("Max", BlockSelection::getSelectionMax, Vector3i.CODEC) + .register("Position", selection -> new Vector3i(selection.x, selection.y, selection.z), Vector3iUtil.CODEC) + .register("Anchor", selection -> new Vector3i(selection.anchorX, selection.anchorY, selection.anchorZ), Vector3iUtil.CODEC) + .register("Min", BlockSelection::getSelectionMin, Vector3iUtil.CODEC) + .register("Max", BlockSelection::getSelectionMax, Vector3iUtil.CODEC) .register("BlockCount", BlockSelection::getBlockCount, Codec.INTEGER) .register("EntityCount", BlockSelection::getEntityCount, Codec.INTEGER); private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @@ -99,15 +106,17 @@ public class BlockSelection implements NetworkSerializable, private int anchorZ; private int prefabId = -1; @Nonnull - private Vector3i min = Vector3i.ZERO; + private Vector3i min = new Vector3i(); @Nonnull - private Vector3i max = Vector3i.ZERO; + private Vector3i max = new Vector3i(); @Nonnull private final Long2ObjectMap blocks; @Nonnull private final Long2ObjectMap fluids; @Nonnull private final List> entities; + @Nonnull + private final Long2IntMap tints; private final ReentrantReadWriteLock blocksLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock entitiesLock = new ReentrantReadWriteLock(); @@ -115,12 +124,14 @@ public class BlockSelection implements NetworkSerializable, this.blocks = new Long2ObjectOpenHashMap<>(); this.fluids = new Long2ObjectOpenHashMap<>(); this.entities = new ObjectArrayList<>(); + this.tints = new Long2IntOpenHashMap(); } public BlockSelection(int initialBlockCapacity, int initialEntityCapacity) { this.blocks = new Long2ObjectOpenHashMap<>(initialBlockCapacity); this.fluids = new Long2ObjectOpenHashMap<>(initialBlockCapacity); this.entities = new ObjectArrayList<>(initialEntityCapacity); + this.tints = new Long2IntOpenHashMap(); } public BlockSelection(@Nonnull BlockSelection other) { @@ -130,6 +141,7 @@ public class BlockSelection implements NetworkSerializable, this.blocks = new Long2ObjectOpenHashMap<>(other.getBlockCount()); this.fluids = new Long2ObjectOpenHashMap<>(other.getFluidCount()); this.entities = new ObjectArrayList<>(other.getEntityCount()); + this.tints = new Long2IntOpenHashMap(); this.copyPropertiesFrom(other); this.add(other); } @@ -161,16 +173,16 @@ public class BlockSelection implements NetworkSerializable, @Nonnull public Vector3i getSelectionMin() { - return this.min.clone(); + return new Vector3i(this.min); } @Nonnull public Vector3i getSelectionMax() { - return this.max.clone(); + return new Vector3i(this.max); } public boolean hasSelectionBounds() { - return !this.min.equals(Vector3i.ZERO) || !this.max.equals(Vector3i.ZERO); + return !this.min.equals(Vector3iUtil.ZERO) || !this.max.equals(Vector3iUtil.ZERO); } public int getBlockCount() { @@ -199,6 +211,19 @@ public class BlockSelection implements NetworkSerializable, return var1; } + public int getTintCount() { + this.blocksLock.readLock().lock(); + + int var1; + try { + var1 = this.tints.size(); + } finally { + this.blocksLock.readLock().unlock(); + } + + return var1; + } + public int getSelectionVolume() { int xLength = this.max.x - this.min.x; int yLength = this.max.y - this.min.y; @@ -235,9 +260,9 @@ public class BlockSelection implements NetworkSerializable, this.anchorZ = anchorZ; } - public void setSelectionArea(@Nonnull Vector3i min, @Nonnull Vector3i max) { - this.min = Vector3i.min(min, max); - this.max = Vector3i.max(min, max); + public void setSelectionArea(@Nonnull Vector3ic min, @Nonnull Vector3ic max) { + this.min = Vector3iUtil.min(min, max); + this.max = Vector3iUtil.max(min, max); } public void setPrefabId(int id) { @@ -251,15 +276,15 @@ public class BlockSelection implements NetworkSerializable, this.anchorX = other.anchorX; this.anchorY = other.anchorY; this.anchorZ = other.anchorZ; - this.min = other.min.clone(); - this.max = other.max.clone(); + this.min = new Vector3i(other.min); + this.max = new Vector3i(other.max); } public boolean canPlace(@Nonnull World world, @Nonnull Vector3i position, @Nullable IntList mask) { return this.compare((x1, y1, z1, block) -> { - int blockX = x1 + position.getX() - this.anchorX; - int blockY = y1 + position.getY() - this.anchorY; - int blockZ = z1 + position.getZ() - this.anchorZ; + int blockX = x1 + position.x() - this.anchorX; + int blockY = y1 + position.y() - this.anchorY; + int blockZ = z1 + position.z() - this.anchorZ; int blockId = world.getBlock(blockX, blockY, blockZ); return blockId == 0 || mask == null || mask.contains(blockId); }); @@ -267,9 +292,9 @@ public class BlockSelection implements NetworkSerializable, public boolean matches(@Nonnull World world, @Nonnull Vector3i position) { return this.compare((x1, y1, z1, block) -> { - int blockX = x1 + position.getX() - this.anchorX; - int blockY = y1 + position.getY() - this.anchorY; - int blockZ = z1 + position.getZ() - this.anchorZ; + int blockX = x1 + position.x() - this.anchorX; + int blockY = y1 + position.y() - this.anchorY; + int blockZ = z1 + position.z() - this.anchorZ; int blockId = world.getBlock(blockX, blockY, blockZ); return block.blockId == blockId; }); @@ -454,6 +479,38 @@ public class BlockSelection implements NetworkSerializable, } } + public void addTintAtLocalPos(int x, int z, int color) { + this.tints.put(BlockUtil.pack(x, 0, z), color); + } + + public void addTintAtWorldPos(int worldX, int worldZ, int color) { + this.tints.put(BlockUtil.pack(worldX - this.x, 0, worldZ - this.z), color); + } + + public int getTintAtWorldPos(int worldX, int worldZ) { + return this.tints.getOrDefault(BlockUtil.pack(worldX - this.x, 0, worldZ - this.z), -1); + } + + public boolean hasTintAtWorldPos(int worldX, int worldZ) { + return this.tints.containsKey(BlockUtil.pack(worldX - this.x, 0, worldZ - this.z)); + } + + public void forEachTint(@Nonnull BlockSelection.TintIterator iterator) { + this.blocksLock.readLock().lock(); + + try { + Long2IntMaps.fastForEach(this.tints, e -> { + long packed = e.getLongKey(); + int color = e.getIntValue(); + int x1 = BlockUtil.unpackX(packed); + int z1 = BlockUtil.unpackZ(packed); + iterator.accept(x1, z1, color); + }); + } finally { + this.blocksLock.readLock().unlock(); + } + } + public void forEachFluid(@Nonnull BlockSelection.FluidIterator iterator) { this.blocksLock.readLock().lock(); @@ -569,23 +626,6 @@ public class BlockSelection implements NetworkSerializable, if (blockType.getBlockEntity() != null) { holder = blockType.getBlockEntity().clone(); } - - StateData state = blockType.getState(); - if (state != null && state.getId() != null) { - Vector3i position = new Vector3i(BlockUtil.unpackX(k), BlockUtil.unpackY(k), BlockUtil.unpackZ(k)); - Codec codec = BlockState.CODEC.getCodecFor(state.getId()); - if (codec == null) { - return (BlockSelection.BlockHolder)b; - } - - BlockState blockState = codec.decode(new BsonDocument()); - if (blockState == null) { - return (BlockSelection.BlockHolder)b; - } - - blockState.setPosition(null, position); - holder = blockState.toHolder(); - } } if (holder != null && b.filler != 0) { @@ -654,7 +694,7 @@ public class BlockSelection implements NetworkSerializable, assert transformComponent != null; - transformComponent.getPosition().subtract(this.x, this.y, this.z); + transformComponent.getPosition().sub(this.x, this.y, this.z); this.addEntityHolderRaw(entityHolder); } @@ -692,12 +732,12 @@ public class BlockSelection implements NetworkSerializable, } else if (pb == null) { return -1; } else { - int cmp = Double.compare(pa.getX(), pb.getX()); + int cmp = Double.compare(pa.x(), pb.x()); if (cmp != 0) { return cmp; } else { - cmp = Double.compare(pa.getY(), pb.getY()); - return cmp != 0 ? cmp : Double.compare(pa.getZ(), pb.getZ()); + cmp = Double.compare(pa.y(), pb.y()); + return cmp != 0 ? cmp : Double.compare(pa.z(), pb.z()); } } } @@ -712,7 +752,7 @@ public class BlockSelection implements NetworkSerializable, } public void placeNoReturn(String feedbackKey, CommandSender feedback, @Nonnull World outerWorld, ComponentAccessor componentAccessor) { - this.placeNoReturn(feedbackKey, feedback, FeedbackConsumer.DEFAULT, outerWorld, Vector3i.ZERO, null, componentAccessor); + this.placeNoReturn(feedbackKey, feedback, FeedbackConsumer.DEFAULT, outerWorld, Vector3iUtil.ZERO, null, componentAccessor); } public void placeNoReturn( @@ -722,7 +762,7 @@ public class BlockSelection implements NetworkSerializable, @Nonnull World outerWorld, ComponentAccessor componentAccessor ) { - this.placeNoReturn(feedbackKey, feedback, feedbackConsumer, outerWorld, Vector3i.ZERO, null, componentAccessor); + this.placeNoReturn(feedbackKey, feedback, feedbackConsumer, outerWorld, Vector3iUtil.ZERO, null, componentAccessor); } public void placeNoReturn( @@ -730,27 +770,27 @@ public class BlockSelection implements NetworkSerializable, @Nullable CommandSender feedback, @Nonnull FeedbackConsumer feedbackConsumer, @Nonnull World outerWorld, - @Nullable Vector3i position, + @Nullable Vector3ic position, @Nullable BlockMask blockMask, ComponentAccessor componentAccessor ) { IntUnaryOperator xConvert; - if (position != null && position.getX() != 0) { - xConvert = localX -> localX + this.x + position.getX() - this.anchorX; + if (position != null && position.x() != 0) { + xConvert = localX -> localX + this.x + position.x() - this.anchorX; } else { xConvert = localX -> localX + this.x - this.anchorX; } IntUnaryOperator yConvert; - if (position != null && position.getY() != 0) { - yConvert = localY -> localY + this.y + position.getY() - this.anchorY; + if (position != null && position.y() != 0) { + yConvert = localY -> localY + this.y + position.y() - this.anchorY; } else { yConvert = localY -> localY + this.y - this.anchorY; } IntUnaryOperator zConvert; - if (position != null && position.getZ() != 0) { - zConvert = localZ -> localZ + this.z + position.getZ() - this.anchorZ; + if (position != null && position.z() != 0) { + zConvert = localZ -> localZ + this.z + position.z() - this.anchorZ; } else { zConvert = localZ -> localZ + this.z - this.anchorZ; } @@ -818,11 +858,22 @@ public class BlockSelection implements NetworkSerializable, componentAccessor ) ); + this.forEachTint((x, z, newColor) -> { + int worldX = this.x + x; + int worldZ = this.z + z; + long chunkIdx = ChunkUtil.indexChunkFromBlock(worldX, worldZ); + WorldChunk chunk = outerWorld.getNonTickingChunk(chunkIdx); + chunk.getBlockChunk().setTint(worldX, worldZ, newColor); + dirtyChunks.add(chunkIdx); + }); } finally { this.blocksLock.readLock().unlock(); } - dirtyChunks.forEach(value -> outerWorld.getChunkLighting().invalidateLightInChunk(outerWorld.getChunkIfInMemory(value))); + dirtyChunks.forEach( + value -> outerWorld.getChunkLighting() + .invalidateLightInChunk(outerWorld.getChunkStore(), ChunkUtil.xOfChunkIndex(value), ChunkUtil.zOfChunkIndex(value)) + ); this.placeEntities(outerWorld, position); dirtyChunks.forEach(value -> outerWorld.getNotificationHandler().updateChunk(value)); } @@ -852,8 +903,8 @@ public class BlockSelection implements NetworkSerializable, int oldBlockId = chunk.getBlock(blockX, blockY, blockZ); if (blockMask == null || !blockMask.isExcluded(outerWorld, blockX, blockY, blockZ, this.min, this.max, oldBlockId)) { BlockChunk blockChunk = chunk.getBlockChunk(); + BlockType newBlockType = assetMap.getAsset(newBlockId); if (blockChunk.setBlock(blockX, blockY, blockZ, newBlockId, newRotation, newFiller)) { - BlockType newBlockType = assetMap.getAsset(newBlockId); if (newBlockType != null && FluidTicker.isFullySolid(newBlockType)) { this.clearFluidAtPosition(outerWorld, chunk, blockX, blockY, blockZ); } @@ -868,7 +919,7 @@ public class BlockSelection implements NetworkSerializable, } } - chunk.setState(blockX, blockY, blockZ, holder); + chunk.setState(blockX, blockY, blockZ, newBlockType, newRotation, holder); dirtyChunks.add(chunkIndex); feedbackConsumer.accept(feedbackKey, totalBlocks, counter, feedback, componentAccessor); } @@ -925,16 +976,16 @@ public class BlockSelection implements NetworkSerializable, @Nonnull public BlockSelection place(CommandSender feedback, @Nonnull World outerWorld) { - return this.place(feedback, outerWorld, Vector3i.ZERO, null); + return this.place(feedback, outerWorld, Vector3iUtil.ZERO, null); } @Nonnull public BlockSelection place(CommandSender feedback, @Nonnull World outerWorld, BlockMask blockMask) { - return this.place(feedback, outerWorld, Vector3i.ZERO, blockMask); + return this.place(feedback, outerWorld, Vector3iUtil.ZERO, blockMask); } @Nonnull - public BlockSelection place(CommandSender feedback, @Nonnull World outerWorld, Vector3i position, BlockMask blockMask) { + public BlockSelection place(CommandSender feedback, @Nonnull World outerWorld, Vector3ic position, BlockMask blockMask) { return this.place(feedback, outerWorld, position, blockMask, DEFAULT_ENTITY_CONSUMER); } @@ -942,7 +993,7 @@ public class BlockSelection implements NetworkSerializable, public BlockSelection place( CommandSender feedback, @Nonnull World outerWorld, - @Nullable Vector3i position, + @Nullable Vector3ic position, @Nullable BlockMask blockMask, @Nonnull Consumer> entityConsumer ) { @@ -950,22 +1001,22 @@ public class BlockSelection implements NetworkSerializable, before.setAnchor(this.anchorX, this.anchorY, this.anchorZ); before.setPosition(this.x, this.y, this.z); IntUnaryOperator xConvert; - if (position != null && position.getX() != 0) { - xConvert = localX -> localX + this.x + position.getX() - this.anchorX; + if (position != null && position.x() != 0) { + xConvert = localX -> localX + this.x + position.x() - this.anchorX; } else { xConvert = localX -> localX + this.x - this.anchorX; } IntUnaryOperator yConvert; - if (position != null && position.getY() != 0) { - yConvert = localY -> localY + this.y + position.getY() - this.anchorY; + if (position != null && position.y() != 0) { + yConvert = localY -> localY + this.y + position.y() - this.anchorY; } else { yConvert = localY -> localY + this.y - this.anchorY; } IntUnaryOperator zConvert; - if (position != null && position.getZ() != 0) { - zConvert = localZ -> localZ + this.z + position.getZ() - this.anchorZ; + if (position != null && position.z() != 0) { + zConvert = localZ -> localZ + this.z + position.z() - this.anchorZ; } else { zConvert = localZ -> localZ + this.z - this.anchorZ; } @@ -1029,11 +1080,24 @@ public class BlockSelection implements NetworkSerializable, fluidStore.fluidLevel ) ); + this.forEachTint((x, z, newColor) -> { + int worldX = this.x + x; + int worldZ = this.z + z; + long chunkIdx = ChunkUtil.indexChunkFromBlock(worldX, worldZ); + WorldChunk chunk = outerWorld.getNonTickingChunk(chunkIdx); + int beforeColor = chunk.getBlockChunk().getTint(worldX, worldZ); + before.addTintAtWorldPos(worldX, worldZ, beforeColor); + chunk.getBlockChunk().setTint(worldX, worldZ, newColor); + dirtyChunks.add(chunkIdx); + }); } finally { this.blocksLock.readLock().unlock(); } - dirtyChunks.forEach(value -> outerWorld.getChunkLighting().invalidateLightInChunk(outerWorld.getChunkIfInMemory(value))); + dirtyChunks.forEach( + value -> outerWorld.getChunkLighting() + .invalidateLightInChunk(outerWorld.getChunkStore(), ChunkUtil.xOfChunkIndex(value), ChunkUtil.zOfChunkIndex(value)) + ); this.placeEntities(outerWorld, position, entityConsumer); dirtyChunks.forEach(value -> outerWorld.getNotificationHandler().updateChunk(value)); return before; @@ -1073,8 +1137,8 @@ public class BlockSelection implements NetworkSerializable, int rotation = blockSection.getRotationIndex(blockX, blockY, blockZ); before.addBlockAtLocalPos(localX, localY, localZ, oldBlockId, rotation, filler, supportValue, chunk.getBlockComponentHolder(blockX, blockY, blockZ)); BlockChunk blockChunk = chunk.getBlockChunk(); + BlockType newBlockType = assetMap.getAsset(newBlockId); if (blockChunk.setBlock(blockX, blockY, blockZ, newBlockId, newRotation, newFiller)) { - BlockType newBlockType = assetMap.getAsset(newBlockId); if (newBlockType != null && FluidTicker.isFullySolid(newBlockType)) { this.clearFluidAtPosition(outerWorld, chunk, blockX, blockY, blockZ); } @@ -1101,7 +1165,7 @@ public class BlockSelection implements NetworkSerializable, } } - chunk.setState(blockX, blockY, blockZ, holder); + chunk.setState(blockX, blockY, blockZ, newBlockType, newRotation, holder); dirtyChunks.add(chunkIndex); } } @@ -1138,11 +1202,11 @@ public class BlockSelection implements NetworkSerializable, } } - private void placeEntities(@Nonnull World world, @Nonnull Vector3i pos) { + private void placeEntities(@Nonnull World world, @Nonnull Vector3ic pos) { this.placeEntities(world, pos, DEFAULT_ENTITY_CONSUMER); } - private void placeEntities(@Nonnull World world, @Nonnull Vector3i pos, @Nonnull Consumer> entityConsumer) { + private void placeEntities(@Nonnull World world, @Nonnull Vector3ic pos, @Nonnull Consumer> entityConsumer) { this.entitiesLock.readLock().lock(); try { @@ -1160,12 +1224,12 @@ public class BlockSelection implements NetworkSerializable, } @Nonnull - private Ref placeEntity(@Nonnull World world, @Nonnull Holder entityHolder, @Nonnull Vector3i pos, int prefabId) { + private Ref placeEntity(@Nonnull World world, @Nonnull Holder entityHolder, @Nonnull Vector3ic pos, int prefabId) { TransformComponent transformComponent = entityHolder.getComponent(TransformComponent.getComponentType()); assert transformComponent != null; - transformComponent.getPosition().add(this.x + pos.getX() - this.anchorX, this.y + pos.getY() - this.anchorY, this.z + pos.getZ() - this.anchorZ); + transformComponent.getPosition().add(this.x + pos.x() - this.anchorX, this.y + pos.y() - this.anchorY, this.z + pos.z() - this.anchorZ); Store store = world.getEntityStore().getStore(); PrefabPlaceEntityEvent prefabPlaceEntityEvent = new PrefabPlaceEntityEvent(prefabId, entityHolder); store.invoke(prefabPlaceEntityEvent); @@ -1177,33 +1241,23 @@ public class BlockSelection implements NetworkSerializable, @Nonnull public BlockSelection rotate(@Nonnull Axis axis, int angle) { - BlockTypeAssetMap assetMap = BlockType.getAssetMap(); BlockSelection selection = new BlockSelection(this.getBlockCount(), this.getEntityCount()); selection.copyPropertiesFrom(this); Vector3i mutable = new Vector3i(0, 0, 0); Rotation rotation = Rotation.ofDegrees(angle); this.forEachBlock( (x1, y1, z1, block) -> { - mutable.assign(x1 - this.anchorX, y1 - this.anchorY, z1 - this.anchorZ); + mutable.set(x1 - this.anchorX, y1 - this.anchorY, z1 - this.anchorZ); axis.rotate(mutable, angle); int blockId = block.blockId; Holder holder = block.holder; RotationTuple blockRotation = RotationTuple.get(block.rotation); - - RotationTuple rotatedRotation = switch (axis) { - case X -> RotationTuple.of(blockRotation.yaw(), blockRotation.pitch().add(rotation), blockRotation.roll()); - case Y -> RotationTuple.of(blockRotation.yaw().add(rotation), blockRotation.pitch(), blockRotation.roll()); - case Z -> RotationTuple.of(blockRotation.yaw(), blockRotation.pitch(), blockRotation.roll().add(rotation)); - }; - if (rotatedRotation == null) { - rotatedRotation = blockRotation; - } - + RotationTuple rotatedRotation = blockRotation.composeOnAxis(axis, rotation); int rotatedFiller = BlockRotationUtil.getRotatedFiller(block.filler, axis, rotation); selection.addBlock0( - mutable.getX() + this.anchorX, - mutable.getY() + this.anchorY, - mutable.getZ() + this.anchorZ, + mutable.x() + this.anchorX, + mutable.y() + this.anchorY, + mutable.z() + this.anchorZ, blockId, rotatedRotation.index(), rotatedFiller, @@ -1212,6 +1266,7 @@ public class BlockSelection implements NetworkSerializable, ); } ); + Quaterniond axisRot = new Quaterniond().rotateAxis(Math.toRadians(angle), axis.getDirection().x(), axis.getDirection().y(), axis.getDirection().z()); this.forEachEntity(entityHolder -> { Holder copy = entityHolder.clone(); TransformComponent transformComponent = copy.getComponent(TransformComponent.getComponentType()); @@ -1220,49 +1275,48 @@ public class BlockSelection implements NetworkSerializable, Vector3d position = transformComponent.getPosition(); HeadRotation headRotationComponent = copy.getComponent(HeadRotation.getComponentType()); - position.subtract(this.anchorX, this.anchorY, this.anchorZ).subtract(0.5, 0.0, 0.5); + boolean isBlockEntity = copy.getComponent(BlockEntity.getComponentType()) != null; + Vector3d offset = isBlockEntity ? new Vector3d(0.5, 0.0, 0.5) : new Vector3d(0.5, 0.5, 0.5); + position.sub(this.anchorX, this.anchorY, this.anchorZ).sub(offset); axis.rotate(position, angle); - position.add(this.anchorX, this.anchorY, this.anchorZ).add(0.5, 0.0, 0.5); - transformComponent.getRotation().addRotationOnAxis(axis, angle); + position.add(this.anchorX, this.anchorY, this.anchorZ).add(offset); + composeAxisRotation(axisRot, transformComponent.getRotation()); if (headRotationComponent != null) { - headRotationComponent.getRotation().addRotationOnAxis(axis, angle); + composeAxisRotation(axisRot, headRotationComponent.getRotation()); } selection.addEntity0(copy); }); + Vector3i fluidMutable = new Vector3i(0, 0, 0); + this.forEachFluid((x1, y1, z1, fluidId, fluidLevel) -> { + fluidMutable.set(x1 - this.anchorX, y1 - this.anchorY, z1 - this.anchorZ); + axis.rotate(fluidMutable, angle); + selection.addFluid0(fluidMutable.x() + this.anchorX, fluidMutable.y() + this.anchorY, fluidMutable.z() + this.anchorZ, fluidId, fluidLevel); + }); return selection; } @Nonnull - public BlockSelection rotate(@Nonnull Axis axis, int angle, @Nonnull Vector3f originOfRotation) { + public BlockSelection rotate(@Nonnull Axis axis, int angle, @Nonnull Vector3d originOfRotation) { BlockSelection selection = new BlockSelection(this.getBlockCount(), this.getEntityCount()); selection.copyPropertiesFrom(this); Vector3d mutable = new Vector3d(0.0, 0.0, 0.0); Rotation rotation = Rotation.ofDegrees(angle); - Vector3f finalOriginOfRotation = originOfRotation.clone().subtract(this.x, this.y, this.z); + Vector3d finalOriginOfRotation = new Vector3d(originOfRotation).sub(this.x, this.y, this.z); this.forEachBlock( (x1, y1, z1, block) -> { - mutable.assign(x1 - finalOriginOfRotation.x, y1 - finalOriginOfRotation.y, z1 - finalOriginOfRotation.z); + mutable.set(x1 - finalOriginOfRotation.x, y1 - finalOriginOfRotation.y, z1 - finalOriginOfRotation.z); axis.rotate(mutable, angle); int blockId = block.blockId; Holder holder = block.holder; int supportValue = block.supportValue(); RotationTuple blockRotation = RotationTuple.get(block.rotation); - - RotationTuple rotatedRotation = switch (axis) { - case X -> RotationTuple.of(blockRotation.yaw(), blockRotation.pitch().add(rotation), blockRotation.roll()); - case Y -> RotationTuple.of(blockRotation.yaw().add(rotation), blockRotation.pitch(), blockRotation.roll()); - case Z -> RotationTuple.of(blockRotation.yaw(), blockRotation.pitch(), blockRotation.roll().add(rotation)); - }; - if (rotatedRotation == null) { - rotatedRotation = blockRotation; - } - + RotationTuple rotatedRotation = blockRotation.composeOnAxis(axis, rotation); int rotatedFiller = BlockRotationUtil.getRotatedFiller(block.filler, axis, rotation); selection.addBlock0( - (int)(mutable.getX() + finalOriginOfRotation.x), - (int)(mutable.getY() + finalOriginOfRotation.z), - (int)(mutable.getZ() + finalOriginOfRotation.z), + (int)(mutable.x() + finalOriginOfRotation.x), + (int)(mutable.y() + finalOriginOfRotation.y), + (int)(mutable.z() + finalOriginOfRotation.z), blockId, rotatedRotation.index(), rotatedFiller, @@ -1271,6 +1325,7 @@ public class BlockSelection implements NetworkSerializable, ); } ); + Quaterniond axisRot2 = new Quaterniond().rotateAxis(Math.toRadians(angle), axis.getDirection().x(), axis.getDirection().y(), axis.getDirection().z()); this.forEachEntity(entityHolder -> { Holder copy = entityHolder.clone(); TransformComponent transformComponent = copy.getComponent(TransformComponent.getComponentType()); @@ -1279,28 +1334,46 @@ public class BlockSelection implements NetworkSerializable, Vector3d position = transformComponent.getPosition(); HeadRotation headRotationComponent = copy.getComponent(HeadRotation.getComponentType()); - position.subtract(this.anchorX, this.anchorY, this.anchorZ).subtract(0.5, 0.0, 0.5); + boolean isBlockEntity = entityHolder.getComponent(BlockEntity.getComponentType()) != null; + Vector3d offset = isBlockEntity ? new Vector3d(0.5, 0.0, 0.5) : new Vector3d(0.5, 0.5, 0.5); + position.sub(this.anchorX, this.anchorY, this.anchorZ).sub(offset); axis.rotate(position, angle); - position.add(this.anchorX, this.anchorY, this.anchorZ).add(0.5, 0.0, 0.5); - transformComponent.getRotation().addRotationOnAxis(axis, angle); + position.add(this.anchorX, this.anchorY, this.anchorZ).add(offset); + composeAxisRotation(axisRot2, transformComponent.getRotation()); if (headRotationComponent != null) { - headRotationComponent.getRotation().addRotationOnAxis(axis, angle); + composeAxisRotation(axisRot2, headRotationComponent.getRotation()); } selection.addEntity0(copy); }); + Vector3d fluidMutable2 = new Vector3d(0.0, 0.0, 0.0); + this.forEachFluid( + (x1, y1, z1, fluidId, fluidLevel) -> { + fluidMutable2.set(x1 - finalOriginOfRotation.x, y1 - finalOriginOfRotation.y, z1 - finalOriginOfRotation.z); + axis.rotate(fluidMutable2, angle); + selection.addFluid0( + (int)(fluidMutable2.x() + finalOriginOfRotation.x), + (int)(fluidMutable2.y() + finalOriginOfRotation.y), + (int)(fluidMutable2.z() + finalOriginOfRotation.z), + fluidId, + fluidLevel + ); + } + ); return selection; } + private static void composeAxisRotation(@Nonnull Quaterniond axisRotation, @Nonnull Rotation3f euler) { + euler.premul(axisRotation); + } + @Nonnull public BlockSelection rotateArbitrary(float yawDegrees, float pitchDegrees, float rollDegrees) { double pitchRad = Math.toRadians(pitchDegrees); double yawRad = Math.toRadians(yawDegrees); double rollRad = Math.toRadians(rollDegrees); - Matrix4d rotation = new Matrix4d(); - rotation.setRotateEuler(pitchRad, yawRad, rollRad); - Matrix4d inverse = new Matrix4d(rotation); - inverse.invert(); + Quaterniond rotation = new Quaterniond().rotateYXZ(yawRad, pitchRad, rollRad); + Quaterniond inverse = new Quaterniond(rotation).conjugate(); Vector3d tempVec = new Vector3d(); int destMinX = Integer.MAX_VALUE; int destMinY = Integer.MAX_VALUE; @@ -1350,8 +1423,8 @@ public class BlockSelection implements NetworkSerializable, }; for (int[] corner : corners) { - tempVec.assign(corner[0], corner[1], corner[2]); - rotation.multiplyDirection(tempVec); + tempVec.set(corner[0], corner[1], corner[2]); + rotation.transform(tempVec); int rx = MathUtil.floor(tempVec.x); int ry = MathUtil.floor(tempVec.y); int rz = MathUtil.floor(tempVec.z); @@ -1368,14 +1441,15 @@ public class BlockSelection implements NetworkSerializable, Rotation snappedYaw = Rotation.ofDegrees(Math.round(yawDegrees / 90.0F) * 90); Rotation snappedPitch = Rotation.ofDegrees(Math.round(pitchDegrees / 90.0F) * 90); Rotation snappedRoll = Rotation.ofDegrees(Math.round(rollDegrees / 90.0F) * 90); + RotationTuple snappedRotation = RotationTuple.of(snappedYaw, snappedPitch, snappedRoll); this.blocksLock.readLock().lock(); try { for (int dx = destMinX; dx <= destMaxX; dx++) { for (int dy = destMinY; dy <= destMaxY; dy++) { for (int dz = destMinZ; dz <= destMaxZ; dz++) { - tempVec.assign(dx, dy, dz); - inverse.multiplyDirection(tempVec); + tempVec.set(dx, dy, dz); + inverse.transform(tempVec); int sx = (int)Math.round(tempVec.x); int sy = (int)Math.round(tempVec.y); int sz = (int)Math.round(tempVec.z); @@ -1383,9 +1457,7 @@ public class BlockSelection implements NetworkSerializable, BlockSelection.BlockHolder block = this.blocks.get(packedSource); if (block != null) { RotationTuple blockRotation = RotationTuple.get(block.rotation()); - RotationTuple rotatedRotation = RotationTuple.of( - blockRotation.yaw().add(snappedYaw), blockRotation.pitch().add(snappedPitch), blockRotation.roll().add(snappedRoll) - ); + RotationTuple rotatedRotation = RotationTuple.compose(snappedRotation, blockRotation); if (rotatedRotation == null) { rotatedRotation = blockRotation; } @@ -1395,8 +1467,8 @@ public class BlockSelection implements NetworkSerializable, int fillerX = FillerBlockUtil.unpackX(rotatedFiller); int fillerY = FillerBlockUtil.unpackY(rotatedFiller); int fillerZ = FillerBlockUtil.unpackZ(rotatedFiller); - tempVec.assign(fillerX, fillerY, fillerZ); - rotation.multiplyDirection(tempVec); + tempVec.set(fillerX, fillerY, fillerZ); + rotation.transform(tempVec); rotatedFiller = FillerBlockUtil.pack((int)Math.round(tempVec.x), (int)Math.round(tempVec.y), (int)Math.round(tempVec.z)); } @@ -1419,8 +1491,8 @@ public class BlockSelection implements NetworkSerializable, for (int dx = destMinX; dx <= destMaxX; dx++) { for (int dy = destMinY; dy <= destMaxY; dy++) { for (int dzx = destMinZ; dzx <= destMaxZ; dzx++) { - tempVec.assign(dx, dy, dzx); - inverse.multiplyDirection(tempVec); + tempVec.set(dx, dy, dzx); + inverse.transform(tempVec); int sx = (int)Math.round(tempVec.x); int sy = (int)Math.round(tempVec.y); int sz = (int)Math.round(tempVec.z); @@ -1436,9 +1508,6 @@ public class BlockSelection implements NetworkSerializable, this.blocksLock.readLock().unlock(); } - float var64 = (float)yawRad; - float var68 = (float)pitchRad; - float var71 = (float)rollRad; this.forEachEntity(entityHolder -> { Holder copy = entityHolder.clone(); TransformComponent transformComponent = copy.getComponent(TransformComponent.getComponentType()); @@ -1447,18 +1516,14 @@ public class BlockSelection implements NetworkSerializable, Vector3d position = transformComponent.getPosition(); HeadRotation headRotationComp = copy.getComponent(HeadRotation.getComponentType()); - position.subtract(this.anchorX, this.anchorY, this.anchorZ).subtract(0.5, 0.0, 0.5); - rotation.multiplyDirection(position); - position.add(this.anchorX, this.anchorY, this.anchorZ).add(0.5, 0.0, 0.5); - Vector3f bodyRotation = transformComponent.getRotation(); - bodyRotation.addPitch(var68); - bodyRotation.addYaw(var64); - bodyRotation.addRoll(var71); + boolean isBlockEntity = entityHolder.getComponent(BlockEntity.getComponentType()) != null; + Vector3d offset = isBlockEntity ? new Vector3d(0.5, 0.0, 0.5) : new Vector3d(0.5, 0.5, 0.5); + position.sub(this.anchorX, this.anchorY, this.anchorZ).sub(offset); + rotation.transform(position); + position.add(this.anchorX, this.anchorY, this.anchorZ).add(offset); + composeAxisRotation(rotation, transformComponent.getRotation()); if (headRotationComp != null) { - Vector3f headRot = headRotationComp.getRotation(); - headRot.addPitch(var68); - headRot.addYaw(var64); - headRot.addRoll(var71); + composeAxisRotation(rotation, headRotationComp.getRotation()); } selection.addEntity0(copy); @@ -1475,35 +1540,30 @@ public class BlockSelection implements NetworkSerializable, Vector3i mutable = new Vector3i(0, 0, 0); this.forEachBlock( (x1, y1, z1, block) -> { - mutable.assign(x1 - this.anchorX, y1 - this.anchorY, z1 - this.anchorZ); + mutable.set(x1 - this.anchorX, y1 - this.anchorY, z1 - this.anchorZ); axis.flip(mutable); int blockId = block.blockId; Holder holder = block.holder; int supportValue = block.supportValue(); int filler = block.filler; BlockType blockType = assetMap.getAsset(blockId); - VariantRotation variantRotation = blockType.getVariantRotation(); - if (variantRotation == VariantRotation.None) { - selection.addBlock0(mutable.getX() + this.anchorX, mutable.getY() + this.anchorY, mutable.getZ() + this.anchorZ, block); - } else { - RotationTuple blockRotation = RotationTuple.get(block.rotation); - RotationTuple rotatedRotation = BlockRotationUtil.getFlipped(blockRotation, blockType.getFlipType(), axis, variantRotation); - if (rotatedRotation != null) { - rotatedRotation = blockRotation; - } - - int rotatedFiller = BlockRotationUtil.getFlippedFiller(filler, axis); - selection.addBlock0( - mutable.getX() + this.anchorX, - mutable.getY() + this.anchorY, - mutable.getZ() + this.anchorZ, - blockId, - rotatedRotation.index(), - rotatedFiller, - supportValue, - holder != null ? holder.clone() : null - ); + RotationTuple blockRotation = RotationTuple.get(block.rotation); + RotationTuple rotatedRotation = BlockRotationUtil.getFlipped(blockRotation, blockType.getFlipType(), axis); + if (rotatedRotation == null) { + rotatedRotation = blockRotation; } + + int rotatedFiller = BlockRotationUtil.getFlippedFiller(filler, axis); + selection.addBlock0( + mutable.x() + this.anchorX, + mutable.y() + this.anchorY, + mutable.z() + this.anchorZ, + blockId, + rotatedRotation.index(), + rotatedFiller, + supportValue, + holder != null ? holder.clone() : null + ); } ); this.forEachEntity(entityHolder -> { @@ -1512,16 +1572,18 @@ public class BlockSelection implements NetworkSerializable, assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); + Rotation3f headRotation = headRotationComponent.getRotation(); TransformComponent transformComponent = copy.getComponent(TransformComponent.getComponentType()); assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - Vector3f bodyRotation = transformComponent.getRotation(); - position.subtract(this.anchorX, this.anchorY, this.anchorZ).subtract(0.5, 0.0, 0.5); + Rotation3f bodyRotation = transformComponent.getRotation(); + boolean isBlockEntity = entityHolder.getComponent(BlockEntity.getComponentType()) != null; + Vector3d offset = isBlockEntity ? new Vector3d(0.5, 0.0, 0.5) : new Vector3d(0.5, 0.5, 0.5); + position.sub(this.anchorX, this.anchorY, this.anchorZ).sub(offset); axis.flip(position); - position.add(this.anchorX, this.anchorY, this.anchorZ).add(0.5, 0.0, 0.5); + position.add(this.anchorX, this.anchorY, this.anchorZ).add(offset); axis.flipRotation(bodyRotation); axis.flipRotation(headRotation); selection.addEntity0(copy); @@ -1542,7 +1604,7 @@ public class BlockSelection implements NetworkSerializable, BlockSelection selection = new BlockSelection(this.getBlockCount(), this.getEntityCount()); selection.setAnchor(this.anchorX - originX, this.anchorY - originY, this.anchorZ - originZ); selection.setPosition(this.x - originX, this.y - originY, this.z - originZ); - selection.setSelectionArea(this.min.clone().subtract(originX, originY, originZ), this.max.clone().subtract(originX, originY, originZ)); + selection.setSelectionArea(new Vector3i(this.min).sub(originX, originY, originZ), new Vector3i(this.max).sub(originX, originY, originZ)); this.forEachBlock((x, y, z, block) -> selection.addBlock0(x - originX, y - originY, z - originZ, block)); this.forEachEntity(holder -> { Holder copy = holder.clone(); @@ -1550,7 +1612,7 @@ public class BlockSelection implements NetworkSerializable, assert transformComponent != null; - transformComponent.getPosition().subtract(originX, originY, originZ); + transformComponent.getPosition().sub(originX, originY, originZ); selection.addEntity0(copy); }); return selection; @@ -1566,6 +1628,7 @@ public class BlockSelection implements NetworkSerializable, try { Long2ObjectMaps.fastForEach(this.blocks, entry -> selection.blocks.put(entry.getLongKey(), entry.getValue().cloneBlockHolder())); selection.fluids.putAll(this.fluids); + selection.tints.putAll(this.tints); } finally { this.blocksLock.readLock().unlock(); } @@ -1591,7 +1654,7 @@ public class BlockSelection implements NetworkSerializable, assert transformComponent != null; - transformComponent.getPosition().add(other.x, other.y, other.z).subtract(this.x, this.y, this.z); + transformComponent.getPosition().add(other.x, other.y, other.z).sub(this.x, this.y, this.z); this.addEntity0(copy); }); } finally { @@ -1643,23 +1706,86 @@ public class BlockSelection implements NetworkSerializable, this.blocksLock.readLock().unlock(); } + this.entitiesLock.readLock().lock(); + + try { + if (!this.entities.isEmpty()) { + ObjectArrayList entityList = new ObjectArrayList<>(this.entities.size()); + + for (Holder holder : this.entities) { + ClipboardEntityChange ec = toClipboardEntityChange(holder, this.anchorX, this.anchorY, this.anchorZ); + if (ec != null) { + entityList.add(ec); + } + } + + packet.entityChanges = entityList.toArray(ClipboardEntityChange[]::new); + } + } finally { + this.entitiesLock.readLock().unlock(); + } + return packet; } + @Nullable + public static ClipboardEntityChange toClipboardEntityChange(@Nonnull Holder holder, double anchorX, double anchorY, double anchorZ) { + TransformComponent transform = holder.getComponent(TransformComponent.getComponentType()); + if (transform != null && transform.getPosition() != null) { + Vector3d pos = transform.getPosition(); + ClipboardEntityChange ec = new ClipboardEntityChange(); + ec.x = (float)(pos.x() - anchorX); + ec.y = (float)(pos.y() - anchorY); + ec.z = (float)(pos.z() - anchorZ); + BlockEntity blockEntityComp = holder.getComponent(BlockEntity.getComponentType()); + if (blockEntityComp != null) { + String key = blockEntityComp.getBlockTypeKey(); + ec.blockId = key != null ? BlockType.getAssetMap().getIndex(key) : 0; + } + + ModelComponent modelComp = holder.getComponent(ModelComponent.getComponentType()); + if (modelComp != null && modelComp.getModel() != null) { + ec.model = modelComp.getModel().toPacket(); + } + + ItemComponent itemComp = holder.getComponent(ItemComponent.getComponentType()); + if (itemComp != null && itemComp.getItemStack() != null) { + ec.itemId = itemComp.getItemStack().getItemId(); + } + + Rotation3f rot = transform.getRotation(); + if (rot != null) { + ec.bodyOrientation = new Direction(rot.y(), rot.x(), rot.z()); + } + + HeadRotation headRot = holder.getComponent(HeadRotation.getComponentType()); + if (headRot != null && headRot.getRotation() != null) { + Rotation3f hr = headRot.getRotation(); + ec.lookOrientation = new Direction(hr.y(), hr.x(), hr.z()); + } + + EntityScaleComponent scaleComp = holder.getComponent(EntityScaleComponent.getComponentType()); + ec.scale = scaleComp != null ? scaleComp.getScale() : 0.0F; + return ec; + } else { + return null; + } + } + @Nonnull public EditorBlocksChange toSelectionPacket() { EditorBlocksChange packet = new EditorBlocksChange(); EditorSelection selection = new EditorSelection(); if (this.min != null) { - selection.minX = this.min.getX(); - selection.minY = this.min.getY(); - selection.minZ = this.min.getZ(); + selection.minX = this.min.x(); + selection.minY = this.min.y(); + selection.minZ = this.min.z(); } if (this.max != null) { - selection.maxX = this.max.getX(); - selection.maxY = this.max.getY(); - selection.maxZ = this.max.getZ(); + selection.maxX = this.max.x(); + selection.maxY = this.max.y(); + selection.maxZ = this.max.z(); } packet.selection = selection; @@ -1671,12 +1797,12 @@ public class BlockSelection implements NetworkSerializable, EditorBlocksChange packet = this.toPacket(); if (this.min != null && this.max != null) { EditorSelection selection = new EditorSelection(); - selection.minX = this.min.getX(); - selection.minY = this.min.getY(); - selection.minZ = this.min.getZ(); - selection.maxX = this.max.getX(); - selection.maxY = this.max.getY(); - selection.maxZ = this.max.getZ(); + selection.minX = this.min.x(); + selection.minY = this.min.y(); + selection.minZ = this.min.z(); + selection.maxX = this.max.x(); + selection.maxY = this.max.y(); + selection.maxZ = this.max.z(); packet.selection = selection; } @@ -1714,19 +1840,23 @@ public class BlockSelection implements NetworkSerializable, int fillerY = FillerBlockUtil.unpackY(blockHolder.filler); int fillerZ = FillerBlockUtil.unpackZ(blockHolder.filler); BlockSelection.BlockHolder baseBlockHolder = this.getBlockHolderAtLocalPos(x - fillerX, y - fillerY, z - fillerZ); - BlockType baseBlock = (BlockType)blockTypeAssetMap.getAsset(baseBlockHolder.blockId); - if (baseBlock == null) { + if (baseBlockHolder == null) { this.addBlockAtLocalPos(x, y, z, 0, 0, 0, 0); } else { - String baseId = baseBlock.getId(); - BlockBoundingBoxes hitbox = (BlockBoundingBoxes)hitboxAssetMap.getAsset(baseBlock.getHitboxTypeIndex()); - if (hitbox != null - && ( - !id.equals(baseId) - || baseBlockHolder.rotation != blockHolder.rotation - || !hitbox.get(blockHolder.rotation).getBoundingBox().containsBlock(fillerX, fillerY, fillerZ) - )) { + BlockType baseBlock = (BlockType)blockTypeAssetMap.getAsset(baseBlockHolder.blockId); + if (baseBlock == null) { this.addBlockAtLocalPos(x, y, z, 0, 0, 0, 0); + } else { + String baseId = baseBlock.getId(); + BlockBoundingBoxes hitbox = (BlockBoundingBoxes)hitboxAssetMap.getAsset(baseBlock.getHitboxTypeIndex()); + if (hitbox != null + && ( + !id.equals(baseId) + || baseBlockHolder.rotation != blockHolder.rotation + || !hitbox.get(blockHolder.rotation).getBoundingBox().containsBlock(fillerX, fillerY, fillerZ) + )) { + this.addBlockAtLocalPos(x, y, z, 0, 0, 0, 0); + } } } } else { @@ -1740,10 +1870,10 @@ public class BlockSelection implements NetworkSerializable, int worldY = y + y1; int worldZ = z + z1; BlockSelection.BlockHolder fillerBlockHolder = this.getBlockHolderAtLocalPos(worldX, worldY, worldZ); - BlockType fillerBlock = (BlockType)blockTypeAssetMap.getAsset(fillerBlockHolder.blockId); + BlockType fillerBlock = fillerBlockHolder != null ? (BlockType)blockTypeAssetMap.getAsset(fillerBlockHolder.blockId) : null; int filler = FillerBlockUtil.pack(x1, y1, z1); if (fillerBlock == null || !fillerBlock.getId().equals(id) || filler != fillerBlockHolder.filler) { - if (!allowDestructive && fillerBlockHolder.blockId != 0) { + if (!allowDestructive && fillerBlockHolder != null && fillerBlockHolder.blockId != 0) { throw new IllegalArgumentException( "Cannot replace " + fillerBlock.getId() @@ -1879,4 +2009,9 @@ public class BlockSelection implements NetworkSerializable, public interface FluidIterator { void accept(int var1, int var2, int var3, int var4, byte var5); } + + @FunctionalInterface + public interface TintIterator { + void accept(int var1, int var2, int var3); + } } diff --git a/src/com/hypixel/hytale/server/core/schema/SchemaGenerator.java b/src/com/hypixel/hytale/server/core/schema/SchemaGenerator.java new file mode 100644 index 00000000..cc6d1568 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/schema/SchemaGenerator.java @@ -0,0 +1,299 @@ +package com.hypixel.hytale.server.core.schema; + +import com.hypixel.hytale.assetstore.AssetRegistry; +import com.hypixel.hytale.assetstore.AssetStore; +import com.hypixel.hytale.assetstore.codec.AssetCodec; +import com.hypixel.hytale.assetstore.codec.AssetCodecMapCodec; +import com.hypixel.hytale.codec.EmptyExtraInfo; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.codec.schema.SchemaContext; +import com.hypixel.hytale.codec.schema.config.ObjectSchema; +import com.hypixel.hytale.codec.schema.config.Schema; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.server.core.util.BsonUtil; +import com.hypixel.hytale.sneakythrow.SneakyThrow; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.bson.BsonArray; +import org.bson.BsonDocument; +import org.bson.BsonInt32; +import org.bson.BsonString; +import org.bson.BsonValue; + +public class SchemaGenerator { + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + private static final List configRegistrations = new CopyOnWriteArrayList<>(); + private static final List assetSchemaRegistrations = new CopyOnWriteArrayList<>(); + + public static void registerConfig( + @Nonnull String name, @Nonnull BuilderCodec codec, @Nullable String virtualPath, @Nullable List fileMatchPatterns + ) { + configRegistrations.add(new SchemaGenerator.ConfigRegistration(name, codec, virtualPath, fileMatchPatterns)); + } + + public static void registerAssetSchema( + @Nonnull String fileName, @Nonnull Function factory, @Nullable List fileMatchPatterns, @Nullable String extension + ) { + assetSchemaRegistrations.add(new SchemaGenerator.AssetSchemaRegistration(fileName, factory, fileMatchPatterns, extension)); + } + + @Nonnull + public static Map generateAssetSchemas() { + SchemaGenerator.GenerationResult result = collectSchemas(false); + LinkedHashMap schemas = new LinkedHashMap<>(); + schemas.putAll(result.assetSchemas()); + schemas.putAll(result.customAssetSchemas()); + schemas.putAll(result.sharedSchemas()); + return schemas; + } + + public static void generate(@Nullable Path assetOutputDir, @Nullable Path configOutputDir) { + try { + SchemaGenerator.GenerationResult result = collectSchemas(configOutputDir != null); + boolean sameDir = assetOutputDir != null + && configOutputDir != null + && assetOutputDir.toAbsolutePath().normalize().equals(configOutputDir.toAbsolutePath().normalize()); + if (sameDir) { + Path schemaDir = assetOutputDir.resolve("Schema"); + cleanAndCreateSchemaDir(schemaDir); + LinkedHashMap allSchemas = new LinkedHashMap<>(); + allSchemas.putAll(result.assetSchemas()); + allSchemas.putAll(result.customAssetSchemas()); + allSchemas.putAll(result.configSchemas()); + allSchemas.putAll(result.sharedSchemas()); + writeSchemas(allSchemas, schemaDir); + ArrayList allVsCodeEntries = new ArrayList<>(); + allVsCodeEntries.addAll(result.assetVsCodeEntries()); + allVsCodeEntries.addAll(result.customAssetVsCodeEntries()); + allVsCodeEntries.addAll(result.configVsCodeEntries()); + writeVsCodeSettings(assetOutputDir, allVsCodeEntries); + } else { + if (assetOutputDir != null) { + Path schemaDir = assetOutputDir.resolve("Schema"); + cleanAndCreateSchemaDir(schemaDir); + LinkedHashMap allAssetSchemas = new LinkedHashMap<>(); + allAssetSchemas.putAll(result.assetSchemas()); + allAssetSchemas.putAll(result.customAssetSchemas()); + allAssetSchemas.putAll(result.sharedSchemas()); + writeSchemas(allAssetSchemas, schemaDir); + ArrayList allAssetVsCode = new ArrayList<>(); + allAssetVsCode.addAll(result.assetVsCodeEntries()); + allAssetVsCode.addAll(result.customAssetVsCodeEntries()); + writeVsCodeSettings(assetOutputDir, allAssetVsCode); + } + + if (configOutputDir != null) { + Path schemaDir = configOutputDir.resolve("Schema"); + cleanAndCreateSchemaDir(schemaDir); + LinkedHashMap allConfigSchemas = new LinkedHashMap<>(); + allConfigSchemas.putAll(result.configSchemas()); + allConfigSchemas.putAll(result.sharedSchemas()); + writeSchemas(allConfigSchemas, schemaDir); + writeVsCodeSettings(configOutputDir, result.configVsCodeEntries()); + } + } + } catch (Throwable var7) { + LOGGER.at(Level.SEVERE).withCause(var7).log("Schema generation failed"); + throw new RuntimeException("Schema generation failed", var7); + } + } + + private static SchemaGenerator.GenerationResult collectSchemas(boolean includeConfigs) { + SchemaContext context = new SchemaContext(); + AssetStore[] assetStores = AssetRegistry.getStoreMap().values().toArray(AssetStore[]::new); + Arrays.sort(assetStores, Comparator.comparing(store -> store.getAssetClass().getSimpleName())); + + for (AssetStore store : assetStores) { + String name = store.getAssetClass().getSimpleName(); + context.addFileReference(name + ".json", store.getCodec()); + } + + LinkedHashMap assetSchemas = new LinkedHashMap<>(); + ArrayList assetVsCodeEntries = new ArrayList<>(); + + for (AssetStore store : assetStores) { + Class assetClass = store.getAssetClass(); + String path = store.getPath(); + String name = assetClass.getSimpleName(); + AssetCodec codec = store.getCodec(); + Schema schema = codec.toSchema(context); + if (codec instanceof AssetCodecMapCodec) { + schema.setTitle(name); + } + + schema.setId(name + ".json"); + Schema.HytaleMetadata hytale = schema.getHytale(); + hytale.setPath(path); + hytale.setExtension(store.getExtension()); + Class idProvider = store.getIdProvider(); + if (idProvider != null) { + hytale.setIdProvider(idProvider.getSimpleName()); + } + + List preload = store.getPreAddedAssets(); + if (preload != null && !preload.isEmpty()) { + String[] internal = new String[preload.size()]; + + for (int i = 0; i < preload.size(); i++) { + Object p = preload.get(i); + Object k = store.getKeyFunction().apply(p); + internal[i] = k.toString(); + } + + hytale.setInternalKeys(internal); + } + + assetSchemas.put(name + ".json", schema); + assetVsCodeEntries.add( + new SchemaGenerator.VsCodeEntry( + name + ".json", + List.of("/Server/" + path + "/*" + store.getExtension(), "/Server/" + path + "/**/*" + store.getExtension()), + store.getExtension() + ) + ); + } + + LinkedHashMap customAssetSchemas = new LinkedHashMap<>(); + ArrayList customAssetVsCodeEntries = new ArrayList<>(); + + for (SchemaGenerator.AssetSchemaRegistration reg : assetSchemaRegistrations) { + Schema schemax = reg.factory().apply(context); + customAssetSchemas.put(reg.fileName(), schemax); + if (reg.fileMatchPatterns() != null && !reg.fileMatchPatterns().isEmpty()) { + String ext = reg.extension() != null ? reg.extension() : ".json"; + customAssetVsCodeEntries.add( + new SchemaGenerator.VsCodeEntry(reg.fileName(), reg.fileMatchPatterns().stream().map(px -> "/Server/" + px).toList(), ext) + ); + } + } + + LinkedHashMap configSchemas = new LinkedHashMap<>(); + ArrayList configVsCodeEntries = new ArrayList<>(); + if (includeConfigs) { + for (SchemaGenerator.ConfigRegistration regx : configRegistrations) { + try { + ObjectSchema schemax = regx.codec().toSchema(context); + schemax.setTitle(regx.name()); + String fileName = toFileName(regx.name()); + schemax.setId(fileName); + Schema.HytaleMetadata hytalex = schemax.getHytale(); + if (regx.virtualPath() != null) { + hytalex.setVirtualPath(regx.virtualPath()); + } + + configSchemas.put(fileName, schemax); + if (regx.fileMatchPatterns() != null && !regx.fileMatchPatterns().isEmpty()) { + configVsCodeEntries.add(new SchemaGenerator.VsCodeEntry(fileName, regx.fileMatchPatterns(), null)); + } + } catch (Throwable var21) { + LOGGER.at(Level.WARNING).withCause(var21).log("Failed to generate config schema for '%s', skipping", regx.name()); + } + } + } + + LinkedHashMap sharedSchemas = new LinkedHashMap<>(); + Schema definitions = new Schema(); + definitions.setDefinitions(context.getDefinitions()); + definitions.setId("common.json"); + sharedSchemas.put("common.json", definitions); + Schema otherDefinitions = new Schema(); + otherDefinitions.setDefinitions(context.getOtherDefinitions()); + otherDefinitions.setId("other.json"); + sharedSchemas.put("other.json", otherDefinitions); + return new SchemaGenerator.GenerationResult( + assetSchemas, customAssetSchemas, configSchemas, sharedSchemas, assetVsCodeEntries, customAssetVsCodeEntries, configVsCodeEntries + ); + } + + private static void cleanAndCreateSchemaDir(@Nonnull Path schemaDir) { + try { + Files.createDirectories(schemaDir); + + try (Stream stream = Files.walk(schemaDir, 1)) { + stream.filter(v -> v.toString().endsWith(".json")).forEach(SneakyThrow.sneakyConsumer(Files::delete)); + } + } catch (Exception var6) { + throw new RuntimeException("Failed to prepare schema directory: " + schemaDir, var6); + } + } + + private static void writeVsCodeSettings(@Nonnull Path outputDir, @Nonnull List entries) { + try { + BsonDocument vsCodeConfig = new BsonDocument(); + BsonArray vsCodeSchemas = new BsonArray(); + BsonDocument vsCodeFiles = new BsonDocument(); + vsCodeConfig.put("json.schemas", vsCodeSchemas); + vsCodeConfig.put("files.associations", vsCodeFiles); + vsCodeConfig.put("editor.tabSize", new BsonInt32(2)); + + for (SchemaGenerator.VsCodeEntry entry : entries) { + addVsCodeSchemaLink(vsCodeConfig, entry.schemaFileName(), entry.fileMatchPatterns(), entry.extension()); + } + + Files.createDirectories(outputDir.resolve(".vscode")); + BsonUtil.writeDocument(outputDir.resolve(".vscode/settings.json"), vsCodeConfig, false).join(); + } catch (Exception var7) { + throw new RuntimeException("Failed to write .vscode/settings.json", var7); + } + } + + @Nonnull + public static String toFileName(@Nonnull String name) { + return name.replace(':', '.') + ".json"; + } + + public static void writeSchemas(@Nonnull Map schemas, @Nonnull Path schemaDir) { + for (Entry schema : schemas.entrySet()) { + BsonUtil.writeDocument(schemaDir.resolve(schema.getKey()), Schema.CODEC.encode(schema.getValue(), EmptyExtraInfo.EMPTY).asDocument(), false).join(); + } + } + + public static void addVsCodeSchemaLink( + @Nonnull BsonDocument vsCodeConfig, @Nonnull String schemaFileName, @Nonnull List fileMatchPatterns, @Nullable String extension + ) { + BsonDocument config = new BsonDocument(); + config.put("fileMatch", new BsonArray(fileMatchPatterns.stream().map(BsonString::new).toList())); + config.put("url", new BsonString("./Schema/" + schemaFileName)); + vsCodeConfig.getArray("json.schemas").add((BsonValue)config); + if (extension != null && !extension.equals(".json")) { + vsCodeConfig.getDocument("files.associations").put("*" + extension, new BsonString("json")); + } + } + + private record AssetSchemaRegistration( + @Nonnull String fileName, @Nonnull Function factory, @Nullable List fileMatchPatterns, @Nullable String extension + ) { + } + + private record ConfigRegistration( + @Nonnull String name, @Nonnull BuilderCodec codec, @Nullable String virtualPath, @Nullable List fileMatchPatterns + ) { + } + + private record GenerationResult( + LinkedHashMap assetSchemas, + LinkedHashMap customAssetSchemas, + LinkedHashMap configSchemas, + LinkedHashMap sharedSchemas, + List assetVsCodeEntries, + List customAssetVsCodeEntries, + List configVsCodeEntries + ) { + } + + private record VsCodeEntry(String schemaFileName, List fileMatchPatterns, @Nullable String extension) { + } +} diff --git a/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowser.java b/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowser.java new file mode 100644 index 00000000..bbc9090d --- /dev/null +++ b/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowser.java @@ -0,0 +1,634 @@ +package com.hypixel.hytale.server.core.ui.browser; + +import com.hypixel.hytale.assetstore.AssetPack; +import com.hypixel.hytale.common.plugin.AuthorInfo; +import com.hypixel.hytale.common.plugin.PluginIdentifier; +import com.hypixel.hytale.common.plugin.PluginManifest; +import com.hypixel.hytale.common.semver.Semver; +import com.hypixel.hytale.common.util.StringCompareUtil; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; +import com.hypixel.hytale.server.core.Constants; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.HytaleServerConfig; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.Options; +import com.hypixel.hytale.server.core.asset.AssetModule; +import com.hypixel.hytale.server.core.plugin.PluginManager; +import com.hypixel.hytale.server.core.ui.DropdownEntryInfo; +import com.hypixel.hytale.server.core.ui.LocalizableString; +import com.hypixel.hytale.server.core.ui.Value; +import com.hypixel.hytale.server.core.ui.builder.EventData; +import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder; +import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder; +import com.hypixel.hytale.server.core.util.BsonUtil; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class AssetPackSaveBrowser { + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + private static final Value BUTTON_SELECTED = Value.ref("Pages/BasicTextButton.ui", "SelectedLabelStyle"); + private static final Value TOOLTIP_STYLE = Value.ref("Common.ui", "DefaultTextTooltipStyle"); + private static final Pattern INVALID_FILENAME_CHARS = Pattern.compile("[<>:\"/\\\\|?*\\x00-\\x1F]"); + private static final String DIRECTORY_FILTER_ALL = ""; + @Nonnull + private final AssetPackSaveBrowserConfig config; + @Nonnull + private String searchQuery = ""; + @Nullable + private String selectedPackKey; + @Nullable + private AssetPackSaveBrowser.PendingPack pendingPack; + @Nonnull + private String selectedDirectoryFilter = ""; + public static final String ACTION_OPEN_PACK_BROWSER = "OpenPackBrowser"; + public static final String ACTION_CONFIRM_PACK_BROWSER = "ConfirmPackBrowser"; + public static final String ACTION_CANCEL_PACK_BROWSER = "CancelPackBrowser"; + public static final String ACTION_OPEN_CREATE_PACK = "OpenCreatePack"; + public static final String ACTION_CREATE_PACK = "CreatePack"; + public static final String ACTION_CANCEL_CREATE_PACK = "CancelCreatePack"; + public static final String ACTION_PACK_SEARCH = "PackSearch"; + public static final String ACTION_PACK_SELECT = "PackSelect"; + + public AssetPackSaveBrowser(@Nonnull AssetPackSaveBrowserConfig config) { + this.config = config; + } + + @Nonnull + private List collectModsDirectories() { + ObjectArrayList dirs = new ObjectArrayList<>(); + if (Constants.SINGLEPLAYER) { + dirs.add( + new AssetPackSaveBrowser.ModsDirectory( + "server.customUI.assetPackBrowser.create.targetDir.world", "server.customUI.assetPackBrowser.filter.world", PluginManager.MODS_PATH + ) + ); + + for (Path modsPath : Options.getOptionSet().valuesOf(Options.MODS_DIRECTORIES)) { + dirs.add( + new AssetPackSaveBrowser.ModsDirectory( + "server.customUI.assetPackBrowser.create.targetDir.global", "server.customUI.assetPackBrowser.filter.global", modsPath + ) + ); + } + } else { + dirs.add( + new AssetPackSaveBrowser.ModsDirectory( + "server.customUI.assetPackBrowser.create.targetDir.server", "server.customUI.assetPackBrowser.filter.server", PluginManager.MODS_PATH + ) + ); + + for (Path modsPath : Options.getOptionSet().valuesOf(Options.MODS_DIRECTORIES)) { + dirs.add( + new AssetPackSaveBrowser.ModsDirectory( + "server.customUI.assetPackBrowser.create.targetDir.server", "server.customUI.assetPackBrowser.filter.server", modsPath + ) + ); + } + } + + return dirs; + } + + @Nonnull + private Path getDefaultTargetDirectory() { + if (Constants.SINGLEPLAYER) { + List cliDirs = Options.getOptionSet().valuesOf(Options.MODS_DIRECTORIES); + if (!cliDirs.isEmpty()) { + return cliDirs.getFirst(); + } + } + + return PluginManager.MODS_PATH; + } + + @Nullable + private Path resolveTargetDirectory(@Nullable String pathStr) { + if (pathStr != null && !pathStr.isBlank()) { + Path requested = Path.of(pathStr).normalize(); + + for (AssetPackSaveBrowser.ModsDirectory dir : this.collectModsDirectories()) { + if (dir.path().normalize().equals(requested)) { + return dir.path(); + } + } + + return null; + } else { + return this.getDefaultTargetDirectory(); + } + } + + private void populateTargetDirectoryDropdown(@Nonnull UICommandBuilder commandBuilder) { + List dirs = this.collectModsDirectories(); + if (dirs.size() <= 1) { + commandBuilder.set("#CreatePackPage #TargetDirectory.Visible", false); + } else { + commandBuilder.set("#CreatePackPage #TargetDirectory.Visible", true); + ObjectArrayList entries = new ObjectArrayList<>(); + + for (AssetPackSaveBrowser.ModsDirectory dir : dirs) { + entries.add(new DropdownEntryInfo(LocalizableString.fromMessageId(dir.langKey()), dir.path().toString())); + } + + commandBuilder.set("#CreatePackPage #TargetDirDropdown.Entries", entries); + commandBuilder.set("#CreatePackPage #TargetDirDropdown.Value", this.getDefaultTargetDirectory().toString()); + } + } + + private void populateDirectoryFilterDropdown(@Nonnull UICommandBuilder commandBuilder) { + List dirs = this.collectModsDirectories(); + if (dirs.size() <= 1) { + commandBuilder.set("#PackBrowserPage #DirectoryFilter.Visible", false); + } else { + commandBuilder.set("#PackBrowserPage #DirectoryFilter.Visible", true); + ObjectArrayList entries = new ObjectArrayList<>(); + entries.add(new DropdownEntryInfo(LocalizableString.fromMessageId("server.customUI.assetPackBrowser.filter.all"), "")); + + for (AssetPackSaveBrowser.ModsDirectory dir : dirs) { + entries.add(new DropdownEntryInfo(LocalizableString.fromMessageId(dir.filterLangKey()), dir.path().toString())); + } + + commandBuilder.set("#PackBrowserPage #DirectoryFilter.Entries", entries); + commandBuilder.set("#PackBrowserPage #DirectoryFilter.Value", this.selectedDirectoryFilter); + } + } + + private boolean packBelongsToDirectory(@Nonnull AssetPack pack, @Nonnull Path directory) { + Path packLocation = pack.getPackLocation().toAbsolutePath().normalize(); + Path dirNormalized = directory.toAbsolutePath().normalize(); + return packLocation.startsWith(dirNormalized); + } + + public void setSelectedPackKey(@Nullable String key) { + if (key == null) { + this.selectedPackKey = null; + } else { + AssetPack pack = AssetModule.get().getAssetPack(key); + if (pack != null && !pack.isImmutable() && AssetModule.get().validatePackExistsOnDisk(pack)) { + this.selectedPackKey = key; + } + } + } + + public void buildUI(@Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder) { + this.buildSearchInput(commandBuilder, eventBuilder); + this.buildPackList(commandBuilder, eventBuilder); + } + + public void buildEventBindings(@Nonnull UIEventBuilder eventBuilder, @Nonnull String browseButtonSelector) { + eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, browseButtonSelector, new EventData().append("Action", "OpenPackBrowser")); + eventBuilder.addEventBinding( + CustomUIEventBindingType.Activating, "#PackBrowserPage #SelectButton", new EventData().append("Action", "ConfirmPackBrowser") + ); + eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#PackBrowserPage #CancelButton", new EventData().append("Action", "CancelPackBrowser")); + eventBuilder.addEventBinding( + CustomUIEventBindingType.Activating, "#PackBrowserPage #CreateNewPackButton", new EventData().append("Action", "OpenCreatePack") + ); + eventBuilder.addEventBinding( + CustomUIEventBindingType.Activating, + "#CreatePackPage #CreateButton", + new EventData() + .append("Action", "CreatePack") + .append("@CreateName", "#CreatePackPage #PackName #Input.Value") + .append("@CreateGroup", "#CreatePackPage #PackGroup #Input.Value") + .append("@CreateDescription", "#CreatePackPage #PackDescription #Input.Value") + .append("@CreateVersion", "#CreatePackPage #PackVersion #Input.Value") + .append("@CreateWebsite", "#CreatePackPage #PackWebsite #Input.Value") + .append("@CreateAuthorName", "#CreatePackPage #PackAuthorName #Input.Value") + .append("@CreateTargetDir", "#CreatePackPage #TargetDirDropdown.Value") + ); + eventBuilder.addEventBinding( + CustomUIEventBindingType.Activating, "#CreatePackPage #CancelCreateButton", new EventData().append("Action", "CancelCreatePack") + ); + eventBuilder.addEventBinding( + CustomUIEventBindingType.ValueChanged, + "#CreatePackPage #PackName #Input", + new EventData() + .append("ValidateCreate", "1") + .append("@CreateName", "#CreatePackPage #PackName #Input.Value") + .append("@CreateGroup", "#CreatePackPage #PackGroup #Input.Value"), + false + ); + eventBuilder.addEventBinding( + CustomUIEventBindingType.ValueChanged, + "#CreatePackPage #PackGroup #Input", + new EventData() + .append("ValidateCreate", "1") + .append("@CreateName", "#CreatePackPage #PackName #Input.Value") + .append("@CreateGroup", "#CreatePackPage #PackGroup #Input.Value"), + false + ); + eventBuilder.addEventBinding( + CustomUIEventBindingType.ValueChanged, + "#PackBrowserPage #DirectoryFilter", + new EventData().append("@DirectoryFilter", "#PackBrowserPage #DirectoryFilter.Value"), + false + ); + } + + private void buildSearchInput(@Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder) { + if (this.config.searchInputId() != null) { + if (!this.searchQuery.isEmpty()) { + commandBuilder.set(this.config.searchInputId() + ".Value", this.searchQuery); + } + + eventBuilder.addEventBinding( + CustomUIEventBindingType.ValueChanged, this.config.searchInputId(), EventData.of("@PackSearch", this.config.searchInputId() + ".Value"), false + ); + } + } + + public void buildPackList(@Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder) { + commandBuilder.clear(this.config.listElementId()); + List entries = this.collectPackEntries(); + int buttonIndex = 0; + + for (AssetPackSaveBrowser.PackEntry entry : entries) { + commandBuilder.append(this.config.listElementId(), "Pages/BasicTextButton.ui"); + String selector = this.config.listElementId() + "[" + buttonIndex + "]"; + if (entry.immutable) { + commandBuilder.set( + selector + ".TextSpans", Message.raw(entry.displayName + " ").insert(Message.translation("server.customUI.assetPackBrowser.readOnly")) + ); + commandBuilder.set(selector + ".Disabled", true); + commandBuilder.set(selector + ".Style.Default.LabelStyle.TextColor", "#6e7da1"); + commandBuilder.set(selector + ".TextTooltipStyle", TOOLTIP_STYLE); + commandBuilder.set(selector + ".TooltipTextSpans", Message.translation("server.customUI.assetPackBrowser.readOnlyTooltip")); + } else { + commandBuilder.set(selector + ".Text", entry.displayName); + } + + if (entry.key.equals(this.selectedPackKey)) { + commandBuilder.set(selector + ".Style", BUTTON_SELECTED); + } + + if (!entry.immutable) { + eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, selector, EventData.of("Pack", entry.key)); + } + + buttonIndex++; + } + } + + public boolean handleEvent(@Nonnull AssetPackSaveBrowserEventData data) { + if (data.getSearch() != null) { + this.searchQuery = data.getSearch().trim().toLowerCase(); + return true; + } else if (data.getPack() != null) { + this.selectedPackKey = data.getPack(); + return true; + } else if (data.getDirectoryFilter() != null) { + this.selectedDirectoryFilter = data.getDirectoryFilter(); + return true; + } else { + return data.getValidateCreate() != null; + } + } + + @Nullable + public AssetPackSaveBrowser.ActionResult handleAction( + @Nullable String actionName, @Nonnull AssetPackSaveBrowserEventData data, @Nonnull String selectedPackLabelSelector + ) { + if (actionName == null) { + if (data.getValidateCreate() != null) { + UICommandBuilder commandBuilder = new UICommandBuilder(); + this.buildCreateFormValidation(commandBuilder, data.getCreateName(), data.getCreateGroup()); + return new AssetPackSaveBrowser.ActionResult(commandBuilder, null, null, false); + } else if (this.handleEvent(data)) { + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + this.buildPackList(commandBuilder, eventBuilder); + return new AssetPackSaveBrowser.ActionResult(commandBuilder, eventBuilder, null, false); + } else { + return null; + } + } else { + return switch (actionName) { + case "OpenPackBrowser" -> { + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + commandBuilder.set("#PackBrowserPage.Visible", true); + this.populateDirectoryFilterDropdown(commandBuilder); + this.buildPackList(commandBuilder, eventBuilder); + yield new AssetPackSaveBrowser.ActionResult(commandBuilder, eventBuilder, null, false); + } + case "ConfirmPackBrowser" -> { + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#PackBrowserPage.Visible", false); + if (this.hasSelectedPack()) { + commandBuilder.set(selectedPackLabelSelector + ".Text", this.getSelectedPackDisplayName()); + } + + yield new AssetPackSaveBrowser.ActionResult(commandBuilder, null, null, this.hasSelectedPack()); + } + case "CancelPackBrowser" -> { + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#PackBrowserPage.Visible", false); + yield new AssetPackSaveBrowser.ActionResult(commandBuilder, null, null, false); + } + case "OpenCreatePack" -> { + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#PackBrowserPage.Visible", false); + commandBuilder.set("#CreatePackPage.Visible", true); + commandBuilder.set("#CreatePackPage #PackName #Input.Value", ""); + commandBuilder.set("#CreatePackPage #PackGroup #Input.Value", ""); + commandBuilder.set("#CreatePackPage #PackDescription #Input.Value", ""); + commandBuilder.set("#CreatePackPage #PackVersion #Input.Value", ""); + commandBuilder.set("#CreatePackPage #PackWebsite #Input.Value", ""); + commandBuilder.set("#CreatePackPage #PackAuthorName #Input.Value", ""); + commandBuilder.set("#CreatePackPage #CreateErrorText.Visible", false); + commandBuilder.set("#CreatePackPage #CreateButton.Disabled", false); + this.populateTargetDirectoryDropdown(commandBuilder); + yield new AssetPackSaveBrowser.ActionResult(commandBuilder, null, null, false); + } + case "CreatePack" -> { + AssetPackSaveBrowser.CreatePackResult result = this.createPack(data); + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = null; + String errorKey = null; + if (result.success()) { + eventBuilder = new UIEventBuilder(); + commandBuilder.set("#CreatePackPage.Visible", false); + commandBuilder.set("#PackBrowserPage.Visible", true); + this.buildPackList(commandBuilder, eventBuilder); + } else { + errorKey = result.errorKey(); + } + + yield new AssetPackSaveBrowser.ActionResult(commandBuilder, eventBuilder, errorKey, false); + } + case "CancelCreatePack" -> { + UICommandBuilder commandBuilder = new UICommandBuilder(); + commandBuilder.set("#CreatePackPage.Visible", false); + commandBuilder.set("#PackBrowserPage.Visible", true); + yield new AssetPackSaveBrowser.ActionResult(commandBuilder, null, null, false); + } + case "PackSearch", "PackSelect" -> { + this.handleEvent(data); + UICommandBuilder commandBuilder = new UICommandBuilder(); + UIEventBuilder eventBuilder = new UIEventBuilder(); + this.buildPackList(commandBuilder, eventBuilder); + yield new AssetPackSaveBrowser.ActionResult(commandBuilder, eventBuilder, null, false); + } + default -> null; + }; + } + } + + @Nullable + public AssetPack getSelectedPack() { + return this.selectedPackKey == null ? null : AssetModule.get().getAssetPack(this.selectedPackKey); + } + + public boolean hasSelectedPack() { + return this.selectedPackKey != null && AssetModule.get().getAssetPack(this.selectedPackKey) != null; + } + + @Nonnull + public String getSelectedPackDisplayName() { + if (this.selectedPackKey == null) { + return ""; + } else { + AssetPack pack = AssetModule.get().getAssetPack(this.selectedPackKey); + if (pack == null) { + return this.selectedPackKey; + } else { + String displayName = getPackDisplayName(pack); + + for (AssetPack other : AssetModule.get().getAssetPacks()) { + if (other != pack && displayName.equalsIgnoreCase(getPackDisplayName(other))) { + return pack.getName(); + } + } + + return displayName; + } + } + } + + @Nonnull + public AssetPackSaveBrowser.CreatePackResult createPack(@Nonnull AssetPackSaveBrowserEventData data) { + String name = data.getCreateName(); + String group = data.getCreateGroup(); + if (name == null || name.isBlank()) { + return new AssetPackSaveBrowser.CreatePackResult(false, "server.customUI.assetPackBrowser.create.nameRequired"); + } else if (group != null && !group.isBlank()) { + PluginManifest manifest = new PluginManifest(); + manifest.setName(name.trim()); + manifest.setGroup(group.trim()); + if (data.getCreateDescription() != null && !data.getCreateDescription().isBlank()) { + manifest.setDescription(data.getCreateDescription().trim()); + } + + if (data.getCreateVersion() != null && !data.getCreateVersion().isBlank()) { + try { + manifest.setVersion(Semver.fromString(data.getCreateVersion().trim())); + } catch (IllegalArgumentException var14) { + } + } + + if (data.getCreateWebsite() != null && !data.getCreateWebsite().isBlank()) { + manifest.setWebsite(data.getCreateWebsite().trim()); + } + + if (data.getCreateAuthorName() != null && !data.getCreateAuthorName().isBlank()) { + AuthorInfo author = new AuthorInfo(); + author.setName(data.getCreateAuthorName().trim()); + manifest.setAuthors(List.of(author)); + } + + String packId = new PluginIdentifier(manifest).toString(); + if (AssetModule.get().getAssetPack(packId) != null) { + return new AssetPackSaveBrowser.CreatePackResult(false, "server.customUI.assetPackBrowser.create.alreadyExists"); + } else { + String dirName = INVALID_FILENAME_CHARS.matcher(group.trim() + "." + name.trim()).replaceAll(""); + Path normalized = Path.of(dirName).normalize(); + if (!dirName.isEmpty() && !normalized.toString().isEmpty()) { + Path modsPath = this.resolveTargetDirectory(data.getCreateTargetDir()); + if (modsPath == null) { + return new AssetPackSaveBrowser.CreatePackResult(false, "server.customUI.assetPackBrowser.create.failed"); + } else { + Path packPath = modsPath.resolve(normalized).normalize(); + if (!packPath.startsWith(modsPath)) { + return new AssetPackSaveBrowser.CreatePackResult(false, "server.customUI.assetPackBrowser.create.failed"); + } else if (Files.exists(packPath)) { + return new AssetPackSaveBrowser.CreatePackResult(false, "server.customUI.assetPackBrowser.create.alreadyExists"); + } else { + try { + Files.createDirectories(packPath); + Path manifestPath = packPath.resolve("manifest.json"); + BsonUtil.writeSync(manifestPath, PluginManifest.CODEC, manifest, LOGGER); + HytaleServer.SCHEDULED_EXECUTOR.execute(() -> { + HytaleServerConfig serverConfig = HytaleServer.get().getConfig(); + HytaleServerConfig.setBoot(serverConfig, new PluginIdentifier(manifest), true); + serverConfig.markChanged(); + if (serverConfig.consumeHasChanged()) { + HytaleServerConfig.save(serverConfig).join(); + } + + AssetModule.get().registerPack(packId, packPath, manifest, false); + LOGGER.atInfo().log("Created new asset pack: %s at %s", packId, packPath); + }); + this.selectedPackKey = packId; + this.pendingPack = new AssetPackSaveBrowser.PendingPack(packId, name.trim()); + return new AssetPackSaveBrowser.CreatePackResult(true, null); + } catch (IOException var13) { + LOGGER.atSevere().withCause(var13).log("Failed to create asset pack %s", packId); + return new AssetPackSaveBrowser.CreatePackResult(false, "server.customUI.assetPackBrowser.create.failed"); + } + } + } + } else { + return new AssetPackSaveBrowser.CreatePackResult(false, "server.customUI.assetPackBrowser.create.nameRequired"); + } + } + } else { + return new AssetPackSaveBrowser.CreatePackResult(false, "server.customUI.assetPackBrowser.create.groupRequired"); + } + } + + public boolean checkDuplicatePack(@Nullable String name, @Nullable String group) { + if (name != null && !name.isBlank() && group != null && !group.isBlank()) { + String packId = group.trim() + ":" + name.trim(); + if (AssetModule.get().getAssetPack(packId) != null) { + return true; + } else { + String dirName = INVALID_FILENAME_CHARS.matcher(group.trim() + "." + name.trim()).replaceAll(""); + if (dirName.isEmpty()) { + return false; + } else { + Path normalized = Path.of(dirName).normalize(); + + for (AssetPackSaveBrowser.ModsDirectory dir : this.collectModsDirectories()) { + if (Files.exists(dir.path().resolve(normalized).normalize())) { + return true; + } + } + + return false; + } + } + } else { + return false; + } + } + + public void buildCreateFormValidation(@Nonnull UICommandBuilder commandBuilder, @Nullable String name, @Nullable String group) { + boolean duplicate = this.checkDuplicatePack(name, group); + commandBuilder.set("#CreatePackPage #CreateErrorText.Visible", duplicate); + if (duplicate) { + commandBuilder.set("#CreatePackPage #CreateErrorText.Text", Message.translation("server.customUI.assetPackBrowser.create.alreadyExists")); + } + + commandBuilder.set("#CreatePackPage #CreateButton.Disabled", duplicate); + } + + private List collectPackEntries() { + List mutable = new ObjectArrayList<>(); + List immutable = new ObjectArrayList<>(); + boolean pendingFound = false; + + for (AssetPack pack : AssetModule.get().getAssetPacks()) { + if (AssetModule.get().validatePackExistsOnDisk(pack)) { + String displayName = getPackDisplayName(pack); + boolean isImmutable = pack.isImmutable(); + if (this.pendingPack != null && pack.getName().equals(this.pendingPack.key)) { + pendingFound = true; + } + + if (!this.searchQuery.isEmpty()) { + int score = StringCompareUtil.getFuzzyDistance(displayName.toLowerCase(), this.searchQuery, Locale.ENGLISH); + if (score <= 0) { + continue; + } + } + + if (!this.selectedDirectoryFilter.isEmpty()) { + Path filterPath = Path.of(this.selectedDirectoryFilter); + if (!this.packBelongsToDirectory(pack, filterPath)) { + continue; + } + } + + AssetPackSaveBrowser.PackEntry entry = new AssetPackSaveBrowser.PackEntry(pack.getName(), displayName, isImmutable); + if (isImmutable) { + immutable.add(entry); + } else { + mutable.add(entry); + } + } + } + + if (pendingFound) { + this.pendingPack = null; + } else if (this.pendingPack != null) { + String displayNamex = this.pendingPack.displayName; + if (this.searchQuery.isEmpty() || StringCompareUtil.getFuzzyDistance(displayNamex.toLowerCase(), this.searchQuery, Locale.ENGLISH) > 0) { + mutable.add(new AssetPackSaveBrowser.PackEntry(this.pendingPack.key, displayNamex, false)); + } + } + + disambiguateDisplayNames(mutable); + disambiguateDisplayNames(immutable); + String basePackName = AssetModule.get().getBaseAssetPack().getName(); + Comparator packOrder = Comparator.comparing(e -> !e.key.equals(basePackName)) + .thenComparing(e -> e.displayName.toLowerCase()); + mutable.sort(packOrder); + immutable.sort(packOrder); + List result = new ObjectArrayList<>(mutable.size() + immutable.size()); + result.addAll(mutable); + result.addAll(immutable); + return result; + } + + private static void disambiguateDisplayNames(@Nonnull List entries) { + Map nameCounts = new HashMap<>(); + + for (AssetPackSaveBrowser.PackEntry entry : entries) { + nameCounts.merge(entry.displayName.toLowerCase(), 1, Integer::sum); + } + + for (int i = 0; i < entries.size(); i++) { + AssetPackSaveBrowser.PackEntry entry = entries.get(i); + if (nameCounts.getOrDefault(entry.displayName.toLowerCase(), 0) > 1) { + entries.set(i, new AssetPackSaveBrowser.PackEntry(entry.key, entry.key, entry.immutable)); + } + } + } + + @Nonnull + private static String getPackDisplayName(@Nonnull AssetPack pack) { + if (pack.equals(AssetModule.get().getBaseAssetPack())) { + return "HytaleAssets"; + } else { + PluginManifest manifest = pack.getManifest(); + return manifest != null ? manifest.getName() : pack.getName(); + } + } + + public record ActionResult(@Nonnull UICommandBuilder commandBuilder, @Nullable UIEventBuilder eventBuilder, @Nullable String errorKey, boolean packConfirmed) { + } + + public record CreatePackResult(boolean success, @Nullable String errorKey) { + } + + public record ModsDirectory(@Nonnull String langKey, @Nonnull String filterLangKey, @Nonnull Path path) { + } + + private record PackEntry(@Nonnull String key, @Nonnull String displayName, boolean immutable) { + } + + private record PendingPack(@Nonnull String key, @Nonnull String displayName) { + } +} diff --git a/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowserConfig.java b/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowserConfig.java new file mode 100644 index 00000000..53d0ddc1 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowserConfig.java @@ -0,0 +1,10 @@ +package com.hypixel.hytale.server.core.ui.browser; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public record AssetPackSaveBrowserConfig(@Nonnull String listElementId, @Nullable String searchInputId) { + public static AssetPackSaveBrowserConfig defaults() { + return new AssetPackSaveBrowserConfig("#PackList", "#SearchInput"); + } +} diff --git a/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowserEventData.java b/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowserEventData.java new file mode 100644 index 00000000..8477a8ed --- /dev/null +++ b/src/com/hypixel/hytale/server/core/ui/browser/AssetPackSaveBrowserEventData.java @@ -0,0 +1,123 @@ +package com.hypixel.hytale.server.core.ui.browser; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import javax.annotation.Nullable; + +public class AssetPackSaveBrowserEventData { + public static final String KEY_PACK = "Pack"; + public static final String KEY_SEARCH = "@PackSearch"; + public static final String KEY_CREATE_NAME = "@CreateName"; + public static final String KEY_CREATE_GROUP = "@CreateGroup"; + public static final String KEY_CREATE_DESCRIPTION = "@CreateDescription"; + public static final String KEY_CREATE_VERSION = "@CreateVersion"; + public static final String KEY_CREATE_WEBSITE = "@CreateWebsite"; + public static final String KEY_CREATE_AUTHOR_NAME = "@CreateAuthorName"; + public static final String KEY_VALIDATE_CREATE = "ValidateCreate"; + public static final String KEY_CREATE_TARGET_DIR = "@CreateTargetDir"; + public static final String KEY_DIRECTORY_FILTER = "@DirectoryFilter"; + public static final BuilderCodec CODEC = BuilderCodec.builder( + AssetPackSaveBrowserEventData.class, AssetPackSaveBrowserEventData::new + ) + .append(new KeyedCodec<>("Pack", Codec.STRING), (e, s) -> e.pack = s, e -> e.pack) + .add() + .append(new KeyedCodec<>("@PackSearch", Codec.STRING), (e, s) -> e.search = s, e -> e.search) + .add() + .append(new KeyedCodec<>("@CreateName", Codec.STRING), (e, s) -> e.createName = s, e -> e.createName) + .add() + .append(new KeyedCodec<>("@CreateGroup", Codec.STRING), (e, s) -> e.createGroup = s, e -> e.createGroup) + .add() + .append(new KeyedCodec<>("@CreateDescription", Codec.STRING), (e, s) -> e.createDescription = s, e -> e.createDescription) + .add() + .append(new KeyedCodec<>("@CreateVersion", Codec.STRING), (e, s) -> e.createVersion = s, e -> e.createVersion) + .add() + .append(new KeyedCodec<>("@CreateWebsite", Codec.STRING), (e, s) -> e.createWebsite = s, e -> e.createWebsite) + .add() + .append(new KeyedCodec<>("@CreateAuthorName", Codec.STRING), (e, s) -> e.createAuthorName = s, e -> e.createAuthorName) + .add() + .append(new KeyedCodec<>("ValidateCreate", Codec.STRING), (e, s) -> e.validateCreate = s, e -> e.validateCreate) + .add() + .append(new KeyedCodec<>("@CreateTargetDir", Codec.STRING), (e, s) -> e.createTargetDir = s, e -> e.createTargetDir) + .add() + .append(new KeyedCodec<>("@DirectoryFilter", Codec.STRING), (e, s) -> e.directoryFilter = s, e -> e.directoryFilter) + .add() + .build(); + @Nullable + public String pack; + @Nullable + public String search; + @Nullable + public String createName; + @Nullable + public String createGroup; + @Nullable + public String createDescription; + @Nullable + public String createVersion; + @Nullable + public String createWebsite; + @Nullable + public String createAuthorName; + @Nullable + public String validateCreate; + @Nullable + public String createTargetDir; + @Nullable + public String directoryFilter; + + @Nullable + public String getPack() { + return this.pack; + } + + @Nullable + public String getSearch() { + return this.search; + } + + @Nullable + public String getCreateName() { + return this.createName; + } + + @Nullable + public String getCreateGroup() { + return this.createGroup; + } + + @Nullable + public String getCreateDescription() { + return this.createDescription; + } + + @Nullable + public String getCreateVersion() { + return this.createVersion; + } + + @Nullable + public String getCreateWebsite() { + return this.createWebsite; + } + + @Nullable + public String getCreateAuthorName() { + return this.createAuthorName; + } + + @Nullable + public String getValidateCreate() { + return this.validateCreate; + } + + @Nullable + public String getCreateTargetDir() { + return this.createTargetDir; + } + + @Nullable + public String getDirectoryFilter() { + return this.directoryFilter; + } +} diff --git a/src/com/hypixel/hytale/server/core/ui/browser/ServerFileBrowser.java b/src/com/hypixel/hytale/server/core/ui/browser/ServerFileBrowser.java index bb2dc132..31d14a69 100644 --- a/src/com/hypixel/hytale/server/core/ui/browser/ServerFileBrowser.java +++ b/src/com/hypixel/hytale/server/core/ui/browser/ServerFileBrowser.java @@ -28,6 +28,7 @@ import java.util.Comparator; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -261,6 +262,10 @@ public class ServerFileBrowser { } else { try { Files.walkFileTree(this.root, new SimpleFileVisitor() { + { + Objects.requireNonNull(ServerFileBrowser.this); + } + @Nonnull public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) { String fileName = file.getFileName().toString(); @@ -406,6 +411,10 @@ public class ServerFileBrowser { ) { try { Files.walkFileTree(searchRoot, new SimpleFileVisitor() { + { + Objects.requireNonNull(ServerFileBrowser.this); + } + @Nonnull public FileVisitResult preVisitDirectory(@Nonnull Path dir, @Nonnull BasicFileAttributes attrs) { if (dir.equals(searchRoot)) { diff --git a/src/com/hypixel/hytale/server/core/universe/PlayerRef.java b/src/com/hypixel/hytale/server/core/universe/PlayerRef.java index 9f359908..752dff25 100644 --- a/src/com/hypixel/hytale/server/core/universe/PlayerRef.java +++ b/src/com/hypixel/hytale/server/core/universe/PlayerRef.java @@ -11,8 +11,8 @@ import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.logger.sentry.SkipSentryException; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricResults; import com.hypixel.hytale.metrics.MetricsRegistry; @@ -22,6 +22,7 @@ import com.hypixel.hytale.protocol.packets.connection.PongType; import com.hypixel.hytale.protocol.packets.interface_.ChatType; import com.hypixel.hytale.protocol.packets.interface_.ServerMessage; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.entity.entities.player.CameraManager; import com.hypixel.hytale.server.core.entity.entities.player.HiddenPlayersManager; import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager; @@ -29,7 +30,7 @@ import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.io.PacketStatsRecorderImpl; import com.hypixel.hytale.server.core.modules.entity.player.ChunkTracker; -import com.hypixel.hytale.server.core.receiver.IMessageReceiver; +import com.hypixel.hytale.server.core.permissions.PermissionsModule; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Objects; @@ -39,7 +40,7 @@ import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class PlayerRef implements Component, MetricProvider, IMessageReceiver { +public class PlayerRef implements Component, MetricProvider, CommandSender { @Nonnull public static final MetricsRegistry METRICS_REGISTRY = new MetricsRegistry() .register("Username", PlayerRef::getUsername, Codec.STRING) @@ -109,7 +110,7 @@ public class PlayerRef implements Component, MetricProvider, IMessa @Nullable private UUID worldUuid; private Transform transform = new Transform(0.0, 0.0, 0.0, 0.0F, 0.0F, 0.0F); - private Vector3f headRotation = new Vector3f(0.0F, 0.0F, 0.0F); + private Rotation3f headRotation = new Rotation3f(0.0F, 0.0F, 0.0F); @Nonnull public static ComponentType getComponentType() { @@ -192,15 +193,27 @@ public class PlayerRef implements Component, MetricProvider, IMessa } @Nonnull + @Override public UUID getUuid() { return this.uuid; } @Nonnull + @Override public String getUsername() { return this.username; } + @Override + public boolean hasPermission(@Nonnull String id) { + return PermissionsModule.get().hasPermission(this.getUuid(), id); + } + + @Override + public boolean hasPermission(@Nonnull String id, boolean def) { + return PermissionsModule.get().hasPermission(this.getUuid(), id, def); + } + @Nonnull public PacketHandler getPacketHandler() { return this.packetHandler; @@ -236,14 +249,14 @@ public class PlayerRef implements Component, MetricProvider, IMessa } @Nonnull - public Vector3f getHeadRotation() { + public Rotation3f getHeadRotation() { return this.headRotation; } - public void updatePosition(@Nonnull World world, @Nonnull Transform transform, @Nonnull Vector3f headRotation) { + public void updatePosition(@Nonnull World world, @Nonnull Transform transform, @Nonnull Rotation3f headRotation) { this.worldUuid = world.getWorldConfig().getUuid(); - this.transform.assign(transform); - this.headRotation.assign(headRotation); + this.transform.set(transform); + this.headRotation.set(headRotation); } @Deprecated diff --git a/src/com/hypixel/hytale/server/core/universe/StorageManager.java b/src/com/hypixel/hytale/server/core/universe/StorageManager.java new file mode 100644 index 00000000..1b445619 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/universe/StorageManager.java @@ -0,0 +1,202 @@ +package com.hypixel.hytale.server.core.universe; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.StampedLock; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class StorageManager { + private final BooleanSupplier isSavingLocked; + private final StampedLock lock = new StampedLock(); + private final Map pendingOps = new HashMap<>(); + + public StorageManager(BooleanSupplier isSavingLocked) { + this.isSavingLocked = isSavingLocked; + } + + public boolean hasQueuedSave(@Nonnull Path path) { + path = path.normalize(); + long stamp = this.lock.readLock(); + + boolean var5; + try { + StorageManager.PendingOp pending = this.pendingOps.get(path); + var5 = pending != null && pending.activeLoads == 0; + } finally { + this.lock.unlockRead(stamp); + } + + return var5; + } + + private void clearSave(@Nonnull Path path) { + long stamp = this.lock.writeLock(); + + try { + this.pendingOps.remove(path); + } finally { + this.lock.unlockWrite(stamp); + } + } + + public CompletableFuture doSave(@Nonnull Path path, @Nonnull Supplier> saveFunc) { + path = path.normalize(); + long stamp = this.lock.writeLock(); + + CompletableFuture completedFuture; + try { + StorageManager.PendingOp pending = this.pendingOps.get(path); + if (pending == null) { + completedFuture = new CompletableFuture<>(); + if (this.isSavingLocked.getAsBoolean()) { + pending = new StorageManager.PendingOp(completedFuture, saveFunc); + } else { + saveFunc.get().whenCompleteAsync((v, e) -> { + this.clearSave(path); + if (e != null) { + completedFuture.completeExceptionally(e); + } else { + completedFuture.complete(v); + } + }); + pending = new StorageManager.PendingOp(completedFuture, null); + } + + this.pendingOps.put(path, pending); + return completedFuture; + } + + if (pending.doSave == null) { + return pending.onComplete = pending.onComplete.thenCompose(v -> this.doSave(path, saveFunc)); + } + + pending.doSave = saveFunc; + completedFuture = pending.onComplete; + } finally { + this.lock.unlockWrite(stamp); + } + + return completedFuture; + } + + public CompletableFuture doLoad(@Nonnull Path path, @Nonnull Supplier> loadFunc) { + path = path.normalize(); + long stamp = this.lock.writeLock(); + + CompletableFuture loadBarrier; + try { + StorageManager.PendingOp pending = this.pendingOps.get(path); + if (pending == null || pending.activeLoads != 0) { + CompletableFuture loadResult = loadFunc.get(); + if (pending == null) { + loadBarrier = new CompletableFuture<>(); + pending = new StorageManager.PendingOp(loadBarrier, null); + pending.loadBarrier = loadBarrier; + pending.activeLoads = 1; + this.pendingOps.put(path, pending); + } else { + pending.activeLoads++; + } + + StorageManager.PendingOp finalPending = pending; + loadResult.whenCompleteAsync((v, e) -> { + CompletableFuture barrier = null; + long s = this.lock.writeLock(); + + try { + finalPending.activeLoads--; + if (finalPending.activeLoads == 0) { + if (this.pendingOps.get(path) == finalPending) { + this.pendingOps.remove(path); + } + + barrier = finalPending.loadBarrier; + } + } finally { + this.lock.unlockWrite(s); + } + + if (barrier != null) { + barrier.complete(null); + } + }); + return loadResult; + } + + loadBarrier = pending.onComplete.thenCompose(v -> this.doLoad(path, loadFunc)); + } finally { + this.lock.unlockWrite(stamp); + } + + return (CompletableFuture)loadBarrier; + } + + public CompletableFuture pendingOperations() { + long stamp = this.lock.readLock(); + + CompletableFuture var9; + try { + ArrayList> futures = new ArrayList<>(); + + for (StorageManager.PendingOp pending : this.pendingOps.values()) { + if (pending.doSave == null && pending.activeLoads <= 0) { + futures.add(pending.onComplete); + } + } + + var9 = CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)); + } finally { + this.lock.unlockRead(stamp); + } + + return var9; + } + + public void onSavingRelease() { + long stamp = this.lock.writeLock(); + + try { + for (Entry e : this.pendingOps.entrySet()) { + Path path = e.getKey(); + StorageManager.PendingOp pending = e.getValue(); + if (pending.doSave != null) { + Supplier> save = pending.doSave; + pending.doSave = null; + CompletableFuture completedFuture = pending.onComplete; + save.get().whenCompleteAsync((v, ex) -> { + this.clearSave(path); + if (ex != null) { + completedFuture.completeExceptionally(ex); + } else { + completedFuture.complete(v); + } + }); + } + } + } finally { + this.lock.unlockWrite(stamp); + } + } + + private static final class PendingOp { + @Nonnull + private CompletableFuture onComplete; + @Nullable + private Supplier> doSave; + @Nullable + private CompletableFuture loadBarrier; + private int activeLoads; + + private PendingOp(@Nonnull CompletableFuture onComplete, @Nullable Supplier> doSave) { + this.onComplete = onComplete; + this.doSave = doSave; + } + } +} diff --git a/src/com/hypixel/hytale/server/core/universe/Universe.java b/src/com/hypixel/hytale/server/core/universe/Universe.java index 64f0c8b9..2083e84c 100644 --- a/src/com/hypixel/hytale/server/core/universe/Universe.java +++ b/src/com/hypixel/hytale/server/core/universe/Universe.java @@ -18,10 +18,13 @@ import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.event.IEventDispatcher; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.vector.Transform; import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricResults; import com.hypixel.hytale.metrics.MetricsRegistry; +import com.hypixel.hytale.protocol.FormattedMessage; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.PlayerSkin; import com.hypixel.hytale.protocol.ToClientPacket; @@ -33,7 +36,9 @@ import com.hypixel.hytale.server.core.HytaleServerConfig; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.NameMatching; import com.hypixel.hytale.server.core.Options; +import com.hypixel.hytale.server.core.ShutdownReason; import com.hypixel.hytale.server.core.auth.PlayerAuthentication; +import com.hypixel.hytale.server.core.command.system.CommandManager; import com.hypixel.hytale.server.core.command.system.CommandRegistry; import com.hypixel.hytale.server.core.config.BackupConfig; import com.hypixel.hytale.server.core.cosmetics.CosmeticsModule; @@ -80,7 +85,10 @@ import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider; import com.hypixel.hytale.server.core.universe.world.spawn.IndividualSpawnProvider; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.core.universe.world.storage.IChunkLoader; +import com.hypixel.hytale.server.core.universe.world.storage.IChunkSaver; import com.hypixel.hytale.server.core.universe.world.storage.component.ChunkSavingSystems; +import com.hypixel.hytale.server.core.universe.world.storage.provider.BackupChunkLoader; import com.hypixel.hytale.server.core.universe.world.storage.provider.DefaultChunkStorageProvider; import com.hypixel.hytale.server.core.universe.world.storage.provider.EmptyChunkStorageProvider; import com.hypixel.hytale.server.core.universe.world.storage.provider.IChunkStorageProvider; @@ -110,12 +118,20 @@ import io.netty.handler.codec.quic.QuicChannel; import io.netty.handler.codec.quic.QuicStreamChannel; import io.netty.handler.codec.quic.QuicStreamType; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; @@ -129,8 +145,11 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiPredicate; import java.util.logging.Level; +import java.util.stream.Stream; import javax.annotation.CheckReturnValue; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -154,7 +173,9 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv private final Path worldsPath = this.path.resolve("worlds"); private final Path worldsDeletedPath = this.worldsPath.resolveSibling("worlds-deleted"); @Nonnull - private final Map players = new ConcurrentHashMap<>(); + private final Map playersByUuid = new ConcurrentHashMap<>(); + @Nonnull + private final Collection players = Collections.unmodifiableCollection(this.playersByUuid.values()); @Nonnull private final Map worlds = new ConcurrentHashMap<>(); @Nonnull @@ -166,6 +187,8 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv private ResourceType worldMarkersResourceType; private CompletableFuture universeReady; private final AtomicBoolean isBackingUp = new AtomicBoolean(false); + private final StorageManager storageManager = new StorageManager(this::isSavingLocked); + private final AtomicInteger savingLocks = new AtomicInteger(0); public static Universe get() { return instance; @@ -212,20 +235,46 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv @Nonnull public CompletableFuture runBackup() { Path backupDir = HytaleServer.get().getConfig().getBackupConfig().getDirectory(); - return backupDir == null - ? CompletableFuture.failedFuture(new IllegalStateException("Backup directory not configured")) - : CompletableFuture.allOf(this.worlds.values().stream().map(world -> CompletableFuture.supplyAsync(() -> { - Store componentStore = world.getChunkStore().getStore(); - ChunkSavingSystems.Data data = componentStore.getResource(ChunkStore.SAVE_RESOURCE); - data.isSaving = false; - return data; - }, world).thenCompose(ChunkSavingSystems.Data::waitForSavingChunks)).toArray(CompletableFuture[]::new)) - .thenCompose(aVoid -> BackupTask.start(this.path, backupDir)) - .thenCompose(success -> CompletableFuture.allOf(this.worlds.values().stream().map(world -> CompletableFuture.runAsync(() -> { - Store componentStore = world.getChunkStore().getStore(); - ChunkSavingSystems.Data data = componentStore.getResource(ChunkStore.SAVE_RESOURCE); - data.isSaving = true; - }, world)).toArray(CompletableFuture[]::new)).thenApply(aVoid -> success)); + if (backupDir == null) { + return CompletableFuture.failedFuture(new IllegalStateException("Backup directory not configured")); + } else { + this.lockSaving(); + return this.storageManager + .pendingOperations() + .thenCompose( + v -> CompletableFuture.allOf(this.worlds.values().stream().map(world -> CompletableFuture.supplyAsync(() -> { + Store componentStore = world.getChunkStore().getStore(); + ChunkSavingSystems.Data data = componentStore.getResource(ChunkStore.SAVE_RESOURCE); + world.getChunkStore().pauseBackgroundSaving(data); + world.lockSaving(); + return data; + }, world).thenCompose(ChunkSavingSystems.Data::waitForSavingChunks)).toArray(CompletableFuture[]::new)) + .thenCompose(aVoid -> BackupTask.start(this.path, backupDir)) + .thenCompose(success -> CompletableFuture.allOf(this.worlds.values().stream().map(world -> CompletableFuture.runAsync(() -> { + world.getChunkStore().resumeBackgroundSaving(); + world.unlockSaving(); + }, world)).toArray(CompletableFuture[]::new)).thenApply(aVoid -> success)) + ) + .whenComplete((u, throwable) -> this.unlockSaving()); + } + } + + public boolean isSavingLocked() { + return this.savingLocks.get() > 0; + } + + public void lockSaving() { + this.savingLocks.incrementAndGet(); + } + + public void unlockSaving() { + int result = this.savingLocks.decrementAndGet(); + + assert result >= 0 : "savingLocks underflow"; + + if (result == 0) { + this.storageManager.onSavingRelease(); + } } @Override @@ -304,8 +353,8 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv LEGACY_BLOCK_ID_MAP = Collections.unmodifiableMap(map); } - } catch (IOException var15) { - this.getLogger().at(Level.SEVERE).withCause(var15).log("Failed to delete blockIdMap.json"); + } catch (IOException var21) { + this.getLogger().at(Level.SEVERE).withCause(var21).log("Failed to delete blockIdMap.json"); } if (Options.getOptionSet().has(Options.BARE)) { @@ -318,48 +367,303 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv if (Files.exists(this.worldsDeletedPath)) { FileUtil.deleteDirectory(this.worldsDeletedPath); } - } catch (Throwable var14) { - throw new RuntimeException("Failed to complete deletion of " + this.worldsDeletedPath.toAbsolutePath(), var14); + } catch (Throwable var16) { + throw new RuntimeException("Failed to complete deletion of " + this.worldsDeletedPath.toAbsolutePath(), var16); } try { Files.createDirectories(this.worldsPath); + if (Options.getOptionSet().has(Options.VERIFY_WORLDS)) { + boolean isRecovery = Options.getOptionSet().has(Options.RECOVERY_MODE); - try (DirectoryStream stream = Files.newDirectoryStream(this.worldsPath)) { - for (Path file : stream) { - if (HytaleServer.get().isShuttingDown()) { - return; - } + try (DirectoryStream stream = Files.newDirectoryStream(this.worldsPath)) { + for (Path file : stream) { + if (HytaleServer.get().isShuttingDown()) { + return; + } - if (!file.equals(this.worldsPath) && Files.isDirectory(file)) { - String name = file.getFileName().toString(); - if (this.getWorld(name) == null) { - loadingWorlds.add(this.loadWorldFromStart(file, name).exceptionally(throwable -> { - this.getLogger().at(Level.SEVERE).withCause(throwable).log("Failed to load world: %s", name); - return null; - })); - } else { - this.getLogger().at(Level.SEVERE).log("Skipping loading world '%s' because it already exists!", name); + if (!file.equals(this.worldsPath) && Files.isDirectory(file)) { + Path recoveryPath = isRecovery ? file.resolve("recovery-tmp") : null; + if (recoveryPath != null) { + if (Files.exists(recoveryPath)) { + FileUtil.deleteDirectory(recoveryPath); + } + + Files.createDirectories(recoveryPath); + } + + String name = file.getFileName().toString(); + + try { + AtomicReference worldCfg = new AtomicReference<>(); + IntIntPair result = worldConfigProvider.load(file, name) + .thenApplyAsync(SneakyThrow.sneakyFunction(cfg -> { + if (recoveryPath != null) { + cfg.getChunkStorageProvider().beginRecovery(file, recoveryPath); + } + + worldCfg.set(cfg); + return cfg; + })) + .thenCompose(v -> this.makeWorld(name, file, v)) + .thenCompose(SneakyThrow.sneakyFunction(world -> this.verifyWorld(world, recoveryPath))) + .whenComplete((v, ex) -> { + WorldConfig cfg = worldCfg.get(); + if (recoveryPath != null && cfg != null) { + try { + if (ex == null) { + if (Files.exists(recoveryPath)) { + FileUtil.deleteDirectory(recoveryPath); + } + } else { + cfg.getChunkStorageProvider().revertRecovery(file, recoveryPath); + } + } catch (IOException var7x) { + throw SneakyThrow.sneakyThrow(var7x); + } + } + }) + .join(); + if (result.leftInt() > 0) { + this.getLogger() + .at(Level.SEVERE) + .log("Failed to verify world " + name + ", %d/%d chunks corrupted", result.leftInt(), result.rightInt()); + HytaleServer.get() + .shutdownServer( + ShutdownReason.VERIFY_ERROR + .withMessage( + Message.translation("client.disconnection.shutdownReason.verifyError.chunksCorrupted") + .param("world", name) + .param("corrupted", result.leftInt()) + .param("total", result.rightInt()) + ) + ); + return; + } + } catch (Exception var17) { + this.getLogger().at(Level.SEVERE).withCause(var17).log("Failed to %s world %s", isRecovery ? "recover" : "verify", name); + HytaleServer.get() + .shutdownServer( + ShutdownReason.VERIFY_ERROR + .withMessage( + Message.translation("client.disconnection.shutdownReason.verifyError.detail") + .param("action", isRecovery ? "recover" : "verify") + .param("world", name) + .param("detail", var17.getMessage()) + ) + ); + return; + } } } } + + HytaleServer.get().shutdownServer(ShutdownReason.SHUTDOWN); + } else { + try (DirectoryStream stream = Files.newDirectoryStream(this.worldsPath)) { + for (Path file : stream) { + if (HytaleServer.get().isShuttingDown()) { + return; + } + + if (!file.equals(this.worldsPath) && Files.isDirectory(file)) { + String name = file.getFileName().toString(); + if (this.getWorld(name) == null) { + loadingWorlds.add(this.loadWorldFromStart(file, name).exceptionally(throwable -> { + this.getLogger().at(Level.SEVERE).withCause(throwable).log("Failed to load world: %s", name); + return null; + })); + } else { + this.getLogger().at(Level.SEVERE).log("Skipping loading world '%s' because it already exists!", name); + } + } + } + } + + this.universeReady = CompletableFutureUtil._catch( + CompletableFuture.allOf(loadingWorlds.toArray(CompletableFuture[]::new)) + .thenCompose( + v -> { + String worldName = config.getDefaults().getWorld(); + return worldName != null && !this.worlds.containsKey(worldName.toLowerCase()) + ? CompletableFutureUtil._catch(this.addWorld(worldName)) + : CompletableFuture.completedFuture(null); + } + ) + .thenRun(() -> HytaleServer.get().getEventBus().dispatch(AllWorldsLoadedEvent.class)) + ); + } + } catch (IOException var20) { + throw new RuntimeException("Failed to load Worlds", var20); + } + } + } + } + + private CompletableFuture verifyWorld(World world, @Nullable Path recoveryPath) throws IOException { + ChunkStore store = world.getChunkStore(); + IChunkLoader loader = recoveryPath == null + ? store.getLoader() + : world.getWorldConfig().getChunkStorageProvider().getRecoveryLoader(store.getStore(), recoveryPath); + IChunkSaver saver = store.getSaver(); + if (loader != null && saver != null) { + LongSet chunks = loader.getIndexes(); + IChunkLoader fallbackLoader; + if (Options.getOptionSet().valueOf(Options.RECOVERY_MODE) == Options.RecoveryMode.FROM_BACKUP_OR_REGENERATE) { + Path backupDir = HytaleServer.get().getConfig().getBackupConfig().getDirectory(); + if (backupDir == null || !Files.exists(backupDir)) { + Path legacyPath = this.path.resolve("../backup"); + if (Files.exists(legacyPath)) { + backupDir = legacyPath; + } + } + + List backups = new ArrayList<>(); + if (backupDir == null) { + return CompletableFuture.failedFuture(new RuntimeException("No usable backups")); + } + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); + collectBackupZips(backupDir, formatter, backups); + collectBackupZips(backupDir.resolve("archive"), formatter, backups); + backups.sort(Comparator.comparing(p -> p.getFileName().toString()).reversed()); + fallbackLoader = new BackupChunkLoader(store, backups); + } else { + fallbackLoader = null; + } + + AtomicInteger completed = new AtomicInteger(); + AtomicInteger corrupted = new AtomicInteger(); + int total = chunks.size(); + return this.verifyAllChunks(world, loader, saver, fallbackLoader, chunks.iterator(), completed, corrupted, total) + .thenApply(v -> IntIntPair.of(corrupted.get(), total)) + .whenCompleteAsync((intIntPair, throwable) -> { + try { + saver.flush(); + if (recoveryPath != null) { + loader.close(); + } + } catch (IOException var10x) { + throw SneakyThrow.sneakyThrow(var10x); } - this.universeReady = CompletableFutureUtil._catch( - CompletableFuture.allOf(loadingWorlds.toArray(CompletableFuture[]::new)) - .thenCompose( - v -> { - String worldName = config.getDefaults().getWorld(); - return worldName != null && !this.worlds.containsKey(worldName.toLowerCase()) - ? CompletableFutureUtil._catch(this.addWorld(worldName)) - : CompletableFuture.completedFuture(null); - } - ) - .thenRun(() -> HytaleServer.get().getEventBus().dispatch(AllWorldsLoadedEvent.class)) - ); - } catch (IOException var13) { - throw new RuntimeException("Failed to load Worlds", var13); - } + this.removeWorld(world.getName()); + if (fallbackLoader != null) { + try { + fallbackLoader.close(); + } catch (IOException var9x) { + throw SneakyThrow.sneakyThrow(var9x); + } + } + }); + } else { + return CompletableFuture.failedFuture(new RuntimeException("Failed to load World")); + } + } + + private CompletableFuture verifyAllChunks( + @Nonnull World world, + @Nonnull IChunkLoader loader, + @Nonnull IChunkSaver saver, + @Nullable IChunkLoader fallbackLoader, + @Nonnull LongIterator iterator, + @Nonnull AtomicInteger completed, + @Nonnull AtomicInteger corrupted, + int total + ) { + CompletableFuture result = new CompletableFuture<>(); + this.verifyNextChunk(result, world, loader, saver, fallbackLoader, iterator, completed, corrupted, total); + return result; + } + + private void verifyNextChunk( + @Nonnull CompletableFuture result, + @Nonnull World world, + @Nonnull IChunkLoader loader, + @Nonnull IChunkSaver saver, + @Nullable IChunkLoader fallbackLoader, + @Nonnull LongIterator iterator, + @Nonnull AtomicInteger completed, + @Nonnull AtomicInteger corrupted, + int total + ) { + if (!iterator.hasNext()) { + result.complete(null); + } else { + long index = iterator.nextLong(); + int x = ChunkUtil.xOfChunkIndex(index); + int z = ChunkUtil.zOfChunkIndex(index); + Options.RecoveryMode mode = Options.getOptionSet().valueOf(Options.RECOVERY_MODE); + loader.loadHolder(x, z) + .thenCompose(v -> saver.saveHolder(x, z, (Holder)v)) + .exceptionallyCompose( + t -> { + if (mode == null) { + corrupted.incrementAndGet(); + return CompletableFuture.completedFuture(null); + } else { + return switch (mode) { + case REGENERATE -> saver.removeHolder(x, z); + case FROM_BACKUP_OR_REGENERATE -> fallbackLoader == null + ? CompletableFuture.failedFuture( + new RuntimeException("Recovery of individual chunks from backups not supported by storage type. Please restore instead.") + ) + : saver.removeHolder(x, z).thenCompose(v -> fallbackLoader.loadHolder(x, z)).thenCompose(v -> { + if (v == null) { + this.getLogger().atWarning().log("Failed to recover a chunk at %d, %d", x, z); + return CompletableFuture.completedFuture(null); + } else { + this.getLogger().atInfo().log("Managed to recover a chunk at %d, %d", x, z); + return saver.saveHolder(x, z, (Holder)v); + } + }).exceptionally(t1 -> { + this.getLogger().atWarning().log("Failed to recover a chunk at %d, %d", x, z); + return null; + }); + }; + } + } + ) + .thenAccept( + ignored -> { + int done = completed.incrementAndGet(); + if (done % 100 == 0 || done == total) { + String msg = String.format("%s %d/%d chunks for world %s", mode == null ? "Verified" : "Recovering", done, total, world.getName()); + this.getLogger().atInfo().log(msg); + String statusKey = mode == null ? "client.gameLoadingView.status.verifiedChunks" : "client.gameLoadingView.status.recoveringChunks"; + double progress = MathUtil.round((double)done / total, 2) * 100.0; + HytaleServer.get() + .reportSingleplayerStatus( + Message.translation(statusKey).param("done", done).param("total", total).param("name", world.getName()), progress + ); + } + } + ) + .whenComplete((v, throwable) -> { + if (throwable != null) { + result.completeExceptionally(throwable); + } else { + this.verifyNextChunk(result, world, loader, saver, fallbackLoader, iterator, completed, corrupted, total); + } + }); + } + } + + private static void collectBackupZips(@Nonnull Path dir, @Nonnull DateTimeFormatter formatter, @Nonnull List out) { + if (Files.isDirectory(dir)) { + try (Stream stream = Files.list(dir)) { + stream.filter(p -> Files.isRegularFile(p) && p.getFileName().toString().endsWith(".zip")).filter(p -> { + String name = p.getFileName().toString(); + name = name.substring(0, name.length() - 4); + + try { + LocalDateTime.parse(name, formatter); + return true; + } catch (DateTimeParseException var4) { + return false; + } + }).forEach(out::add); + } catch (IOException var8) { } } } @@ -371,7 +675,11 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv } public void disconnectAllPLayers() { - this.players.values().forEach(player -> player.getPacketHandler().disconnect("Stopping server!")); + ShutdownReason reason = HytaleServer.get().getShutdownReason(); + FormattedMessage message = reason != null && reason.getFormattedMessage() != null + ? reason.getFormattedMessage() + : Message.translation("server.general.disconnect.stoppingServer").getFormattedMessage(); + this.playersByUuid.values().forEach(player -> player.getPacketHandler().disconnect(message)); } public void shutdownAllWorlds() { @@ -595,8 +903,6 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv if (event.isCancelled()) { return false; } else { - this.worlds.remove(nameLower); - this.worldsByUuid.remove(world.getWorldConfig().getUuid()); if (world.isAlive()) { if (world.isInThread()) { world.stopIndividualWorld(); @@ -605,6 +911,8 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv } } + this.worlds.remove(nameLower); + this.worldsByUuid.remove(world.getWorldConfig().getUuid()); world.validateDeleteOnRemove(); return true; } @@ -623,8 +931,6 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv .getEventBus() .dispatchFor(RemoveWorldEvent.class, name) .dispatch(new RemoveWorldEvent(world, RemoveWorldEvent.RemovalReason.EXCEPTIONAL)); - this.worlds.remove(nameLower); - this.worldsByUuid.remove(world.getWorldConfig().getUuid()); if (world.isAlive()) { if (world.isInThread()) { world.stopIndividualWorld(players); @@ -633,6 +939,8 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv } } + this.worlds.remove(nameLower); + this.worldsByUuid.remove(world.getWorldConfig().getUuid()); world.validateDeleteOnRemove(); } } @@ -656,37 +964,37 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv } @Nonnull - public List getPlayers() { - return new ObjectArrayList<>(this.players.values()); + public Collection getPlayers() { + return this.players; } @Nullable public PlayerRef getPlayer(@Nonnull UUID uuid) { - return this.players.get(uuid); + return this.playersByUuid.get(uuid); } @Nullable public PlayerRef getPlayer(@Nonnull String value, @Nonnull NameMatching matching) { - return matching.find(this.players.values(), value, v -> v.getComponent(PlayerRef.getComponentType()).getUsername()); + return matching.find(this.playersByUuid.values(), value, v -> v.getComponent(PlayerRef.getComponentType()).getUsername()); } @Nullable public PlayerRef getPlayer(@Nonnull String value, @Nonnull Comparator comparator, @Nonnull BiPredicate equality) { - return NameMatching.find(this.players.values(), value, v -> v.getComponent(PlayerRef.getComponentType()).getUsername(), comparator, equality); + return NameMatching.find(this.playersByUuid.values(), value, v -> v.getComponent(PlayerRef.getComponentType()).getUsername(), comparator, equality); } @Nullable public PlayerRef getPlayerByUsername(@Nonnull String value, @Nonnull NameMatching matching) { - return matching.find(this.players.values(), value, PlayerRef::getUsername); + return matching.find(this.playersByUuid.values(), value, PlayerRef::getUsername); } @Nullable public PlayerRef getPlayerByUsername(@Nonnull String value, @Nonnull Comparator comparator, @Nonnull BiPredicate equality) { - return NameMatching.find(this.players.values(), value, PlayerRef::getUsername, comparator, equality); + return NameMatching.find(this.playersByUuid.values(), value, PlayerRef::getUsername, comparator, equality); } public int getPlayerCount() { - return this.players.size(); + return this.playersByUuid.size(); } @Nonnull @@ -749,7 +1057,7 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv holder.putComponent(ModelComponent.getComponentType(), new ModelComponent(CosmeticsModule.get().createModel(skin))); } - playerConnection.setPlayerRef(playerRefComponent, playerComponent); + playerConnection.setPlayerRef(playerRefComponent); NettyUtil.setChannelHandler(channel, playerConnection); playerComponent.setClientViewRadius(clientViewRadiusChunks); EntityTrackerSystems.EntityViewer entityViewerComponent = holder.getComponent(EntityTrackerSystems.EntityViewer.getComponentType()); @@ -760,10 +1068,10 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv holder.addComponent(EntityTrackerSystems.EntityViewer.getComponentType(), entityViewerComponent); } - PlayerRef existingPlayer = this.players.putIfAbsent(uuid, playerRefComponent); + PlayerRef existingPlayer = this.playersByUuid.putIfAbsent(uuid, playerRefComponent); if (existingPlayer != null) { this.getLogger().at(Level.WARNING).log("Player '%s' (%s) already joining from another connection, rejecting duplicate", username, uuid); - playerConnection.disconnect("A connection with this account is already in progress"); + playerConnection.disconnect(Message.translation("client.general.disconnect.accountAlreadyConnecting")); return CompletableFuture.completedFuture(null); } else { String lastWorldName = playerConfig.getWorld(); @@ -773,19 +1081,19 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv .dispatchFor(PlayerConnectEvent.class) .dispatch(new PlayerConnectEvent((Holder)holder, playerRefComponent, lastWorld != null ? lastWorld : this.getDefaultWorld())); if (!channel.isActive()) { - this.players.remove(uuid, playerRefComponent); + this.playersByUuid.remove(uuid, playerRefComponent); this.getLogger().at(Level.INFO).log("Player '%s' (%s) disconnected during PlayerConnectEvent, cleaned up", username, uuid); return CompletableFuture.completedFuture(null); } else { World world = event.getWorld() != null ? event.getWorld() : this.getDefaultWorld(); if (world == null) { - this.players.remove(uuid, playerRefComponent); - playerConnection.disconnect("No world available to join"); + this.playersByUuid.remove(uuid, playerRefComponent); + playerConnection.disconnect(Message.translation("client.general.disconnect.noWorldAvailable")); this.getLogger().at(Level.SEVERE).log("Player '%s' (%s) could not join - no default world configured", username, uuid); return CompletableFuture.completedFuture(null); } else { if (lastWorldName != null && lastWorld == null) { - playerComponent.sendMessage( + playerRefComponent.sendMessage( Message.translation("server.universe.failedToFindWorld").param("lastWorldName", lastWorldName).param("name", world.getName()) ); } @@ -794,7 +1102,7 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv playerRefComponent.getPacketHandler().write(new ServerTags(AssetRegistry.getClientTags())); CompletableFuture addPlayerFuture = world.addPlayer(playerRefComponent, null, false, false); if (addPlayerFuture == null) { - this.players.remove(uuid, playerRefComponent); + this.playersByUuid.remove(uuid, playerRefComponent); this.getLogger().at(Level.INFO).log("Player '%s' (%s) disconnected before world addition, cleaned up", username, uuid); return CompletableFuture.completedFuture(null); } else { @@ -806,21 +1114,22 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv playerComponent.remove(); } - this.players.remove(uuid, playerRefComponent); + this.playersByUuid.remove(uuid, playerRefComponent); this.getLogger() .at(Level.WARNING) .log("Player '%s' (%s) disconnected during world join, cleaned up from universe", username, uuid); return null; } else if (playerComponent.wasRemoved()) { - this.players.remove(uuid, playerRefComponent); + this.playersByUuid.remove(uuid, playerRefComponent); return null; } else { + CommandManager.get().broadcastArgCacheInvalidation("player_ref"); return (PlayerRef)p; } } ) .exceptionally(throwable -> { - this.players.remove(uuid, playerRefComponent); + this.playersByUuid.remove(uuid, playerRefComponent); playerComponent.remove(); throw new RuntimeException("Exception when adding player to universe:", throwable); }); @@ -885,14 +1194,19 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv } private void finalizePlayerRemoval(@Nonnull PlayerRef playerRef) { - this.players.remove(playerRef.getUuid()); + this.playersByUuid.remove(playerRef.getUuid()); + CommandManager.get().broadcastArgCacheInvalidation("player_ref"); if (Constants.SINGLEPLAYER) { - if (this.players.isEmpty()) { + if (this.playersByUuid.isEmpty()) { this.getLogger().at(Level.INFO).log("No players left on singleplayer server shutting down!"); HytaleServer.get().shutdownServer(); } else if (SingleplayerModule.isOwner(playerRef)) { this.getLogger().at(Level.INFO).log("Owner left the singleplayer server shutting down!"); - this.getPlayers().forEach(p -> p.getPacketHandler().disconnect(playerRef.getUsername() + " left! Shutting down singleplayer world!")); + this.getPlayers() + .forEach( + p -> p.getPacketHandler() + .disconnect(Message.translation("server.general.disconnect.singleplayerOwnerLeft").param("username", playerRef.getUsername())) + ); HytaleServer.get().shutdownServer(); } } @@ -956,7 +1270,7 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv holder.addComponent(EntityTrackerSystems.EntityViewer.getComponentType(), viewer); } - playerConnection.setPlayerRef(playerRef, newPlayer); + playerConnection.setPlayerRef(playerRef); playerRef.replaceHolder(holder); holder.putComponent(PlayerRef.getComponentType(), playerRef); }).thenCompose(v -> targetWorld.addPlayer(playerRef, transform)); @@ -964,29 +1278,33 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv @Override public void sendMessage(@Nonnull Message message) { - for (PlayerRef ref : this.players.values()) { + for (PlayerRef ref : this.playersByUuid.values()) { ref.sendMessage(message); } } public void broadcastPacket(@Nonnull ToClientPacket packet) { - for (PlayerRef player : this.players.values()) { + for (PlayerRef player : this.playersByUuid.values()) { player.getPacketHandler().write(packet); } } public void broadcastPacketNoCache(@Nonnull ToClientPacket packet) { - for (PlayerRef player : this.players.values()) { + for (PlayerRef player : this.playersByUuid.values()) { player.getPacketHandler().writeNoCache(packet); } } public void broadcastPacket(@Nonnull ToClientPacket... packets) { - for (PlayerRef player : this.players.values()) { + for (PlayerRef player : this.playersByUuid.values()) { player.getPacketHandler().write(packets); } } + public StorageManager getStorageManager() { + return this.storageManager; + } + public PlayerStorage getPlayerStorage() { return this.playerStorage; } diff --git a/src/com/hypixel/hytale/server/core/universe/playerdata/DiskPlayerStorageProvider.java b/src/com/hypixel/hytale/server/core/universe/playerdata/DiskPlayerStorageProvider.java index fe4e3fbb..c2bb75a3 100644 --- a/src/com/hypixel/hytale/server/core/universe/playerdata/DiskPlayerStorageProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/playerdata/DiskPlayerStorageProvider.java @@ -7,6 +7,7 @@ import com.hypixel.hytale.common.util.PathUtil; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.server.core.Constants; import com.hypixel.hytale.server.core.Options; +import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.BsonUtil; import java.io.IOException; @@ -16,6 +17,7 @@ import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; @@ -71,21 +73,40 @@ public class DiskPlayerStorageProvider implements PlayerStorageProvider { @Override public CompletableFuture> load(@Nonnull UUID uuid) { Path file = this.path.resolve(uuid + ".json"); - return BsonUtil.readDocument(file).thenApply(bsonDocument -> { + return Universe.get().getStorageManager().doLoad(file, () -> BsonUtil.readDocument(file).thenApply(bsonDocument -> { if (bsonDocument == null) { bsonDocument = new BsonDocument(); } return EntityStore.REGISTRY.deserialize(bsonDocument); - }); + })); } @Nonnull @Override - public CompletableFuture save(@Nonnull UUID uuid, @Nonnull Holder holder) { + public CompletableFuture save(@Nonnull UUID uuid, @Nonnull Holder holder, boolean required) { Path file = this.path.resolve(uuid + ".json"); - BsonDocument document = EntityStore.REGISTRY.serialize(holder); - return BsonUtil.writeDocument(file, document); + if (!required && Universe.get().getStorageManager().hasQueuedSave(file)) { + return CompletableFuture.completedFuture(null); + } else { + BsonDocument document = EntityStore.REGISTRY.serialize(holder); + return Universe.get().getStorageManager().doSave(file, () -> BsonUtil.writeDocument(file, document)); + } + } + + @Nonnull + @Override + public CompletableFuture update(@Nonnull UUID uuid, @Nonnull Consumer> modifier) { + Path file = this.path.resolve(uuid + ".json"); + return Universe.get().getStorageManager().doSave(file, () -> BsonUtil.readDocument(file).thenApply(bsonDocument -> { + if (bsonDocument == null) { + bsonDocument = new BsonDocument(); + } + + Holder holder = EntityStore.REGISTRY.deserialize(bsonDocument); + modifier.accept(holder); + return EntityStore.REGISTRY.serialize(holder); + }).thenCompose(document -> BsonUtil.writeDocument(file, document))); } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/universe/playerdata/PlayerStorage.java b/src/com/hypixel/hytale/server/core/universe/playerdata/PlayerStorage.java index 177e759e..abbd7ae4 100644 --- a/src/com/hypixel/hytale/server/core/universe/playerdata/PlayerStorage.java +++ b/src/com/hypixel/hytale/server/core/universe/playerdata/PlayerStorage.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; import javax.annotation.Nonnull; public interface PlayerStorage { @@ -13,7 +14,10 @@ public interface PlayerStorage { CompletableFuture> load(@Nonnull UUID var1); @Nonnull - CompletableFuture save(@Nonnull UUID var1, @Nonnull Holder var2); + CompletableFuture save(@Nonnull UUID var1, @Nonnull Holder var2, boolean var3); + + @Nonnull + CompletableFuture update(@Nonnull UUID var1, @Nonnull Consumer> var2); @Nonnull CompletableFuture remove(@Nonnull UUID var1); diff --git a/src/com/hypixel/hytale/server/core/universe/system/PlayerVelocityInstructionSystem.java b/src/com/hypixel/hytale/server/core/universe/system/PlayerVelocityInstructionSystem.java index 35be5332..15b54664 100644 --- a/src/com/hypixel/hytale/server/core/universe/system/PlayerVelocityInstructionSystem.java +++ b/src/com/hypixel/hytale/server/core/universe/system/PlayerVelocityInstructionSystem.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.dependency.SystemTypeDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ChangeVelocityType; import com.hypixel.hytale.protocol.packets.entities.ChangeVelocity; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; @@ -23,6 +22,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PlayerVelocityInstructionSystem extends EntityTickingSystem { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/universe/system/WorldConfigSaveSystem.java b/src/com/hypixel/hytale/server/core/universe/system/WorldConfigSaveSystem.java index 4b57bdc5..de022c3e 100644 --- a/src/com/hypixel/hytale/server/core/universe/system/WorldConfigSaveSystem.java +++ b/src/com/hypixel/hytale/server/core/universe/system/WorldConfigSaveSystem.java @@ -17,7 +17,9 @@ public class WorldConfigSaveSystem extends DelayedSystem { @Override public void delayedTick(float dt, int systemIndex, @Nonnull Store store) { World world = store.getExternalData().getWorld(); - saveWorldConfigAndResources(world).join(); + if (!world.isSavingLocked()) { + saveWorldConfigAndResources(world).join(); + } } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/universe/world/ParticleUtil.java b/src/com/hypixel/hytale/server/core/universe/world/ParticleUtil.java index 834779f1..8f344025 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/ParticleUtil.java +++ b/src/com/hypixel/hytale/server/core/universe/world/ParticleUtil.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.Color; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.Position; @@ -14,25 +13,26 @@ import com.hypixel.hytale.server.core.asset.type.particle.config.WorldParticle; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class ParticleUtil { public static final double DEFAULT_PARTICLE_DISTANCE = 75.0; public static void spawnParticleEffect(@Nonnull String name, @Nonnull Vector3d position, @Nonnull ComponentAccessor componentAccessor) { SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> playerRefs = SpatialResource.getThreadLocalReferenceList(); + List> playerRefs = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, 75.0, playerRefs); - spawnParticleEffect(name, position.getX(), position.getY(), position.getZ(), null, playerRefs, componentAccessor); + spawnParticleEffect(name, position.x(), position.y(), position.z(), null, playerRefs, componentAccessor); } public static void spawnParticleEffect( @Nonnull String name, @Nonnull Vector3d position, @Nonnull List> playerRefs, @Nonnull ComponentAccessor componentAccessor ) { - spawnParticleEffect(name, position.getX(), position.getY(), position.getZ(), null, playerRefs, componentAccessor); + spawnParticleEffect(name, position.x(), position.y(), position.z(), null, playerRefs, componentAccessor); } public static void spawnParticleEffect( @@ -42,49 +42,31 @@ public class ParticleUtil { @Nonnull List> playerRefs, @Nonnull ComponentAccessor componentAccessor ) { - spawnParticleEffect(name, position.getX(), position.getY(), position.getZ(), sourceRef, playerRefs, componentAccessor); + spawnParticleEffect(name, position.x(), position.y(), position.z(), sourceRef, playerRefs, componentAccessor); } public static void spawnParticleEffect( @Nonnull String name, @Nonnull Vector3d position, - @Nonnull Vector3f rotation, + @Nonnull Rotation3f rotation, @Nonnull List> playerRefs, @Nonnull ComponentAccessor componentAccessor ) { spawnParticleEffect( - name, - position.getX(), - position.getY(), - position.getZ(), - rotation.getYaw(), - rotation.getPitch(), - rotation.getRoll(), - null, - playerRefs, - componentAccessor + name, position.x(), position.y(), position.z(), rotation.yaw(), rotation.pitch(), rotation.roll(), null, playerRefs, componentAccessor ); } public static void spawnParticleEffect( @Nonnull String name, @Nonnull Vector3d position, - @Nonnull Vector3f rotation, + @Nonnull Rotation3f rotation, @Nullable Ref sourceRef, @Nonnull List> playerRefs, @Nonnull ComponentAccessor componentAccessor ) { spawnParticleEffect( - name, - position.getX(), - position.getY(), - position.getZ(), - rotation.getYaw(), - rotation.getPitch(), - rotation.getRoll(), - sourceRef, - playerRefs, - componentAccessor + name, position.x(), position.y(), position.z(), rotation.yaw(), rotation.pitch(), rotation.roll(), sourceRef, playerRefs, componentAccessor ); } @@ -98,7 +80,7 @@ public class ParticleUtil { @Nonnull List> playerRefs, @Nonnull ComponentAccessor componentAccessor ) { - spawnParticleEffect(name, position.getX(), position.getY(), position.getZ(), yaw, pitch, roll, sourceRef, playerRefs, componentAccessor); + spawnParticleEffect(name, position.x(), position.y(), position.z(), yaw, pitch, roll, sourceRef, playerRefs, componentAccessor); } public static void spawnParticleEffect( @@ -112,7 +94,7 @@ public class ParticleUtil { @Nonnull List> playerRefs, @Nonnull ComponentAccessor componentAccessor ) { - spawnParticleEffect(name, position.getX(), position.getY(), position.getZ(), yaw, pitch, roll, scale, color, null, playerRefs, componentAccessor); + spawnParticleEffect(name, position.x(), position.y(), position.z(), yaw, pitch, roll, scale, color, null, playerRefs, componentAccessor); } public static void spawnParticleEffect( @@ -156,7 +138,7 @@ public class ParticleUtil { @Nonnull List> playerRefs, @Nonnull ComponentAccessor componentAccessor ) { - com.hypixel.hytale.protocol.Vector3f positionOffset = particles.getPositionOffset(); + Vector3f positionOffset = particles.getPositionOffset(); if (positionOffset != null) { Vector3d offset = new Vector3d(positionOffset.x, positionOffset.y, positionOffset.z); offset.rotateY(yaw); @@ -178,9 +160,9 @@ public class ParticleUtil { if (systemId != null) { spawnParticleEffect( systemId, - position.getX(), - position.getY(), - position.getZ(), + position.x(), + position.y(), + position.z(), yaw, pitch, roll, diff --git a/src/com/hypixel/hytale/server/core/universe/world/SoundUtil.java b/src/com/hypixel/hytale/server/core/universe/world/SoundUtil.java index f9b52382..5e9766a1 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/SoundUtil.java +++ b/src/com/hypixel/hytale/server/core/universe/world/SoundUtil.java @@ -4,13 +4,13 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ItemSoundEvent; import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.protocol.SoundCategory; import com.hypixel.hytale.protocol.packets.world.PlaySoundEvent2D; import com.hypixel.hytale.protocol.packets.world.PlaySoundEvent3D; import com.hypixel.hytale.protocol.packets.world.PlaySoundEventEntity; +import com.hypixel.hytale.protocol.packets.world.PlaySoundEventLocalPlayer; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.itemsound.config.ItemSoundSet; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; @@ -20,10 +20,11 @@ import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SoundUtil { public static void playItemSoundEvent( @@ -65,6 +66,26 @@ public class SoundUtil { } } + public static void playLocalPlayerSoundEvent( + @Nonnull PlayerRef playerRefComponent, int localSoundEventIndex, int worldSoundEventIndex, @Nonnull SoundCategory soundCategory + ) { + playLocalPlayerSoundEvent(playerRefComponent, localSoundEventIndex, worldSoundEventIndex, soundCategory, 1.0F, 1.0F); + } + + public static void playLocalPlayerSoundEvent( + @Nonnull PlayerRef playerRefComponent, + int localSoundEventIndex, + int worldSoundEventIndex, + @Nonnull SoundCategory soundCategory, + float volumeModifier, + float pitchModifier + ) { + if (localSoundEventIndex != 0 || worldSoundEventIndex != 0) { + playerRefComponent.getPacketHandler() + .write(new PlaySoundEventLocalPlayer(localSoundEventIndex, worldSoundEventIndex, soundCategory, volumeModifier, pitchModifier)); + } + } + public static void playSoundEvent2d(int soundEventIndex, @Nonnull SoundCategory soundCategory, @Nonnull ComponentAccessor componentAccessor) { playSoundEvent2d(soundEventIndex, soundCategory, 1.0F, 1.0F, componentAccessor); } @@ -127,7 +148,7 @@ public class SoundUtil { SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource( EntityModule.get().getPlayerSpatialResourceType() ); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, soundEvent.getMaxDistance(), results); for (Ref playerRef : results) { @@ -146,7 +167,7 @@ public class SoundUtil { public static void playSoundEvent3d( int soundEventIndex, @Nonnull SoundCategory soundCategory, @Nonnull Vector3d position, @Nonnull ComponentAccessor componentAccessor ) { - playSoundEvent3d(soundEventIndex, soundCategory, position.getX(), position.getY(), position.getZ(), componentAccessor); + playSoundEvent3d(soundEventIndex, soundCategory, position.x(), position.y(), position.z(), componentAccessor); } public static void playSoundEvent3d( @@ -179,7 +200,7 @@ public class SoundUtil { SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource( EntityModule.get().getPlayerSpatialResourceType() ); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(new Vector3d(x, y, z), soundEvent.getMaxDistance(), results); for (Ref playerRef : results) { @@ -198,7 +219,7 @@ public class SoundUtil { public static void playSoundEvent3d( @Nullable Ref sourceRef, int soundEventIndex, @Nonnull Vector3d pos, @Nonnull ComponentAccessor componentAccessor ) { - playSoundEvent3d(sourceRef, soundEventIndex, pos.getX(), pos.getY(), pos.getZ(), componentAccessor); + playSoundEvent3d(sourceRef, soundEventIndex, pos.x(), pos.y(), pos.z(), componentAccessor); } public static void playSoundEvent3d( @@ -214,7 +235,7 @@ public class SoundUtil { boolean ignoreSource, @Nonnull ComponentAccessor componentAccessor ) { - playSoundEvent3d(sourceRef, soundEventIndex, position.getX(), position.getY(), position.getZ(), ignoreSource, componentAccessor); + playSoundEvent3d(sourceRef, soundEventIndex, position.x(), position.y(), position.z(), ignoreSource, componentAccessor); } public static void playSoundEvent3d( @@ -278,7 +299,7 @@ public class SoundUtil { assert transformComponent != null; - if (transformComponent.getPosition().distanceSquaredTo(x, y, z) <= maxDistance * maxDistance) { + if (transformComponent.getPosition().distanceSquared(x, y, z) <= maxDistance * maxDistance) { PlayerRef playerRefComponent = componentAccessor.getComponent(playerRef, PlayerRef.getComponentType()); assert playerRefComponent != null; diff --git a/src/com/hypixel/hytale/server/core/universe/world/SpawnUtil.java b/src/com/hypixel/hytale/server/core/universe/world/SpawnUtil.java index 745b05db..6ec8472e 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/SpawnUtil.java +++ b/src/com/hypixel/hytale/server/core/universe/world/SpawnUtil.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.server.core.universe.world; import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider; @@ -21,7 +21,7 @@ public final class SpawnUtil { return null; } else { Transform spawnPoint = spawnProvider.getSpawnPoint(world, playerUuid); - Vector3f bodyRotation = new Vector3f(0.0F, spawnPoint.getRotation().getYaw(), 0.0F); + Rotation3f bodyRotation = new Rotation3f(0.0F, spawnPoint.getRotation().yaw(), 0.0F); TransformComponent transformComponent = new TransformComponent(spawnPoint.getPosition(), bodyRotation); holder.addComponent(TransformComponent.getComponentType(), transformComponent); HeadRotation headRotationComponent = holder.ensureAndGetComponent(HeadRotation.getComponentType()); @@ -36,7 +36,7 @@ public final class SpawnUtil { assert transformComponent != null; transformComponent.setPosition(transform.getPosition()); - transformComponent.getRotation().setYaw(transform.getRotation().getYaw()); + transformComponent.getRotation().setYaw(transform.getRotation().yaw()); HeadRotation headRotationComponent = holder.ensureAndGetComponent(HeadRotation.getComponentType()); headRotationComponent.teleportRotation(transform.getRotation()); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/World.java b/src/com/hypixel/hytale/server/core/universe/world/World.java index c4ee429d..203b75bc 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/World.java +++ b/src/com/hypixel/hytale/server/core/universe/world/World.java @@ -2,6 +2,7 @@ package com.hypixel.hytale.server.core.universe.world; import com.hypixel.hytale.assetstore.AssetRegistry; import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.common.plugin.PluginIdentifier; import com.hypixel.hytale.common.util.FormatUtil; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.ComponentAccessor; @@ -17,9 +18,8 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.logger.sentry.SkipSentryException; import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.metrics.ExecutorMetricsRegistry; import com.hypixel.hytale.metrics.metric.HistoricMetric; import com.hypixel.hytale.protocol.packets.entities.SetEntitySeed; @@ -96,6 +96,7 @@ import java.util.EnumMap; import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Random; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -112,6 +113,7 @@ import java.util.function.IntUnaryOperator; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class World extends TickingThread implements Executor, ExecutorMetricsRegistry.ExecutorMetric, ChunkAccessor, IWorldChunks, IMessageReceiver { public static final float SAVE_INTERVAL = 10.0F; @@ -163,6 +165,7 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg @Nonnull private final Map features = Collections.synchronizedMap(new EnumMap<>(ClientFeature.class)); private volatile boolean gcHasRun; + private final AtomicInteger savingLocks = new AtomicInteger(0); public World(@Nonnull String name, @Nonnull Path savePath, @Nonnull WorldConfig worldConfig) throws IOException { super("WorldThread - " + name); @@ -217,13 +220,15 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg return this; } catch (WorldGenLoadException var3x) { if (this.name.equals(HytaleServer.get().getConfig().getDefaults().getWorld())) { - HytaleServer.get().shutdownServer(ShutdownReason.WORLD_GEN.withMessage(var3x.getTraceMessage("\n"))); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.worldGen.detail").param("detail", var3x.getTraceMessage("\n")); + HytaleServer.get().shutdownServer(ShutdownReason.WORLD_GEN.withMessage(reasonMessage)); } throw new SkipSentryException("Failed to load WorldGen!", var3x); } catch (WorldMapLoadException var4) { if (this.name.equals(HytaleServer.get().getConfig().getDefaults().getWorld())) { - HytaleServer.get().shutdownServer(ShutdownReason.WORLD_GEN.withMessage(var4.getTraceMessage("\n"))); + Message reasonMessage = Message.translation("client.disconnection.shutdownReason.worldGen.detail").param("detail", var4.getTraceMessage("\n")); + HytaleServer.get().shutdownServer(ShutdownReason.WORLD_GEN.withMessage(reasonMessage)); } throw new SkipSentryException("Failed to load WorldGen!", var4); @@ -285,13 +290,13 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg .join(); } } else { - String messagex; + Message messagex; if (this.getFailureException() == null) { - messagex = "The world you were on was removed"; + messagex = Message.translation("server.general.disconnect.worldRemoved"); } else if (this.getPossibleFailureCause() == null) { - messagex = "The world you were on has crashed"; + messagex = Message.translation("server.general.disconnect.worldCrashed"); } else { - messagex = "The world you were on has crashed (possibly caused by " + this.getPossibleFailureCause() + ")"; + messagex = Message.translation("server.general.disconnect.worldCrashedCause").param("cause", this.getPossibleFailureCause().toString()); } for (PlayerRef playerRef : players.values()) { @@ -393,7 +398,7 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg try { this.logger.at(Level.INFO).log("Shutting down stores..."); - HytaleServer.get().reportSingleplayerStatus("Saving world '" + this.name + "'"); + HytaleServer.get().reportSingleplayerStatus(Message.translation("client.gameLoadingView.status.savingWorld").param("name", this.name)); this.chunkStore.shutdown(); this.consumeTaskQueue(); this.entityStore.shutdown(); @@ -412,7 +417,7 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg Universe.get().removeWorldExceptionally(this.name, currentPlayers); } - HytaleServer.get().reportSingleplayerStatus("Closing world '" + this.name + "'"); + HytaleServer.get().reportSingleplayerStatus(Message.translation("client.gameLoadingView.status.closingWorld").param("name", this.name)); } @Override @@ -683,13 +688,13 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg @Deprecated @Nullable - public T spawnEntity(T entity, @Nonnull Vector3d position, Vector3f rotation) { + public T spawnEntity(T entity, @Nonnull Vector3d position, Rotation3f rotation) { return this.addEntity(entity, position, rotation, AddReason.SPAWN); } @Deprecated @Nullable - public T addEntity(T entity, @Nonnull Vector3d position, @Nullable Vector3f rotation, @Nonnull AddReason reason) { + public T addEntity(T entity, @Nonnull Vector3d position, @Nullable Rotation3f rotation, @Nonnull AddReason reason) { if (!EntityModule.get().isKnown(entity)) { throw new IllegalArgumentException("Unknown entity"); } else if (entity instanceof Player) { @@ -700,7 +705,7 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg throw new IllegalStateException("Expected entity to already have its world set to " + this.getName() + " but it has " + entity.getWorld()); } else if (entity.getReference() != null && entity.getReference().isValid()) { throw new IllegalArgumentException("Entity already has a valid EntityReference: " + entity.getReference()); - } else if (position.getY() < -32.0) { + } else if (position.y() < -32.0) { throw new IllegalArgumentException("Unable to spawn entity below the world! -32 < " + position); } else if (!this.isInThread()) { this.logger.at(Level.WARNING).withCause(new SkipSentryException()).log("Warning addEntity was called off thread!"); @@ -851,10 +856,13 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg SpawnUtil.applyTransform(holder, transform); } + Message joinMessage = Message.translation("server.general.playerJoinedWorld") + .param("username", playerRef.getUsername()) + .param("world", this.worldConfig.getDisplayName() != null ? this.worldConfig.getDisplayName() : WorldConfig.formatDisplayName(this.name)); AddPlayerToWorldEvent event = HytaleServer.get() .getEventBus() .dispatchFor(AddPlayerToWorldEvent.class, this.name) - .dispatch(new AddPlayerToWorldEvent(holder, this)); + .dispatch(new AddPlayerToWorldEvent(holder, this, joinMessage)); ChunkTracker chunkTrackerComponent = holder.getComponent(ChunkTracker.getComponentType()); boolean clearWorld = clearWorldOverride != null ? clearWorldOverride : true; boolean fadeInOut = fadeInOutOverride != null ? fadeInOutOverride : true; @@ -863,7 +871,7 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg } Vector3d spawnPosition = transformComponent.getPosition(); - long chunkIndex = ChunkUtil.indexChunkFromBlock(spawnPosition.getX(), spawnPosition.getZ()); + long chunkIndex = ChunkUtil.indexChunkFromBlock(spawnPosition.x(), spawnPosition.z()); CompletableFuture loadTargetChunkFuture = this.chunkStore .getChunkReferenceAsync(chunkIndex) .thenAccept(v -> playerComponent.startClientReadyTimeout()); @@ -874,19 +882,35 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg ); CompletableFuture playerReadyFuture = clientReadyFuture.orTimeout(30L, TimeUnit.SECONDS); return CompletableFuture.allOf(setupPlayerFuture, playerReadyFuture, loadTargetChunkFuture) - .thenApplyAsync(aVoid -> this.onFinishPlayerJoining(playerComponent, playerRef, packetHandler, event.shouldBroadcastJoinMessage()), this) - .exceptionally(throwable -> { - this.logger.at(Level.WARNING).withCause(throwable).log("Exception when adding player to world!"); - playerRef.getPacketHandler().disconnect("Exception when adding player to world!"); - throw new RuntimeException("Exception when adding player '" + playerRef.getUsername() + "' to world '" + this.name + "'", throwable); - }); + .thenApplyAsync( + aVoid -> this.onFinishPlayerJoining(playerComponent, playerRef, packetHandler, event.getJoinMessage(), event.shouldBroadcastJoinMessage()), + this + ) + .exceptionally( + throwable -> { + this.logger.at(Level.WARNING).withCause(throwable).log("Exception when adding player to world!"); + PluginIdentifier possibleCause = PluginIdentifier.identifyThirdPartyPlugin(throwable); + if (possibleCause == null) { + playerRef.getPacketHandler().disconnect(Message.translation("server.general.disconnect.exceptionJoiningWorld")); + } else { + playerRef.getPacketHandler() + .disconnect(Message.translation("server.general.disconnect.exceptionJoiningWorldCause").param("cause", possibleCause.toString())); + } + + throw new RuntimeException("Exception when adding player '" + playerRef.getUsername() + "' to world '" + this.name + "'", throwable); + } + ); } } } @Nonnull private PlayerRef onFinishPlayerJoining( - @Nonnull Player playerComponent, @Nonnull PlayerRef playerRefComponent, @Nonnull PacketHandler packetHandler, boolean broadcastJoin + @Nonnull Player playerComponent, + @Nonnull PlayerRef playerRefComponent, + @Nonnull PacketHandler packetHandler, + @Nullable Message joinMessage, + boolean shouldBroadcastJoinMessage ) { TimeResource timeResource = this.entityStore.getStore().getResource(TimeResource.getResourceType()); float timeDilationModifier = timeResource.getTimeDilationModifier(); @@ -915,11 +939,8 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg WorldMapTracker worldMapTracker = playerComponent.getWorldMapTracker(); worldMapTracker.clear(); worldMapTracker.sendSettings(world); - if (broadcastJoin) { - Message message = Message.translation("server.general.playerJoinedWorld") - .param("username", playerRefComponent.getUsername()) - .param("world", this.worldConfig.getDisplayName() != null ? this.worldConfig.getDisplayName() : WorldConfig.formatDisplayName(this.name)); - PlayerUtil.broadcastMessageToPlayers(playerUuid, message, store); + if (shouldBroadcastJoinMessage) { + PlayerUtil.broadcastMessageToPlayers(playerUuid, joinMessage, store); } TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); @@ -1041,6 +1062,20 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg return gcHasRun; } + public boolean isSavingLocked() { + return this.savingLocks.get() > 0; + } + + public void lockSaving() { + this.savingLocks.incrementAndGet(); + } + + public void unlockSaving() { + int result = this.savingLocks.decrementAndGet(); + + assert result >= 0 : "savingLocks underflow"; + } + @Override public int hashCode() { return this.name != null ? this.name.hashCode() : 0; @@ -1120,6 +1155,10 @@ public class World extends TickingThread implements Executor, ExecutorMetricsReg ); if (options.contains(ValidationOption.BLOCK_FILLER)) { var fetcher = new FillerBlockUtil.FillerFetcher() { + { + Objects.requireNonNull(World.this); + } + public int getBlock(BlockSection blockSection, ChunkStore chunkStore, int xx, int yx, int zx) { if (xx >= 0 && yx >= 0 && zx >= 0 && xx < 32 && yx < 32 && zx < 32) { return blockSection.get(xx, yx, zx); diff --git a/src/com/hypixel/hytale/server/core/universe/world/WorldConfig.java b/src/com/hypixel/hytale/server/core/universe/world/WorldConfig.java index 7977d7c4..bb03d9c8 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/WorldConfig.java +++ b/src/com/hypixel/hytale/server/core/universe/world/WorldConfig.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.map.ObjectMapCodec; +import com.hypixel.hytale.codec.codecs.set.SetCodec; import com.hypixel.hytale.codec.lookup.MapKeyMapCodec; import com.hypixel.hytale.codec.schema.metadata.NoDefaultValue; import com.hypixel.hytale.codec.util.RawJsonReader; @@ -15,7 +16,6 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.shape.Box2D; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector2d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.asset.type.gameplay.DeathConfig; @@ -23,6 +23,7 @@ import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig; import com.hypixel.hytale.server.core.asset.type.weather.config.Weather; import com.hypixel.hytale.server.core.codec.ProtocolCodecs; import com.hypixel.hytale.server.core.codec.ShapeCodecs; +import com.hypixel.hytale.server.core.config.WorldWorldMapConfig; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; import com.hypixel.hytale.server.core.universe.world.spawn.GlobalSpawnProvider; import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider; @@ -37,13 +38,16 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bson.BsonDocument; +import org.joml.Vector2d; public class WorldConfig { public static final int VERSION = 4; @@ -82,6 +86,9 @@ public class WorldConfig { .add() .append(new KeyedCodec<>("WorldMap", IWorldMapProvider.CODEC), (o, i) -> o.worldMapProvider = i, o -> o.worldMapProvider) .add() + .append(new KeyedCodec<>("WorldMapConfig", WorldWorldMapConfig.CODEC), (o, i) -> o.worldMapConfig = i, o -> o.worldMapConfig) + .documentation("Optional per-world overrides for world map configuration and limits.") + .add() .>append( new KeyedCodec<>("ChunkStorage", IChunkStorageProvider.CODEC), (o, i) -> o.chunkStorageProvider = i, o -> o.chunkStorageProvider ) @@ -203,6 +210,15 @@ public class WorldConfig { .documentation("Instance specific configuration.") .addValidator(Validators.deprecated()) .add() + .append( + new KeyedCodec<>("DisabledFluidTickers", new SetCodec<>(Codec.STRING, HashSet::new, false)), + (o, i) -> o.disabledFluidTickers = i, + o -> o.disabledFluidTickers + ) + .documentation( + "A set of fluid tag strings (e.g. \"Fluid=Water\", \"Fire=Fire\") whose tickers should be disabled in this world. Fluids matching any of these tags will not tick." + ) + .add() .append( new KeyedCodec<>("ResourceStorage", IResourceStorageProvider.CODEC), (o, i) -> o.resourceStorageProvider = i, o -> o.resourceStorageProvider ) @@ -221,7 +237,7 @@ public class WorldConfig { .add() .build(); @Nonnull - private transient AtomicBoolean hasChanged = new AtomicBoolean(); + private final transient AtomicBoolean hasChanged = new AtomicBoolean(); private UUID uuid = UUID.randomUUID(); private String displayName; private long seed = System.currentTimeMillis(); @@ -229,6 +245,8 @@ public class WorldConfig { private ISpawnProvider spawnProvider = null; private IWorldGenProvider worldGenProvider = IWorldGenProvider.CODEC.getDefault(); private IWorldMapProvider worldMapProvider = IWorldMapProvider.CODEC.getDefault(); + @Nullable + private WorldWorldMapConfig worldMapConfig; private IChunkStorageProvider chunkStorageProvider = IChunkStorageProvider.CODEC.getDefault(); @Nonnull private WorldConfig.ChunkConfig chunkConfig = new WorldConfig.ChunkConfig(); @@ -260,6 +278,8 @@ public class WorldConfig { private boolean isObjectiveMarkersEnabled = true; private boolean deleteOnUniverseStart = false; private boolean deleteOnRemove = false; + @Nonnull + private Set disabledFluidTickers = Collections.emptySet(); private IResourceStorageProvider resourceStorageProvider = IResourceStorageProvider.CODEC.getDefault(); protected MapKeyMapCodec.TypeMap pluginConfig = new MapKeyMapCodec.TypeMap<>(PLUGIN_CODEC); @Nullable @@ -356,6 +376,16 @@ public class WorldConfig { this.worldMapProvider = worldMapProvider; } + @Nullable + public WorldWorldMapConfig getWorldMapConfig() { + return this.worldMapConfig; + } + + public void setWorldMapConfig(@Nullable WorldWorldMapConfig worldMapConfig) { + this.worldMapConfig = worldMapConfig; + this.markChanged(); + } + public IChunkStorageProvider getChunkStorageProvider() { return this.chunkStorageProvider; } @@ -393,12 +423,16 @@ public class WorldConfig { return this.isPvpEnabled; } + public void setPvpEnabled(boolean pvpEnabled) { + this.isPvpEnabled = pvpEnabled; + } + public boolean isFallDamageEnabled() { return this.isFallDamageEnabled; } - public void setPvpEnabled(boolean pvpEnabled) { - this.isPvpEnabled = pvpEnabled; + public void setFallDamageEnabled(boolean fallDamageEnabled) { + this.isFallDamageEnabled = fallDamageEnabled; } public boolean isGameTimePaused() { @@ -545,6 +579,15 @@ public class WorldConfig { this.isObjectiveMarkersEnabled = objectiveMarkersEnabled; } + @Nonnull + public Set getDisabledFluidTickers() { + return this.disabledFluidTickers; + } + + public void setDisabledFluidTickers(@Nonnull Set disabledFluidTickers) { + this.disabledFluidTickers = disabledFluidTickers; + } + public IResourceStorageProvider getResourceStorageProvider() { return this.resourceStorageProvider; } diff --git a/src/com/hypixel/hytale/server/core/universe/world/WorldMapTracker.java b/src/com/hypixel/hytale/server/core/universe/world/WorldMapTracker.java index 0a70bb54..103e983b 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/WorldMapTracker.java +++ b/src/com/hypixel/hytale/server/core/universe/world/WorldMapTracker.java @@ -7,12 +7,12 @@ import com.hypixel.hytale.common.util.CompletableFutureUtil; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.event.EventBus; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.iterator.CircleSpiralIterator; import com.hypixel.hytale.math.shape.Box2D; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.NetworkChannel; import com.hypixel.hytale.protocol.SoundCategory; @@ -22,13 +22,19 @@ import com.hypixel.hytale.protocol.packets.worldmap.MapImage; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; import com.hypixel.hytale.protocol.packets.worldmap.UpdateWorldMap; import com.hypixel.hytale.protocol.packets.worldmap.UpdateWorldMapSettings; +import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.gameplay.WorldMapConfig; import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.ecs.DiscoverZoneEvent; +import com.hypixel.hytale.server.core.event.events.permissions.GroupPermissionChangeEvent; +import com.hypixel.hytale.server.core.event.events.permissions.PlayerGroupEvent; +import com.hypixel.hytale.server.core.event.events.permissions.PlayerPermissionChangeEvent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.permissions.PermissionsModule; import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.universe.world.worldmap.WorldMapManager; import com.hypixel.hytale.server.core.universe.world.worldmap.WorldMapSettings; @@ -42,17 +48,18 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Predicate; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class WorldMapTracker implements Tickable { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); public static final float UPDATE_SPEED = 1.0F; - public static final int RADIUS_MAX = 512; public static final int EMPTY_UPDATE_WORLD_MAP_SIZE = 13; private static final int EMPTY_MAP_CHUNK_SIZE = 10; private static final int FULL_MAP_CHUNK_SIZE = 23; @@ -75,15 +82,31 @@ public class WorldMapTracker implements Tickable { private String currentBiomeName; @Nullable private WorldMapTracker.ZoneDiscoveryInfo currentZone; - private boolean allowTeleportToCoordinates = true; - private boolean allowTeleportToMarkers = true; - private boolean clientHasWorldMapVisible; @Nullable private TransformComponent transformComponent; + private void handleGroupPermissionsChanged(@Nonnull GroupPermissionChangeEvent event) { + String groupName = event.getGroupName(); + PermissionsModule permissionsModule = PermissionsModule.get(); + + for (PlayerRef playerRef : Universe.get().getPlayers()) { + Set groups = permissionsModule.getGroupsForUser(playerRef.getUuid()); + if (groups.contains(groupName)) { + this.resendWorldMapSettings(playerRef.getUuid()); + } + } + } + public WorldMapTracker(@Nonnull Player player) { this.player = player; this.markerTracker = new MapMarkerTracker(this); + EventBus eventBus = HytaleServer.get().getEventBus(); + eventBus.register(PlayerGroupEvent.Added.class, event -> this.resendWorldMapSettings(event.getPlayerUuid())); + eventBus.register(PlayerGroupEvent.Removed.class, event -> this.resendWorldMapSettings(event.getPlayerUuid())); + eventBus.register(PlayerPermissionChangeEvent.PermissionsAdded.class, event -> this.resendWorldMapSettings(event.getPlayerUuid())); + eventBus.register(PlayerPermissionChangeEvent.PermissionsRemoved.class, event -> this.resendWorldMapSettings(event.getPlayerUuid())); + eventBus.register(GroupPermissionChangeEvent.Added.class, this::handleGroupPermissionsChanged); + eventBus.register(GroupPermissionChangeEvent.Removed.class, this::handleGroupPermissionsChanged); } @Override @@ -96,7 +119,15 @@ public class WorldMapTracker implements Tickable { World world = this.player.getWorld(); if (world != null && this.player.getPlayerConnection().getChannel(NetworkChannel.WorldMap).isWritable()) { if (this.transformComponent == null) { - this.transformComponent = this.player.getTransformComponent(); + this.transformComponent = CompletableFuture.supplyAsync(() -> { + Ref playerRef = this.player.getReference(); + if (playerRef != null && playerRef.isValid()) { + Store store = playerRef.getStore(); + return store.getComponent(playerRef, TransformComponent.getComponentType()); + } else { + return null; + } + }, world).join(); if (this.transformComponent == null) { return; } @@ -112,8 +143,8 @@ public class WorldMapTracker implements Tickable { } Vector3d position = this.transformComponent.getPosition(); - int playerX = MathUtil.floor(position.getX()); - int playerZ = MathUtil.floor(position.getZ()); + int playerX = MathUtil.floor(position.x()); + int playerZ = MathUtil.floor(position.z()); int playerChunkX = playerX >> 5; int playerChunkZ = playerZ >> 5; if (world.isCompassUpdating()) { @@ -474,6 +505,19 @@ public class WorldMapTracker implements Tickable { this.updateTimer = 0.0F; } + private void resendWorldMapSettings(@Nonnull UUID uuid) { + PlayerRef playerRef = Universe.get().getPlayer(uuid); + if (playerRef != null) { + Player player = playerRef.getComponent(Player.getComponentType()); + if (player != null) { + World world = player.getWorld(); + if (world != null) { + player.getWorldMapTracker().sendSettings(world); + } + } + } + } + public void sendSettings(@Nonnull World world) { UpdateWorldMapSettings worldMapSettingsPacket = new UpdateWorldMapSettings(world.getWorldMapManager().getWorldMapSettings().getSettingsPacket()); world.execute(() -> { @@ -488,8 +532,8 @@ public class WorldMapTracker implements Tickable { assert playerRefComponent != null; - worldMapSettingsPacket.allowTeleportToCoordinates = this.allowTeleportToCoordinates && playerComponent.getGameMode() != GameMode.Adventure; - worldMapSettingsPacket.allowTeleportToMarkers = this.allowTeleportToMarkers && playerComponent.getGameMode() != GameMode.Adventure; + worldMapSettingsPacket.allowTeleportToCoordinates = this.isAllowTeleportToCoordinates(); + worldMapSettingsPacket.allowTeleportToMarkers = this.isAllowTeleportToMarkers(); WorldMapConfig worldMapConfig = world.getGameplayConfig().getWorldMapConfig(); worldMapSettingsPacket.allowCreatingMapMarkers = worldMapConfig.getUserMapMarkerConfig().isAllowCreatingMarkers(); worldMapSettingsPacket.allowShowOnMapToggle = worldMapConfig.canTogglePlayersInMap(); @@ -568,10 +612,6 @@ public class WorldMapTracker implements Tickable { this.markerTracker.setPlayerMapFilter(playerMapFilter); } - public void setClientHasWorldMapVisible(boolean visible) { - this.clientHasWorldMapVisible = visible; - } - @Nullable public Integer getViewRadiusOverride() { return this.viewRadiusOverride; @@ -601,8 +641,8 @@ public class WorldMapTracker implements Tickable { public boolean shouldBeVisible(int chunkViewRadius, long chunkCoordinates) { if (this.player != null && this.transformComponent != null) { Vector3d position = this.transformComponent.getPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; + int chunkX = MathUtil.floor(position.x()) >> 5; + int chunkZ = MathUtil.floor(position.z()) >> 5; int x = ChunkUtil.xOfChunkIndex(chunkCoordinates); int z = ChunkUtil.zOfChunkIndex(chunkCoordinates); return shouldBeVisible(chunkViewRadius, chunkX, chunkZ, x, z); diff --git a/src/com/hypixel/hytale/server/core/universe/world/WorldNotificationHandler.java b/src/com/hypixel/hytale/server/core/universe/world/WorldNotificationHandler.java index 9897f8ff..13c29b06 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/WorldNotificationHandler.java +++ b/src/com/hypixel/hytale/server/core/universe/world/WorldNotificationHandler.java @@ -8,13 +8,7 @@ import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.protocol.packets.world.SpawnBlockParticleSystem; import com.hypixel.hytale.protocol.packets.world.UpdateBlockDamage; -import com.hypixel.hytale.server.core.modules.entity.player.ChunkTracker; import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.SendableBlockState; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.List; -import java.util.function.Consumer; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -27,60 +21,6 @@ public class WorldNotificationHandler { this.world = world; } - public void updateState(int x, int y, int z, BlockState state, BlockState oldState) { - this.updateState(x, y, z, state, oldState, null); - } - - public void updateState(int x, int y, int z, BlockState state, BlockState oldState, @Nullable Predicate skip) { - if (y >= 0 && y < 320) { - Consumer> removeOldState; - Predicate canPlayerSeeOld; - if (oldState instanceof SendableBlockState sendableBlockState && state != oldState) { - removeOldState = sendableBlockState::unloadFrom; - canPlayerSeeOld = sendableBlockState::canPlayerSee; - } else { - removeOldState = null; - canPlayerSeeOld = null; - } - - Predicate canPlayerSee; - Consumer> updateBlockState; - if (state instanceof SendableBlockState sendableBlockState) { - updateBlockState = sendableBlockState::sendTo; - canPlayerSee = sendableBlockState::canPlayerSee; - } else { - updateBlockState = null; - canPlayerSee = null; - } - - if (removeOldState != null || updateBlockState != null) { - long indexChunk = ChunkUtil.indexChunkFromBlock(x, z); - List packets = new ObjectArrayList<>(); - - for (PlayerRef playerRef : this.world.getPlayerRefs()) { - ChunkTracker chunkTracker = playerRef.getChunkTracker(); - if (chunkTracker.isLoaded(indexChunk) && (skip == null || !skip.test(playerRef))) { - if (removeOldState != null && canPlayerSeeOld.test(playerRef)) { - removeOldState.accept(packets); - } - - if (updateBlockState != null && canPlayerSee.test(playerRef)) { - updateBlockState.accept(packets); - } - - for (ToClientPacket packet : packets) { - playerRef.getPacketHandler().write(packet); - } - - packets.clear(); - } - } - } - } else { - throw new IllegalArgumentException("Y value is outside the world! " + x + ", " + y + ", " + z); - } - } - public void updateChunk(long indexChunk) { for (PlayerRef playerRef : this.world.getPlayerRefs()) { playerRef.getChunkTracker().removeForReload(indexChunk); diff --git a/src/com/hypixel/hytale/server/core/universe/world/accessor/BlockAccessor.java b/src/com/hypixel/hytale/server/core/universe/world/accessor/BlockAccessor.java index 87ed1689..6c544233 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/accessor/BlockAccessor.java +++ b/src/com/hypixel/hytale/server/core/universe/world/accessor/BlockAccessor.java @@ -4,17 +4,17 @@ import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.function.predicate.TriIntPredicate; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; +import org.joml.Vector3ic; public interface BlockAccessor { int getX(); @@ -26,7 +26,7 @@ public interface BlockAccessor { int getBlock(int var1, int var2, int var3); default int getBlock(@Nonnull Vector3i pos) { - return this.getBlock(pos.getX(), pos.getY(), pos.getZ()); + return this.getBlock(pos.x(), pos.y(), pos.z()); } boolean setBlock(int var1, int var2, int var3, int var4, BlockType var5, int var6, int var7, int var8); @@ -175,30 +175,18 @@ public interface BlockAccessor { } @Nullable - default BlockType getBlockType(@Nonnull Vector3i block) { - return this.getBlockType(block.getX(), block.getY(), block.getZ()); + default BlockType getBlockType(@Nonnull Vector3ic block) { + return this.getBlockType(block.x(), block.y(), block.z()); } boolean setTicking(int var1, int var2, int var3, boolean var4); boolean isTicking(int var1, int var2, int var3); - @Nullable - @Deprecated - BlockState getState(int var1, int var2, int var3); - @Nullable @Deprecated Holder getBlockComponentHolder(int var1, int var2, int var3); - @Deprecated - void setState(int var1, int var2, int var3, BlockState var4, boolean var5); - - @Deprecated - default void setState(int x, int y, int z, BlockState state) { - this.setState(x, y, z, state, true); - } - default void setBlockInteractionState(@Nonnull Vector3i blockPosition, @Nonnull BlockType blockType, @Nonnull String state) { this.setBlockInteractionState(blockPosition.x, blockPosition.y, blockPosition.z, blockType, state, false); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/accessor/EmptyBlockAccessor.java b/src/com/hypixel/hytale/server/core/universe/world/accessor/EmptyBlockAccessor.java index 8c086ff8..59e22e11 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/accessor/EmptyBlockAccessor.java +++ b/src/com/hypixel/hytale/server/core/universe/world/accessor/EmptyBlockAccessor.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.universe.world.accessor; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.function.predicate.TriIntPredicate; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import javax.annotation.Nullable; @@ -70,22 +69,12 @@ public class EmptyBlockAccessor implements BlockAccessor { return false; } - @Nullable - @Override - public BlockState getState(int x, int y, int z) { - return null; - } - @Nullable @Override public Holder getBlockComponentHolder(int x, int y, int z) { return null; } - @Override - public void setState(int x, int y, int z, BlockState state, boolean notify) { - } - @Override public int getFluidId(int x, int y, int z) { return 0; diff --git a/src/com/hypixel/hytale/server/core/universe/world/accessor/IChunkAccessorSync.java b/src/com/hypixel/hytale/server/core/universe/world/accessor/IChunkAccessorSync.java index 81729b3c..e60913b7 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/accessor/IChunkAccessorSync.java +++ b/src/com/hypixel/hytale/server/core/universe/world/accessor/IChunkAccessorSync.java @@ -2,14 +2,13 @@ package com.hypixel.hytale.server.core.universe.world.accessor; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockPosition; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; @Deprecated public interface IChunkAccessorSync { @@ -32,7 +31,7 @@ public interface IChunkAccessorSync { WorldChunk getNonTickingChunk(long var1); default int getBlock(@Nonnull Vector3i pos) { - return this.getBlock(pos.getX(), pos.getY(), pos.getZ()); + return this.getBlock(pos.x(), pos.y(), pos.z()); } default int getBlock(int x, int y, int z) { @@ -43,7 +42,7 @@ public interface IChunkAccessorSync { @Nullable default BlockType getBlockType(@Nonnull Vector3i pos) { - return this.getBlockType(pos.getX(), pos.getY(), pos.getZ()); + return this.getBlockType(pos.x(), pos.y(), pos.z()); } @Nullable @@ -86,22 +85,6 @@ public interface IChunkAccessorSync { return this.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).testPlaceBlock(x, y, z, blockTypeToTest, rotation, predicate); } - @Nullable - @Deprecated - default BlockState getState(int x, int y, int z, boolean followFiller) { - WorldChunk chunk = this.getChunk(ChunkUtil.indexChunkFromBlock(x, z)); - if (followFiller) { - int filler = chunk.getFiller(x, y, z); - if (filler != 0) { - x -= FillerBlockUtil.unpackX(filler); - y -= FillerBlockUtil.unpackY(filler); - z -= FillerBlockUtil.unpackZ(filler); - } - } - - return y >= 0 && y < 320 ? chunk.getState(x, y, z) : null; - } - @Nullable default Holder getBlockComponentHolder(int x, int y, int z) { return y >= 0 && y < 320 ? this.getChunk(ChunkUtil.indexChunkFromBlock(x, z)).getBlockComponentHolder(x, y, z) : null; diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/AbstractCachedAccessor.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/AbstractCachedAccessor.java index 76f928f6..4a64264b 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/AbstractCachedAccessor.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/AbstractCachedAccessor.java @@ -102,12 +102,12 @@ public abstract class AbstractCachedAccessor { if (index >= 0 && index < this.sections.length) { Ref section = this.sections[index]; if (section == null) { - this.sections[index] = section = this.commandBuffer.getExternalData().getChunkSectionReference(this.commandBuffer, cx, cy, cz); + this.sections[index] = section = this.commandBuffer.getExternalData().getChunkSectionReference(cx, cy, cz); } return section; } else { - return this.commandBuffer.getExternalData().getChunkSectionReference(this.commandBuffer, cx, cy, cz); + return this.commandBuffer.getExternalData().getChunkSectionReference(cx, cy, cz); } } else { return null; diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockChunk.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockChunk.java index d6ca098b..f99f97b0 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockChunk.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockChunk.java @@ -15,8 +15,6 @@ import com.hypixel.hytale.function.predicate.ObjectPositionBlockFunction; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.CachedPacket; import com.hypixel.hytale.protocol.Opacity; import com.hypixel.hytale.protocol.ToClientPacket; @@ -51,6 +49,8 @@ import java.util.function.Function; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class BlockChunk implements Component { public static final int VERSION = 3; @@ -70,9 +70,6 @@ public class BlockChunk implements Component { private final IntBytePalette tint; @Deprecated(forRemoval = true) private BlockSection[] chunkSections; - @Nullable - @Deprecated(forRemoval = true) - private BlockSection[] migratedChunkSections; private EnvironmentChunk environments; private boolean needsPhysics = true; private boolean needsSaving = false; @@ -203,9 +200,7 @@ public class BlockChunk implements Component { for (int i = 0; i < sections.length; i++) { Holder section = sections[i]; - this.chunkSections[i] = this.migratedChunkSections != null - ? this.migratedChunkSections[i] - : section.ensureAndGetComponent(BlockSection.getComponentType()); + this.chunkSections[i] = section.ensureAndGetComponent(BlockSection.getComponentType()); } } } @@ -464,20 +459,6 @@ public class BlockChunk implements Component { this.chunkSections[sectionIndex].invalidate(); } - @Nullable - @Deprecated(forRemoval = true) - public BlockSection[] takeMigratedSections() { - BlockSection[] temp = this.migratedChunkSections; - this.migratedChunkSections = null; - return temp; - } - - @Nullable - @Deprecated(forRemoval = true) - public BlockSection[] getMigratedSections() { - return this.migratedChunkSections; - } - private byte[] serialize(ExtraInfo extraInfo) { ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(); @@ -493,19 +474,13 @@ public class BlockChunk implements Component { } private void deserialize(@Nonnull byte[] bytes, @Nonnull ExtraInfo extraInfo) { - ByteBuf buf = Unpooled.wrappedBuffer(bytes); - this.needsPhysics = buf.readBoolean(); - this.height.deserialize(buf); - this.tint.deserialize(buf); - if (extraInfo.getVersion() <= 2) { - int sections = buf.readInt(); - this.migratedChunkSections = new BlockSection[sections]; - - for (int y = 0; y < sections; y++) { - BlockSection section = new BlockSection(); - section.deserialize(BlockType.KEY_DESERIALIZER, buf, extraInfo.getVersion()); - this.migratedChunkSections[y] = section; - } + if (extraInfo.getVersion() < 3) { + throw new IllegalArgumentException("Version not supported"); + } else { + ByteBuf buf = Unpooled.wrappedBuffer(bytes); + this.needsPhysics = buf.readBoolean(); + this.height.deserialize(buf); + this.tint.deserialize(buf); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockComponentChunk.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockComponentChunk.java index d8d87904..6ff0dd4a 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockComponentChunk.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockComponentChunk.java @@ -20,18 +20,19 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.ToClientPacket; import com.hypixel.hytale.server.core.modules.LegacyModule; import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; -import it.unimi.dsi.fastutil.objects.ObjectCollection; +import it.unimi.dsi.fastutil.objects.ReferenceCollection; import java.util.List; import java.util.Objects; import java.util.logging.Level; @@ -53,7 +54,7 @@ public class BlockComponentChunk implements Component { Int2ObjectMap> map = new Int2ObjectOpenHashMap<>(entityChunk.entityHolders.size() + entityChunk.entityReferences.size()); map.putAll(entityChunk.entityHolders); - for (Entry> entry : entityChunk.entityReferences.int2ObjectEntrySet()) { + for (it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry> entry : entityChunk.entityReferences.int2ReferenceEntrySet()) { Ref reference = entry.getValue(); Store store = reference.getStore(); if (store.getArchetype(reference).hasSerializableComponents(store.getRegistry().getData())) { @@ -69,11 +70,11 @@ public class BlockComponentChunk implements Component { @Nonnull private final Int2ObjectMap> entityHolders; @Nonnull - private final Int2ObjectMap> entityReferences; + private final Int2ReferenceMap> entityReferences; @Nonnull private final Int2ObjectMap> entityHoldersUnmodifiable; @Nonnull - private final Int2ObjectMap> entityReferencesUnmodifiable; + private final Int2ReferenceMap> entityReferencesUnmodifiable; private boolean needsSaving; public static ComponentType getComponentType() { @@ -82,16 +83,16 @@ public class BlockComponentChunk implements Component { public BlockComponentChunk() { this.entityHolders = new Int2ObjectOpenHashMap<>(); - this.entityReferences = new Int2ObjectOpenHashMap<>(); + this.entityReferences = new Int2ReferenceOpenHashMap<>(); this.entityHoldersUnmodifiable = Int2ObjectMaps.unmodifiable(this.entityHolders); - this.entityReferencesUnmodifiable = Int2ObjectMaps.unmodifiable(this.entityReferences); + this.entityReferencesUnmodifiable = Int2ReferenceMaps.unmodifiable(this.entityReferences); } - public BlockComponentChunk(@Nonnull Int2ObjectMap> entityHolders, @Nonnull Int2ObjectMap> entityReferences) { + public BlockComponentChunk(@Nonnull Int2ObjectMap> entityHolders, @Nonnull Int2ReferenceMap> entityReferences) { this.entityHolders = entityHolders; this.entityReferences = entityReferences; this.entityHoldersUnmodifiable = Int2ObjectMaps.unmodifiable(entityHolders); - this.entityReferencesUnmodifiable = Int2ObjectMaps.unmodifiable(entityReferences); + this.entityReferencesUnmodifiable = Int2ReferenceMaps.unmodifiable(entityReferences); } @Nonnull @@ -103,12 +104,12 @@ public class BlockComponentChunk implements Component { entityHoldersClone.put(entry.getIntKey(), entry.getValue().clone()); } - for (Entry> entry : this.entityReferences.int2ObjectEntrySet()) { + for (it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry> entry : this.entityReferences.int2ReferenceEntrySet()) { Ref reference = entry.getValue(); entityHoldersClone.put(entry.getIntKey(), reference.getStore().copyEntity(reference)); } - return new BlockComponentChunk(entityHoldersClone, new Int2ObjectOpenHashMap<>()); + return new BlockComponentChunk(entityHoldersClone, new Int2ReferenceOpenHashMap<>()); } @Nonnull @@ -124,7 +125,7 @@ public class BlockComponentChunk implements Component { } } - for (Entry> entryx : this.entityReferences.int2ObjectEntrySet()) { + for (it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry> entryx : this.entityReferences.int2ReferenceEntrySet()) { Ref reference = entryx.getValue(); Store store = reference.getStore(); if (store.getArchetype(reference).hasSerializableComponents(data)) { @@ -132,7 +133,7 @@ public class BlockComponentChunk implements Component { } } - return new BlockComponentChunk(entityHoldersClone, new Int2ObjectOpenHashMap<>()); + return new BlockComponentChunk(entityHoldersClone, new Int2ReferenceOpenHashMap<>()); } @Nonnull @@ -172,7 +173,7 @@ public class BlockComponentChunk implements Component { } @Nonnull - public Int2ObjectMap> getEntityReferences() { + public Int2ReferenceMap> getEntityReferences() { return this.entityReferencesUnmodifiable; } @@ -354,10 +355,6 @@ public class BlockComponentChunk implements Component { int y = ChunkUtil.yFromBlockInColumn(index); int z = ChunkUtil.zFromBlockInColumn(index); holder.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(index, ref)); - BlockState state = BlockState.getBlockState(holder); - if (state != null) { - state.setPosition(chunk, new Vector3i(x, y, z)); - } } } @@ -387,7 +384,7 @@ public class BlockComponentChunk implements Component { @Nonnull List results ) { BlockComponentChunk component = archetypeChunk.getComponent(index, this.componentType); - ObjectCollection> references = component.entityReferences.values(); + ReferenceCollection> references = component.entityReferences.values(); Store componentStore = store.getExternalData().getWorld().getChunkStore().getStore(); componentStore.fetch(references, ChunkStore.LOAD_PACKETS_DATA_QUERY_SYSTEM_TYPE, player, results); } @@ -414,7 +411,7 @@ public class BlockComponentChunk implements Component { @Nonnull List results ) { BlockComponentChunk component = archetypeChunk.getComponent(index, this.componentType); - ObjectCollection> references = component.entityReferences.values(); + ReferenceCollection> references = component.entityReferences.values(); Store componentStore = store.getExternalData().getWorld().getChunkStore().getStore(); componentStore.fetch(references, ChunkStore.UNLOAD_PACKETS_DATA_QUERY_SYSTEM_TYPE, player, results); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockOperations.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockOperations.java new file mode 100644 index 00000000..ac48c709 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockOperations.java @@ -0,0 +1,66 @@ +package com.hypixel.hytale.server.core.universe.world.chunk; + +import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.protocol.BlockParticleEvent; +import com.hypixel.hytale.protocol.Opacity; +import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.universe.world.WorldNotificationHandler; +import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.util.FillerBlockUtil; + +public class BlockOperations { + public static void updateBlockArea(ChunkStore chunkStore, BlockSection section, BlockType blockType, int rotation, int x, int y, int z) { + IndexedLookupTableAssetMap boundingBoxAssets = BlockBoundingBoxes.getAssetMap(); + BlockBoundingBoxes boundingBox = boundingBoxAssets.getAsset(blockType.getHitboxTypeIndex()); + if (boundingBox != null) { + FillerBlockUtil.forEachFillerBlock(0.0F, 1, boundingBox.get(rotation), (x1, y1, z1) -> { + int bx = x + x1; + int by = y + y1; + int bz = z + z1; + if (ChunkUtil.isSameChunkSection(bx, by, bz, x, y, z)) { + section.setTicking(bx, by, bz, true); + } else { + chunkStore.getChunkSectionReferenceAtBlockAsync(bx, by, bz, 3).thenAccept(ref -> { + if (ref != null && ref.isValid()) { + BlockSection otherSection = ref.getStore().getComponent((Ref)ref, BlockSection.getComponentType()); + if (otherSection != null) { + otherSection.setTicking(bx, by, bz, true); + } + } + }); + } + }); + } + } + + public static short updateBlockHeight(BlockChunk blockChunk, int newBlockId, BlockType newBlock, int x, int y, int z, short oldHeight) { + short newHeight = oldHeight; + if (oldHeight <= y) { + if (oldHeight == y && newBlockId == 0) { + newHeight = blockChunk.updateHeight(x, z, (short)y); + } else if (oldHeight < y && newBlockId != 0 && newBlock.getOpacity() != Opacity.Transparent) { + newHeight = (short)y; + blockChunk.setHeight(x, z, newHeight); + } + } + + return newHeight; + } + + public static void spawnBlockParticles(ChunkStore chunkStore, int oldBlockId, int newBlockId, int x, int y, int z, boolean isPhysics) { + WorldNotificationHandler notificationHandler = chunkStore.getWorld().getNotificationHandler(); + if (oldBlockId == 0 && newBlockId != 0) { + notificationHandler.sendBlockParticle(x + 0.5, y + 0.5, z + 0.5, newBlockId, BlockParticleEvent.Build); + } else if (oldBlockId != 0 && newBlockId == 0) { + BlockParticleEvent particleType = isPhysics ? BlockParticleEvent.Physics : BlockParticleEvent.Break; + notificationHandler.sendBlockParticle(x + 0.5, y + 0.5, z + 0.5, oldBlockId, particleType); + } else { + notificationHandler.sendBlockParticle(x + 0.5, y + 0.5, z + 0.5, oldBlockId, BlockParticleEvent.Break); + notificationHandler.sendBlockParticle(x + 0.5, y + 0.5, z + 0.5, newBlockId, BlockParticleEvent.Build); + } + } +} diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockRotationUtil.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockRotationUtil.java index 4d8e844b..c927cca1 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockRotationUtil.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/BlockRotationUtil.java @@ -9,19 +9,34 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class BlockRotationUtil { - @Nullable - public static RotationTuple getFlipped( - @Nonnull RotationTuple blockRotation, @Nullable BlockFlipType flipType, @Nonnull Axis axis, @Nonnull VariantRotation variantRotation - ) { - Rotation rotationYaw = blockRotation.yaw(); - Rotation rotationPitch = blockRotation.pitch(); - Rotation rotationRoll = blockRotation.roll(); - if (flipType != null) { - rotationYaw = flipType.flipYaw(rotationYaw, axis); - } + private static final int[][][] LOCAL_FLIP_CORRECTIONS = new int[][][]{ + {{0, 0, 1}, {0, 1, 0}, {1, 0, 0}}, {{0, 0, -1}, {0, 1, 0}, {-1, 0, 0}}, {{-1, 0, 0}, {0, 1, 0}, {0, 0, 1}} + }; - boolean preventPitchRotation = axis != Axis.Y; - return get(rotationYaw, rotationPitch, rotationRoll, axis, Rotation.OneEighty, variantRotation, preventPitchRotation); + @Nullable + public static RotationTuple getFlipped(@Nonnull RotationTuple blockRotation, @Nullable BlockFlipType flipType, @Nonnull Axis axis) { + if (flipType == null) { + Rotation yaw = blockRotation.yaw(); + Rotation pitch = blockRotation.pitch(); + Rotation roll = blockRotation.roll(); + switch (axis) { + case X: + yaw = yaw.toInverse(); + roll = roll.toInverse(); + break; + case Y: + pitch = pitch.add(Rotation.OneEighty); + roll = roll.toInverse(); + break; + case Z: + yaw = yaw.toInverse(); + pitch = pitch.toInverse(); + } + + return RotationTuple.of(yaw, pitch, roll); + } else { + return RotationTuple.flip(blockRotation, flipType, axis, LOCAL_FLIP_CORRECTIONS); + } } @Nullable @@ -42,15 +57,13 @@ public class BlockRotationUtil { RotationTuple rotationPair = null; switch (axis) { case X: - RotationTuple rotateX = variantRotation.rotateX(RotationTuple.of(rotationYaw, rotationPitch), rotation); - rotationPair = variantRotation.verify(rotateX); + rotationPair = variantRotation.rotateX(RotationTuple.of(rotationYaw, rotationPitch), rotation); break; case Y: rotationPair = variantRotation.verify(RotationTuple.of(rotationYaw.add(rotation), rotationPitch)); break; case Z: - RotationTuple rotateZ = variantRotation.rotateZ(RotationTuple.of(rotationYaw, rotationPitch), rotation); - rotationPair = variantRotation.verify(rotateZ); + rotationPair = variantRotation.rotateZ(RotationTuple.of(rotationYaw, rotationPitch), rotation); } if (rotationPair == null) { @@ -75,4 +88,10 @@ public class BlockRotationUtil { case Z -> rotation.rotateZ(filler); }; } + + public static int getRotatedFiller(int filler, @Nonnull RotationTuple rotation) { + filler = getRotatedFiller(filler, Axis.Z, rotation.roll()); + filler = getRotatedFiller(filler, Axis.X, rotation.pitch()); + return getRotatedFiller(filler, Axis.Y, rotation.yaw()); + } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/EntityChunk.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/EntityChunk.java index fa022a16..c48371a1 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/EntityChunk.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/EntityChunk.java @@ -25,9 +25,11 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import it.unimi.dsi.fastutil.objects.ReferenceSet; +import it.unimi.dsi.fastutil.objects.ReferenceSets; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -63,7 +65,7 @@ public class EntityChunk implements Component { @Nonnull private final List> entityHolders; @Nonnull - private final Set> entityReferences; + private final ReferenceSet> entityReferences; @Nonnull private final List> entityHoldersUnmodifiable; @Nonnull @@ -77,16 +79,16 @@ public class EntityChunk implements Component { public EntityChunk() { this.entityHolders = new ObjectArrayList<>(); - this.entityReferences = new HashSet<>(); + this.entityReferences = new ReferenceOpenHashSet<>(); this.entityHoldersUnmodifiable = Collections.unmodifiableList(this.entityHolders); - this.entityReferencesUnmodifiable = Collections.unmodifiableSet(this.entityReferences); + this.entityReferencesUnmodifiable = ReferenceSets.unmodifiable(this.entityReferences); } - public EntityChunk(@Nonnull List> entityHolders, @Nonnull Set> entityReferences) { + public EntityChunk(@Nonnull List> entityHolders, @Nonnull ReferenceSet> entityReferences) { this.entityHolders = entityHolders; this.entityReferences = entityReferences; this.entityHoldersUnmodifiable = Collections.unmodifiableList(entityHolders); - this.entityReferencesUnmodifiable = Collections.unmodifiableSet(entityReferences); + this.entityReferencesUnmodifiable = ReferenceSets.unmodifiable(entityReferences); } @Nonnull @@ -102,7 +104,7 @@ public class EntityChunk implements Component { entityHoldersClone.add(reference.getStore().copyEntity(reference)); } - return new EntityChunk(entityHoldersClone, new HashSet<>()); + return new EntityChunk(entityHoldersClone, new ReferenceOpenHashSet<>()); } @Nonnull @@ -124,7 +126,7 @@ public class EntityChunk implements Component { } } - return new EntityChunk(entityHoldersClone, new HashSet<>()); + return new EntityChunk(entityHoldersClone, new ReferenceOpenHashSet<>()); } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/WorldChunk.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/WorldChunk.java index 09dd6085..73b42634 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/WorldChunk.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/WorldChunk.java @@ -1,40 +1,28 @@ package com.hypixel.hytale.server.core.universe.world.chunk; -import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; -import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.common.collection.Flags; import com.hypixel.hytale.common.util.CompletableFutureUtil; -import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Archetype; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.protocol.BlockParticleEvent; -import com.hypixel.hytale.protocol.Opacity; -import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickManager; import com.hypixel.hytale.server.core.asset.type.blocktick.config.TickProcedure; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; import com.hypixel.hytale.server.core.modules.LegacyModule; -import com.hypixel.hytale.server.core.modules.block.BlockModule; +import com.hypixel.hytale.server.core.modules.block.BlockEntity; import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.WorldNotificationHandler; import com.hypixel.hytale.server.core.universe.world.accessor.BlockAccessor; import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor; import com.hypixel.hytale.server.core.universe.world.chunk.environment.EnvironmentChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import java.util.concurrent.CompletableFuture; @@ -222,11 +210,6 @@ public class WorldChunk implements BlockAccessor, Component { this.blockChunk.load(x, z); } - @Deprecated - public void setBlockComponentChunk(BlockComponentChunk blockComponentChunk) { - this.blockComponentChunk = blockComponentChunk; - } - public void initFlags() { this.world.debugAssertInTickingThread(); if (!this.is(ChunkFlag.START_INIT)) { @@ -339,126 +322,56 @@ public class WorldChunk implements BlockAccessor, Component { } short newHeight = oldHeight; - if ((settings & 512) == 0 && oldHeight <= y) { - if (oldHeight == y && id == 0) { - newHeight = this.blockChunk.updateHeight(x, z, (short)y); - } else if (oldHeight < y && id != 0 && blockType.getOpacity() != Opacity.Transparent) { - newHeight = (short)y; - this.blockChunk.setHeight(x, z, newHeight); - } + if ((settings & 512) == 0) { + newHeight = BlockOperations.updateBlockHeight(this.blockChunk, id, blockType, x, y, z, oldHeight); } - WorldNotificationHandler notificationHandler = this.getWorld().getNotificationHandler(); if ((settings & 4) == 0) { - if (oldBlock == 0 && id != 0) { - notificationHandler.sendBlockParticle(worldX + 0.5, y + 0.5, worldZ + 0.5, id, BlockParticleEvent.Build); - } else if (oldBlock != 0 && id == 0) { - BlockParticleEvent particleType = (settings & 32) != 0 ? BlockParticleEvent.Physics : BlockParticleEvent.Break; - notificationHandler.sendBlockParticle(worldX + 0.5, y + 0.5, worldZ + 0.5, oldBlock, particleType); - } else { - notificationHandler.sendBlockParticle(worldX + 0.5, y + 0.5, worldZ + 0.5, oldBlock, BlockParticleEvent.Break); - notificationHandler.sendBlockParticle(worldX + 0.5, y + 0.5, worldZ + 0.5, id, BlockParticleEvent.Build); - } + BlockOperations.spawnBlockParticles(this.world.getChunkStore(), oldBlock, id, worldX, y, worldZ, (settings & 32) != 0); } - BlockTypeAssetMap blockTypeAssetMap = BlockType.getAssetMap(); - IndexedLookupTableAssetMap hitboxAssetMap = BlockBoundingBoxes.getAssetMap(); - String blockTypeKey = blockType.getId(); if ((settings & 2) == 0) { Holder blockEntity = blockType.getBlockEntity(); if (blockEntity != null && filler == 0) { Holder newComponents = blockEntity.clone(); - this.setState(x, y, z, newComponents); + this.setState(x, y, z, blockType, rotation, newComponents); } else { - BlockState blockState = null; - String blockStateType = blockType.getState() != null ? blockType.getState().getId() : null; - if (id != 0 && blockStateType != null && filler == 0) { - blockState = BlockStateModule.get().createBlockState(blockStateType, this, new Vector3i(x, y, z), blockType); - if (blockState == null) { - LOGGER.at(Level.WARNING).log("Failed to create BlockState: %s for BlockType: %s", blockStateType, blockTypeKey); - } - } - - BlockState oldState = this.getState(x, y, z); - if (blockState instanceof ItemContainerState newState) { - FillerBlockUtil.forEachFillerBlock(hitboxAssetMap.getAsset(blockType.getHitboxTypeIndex()).get(rotation), (x1, y1, z1) -> { - int blockX = worldX + x1; - int blockY = y + y1; - int blockZ = worldZ + z1; - boolean isZero = x1 == 0 && y1 == 0 && z1 == 0; - if ((isZero ? oldState : this.getState(blockX, blockY, blockZ)) instanceof ItemContainerState oldContainer) { - oldContainer.getItemContainer().moveAllItemStacksTo(newState.getItemContainer()); - } - }); - } - - this.setState(x, y, z, blockState, (settings & 1) == 0); + this.setState(x, y, z, blockType, rotation, null); } } if (this.lightingUpdatesEnabled) { - this.world.getChunkLighting().invalidateLightAtBlock(this, x, y, z, blockType, oldHeight, newHeight); + this.world.getChunkLighting().invalidateLightAtBlock(this.world.getChunkStore(), x, y, z, blockType, oldHeight, newHeight); } TickProcedure tickProcedure = BlockTickManager.getBlockTickProvider().getTickProcedure(id); this.blockChunk.setTicking(x, y, z, tickProcedure != null); + FillerBlockUtil.ChangeReason changeReason = FillerBlockUtil.ChangeReason.NONE; + if ((settings & 4) == 0) { + changeReason = (settings & 32) != 0 ? FillerBlockUtil.ChangeReason.BY_PHYSICS : FillerBlockUtil.ChangeReason.NORMAL; + } + if ((settings & 16) == 0) { - int settingsWithoutFiller = settings | 8 | 16; - BlockType oldBlockType = blockTypeAssetMap.getAsset(oldBlock); - String oldBlockKey = oldBlockType.getId(); - int fx = FillerBlockUtil.unpackX(oldFiller); - int fy = FillerBlockUtil.unpackY(oldFiller); - int fz = FillerBlockUtil.unpackZ(oldFiller); - int baseX = worldX - fx; - int baseY = y - fy; - int baseZ = worldZ - fz; - FillerBlockUtil.forEachFillerBlock(hitboxAssetMap.getAsset(oldBlockType.getHitboxTypeIndex()).get(oldRotation), (x1, y1, z1) -> { - if (x1 != fx || y1 != fy || z1 != fz) { - int blockX = baseX + x1; - int blockY = baseY + y1; - int blockZ = baseZ + z1; - if (ChunkUtil.isSameChunk(worldX, worldZ, blockX, blockZ)) { - String blockTypeKey1 = this.getBlockType(blockX, blockY, blockZ).getId(); - if (blockTypeKey1.equals(oldBlockKey)) { - this.breakBlock(blockX, blockY, blockZ, settingsWithoutFiller); - } - } else { - String blockTypeKey1 = this.getWorld().getBlockType(blockX, blockY, blockZ).getId(); - if (blockTypeKey1.equals(oldBlockKey)) { - this.getWorld().breakBlock(blockX, blockY, blockZ, settingsWithoutFiller); - } - } - } - }); + FillerBlockUtil.removeFillerBlocksAt( + this.world.getChunkStore().getStore(), blockSection, worldX, y, worldZ, oldBlock, oldFiller, oldRotation, changeReason + ); } if ((settings & 8) == 0 && filler == 0) { - int settingsWithoutSetFiller = settings | 8; - FillerBlockUtil.forEachFillerBlock( - hitboxAssetMap.getAsset(blockType.getHitboxTypeIndex()).get(rotation), - (x1, y1, z1) -> { - if (x1 != 0 || y1 != 0 || z1 != 0) { - int blockX = worldX + x1; - int blockY = y + y1; - int blockZ = worldZ + z1; - boolean sameChunk = ChunkUtil.isSameChunk(worldX, worldZ, blockX, blockZ); - if (sameChunk) { - this.setBlock(blockX, blockY, blockZ, id, blockType, rotation, FillerBlockUtil.pack(x1, y1, z1), settingsWithoutSetFiller); - } else { - this.getWorld() - .getNonTickingChunk(ChunkUtil.indexChunkFromBlock(blockX, blockZ)) - .setBlock(blockX, blockY, blockZ, id, blockType, rotation, FillerBlockUtil.pack(x1, y1, z1), settingsWithoutSetFiller); - } - } - } - ); + Store store = this.reference.getStore(); + ChunkColumn column = store.getComponent(this.reference, ChunkColumn.getComponentType()); + + assert column != null; + + Ref section = column.getSection(ChunkUtil.chunkCoordinate(y)); + + assert section != null; + + FillerBlockUtil.setFillerBlocksAt(store, section, blockSection, worldX, y, worldZ, id, filler, rotation, changeReason); } if ((settings & 256) != 0) { - FillerBlockUtil.forEachFillerBlock( - hitboxAssetMap.getAsset(blockType.getHitboxTypeIndex()).get(rotation), - (x1, y1, z1) -> this.getChunkAccessor().performBlockUpdate(worldX + x1, y + y1, worldZ + z1) - ); + BlockOperations.updateBlockArea(this.world.getChunkStore(), blockSection, blockType, rotation, x, y, z); } if (this.reference != null && this.reference.isValid()) { @@ -523,25 +436,6 @@ public class WorldChunk implements BlockAccessor, Component { return this.blockChunk.getTint(x, z); } - @Nullable - @Override - public BlockState getState(int x, int y, int z) { - if (y < 0 || y >= 320) { - return null; - } else if (!this.world.isInThread()) { - return CompletableFuture.supplyAsync(() -> this.getState(x, y, z), this.world).join(); - } else { - int index = ChunkUtil.indexBlockInColumn(x, y, z); - Ref reference = this.blockComponentChunk.getEntityReference(index); - if (reference != null) { - return BlockState.getBlockState(reference, reference.getStore()); - } else { - Holder holder = this.blockComponentChunk.getEntityHolder(index); - return holder != null ? BlockState.getBlockState(holder) : null; - } - } - } - @Nullable public Ref getBlockComponentEntity(int x, int y, int z) { if (y < 0 || y >= 320) { @@ -573,14 +467,6 @@ public class WorldChunk implements BlockAccessor, Component { } } - @Override - public void setState(int x, int y, int z, @Nullable BlockState state, boolean notify) { - if (y >= 0 && y < 320) { - Holder holder = state != null ? state.toHolder() : null; - this.setState(x, y, z, holder); - } - } - @Deprecated(forRemoval = true) @Override public int getFluidId(int x, int y, int z) { @@ -627,61 +513,12 @@ public class WorldChunk implements BlockAccessor, Component { } @Deprecated - public void setState(int x, int y, int z, @Nullable Holder holder) { - if (y >= 0 && y < 320) { + public void setState(int x, int y, int z, BlockType blockType, int rotation, @Nullable Holder holder) { + if (y >= 0 && y < 320 && blockType != null) { if (!this.world.isInThread()) { - CompletableFutureUtil._catch(CompletableFuture.runAsync(() -> this.setState(x, y, z, holder), this.world)); + CompletableFutureUtil._catch(CompletableFuture.runAsync(() -> this.setState(x, y, z, blockType, rotation, holder), this.world)); } else { - boolean notify = true; - int index = ChunkUtil.indexBlockInColumn(x, y, z); - if (holder == null) { - Ref reference = this.blockComponentChunk.getEntityReference(index); - if (reference != null) { - Holder oldHolder = reference.getStore().removeEntity(reference, RemoveReason.REMOVE); - BlockState oldState = BlockState.getBlockState(oldHolder); - if (notify) { - this.world.getNotificationHandler().updateState(x, y, z, null, oldState); - } - } else { - this.blockComponentChunk.removeEntityHolder(index); - } - } else { - BlockState state = BlockState.getBlockState(holder); - if (state != null) { - state.setPosition(this, new Vector3i(x, y, z)); - } - - Store blockComponentStore = this.world.getChunkStore().getStore(); - if (!this.is(ChunkFlag.TICKING)) { - Holder oldHolder = this.blockComponentChunk.getEntityHolder(index); - BlockState oldState = null; - if (oldHolder != null) { - oldState = BlockState.getBlockState(oldHolder); - } - - this.blockComponentChunk.removeEntityHolder(index); - holder.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(index, this.reference)); - this.blockComponentChunk.addEntityHolder(index, holder); - if (notify) { - this.world.getNotificationHandler().updateState(x, y, z, state, oldState); - } - } else { - Ref oldReference = this.blockComponentChunk.getEntityReference(index); - BlockState oldStatex = null; - if (oldReference != null) { - Holder oldEntityHolder = blockComponentStore.removeEntity(oldReference, RemoveReason.REMOVE); - oldStatex = BlockState.getBlockState(oldEntityHolder); - } else { - this.blockComponentChunk.removeEntityHolder(index); - } - - holder.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(index, this.reference)); - blockComponentStore.addEntity(holder, AddReason.SPAWN); - if (notify) { - this.world.getNotificationHandler().updateState(x, y, z, state, oldStatex); - } - } - } + BlockEntity.setBlockEntity(this.world.getChunkStore().getStore(), this.reference, this.blockComponentChunk, x, y, z, blockType, rotation, holder); } } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/environment/EnvironmentChunk.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/environment/EnvironmentChunk.java index cfaf2ee7..044fb4d8 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/environment/EnvironmentChunk.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/environment/EnvironmentChunk.java @@ -285,8 +285,8 @@ public class EnvironmentChunk implements Component { } else { this.maxYsReversed.add(y); this.valuesReversed.add(environmentId); + this.count(previousEnvironment, runCounter); previousEnvironment = environmentId; - this.count(environmentId, runCounter); runCounter = 1; } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/palette/BitFieldArr.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/palette/BitFieldArr.java index 7c498c64..9379c091 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/palette/BitFieldArr.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/palette/BitFieldArr.java @@ -4,7 +4,6 @@ import javax.annotation.Nonnull; public class BitFieldArr { public static final int BITS_PER_INDEX = 8; - public static final int LAST_BIT_INDEX = 7; public static final int INDEX_MASK = 255; private final int bits; private final int length; @@ -18,7 +17,7 @@ public class BitFieldArr { throw new IllegalArgumentException("The length must be greater than zero."); } else { this.bits = bits; - this.array = new byte[length * bits / 8]; + this.array = new byte[(length * bits + 8 - 1) / 8]; this.length = length; } } @@ -29,62 +28,48 @@ public class BitFieldArr { public int get(int index) { int bitIndex = index * this.bits; - int endBitIndex = (index + 1) * this.bits - 1; - int endArrIndex = endBitIndex / 8; - int value = 0; + int byteIndex = bitIndex / 8; + int bitOffset = bitIndex % 8; + if (bitOffset + this.bits <= 8) { + int mask = (1 << this.bits) - 1; + return (this.array[byteIndex] & 0xFF) >>> bitOffset & mask; + } else { + int value = 0; + int remainingBits = this.bits; - for (int i = 0; i < this.bits; bitIndex++) { - int arrIndex = bitIndex / 8; - int startBit = bitIndex % 8; - if (arrIndex <= endArrIndex && startBit != 7) { - int endBit; - if (arrIndex == endArrIndex) { - endBit = endBitIndex % 8; - if (startBit == endBit) { - value |= (this.array[arrIndex] >> startBit & 1) << i; - } else if (startBit == 0 && endBit == 7) { - value |= (this.array[arrIndex] & 255) << i; - } else { - int mask = -1 >>> 32 - (endBit + 1 - startBit); - value |= (this.array[arrIndex] >>> startBit & mask) << i; - } - } else { - endBit = 7; - if (startBit == 0) { - value |= (this.array[arrIndex] & 255) << i; - } else { - int mask = -1 >>> 32 - (endBit + 1 - startBit); - value |= (this.array[arrIndex] >>> startBit & mask) << i; - } - } - - int inc = endBit - startBit; - i += inc; - bitIndex += inc; - } else { - value |= (this.array[arrIndex] >> startBit & 1) << i; + for (int shift = 0; remainingBits > 0; bitOffset = 0) { + int bitsInThisByte = Math.min(8 - bitOffset, remainingBits); + int mask = (1 << bitsInThisByte) - 1; + value |= ((this.array[byteIndex] & 255) >>> bitOffset & mask) << shift; + shift += bitsInThisByte; + remainingBits -= bitsInThisByte; + byteIndex++; } - i++; + return value; } - - return value; } public void set(int index, int value) { int bitIndex = index * this.bits; - - for (int i = 0; i < this.bits; bitIndex++) { - this.setBit(bitIndex, value >> i & 1); - i++; - } - } - - private void setBit(int bitIndex, int bit) { - if (bit == 0) { - this.array[bitIndex / 8] = (byte)(this.array[bitIndex / 8] & ~(1 << bitIndex % 8)); + int byteIndex = bitIndex / 8; + int bitOffset = bitIndex % 8; + if (bitOffset + this.bits <= 8) { + int mask = (1 << this.bits) - 1; + int clearMask = ~(mask << bitOffset); + this.array[byteIndex] = (byte)(this.array[byteIndex] & clearMask | (value & mask) << bitOffset); } else { - this.array[bitIndex / 8] = (byte)(this.array[bitIndex / 8] | 1 << bitIndex % 8); + int remainingBits = this.bits; + + for (int currentValue = value; remainingBits > 0; bitOffset = 0) { + int bitsInThisByte = Math.min(8 - bitOffset, remainingBits); + int mask = (1 << bitsInThisByte) - 1; + int clearMask = ~(mask << bitOffset); + this.array[byteIndex] = (byte)(this.array[byteIndex] & clearMask | (currentValue & mask) << bitOffset); + currentValue >>>= bitsInThisByte; + remainingBits -= bitsInThisByte; + byteIndex++; + } } } @@ -110,9 +95,9 @@ public class BitFieldArr { } public void copyFrom(@Nonnull BitFieldArr other) { - if (this.bits == other.bits) { + if (this.bits != other.bits) { throw new IllegalArgumentException("bits must be the same"); - } else if (this.length == other.length) { + } else if (this.length != other.length) { throw new IllegalArgumentException("length must be the same"); } else { System.arraycopy(other.array, 0, this.array, 0, this.array.length); diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/BlockSection.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/BlockSection.java index b85cc846..7783bc07 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/BlockSection.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/BlockSection.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.common.util.BitSetUtil; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.function.consumer.BiIntConsumer; import com.hypixel.hytale.function.predicate.ObjectPositionBlockFunction; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.protocol.CachedPacket; @@ -18,28 +19,22 @@ import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockMigration; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; -import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; -import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; import com.hypixel.hytale.server.core.modules.LegacyModule; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.AbstractSectionPalette; import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.EmptySectionPalette; -import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.ISectionPalette; import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.PaletteTypeEnum; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; -import com.hypixel.hytale.server.core.util.FillerBlockUtil; import com.hypixel.hytale.server.core.util.io.ByteBufUtil; import com.hypixel.hytale.sneakythrow.SneakyThrow; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ShortMap; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectHeapPriorityQueue; import java.lang.ref.SoftReference; import java.time.Instant; @@ -69,9 +64,9 @@ public class BlockSection implements Component { private IntOpenHashSet changedPositions = new IntOpenHashSet(0); @Nonnull private IntOpenHashSet swapChangedPositions = new IntOpenHashSet(0); - private ISectionPalette chunkSection; - private ISectionPalette fillerSection; - private ISectionPalette rotationSection; + private AbstractSectionPalette chunkSection; + private AbstractSectionPalette fillerSection; + private AbstractSectionPalette rotationSection; private ChunkLightData localLight; private short localChangeCounter; private ChunkLightData globalLight; @@ -87,12 +82,6 @@ public class BlockSection implements Component { private double maximumHitboxExtent; @Nullable private transient SoftReference>> cachedChunkPacket; - @Nullable - @Deprecated(forRemoval = true) - private FluidSection migratedFluidSection; - @Nullable - @Deprecated(forRemoval = true) - private BlockPhysics migratedBlockPhysics; private static final Comparator TICK_REQUEST_COMPARATOR = Comparator.comparing(t -> t.requestedGameTime); public static ComponentType getComponentType() { @@ -103,7 +92,7 @@ public class BlockSection implements Component { this(EmptySectionPalette.INSTANCE, EmptySectionPalette.INSTANCE, EmptySectionPalette.INSTANCE); } - public BlockSection(ISectionPalette chunkSection, ISectionPalette fillerSection, ISectionPalette rotationSection) { + public BlockSection(AbstractSectionPalette chunkSection, AbstractSectionPalette fillerSection, AbstractSectionPalette rotationSection) { this.tickRequests = new ObjectHeapPriorityQueue<>(TICK_REQUEST_COMPARATOR); this.maximumHitboxExtent = -1.0; this.chunkSection = chunkSection; @@ -120,11 +109,11 @@ public class BlockSection implements Component { this.globalChangeCounter = 0; } - public ISectionPalette getChunkSection() { + public AbstractSectionPalette getChunkSection() { return this.chunkSection; } - public void setChunkSection(ISectionPalette chunkSection) { + public void setChunkSection(AbstractSectionPalette chunkSection) { this.chunkSection = chunkSection; } @@ -254,15 +243,15 @@ public class BlockSection implements Component { boolean changed; try { - ISectionPalette.SetResult result = this.chunkSection.set(blockIdx, blockId); - if (result == ISectionPalette.SetResult.REQUIRES_PROMOTE) { + AbstractSectionPalette.SetResult result = this.chunkSection.set(blockIdx, blockId); + if (result == AbstractSectionPalette.SetResult.REQUIRES_PROMOTE) { this.chunkSection = this.chunkSection.promote(); - ISectionPalette.SetResult repeatResult = this.chunkSection.set(blockIdx, blockId); - if (repeatResult != ISectionPalette.SetResult.ADDED_OR_REMOVED) { + AbstractSectionPalette.SetResult repeatResult = this.chunkSection.set(blockIdx, blockId); + if (repeatResult != AbstractSectionPalette.SetResult.ADDED_OR_REMOVED) { throw new IllegalStateException("Promoted chunk section failed to correctly add the new block!"); } } else { - if (result == ISectionPalette.SetResult.ADDED_OR_REMOVED) { + if (result == AbstractSectionPalette.SetResult.ADDED_OR_REMOVED) { this.maximumHitboxExtent = -1.0; } @@ -271,31 +260,31 @@ public class BlockSection implements Component { } } - changed = result != ISectionPalette.SetResult.UNCHANGED; + changed = result != AbstractSectionPalette.SetResult.UNCHANGED; result = this.fillerSection.set(blockIdx, filler); - if (result == ISectionPalette.SetResult.REQUIRES_PROMOTE) { + if (result == AbstractSectionPalette.SetResult.REQUIRES_PROMOTE) { this.fillerSection = this.fillerSection.promote(); - ISectionPalette.SetResult repeatResult = this.fillerSection.set(blockIdx, filler); - if (repeatResult != ISectionPalette.SetResult.ADDED_OR_REMOVED) { + AbstractSectionPalette.SetResult repeatResult = this.fillerSection.set(blockIdx, filler); + if (repeatResult != AbstractSectionPalette.SetResult.ADDED_OR_REMOVED) { throw new IllegalStateException("Promoted chunk section failed to correctly add the new block!"); } } else if (this.fillerSection.shouldDemote()) { this.fillerSection = this.fillerSection.demote(); } - changed |= result != ISectionPalette.SetResult.UNCHANGED; + changed |= result != AbstractSectionPalette.SetResult.UNCHANGED; result = this.rotationSection.set(blockIdx, rotation); - if (result == ISectionPalette.SetResult.REQUIRES_PROMOTE) { + if (result == AbstractSectionPalette.SetResult.REQUIRES_PROMOTE) { this.rotationSection = this.rotationSection.promote(); - ISectionPalette.SetResult repeatResult = this.rotationSection.set(blockIdx, rotation); - if (repeatResult != ISectionPalette.SetResult.ADDED_OR_REMOVED) { + AbstractSectionPalette.SetResult repeatResult = this.rotationSection.set(blockIdx, rotation); + if (repeatResult != AbstractSectionPalette.SetResult.ADDED_OR_REMOVED) { throw new IllegalStateException("Promoted chunk section failed to correctly add the new block!"); } } else if (this.rotationSection.shouldDemote()) { this.rotationSection = this.rotationSection.demote(); } - changed |= result != ISectionPalette.SetResult.UNCHANGED; + changed |= result != AbstractSectionPalette.SetResult.UNCHANGED; if (changed && this.loaded) { this.changedPositions.add(blockIdx); } @@ -474,11 +463,26 @@ public class BlockSection implements Component { } } - public void find(IntList ids, IntSet internalIdHolder, IntConsumer indexConsumer) { + @Deprecated(since = "2026-02-26", forRemoval = true) + public void find(IntList ids, IntSet ignoredInternalIdHolder, IntConsumer indexConsumer) { + this.find(ids, indexConsumer); + } + + public void find(IntList ids, IntConsumer indexConsumer) { long lock = this.chunkSectionLock.readLock(); try { - this.chunkSection.find(ids, internalIdHolder, indexConsumer); + this.chunkSection.find(ids, indexConsumer); + } finally { + this.chunkSectionLock.unlockRead(lock); + } + } + + public void find(IntList ids, BiIntConsumer indexBlockConsumer) { + long lock = this.chunkSectionLock.readLock(); + + try { + this.chunkSection.find(ids, indexBlockConsumer); } finally { this.chunkSectionLock.unlockRead(lock); } @@ -518,6 +522,35 @@ public class BlockSection implements Component { return var7; } + public int setTicking(@Nonnull IntList indices, boolean ticking) { + long writeStamp = this.chunkSectionLock.writeLock(); + + int var11; + try { + int count = 0; + + for (int i = 0; i < indices.size(); i++) { + int blockIdx = indices.getInt(i); + if (this.tickingBlocks.get(blockIdx) != ticking) { + if (ticking) { + this.tickingBlocksCount++; + } else { + this.tickingBlocksCount--; + } + + this.tickingBlocks.set(blockIdx, ticking); + count++; + } + } + + var11 = count; + } finally { + this.chunkSectionLock.unlockWrite(writeStamp); + } + + return var11; + } + public int getTickingBlocksCount() { return this.tickingBlocksCount > 0 ? this.tickingBlocksCount : 0; } @@ -676,22 +709,6 @@ public class BlockSection implements Component { } } - @Nullable - @Deprecated(forRemoval = true) - public FluidSection takeMigratedFluid() { - FluidSection temp = this.migratedFluidSection; - this.migratedFluidSection = null; - return temp; - } - - @Nullable - @Deprecated(forRemoval = true) - public BlockPhysics takeMigratedDecoBlocks() { - BlockPhysics temp = this.migratedBlockPhysics; - this.migratedBlockPhysics = null; - return temp; - } - public void serializeForPacket(@Nonnull ByteBuf buf) { long lock = this.chunkSectionLock.readLock(); @@ -713,7 +730,7 @@ public class BlockSection implements Component { } } - public void serialize(ISectionPalette.KeySerializer keySerializer, @Nonnull ByteBuf buf) { + public void serialize(AbstractSectionPalette.KeySerializer keySerializer, @Nonnull ByteBuf buf) { long lock = this.chunkSectionLock.readLock(); try { @@ -759,249 +776,61 @@ public class BlockSection implements Component { } public void deserialize(ToIntFunction keyDeserializer, @Nonnull ByteBuf buf, int version) { - int blockMigrationVersion = 0; - if (version >= 6) { - blockMigrationVersion = buf.readInt(); - } - - Function blockMigration = null; - Map blockMigrationMap = BlockMigration.getAssetMap().getAssetMap(); - - for (BlockMigration migration = blockMigrationMap.get(blockMigrationVersion); - migration != null; - migration = blockMigrationMap.get(++blockMigrationVersion) - ) { - if (blockMigration == null) { - blockMigration = migration::getMigration; - } else { - blockMigration = blockMigration.andThen(migration::getMigration); - } - } - - PaletteTypeEnum typeEnum = PaletteTypeEnum.get(buf.readByte()); - PaletteType paletteType = typeEnum.getPaletteType(); - this.chunkSection = typeEnum.getConstructor().get(); - if (version <= 4) { - ISectionPalette tempSection = typeEnum.getConstructor().get(); - boolean[] foundMigratable = new boolean[]{false}; - boolean[] needsPhysics = new boolean[]{false}; - int[] nextTempIndex = new int[]{-1}; - Int2ObjectOpenHashMap types = new Int2ObjectOpenHashMap<>(); - Object2IntOpenHashMap typesRev = new Object2IntOpenHashMap<>(); - typesRev.defaultReturnValue(Integer.MIN_VALUE); - Function finalBlockMigration = blockMigration; - tempSection.deserialize( - bytebuf -> { - String keyx = ByteBufUtil.readUTF(bytebuf); - if (finalBlockMigration != null) { - keyx = finalBlockMigration.apply(keyx); - } - - int indexx = typesRev.getInt(keyx); - if (indexx != Integer.MIN_VALUE) { - return indexx; - } else { - boolean migratable = keyx.startsWith("Fluid_") - || keyx.contains("|Fluid=") - || keyx.contains("|Deco") - || keyx.contains("|Support") - || keyx.contains("|Filler") - || keyx.contains("|Yaw=") - || keyx.contains("|Pitch=") - || keyx.contains("|Roll="); - foundMigratable[0] |= migratable; - Object var10x; - if (migratable) { - var10x = nextTempIndex[0]--; - } else { - var10x = BlockType.getBlockIdOrUnknown(keyx, "Unknown BlockType %s", keyx); - needsPhysics[0] |= BlockType.getAssetMap().getAsset((int)var10x).hasSupport(); - } - - types.put((int)var10x, keyx); - typesRev.put(keyx, (int)var10x); - return (int)var10x; - } - }, - buf, - version - ); - if (needsPhysics[0]) { - this.migratedBlockPhysics = new BlockPhysics(); - } - - if (foundMigratable[0]) { - for (int index = 0; index < 32768; index++) { - int id = tempSection.get(index); - if (id >= 0) { - this.chunkSection.set(index, id); - } else { - Rotation rotationYaw = Rotation.None; - Rotation rotationPitch = Rotation.None; - Rotation rotationRoll = Rotation.None; - String key = types.get(id); - if (key.startsWith("Fluid_") || key.contains("|Fluid=")) { - if (this.migratedFluidSection == null) { - this.migratedFluidSection = new FluidSection(); - } - - Fluid.ConversionResult result = Fluid.convertBlockToFluid(key); - if (result == null) { - throw new RuntimeException("Invalid Fluid Key " + key); - } - - if (result.blockTypeStr == null) { - this.migratedFluidSection.setFluid(index, result.fluidId, result.fluidLevel); - continue; - } - - key = result.blockTypeStr; - this.migratedFluidSection.setFluid(index, result.fluidId, result.fluidLevel); - } - - if (key.contains("|Deco")) { - if (this.migratedBlockPhysics == null) { - this.migratedBlockPhysics = new BlockPhysics(); - } - - this.migratedBlockPhysics.set(index, 15); - } - - if (key.contains("|Support=")) { - if (this.migratedBlockPhysics == null) { - this.migratedBlockPhysics = new BlockPhysics(); - } - - int start = key.indexOf("|Support=") + "|Support=".length(); - int end = key.indexOf(124, start); - if (end == -1) { - end = key.length(); - } - - this.migratedBlockPhysics.set(index, Integer.parseInt(key, start, end, 10)); - } - - if (key.contains("|Filler=")) { - int start = key.indexOf("|Filler=") + "|Filler=".length(); - int firstComma = key.indexOf(44, start); - if (firstComma == -1) { - throw new IllegalArgumentException("Invalid filler metadata! Missing comma"); - } - - int secondComma = key.indexOf(44, firstComma + 1); - if (secondComma == -1) { - throw new IllegalArgumentException("Invalid filler metadata! Missing second comma"); - } - - int end = key.indexOf(124, start); - if (end == -1) { - end = key.length(); - } - - int fillerX = Integer.parseInt(key, start, firstComma, 10); - int fillerY = Integer.parseInt(key, firstComma + 1, secondComma, 10); - int fillerZ = Integer.parseInt(key, secondComma + 1, end, 10); - int filler = FillerBlockUtil.pack(fillerX, fillerY, fillerZ); - ISectionPalette.SetResult resultx = this.fillerSection.set(index, filler); - if (resultx == ISectionPalette.SetResult.REQUIRES_PROMOTE) { - this.fillerSection = this.fillerSection.promote(); - this.fillerSection.set(index, filler); - } - } - - if (key.contains("|Yaw=")) { - int startx = key.indexOf("|Yaw=") + "|Yaw=".length(); - int endx = key.indexOf(124, startx); - if (endx == -1) { - endx = key.length(); - } - - rotationYaw = Rotation.ofDegrees(Integer.parseInt(key, startx, endx, 10)); - } - - if (key.contains("|Pitch=")) { - int startx = key.indexOf("|Pitch=") + "|Pitch=".length(); - int endx = key.indexOf(124, startx); - if (endx == -1) { - endx = key.length(); - } - - rotationPitch = Rotation.ofDegrees(Integer.parseInt(key, startx, endx, 10)); - } - - if (key.contains("|Roll=")) { - int startx = key.indexOf("|Roll=") + "|Roll=".length(); - int endx = key.indexOf(124, startx); - if (endx == -1) { - endx = key.length(); - } - - rotationRoll = Rotation.ofDegrees(Integer.parseInt(key, startx, endx, 10)); - } - - if (rotationYaw != Rotation.None || rotationPitch != Rotation.None || rotationRoll != Rotation.None) { - int rotation = RotationTuple.index(rotationYaw, rotationPitch, rotationRoll); - ISectionPalette.SetResult resultx = this.rotationSection.set(index, rotation); - if (resultx == ISectionPalette.SetResult.REQUIRES_PROMOTE) { - this.rotationSection = this.rotationSection.promote(); - this.rotationSection.set(index, rotation); - } - } - - int endOfName = key.indexOf(124); - if (endOfName != -1) { - key = key.substring(0, endOfName); - } - - this.chunkSection.set(index, BlockType.getBlockIdOrUnknown(key, "Unknown BlockType: %s", key)); - } - } - - if (this.chunkSection.shouldDemote()) { - this.chunkSection.demote(); - } - } else { - this.chunkSection = tempSection; - } - } else if (blockMigration != null) { - this.chunkSection.deserialize(bytebuf -> { - String keyx = ByteBufUtil.readUTF(bytebuf); - keyx = blockMigration.apply(keyx); - return BlockType.getBlockIdOrUnknown(keyx, "Unknown BlockType %s", keyx); - }, buf, version); + if (version < 6) { + throw new IllegalArgumentException("Version not supported: " + version); } else { - this.chunkSection.deserialize(keyDeserializer, buf, version); - } + int blockMigrationVersion = buf.readInt(); + Function blockMigration = null; + Map blockMigrationMap = BlockMigration.getAssetMap().getAssetMap(); - if (paletteType != PaletteType.Empty) { - this.tickingBlocksCount = buf.readUnsignedShort(); - int len = buf.readUnsignedShort(); - long[] tickingBlocksData = new long[len]; - - for (int i = 0; i < tickingBlocksData.length; i++) { - tickingBlocksData[i] = buf.readLong(); + for (BlockMigration migration = blockMigrationMap.get(blockMigrationVersion); + migration != null; + migration = blockMigrationMap.get(++blockMigrationVersion) + ) { + if (blockMigration == null) { + blockMigration = migration::getMigration; + } else { + blockMigration = blockMigration.andThen(migration::getMigration); + } } - this.tickingBlocks = BitSet.valueOf(tickingBlocksData); - this.tickingBlocksCount = this.tickingBlocks.cardinality(); - } + PaletteTypeEnum typeEnum = PaletteTypeEnum.get(buf.readByte()); + PaletteType paletteType = typeEnum.getPaletteType(); + this.chunkSection = typeEnum.getConstructor().get(); + if (blockMigration != null) { + this.chunkSection.deserialize(bytebuf -> { + String key = ByteBufUtil.readUTF(bytebuf); + key = blockMigration.apply(key); + return BlockType.getBlockIdOrUnknown(key, "Unknown BlockType %s", key); + }, buf, version); + } else { + this.chunkSection.deserialize(keyDeserializer, buf, version); + } + + if (paletteType != PaletteType.Empty) { + this.tickingBlocksCount = buf.readUnsignedShort(); + int len = buf.readUnsignedShort(); + long[] tickingBlocksData = new long[len]; + + for (int i = 0; i < tickingBlocksData.length; i++) { + tickingBlocksData[i] = buf.readLong(); + } + + this.tickingBlocks = BitSet.valueOf(tickingBlocksData); + this.tickingBlocksCount = this.tickingBlocks.cardinality(); + } - if (version >= 4) { PaletteTypeEnum fillerTypeEnum = PaletteTypeEnum.get(buf.readByte()); this.fillerSection = fillerTypeEnum.getConstructor().get(); this.fillerSection.deserialize(ByteBuf::readUnsignedShort, buf, version); - } - - if (version >= 5) { PaletteTypeEnum rotationTypeEnum = PaletteTypeEnum.get(buf.readByte()); this.rotationSection = rotationTypeEnum.getConstructor().get(); this.rotationSection.deserialize(ByteBuf::readUnsignedByte, buf, version); + this.localLight = ChunkLightData.deserialize(buf, version); + this.globalLight = ChunkLightData.deserialize(buf, version); + this.localChangeCounter = buf.readShort(); + this.globalChangeCounter = buf.readShort(); } - - this.localLight = ChunkLightData.deserialize(buf, version); - this.globalLight = ChunkLightData.deserialize(buf, version); - this.localChangeCounter = buf.readShort(); - this.globalChangeCounter = buf.readShort(); } public void deserialize(@Nonnull byte[] bytes, @Nonnull ExtraInfo extraInfo) { diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/FluidSection.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/FluidSection.java index 07e1174d..a6687e4f 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/FluidSection.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/FluidSection.java @@ -12,8 +12,8 @@ import com.hypixel.hytale.protocol.CachedPacket; import com.hypixel.hytale.protocol.packets.world.SetFluids; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.modules.LegacyModule; +import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.AbstractSectionPalette; import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.EmptySectionPalette; -import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.ISectionPalette; import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.PaletteTypeEnum; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.util.io.ByteBufUtil; @@ -42,7 +42,7 @@ public class FluidSection implements Component { private int y; private int z; private boolean loaded = false; - private ISectionPalette typePalette = EmptySectionPalette.INSTANCE; + private AbstractSectionPalette typePalette = EmptySectionPalette.INSTANCE; @Nullable private byte[] levelData = null; private int nonZeroLevels = 0; @@ -75,18 +75,18 @@ public class FluidSection implements Component { } private boolean setFluidRaw(int index, int fluidId) { - ISectionPalette.SetResult result = this.typePalette.set(index, fluidId); - if (result == ISectionPalette.SetResult.REQUIRES_PROMOTE) { + AbstractSectionPalette.SetResult result = this.typePalette.set(index, fluidId); + if (result == AbstractSectionPalette.SetResult.REQUIRES_PROMOTE) { this.typePalette = this.typePalette.promote(); result = this.typePalette.set(index, fluidId); - if (result != ISectionPalette.SetResult.ADDED_OR_REMOVED) { + if (result != AbstractSectionPalette.SetResult.ADDED_OR_REMOVED) { throw new IllegalStateException("Promoted fluid section failed to correctly add the new fluid"); } } else if (this.typePalette.shouldDemote()) { this.typePalette = this.typePalette.demote(); } - return result != ISectionPalette.SetResult.UNCHANGED; + return result != AbstractSectionPalette.SetResult.UNCHANGED; } public boolean setFluid(int x, int y, int z, @Nonnull Fluid fluid, byte level) { diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/blockpositions/BlockPositionProvider.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/blockpositions/BlockPositionProvider.java index 460578b4..c9aea120 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/blockpositions/BlockPositionProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/blockpositions/BlockPositionProvider.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.function.consumer.IntObjectConsumer; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.LegacyModule; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; @@ -19,6 +18,7 @@ import java.util.List; import java.util.function.BiPredicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BlockPositionProvider implements Component { private final BitSet searchedBlockSets; @@ -68,7 +68,7 @@ public class BlockPositionProvider implements Component { IBlockPositionData entry = data.get(i); double entryY = entry.getYCentre(); if (Math.abs(pos.y - entryY) <= yRange - && pos.distanceSquaredTo(entry.getXCentre(), entryY, entry.getZCentre()) <= range2 + && pos.distanceSquared(entry.getXCentre(), entryY, entry.getZCentre()) <= range2 && (filter == null || !filter.test(entry, obj))) { resultList.add(entry); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractByteSectionPalette.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractByteSectionPalette.java deleted file mode 100644 index ec25ef64..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractByteSectionPalette.java +++ /dev/null @@ -1,282 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; - -import com.hypixel.hytale.math.util.NumberUtil; -import io.netty.buffer.ByteBuf; -import it.unimi.dsi.fastutil.bytes.Byte2ByteMap; -import it.unimi.dsi.fastutil.bytes.Byte2ByteOpenHashMap; -import it.unimi.dsi.fastutil.bytes.Byte2IntMap; -import it.unimi.dsi.fastutil.bytes.Byte2IntOpenHashMap; -import it.unimi.dsi.fastutil.bytes.Byte2ShortMap; -import it.unimi.dsi.fastutil.bytes.Byte2ShortOpenHashMap; -import it.unimi.dsi.fastutil.bytes.Byte2ShortMap.Entry; -import it.unimi.dsi.fastutil.ints.Int2ByteMap; -import it.unimi.dsi.fastutil.ints.Int2ByteOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ShortMap; -import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; -import java.util.BitSet; -import java.util.function.IntConsumer; -import java.util.function.ToIntFunction; -import javax.annotation.Nonnull; - -public abstract class AbstractByteSectionPalette implements ISectionPalette { - protected final Int2ByteMap externalToInternal; - protected final Byte2IntMap internalToExternal; - protected final BitSet internalIdSet; - protected final Byte2ShortMap internalIdCount; - protected final byte[] blocks; - - protected AbstractByteSectionPalette(byte[] blocks) { - this(new Int2ByteOpenHashMap(), new Byte2IntOpenHashMap(), new BitSet(), new Byte2ShortOpenHashMap(), blocks); - this.externalToInternal.put(0, (byte)0); - this.internalToExternal.put((byte)0, 0); - this.internalIdSet.set(0); - this.internalIdCount.put((byte)0, (short)-32768); - } - - public AbstractByteSectionPalette(byte[] blocks, @Nonnull int[] data, int[] unique, int count) { - this(new Int2ByteOpenHashMap(count), new Byte2IntOpenHashMap(count), new BitSet(count), new Byte2ShortOpenHashMap(count), blocks); - - for (int internalId = 0; internalId < count; internalId++) { - int blockId = unique[internalId]; - this.internalToExternal.put((byte)internalId, blockId); - this.externalToInternal.put(blockId, (byte)internalId); - this.internalIdSet.set(this.unsignedInternalId((byte)internalId)); - this.internalIdCount.put((byte)internalId, (short)0); - } - - for (int index = 0; index < data.length; index++) { - int id = data[index]; - byte internalId = this.externalToInternal.get(id); - this.incrementBlockCount(internalId); - this.set0(index, internalId); - } - } - - protected AbstractByteSectionPalette( - Int2ByteMap externalToInternal, Byte2IntMap internalToExternal, BitSet internalIdSet, Byte2ShortMap internalIdCount, byte[] blocks - ) { - this.externalToInternal = externalToInternal; - this.internalToExternal = internalToExternal; - this.internalIdSet = internalIdSet; - this.internalIdCount = internalIdCount; - this.blocks = blocks; - } - - @Override - public int get(int index) { - byte internalId = this.get0(index); - return this.internalToExternal.get(internalId); - } - - @Nonnull - @Override - public ISectionPalette.SetResult set(int index, int id) { - byte oldInternalId = this.get0(index); - if (this.externalToInternal.containsKey(id)) { - byte newInternalId = this.externalToInternal.get(id); - if (newInternalId == oldInternalId) { - return ISectionPalette.SetResult.UNCHANGED; - } else { - boolean removed = this.decrementBlockCount(oldInternalId); - this.incrementBlockCount(newInternalId); - this.set0(index, newInternalId); - return removed ? ISectionPalette.SetResult.ADDED_OR_REMOVED : ISectionPalette.SetResult.CHANGED; - } - } else { - int nextInternalId = this.nextInternalId(oldInternalId); - if (!this.isValidInternalId(nextInternalId)) { - return ISectionPalette.SetResult.REQUIRES_PROMOTE; - } else { - this.decrementBlockCount(oldInternalId); - byte newInternalId = (byte)nextInternalId; - this.createBlockId(newInternalId, id); - this.set0(index, newInternalId); - return ISectionPalette.SetResult.ADDED_OR_REMOVED; - } - } - } - - protected abstract byte get0(int var1); - - protected abstract void set0(int var1, byte var2); - - @Override - public boolean contains(int id) { - return this.externalToInternal.containsKey(id); - } - - @Override - public boolean containsAny(@Nonnull IntList ids) { - for (int i = 0; i < ids.size(); i++) { - if (this.externalToInternal.containsKey(ids.getInt(i))) { - return true; - } - } - - return false; - } - - @Override - public int count() { - return this.internalIdCount.size(); - } - - @Override - public int count(int id) { - if (this.externalToInternal.containsKey(id)) { - byte internalId = this.externalToInternal.get(id); - return this.internalIdCount.get(internalId); - } else { - return 0; - } - } - - @Nonnull - @Override - public IntSet values() { - return new IntOpenHashSet(this.externalToInternal.keySet()); - } - - @Override - public void forEachValue(IntConsumer consumer) { - this.externalToInternal.keySet().forEach(consumer); - } - - @Nonnull - @Override - public Int2ShortMap valueCounts() { - Int2ShortMap map = new Int2ShortOpenHashMap(); - - for (Entry entry : this.internalIdCount.byte2ShortEntrySet()) { - byte internalId = entry.getByteKey(); - short count = entry.getShortValue(); - int externalId = this.internalToExternal.get(internalId); - map.put(externalId, count); - } - - return map; - } - - private void createBlockId(byte internalId, int blockId) { - this.internalToExternal.put(internalId, blockId); - this.externalToInternal.put(blockId, internalId); - this.internalIdSet.set(this.unsignedInternalId(internalId)); - this.internalIdCount.put(internalId, (short)1); - } - - private boolean decrementBlockCount(byte internalId) { - short oldCount = this.internalIdCount.get(internalId); - if (oldCount == 1) { - this.internalIdCount.remove(internalId); - int externalId = this.internalToExternal.remove(internalId); - this.externalToInternal.remove(externalId); - this.internalIdSet.clear(this.unsignedInternalId(internalId)); - return true; - } else { - this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::subtract); - return false; - } - } - - private void incrementBlockCount(byte internalId) { - this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::sum); - } - - private int nextInternalId(byte oldInternalId) { - return this.internalIdCount.get(oldInternalId) == 1 ? this.unsignedInternalId(oldInternalId) : this.internalIdSet.nextClearBit(0); - } - - protected abstract boolean isValidInternalId(int var1); - - protected abstract int unsignedInternalId(byte var1); - - @Override - public void serializeForPacket(@Nonnull ByteBuf buf) { - buf.writeShortLE(this.internalToExternal.size()); - - for (it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry entry : this.internalToExternal.byte2IntEntrySet()) { - byte internalId = entry.getByteKey(); - int externalId = entry.getIntValue(); - buf.writeByte(internalId); - buf.writeIntLE(externalId); - buf.writeShortLE(this.internalIdCount.get(internalId)); - } - - buf.writeBytes(this.blocks); - } - - @Override - public void serialize(@Nonnull ISectionPalette.KeySerializer keySerializer, @Nonnull ByteBuf buf) { - buf.writeShort(this.internalToExternal.size()); - - for (it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry entry : this.internalToExternal.byte2IntEntrySet()) { - byte internalId = entry.getByteKey(); - int externalId = entry.getIntValue(); - buf.writeByte(internalId); - keySerializer.serialize(buf, externalId); - buf.writeShort(this.internalIdCount.get(internalId)); - } - - buf.writeBytes(this.blocks); - } - - @Override - public void deserialize(@Nonnull ToIntFunction deserializer, @Nonnull ByteBuf buf, int version) { - this.externalToInternal.clear(); - this.internalToExternal.clear(); - this.internalIdSet.clear(); - this.internalIdCount.clear(); - Byte2ByteMap internalIdRemapping = null; - int blockCount = buf.readShort(); - - for (int i = 0; i < blockCount; i++) { - byte internalId = buf.readByte(); - int externalId = deserializer.applyAsInt(buf); - short count = buf.readShort(); - if (this.externalToInternal.containsKey(externalId)) { - byte existingInternalId = this.externalToInternal.get(externalId); - if (internalIdRemapping == null) { - internalIdRemapping = new Byte2ByteOpenHashMap(); - } - - internalIdRemapping.put(internalId, existingInternalId); - this.internalIdCount.mergeShort(existingInternalId, count, NumberUtil::sum); - } else { - this.externalToInternal.put(externalId, internalId); - this.internalToExternal.put(internalId, externalId); - this.internalIdSet.set(this.unsignedInternalId(internalId)); - this.internalIdCount.put(internalId, count); - } - } - - buf.readBytes(this.blocks); - if (internalIdRemapping != null) { - for (int ix = 0; ix < 32768; ix++) { - byte oldInternalId = this.get0(ix); - if (internalIdRemapping.containsKey(oldInternalId)) { - this.set0(ix, internalIdRemapping.get(oldInternalId)); - } - } - } - } - - @Override - public void find(@Nonnull IntList ids, @Nonnull IntSet internalIdHolder, @Nonnull IntConsumer indexConsumer) { - for (int i = 0; i < ids.size(); i++) { - byte internal = this.externalToInternal.getOrDefault(ids.getInt(i), (byte)-128); - if (internal != -128) { - internalIdHolder.add(internal); - } - } - - for (int ix = 0; ix < 32768; ix++) { - byte type = this.get0(ix); - if (internalIdHolder.contains(type)) { - indexConsumer.accept(ix); - } - } - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractSectionPalette.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractSectionPalette.java new file mode 100644 index 00000000..e8bd7492 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractSectionPalette.java @@ -0,0 +1,85 @@ +package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; + +import com.hypixel.hytale.function.consumer.BiIntConsumer; +import com.hypixel.hytale.protocol.packets.world.PaletteType; +import io.netty.buffer.ByteBuf; +import it.unimi.dsi.fastutil.ints.Int2ShortMap; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntSet; +import java.util.function.IntConsumer; +import java.util.function.ToIntFunction; +import javax.annotation.Nonnull; + +public abstract sealed class AbstractSectionPalette permits EmptySectionPalette, HalfByteSectionPalette, ByteSectionPalette, ShortSectionPalette { + public abstract PaletteType getPaletteType(); + + public abstract AbstractSectionPalette.SetResult set(int var1, int var2); + + public abstract int get(int var1); + + public abstract boolean contains(int var1); + + public abstract boolean containsAny(IntList var1); + + public boolean isSolid(int id) { + return this.count() == 1 && this.contains(id); + } + + public abstract int count(); + + public abstract int count(int var1); + + public abstract IntSet values(); + + public abstract void forEachValue(IntConsumer var1); + + public abstract Int2ShortMap valueCounts(); + + public abstract void find(@Nonnull IntList var1, @Nonnull IntConsumer var2); + + public abstract void find(@Nonnull IntList var1, @Nonnull BiIntConsumer var2); + + @Deprecated(since = "2026-02-26", forRemoval = true) + public void find(@Nonnull IntList ids, @Nonnull IntSet ignoredInternalIdHolder, @Nonnull IntConsumer indexConsumer) { + this.find(ids, indexConsumer); + } + + public abstract boolean shouldDemote(); + + public abstract AbstractSectionPalette demote(); + + public abstract AbstractSectionPalette promote(); + + public abstract void serializeForPacket(ByteBuf var1); + + public abstract void serialize(AbstractSectionPalette.KeySerializer var1, ByteBuf var2); + + public abstract void deserialize(ToIntFunction var1, ByteBuf var2, int var3); + + @Nonnull + public static AbstractSectionPalette from(@Nonnull int[] data, @Nonnull Int2ShortMap idCounts) { + if (idCounts.size() == 1 && idCounts.containsKey(0)) { + return EmptySectionPalette.INSTANCE; + } else if (idCounts.size() < 16) { + return new HalfByteSectionPalette(data, idCounts); + } else if (idCounts.size() < 256) { + return new ByteSectionPalette(data, idCounts); + } else if (idCounts.size() < 65536) { + return new ShortSectionPalette(data, idCounts); + } else { + throw new UnsupportedOperationException("Too many block types for palette."); + } + } + + @FunctionalInterface + public interface KeySerializer { + void serialize(ByteBuf var1, int var2); + } + + public static enum SetResult { + ADDED_OR_REMOVED, + CHANGED, + UNCHANGED, + REQUIRES_PROMOTE; + } +} diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractShortSectionPalette.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractShortSectionPalette.java deleted file mode 100644 index 429ac666..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/AbstractShortSectionPalette.java +++ /dev/null @@ -1,283 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; - -import com.hypixel.hytale.math.util.NumberUtil; -import io.netty.buffer.ByteBuf; -import it.unimi.dsi.fastutil.ints.Int2ShortMap; -import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.shorts.Short2IntMap; -import it.unimi.dsi.fastutil.shorts.Short2IntOpenHashMap; -import it.unimi.dsi.fastutil.shorts.Short2ShortMap; -import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap; -import it.unimi.dsi.fastutil.shorts.Short2ShortMap.Entry; -import java.util.BitSet; -import java.util.function.IntConsumer; -import java.util.function.ToIntFunction; -import javax.annotation.Nonnull; - -public abstract class AbstractShortSectionPalette implements ISectionPalette { - protected final Int2ShortMap externalToInternal; - protected final Short2IntMap internalToExternal; - protected final BitSet internalIdSet; - protected final Short2ShortMap internalIdCount; - protected final short[] blocks; - - public AbstractShortSectionPalette(short[] blocks) { - this(new Int2ShortOpenHashMap(), new Short2IntOpenHashMap(), new BitSet(), new Short2ShortOpenHashMap(), blocks); - this.externalToInternal.put(0, (short)0); - this.internalToExternal.put((short)0, 0); - this.internalIdSet.set(0); - this.internalIdCount.put((short)0, (short)-32768); - } - - public AbstractShortSectionPalette(short[] blocks, @Nonnull int[] data, int[] unique, int count) { - this(new Int2ShortOpenHashMap(count), new Short2IntOpenHashMap(count), new BitSet(count), new Short2ShortOpenHashMap(count), blocks); - - for (int internalId = 0; internalId < count; internalId++) { - int blockId = unique[internalId]; - this.internalToExternal.put((short)internalId, blockId); - this.externalToInternal.put(blockId, (short)internalId); - this.internalIdSet.set(internalId); - this.internalIdCount.put((short)internalId, (short)0); - } - - for (int index = 0; index < data.length; index++) { - int id = data[index]; - short internalId = this.externalToInternal.get(id); - this.incrementBlockCount(internalId); - this.set0(index, internalId); - } - } - - protected AbstractShortSectionPalette( - Int2ShortMap externalToInternal, Short2IntMap internalToExternal, BitSet internalIdSet, Short2ShortMap internalIdCount, short[] blocks - ) { - this.externalToInternal = externalToInternal; - this.internalToExternal = internalToExternal; - this.internalIdSet = internalIdSet; - this.internalIdCount = internalIdCount; - this.blocks = blocks; - } - - @Override - public int get(int index) { - short internalId = this.get0(index); - return this.internalToExternal.get(internalId); - } - - @Nonnull - @Override - public ISectionPalette.SetResult set(int index, int id) { - short oldInternalId = this.get0(index); - if (this.externalToInternal.containsKey(id)) { - short newInternalId = this.externalToInternal.get(id); - if (newInternalId == oldInternalId) { - return ISectionPalette.SetResult.UNCHANGED; - } else { - boolean removed = this.decrementBlockCount(oldInternalId); - this.incrementBlockCount(newInternalId); - this.set0(index, newInternalId); - return removed ? ISectionPalette.SetResult.ADDED_OR_REMOVED : ISectionPalette.SetResult.CHANGED; - } - } else { - int nextInternalId = this.nextInternalId(oldInternalId); - if (!this.isValidInternalId(nextInternalId)) { - return ISectionPalette.SetResult.REQUIRES_PROMOTE; - } else { - this.decrementBlockCount(oldInternalId); - short newInternalId = (short)nextInternalId; - this.createBlockId(newInternalId, id); - this.set0(index, newInternalId); - return ISectionPalette.SetResult.ADDED_OR_REMOVED; - } - } - } - - protected abstract short get0(int var1); - - protected abstract void set0(int var1, short var2); - - @Override - public boolean contains(int id) { - return this.externalToInternal.containsKey(id); - } - - @Override - public boolean containsAny(@Nonnull IntList ids) { - for (int i = 0; i < ids.size(); i++) { - if (this.externalToInternal.containsKey(ids.getInt(i))) { - return true; - } - } - - return false; - } - - @Override - public int count() { - return this.internalIdCount.size(); - } - - @Override - public int count(int id) { - if (this.externalToInternal.containsKey(id)) { - short internalId = this.externalToInternal.get(id); - return this.internalIdCount.get(internalId); - } else { - return 0; - } - } - - @Nonnull - @Override - public IntSet values() { - return new IntOpenHashSet(this.externalToInternal.keySet()); - } - - @Override - public void forEachValue(IntConsumer consumer) { - this.externalToInternal.keySet().forEach(consumer); - } - - @Nonnull - @Override - public Int2ShortMap valueCounts() { - Int2ShortMap map = new Int2ShortOpenHashMap(); - - for (Entry entry : this.internalIdCount.short2ShortEntrySet()) { - short internalId = entry.getShortKey(); - short count = entry.getShortValue(); - int externalId = this.internalToExternal.get(internalId); - map.put(externalId, count); - } - - return map; - } - - private void createBlockId(short internalId, int blockId) { - this.internalToExternal.put(internalId, blockId); - this.externalToInternal.put(blockId, internalId); - this.internalIdSet.set(internalId); - this.internalIdCount.put(internalId, (short)1); - } - - private boolean decrementBlockCount(short internalId) { - short oldCount = this.internalIdCount.get(internalId); - if (oldCount == 1) { - this.internalIdCount.remove(internalId); - int externalId = this.internalToExternal.remove(internalId); - this.externalToInternal.remove(externalId); - this.internalIdSet.clear(internalId); - return true; - } else { - this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::subtract); - return false; - } - } - - private void incrementBlockCount(short internalId) { - this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::sum); - } - - private int nextInternalId(short oldInternalId) { - return this.internalIdCount.get(oldInternalId) == 1 ? oldInternalId : this.internalIdSet.nextClearBit(0); - } - - protected abstract boolean isValidInternalId(int var1); - - @Override - public void serializeForPacket(@Nonnull ByteBuf buf) { - buf.writeShortLE(this.internalToExternal.size()); - - for (it.unimi.dsi.fastutil.shorts.Short2IntMap.Entry entry : this.internalToExternal.short2IntEntrySet()) { - short internalId = entry.getShortKey(); - int externalId = entry.getIntValue(); - buf.writeShortLE(internalId & '\uffff'); - buf.writeIntLE(externalId); - buf.writeShortLE(this.internalIdCount.get(internalId)); - } - - for (int i = 0; i < this.blocks.length; i++) { - buf.writeShortLE(this.blocks[i]); - } - } - - @Override - public void serialize(@Nonnull ISectionPalette.KeySerializer keySerializer, @Nonnull ByteBuf buf) { - buf.writeShort(this.internalToExternal.size()); - - for (it.unimi.dsi.fastutil.shorts.Short2IntMap.Entry entry : this.internalToExternal.short2IntEntrySet()) { - short internalId = entry.getShortKey(); - int externalId = entry.getIntValue(); - buf.writeShort(internalId & '\uffff'); - keySerializer.serialize(buf, externalId); - buf.writeShort(this.internalIdCount.get(internalId)); - } - - for (int i = 0; i < this.blocks.length; i++) { - buf.writeShort(this.blocks[i]); - } - } - - @Override - public void deserialize(@Nonnull ToIntFunction deserializer, @Nonnull ByteBuf buf, int version) { - this.externalToInternal.clear(); - this.internalToExternal.clear(); - this.internalIdSet.clear(); - this.internalIdCount.clear(); - Short2ShortMap internalIdRemapping = null; - int blockCount = buf.readShort(); - - for (int i = 0; i < blockCount; i++) { - short internalId = buf.readShort(); - int externalId = deserializer.applyAsInt(buf); - short count = buf.readShort(); - if (this.externalToInternal.containsKey(externalId)) { - short existingInternalId = this.externalToInternal.get(externalId); - if (internalIdRemapping == null) { - internalIdRemapping = new Short2ShortOpenHashMap(); - } - - internalIdRemapping.put(internalId, existingInternalId); - this.internalIdCount.mergeShort(existingInternalId, count, NumberUtil::sum); - } else { - this.externalToInternal.put(externalId, internalId); - this.internalToExternal.put(internalId, externalId); - this.internalIdSet.set(internalId); - this.internalIdCount.put(internalId, count); - } - } - - for (int ix = 0; ix < this.blocks.length; ix++) { - this.blocks[ix] = buf.readShort(); - } - - if (internalIdRemapping != null) { - for (int ix = 0; ix < 32768; ix++) { - short oldInternalId = this.get0(ix); - if (internalIdRemapping.containsKey(oldInternalId)) { - this.set0(ix, internalIdRemapping.get(oldInternalId)); - } - } - } - } - - @Override - public void find(@Nonnull IntList ids, @Nonnull IntSet internalIdHolder, @Nonnull IntConsumer indexConsumer) { - for (int i = 0; i < ids.size(); i++) { - short internal = this.externalToInternal.getOrDefault(ids.getInt(i), (short)-32768); - if (internal != -32768) { - internalIdHolder.add(internal); - } - } - - for (int ix = 0; ix < 32768; ix++) { - short type = this.get0(ix); - if (internalIdHolder.contains(type)) { - indexConsumer.accept(ix); - } - } - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ByteSectionPalette.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ByteSectionPalette.java index 86785557..6cd8c3c8 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ByteSectionPalette.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ByteSectionPalette.java @@ -1,31 +1,96 @@ package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; import com.hypixel.hytale.common.util.BitSetUtil; +import com.hypixel.hytale.common.util.BitUtil; +import com.hypixel.hytale.function.consumer.BiIntConsumer; +import com.hypixel.hytale.math.util.NumberUtil; import com.hypixel.hytale.protocol.packets.world.PaletteType; +import io.netty.buffer.ByteBuf; +import it.unimi.dsi.fastutil.bytes.Byte2ByteMap; +import it.unimi.dsi.fastutil.bytes.Byte2ByteOpenHashMap; import it.unimi.dsi.fastutil.bytes.Byte2IntMap; +import it.unimi.dsi.fastutil.bytes.Byte2IntOpenHashMap; import it.unimi.dsi.fastutil.bytes.Byte2ShortMap; +import it.unimi.dsi.fastutil.bytes.Byte2ShortOpenHashMap; +import it.unimi.dsi.fastutil.bytes.ByteSet; import it.unimi.dsi.fastutil.ints.Int2ByteMap; +import it.unimi.dsi.fastutil.ints.Int2ByteOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ShortMap; +import it.unimi.dsi.fastutil.ints.Int2ShortMaps; +import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.Int2ShortMap.Entry; import it.unimi.dsi.fastutil.shorts.Short2ByteMap; import it.unimi.dsi.fastutil.shorts.Short2ByteOpenHashMap; -import it.unimi.dsi.fastutil.shorts.Short2IntMap.Entry; import java.util.BitSet; +import java.util.function.IntConsumer; +import java.util.function.ToIntFunction; import javax.annotation.Nonnull; -public class ByteSectionPalette extends AbstractByteSectionPalette { +public final class ByteSectionPalette extends AbstractSectionPalette { private static final int KEY_MASK = 255; public static final int MAX_SIZE = 256; public static final int DEMOTE_SIZE = 14; + final Int2ByteMap externalToInternal; + final Byte2IntMap internalToExternal; + final BitSet internalIdSet; + final Byte2ShortMap internalIdCount; + final byte[] blocks; public ByteSectionPalette() { - super(new byte[32768]); + this.externalToInternal = new Int2ByteOpenHashMap(); + this.internalToExternal = new Byte2IntOpenHashMap(); + this.internalIdSet = new BitSet(256); + this.internalIdCount = new Byte2ShortOpenHashMap(); + this.blocks = new byte[32768]; + this.externalToInternal.put(0, (byte)0); + this.internalToExternal.put((byte)0, 0); + this.internalIdSet.set(0); + this.internalIdCount.put((byte)0, (short)-32768); } - public ByteSectionPalette(Int2ByteMap externalToInternal, Byte2IntMap internalToExternal, BitSet internalIdSet, Byte2ShortMap internalIdCount, byte[] blocks) { - super(externalToInternal, internalToExternal, internalIdSet, internalIdCount, blocks); + ByteSectionPalette(Int2ByteMap externalToInternal, Byte2IntMap internalToExternal, BitSet internalIdSet, Byte2ShortMap internalIdCount, byte[] blocks) { + this.externalToInternal = externalToInternal; + this.internalToExternal = internalToExternal; + this.internalIdSet = internalIdSet; + this.internalIdCount = internalIdCount; + this.blocks = blocks; } - public ByteSectionPalette(@Nonnull int[] data, int[] unique, int count) { - super(new byte[32768], data, unique, count); + public ByteSectionPalette(@Nonnull int[] data, @Nonnull Int2ShortMap externalIdCounts) { + this.blocks = new byte[32768]; + this.externalToInternal = new Int2ByteOpenHashMap(externalIdCounts.size()); + this.internalToExternal = new Byte2IntOpenHashMap(externalIdCounts.size()); + this.internalIdSet = new BitSet(externalIdCounts.size()); + this.internalIdCount = new Byte2ShortOpenHashMap(externalIdCounts.size()); + byte internalIdCounter = 0; + + for (Entry entry : Int2ShortMaps.fastIterable(externalIdCounts)) { + this.internalToExternal.put(internalIdCounter, entry.getIntKey()); + this.externalToInternal.put(entry.getIntKey(), internalIdCounter); + this.internalIdSet.set(this.unsignedInternalId(internalIdCounter)); + this.internalIdCount.put(internalIdCounter, entry.getShortValue()); + internalIdCounter++; + } + + int index = 0; + + while (index < data.length) { + int externalId = data[index]; + int start = index; + + do { + index++; + } while (index < data.length && data[index] == externalId); + + byte internalId = this.externalToInternal.get(externalId); + + for (int i = start; i < index; i++) { + this.blocks[i] = internalId; + } + } } @Nonnull @@ -35,13 +100,122 @@ public class ByteSectionPalette extends AbstractByteSectionPalette { } @Override - protected byte get0(int idx) { - return this.blocks[idx]; + public int get(int index) { + return this.internalToExternal.get(this.blocks[index]); + } + + @Nonnull + @Override + public AbstractSectionPalette.SetResult set(int index, int id) { + byte oldInternalId = this.blocks[index]; + if (this.externalToInternal.containsKey(id)) { + byte newInternalId = this.externalToInternal.get(id); + if (newInternalId == oldInternalId) { + return AbstractSectionPalette.SetResult.UNCHANGED; + } else { + boolean removed = this.decrementBlockCount(oldInternalId); + this.incrementBlockCount(newInternalId); + this.blocks[index] = newInternalId; + return removed ? AbstractSectionPalette.SetResult.ADDED_OR_REMOVED : AbstractSectionPalette.SetResult.CHANGED; + } + } else { + int nextInternalId = this.nextInternalId(oldInternalId); + if (!this.isValidInternalId(nextInternalId)) { + return AbstractSectionPalette.SetResult.REQUIRES_PROMOTE; + } else { + this.decrementBlockCount(oldInternalId); + byte newInternalId = (byte)nextInternalId; + this.createBlockId(newInternalId, id); + this.blocks[index] = newInternalId; + return AbstractSectionPalette.SetResult.ADDED_OR_REMOVED; + } + } + } + + private int nextInternalId(byte oldInternalId) { + return this.internalIdCount.get(oldInternalId) == 1 ? this.unsignedInternalId(oldInternalId) : this.internalIdSet.nextClearBit(0); + } + + private void createBlockId(byte internalId, int blockId) { + this.internalToExternal.put(internalId, blockId); + this.externalToInternal.put(blockId, internalId); + this.internalIdSet.set(this.unsignedInternalId(internalId)); + this.internalIdCount.put(internalId, (short)1); + } + + private boolean decrementBlockCount(byte internalId) { + short oldCount = this.internalIdCount.get(internalId); + if (oldCount == 1) { + this.internalIdCount.remove(internalId); + int externalId = this.internalToExternal.remove(internalId); + this.externalToInternal.remove(externalId); + this.internalIdSet.clear(this.unsignedInternalId(internalId)); + return true; + } else { + this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::subtract); + return false; + } + } + + private void incrementBlockCount(byte internalId) { + this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::sum); } @Override - protected void set0(int idx, byte b) { - this.blocks[idx] = b; + public boolean contains(int id) { + return this.externalToInternal.containsKey(id); + } + + @Override + public boolean containsAny(@Nonnull IntList ids) { + for (int i = 0; i < ids.size(); i++) { + if (this.externalToInternal.containsKey(ids.getInt(i))) { + return true; + } + } + + return false; + } + + @Override + public int count() { + return this.internalIdCount.size(); + } + + @Override + public int count(int id) { + if (this.externalToInternal.containsKey(id)) { + byte internalId = this.externalToInternal.get(id); + return this.internalIdCount.get(internalId); + } else { + return 0; + } + } + + @Nonnull + @Override + public IntSet values() { + return new IntOpenHashSet(this.externalToInternal.keySet()); + } + + @Override + public void forEachValue(@Nonnull IntConsumer consumer) { + this.externalToInternal.keySet().forEach(consumer); + } + + @Nonnull + @Override + public Int2ShortMap valueCounts() { + Int2ShortMap map = new Int2ShortOpenHashMap(); + + for (it.unimi.dsi.fastutil.bytes.Byte2ShortMap.Entry entry : this.internalIdCount.byte2ShortEntrySet()) { + byte internalId = entry.getByteKey(); + short count = entry.getShortValue(); + int externalId = this.internalToExternal.get(internalId); + map.put(externalId, count); + } + + return map; } @Override @@ -59,13 +233,11 @@ public class ByteSectionPalette extends AbstractByteSectionPalette { return ShortSectionPalette.fromBytePalette(this); } - @Override - protected boolean isValidInternalId(int internalId) { + private boolean isValidInternalId(int internalId) { return (internalId & 0xFF) == internalId; } - @Override - protected int unsignedInternalId(byte internalId) { + private int unsignedInternalId(byte internalId) { return internalId & 0xFF; } @@ -73,22 +245,153 @@ public class ByteSectionPalette extends AbstractByteSectionPalette { return internalId & 0xFF; } - @Nonnull - public static ByteSectionPalette fromHalfBytePalette(@Nonnull HalfByteSectionPalette section) { - ByteSectionPalette byteSection = new ByteSectionPalette(); - byteSection.externalToInternal.clear(); - byteSection.externalToInternal.putAll(section.externalToInternal); - byteSection.internalToExternal.clear(); - byteSection.internalToExternal.putAll(section.internalToExternal); - BitSetUtil.copyValues(section.internalIdSet, byteSection.internalIdSet); - byteSection.internalIdCount.clear(); - byteSection.internalIdCount.putAll(section.internalIdCount); + @Override + public void find(@Nonnull IntList ids, @Nonnull IntConsumer indexConsumer) { + ByteSet internalIds = this.buildInternalByteSet(ids); + if (!internalIds.isEmpty()) { + int index = 0; + byte type = this.blocks[0]; - for (int i = 0; i < byteSection.blocks.length; i++) { - byteSection.blocks[i] = section.get0(i); + while (index < 32768) { + int start = index; + byte runType = type; + + do { + index++; + } while (index < 32768 && (type = this.blocks[index]) == runType); + + if (internalIds.contains(runType)) { + for (int i = start; i < index; i++) { + indexConsumer.accept(i); + } + } + } + } + } + + @Override + public void find(@Nonnull IntList ids, @Nonnull BiIntConsumer indexBlockConsumer) { + ByteSet internalIds = this.buildInternalByteSet(ids); + if (!internalIds.isEmpty()) { + int index = 0; + byte type = this.blocks[0]; + + while (index < 32768) { + int start = index; + byte runType = type; + + do { + index++; + } while (index < 32768 && (type = this.blocks[index]) == runType); + + if (internalIds.contains(runType)) { + int external = this.internalToExternal.get(runType); + + for (int i = start; i < index; i++) { + indexBlockConsumer.accept(i, external); + } + } + } + } + } + + private ByteSet buildInternalByteSet(IntList ids) { + ByteSet internalIds = PaletteSetProvider.get().getByteSet(ids.size()); + + for (int i = 0; i < ids.size(); i++) { + byte internal = this.externalToInternal.getOrDefault(ids.getInt(i), (byte)-128); + if (internal != -128) { + internalIds.add(internal); + } } - return byteSection; + return internalIds; + } + + @Override + public void serializeForPacket(@Nonnull ByteBuf buf) { + buf.writeShortLE(this.internalToExternal.size()); + + for (it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry entry : this.internalToExternal.byte2IntEntrySet()) { + byte internalId = entry.getByteKey(); + int externalId = entry.getIntValue(); + buf.writeByte(internalId); + buf.writeIntLE(externalId); + buf.writeShortLE(this.internalIdCount.get(internalId)); + } + + buf.writeBytes(this.blocks); + } + + @Override + public void serialize(@Nonnull AbstractSectionPalette.KeySerializer keySerializer, @Nonnull ByteBuf buf) { + buf.writeShort(this.internalToExternal.size()); + + for (it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry entry : this.internalToExternal.byte2IntEntrySet()) { + byte internalId = entry.getByteKey(); + int externalId = entry.getIntValue(); + buf.writeByte(internalId); + keySerializer.serialize(buf, externalId); + buf.writeShort(this.internalIdCount.get(internalId)); + } + + buf.writeBytes(this.blocks); + } + + @Override + public void deserialize(@Nonnull ToIntFunction deserializer, @Nonnull ByteBuf buf, int version) { + this.externalToInternal.clear(); + this.internalToExternal.clear(); + this.internalIdSet.clear(); + this.internalIdCount.clear(); + Byte2ByteMap internalIdRemapping = null; + int blockCount = buf.readShort(); + + for (int i = 0; i < blockCount; i++) { + byte internalId = buf.readByte(); + int externalId = deserializer.applyAsInt(buf); + short count = buf.readShort(); + if (this.externalToInternal.containsKey(externalId)) { + byte existingInternalId = this.externalToInternal.get(externalId); + if (internalIdRemapping == null) { + internalIdRemapping = new Byte2ByteOpenHashMap(); + } + + internalIdRemapping.put(internalId, existingInternalId); + this.internalIdCount.mergeShort(existingInternalId, count, NumberUtil::sum); + } else { + this.externalToInternal.put(externalId, internalId); + this.internalToExternal.put(internalId, externalId); + this.internalIdSet.set(this.unsignedInternalId(internalId)); + this.internalIdCount.put(internalId, count); + } + } + + buf.readBytes(this.blocks); + if (internalIdRemapping != null) { + for (int ix = 0; ix < 32768; ix++) { + byte oldInternalId = this.blocks[ix]; + if (internalIdRemapping.containsKey(oldInternalId)) { + this.blocks[ix] = internalIdRemapping.get(oldInternalId); + } + } + } + } + + @Nonnull + public static ByteSectionPalette fromHalfBytePalette(@Nonnull HalfByteSectionPalette section) { + Int2ByteMap externalToInternal = new Int2ByteOpenHashMap(section.externalToInternal); + Byte2IntMap internalToExternal = new Byte2IntOpenHashMap(section.internalToExternal); + BitSet internalIdSet = new BitSet(256); + BitSetUtil.copyValues(section.internalIdSet, internalIdSet); + Byte2ShortMap internalIdCount = new Byte2ShortOpenHashMap(section.internalIdCount); + byte[] newBlocks = new byte[32768]; + + for (int i = 0; i < 32768; i++) { + newBlocks[i] = BitUtil.getNibble(section.blocks, i); + } + + return new ByteSectionPalette(externalToInternal, internalToExternal, internalIdSet, internalIdCount, newBlocks); } @Nonnull @@ -96,31 +399,33 @@ public class ByteSectionPalette extends AbstractByteSectionPalette { if (section.count() > 256) { throw new IllegalStateException("Cannot demote short palette to byte palette. Too many blocks! Count: " + section.count()); } else { - ByteSectionPalette byteSection = new ByteSectionPalette(); - Short2ByteMap internalIdRemapping = new Short2ByteOpenHashMap(); - byteSection.internalToExternal.clear(); - byteSection.externalToInternal.clear(); - byteSection.internalIdSet.clear(); - byteSection.internalIdCount.clear(); + Int2ByteMap externalToInternal = new Int2ByteOpenHashMap(section.count()); + Byte2IntMap internalToExternal = new Byte2IntOpenHashMap(section.count()); + BitSet internalIdSet = new BitSet(256); + Byte2ShortMap internalIdCount = new Byte2ShortOpenHashMap(section.count()); + Short2ByteMap internalIdRemapping = new Short2ByteOpenHashMap(section.count()); + byte nextId = 0; - for (Entry entry : section.internalToExternal.short2IntEntrySet()) { - short oldInternalId = entry.getShortKey(); - int externalId = entry.getIntValue(); - byte newInternalId = (byte)byteSection.internalIdSet.nextClearBit(0); - byteSection.internalIdSet.set(sUnsignedInternalId(newInternalId)); - internalIdRemapping.put(oldInternalId, newInternalId); - byteSection.internalToExternal.put(newInternalId, externalId); - byteSection.externalToInternal.put(externalId, newInternalId); - byteSection.internalIdCount.put(newInternalId, section.internalIdCount.get(oldInternalId)); + for (it.unimi.dsi.fastutil.shorts.Short2IntMap.Entry entry : section.internalToExternal.short2IntEntrySet()) { + short oldInternal = entry.getShortKey(); + int external = entry.getIntValue(); + byte newInternal = nextId++; + internalToExternal.put(newInternal, external); + externalToInternal.put(external, newInternal); + internalIdSet.set(sUnsignedInternalId(newInternal)); + internalIdCount.put(newInternal, section.internalIdCount.get(oldInternal)); + internalIdRemapping.put(oldInternal, newInternal); } + byte[] newBlocks = new byte[32768]; + for (int i = 0; i < 32768; i++) { short internalId = section.blocks[i]; byte byteInternalId = internalIdRemapping.get(internalId); - byteSection.blocks[i] = byteInternalId; + newBlocks[i] = byteInternalId; } - return byteSection; + return new ByteSectionPalette(externalToInternal, internalToExternal, internalIdSet, internalIdCount, newBlocks); } } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/EmptySectionPalette.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/EmptySectionPalette.java index 6880c968..4ac8244a 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/EmptySectionPalette.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/EmptySectionPalette.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; +import com.hypixel.hytale.function.consumer.BiIntConsumer; import com.hypixel.hytale.protocol.packets.world.PaletteType; import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.Int2ShortMap; @@ -11,7 +12,7 @@ import java.util.function.IntConsumer; import java.util.function.ToIntFunction; import javax.annotation.Nonnull; -public class EmptySectionPalette implements ISectionPalette { +public final class EmptySectionPalette extends AbstractSectionPalette { public static final int EMPTY_ID = 0; public static final EmptySectionPalette INSTANCE = new EmptySectionPalette(); @@ -26,8 +27,8 @@ public class EmptySectionPalette implements ISectionPalette { @Nonnull @Override - public ISectionPalette.SetResult set(int index, int id) { - return id == 0 ? ISectionPalette.SetResult.UNCHANGED : ISectionPalette.SetResult.REQUIRES_PROMOTE; + public AbstractSectionPalette.SetResult set(int index, int id) { + return id == 0 ? AbstractSectionPalette.SetResult.UNCHANGED : AbstractSectionPalette.SetResult.REQUIRES_PROMOTE; } @Override @@ -41,13 +42,13 @@ public class EmptySectionPalette implements ISectionPalette { } @Override - public ISectionPalette demote() { + public AbstractSectionPalette demote() { throw new UnsupportedOperationException("Cannot demote empty chunk section!"); } @Nonnull @Override - public ISectionPalette promote() { + public AbstractSectionPalette promote() { return new HalfByteSectionPalette(); } @@ -98,7 +99,7 @@ public class EmptySectionPalette implements ISectionPalette { } @Override - public void find(@Nonnull IntList ids, IntSet internalIdHolder, @Nonnull IntConsumer indexConsumer) { + public void find(@Nonnull IntList ids, @Nonnull IntConsumer indexConsumer) { if (ids.contains(0)) { for (int i = 0; i < 32768; i++) { indexConsumer.accept(i); @@ -106,12 +107,21 @@ public class EmptySectionPalette implements ISectionPalette { } } + @Override + public void find(@Nonnull IntList ids, @Nonnull BiIntConsumer indexBlockConsumer) { + if (ids.contains(0)) { + for (int i = 0; i < 32768; i++) { + indexBlockConsumer.accept(i, 0); + } + } + } + @Override public void serializeForPacket(ByteBuf buf) { } @Override - public void serialize(ISectionPalette.KeySerializer keySerializer, ByteBuf buf) { + public void serialize(AbstractSectionPalette.KeySerializer keySerializer, ByteBuf buf) { } @Override diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/HalfByteSectionPalette.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/HalfByteSectionPalette.java index c40b72a1..68724397 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/HalfByteSectionPalette.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/HalfByteSectionPalette.java @@ -1,32 +1,92 @@ package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; import com.hypixel.hytale.common.util.BitUtil; +import com.hypixel.hytale.function.consumer.BiIntConsumer; +import com.hypixel.hytale.math.util.NumberUtil; import com.hypixel.hytale.protocol.packets.world.PaletteType; +import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.bytes.Byte2ByteMap; import it.unimi.dsi.fastutil.bytes.Byte2ByteOpenHashMap; import it.unimi.dsi.fastutil.bytes.Byte2IntMap; +import it.unimi.dsi.fastutil.bytes.Byte2IntOpenHashMap; import it.unimi.dsi.fastutil.bytes.Byte2ShortMap; -import it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry; +import it.unimi.dsi.fastutil.bytes.Byte2ShortOpenHashMap; +import it.unimi.dsi.fastutil.bytes.ByteSet; import it.unimi.dsi.fastutil.ints.Int2ByteMap; +import it.unimi.dsi.fastutil.ints.Int2ByteOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ShortMap; +import it.unimi.dsi.fastutil.ints.Int2ShortMaps; +import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.Int2ShortMap.Entry; import java.util.BitSet; +import java.util.function.IntConsumer; +import java.util.function.ToIntFunction; import javax.annotation.Nonnull; -public class HalfByteSectionPalette extends AbstractByteSectionPalette { +public final class HalfByteSectionPalette extends AbstractSectionPalette { private static final int KEY_MASK = 15; public static final int MAX_SIZE = 16; + final Int2ByteMap externalToInternal; + final Byte2IntMap internalToExternal; + final BitSet internalIdSet; + final Byte2ShortMap internalIdCount; + final byte[] blocks; public HalfByteSectionPalette() { - super(new byte[16384]); + this.externalToInternal = new Int2ByteOpenHashMap(); + this.internalToExternal = new Byte2IntOpenHashMap(); + this.internalIdSet = new BitSet(16); + this.internalIdCount = new Byte2ShortOpenHashMap(); + this.blocks = new byte[16384]; + this.externalToInternal.put(0, (byte)0); + this.internalToExternal.put((byte)0, 0); + this.internalIdSet.set(0); + this.internalIdCount.put((byte)0, (short)-32768); } - public HalfByteSectionPalette( - Int2ByteMap externalToInternal, Byte2IntMap internalToExternal, BitSet internalIdSet, Byte2ShortMap internalIdCount, byte[] blocks - ) { - super(externalToInternal, internalToExternal, internalIdSet, internalIdCount, blocks); + HalfByteSectionPalette(Int2ByteMap externalToInternal, Byte2IntMap internalToExternal, BitSet internalIdSet, Byte2ShortMap internalIdCount, byte[] blocks) { + this.externalToInternal = externalToInternal; + this.internalToExternal = internalToExternal; + this.internalIdSet = internalIdSet; + this.internalIdCount = internalIdCount; + this.blocks = blocks; } - public HalfByteSectionPalette(@Nonnull int[] data, int[] unique, int count) { - super(new byte[16384], data, unique, count); + public HalfByteSectionPalette(@Nonnull int[] data, @Nonnull Int2ShortMap externalIdCounts) { + this.blocks = new byte[16384]; + this.externalToInternal = new Int2ByteOpenHashMap(externalIdCounts.size()); + this.internalToExternal = new Byte2IntOpenHashMap(externalIdCounts.size()); + this.internalIdSet = new BitSet(externalIdCounts.size()); + this.internalIdCount = new Byte2ShortOpenHashMap(externalIdCounts.size()); + byte internalIdCounter = 0; + + for (Entry entry : Int2ShortMaps.fastIterable(externalIdCounts)) { + this.internalToExternal.put(internalIdCounter, entry.getIntKey()); + this.externalToInternal.put(entry.getIntKey(), internalIdCounter); + this.internalIdSet.set(this.unsignedInternalId(internalIdCounter)); + this.internalIdCount.put(internalIdCounter, entry.getShortValue()); + internalIdCounter++; + } + + int index = 0; + + while (index < data.length) { + int externalId = data[index]; + int start = index; + + do { + index++; + } while (index < data.length && data[index] == externalId); + + byte internalId = this.externalToInternal.get(externalId); + + for (int i = start; i < index; i++) { + BitUtil.setNibble(this.blocks, i, internalId); + } + } } @Nonnull @@ -36,13 +96,124 @@ public class HalfByteSectionPalette extends AbstractByteSectionPalette { } @Override - protected void set0(int idx, byte b) { - BitUtil.setNibble(this.blocks, idx, b); + public int get(int index) { + return this.internalToExternal.get(BitUtil.getNibble(this.blocks, index)); + } + + @Nonnull + @Override + public AbstractSectionPalette.SetResult set(int index, int id) { + byte oldInternalId = BitUtil.getNibble(this.blocks, index); + if (this.externalToInternal.containsKey(id)) { + byte newInternalId = this.externalToInternal.get(id); + if (newInternalId == oldInternalId) { + return AbstractSectionPalette.SetResult.UNCHANGED; + } else { + boolean removed = this.decrementBlockCount(oldInternalId); + this.incrementBlockCount(newInternalId); + BitUtil.setNibble(this.blocks, index, newInternalId); + return removed ? AbstractSectionPalette.SetResult.ADDED_OR_REMOVED : AbstractSectionPalette.SetResult.CHANGED; + } + } else { + int nextInternalId = this.nextInternalId(oldInternalId); + if (!this.isValidInternalId(nextInternalId)) { + return AbstractSectionPalette.SetResult.REQUIRES_PROMOTE; + } else { + this.decrementBlockCount(oldInternalId); + byte newInternalId = (byte)nextInternalId; + this.createBlockId(newInternalId, id); + BitUtil.setNibble(this.blocks, index, newInternalId); + return AbstractSectionPalette.SetResult.ADDED_OR_REMOVED; + } + } + } + + private int nextInternalId(byte oldInternalId) { + return this.internalIdCount.get(oldInternalId) == 1 ? this.unsignedInternalId(oldInternalId) : this.internalIdSet.nextClearBit(0); + } + + private void createBlockId(byte internalId, int blockId) { + this.internalToExternal.put(internalId, blockId); + this.externalToInternal.put(blockId, internalId); + this.internalIdSet.set(this.unsignedInternalId(internalId)); + this.internalIdCount.put(internalId, (short)1); + } + + private boolean decrementBlockCount(byte internalId) { + short oldCount = this.internalIdCount.get(internalId); + if (oldCount == 1) { + this.internalIdCount.remove(internalId); + int externalId = this.internalToExternal.remove(internalId); + this.externalToInternal.remove(externalId); + this.internalIdSet.clear(this.unsignedInternalId(internalId)); + return true; + } else { + this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::subtract); + return false; + } + } + + private void incrementBlockCount(byte internalId) { + this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::sum); } @Override - protected byte get0(int idx) { - return BitUtil.getNibble(this.blocks, idx); + public boolean contains(int id) { + return this.externalToInternal.containsKey(id); + } + + @Override + public boolean containsAny(@Nonnull IntList ids) { + int itr = 0; + + for (int len = ids.size(); itr < len; itr++) { + if (this.externalToInternal.containsKey(ids.getInt(itr))) { + return true; + } + } + + return false; + } + + @Override + public int count() { + return this.internalIdCount.size(); + } + + @Override + public int count(int id) { + if (this.externalToInternal.containsKey(id)) { + byte internalId = this.externalToInternal.get(id); + return this.internalIdCount.get(internalId); + } else { + return 0; + } + } + + @Nonnull + @Override + public IntSet values() { + return new IntOpenHashSet(this.externalToInternal.keySet()); + } + + @Override + public void forEachValue(@Nonnull IntConsumer consumer) { + this.externalToInternal.keySet().forEach(consumer); + } + + @Nonnull + @Override + public Int2ShortMap valueCounts() { + Int2ShortMap map = new Int2ShortOpenHashMap(); + + for (it.unimi.dsi.fastutil.bytes.Byte2ShortMap.Entry entry : this.internalIdCount.byte2ShortEntrySet()) { + byte internalId = entry.getByteKey(); + short count = entry.getShortValue(); + int externalId = this.internalToExternal.get(internalId); + map.put(externalId, count); + } + + return map; } @Override @@ -52,7 +223,7 @@ public class HalfByteSectionPalette extends AbstractByteSectionPalette { @Nonnull @Override - public ISectionPalette demote() { + public AbstractSectionPalette demote() { return EmptySectionPalette.INSTANCE; } @@ -61,13 +232,11 @@ public class HalfByteSectionPalette extends AbstractByteSectionPalette { return ByteSectionPalette.fromHalfBytePalette(this); } - @Override - protected boolean isValidInternalId(int internalId) { + private boolean isValidInternalId(int internalId) { return (internalId & 15) == internalId; } - @Override - protected int unsignedInternalId(byte internalId) { + private int unsignedInternalId(byte internalId) { return internalId & 15; } @@ -75,43 +244,173 @@ public class HalfByteSectionPalette extends AbstractByteSectionPalette { return internalId & 15; } + @Override + public void find(@Nonnull IntList ids, @Nonnull IntConsumer indexConsumer) { + ByteSet internalIds = this.buildInternalByteSet(ids); + if (!internalIds.isEmpty()) { + int index = 0; + byte type = BitUtil.getNibble(this.blocks, 0); + + while (index < 32768) { + int start = index; + byte runType = type; + + do { + index++; + } while (index < 32768 && (type = BitUtil.getNibble(this.blocks, index)) == runType); + + if (internalIds.contains(runType)) { + for (int i = start; i < index; i++) { + indexConsumer.accept(i); + } + } + } + } + } + + @Override + public void find(@Nonnull IntList ids, @Nonnull BiIntConsumer indexBlockConsumer) { + ByteSet internalIds = this.buildInternalByteSet(ids); + if (!internalIds.isEmpty()) { + int index = 0; + byte type = BitUtil.getNibble(this.blocks, 0); + + while (index < 32768) { + int start = index; + byte runType = type; + + do { + index++; + } while (index < 32768 && (type = BitUtil.getNibble(this.blocks, index)) == runType); + + if (internalIds.contains(runType)) { + int external = this.internalToExternal.get(runType); + + for (int i = start; i < index; i++) { + indexBlockConsumer.accept(i, external); + } + } + } + } + } + + private ByteSet buildInternalByteSet(IntList ids) { + ByteSet internalIds = PaletteSetProvider.get().getByteSet(ids.size()); + + for (int i = 0; i < ids.size(); i++) { + byte internal = this.externalToInternal.getOrDefault(ids.getInt(i), (byte)-128); + if (internal != -128) { + internalIds.add(internal); + } + } + + return internalIds; + } + + @Override + public void serializeForPacket(@Nonnull ByteBuf buf) { + buf.writeShortLE(this.internalToExternal.size()); + + for (it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry entry : this.internalToExternal.byte2IntEntrySet()) { + byte internalId = entry.getByteKey(); + int externalId = entry.getIntValue(); + buf.writeByte(internalId); + buf.writeIntLE(externalId); + buf.writeShortLE(this.internalIdCount.get(internalId)); + } + + buf.writeBytes(this.blocks); + } + + @Override + public void serialize(@Nonnull AbstractSectionPalette.KeySerializer keySerializer, @Nonnull ByteBuf buf) { + buf.writeShort(this.internalToExternal.size()); + + for (it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry entry : this.internalToExternal.byte2IntEntrySet()) { + byte internalId = entry.getByteKey(); + int externalId = entry.getIntValue(); + buf.writeByte(internalId); + keySerializer.serialize(buf, externalId); + buf.writeShort(this.internalIdCount.get(internalId)); + } + + buf.writeBytes(this.blocks); + } + + @Override + public void deserialize(@Nonnull ToIntFunction deserializer, @Nonnull ByteBuf buf, int version) { + this.externalToInternal.clear(); + this.internalToExternal.clear(); + this.internalIdSet.clear(); + this.internalIdCount.clear(); + Byte2ByteMap internalIdRemapping = null; + int blockCount = buf.readShort(); + + for (int i = 0; i < blockCount; i++) { + byte internalId = buf.readByte(); + int externalId = deserializer.applyAsInt(buf); + short count = buf.readShort(); + if (this.externalToInternal.containsKey(externalId)) { + byte existingInternalId = this.externalToInternal.get(externalId); + if (internalIdRemapping == null) { + internalIdRemapping = new Byte2ByteOpenHashMap(); + } + + internalIdRemapping.put(internalId, existingInternalId); + this.internalIdCount.mergeShort(existingInternalId, count, NumberUtil::sum); + } else { + this.externalToInternal.put(externalId, internalId); + this.internalToExternal.put(internalId, externalId); + this.internalIdSet.set(this.unsignedInternalId(internalId)); + this.internalIdCount.put(internalId, count); + } + } + + buf.readBytes(this.blocks); + if (internalIdRemapping != null) { + for (int ix = 0; ix < 32768; ix++) { + byte old = BitUtil.getNibble(this.blocks, ix); + if (internalIdRemapping.containsKey(old)) { + BitUtil.setNibble(this.blocks, ix, internalIdRemapping.get(old)); + } + } + } + } + @Nonnull public static HalfByteSectionPalette fromBytePalette(@Nonnull ByteSectionPalette section) { if (section.count() > 16) { throw new IllegalStateException("Cannot demote byte palette to half byte palette. Too many blocks! Count: " + section.count()); } else { - HalfByteSectionPalette halfByteSection = new HalfByteSectionPalette(); Byte2ByteMap internalIdRemapping = new Byte2ByteOpenHashMap(); - halfByteSection.internalToExternal.clear(); - halfByteSection.externalToInternal.clear(); - halfByteSection.internalIdSet.clear(); + Int2ByteMap externalToInternal = new Int2ByteOpenHashMap(); + Byte2IntMap internalToExternal = new Byte2IntOpenHashMap(); + BitSet internalIdSet = new BitSet(16); + Byte2ShortMap internalIdCount = new Byte2ShortOpenHashMap(); - for (Entry entry : section.internalToExternal.byte2IntEntrySet()) { + for (it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry entry : section.internalToExternal.byte2IntEntrySet()) { byte oldInternalId = entry.getByteKey(); int externalId = entry.getIntValue(); - byte newInternalId = (byte)halfByteSection.internalIdSet.nextClearBit(0); - halfByteSection.internalIdSet.set(sUnsignedInternalId(newInternalId)); + byte newInternalId = (byte)internalIdSet.nextClearBit(0); + internalIdSet.set(sUnsignedInternalId(newInternalId)); internalIdRemapping.put(oldInternalId, newInternalId); - halfByteSection.internalToExternal.put(newInternalId, externalId); - halfByteSection.externalToInternal.put(externalId, newInternalId); + internalToExternal.put(newInternalId, externalId); + externalToInternal.put(externalId, newInternalId); } - halfByteSection.internalIdCount.clear(); - for (it.unimi.dsi.fastutil.bytes.Byte2ShortMap.Entry entry : section.internalIdCount.byte2ShortEntrySet()) { - byte internalId = entry.getByteKey(); - short count = entry.getShortValue(); - byte newInternalId = internalIdRemapping.get(internalId); - halfByteSection.internalIdCount.put(newInternalId, count); + internalIdCount.put(internalIdRemapping.get(entry.getByteKey()), entry.getShortValue()); } + byte[] newBlocks = new byte[16384]; + for (int i = 0; i < section.blocks.length; i++) { byte internalId = section.blocks[i]; byte byteInternalId = internalIdRemapping.get(internalId); - halfByteSection.set0(i, byteInternalId); + BitUtil.setNibble(newBlocks, i, byteInternalId); } - return halfByteSection; + return new HalfByteSectionPalette(externalToInternal, internalToExternal, internalIdSet, internalIdCount, newBlocks); } } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ISectionPalette.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ISectionPalette.java deleted file mode 100644 index 4fac9bdd..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ISectionPalette.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; - -import com.hypixel.hytale.protocol.packets.world.PaletteType; -import io.netty.buffer.ByteBuf; -import it.unimi.dsi.fastutil.ints.Int2ShortMap; -import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntSet; -import java.util.function.IntConsumer; -import java.util.function.ToIntFunction; -import javax.annotation.Nonnull; - -public interface ISectionPalette { - PaletteType getPaletteType(); - - ISectionPalette.SetResult set(int var1, int var2); - - int get(int var1); - - boolean contains(int var1); - - boolean containsAny(IntList var1); - - default boolean isSolid(int id) { - return this.count() == 1 && this.contains(id); - } - - int count(); - - int count(int var1); - - IntSet values(); - - void forEachValue(IntConsumer var1); - - Int2ShortMap valueCounts(); - - void find(IntList var1, IntSet var2, IntConsumer var3); - - boolean shouldDemote(); - - ISectionPalette demote(); - - ISectionPalette promote(); - - void serializeForPacket(ByteBuf var1); - - void serialize(ISectionPalette.KeySerializer var1, ByteBuf var2); - - void deserialize(ToIntFunction var1, ByteBuf var2, int var3); - - @Nonnull - static ISectionPalette from(@Nonnull int[] data, int[] unique, int count) { - if (count == 1 && unique[0] == 0) { - return EmptySectionPalette.INSTANCE; - } else if (count < 16) { - return new HalfByteSectionPalette(data, unique, count); - } else if (count < 256) { - return new ByteSectionPalette(data, unique, count); - } else if (count < 65536) { - return new ShortSectionPalette(data, unique, count); - } else { - throw new UnsupportedOperationException("Too many block types for palette."); - } - } - - @FunctionalInterface - public interface KeySerializer { - void serialize(ByteBuf var1, int var2); - } - - public static enum SetResult { - ADDED_OR_REMOVED, - CHANGED, - UNCHANGED, - REQUIRES_PROMOTE; - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/PaletteSetProvider.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/PaletteSetProvider.java new file mode 100644 index 00000000..d9fabbd0 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/PaletteSetProvider.java @@ -0,0 +1,28 @@ +package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; + +import it.unimi.dsi.fastutil.bytes.ByteOpenHashSet; +import it.unimi.dsi.fastutil.bytes.ByteSet; +import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import it.unimi.dsi.fastutil.shorts.ShortSet; + +public class PaletteSetProvider { + private static final ThreadLocal LOCAL = ThreadLocal.withInitial(PaletteSetProvider::new); + private final ByteOpenHashSet byteHashSet = new ByteOpenHashSet(); + private final ShortOpenHashSet shortHashSet = new ShortOpenHashSet(); + + public ByteSet getByteSet(int size) { + this.byteHashSet.clear(); + this.byteHashSet.ensureCapacity(size); + return this.byteHashSet; + } + + public ShortSet getShortSet(int size) { + this.shortHashSet.clear(); + this.shortHashSet.ensureCapacity(size); + return this.shortHashSet; + } + + protected static PaletteSetProvider get() { + return LOCAL.get(); + } +} diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/PaletteTypeEnum.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/PaletteTypeEnum.java index 4d087fe6..a2cebc38 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/PaletteTypeEnum.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/PaletteTypeEnum.java @@ -13,14 +13,14 @@ public enum PaletteTypeEnum { private static final PaletteTypeEnum[] values = values(); @Nonnull private final PaletteType paletteType; - private final Supplier constructor; + private final Supplier constructor; private final byte paletteId; public static PaletteTypeEnum get(byte paletteId) { return values[paletteId]; } - private PaletteTypeEnum(@Nonnull PaletteType paletteType, Supplier constructor) { + private PaletteTypeEnum(@Nonnull PaletteType paletteType, Supplier constructor) { this.paletteType = paletteType; this.constructor = constructor; this.paletteId = (byte)paletteType.ordinal(); @@ -31,7 +31,7 @@ public enum PaletteTypeEnum { return this.paletteType; } - public Supplier getConstructor() { + public Supplier getConstructor() { return this.constructor; } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ShortSectionPalette.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ShortSectionPalette.java index bc938557..b7256860 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ShortSectionPalette.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/section/palette/ShortSectionPalette.java @@ -1,33 +1,88 @@ package com.hypixel.hytale.server.core.universe.world.chunk.section.palette; +import com.hypixel.hytale.function.consumer.BiIntConsumer; +import com.hypixel.hytale.math.util.NumberUtil; import com.hypixel.hytale.protocol.packets.world.PaletteType; -import it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry; +import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.Int2ShortMap; +import it.unimi.dsi.fastutil.ints.Int2ShortMaps; import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.Int2ShortMap.Entry; import it.unimi.dsi.fastutil.shorts.Short2IntMap; import it.unimi.dsi.fastutil.shorts.Short2IntOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ShortMap; import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap; +import it.unimi.dsi.fastutil.shorts.ShortSet; import java.util.BitSet; +import java.util.function.IntConsumer; +import java.util.function.ToIntFunction; import javax.annotation.Nonnull; -public class ShortSectionPalette extends AbstractShortSectionPalette { +public final class ShortSectionPalette extends AbstractSectionPalette { private static final int KEY_MASK = 65535; public static final int MAX_SIZE = 65536; public static final int DEMOTE_SIZE = 251; + final Int2ShortMap externalToInternal; + final Short2IntMap internalToExternal; + final BitSet internalIdSet; + final Short2ShortMap internalIdCount; + final short[] blocks; public ShortSectionPalette() { - super(new short[32768]); + this.externalToInternal = new Int2ShortOpenHashMap(); + this.internalToExternal = new Short2IntOpenHashMap(); + this.internalIdSet = new BitSet(); + this.internalIdCount = new Short2ShortOpenHashMap(); + this.blocks = new short[32768]; + this.externalToInternal.put(0, (short)0); + this.internalToExternal.put((short)0, 0); + this.internalIdSet.set(0); + this.internalIdCount.put((short)0, (short)-32768); } - public ShortSectionPalette( - Int2ShortMap externalToInternal, Short2IntMap internalToExternal, BitSet internalIdSet, Short2ShortMap internalIdCount, short[] blocks - ) { - super(externalToInternal, internalToExternal, internalIdSet, internalIdCount, blocks); + ShortSectionPalette(Int2ShortMap externalToInternal, Short2IntMap internalToExternal, BitSet internalIdSet, Short2ShortMap internalIdCount, short[] blocks) { + this.externalToInternal = externalToInternal; + this.internalToExternal = internalToExternal; + this.internalIdSet = internalIdSet; + this.internalIdCount = internalIdCount; + this.blocks = blocks; } - public ShortSectionPalette(@Nonnull int[] data, int[] unique, int count) { - super(new short[32768], data, unique, count); + public ShortSectionPalette(@Nonnull int[] data, @Nonnull Int2ShortMap externalIdCounts) { + this.blocks = new short[32768]; + this.externalToInternal = new Int2ShortOpenHashMap(externalIdCounts.size()); + this.internalToExternal = new Short2IntOpenHashMap(externalIdCounts.size()); + this.internalIdSet = new BitSet(externalIdCounts.size()); + this.internalIdCount = new Short2ShortOpenHashMap(externalIdCounts.size()); + short internalIdCounter = 0; + + for (Entry entry : Int2ShortMaps.fastIterable(externalIdCounts)) { + this.internalToExternal.put(internalIdCounter, entry.getIntKey()); + this.externalToInternal.put(entry.getIntKey(), internalIdCounter); + this.internalIdSet.set(internalIdCounter); + this.internalIdCount.put(internalIdCounter, entry.getShortValue()); + internalIdCounter++; + } + + int index = 0; + + while (index < data.length) { + int externalId = data[index]; + int start = index; + + do { + index++; + } while (index < data.length && data[index] == externalId); + + short internalId = this.externalToInternal.get(externalId); + + for (int i = start; i < index; i++) { + this.blocks[i] = internalId; + } + } } @Nonnull @@ -37,13 +92,122 @@ public class ShortSectionPalette extends AbstractShortSectionPalette { } @Override - protected short get0(int idx) { - return this.blocks[idx]; + public int get(int index) { + return this.internalToExternal.get(this.blocks[index]); + } + + @Nonnull + @Override + public AbstractSectionPalette.SetResult set(int index, int id) { + short oldInternalId = this.blocks[index]; + if (this.externalToInternal.containsKey(id)) { + short newInternalId = this.externalToInternal.get(id); + if (newInternalId == oldInternalId) { + return AbstractSectionPalette.SetResult.UNCHANGED; + } else { + boolean removed = this.decrementBlockCount(oldInternalId); + this.incrementBlockCount(newInternalId); + this.blocks[index] = newInternalId; + return removed ? AbstractSectionPalette.SetResult.ADDED_OR_REMOVED : AbstractSectionPalette.SetResult.CHANGED; + } + } else { + int next = this.nextInternalId(oldInternalId); + if (!this.isValidInternalId(next)) { + return AbstractSectionPalette.SetResult.REQUIRES_PROMOTE; + } else { + this.decrementBlockCount(oldInternalId); + short newInternalId = (short)next; + this.createBlockId(newInternalId, id); + this.blocks[index] = newInternalId; + return AbstractSectionPalette.SetResult.ADDED_OR_REMOVED; + } + } + } + + private int nextInternalId(short oldInternalId) { + return this.internalIdCount.get(oldInternalId) == 1 ? oldInternalId : this.internalIdSet.nextClearBit(0); + } + + private void createBlockId(short internalId, int blockId) { + this.internalToExternal.put(internalId, blockId); + this.externalToInternal.put(blockId, internalId); + this.internalIdSet.set(internalId); + this.internalIdCount.put(internalId, (short)1); + } + + private boolean decrementBlockCount(short internalId) { + short oldCount = this.internalIdCount.get(internalId); + if (oldCount == 1) { + this.internalIdCount.remove(internalId); + int externalId = this.internalToExternal.remove(internalId); + this.externalToInternal.remove(externalId); + this.internalIdSet.clear(internalId); + return true; + } else { + this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::subtract); + return false; + } + } + + private void incrementBlockCount(short internalId) { + this.internalIdCount.mergeShort(internalId, (short)1, NumberUtil::sum); } @Override - protected void set0(int idx, short s) { - this.blocks[idx] = s; + public boolean contains(int id) { + return this.externalToInternal.containsKey(id); + } + + @Override + public boolean containsAny(@Nonnull IntList ids) { + for (int i = 0; i < ids.size(); i++) { + if (this.externalToInternal.containsKey(ids.getInt(i))) { + return true; + } + } + + return false; + } + + @Override + public int count() { + return this.internalIdCount.size(); + } + + @Override + public int count(int id) { + if (this.externalToInternal.containsKey(id)) { + short internalId = this.externalToInternal.get(id); + return this.internalIdCount.get(internalId); + } else { + return 0; + } + } + + @Nonnull + @Override + public IntSet values() { + return new IntOpenHashSet(this.externalToInternal.keySet()); + } + + @Override + public void forEachValue(@Nonnull IntConsumer consumer) { + this.externalToInternal.keySet().forEach(consumer); + } + + @Nonnull + @Override + public Int2ShortMap valueCounts() { + Int2ShortMap map = new Int2ShortOpenHashMap(); + + for (it.unimi.dsi.fastutil.shorts.Short2ShortMap.Entry entry : this.internalIdCount.short2ShortEntrySet()) { + short internalId = entry.getShortKey(); + short count = entry.getShortValue(); + int externalId = this.internalToExternal.get(internalId); + map.put(externalId, count); + } + + return map; } @Override @@ -57,23 +221,162 @@ public class ShortSectionPalette extends AbstractShortSectionPalette { } @Override - public ISectionPalette promote() { + public AbstractSectionPalette promote() { throw new UnsupportedOperationException("Short palette cannot be promoted."); } - @Override - protected boolean isValidInternalId(int internalId) { + private boolean isValidInternalId(int internalId) { return (internalId & 65535) == internalId; } + @Override + public void find(@Nonnull IntList ids, @Nonnull IntConsumer indexConsumer) { + ShortSet internalIds = this.buildInternalShortSet(ids); + if (!internalIds.isEmpty()) { + int index = 0; + short type = this.blocks[0]; + + while (index < 32768) { + int start = index; + short runType = type; + + do { + index++; + } while (index < 32768 && (type = this.blocks[index]) == runType); + + if (internalIds.contains(runType)) { + for (int i = start; i < index; i++) { + indexConsumer.accept(i); + } + } + } + } + } + + @Override + public void find(@Nonnull IntList ids, @Nonnull BiIntConsumer indexBlockConsumer) { + ShortSet internalIds = this.buildInternalShortSet(ids); + if (!internalIds.isEmpty()) { + int index = 0; + short type = this.blocks[0]; + + while (index < 32768) { + int start = index; + short runType = type; + + do { + index++; + } while (index < 32768 && (type = this.blocks[index]) == runType); + + if (internalIds.contains(runType)) { + int external = this.internalToExternal.get(runType); + + for (int i = start; i < index; i++) { + indexBlockConsumer.accept(i, external); + } + } + } + } + } + + private ShortSet buildInternalShortSet(IntList ids) { + ShortSet internalIds = PaletteSetProvider.get().getShortSet(ids.size()); + + for (int i = 0; i < ids.size(); i++) { + short internal = this.externalToInternal.getOrDefault(ids.getInt(i), (short)-32768); + if (internal != -32768) { + internalIds.add(internal); + } + } + + return internalIds; + } + + @Override + public void serializeForPacket(@Nonnull ByteBuf buf) { + buf.writeShortLE(this.internalToExternal.size()); + + for (it.unimi.dsi.fastutil.shorts.Short2IntMap.Entry entry : this.internalToExternal.short2IntEntrySet()) { + short internalId = entry.getShortKey(); + int externalId = entry.getIntValue(); + buf.writeShortLE(internalId & '\uffff'); + buf.writeIntLE(externalId); + buf.writeShortLE(this.internalIdCount.get(internalId)); + } + + for (int i = 0; i < this.blocks.length; i++) { + buf.writeShortLE(this.blocks[i]); + } + } + + @Override + public void serialize(@Nonnull AbstractSectionPalette.KeySerializer keySerializer, @Nonnull ByteBuf buf) { + buf.writeShort(this.internalToExternal.size()); + + for (it.unimi.dsi.fastutil.shorts.Short2IntMap.Entry entry : this.internalToExternal.short2IntEntrySet()) { + short internalId = entry.getShortKey(); + int externalId = entry.getIntValue(); + buf.writeShort(internalId & '\uffff'); + keySerializer.serialize(buf, externalId); + buf.writeShort(this.internalIdCount.get(internalId)); + } + + for (int i = 0; i < this.blocks.length; i++) { + buf.writeShort(this.blocks[i]); + } + } + + @Override + public void deserialize(@Nonnull ToIntFunction deserializer, @Nonnull ByteBuf buf, int version) { + this.externalToInternal.clear(); + this.internalToExternal.clear(); + this.internalIdSet.clear(); + this.internalIdCount.clear(); + Short2ShortMap internalIdRemapping = null; + int blockCount = buf.readShort(); + + for (int i = 0; i < blockCount; i++) { + short internalId = buf.readShort(); + int externalId = deserializer.applyAsInt(buf); + short count = buf.readShort(); + if (this.externalToInternal.containsKey(externalId)) { + short existingInternalId = this.externalToInternal.get(externalId); + if (internalIdRemapping == null) { + internalIdRemapping = new Short2ShortOpenHashMap(); + } + + internalIdRemapping.put(internalId, existingInternalId); + this.internalIdCount.mergeShort(existingInternalId, count, NumberUtil::sum); + } else { + this.externalToInternal.put(externalId, internalId); + this.internalToExternal.put(internalId, externalId); + this.internalIdSet.set(internalId); + this.internalIdCount.put(internalId, count); + } + } + + for (int ix = 0; ix < this.blocks.length; ix++) { + this.blocks[ix] = buf.readShort(); + } + + if (internalIdRemapping != null) { + for (int ix = 0; ix < 32768; ix++) { + short oldInternalId = this.blocks[ix]; + if (internalIdRemapping.containsKey(oldInternalId)) { + this.blocks[ix] = internalIdRemapping.get(oldInternalId); + } + } + } + } + @Nonnull public static ShortSectionPalette fromBytePalette(@Nonnull ByteSectionPalette section) { - Int2ShortMap shortExternalToInternal = new Int2ShortOpenHashMap(); - Short2IntMap shortInternalToExternal = new Short2IntOpenHashMap(); - BitSet shortInternalIdSet = new BitSet(section.internalToExternal.size()); - Short2ShortMap shortInternalIdCount = new Short2ShortOpenHashMap(); + Int2ShortMap shortExternalToInternal = new Int2ShortOpenHashMap(section.count()); + Short2IntMap shortInternalToExternal = new Short2IntOpenHashMap(section.count()); + BitSet shortInternalIdSet = new BitSet(section.count()); + Short2ShortMap shortInternalIdCount = new Short2ShortOpenHashMap(section.count()); - for (Entry entry : section.internalToExternal.byte2IntEntrySet()) { + for (it.unimi.dsi.fastutil.bytes.Byte2IntMap.Entry entry : section.internalToExternal.byte2IntEntrySet()) { short internal = (short)(entry.getByteKey() & 255); int external = entry.getIntValue(); shortInternalToExternal.put(internal, external); @@ -82,12 +385,12 @@ public class ShortSectionPalette extends AbstractShortSectionPalette { shortInternalIdCount.put(internal, section.internalIdCount.get(entry.getByteKey())); } - short[] shortBlocks = new short[32768]; + short[] newBlocks = new short[32768]; for (int i = 0; i < 32768; i++) { - shortBlocks[i] = (short)(section.blocks[i] & 255); + newBlocks[i] = (short)(section.blocks[i] & 255); } - return new ShortSectionPalette(shortExternalToInternal, shortInternalToExternal, shortInternalIdSet, shortInternalIdCount, shortBlocks); + return new ShortSectionPalette(shortExternalToInternal, shortInternalToExternal, shortInternalIdSet, shortInternalIdCount, newBlocks); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/state/TickableBlockState.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/state/TickableBlockState.java deleted file mode 100644 index cd007baf..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/state/TickableBlockState.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.chunk.state; - -import com.hypixel.hytale.component.ArchetypeChunk; -import com.hypixel.hytale.component.CommandBuffer; -import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; -import javax.annotation.Nullable; - -public interface TickableBlockState { - void tick(float var1, int var2, ArchetypeChunk var3, Store var4, CommandBuffer var5); - - Vector3i getPosition(); - - Vector3i getBlockPosition(); - - @Nullable - WorldChunk getChunk(); - - void invalidate(); -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/chunk/systems/ChunkSystems.java b/src/com/hypixel/hytale/server/core/universe/world/chunk/systems/ChunkSystems.java index 93387e68..68e8a5ab 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/chunk/systems/ChunkSystems.java +++ b/src/com/hypixel/hytale/server/core/universe/world/chunk/systems/ChunkSystems.java @@ -11,9 +11,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.dependency.Dependency; -import com.hypixel.hytale.component.dependency.Order; import com.hypixel.hytale.component.dependency.RootDependency; -import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefChangeSystem; @@ -28,7 +26,6 @@ import com.hypixel.hytale.protocol.packets.world.ServerSetBlocks; import com.hypixel.hytale.protocol.packets.world.SetBlockCmd; import com.hypixel.hytale.protocol.packets.world.SetChunk; import com.hypixel.hytale.server.core.modules.entity.player.ChunkTracker; -import com.hypixel.hytale.server.core.modules.migrations.ChunkColumnMigrationSystem; import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; @@ -98,55 +95,11 @@ public class ChunkSystems { public static class OnChunkLoad extends RefSystem { private static final Query QUERY = Query.and(ChunkColumn.getComponentType(), WorldChunk.getComponentType()); - private static final Set> DEPENDENCIES = Set.of(new SystemDependency<>(Order.AFTER, ChunkSystems.OnNewChunk.class)); @Override public void onEntityAdded( @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { - ChunkColumn chunk = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); - - assert chunk != null; - - WorldChunk worldChunk = commandBuffer.getComponent(ref, WorldChunk.getComponentType()); - - assert worldChunk != null; - - Ref[] sections = chunk.getSections(); - Holder[] sectionHolders = chunk.takeSectionHolders(); - boolean isNonTicking = commandBuffer.getArchetype(ref).contains(ChunkStore.REGISTRY.getNonTickingComponentType()); - if (sectionHolders != null && sectionHolders.length > 0 && sectionHolders[0] != null) { - for (int i = 0; i < sectionHolders.length; i++) { - if (isNonTicking) { - sectionHolders[i].ensureComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); - } else { - sectionHolders[i].tryRemoveComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); - } - - ChunkSection section = sectionHolders[i].getComponent(ChunkSection.getComponentType()); - if (section == null) { - sectionHolders[i].addComponent(ChunkSection.getComponentType(), new ChunkSection(ref, worldChunk.getX(), i, worldChunk.getZ())); - } else { - section.load(ref, worldChunk.getX(), i, worldChunk.getZ()); - } - } - - commandBuffer.addEntities(sectionHolders, 0, sections, 0, sections.length, AddReason.LOAD); - } - - for (int i = 0; i < sections.length; i++) { - if (sections[i] == null) { - Holder newSection = ChunkStore.REGISTRY.newHolder(); - if (isNonTicking) { - newSection.ensureComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); - } else { - newSection.tryRemoveComponent(ChunkStore.REGISTRY.getNonTickingComponentType()); - } - - newSection.addComponent(ChunkSection.getComponentType(), new ChunkSection(ref, worldChunk.getX(), i, worldChunk.getZ())); - sections[i] = commandBuffer.addEntity(newSection, AddReason.SPAWN); - } - } } @Override @@ -175,43 +128,6 @@ public class ChunkSystems { public Query getQuery() { return QUERY; } - - @Nonnull - @Override - public Set> getDependencies() { - return DEPENDENCIES; - } - } - - public static class OnNewChunk extends ChunkColumnMigrationSystem { - private static final Query QUERY = Query.and(WorldChunk.getComponentType(), Query.not(ChunkColumn.getComponentType())); - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - Holder[] sectionHolders = new Holder[10]; - - for (int i = 0; i < sectionHolders.length; i++) { - sectionHolders[i] = ChunkStore.REGISTRY.newHolder(); - } - - holder.addComponent(ChunkColumn.getComponentType(), new ChunkColumn(sectionHolders)); - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return QUERY; - } - - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } } public static class OnNonTicking extends RefChangeSystem> { diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/WorldSettingsCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/WorldSettingsCommand.java index 49ce377e..90bf8d13 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/WorldSettingsCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/WorldSettingsCommand.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.core.universe.world.commands; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box2D; -import com.hypixel.hytale.math.vector.Vector2d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -18,10 +17,13 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.universe.world.storage.provider.IChunkStorageProvider; import com.hypixel.hytale.server.core.universe.world.worldgen.provider.IWorldGenProvider; import com.hypixel.hytale.server.core.universe.world.worldmap.provider.IWorldMapProvider; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class WorldSettingsCommand extends AbstractCommandCollection { @Nonnull @@ -35,7 +37,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.worldgentype.desc", "type", ArgTypes.STRING, - "WorldGen Type", + "server.commands.world.settings.worldgentype.name", worldConfig -> IWorldGenProvider.CODEC.getIdFor((Class)worldConfig.getWorldGenProvider().getClass()), (worldConfig, path) -> worldConfig.setWorldGenProvider(IWorldGenProvider.CODEC.getCodecFor(path).getDefaultValue()) ); @@ -44,7 +46,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.worldmaptype.desc", "type", ArgTypes.STRING, - "WorldMap Type", + "server.commands.world.settings.worldmaptype.name", worldConfig -> IWorldMapProvider.CODEC.getIdFor((Class)worldConfig.getWorldMapProvider().getClass()), (worldConfig, path) -> worldConfig.setWorldMapProvider(IWorldMapProvider.CODEC.getCodecFor(path).getDefaultValue()) ); @@ -53,31 +55,43 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.chunkstoragetype.desc", "type", ArgTypes.STRING, - "ChunkStorage Type", + "server.commands.world.settings.chunkstoragetype.name", worldConfig -> IChunkStorageProvider.CODEC.getIdFor((Class>)worldConfig.getChunkStorageProvider().getClass()), (worldConfig, path) -> worldConfig.setChunkStorageProvider((IChunkStorageProvider)IChunkStorageProvider.CODEC.getCodecFor(path).getDefaultValue()) ); this.generateSubCommand( - "ticking", "server.commands.world.settings.ticking.desc", "ticking", ArgTypes.BOOLEAN, "Ticking", WorldConfig::isTicking, WorldConfig::setTicking + "ticking", + "server.commands.world.settings.ticking.desc", + "ticking", + ArgTypes.BOOLEAN, + "server.commands.world.settings.ticking.name", + WorldConfig::isTicking, + WorldConfig::setTicking ); this.generateSubCommand( "blockticking", "server.commands.world.settings.blockticking.desc", "blockticking", ArgTypes.BOOLEAN, - "Block Ticking", + "server.commands.world.settings.blockticking.name", WorldConfig::isBlockTicking, WorldConfig::setBlockTicking ); this.generateSubCommand( - "pvp", "server.commands.world.settings.pvp.desc", "pvp", ArgTypes.BOOLEAN, "PvP", WorldConfig::isPvpEnabled, WorldConfig::setPvpEnabled + "pvp", + "server.commands.world.settings.pvp.desc", + "pvp", + ArgTypes.BOOLEAN, + "server.commands.world.settings.pvp.name", + WorldConfig::isPvpEnabled, + WorldConfig::setPvpEnabled ); this.generateSubCommand( "timepaused", "server.commands.world.settings.timepaused.desc", "timepaused", ArgTypes.BOOLEAN, - "Time Paused", + "server.commands.world.settings.timepaused.name", WorldConfig::isGameTimePaused, WorldConfig::setGameTimePaused ); @@ -86,7 +100,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.spawningnpc.desc", "spawning", ArgTypes.BOOLEAN, - "Spawning NPC's", + "server.commands.world.settings.spawningnpc.name", WorldConfig::isSpawningNPC, WorldConfig::setSpawningNPC ); @@ -95,7 +109,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.spawnmarkers.desc", "enabled", ArgTypes.BOOLEAN, - "Spawn markers enabled", + "server.commands.world.settings.spawnmarkers.name", WorldConfig::isSpawnMarkersEnabled, WorldConfig::setIsSpawnMarkersEnabled ); @@ -104,7 +118,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.freezeallnpcs.desc", "enabled", ArgTypes.BOOLEAN, - "NPCs will be frozen", + "server.commands.world.settings.freezeallnpcs.name", WorldConfig::isAllNPCFrozen, WorldConfig::setIsAllNPCFrozen ); @@ -113,7 +127,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.compassupdating.desc", "updating", ArgTypes.BOOLEAN, - "Compass Updating", + "server.commands.world.settings.compassupdating.name", World::isCompassUpdating, WorldConfig::isCompassUpdating, World::setCompassUpdating @@ -123,25 +137,25 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.playersaving.desc", "enabled", ArgTypes.BOOLEAN, - "Player Saving Enabled", + "server.commands.world.settings.playersaving.name", WorldConfig::isSavingPlayers, WorldConfig::setSavingPlayers ); this.generateSubCommand( "chunksaving", - "server.commands.world.settings.chunksaving.desc", + "server.commands.world.settings.chunkSaving.desc", "enabled", ArgTypes.BOOLEAN, - "Chunk Saving Enabled", + "server.commands.world.settings.chunksaving.name", WorldConfig::canSaveChunks, WorldConfig::setCanSaveChunks ); this.generateSubCommand( "chunkunloading", - "server.commands.world.settings.chunkunload.desc", + "server.commands.world.settings.chunkUnload.desc", "enabled", ArgTypes.BOOLEAN, - "Chunk Unloading Enabled", + "server.commands.world.settings.chunkunloading.name", WorldConfig::canUnloadChunks, WorldConfig::setCanUnloadChunks ); @@ -150,7 +164,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.gamemode.desc", "gamemode", new EnumArgumentType<>("server.commands.parsing.argtype.gamemode.name", GameMode.class), - "Default GameMode", + "server.commands.world.settings.gamemode.name", WorldConfig::getGameMode, WorldConfig::setGameMode ); @@ -159,7 +173,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { "server.commands.world.settings.gameplayconfig.desc", "id", ArgTypes.STRING, - "GameplayConfigId", + "server.commands.world.settings.gameplayconfig.name", WorldConfig::getGameplayConfig, WorldConfig::setGameplayConfig ); @@ -167,7 +181,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { new WorldSettingsCommand.WorldSettingsBox2DCommand( "pregenerate", "server.commands.world.settings.pregenerate.desc", - "Pre-generate region", + "server.commands.world.settings.pregenerate.name", w -> w.getWorldConfig().getChunkConfig().getPregenerateRegion(), wc -> wc.getChunkConfig().getPregenerateRegion(), (w, v) -> { @@ -181,7 +195,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { new WorldSettingsCommand.WorldSettingsBox2DCommand( "keeploaded", "server.commands.world.settings.keeploaded.desc", - "Keep loaded region", + "server.commands.world.settings.keeploaded.name", w -> w.getWorldConfig().getChunkConfig().getKeepLoadedRegion(), wc -> wc.getChunkConfig().getKeepLoadedRegion(), (w, v) -> { @@ -191,6 +205,18 @@ public class WorldSettingsCommand extends AbstractCommandCollection { } ) ); + this.addSubCommand( + new WorldSettingsCommand.WorldSettingsSetCommand<>( + "disabledfluidtickers", + "server.commands.world.settings.disabledfluidtickers.desc", + "server.commands.world.settings.disabledfluidtickers.name", + w -> w.getWorldConfig().getDisabledFluidTickers(), + (w, v) -> { + w.getWorldConfig().setDisabledFluidTickers(v); + w.getWorldConfig().markChanged(); + } + ) + ); } private void generateSubCommand( @@ -266,7 +292,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { Box2D currentValue = this.getter.apply(world); context.sendMessage( Message.translation("server.commands.world.settings.currentValue") - .param("display", this.display) + .param("display", Message.translation(this.display)) .param("worldName", world.getName()) .param("currentValue", Objects.toString(currentValue)) ); @@ -274,6 +300,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { private class ResetSubCommand extends AbstractWorldCommand { public ResetSubCommand() { + Objects.requireNonNull(WorldSettingsBox2DCommand.this); super("reset", "server.commands.world.settings.reset.desc"); } @@ -285,7 +312,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { world.getWorldConfig().markChanged(); context.sendMessage( Message.translation("server.commands.world.settings.displaySetDefault") - .param("display", WorldSettingsBox2DCommand.this.display) + .param("display", Message.translation(WorldSettingsBox2DCommand.this.display)) .param("worldName", world.getName()) .param("newValue", Objects.toString(newValue)) .param("oldValue", Objects.toString(currentValue)) @@ -295,16 +322,21 @@ public class WorldSettingsCommand extends AbstractCommandCollection { private class SetSubCommand extends AbstractWorldCommand { @Nonnull - private final RequiredArg minXArg = this.withRequiredArg("minX", "server.commands.world.settings.box2d.minX.desc", ArgTypes.DOUBLE); + private final RequiredArg minXArg; @Nonnull - private final RequiredArg minZArg = this.withRequiredArg("minZ", "server.commands.world.settings.box2d.minZ.desc", ArgTypes.DOUBLE); + private final RequiredArg minZArg; @Nonnull - private final RequiredArg maxXArg = this.withRequiredArg("maxX", "server.commands.world.settings.box2d.maxX.desc", ArgTypes.DOUBLE); + private final RequiredArg maxXArg; @Nonnull - private final RequiredArg maxZArg = this.withRequiredArg("maxZ", "server.commands.world.settings.box2d.maxZ.desc", ArgTypes.DOUBLE); + private final RequiredArg maxZArg; public SetSubCommand() { + Objects.requireNonNull(WorldSettingsBox2DCommand.this); super("set", "server.commands.world.settings.set.desc"); + this.minXArg = this.withRequiredArg("minX", "server.commands.world.settings.box2d.minX.desc", ArgTypes.DOUBLE); + this.minZArg = this.withRequiredArg("minZ", "server.commands.world.settings.box2d.minZ.desc", ArgTypes.DOUBLE); + this.maxXArg = this.withRequiredArg("maxX", "server.commands.world.settings.box2d.maxX.desc", ArgTypes.DOUBLE); + this.maxZArg = this.withRequiredArg("maxZ", "server.commands.world.settings.box2d.maxZ.desc", ArgTypes.DOUBLE); } @Override @@ -317,7 +349,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { world.getWorldConfig().markChanged(); context.sendMessage( Message.translation("server.commands.world.settings.displaySet") - .param("display", WorldSettingsBox2DCommand.this.display) + .param("display", Message.translation(WorldSettingsBox2DCommand.this.display)) .param("worldName", world.getName()) .param("newValue", Objects.toString(newValue)) .param("oldValue", Objects.toString(currentValue)) @@ -326,6 +358,144 @@ public class WorldSettingsCommand extends AbstractCommandCollection { } } + private static class WorldSettingsSetCommand extends AbstractWorldCommand { + @Nonnull + private final String display; + @Nonnull + private final Function> getter; + @Nonnull + private final BiConsumer> setter; + + public WorldSettingsSetCommand( + @Nonnull String name, + @Nonnull String description, + @Nonnull String display, + @Nonnull Function> getter, + @Nonnull BiConsumer> setter + ) { + super(name, description); + this.display = display; + this.getter = getter; + this.setter = setter; + this.addSubCommand(new WorldSettingsCommand.WorldSettingsSetCommand.AddSubCommand()); + this.addSubCommand(new WorldSettingsCommand.WorldSettingsSetCommand.RemoveSubCommand()); + this.addSubCommand(new WorldSettingsCommand.WorldSettingsSetCommand.ClearSubCommand()); + } + + @Override + protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store store) { + Set currentValue = this.getter.apply(world); + if (currentValue.isEmpty()) { + context.sendMessage( + Message.translation("server.commands.world.settings.set.empty") + .param("display", Message.translation(this.display)) + .param("worldName", world.getName()) + ); + } else { + context.sendMessage( + Message.translation("server.commands.world.settings.set.list") + .param("display", Message.translation(this.display)) + .param("worldName", world.getName()) + .param("values", String.join(", ", currentValue.stream().map(Objects::toString).toList())) + ); + } + } + + private class AddSubCommand extends AbstractWorldCommand { + @Nonnull + private final RequiredArg valueArg; + + public AddSubCommand() { + Objects.requireNonNull(WorldSettingsSetCommand.this); + super("add", "server.commands.world.settings.set.add.desc"); + this.valueArg = this.withRequiredArg("value", "server.commands.world.settings.set.add.value.desc", ArgTypes.STRING); + } + + @Override + protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store store) { + T value = context.get(this.valueArg); + Set current = WorldSettingsSetCommand.this.getter.apply(world); + HashSet updated = new HashSet<>(current); + if (!updated.add(value)) { + context.sendMessage( + Message.translation("server.commands.world.settings.set.alreadyPresent") + .param("display", Message.translation(WorldSettingsSetCommand.this.display)) + .param("worldName", world.getName()) + .param("value", value.toString()) + ); + } else { + WorldSettingsSetCommand.this.setter.accept(world, updated); + context.sendMessage( + Message.translation("server.commands.world.settings.set.added") + .param("display", Message.translation(WorldSettingsSetCommand.this.display)) + .param("worldName", world.getName()) + .param("value", value.toString()) + ); + } + } + } + + private class ClearSubCommand extends AbstractWorldCommand { + public ClearSubCommand() { + Objects.requireNonNull(WorldSettingsSetCommand.this); + super("clear", "server.commands.world.settings.set.clear.desc"); + } + + @Override + protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store store) { + Set current = WorldSettingsSetCommand.this.getter.apply(world); + if (current.isEmpty()) { + context.sendMessage( + Message.translation("server.commands.world.settings.set.empty") + .param("display", Message.translation(WorldSettingsSetCommand.this.display)) + .param("worldName", world.getName()) + ); + } else { + WorldSettingsSetCommand.this.setter.accept(world, Set.of()); + context.sendMessage( + Message.translation("server.commands.world.settings.set.cleared") + .param("display", Message.translation(WorldSettingsSetCommand.this.display)) + .param("worldName", world.getName()) + ); + } + } + } + + private class RemoveSubCommand extends AbstractWorldCommand { + @Nonnull + private final RequiredArg valueArg; + + public RemoveSubCommand() { + Objects.requireNonNull(WorldSettingsSetCommand.this); + super("remove", "server.commands.world.settings.set.remove.desc"); + this.valueArg = this.withRequiredArg("value", "server.commands.world.settings.set.remove.value.desc", ArgTypes.STRING); + } + + @Override + protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store store) { + T value = context.get(this.valueArg); + Set current = WorldSettingsSetCommand.this.getter.apply(world); + HashSet updated = new HashSet<>(current); + if (!updated.remove(value)) { + context.sendMessage( + Message.translation("server.commands.world.settings.set.notPresent") + .param("display", Message.translation(WorldSettingsSetCommand.this.display)) + .param("worldName", world.getName()) + .param("value", value.toString()) + ); + } else { + WorldSettingsSetCommand.this.setter.accept(world, updated); + context.sendMessage( + Message.translation("server.commands.world.settings.set.removed") + .param("display", Message.translation(WorldSettingsSetCommand.this.display)) + .param("worldName", world.getName()) + .param("value", value.toString()) + ); + } + } + } + } + private static class WorldSettingsSubCommand extends AbstractWorldCommand { @Nonnull private final ArgumentType argumentType; @@ -366,7 +536,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { T currentValue = this.getter.apply(world); context.sendMessage( Message.translation("server.commands.world.settings.currentValue") - .param("display", this.display) + .param("display", Message.translation(this.display)) .param("worldName", world.getName()) .param("currentValue", currentValue.toString()) ); @@ -374,6 +544,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { private class ResetSubCommand extends AbstractWorldCommand { public ResetSubCommand() { + Objects.requireNonNull(WorldSettingsSubCommand.this); super("reset", "server.commands.world.settings.reset.desc"); } @@ -385,7 +556,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { world.getWorldConfig().markChanged(); context.sendMessage( Message.translation("server.commands.world.settings.displaySetDefault") - .param("display", WorldSettingsSubCommand.this.display) + .param("display", Message.translation(WorldSettingsSubCommand.this.display)) .param("worldName", world.getName()) .param("newValue", newValue.toString()) .param("oldValue", currentValue.toString()) @@ -395,12 +566,12 @@ public class WorldSettingsCommand extends AbstractCommandCollection { private class SetSubCommand extends AbstractWorldCommand { @Nonnull - private final RequiredArg valueArg = this.withRequiredArg( - "value", "server.commands.world.settings.value.desc", WorldSettingsSubCommand.this.argumentType - ); + private final RequiredArg valueArg; public SetSubCommand() { + Objects.requireNonNull(WorldSettingsSubCommand.this); super("set", "server.commands.world.settings.set.desc"); + this.valueArg = this.withRequiredArg("value", "server.commands.world.settings.value.desc", WorldSettingsSubCommand.this.argumentType); } @Override @@ -411,7 +582,7 @@ public class WorldSettingsCommand extends AbstractCommandCollection { world.getWorldConfig().markChanged(); context.sendMessage( Message.translation("server.commands.world.settings.displaySet") - .param("display", WorldSettingsSubCommand.this.display) + .param("display", Message.translation(WorldSettingsSubCommand.this.display)) .param("worldName", world.getName()) .param("newValue", newValue.toString()) .param("oldValue", currentValue.toString()) diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectFillerCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectFillerCommand.java index 05ce2bed..11890196 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectFillerCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectFillerCommand.java @@ -8,8 +8,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -24,6 +22,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class BlockInspectFillerCommand extends AbstractPlayerCommand { @Nonnull @@ -45,9 +45,9 @@ public class BlockInspectFillerCommand extends AbstractPlayerCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - int x = MathUtil.floor(position.getX()); - int z = MathUtil.floor(position.getZ()); - int y = MathUtil.floor(position.getY()); + int x = MathUtil.floor(position.x()); + int z = MathUtil.floor(position.z()); + int y = MathUtil.floor(position.y()); int chunkX = ChunkUtil.chunkCoordinate(x); int chunkY = ChunkUtil.chunkCoordinate(y); int chunkZ = ChunkUtil.chunkCoordinate(z); @@ -110,11 +110,11 @@ public class BlockInspectFillerCommand extends AbstractPlayerCommand { maxZ++; } - Vector3f colour = new Vector3f(); - colour.x = (float)(fillerX - minX) / (maxX - minX); - colour.y = (float)(fillerY - minY) / (maxY - minY); - colour.z = (float)(fillerZ - minZ) / (maxZ - minZ); - DebugUtils.addCube(chunkStore.getExternalData().getWorld(), pos, colour, 1.05, 30.0F); + Vector3f color = new Vector3f(); + color.x = (float)(fillerX - minX) / (maxX - minX); + color.y = (float)(fillerY - minY) / (maxY - minY); + color.z = (float)(fillerZ - minZ) / (maxZ - minZ); + DebugUtils.addCube(chunkStore.getExternalData().getWorld(), pos, color, 1.05, 30.0F); } } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectPhysicsCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectPhysicsCommand.java index 75e96b7f..3c216b9d 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectPhysicsCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectPhysicsCommand.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; @@ -20,6 +19,8 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class BlockInspectPhysicsCommand extends AbstractPlayerCommand { @Nonnull @@ -43,9 +44,9 @@ public class BlockInspectPhysicsCommand extends AbstractPlayerCommand { Vector3d position = transformComponent.getPosition(); boolean all = this.ALL.get(context); - int x = MathUtil.floor(position.getX()); - int z = MathUtil.floor(position.getZ()); - int y = MathUtil.floor(position.getY()); + int x = MathUtil.floor(position.x()); + int z = MathUtil.floor(position.z()); + int y = MathUtil.floor(position.y()); int chunkX = ChunkUtil.chunkCoordinate(x); int chunkY = ChunkUtil.chunkCoordinate(y); int chunkZ = ChunkUtil.chunkCoordinate(z); @@ -66,26 +67,26 @@ public class BlockInspectPhysicsCommand extends AbstractPlayerCommand { Vector3d pos = new Vector3d(bx, by, bz); pos.add(0.5, 0.5, 0.5); pos.add(offset); - Vector3f colour; + Vector3f color; if (supportValue == 15) { - colour = new Vector3f(0.0F, 1.0F, 0.0F); + color = new Vector3f(0.0F, 1.0F, 0.0F); } else { - BlockType block = world.getBlockType(pos.toVector3i()); + BlockType block = world.getBlockType(Vector3dUtil.toVector3i(pos)); if (!block.hasSupport()) { if (supportValue == 0) { continue; } - colour = new Vector3f(1.0F, 1.0F, 0.0F); + color = new Vector3f(1.0F, 1.0F, 0.0F); } else if (block.getMaxSupportDistance() != 0) { float len = (float)supportValue / block.getMaxSupportDistance(); - colour = new Vector3f(len, 0.0F, 1.0F - len); + color = new Vector3f(len, 0.0F, 1.0F - len); } else { - colour = new Vector3f(0.0F, 1.0F, 1.0F); + color = new Vector3f(0.0F, 1.0F, 1.0F); } } - DebugUtils.addCube(chunkStore.getExternalData().getWorld(), pos, colour, 1.05, 30.0F); + DebugUtils.addCube(chunkStore.getExternalData().getWorld(), pos, color, 1.05, 30.0F); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectRotationCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectRotationCommand.java index 8c02a394..6be3ebcc 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectRotationCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockInspectRotationCommand.java @@ -6,8 +6,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; @@ -22,6 +20,8 @@ import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public class BlockInspectRotationCommand extends AbstractPlayerCommand { @Nonnull @@ -43,9 +43,9 @@ public class BlockInspectRotationCommand extends AbstractPlayerCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - int x = MathUtil.floor(position.getX()); - int z = MathUtil.floor(position.getZ()); - int y = MathUtil.floor(position.getY()); + int x = MathUtil.floor(position.x()); + int z = MathUtil.floor(position.z()); + int y = MathUtil.floor(position.y()); int chunkX = ChunkUtil.chunkCoordinate(x); int chunkY = ChunkUtil.chunkCoordinate(y); int chunkZ = ChunkUtil.chunkCoordinate(z); diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockRowCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockRowCommand.java index 555eadb1..5060e57f 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockRowCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/block/BlockRowCommand.java @@ -8,8 +8,7 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -29,6 +28,8 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.Comparator; import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class BlockRowCommand extends AbstractPlayerCommand { private final RequiredArg queryArg = this.withRequiredArg("wildcard block query", "server.commands.block.row.arg.desc", ArgTypes.STRING); @@ -73,17 +74,16 @@ public class BlockRowCommand extends AbstractPlayerCommand { int step = 25; for (int x = 0; x < blockTypes.size(); x += step) { - double distance = 1.0; + int distance = 1; for (int i = 0; i < step; i++) { BlockType blockType = blockTypes.get(i + x); Box boundingBox = boundingBoxes.getAsset(blockType.getHitboxTypeIndex()).get(0).getBoundingBox(); double dimension = Math.ceil(boundingBox.dimension(axis)); - distance += Math.floor(dimension) + 1.0; - Vector3i blockPos = origin.clone() - .add(direction.clone().scale(distance)) - .add(Rotation.Ninety.rotateY(direction, new Vector3i()).scale(x / step * 2)) - .toVector3i(); + distance += (int)(Math.floor(dimension) + 1.0); + Vector3i blockPos = new Vector3i(MathUtil.floor(origin.x), MathUtil.floor(origin.y), MathUtil.floor(origin.z)) + .add(new Vector3i(direction).mul(distance)) + .add(Rotation.Ninety.rotateY(direction, new Vector3i()).mul(x / step * 2)); long chunkIndex = ChunkUtil.indexChunkFromBlock(blockPos.x, blockPos.z); world.getChunkAsync(chunkIndex).thenAccept(chunk -> { int settings = 196; diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/block/SimpleBlockCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/block/SimpleBlockCommand.java index f4753219..1ab0a7a4 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/block/SimpleBlockCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/block/SimpleBlockCommand.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.universe.world.commands.block; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.CommandSender; @@ -16,6 +15,7 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3i; public abstract class SimpleBlockCommand extends AbstractWorldCommand { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkFindCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkFindCommand.java index 3de98f2f..20b77d60 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkFindCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkFindCommand.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.iterator.SpiralIterator; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -22,10 +21,10 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntLists; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BlockBulkFindCommand extends AbstractWorldCommand { @Nonnull @@ -62,8 +61,8 @@ public class BlockBulkFindCommand extends AbstractWorldCommand { assert transformComponent != null; Vector3d playerPos = transformComponent.getPosition(); - baseChunkX = MathUtil.floor(playerPos.getX()) >> 5; - baseChunkZ = MathUtil.floor(playerPos.getZ()) >> 5; + baseChunkX = MathUtil.floor(playerPos.x()) >> 5; + baseChunkZ = MathUtil.floor(playerPos.z()) >> 5; } } @@ -79,11 +78,10 @@ public class BlockBulkFindCommand extends AbstractWorldCommand { long start = System.nanoTime(); int tested = 0; int[] found = new int[]{0}; - IntOpenHashSet temp = new IntOpenHashSet(); ChunkStore chunkComponentStore = world.getChunkStore(); SpiralIterator iterator = new SpiralIterator(originChunkX, originChunkZ, SpiralIterator.MAX_RADIUS); - label34: + label32: while (iterator.hasNext()) { long key = iterator.next(); BlockChunk blockChunk = chunkComponentStore.getChunkReferenceAsync(key) @@ -96,7 +94,7 @@ public class BlockBulkFindCommand extends AbstractWorldCommand { int chunkX = ChunkUtil.xOfChunkIndex(key); int chunkY = sectionIndex; int chunkZ = ChunkUtil.zOfChunkIndex(key); - section.find(idAsList, temp, blockIndex -> { + section.find(idAsList, blockIndex -> { if (found[0] < count) { found[0]++; int x = chunkX << 5 | ChunkUtil.xFromIndex(blockIndex); @@ -106,10 +104,8 @@ public class BlockBulkFindCommand extends AbstractWorldCommand { } }); if (found[0] >= count) { - break label34; + break label32; } - - temp.clear(); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkFindHereCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkFindHereCommand.java index d45c1ff7..0ca1ee02 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkFindHereCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkFindHereCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.iterator.SpiralIterator; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -26,6 +25,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BlockBulkFindHereCommand extends AbstractPlayerCommand { @Nonnull @@ -55,12 +55,12 @@ public class BlockBulkFindHereCommand extends AbstractPlayerCommand { assert transformComponent != null; Vector3d playerPos = transformComponent.getPosition(); - int originChunkX = MathUtil.floor(playerPos.getX()) >> 5; - int originChunkZ = MathUtil.floor(playerPos.getZ()) >> 5; + int originChunkX = MathUtil.floor(playerPos.x()) >> 5; + int originChunkZ = MathUtil.floor(playerPos.z()) >> 5; CompletableFuture.runAsync( () -> { long start = System.nanoTime(); - IntOpenHashSet temp = new IntOpenHashSet(); + new IntOpenHashSet(); ChunkStore chunkComponentStore = world.getChunkStore(); AtomicInteger found = new AtomicInteger(); SpiralIterator iterator = new SpiralIterator(originChunkX, originChunkZ, radius); @@ -74,21 +74,25 @@ public class BlockBulkFindHereCommand extends AbstractPlayerCommand { for (int sectionIndex = 0; sectionIndex < 10; sectionIndex++) { BlockSection section = blockChunk.getSectionAtIndex(sectionIndex); if (section.containsAny(blockIdList)) { - section.find(blockIdList, temp, blockIndex -> found.getAndIncrement()); - temp.clear(); + section.find(blockIdList, blockIndex -> found.getAndIncrement()); } } } long diff = System.nanoTime() - start; - BlockType findBlock = BlockType.getAssetMap().getAsset(blockId); - String blockName = printBlockName ? " " + findBlock.getId() : ""; - playerRef.sendMessage( - Message.translation("server.commands.block.find-here.result") - .param("count", found.get()) - .param("blockName", blockName) - .param("time", TimeUnit.NANOSECONDS.toSeconds(diff)) - ); + if (printBlockName) { + BlockType findBlock = BlockType.getAssetMap().getAsset(blockId); + playerRef.sendMessage( + Message.translation("server.commands.block.find-here.resultWithName") + .param("count", found.get()) + .param("blockName", findBlock.getId()) + .param("time", TimeUnit.NANOSECONDS.toSeconds(diff)) + ); + } else { + playerRef.sendMessage( + Message.translation("server.commands.block.find-here.result").param("count", found.get()).param("time", TimeUnit.NANOSECONDS.toSeconds(diff)) + ); + } } ); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkReplaceCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkReplaceCommand.java index 449ecad4..7ad20b25 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkReplaceCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/block/bulk/BlockBulkReplaceCommand.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.iterator.SpiralIterator; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; @@ -24,11 +23,11 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BlockBulkReplaceCommand extends AbstractPlayerCommand { @Nonnull @@ -60,12 +59,11 @@ public class BlockBulkReplaceCommand extends AbstractPlayerCommand { IntList replaceBlockIdList = getBlockIdList(replaceBlockId); int radius = this.radiusArg.get(context); Vector3d playerPos = transformComponent.getPosition(); - int originChunkX = MathUtil.floor(playerPos.getX()) >> 5; - int originChunkZ = MathUtil.floor(playerPos.getZ()) >> 5; + int originChunkX = MathUtil.floor(playerPos.x()) >> 5; + int originChunkZ = MathUtil.floor(playerPos.z()) >> 5; CompletableFuture.runAsync( () -> { long start = System.nanoTime(); - IntOpenHashSet temp = new IntOpenHashSet(); ChunkStore chunkComponentStore = world.getChunkStore(); AtomicInteger replaced = new AtomicInteger(); SpiralIterator iterator = new SpiralIterator(originChunkX, originChunkZ, radius); @@ -82,7 +80,7 @@ public class BlockBulkReplaceCommand extends AbstractPlayerCommand { int chunkX = ChunkUtil.xOfChunkIndex(key); int chunkY = sectionIndex; int chunkZ = ChunkUtil.zOfChunkIndex(key); - section.find(findBlockIdList, temp, blockIndex -> { + section.find(findBlockIdList, blockIndex -> { int x = chunkX << 5 | ChunkUtil.xFromIndex(blockIndex); int y = chunkY << 5 | ChunkUtil.yFromIndex(blockIndex); int z = chunkZ << 5 | ChunkUtil.zFromIndex(blockIndex); diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/world/perf/WorldPerfCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/world/perf/WorldPerfCommand.java index 93fd8d68..0c4373c9 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/world/perf/WorldPerfCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/world/perf/WorldPerfCommand.java @@ -7,7 +7,7 @@ import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand; -import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.concurrent.TimeUnit; @@ -34,7 +34,7 @@ public class WorldPerfCommand extends AbstractWorldCommand { Message msg = Message.empty(); boolean showDelta = this.deltaFlag.provided(context); boolean showAll = this.allFlag.provided(context); - if (context.sender() instanceof Player) { + if (context.sender() instanceof PlayerRef) { for (int i = 0; i < periods.length; i++) { String length = FormatUtil.timeUnitToString(periods[i], TimeUnit.NANOSECONDS, true); double average = historicMetric.getAverage(i); diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/worldconfig/WorldConfigPauseTimeCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/worldconfig/WorldConfigPauseTimeCommand.java index cdb88ddb..3f237ef3 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/worldconfig/WorldConfigPauseTimeCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/worldconfig/WorldConfigPauseTimeCommand.java @@ -27,12 +27,9 @@ public class WorldConfigPauseTimeCommand extends AbstractWorldCommand { WorldConfig worldConfig = world.getWorldConfig(); worldConfig.setGameTimePaused(timePause); worldConfig.markChanged(); - Message timePausedMessage = Message.translation(timePause ? "server.general.paused" : "server.general.resumed"); + String timePausedMessageKey = timePause ? "server.commands.pausetime.timeInfo" : "server.commands.pausetime.timeInfoResumed"; commandSender.sendMessage( - Message.translation("server.commands.pausetime.timeInfo") - .param("msg", timePausedMessage) - .param("worldName", world.getName()) - .param("time", worldTimeResource.getGameTime().toString()) + Message.translation(timePausedMessageKey).param("worldName", world.getName()).param("time", worldTimeResource.getGameTime().toString()) ); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/commands/worldconfig/WorldConfigSetSpawnCommand.java b/src/com/hypixel/hytale/server/core/universe/world/commands/worldconfig/WorldConfigSetSpawnCommand.java index acc5e054..7ebfde8c 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/commands/worldconfig/WorldConfigSetSpawnCommand.java +++ b/src/com/hypixel/hytale/server/core/universe/world/commands/worldconfig/WorldConfigSetSpawnCommand.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.server.core.universe.world.commands.worldconfig; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg; @@ -22,6 +22,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.text.DecimalFormat; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class WorldConfigSetSpawnCommand extends AbstractWorldCommand { @Nonnull @@ -33,11 +34,11 @@ public class WorldConfigSetSpawnCommand extends AbstractWorldCommand { "position", "server.commands.world.config.setspawn.position.desc", ArgTypes.RELATIVE_POSITION ); @Nonnull - private final DefaultArg rotationArg = this.withDefaultArg( + private final DefaultArg rotationArg = this.withDefaultArg( "rotation", "server.commands.world.config.setspawn.rotation.desc", ArgTypes.ROTATION, - Vector3f.FORWARD, + Rotation3f.IDENTITY, "server.commands.world.config.setspawn.rotation.default.desc" ); @@ -66,10 +67,10 @@ public class WorldConfigSetSpawnCommand extends AbstractWorldCommand { assert transformComponent != null; - position = transformComponent.getPosition().clone(); + position = new Vector3d(transformComponent.getPosition()); } - Vector3f rotation; + Rotation3fc rotation; if (this.rotationArg.provided(context)) { rotation = this.rotationArg.get(context); } else if (context.isPlayer()) { @@ -87,19 +88,19 @@ public class WorldConfigSetSpawnCommand extends AbstractWorldCommand { rotation = this.rotationArg.get(context); } - Transform transform = new Transform(position, rotation); + Transform transform = new Transform(position, new Rotation3f(rotation)); WorldConfig worldConfig = world.getWorldConfig(); worldConfig.setSpawnProvider(new GlobalSpawnProvider(transform)); worldConfig.markChanged(); world.getLogger().at(Level.INFO).log("Set spawn provider to: %s", worldConfig.getSpawnProvider()); context.sendMessage( Message.translation("server.universe.setspawn.info") - .param("posX", DECIMAL.format(position.getX())) - .param("posY", DECIMAL.format(position.getY())) - .param("posZ", DECIMAL.format(position.getZ())) - .param("rotX", DECIMAL.format(rotation.getX())) - .param("rotY", DECIMAL.format(rotation.getY())) - .param("rotZ", DECIMAL.format(rotation.getZ())) + .param("posX", DECIMAL.format(position.x())) + .param("posY", DECIMAL.format(position.y())) + .param("posZ", DECIMAL.format(position.z())) + .param("rotX", DECIMAL.format(rotation.x())) + .param("rotY", DECIMAL.format(rotation.y())) + .param("rotZ", DECIMAL.format(rotation.z())) ); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockFaceTags.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockFaceTags.java index e46fa013..027cc802 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockFaceTags.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockFaceTags.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Arrays; import java.util.Collections; @@ -12,56 +12,58 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class ConnectedBlockFaceTags { public static final BuilderCodec CODEC = BuilderCodec.builder(ConnectedBlockFaceTags.class, ConnectedBlockFaceTags::new) .append(new KeyedCodec<>("North", new ArrayCodec<>(Codec.STRING, String[]::new), false), (o, tags) -> { HashSet strings = new HashSet<>(tags.length); strings.addAll(Arrays.asList(tags)); - o.blockFaceTags.put(Vector3i.NORTH, strings); - }, o -> o.blockFaceTags.containsKey(Vector3i.NORTH) ? o.blockFaceTags.get(Vector3i.NORTH).toArray(String[]::new) : new String[0]) + o.blockFaceTags.put(Vector3iUtil.NORTH, strings); + }, o -> o.blockFaceTags.containsKey(Vector3iUtil.NORTH) ? o.blockFaceTags.get(Vector3iUtil.NORTH).toArray(String[]::new) : new String[0]) .add() .append(new KeyedCodec<>("East", new ArrayCodec<>(Codec.STRING, String[]::new), false), (o, tags) -> { HashSet strings = new HashSet<>(tags.length); strings.addAll(Arrays.asList(tags)); - o.blockFaceTags.put(Vector3i.EAST, strings); - }, o -> o.blockFaceTags.containsKey(Vector3i.EAST) ? o.blockFaceTags.get(Vector3i.EAST).toArray(String[]::new) : new String[0]) + o.blockFaceTags.put(Vector3iUtil.EAST, strings); + }, o -> o.blockFaceTags.containsKey(Vector3iUtil.EAST) ? o.blockFaceTags.get(Vector3iUtil.EAST).toArray(String[]::new) : new String[0]) .add() .append(new KeyedCodec<>("South", new ArrayCodec<>(Codec.STRING, String[]::new), false), (o, tags) -> { HashSet strings = new HashSet<>(tags.length); strings.addAll(Arrays.asList(tags)); - o.blockFaceTags.put(Vector3i.SOUTH, strings); - }, o -> o.blockFaceTags.containsKey(Vector3i.SOUTH) ? o.blockFaceTags.get(Vector3i.SOUTH).toArray(String[]::new) : new String[0]) + o.blockFaceTags.put(Vector3iUtil.SOUTH, strings); + }, o -> o.blockFaceTags.containsKey(Vector3iUtil.SOUTH) ? o.blockFaceTags.get(Vector3iUtil.SOUTH).toArray(String[]::new) : new String[0]) .add() .append(new KeyedCodec<>("West", new ArrayCodec<>(Codec.STRING, String[]::new), false), (o, tags) -> { HashSet strings = new HashSet<>(tags.length); strings.addAll(Arrays.asList(tags)); - o.blockFaceTags.put(Vector3i.WEST, strings); - }, o -> o.blockFaceTags.containsKey(Vector3i.WEST) ? o.blockFaceTags.get(Vector3i.WEST).toArray(String[]::new) : new String[0]) + o.blockFaceTags.put(Vector3iUtil.WEST, strings); + }, o -> o.blockFaceTags.containsKey(Vector3iUtil.WEST) ? o.blockFaceTags.get(Vector3iUtil.WEST).toArray(String[]::new) : new String[0]) .add() .append(new KeyedCodec<>("Up", new ArrayCodec<>(Codec.STRING, String[]::new), false), (o, tags) -> { HashSet strings = new HashSet<>(tags.length); strings.addAll(Arrays.asList(tags)); - o.blockFaceTags.put(Vector3i.UP, strings); - }, o -> o.blockFaceTags.containsKey(Vector3i.UP) ? o.blockFaceTags.get(Vector3i.UP).toArray(String[]::new) : new String[0]) + o.blockFaceTags.put(Vector3iUtil.UP, strings); + }, o -> o.blockFaceTags.containsKey(Vector3iUtil.UP) ? o.blockFaceTags.get(Vector3iUtil.UP).toArray(String[]::new) : new String[0]) .add() .append(new KeyedCodec<>("Down", new ArrayCodec<>(Codec.STRING, String[]::new), false), (o, tags) -> { HashSet strings = new HashSet<>(tags.length); strings.addAll(Arrays.asList(tags)); - o.blockFaceTags.put(Vector3i.DOWN, strings); - }, o -> o.blockFaceTags.containsKey(Vector3i.DOWN) ? o.blockFaceTags.get(Vector3i.DOWN).toArray(String[]::new) : new String[0]) + o.blockFaceTags.put(Vector3iUtil.DOWN, strings); + }, o -> o.blockFaceTags.containsKey(Vector3iUtil.DOWN) ? o.blockFaceTags.get(Vector3iUtil.DOWN).toArray(String[]::new) : new String[0]) .add() .build(); public static final ConnectedBlockFaceTags EMPTY = new ConnectedBlockFaceTags(); @Nonnull - private final Map> blockFaceTags = new Object2ObjectOpenHashMap<>(); + private final Map> blockFaceTags = new Object2ObjectOpenHashMap<>(); public boolean contains(Vector3i direction, String blockFaceTag) { return this.blockFaceTags.containsKey(direction) && this.blockFaceTags.get(direction).contains(blockFaceTag); } @Nonnull - public Map> getBlockFaceTags() { + public Map> getBlockFaceTags() { return this.blockFaceTags; } @@ -70,7 +72,7 @@ public class ConnectedBlockFaceTags { } @Nonnull - public Set getDirections() { + public Set getDirections() { return this.blockFaceTags.keySet(); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockPatternRule.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockPatternRule.java index be3e02b4..253e99a7 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockPatternRule.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockPatternRule.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.codec.codecs.set.SetCodec; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.asset.type.buildertool.config.BlockTypeListAsset; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockPattern; import java.util.Collections; @@ -14,10 +14,16 @@ import java.util.HashSet; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class ConnectedBlockPatternRule { public static final BuilderCodec CODEC = BuilderCodec.builder(ConnectedBlockPatternRule.class, ConnectedBlockPatternRule::new) - .append(new KeyedCodec<>("Position", Vector3i.CODEC, false), (o, relativePosition) -> o.relativePosition = relativePosition, o -> o.relativePosition) + .append( + new KeyedCodec<>("Position", Vector3iUtil.CODEC, false), + (o, relativePosition) -> o.relativePosition = relativePosition, + o -> new Vector3i(o.relativePosition) + ) .add() .append( new KeyedCodec<>("IncludeOrExclude", new EnumCodec<>(ConnectedBlockPatternRule.IncludeOrExclude.class), true), @@ -77,7 +83,7 @@ public class ConnectedBlockPatternRule { .add() .build(); private ConnectedBlockPatternRule.IncludeOrExclude includeOrExclude; - private Vector3i relativePosition = Vector3i.ZERO; + private Vector3ic relativePosition = Vector3iUtil.ZERO; private final HashSet blockTypes = new HashSet<>(); @Nullable private BlockTypeListAsset[] blockTypeListAssets; @@ -85,7 +91,7 @@ public class ConnectedBlockPatternRule { private ConnectedBlockFaceTags faceTags = ConnectedBlockFaceTags.EMPTY; private ConnectedBlockPatternRule.AdjacentSide[] placementNormals; - public Vector3i getRelativePosition() { + public Vector3ic getRelativePosition() { return this.relativePosition; } @@ -117,16 +123,16 @@ public class ConnectedBlockPatternRule { } public static enum AdjacentSide { - Up(Vector3i.UP), - Down(Vector3i.DOWN), - North(Vector3i.NORTH), - East(Vector3i.EAST), - South(Vector3i.SOUTH), - West(Vector3i.WEST); + Up(Vector3iUtil.UP), + Down(Vector3iUtil.DOWN), + North(Vector3iUtil.NORTH), + East(Vector3iUtil.EAST), + South(Vector3iUtil.SOUTH), + West(Vector3iUtil.WEST); - public final Vector3i relativePosition; + public final Vector3ic relativePosition; - private AdjacentSide(Vector3i side) { + private AdjacentSide(Vector3ic side) { this.relativePosition = side; } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockRuleSet.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockRuleSet.java index 61639fa6..0d89a24e 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockRuleSet.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlockRuleSet.java @@ -2,11 +2,11 @@ package com.hypixel.hytale.server.core.universe.world.connectedblocks; import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.codec.lookup.CodecMapCodec; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.universe.world.World; import java.util.Optional; import javax.annotation.Nullable; +import org.joml.Vector3ic; public abstract class ConnectedBlockRuleSet { public static final CodecMapCodec CODEC = new CodecMapCodec<>("Type"); @@ -14,7 +14,7 @@ public abstract class ConnectedBlockRuleSet { public abstract boolean onlyUpdateOnPlacement(); public abstract Optional getConnectedBlockType( - World var1, Vector3i var2, BlockType var3, int var4, Vector3i var5, boolean var6 + World var1, Vector3ic var2, BlockType var3, int var4, Vector3ic var5, boolean var6 ); public void updateCachedBlockTypes(BlockType blockType, BlockTypeAssetMap assetMap) { diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlocksUtil.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlocksUtil.java index fdefa169..16c4c137 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlocksUtil.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/ConnectedBlocksUtil.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.core.universe.world.connectedblocks; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import com.hypixel.hytale.server.core.universe.world.World; @@ -21,6 +20,8 @@ import java.util.Queue; import java.util.Set; import java.util.Map.Entry; import javax.annotation.Nonnull; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class ConnectedBlocksUtil { private static final int MAX_UPDATE_DEPTH = 3; @@ -28,16 +29,16 @@ public class ConnectedBlocksUtil { public static void setConnectedBlockAndNotifyNeighbors( int blockTypeId, @Nonnull RotationTuple blockTypeRotation, - @Nonnull Vector3i placementNormal, - @Nonnull Vector3i blockPosition, + @Nonnull Vector3ic placementNormal, + @Nonnull Vector3ic blockPosition, @Nonnull WorldChunk worldChunkComponent, @Nonnull BlockChunk blockChunkComponent ) { Vector3i coordinate = new Vector3i(blockPosition); BlockType blockType = BlockType.getAssetMap().getAsset(blockTypeId); if (blockType != null) { - BlockSection sectionAtY = blockChunkComponent.getSectionAtBlockY(blockPosition.y); - int filler = sectionAtY.getFiller(blockPosition.x, blockPosition.y, blockPosition.z); + BlockSection sectionAtY = blockChunkComponent.getSectionAtBlockY(blockPosition.y()); + int filler = sectionAtY.getFiller(blockPosition.x(), blockPosition.y(), blockPosition.z()); int settings = 132; if (blockType.getConnectedBlockRuleSet() != null && filler == 0) { int rotationIndex = blockTypeRotation.index(); @@ -59,7 +60,7 @@ public class ConnectedBlocksUtil { } private static void updateNeighborsWithDepth( - @Nonnull WorldChunk worldChunkComponent, @Nonnull Vector3i startCoordinate, @Nonnull Vector3i placementNormal, int settings + @Nonnull WorldChunk worldChunkComponent, @Nonnull Vector3ic startCoordinate, @Nonnull Vector3ic placementNormal, int settings ) { record QueueEntry(Vector3i coordinate, int depth) { } @@ -89,7 +90,7 @@ public class ConnectedBlocksUtil { Entry result = (Entry)var10.next(); location = result.getKey(); connectedBlockResult = result.getValue(); - if (visited.add(location.clone()) && (location.x != coordinate.x || location.y != coordinate.y || location.z != coordinate.z)) { + if (visited.add(new Vector3i(location)) && (location.x != coordinate.x || location.y != coordinate.y || location.z != coordinate.z)) { newWorldChunk = worldChunkComponent; long chunkIndex = ChunkUtil.indexChunkFromBlock(location.x, location.z); if (chunkIndex == worldChunkComponent.getIndex()) { @@ -130,24 +131,27 @@ public class ConnectedBlocksUtil { } if (depth + 1 < 3) { - queue.add(new QueueEntry(location.clone(), depth + 1)); + queue.add(new QueueEntry(new Vector3i(location), depth + 1)); } } } } public static void notifyNeighborsAndCollectChanges( - @Nonnull World world, @Nonnull Vector3i origin, @Nonnull Map desiredChanges, Vector3i placementNormal + @Nonnull World world, + @Nonnull Vector3ic origin, + @Nonnull Map desiredChanges, + Vector3ic placementNormal ) { - Vector3i coordinate = origin.clone(); - long chunkIndex = ChunkUtil.indexChunkFromBlock(origin.x, origin.z); + Vector3i coordinate = new Vector3i(origin); + long chunkIndex = ChunkUtil.indexChunkFromBlock(origin.x(), origin.z()); WorldChunk chunk = world.getChunkIfLoaded(chunkIndex); for (int x1 = -1; x1 <= 1; x1++) { for (int z1 = -1; z1 <= 1; z1++) { for (int y1 = -1; y1 <= 1; y1++) { if (x1 != 0 || y1 != 0 || z1 != 0) { - coordinate.assign(origin).add(x1, y1, z1); + coordinate.set(origin).add(x1, y1, z1); if (coordinate.y >= 0 && coordinate.y < 320 && !desiredChanges.containsKey(coordinate)) { long neighborChunkIndex = ChunkUtil.indexChunkFromBlock(coordinate.x, coordinate.z); if (neighborChunkIndex != chunkIndex) { @@ -171,7 +175,7 @@ public class ConnectedBlocksUtil { int originX = coordinate.x - FillerBlockUtil.unpackX(filler); int originY = coordinate.y - FillerBlockUtil.unpackY(filler); int originZ = coordinate.z - FillerBlockUtil.unpackZ(filler); - coordinate.assign(originX, originY, originZ); + coordinate.set(originX, originY, originZ); } Optional output = getDesiredConnectedBlockType( @@ -179,7 +183,7 @@ public class ConnectedBlocksUtil { ); if (output.isPresent() && (!neighborBlockType.getId().equals(output.get().blockTypeKey()) || output.get().rotationIndex != existingRotation)) { - desiredChanges.put(coordinate.clone(), output.get()); + desiredChanges.put(new Vector3i(coordinate), output.get()); } } } @@ -196,10 +200,10 @@ public class ConnectedBlocksUtil { @Nonnull public static Optional getDesiredConnectedBlockType( @Nonnull World world, - @Nonnull Vector3i coordinate, + @Nonnull Vector3ic coordinate, @Nonnull BlockType currentBlockType, int currentRotation, - @Nonnull Vector3i placementNormal, + @Nonnull Vector3ic placementNormal, boolean isPlacement ) { ConnectedBlockRuleSet ruleSet = currentBlockType.getConnectedBlockRuleSet(); diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomConnectedBlockPattern.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomConnectedBlockPattern.java index 8595af1b..c5e73b62 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomConnectedBlockPattern.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomConnectedBlockPattern.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.codec.codecs.simple.BooleanCodec; import com.hypixel.hytale.math.Axis; import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFlipType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; @@ -26,6 +25,8 @@ import java.util.Random; import java.util.Set; import java.util.Map.Entry; import javax.annotation.Nonnull; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class CustomConnectedBlockPattern extends CustomTemplateConnectedBlockPattern { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -115,11 +116,11 @@ public class CustomConnectedBlockPattern extends CustomTemplateConnectedBlockPat if (template.connectsToOtherMaterials || placedRuleset.getShapeNameToBlockPatternMap().equals(checkingConnectedBlockRuleSet.getShapeNameToBlockPatternMap())) { ConnectedBlockShape blockToCheckConnectedBlockShape = checkingTemplateAsset.connectedBlockShapes.get(shapeName); - Map> ruleFaceTags = rule.getFaceTags().getBlockFaceTags(); + Map> ruleFaceTags = rule.getFaceTags().getBlockFaceTags(); - for (Entry> ruleFaceTag : ruleFaceTags.entrySet()) { + for (Entry> ruleFaceTag : ruleFaceTags.entrySet()) { Vector3i adjustedDirectionOfPattern = Rotation.rotate( - ruleFaceTag.getKey().clone(), Rotation.None.subtract(rotationToCheckUnrotated.yaw()), Rotation.None + new Vector3i(ruleFaceTag.getKey()), Rotation.None.subtract(rotationToCheckUnrotated.yaw()), Rotation.None ); for (String faceTag : ruleFaceTag.getValue()) { @@ -173,11 +174,11 @@ public class CustomConnectedBlockPattern extends CustomTemplateConnectedBlockPat public Optional getConnectedBlockTypeKey( String shapeName, @Nonnull World world, - @Nonnull Vector3i coordinate, + @Nonnull Vector3ic coordinate, @Nonnull CustomTemplateConnectedBlockRuleSet connectedBlockRuleset, @Nonnull BlockType blockType, int rotation, - @Nonnull Vector3i placementNormal, + @Nonnull Vector3ic placementNormal, boolean isPlacement ) { if ((!isPlacement || !this.onlyOnUpdate) && (isPlacement || !this.onlyOnPlacement)) { @@ -202,13 +203,13 @@ public class CustomConnectedBlockPattern extends CustomTemplateConnectedBlockPat label96: for (ConnectedBlockPatternRule ruleToMatch : this.rulesToMatch) { - coordinateToTest.assign(ruleToMatch.getRelativePosition()); + coordinateToTest.set(ruleToMatch.getRelativePosition()); switch ((PatternRotationDefinition.MirrorAxis)patternTransform.second()) { case X: - coordinateToTest.setX(-coordinateToTest.getX()); + coordinateToTest.x = -coordinateToTest.x(); break; case Z: - coordinateToTest.setZ(-coordinateToTest.getZ()); + coordinateToTest.z = -coordinateToTest.z(); } if (ruleToMatch.getPlacementNormals() != null) { diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomConnectedBlockTemplateAsset.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomConnectedBlockTemplateAsset.java index 83a14b82..0e70b382 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomConnectedBlockTemplateAsset.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomConnectedBlockTemplateAsset.java @@ -11,7 +11,6 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.codecs.map.MapCodec; import com.hypixel.hytale.codec.validation.ValidatorCache; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockPattern; import com.hypixel.hytale.server.core.universe.world.World; @@ -21,6 +20,7 @@ import java.util.Optional; import java.util.Map.Entry; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; +import org.joml.Vector3ic; public class CustomConnectedBlockTemplateAsset implements JsonAssetWithMap> { public static final AssetBuilderCodec CODEC = AssetBuilderCodec.builder( @@ -83,11 +83,11 @@ public class CustomConnectedBlockTemplateAsset implements JsonAssetWithMap getConnectedBlockType( World world, - Vector3i coordinate, + Vector3ic coordinate, CustomTemplateConnectedBlockRuleSet ruleSet, BlockType blockType, int rotation, - Vector3i placementNormal, + Vector3ic placementNormal, boolean useDefaultShapeIfNoMatch, boolean isPlacement ) { diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomTemplateConnectedBlockPattern.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomTemplateConnectedBlockPattern.java index 011123a3..28eef9df 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomTemplateConnectedBlockPattern.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomTemplateConnectedBlockPattern.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.server.core.universe.world.connectedblocks; import com.hypixel.hytale.codec.lookup.CodecMapCodec; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.universe.world.World; import java.util.Optional; import javax.annotation.Nonnull; +import org.joml.Vector3ic; public abstract class CustomTemplateConnectedBlockPattern { public static final CodecMapCodec CODEC = new CodecMapCodec<>("Type"); @@ -13,11 +13,11 @@ public abstract class CustomTemplateConnectedBlockPattern { public abstract Optional getConnectedBlockTypeKey( String var1, @Nonnull World var2, - @Nonnull Vector3i var3, + @Nonnull Vector3ic var3, @Nonnull CustomTemplateConnectedBlockRuleSet var4, @Nonnull BlockType var5, int var6, - @Nonnull Vector3i var7, + @Nonnull Vector3ic var7, boolean var8 ); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomTemplateConnectedBlockRuleSet.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomTemplateConnectedBlockRuleSet.java index bc626022..b94c0fa3 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomTemplateConnectedBlockRuleSet.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/CustomTemplateConnectedBlockRuleSet.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.map.MapCodec; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.prefab.selection.mask.BlockPattern; import com.hypixel.hytale.server.core.universe.world.World; @@ -19,6 +18,7 @@ import java.util.Optional; import java.util.Set; import java.util.Map.Entry; import javax.annotation.Nullable; +import org.joml.Vector3ic; public class CustomTemplateConnectedBlockRuleSet extends ConnectedBlockRuleSet { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -84,7 +84,7 @@ public class CustomTemplateConnectedBlockRuleSet extends ConnectedBlockRuleSet { @Override public Optional getConnectedBlockType( - World world, Vector3i testedCoordinate, BlockType blockType, int rotation, Vector3i placementNormal, boolean isPlacement + World world, Vector3ic testedCoordinate, BlockType blockType, int rotation, Vector3ic placementNormal, boolean isPlacement ) { CustomConnectedBlockTemplateAsset shapeTemplateAsset = this.getShapeTemplateAsset(); return shapeTemplateAsset == null diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/PatternRotationDefinition.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/PatternRotationDefinition.java index 46fa1ac6..fcc3c66b 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/PatternRotationDefinition.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/PatternRotationDefinition.java @@ -10,6 +10,7 @@ import it.unimi.dsi.fastutil.ints.IntList; import java.util.AbstractList; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.annotation.Nonnull; public class PatternRotationDefinition { @@ -35,7 +36,12 @@ public class PatternRotationDefinition { @Nonnull public List> getRotations() { return new AbstractList>() { - private final int[] enabledIndexes = this.computeEnabled(); + private final int[] enabledIndexes; + + { + Objects.requireNonNull(PatternRotationDefinition.this); + this.enabledIndexes = this.computeEnabled(); + } private int[] computeEnabled() { IntList idx = new IntArrayList(); diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/Rotation3D.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/Rotation3D.java index 07d239f4..16865016 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/Rotation3D.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/Rotation3D.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.server.core.universe.world.connectedblocks; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; import javax.annotation.Nonnull; +import org.joml.Vector3f; @Deprecated(forRemoval = true) public class Rotation3D { diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/builtin/RoofConnectedBlockRuleSet.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/builtin/RoofConnectedBlockRuleSet.java index 015a3984..17fa90aa 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/builtin/RoofConnectedBlockRuleSet.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/builtin/RoofConnectedBlockRuleSet.java @@ -7,7 +7,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.simple.IntegerCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.ConnectedBlockRuleSetType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFace; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFaceSupport; @@ -23,6 +23,8 @@ import it.unimi.dsi.fastutil.objects.ObjectIntPair; import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements StairLikeConnectedBlockRuleSet { public static final BuilderCodec CODEC = BuilderCodec.builder(RoofConnectedBlockRuleSet.class, RoofConnectedBlockRuleSet::new) @@ -45,7 +47,7 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements private int width = 1; private static StairConnectedBlockRuleSet.StairType getConnectedBlockStairType( - World world, Vector3i coordinate, StairLikeConnectedBlockRuleSet currentRuleSet, int blockId, int rotation, int width + World world, Vector3ic coordinate, StairLikeConnectedBlockRuleSet currentRuleSet, int blockId, int rotation, int width ) { RotationTuple currentRotation = RotationTuple.get(rotation); Rotation currentYaw = currentRotation.yaw(); @@ -112,7 +114,7 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements private static boolean isWidthFulfilled( World world, - Vector3i coordinate, + Vector3ic coordinate, Vector3i mutablePos, StairConnectedBlockRuleSet.StairConnection backConnection, Rotation currentYaw, @@ -123,10 +125,10 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements boolean valid = true; for (int i = 0; i < width - 1; i++) { - mutablePos.assign(backConnection == StairConnectedBlockRuleSet.StairConnection.CORNER_LEFT ? Vector3i.WEST : Vector3i.EAST).scale(i + 1); + mutablePos.set(backConnection == StairConnectedBlockRuleSet.StairConnection.CORNER_LEFT ? Vector3iUtil.WEST : Vector3iUtil.EAST).mul(i + 1); currentYaw.rotateY(mutablePos, mutablePos); int requiredFiller = FillerBlockUtil.pack(mutablePos.x, mutablePos.y, mutablePos.z); - mutablePos.add(coordinate.x, coordinate.y, coordinate.z); + mutablePos.add(coordinate); WorldChunk chunk = world.getChunkIfLoaded(ChunkUtil.indexChunkFromBlock(mutablePos.x, mutablePos.z)); if (chunk != null) { int otherRotation = chunk.getRotationIndex(mutablePos.x, mutablePos.y, mutablePos.z); @@ -145,7 +147,7 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements private static StairConnectedBlockRuleSet.StairConnection getValleyConnection( World world, - Vector3i placementCoordinate, + Vector3ic placementCoordinate, Vector3i checkCoordinate, StairLikeConnectedBlockRuleSet currentRuleSet, RotationTuple rotation, @@ -156,7 +158,7 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements int width ) { Rotation yaw = rotation.yaw(); - mutablePos.assign(reverse ? Vector3i.SOUTH : Vector3i.NORTH).scale(width); + mutablePos.set(reverse ? Vector3iUtil.SOUTH : Vector3iUtil.NORTH).mul(width); yaw.rotateY(mutablePos, mutablePos); mutablePos.add(checkCoordinate.x, checkCoordinate.y, checkCoordinate.z); ObjectIntPair backStair = StairConnectedBlockRuleSet.getStairData( @@ -171,13 +173,13 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements if (!backConnection) { return null; } else { - mutablePos.assign(reverse ? Vector3i.EAST : Vector3i.WEST).scale(width); + mutablePos.set(reverse ? Vector3iUtil.EAST : Vector3iUtil.WEST).mul(width); yaw.rotateY(mutablePos, mutablePos); mutablePos.add(checkCoordinate.x, checkCoordinate.y, checkCoordinate.z); ObjectIntPair leftStair = StairConnectedBlockRuleSet.getStairData( world, mutablePos, currentRuleSet.getMaterialName() ); - mutablePos.assign(reverse ? Vector3i.WEST : Vector3i.EAST).scale(width); + mutablePos.set(reverse ? Vector3iUtil.WEST : Vector3iUtil.EAST).mul(width); yaw.rotateY(mutablePos, mutablePos); mutablePos.add(checkCoordinate.x, checkCoordinate.y, checkCoordinate.z); ObjectIntPair rightStair = StairConnectedBlockRuleSet.getStairData( @@ -211,11 +213,11 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements World world, Vector3i coordinate, StairLikeConnectedBlockRuleSet currentRuleSet, RotationTuple rotation, Vector3i mutablePos ) { Rotation yaw = rotation.yaw(); - Vector3i[] directions = new Vector3i[]{Vector3i.NORTH, Vector3i.SOUTH, Vector3i.EAST, Vector3i.WEST}; + Vector3ic[] directions = new Vector3ic[]{Vector3iUtil.NORTH, Vector3iUtil.SOUTH, Vector3iUtil.EAST, Vector3iUtil.WEST}; Rotation[] yawOffsets = new Rotation[]{Rotation.OneEighty, Rotation.None, Rotation.Ninety, Rotation.TwoSeventy}; for (int i = 0; i < directions.length; i++) { - mutablePos.assign(directions[i]); + mutablePos.set(directions[i]); yaw.rotateY(mutablePos, mutablePos); mutablePos.add(coordinate.x, coordinate.y, coordinate.z); ObjectIntPair stair = StairConnectedBlockRuleSet.getStairData( @@ -261,15 +263,15 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements @Override public Optional getConnectedBlockType( - World world, Vector3i coordinate, BlockType blockType, int rotation, Vector3i placementNormal, boolean isPlacement + World world, Vector3ic coordinate, BlockType blockType, int rotation, Vector3ic placementNormal, boolean isPlacement ) { - WorldChunk chunk = world.getChunkIfLoaded(ChunkUtil.indexChunkFromBlock(coordinate.x, coordinate.z)); + WorldChunk chunk = world.getChunkIfLoaded(ChunkUtil.indexChunkFromBlock(coordinate.x(), coordinate.z())); if (chunk == null) { return Optional.empty(); } else { - int belowBlockId = chunk.getBlock(coordinate.x, coordinate.y - 1, coordinate.z); + int belowBlockId = chunk.getBlock(coordinate.x(), coordinate.y() - 1, coordinate.z()); BlockType belowBlockType = BlockType.getAssetMap().getAsset(belowBlockId); - int belowBlockRotation = chunk.getRotationIndex(coordinate.x, coordinate.y - 1, coordinate.z); + int belowBlockRotation = chunk.getRotationIndex(coordinate.x(), coordinate.y() - 1, coordinate.z()); boolean hollow = true; if (belowBlockType != null) { Map supporting = belowBlockType.getSupporting(belowBlockRotation); @@ -313,7 +315,7 @@ public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements if (newWidth != previousWidth) { Vector3i mutablePos = new Vector3i(); Rotation currentYaw = RotationTuple.get(rotation).yaw(); - mutablePos.assign(Vector3i.EAST).scale(previousWidth); + mutablePos.set(Vector3iUtil.EAST).mul(previousWidth); currentYaw.rotateY(mutablePos, mutablePos); result.addAdditionalBlock(mutablePos, regularBlockType.getId(), rotation); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/builtin/StairConnectedBlockRuleSet.java b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/builtin/StairConnectedBlockRuleSet.java index 14e47989..814a97e4 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/builtin/StairConnectedBlockRuleSet.java +++ b/src/com/hypixel/hytale/server/core/universe/world/connectedblocks/builtin/StairConnectedBlockRuleSet.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.protocol.ConnectedBlockRuleSetType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; @@ -23,6 +23,8 @@ import it.unimi.dsi.fastutil.objects.ObjectIntImmutablePair; import it.unimi.dsi.fastutil.objects.ObjectIntPair; import java.util.Optional; import javax.annotation.Nullable; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class StairConnectedBlockRuleSet extends ConnectedBlockRuleSet implements StairLikeConnectedBlockRuleSet { public static final String DEFAULT_MATERIAL_NAME = "Stair"; @@ -101,7 +103,7 @@ public class StairConnectedBlockRuleSet extends ConnectedBlockRuleSet implements protected static StairConnectedBlockRuleSet.StairConnection getCornerConnection( World world, StairLikeConnectedBlockRuleSet currentRuleSet, - Vector3i coordinate, + Vector3ic coordinate, Vector3i mutablePos, int rotation, Rotation currentYaw, @@ -109,14 +111,14 @@ public class StairConnectedBlockRuleSet extends ConnectedBlockRuleSet implements int width ) { StairConnectedBlockRuleSet.StairConnection backConnection = null; - mutablePos.assign(Vector3i.NORTH).scale(width); + mutablePos.set(Vector3iUtil.NORTH).mul(width); currentYaw.rotateY(mutablePos, mutablePos); - mutablePos.add(coordinate.x, coordinate.y, coordinate.z); + mutablePos.add(coordinate.x(), coordinate.y(), coordinate.z()); ObjectIntPair backStair = getStairData(world, mutablePos, currentRuleSet.getMaterialName()); if (backStair == null && width > 1) { - mutablePos.assign(Vector3i.NORTH).scale(width + 1); + mutablePos.set(Vector3iUtil.NORTH).mul(width + 1); currentYaw.rotateY(mutablePos, mutablePos); - mutablePos.add(coordinate.x, coordinate.y, coordinate.z); + mutablePos.add(coordinate.x(), coordinate.y(), coordinate.z()); backStair = getStairData(world, mutablePos, currentRuleSet.getMaterialName()); if (backStair != null && backStair.first() == StairConnectedBlockRuleSet.StairType.STRAIGHT) { backStair = null; @@ -133,9 +135,9 @@ public class StairConnectedBlockRuleSet extends ConnectedBlockRuleSet implements } if (canConnectTo(currentYaw, otherYaw, upsideDown, otherUpsideDown)) { - mutablePos.assign(Vector3i.SOUTH); + mutablePos.set(Vector3iUtil.SOUTH); otherYaw.rotateY(mutablePos, mutablePos); - mutablePos.add(coordinate.x, coordinate.y, coordinate.z); + mutablePos.add(coordinate); ObjectIntPair sidewaysStair = getStairData(world, mutablePos, currentRuleSet.getMaterialName()); if (sidewaysStair == null || sidewaysStair.rightInt() != rotation) { backConnection = getConnection(currentYaw, otherYaw, otherStairType, false, upsideDown); @@ -147,12 +149,12 @@ public class StairConnectedBlockRuleSet extends ConnectedBlockRuleSet implements } protected static StairConnectedBlockRuleSet.StairConnection getInvertedCornerConnection( - World world, StairLikeConnectedBlockRuleSet currentRuleSet, Vector3i coordinate, Vector3i mutablePos, Rotation currentYaw, boolean upsideDown + World world, StairLikeConnectedBlockRuleSet currentRuleSet, Vector3ic coordinate, Vector3i mutablePos, Rotation currentYaw, boolean upsideDown ) { StairConnectedBlockRuleSet.StairConnection frontConnection = null; - mutablePos.assign(Vector3i.SOUTH); + mutablePos.set(Vector3iUtil.SOUTH); currentYaw.rotateY(mutablePos, mutablePos); - mutablePos.add(coordinate.x, coordinate.y, coordinate.z); + mutablePos.add(coordinate); ObjectIntPair frontStair = getStairData(world, mutablePos, currentRuleSet.getMaterialName()); if (frontStair != null) { StairConnectedBlockRuleSet.StairType otherStairType = frontStair.left(); @@ -244,7 +246,7 @@ public class StairConnectedBlockRuleSet extends ConnectedBlockRuleSet implements @Override public Optional getConnectedBlockType( - World world, Vector3i coordinate, BlockType currentBlockType, int rotation, Vector3i placementNormal, boolean isPlacement + World world, Vector3ic coordinate, BlockType currentBlockType, int rotation, Vector3ic placementNormal, boolean isPlacement ) { RotationTuple currentRotation = RotationTuple.get(rotation); Rotation currentYaw = currentRotation.yaw(); diff --git a/src/com/hypixel/hytale/server/core/universe/world/lighting/ChunkLightingManager.java b/src/com/hypixel/hytale/server/core/universe/world/lighting/ChunkLightingManager.java index 704f67ad..89d19d4f 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/lighting/ChunkLightingManager.java +++ b/src/com/hypixel/hytale/server/core/universe/world/lighting/ChunkLightingManager.java @@ -2,18 +2,19 @@ package com.hypixel.hytale.server.core.universe.world.lighting; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class ChunkLightingManager implements Runnable { @Nonnull @@ -165,7 +166,7 @@ public class ChunkLightingManager implements Runnable { Vector3i chunkPos = new Vector3i(chunkX, 0, chunkZ); for (int chunkY = 0; chunkY < 10; chunkY++) { - chunkPos.setY(chunkY); + chunkPos.y = chunkY; if (this.isQueued(chunkPos)) { return true; } @@ -184,20 +185,16 @@ public class ChunkLightingManager implements Runnable { } } - public boolean invalidateLightAtBlock(WorldChunk worldChunk, int blockX, int blockY, int blockZ, BlockType blockType, int oldHeight, int newHeight) { - return this.lightCalculation.invalidateLightAtBlock(worldChunk, blockX, blockY, blockZ, blockType, oldHeight, newHeight); + public boolean invalidateLightAtBlock(@Nonnull ChunkStore chunkStore, int blockX, int blockY, int blockZ, BlockType blockType, int oldHeight, int newHeight) { + return this.lightCalculation.invalidateLightAtBlock(chunkStore, blockX, blockY, blockZ, blockType, oldHeight, newHeight); } - public boolean invalidateLightInChunk(WorldChunk worldChunk) { - return this.lightCalculation.invalidateLightInChunkSections(worldChunk, 0, 10); + public boolean invalidateLightInChunk(@Nonnull ChunkStore chunkStore, int chunkX, int chunkZ) { + return this.lightCalculation.invalidateLightInChunkSections(chunkStore, chunkX, chunkZ, 0, 10); } - public boolean invalidateLightInChunkSection(WorldChunk worldChunk, int sectionIndex) { - return this.lightCalculation.invalidateLightInChunkSections(worldChunk, sectionIndex, sectionIndex + 1); - } - - public boolean invalidateLightInChunkSections(WorldChunk worldChunk, int sectionIndexFrom, int sectionIndexTo) { - return this.lightCalculation.invalidateLightInChunkSections(worldChunk, sectionIndexFrom, sectionIndexTo); + public boolean invalidateLightInChunkSection(@Nonnull ChunkStore chunkStore, int chunkX, int chunkZ, int sectionIndex) { + return this.lightCalculation.invalidateLightInChunkSections(chunkStore, chunkX, chunkZ, sectionIndex, sectionIndex + 1); } public void invalidateLoadedChunks() { diff --git a/src/com/hypixel/hytale/server/core/universe/world/lighting/FloodLightCalculation.java b/src/com/hypixel/hytale/server/core/universe/world/lighting/FloodLightCalculation.java index c7f41fdf..e024b31b 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/lighting/FloodLightCalculation.java +++ b/src/com/hypixel/hytale/server/core/universe/world/lighting/FloodLightCalculation.java @@ -4,14 +4,13 @@ import com.hypixel.hytale.common.util.FormatUtil; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector2iUtil; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.metrics.metric.AverageCollector; import com.hypixel.hytale.protocol.ColorLight; import com.hypixel.hytale.protocol.Opacity; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; -import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; @@ -32,6 +31,9 @@ import java.util.function.IntBinaryOperator; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2ic; +import org.joml.Vector3i; +import org.joml.Vector3ic; public class FloodLightCalculation implements LightCalculation { protected final ChunkLightingManager chunkLightingManager; @@ -40,7 +42,7 @@ public class FloodLightCalculation implements LightCalculation { protected final AverageCollector borderAvg = new AverageCollector(); protected final AverageCollector avgChunk = new AverageCollector(); protected final BlockSection[][] fromSections = new BlockSection[][]{ - new BlockSection[Vector3i.BLOCK_SIDES.length], new BlockSection[Vector3i.BLOCK_EDGES.length], new BlockSection[Vector3i.BLOCK_CORNERS.length] + new BlockSection[Vector3iUtil.BLOCK_SIDES.length], new BlockSection[Vector3iUtil.BLOCK_EDGES.length], new BlockSection[Vector3iUtil.BLOCK_CORNERS.length] }; public FloodLightCalculation(ChunkLightingManager chunkLightingManager) { @@ -337,16 +339,16 @@ public class FloodLightCalculation implements LightCalculation { @Override public boolean invalidateLightAtBlock( - @Nonnull WorldChunk worldChunk, int blockX, int blockY, int blockZ, @Nonnull BlockType blockType, int oldHeight, int newHeight + @Nonnull ChunkStore chunkStore, int blockX, int blockY, int blockZ, @Nonnull BlockType blockType, int oldHeight, int newHeight ) { - int chunkX = worldChunk.getX(); - int chunkY = blockY >> 5; - int chunkZ = worldChunk.getZ(); + int chunkX = ChunkUtil.chunkCoordinate(blockX); + int chunkY = ChunkUtil.chunkCoordinate(blockY); + int chunkZ = ChunkUtil.chunkCoordinate(blockZ); int oldHeightChunk = oldHeight >> 5; int newHeightChunk = newHeight >> 5; int from = Math.max(MathUtil.minValue(oldHeightChunk, newHeightChunk, chunkY), 0); int to = MathUtil.maxValue(oldHeightChunk, newHeightChunk, chunkY) + 1; - boolean handled = this.invalidateLightInChunkSections(worldChunk, from, to); + boolean handled = this.invalidateLightInChunkSections(chunkStore, chunkX, chunkZ, from, to); this.chunkLightingManager .getLogger() .at(Level.FINER) @@ -354,45 +356,47 @@ public class FloodLightCalculation implements LightCalculation { return handled; } - @Override - public boolean invalidateLightInChunkSections(@Nonnull WorldChunk worldChunk, int sectionIndexFrom, int sectionIndexTo) { - int chunkX = worldChunk.getX(); - int chunkZ = worldChunk.getZ(); - World world = this.chunkLightingManager.getWorld(); - LocalCachedChunkAccessor accessor = LocalCachedChunkAccessor.atChunkCoords(world, chunkX, chunkZ, 1); - accessor.overwrite(worldChunk); - if (!world.isInThread()) { - CompletableFuture.runAsync(accessor::cacheChunksInRadius, world).join(); - } else { - accessor.cacheChunksInRadius(); - } - + private void invalidateLightingFor(@Nonnull ChunkStore chunkStore, int chunkX, int chunkZ, int sectionIndexFrom, int sectionIndexTo) { for (int x = chunkX - 1; x <= chunkX + 1; x++) { for (int z = chunkZ - 1; z <= chunkZ + 1; z++) { - WorldChunk worldChunkTemp = accessor.getChunkIfInMemory(x, z); - if (worldChunkTemp != null) { - for (int y = sectionIndexTo - 1; y >= sectionIndexFrom; y--) { - BlockSection section = worldChunkTemp.getBlockChunk().getSectionAtIndex(y); - if (worldChunkTemp == worldChunk) { - section.invalidateLocalLight(); - } else { - section.invalidateGlobalLight(); - } + for (int y = sectionIndexTo - 1; y >= sectionIndexFrom; y--) { + Ref sectionRef = chunkStore.getChunkSectionReference(x, y, z); + if (sectionRef != null) { + BlockSection section = chunkStore.getStore().getComponent(sectionRef, BlockSection.getComponentType()); + if (section != null) { + if (x == chunkX && z == chunkZ) { + section.invalidateLocalLight(); + } else { + section.invalidateGlobalLight(); + } - if (BlockChunk.SEND_LOCAL_LIGHTING_DATA || BlockChunk.SEND_GLOBAL_LIGHTING_DATA) { - worldChunkTemp.getBlockChunk().invalidateChunkSection(y); + if (BlockChunk.SEND_LOCAL_LIGHTING_DATA || BlockChunk.SEND_GLOBAL_LIGHTING_DATA) { + section.invalidate(); + } } } } } } + } + + @Override + public boolean invalidateLightInChunkSections(@Nonnull ChunkStore chunkStore, int chunkX, int chunkZ, int sectionIndexFrom, int sectionIndexTo) { + if (!chunkStore.getWorld().isInThread()) { + CompletableFuture.runAsync(() -> this.invalidateLightingFor(chunkStore, chunkX, chunkZ, sectionIndexFrom, sectionIndexTo), chunkStore.getWorld()) + .join(); + } else { + this.invalidateLightingFor(chunkStore, chunkX, chunkZ, sectionIndexFrom, sectionIndexTo); + } for (int x = chunkX - 1; x <= chunkX + 1; x++) { - for (int zx = chunkZ - 1; zx <= chunkZ + 1; zx++) { - WorldChunk worldChunkTemp = accessor.getChunkIfInMemory(x, zx); + for (int z = chunkZ - 1; z <= chunkZ + 1; z++) { + Ref worldChunkTemp = chunkStore.getChunkReference(ChunkUtil.indexChunk(x, z)); if (worldChunkTemp != null) { for (int y = sectionIndexTo - 1; y >= sectionIndexFrom; y--) { - this.chunkLightingManager.addToQueue(new Vector3i(x, y, zx)); + if (y >= 0 && y < 10) { + this.chunkLightingManager.addToQueue(new Vector3i(x, y, z)); + } } } } @@ -451,9 +455,9 @@ public class FloodLightCalculation implements LightCalculation { byte skyLight = light.getLight(column, 3); byte propagatedValue = (byte)(skyLight - 1); if (propagatedValue >= 1) { - for (Vector2i side : Vector2i.DIRECTIONS) { - int nx = x + side.x; - int nz = zx + side.y; + for (Vector2ic side : Vector2iUtil.DIRECTIONS) { + int nx = x + side.x(); + int nz = zx + side.y(); if (nx >= 0 && nx < 32 && nz >= 0 && nz < 32) { int neighbourColumn = ChunkUtil.indexColumn(nx, nz); byte neighbourSkyLight = light.getLight(neighbourColumn, 3); @@ -567,12 +571,12 @@ public class FloodLightCalculation implements LightCalculation { int y = ChunkUtil.yFromIndex(blockIndex); int z = ChunkUtil.zFromIndex(blockIndex); - for (Vector3i side : Vector3i.BLOCK_SIDES) { - int nx = x + side.x; + for (Vector3ic side : Vector3iUtil.BLOCK_SIDES) { + int nx = x + side.x(); if (nx >= 0 && nx < 32) { - int ny = y + side.y; + int ny = y + side.y(); if (ny >= 0 && ny < 32) { - int nz = z + side.z; + int nz = z + side.z(); if (nz >= 0 && nz < 32) { int neighbourBlock = ChunkUtil.indexBlock(nx, ny, nz); this.propagateLight( @@ -597,18 +601,18 @@ public class FloodLightCalculation implements LightCalculation { } public boolean testNeighboursForLocalLight(@Nonnull LocalCachedChunkAccessor accessor, @Nonnull WorldChunk worldChunk, int chunkX, int chunkY, int chunkZ) { - Vector3i[][] blockParts = Vector3i.BLOCK_PARTS; + Vector3ic[][] blockParts = Vector3iUtil.BLOCK_PARTS; for (int partType = 0; partType < this.fromSections.length; partType++) { BlockSection[] partSections = this.fromSections[partType]; Arrays.fill(partSections, null); - Vector3i[] directions = blockParts[partType]; + Vector3ic[] directions = blockParts[partType]; for (int i = 0; i < directions.length; i++) { - Vector3i side = directions[i]; - int nx = chunkX + side.x; - int ny = chunkY + side.y; - int nz = chunkZ + side.z; + Vector3ic side = directions[i]; + int nx = chunkX + side.x(); + int ny = chunkY + side.y(); + int nz = chunkZ + side.z(); if (ny >= 0 && ny < 10) { if (nx == chunkX && nz == chunkZ) { BlockSection fromSection = worldChunk.getBlockChunk().getSectionAtIndex(ny); diff --git a/src/com/hypixel/hytale/server/core/universe/world/lighting/FullBrightLightCalculation.java b/src/com/hypixel/hytale/server/core/universe/world/lighting/FullBrightLightCalculation.java index d419328b..ce113ee6 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/lighting/FullBrightLightCalculation.java +++ b/src/com/hypixel/hytale/server/core/universe/world/lighting/FullBrightLightCalculation.java @@ -1,13 +1,16 @@ package com.hypixel.hytale.server.core.universe.world.lighting; +import com.hypixel.hytale.common.util.CompletableFutureUtil; +import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkLightDataBuilder; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class FullBrightLightCalculation implements LightCalculation { private final ChunkLightingManager chunkLightingManager; @@ -33,7 +36,7 @@ public class FullBrightLightCalculation implements LightCalculation { return CalculationResult.NOT_LOADED; } - this.setFullBright(worldChunk, chunkPosition.y); + setFullBright(this.chunkLightingManager.getWorld().getChunkStore(), chunkPosition.x, chunkPosition.y, chunkPosition.z); } return result; @@ -41,39 +44,49 @@ public class FullBrightLightCalculation implements LightCalculation { @Override public boolean invalidateLightAtBlock( - @Nonnull WorldChunk worldChunk, int blockX, int blockY, int blockZ, @Nonnull BlockType blockType, int oldHeight, int newHeight + @Nonnull ChunkStore chunkStore, int blockX, int blockY, int blockZ, @Nonnull BlockType blockType, int oldHeight, int newHeight ) { - boolean handled = this.delegate.invalidateLightAtBlock(worldChunk, blockX, blockY, blockZ, blockType, oldHeight, newHeight); + boolean handled = this.delegate.invalidateLightAtBlock(chunkStore, blockX, blockY, blockZ, blockType, oldHeight, newHeight); if (handled) { - this.setFullBright(worldChunk, blockY >> 5); + setFullBright(chunkStore, ChunkUtil.chunkCoordinate(blockX), ChunkUtil.chunkCoordinate(blockY), ChunkUtil.chunkCoordinate(blockZ)); } return handled; } @Override - public boolean invalidateLightInChunkSections(@Nonnull WorldChunk worldChunk, int sectionIndexFrom, int sectionIndexTo) { - boolean handled = this.delegate.invalidateLightInChunkSections(worldChunk, sectionIndexFrom, sectionIndexTo); + public boolean invalidateLightInChunkSections(@Nonnull ChunkStore chunkStore, int chunkX, int chunkZ, int sectionIndexFrom, int sectionIndexTo) { + boolean handled = this.delegate.invalidateLightInChunkSections(chunkStore, chunkX, chunkZ, sectionIndexFrom, sectionIndexTo); if (handled) { for (int y = sectionIndexTo - 1; y >= sectionIndexFrom; y--) { - this.setFullBright(worldChunk, y); + setFullBright(chunkStore, chunkX, y, chunkZ); } } return handled; } - public void setFullBright(@Nonnull WorldChunk worldChunk, int chunkY) { - BlockSection section = worldChunk.getBlockChunk().getSectionAtIndex(chunkY); - ChunkLightDataBuilder light = new ChunkLightDataBuilder(section.getGlobalChangeCounter()); + public static void setFullBright(@Nonnull ChunkStore chunkStore, int chunkX, int chunkY, int chunkZ) { + CompletableFutureUtil._catch( + chunkStore.getChunkSectionReferenceAsync(chunkX, chunkY, chunkZ) + .thenApplyAsync( + ref -> ref != null && ref.isValid() ? ref.getStore().getComponent((Ref)ref, BlockSection.getComponentType()) : null, + chunkStore.getWorld() + ) + .thenAcceptAsync(section -> { + if (section != null) { + ChunkLightDataBuilder light = new ChunkLightDataBuilder(section.getGlobalChangeCounter()); - for (int i = 0; i < 32768; i++) { - light.setSkyLight(i, (byte)15); - } + for (int i = 0; i < 32768; i++) { + light.setSkyLight(i, (byte)15); + } - section.setGlobalLight(light); - if (BlockChunk.SEND_LOCAL_LIGHTING_DATA || BlockChunk.SEND_GLOBAL_LIGHTING_DATA) { - worldChunk.getBlockChunk().invalidateChunkSection(chunkY); - } + section.setGlobalLight(light); + if (BlockChunk.SEND_LOCAL_LIGHTING_DATA || BlockChunk.SEND_GLOBAL_LIGHTING_DATA) { + section.invalidate(); + } + } + }) + ); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/lighting/LightCalculation.java b/src/com/hypixel/hytale/server/core/universe/world/lighting/LightCalculation.java index bce852c4..a9b352b3 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/lighting/LightCalculation.java +++ b/src/com/hypixel/hytale/server/core/universe/world/lighting/LightCalculation.java @@ -1,9 +1,10 @@ package com.hypixel.hytale.server.core.universe.world.lighting; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import javax.annotation.Nonnull; +import org.joml.Vector3i; public interface LightCalculation { void init(@Nonnull WorldChunk var1); @@ -11,7 +12,7 @@ public interface LightCalculation { @Nonnull CalculationResult calculateLight(@Nonnull Vector3i var1); - boolean invalidateLightAtBlock(@Nonnull WorldChunk var1, int var2, int var3, int var4, @Nonnull BlockType var5, int var6, int var7); + boolean invalidateLightAtBlock(@Nonnull ChunkStore var1, int var2, int var3, int var4, @Nonnull BlockType var5, int var6, int var7); - boolean invalidateLightInChunkSections(@Nonnull WorldChunk var1, int var2, int var3); + boolean invalidateLightInChunkSections(@Nonnull ChunkStore var1, int var2, int var3, int var4, int var5); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/map/WorldMap.java b/src/com/hypixel/hytale/server/core/universe/world/map/WorldMap.java index 9f253610..72928b9a 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/map/WorldMap.java +++ b/src/com/hypixel/hytale/server/core/universe/world/map/WorldMap.java @@ -2,12 +2,11 @@ package com.hypixel.hytale.server.core.universe.world.map; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.worldmap.MapChunk; import com.hypixel.hytale.protocol.packets.worldmap.MapImage; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; import com.hypixel.hytale.protocol.packets.worldmap.UpdateWorldMap; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.io.NetworkSerializable; import com.hypixel.hytale.server.core.util.PositionUtil; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; @@ -16,6 +15,8 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import java.util.Map; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class WorldMap implements NetworkSerializable { private final Map pointsOfInterest = new Object2ObjectOpenHashMap<>(); @@ -46,7 +47,8 @@ public class WorldMap implements NetworkSerializable { } public void addPointOfInterest(String id, String name, String markerType, @Nonnull Transform transform) { - MapMarker old = this.pointsOfInterest.putIfAbsent(id, new MapMarker(id, null, name, markerType, PositionUtil.toTransformPacket(transform), null, null)); + MapMarker old = this.pointsOfInterest + .putIfAbsent(id, new MapMarker(id, Message.translation(name).getFormattedMessage(), markerType, PositionUtil.toTransformPacket(transform), null, null)); if (old != null) { throw new IllegalArgumentException("Id " + id + " already exists!"); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/BlockState.java b/src/com/hypixel/hytale/server/core/universe/world/meta/BlockState.java deleted file mode 100644 index 096581ee..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/BlockState.java +++ /dev/null @@ -1,293 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta; - -import com.google.common.flogger.StackSize; -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.ExtraInfo; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.codec.lookup.ACodecMapCodec; -import com.hypixel.hytale.codec.lookup.CodecMapCodec; -import com.hypixel.hytale.component.Archetype; -import com.hypixel.hytale.component.ArchetypeChunk; -import com.hypixel.hytale.component.Component; -import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.meta.state.exceptions.NoSuchBlockStateException; -import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Level; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.bson.BsonDocument; - -@Deprecated(forRemoval = true) -public abstract class BlockState implements Component { - private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - public static final CodecMapCodec CODEC = new CodecMapCodec<>("Type"); - public static final BuilderCodec BASE_CODEC = BuilderCodec.abstractBuilder(BlockState.class) - .addField( - new KeyedCodec<>("Position", Vector3i.CODEC), - (entity, o) -> entity.position = o, - entity -> entity.position != null && !Vector3i.ZERO.equals(entity.position) ? entity.position : null - ) - .build(); - public static final KeyedCodec TYPE_STRUCTURE = new KeyedCodec<>("Type", Codec.STRING); - public static final String OPEN_WINDOW = "OpenWindow"; - public static final String CLOSE_WINDOW = "CloseWindow"; - final AtomicBoolean initialized = new AtomicBoolean(false); - @Nullable - private WorldChunk chunk; - private Vector3i position; - protected Ref reference; - - public void setReference(Ref reference) { - if (this.reference != null && this.reference.isValid()) { - throw new IllegalArgumentException("Entity already has a valid EntityReference: " + this.reference + " new reference " + reference); - } else { - this.reference = reference; - } - } - - public Ref getReference() { - return this.reference; - } - - public void unloadFromWorld() { - if (this.reference != null && this.reference.isValid()) { - throw new IllegalArgumentException("Tried to unlock used block state"); - } else { - this.chunk = null; - } - } - - public boolean initialize(BlockType blockType) { - return true; - } - - public void onUnload() { - } - - public void validateInitialized() { - if (!this.initialized.get()) { - throw new IllegalArgumentException(String.valueOf(this)); - } - } - - public int getIndex() { - return ChunkUtil.indexBlockInColumn(this.position.x, this.position.y, this.position.z); - } - - public void setPosition(WorldChunk chunk, @Nullable Vector3i position) { - this.chunk = chunk; - if (position != null) { - position.assign(position.getX() & 31, position.getY(), position.getZ() & 31); - if (position.equals(Vector3i.ZERO)) { - LOGGER.at(Level.WARNING).withStackTrace(StackSize.FULL).log("BlockState position set to (0,0,0): %s", this); - } - - this.position = position; - } - } - - public void setPosition(@Nonnull Vector3i position) { - position.assign(position.getX() & 31, position.getY(), position.getZ() & 31); - if (position.equals(Vector3i.ZERO)) { - LOGGER.at(Level.WARNING).withStackTrace(StackSize.FULL).log("BlockState position set to (0,0,0): %s", this); - } - - this.position = position; - } - - @Nonnull - public Vector3i getPosition() { - return this.position.clone(); - } - - public Vector3i __internal_getPosition() { - return this.position; - } - - public void clearPositionForSerialization() { - this.position = null; - } - - public int getBlockX() { - return this.chunk.getX() << 5 | this.position.getX(); - } - - public int getBlockY() { - return this.position.y; - } - - public int getBlockZ() { - return this.chunk.getZ() << 5 | this.position.getZ(); - } - - @Nonnull - public Vector3i getBlockPosition() { - return new Vector3i(this.getBlockX(), this.getBlockY(), this.getBlockZ()); - } - - @Nonnull - public Vector3d getCenteredBlockPosition() { - BlockType blockType = this.getBlockType(); - Vector3d blockCenter = new Vector3d(0.0, 0.0, 0.0); - blockType.getBlockCenter(this.getRotationIndex(), blockCenter); - return blockCenter.add(this.getBlockX(), this.getBlockY(), this.getBlockZ()); - } - - @Nullable - public WorldChunk getChunk() { - return this.chunk; - } - - @Nullable - public BlockType getBlockType() { - return this.getChunk().getBlockType(this.position); - } - - public int getRotationIndex() { - return this.getChunk().getRotationIndex(this.position.x, this.position.y, this.position.z); - } - - public void invalidate() { - } - - public void markNeedsSave() { - this.getChunk().markNeedsSaving(); - } - - public BsonDocument saveToDocument() { - return CODEC.encode(this).asDocument(); - } - - @Nullable - @Override - public Component clone() { - BsonDocument document = CODEC.encode(this, ExtraInfo.THREAD_LOCAL.get()).asDocument(); - return CODEC.decode(document, ExtraInfo.THREAD_LOCAL.get()); - } - - @Nonnull - public Holder toHolder() { - if (this.reference != null && this.reference.isValid() && this.chunk != null) { - Holder holder = ChunkStore.REGISTRY.newHolder(); - Store componentStore = this.chunk.getWorld().getChunkStore().getStore(); - Archetype archetype = componentStore.getArchetype(this.reference); - - for (int i = archetype.getMinIndex(); i < archetype.length(); i++) { - ComponentType componentType = archetype.get(i); - if (componentType != null) { - holder.addComponent(componentType, componentStore.getComponent(this.reference, componentType)); - } - } - - return holder; - } else { - Holder holder = ChunkStore.REGISTRY.newHolder(); - ComponentType componentType = BlockStateModule.get().getComponentType((Class)this.getClass()); - if (componentType == null) { - throw new IllegalArgumentException("Unable to find component type for: " + this); - } else { - holder.addComponent(componentType, this); - return holder; - } - } - } - - @Nullable - public static BlockState load(BsonDocument doc, @Nonnull WorldChunk chunk, @Nonnull Vector3i pos) throws NoSuchBlockStateException { - return load(doc, chunk, pos, chunk.getBlockType(pos.getX(), pos.getY(), pos.getZ())); - } - - @Nullable - public static BlockState load(BsonDocument doc, @Nullable WorldChunk chunk, Vector3i pos, BlockType blockType) throws NoSuchBlockStateException { - BlockState blockState; - try { - blockState = CODEC.decode(doc); - } catch (ACodecMapCodec.UnknownIdException var6) { - throw new NoSuchBlockStateException(var6); - } - - blockState.setPosition(chunk, pos); - if (chunk != null) { - if (!blockState.initialize(blockType)) { - return null; - } - - blockState.initialized.set(true); - } - - return blockState; - } - - @Nullable - @Deprecated - public static BlockState ensureState(@Nonnull WorldChunk worldChunk, int x, int y, int z) { - BlockType blockType = worldChunk.getBlockType(x, y, z); - if (blockType != null && !blockType.isUnknown()) { - StateData state = blockType.getState(); - if (state != null && state.getId() != null) { - Vector3i position = new Vector3i(x, y, z); - BlockState blockState = BlockStateModule.get().createBlockState(state.getId(), worldChunk, position, blockType); - if (blockState != null) { - worldChunk.setState(x, y, z, blockState); - } - - return blockState; - } else { - return null; - } - } else { - return null; - } - } - - @Deprecated - public static BlockState getBlockState(@Nullable Ref reference, @Nonnull ComponentAccessor componentAccessor) { - if (reference == null) { - return null; - } else { - ComponentType componentType = findComponentType(componentAccessor.getArchetype(reference), BlockState.class); - return componentType == null ? null : componentAccessor.getComponent(reference, componentType); - } - } - - @Nullable - @Deprecated - public static BlockState getBlockState(int index, @Nonnull ArchetypeChunk archetypeChunk) { - ComponentType componentType = findComponentType(archetypeChunk.getArchetype(), BlockState.class); - return componentType == null ? null : archetypeChunk.getComponent(index, componentType); - } - - @Nullable - @Deprecated - public static BlockState getBlockState(@Nonnull Holder holder) { - ComponentType componentType = findComponentType(holder.getArchetype(), BlockState.class); - return componentType == null ? null : holder.getComponent(componentType); - } - - @Nullable - private static , T extends C> ComponentType findComponentType( - @Nonnull Archetype archetype, @Nonnull Class entityClass - ) { - for (int i = archetype.getMinIndex(); i < archetype.length(); i++) { - ComponentType> componentType = (ComponentType>)archetype.get(i); - if (componentType != null && entityClass.isAssignableFrom(componentType.getTypeClass())) { - return (ComponentType)componentType; - } - } - - return null; - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateModule.java b/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateModule.java deleted file mode 100644 index 1a500d65..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateModule.java +++ /dev/null @@ -1,518 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.common.plugin.PluginManifest; -import com.hypixel.hytale.component.AddReason; -import com.hypixel.hytale.component.ArchetypeChunk; -import com.hypixel.hytale.component.CommandBuffer; -import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.component.DisableProcessingAssert; -import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.component.RemoveReason; -import com.hypixel.hytale.component.ResourceType; -import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.component.dependency.Dependency; -import com.hypixel.hytale.component.dependency.RootDependency; -import com.hypixel.hytale.component.query.Query; -import com.hypixel.hytale.component.spatial.KDTree; -import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.component.system.HolderSystem; -import com.hypixel.hytale.component.system.MetricSystem; -import com.hypixel.hytale.component.system.RefSystem; -import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.metrics.MetricResults; -import com.hypixel.hytale.metrics.MetricsRegistry; -import com.hypixel.hytale.protocol.ToClientPacket; -import com.hypixel.hytale.server.core.HytaleServer; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData; -import com.hypixel.hytale.server.core.modules.block.BlockModule; -import com.hypixel.hytale.server.core.modules.block.system.ItemContainerStateSpatialSystem; -import com.hypixel.hytale.server.core.plugin.JavaPlugin; -import com.hypixel.hytale.server.core.plugin.JavaPluginInit; -import com.hypixel.hytale.server.core.plugin.PluginState; -import com.hypixel.hytale.server.core.universe.PlayerRef; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.chunk.ChunkFlag; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.chunk.state.TickableBlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.DestroyableBlockState; -import com.hypixel.hytale.server.core.universe.world.meta.state.ItemContainerState; -import com.hypixel.hytale.server.core.universe.world.meta.state.SendableBlockState; -import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.bson.BsonDocument; - -@Deprecated(forRemoval = true) -public class BlockStateModule extends JavaPlugin { - public static final PluginManifest MANIFEST = PluginManifest.corePlugin(BlockStateModule.class).depends(BlockModule.class).build(); - private static BlockStateModule instance; - @Deprecated - private final Map, ComponentType> classToComponentType = new ConcurrentHashMap<>(); - private ResourceType, ChunkStore>> itemContainerSpatialResourceType; - - public static BlockStateModule get() { - return instance; - } - - public ResourceType, ChunkStore>> getItemContainerSpatialResourceType() { - return this.itemContainerSpatialResourceType; - } - - public BlockStateModule(@Nonnull JavaPluginInit init) { - super(init); - instance = this; - } - - @Override - protected void setup() { - this.registerBlockState( - ItemContainerState.class, - "container", - ItemContainerState.CODEC, - ItemContainerState.ItemContainerStateData.class, - ItemContainerState.ItemContainerStateData.CODEC - ); - this.itemContainerSpatialResourceType = this.getChunkStoreRegistry().registerSpatialResource(() -> new KDTree<>(Ref::isValid)); - this.getChunkStoreRegistry().registerSystem(new ItemContainerStateSpatialSystem(this.itemContainerSpatialResourceType)); - this.getChunkStoreRegistry().registerSystem(new BlockStateModule.ItemContainerStateRefSystem()); - } - - @Nullable - public BlockStateRegistration registerBlockState(@Nonnull Class clazz, @Nonnull String key, Codec codec) { - return this.registerBlockState(clazz, key, codec, null, null); - } - - @Nullable - public BlockStateRegistration registerBlockState( - @Nonnull Class clazz, @Nonnull String key, @Nullable Codec codec, Class dataClass, @Nullable Codec dataCodec - ) { - if (this.isDisabled()) { - return null; - } else { - BlockState.CODEC.register(key, clazz, codec); - if (dataCodec != null) { - StateData.CODEC.register(key, dataClass, dataCodec); - } - - ComponentType componentType; - if (codec != null) { - componentType = this.getChunkStoreRegistry().registerComponent(clazz, key, (BuilderCodec)codec); - } else { - componentType = this.getChunkStoreRegistry().registerComponent(clazz, () -> { - throw new UnsupportedOperationException("Not implemented!"); - }); - } - - this.classToComponentType.put(clazz, componentType); - this.getChunkStoreRegistry().registerSystem(new BlockStateModule.LegacyLateInitBlockStateSystem<>(componentType), true); - this.getChunkStoreRegistry().registerSystem(new BlockStateModule.LegacyBlockStateHolderSystem<>(componentType), true); - this.getChunkStoreRegistry().registerSystem(new BlockStateModule.LegacyBlockStateRefSystem<>(componentType), true); - if (TickableBlockState.class.isAssignableFrom(clazz)) { - this.getChunkStoreRegistry().registerSystem(new BlockStateModule.LegacyTickingBlockStateSystem<>(componentType), true); - } - - if (SendableBlockState.class.isAssignableFrom(clazz)) { - this.getChunkStoreRegistry().registerSystem(new BlockStateModule.LegacyLoadPacketBlockStateSystem<>(componentType), true); - this.getChunkStoreRegistry().registerSystem(new BlockStateModule.LegacyUnloadPacketBlockStateSystem<>(componentType), true); - } - - return new BlockStateRegistration(clazz, () -> this.getState() == PluginState.ENABLED, () -> this.unregisterBlockState(clazz, dataClass)); - } - } - - public void unregisterBlockState(Class clazz, @Nullable Class dataClass) { - if (!HytaleServer.get().isShuttingDown()) { - BlockState.CODEC.remove(clazz); - ChunkStore.REGISTRY.unregisterComponent(this.classToComponentType.remove(clazz)); - if (dataClass != null) { - StateData.CODEC.remove(dataClass); - } - } - } - - @Nullable - public T createBlockState(Class clazz, WorldChunk chunk, Vector3i pos, BlockType blockType) { - String id = BlockState.CODEC.getIdFor(clazz); - return (T)this.createBlockState(id, chunk, pos, blockType); - } - - @Nullable - public BlockState createBlockState(String key, WorldChunk chunk, Vector3i pos, BlockType blockType) { - Codec codec = BlockState.CODEC.getCodecFor(key); - if (codec == null) { - this.getLogger().at(Level.WARNING).log("Failed to create BlockState for '%s' null codec", key); - return null; - } else { - BlockState blockState = codec.decode(new BsonDocument()); - if (blockState == null) { - this.getLogger().at(Level.WARNING).log("Failed to create BlockState for '%s' null value from supplier", key); - return null; - } else { - blockState.setPosition(chunk, pos); - if (!blockState.initialize(blockType)) { - return null; - } else { - blockState.initialized.set(true); - return blockState; - } - } - } - } - - @Nullable - public ComponentType getComponentType(@Nullable Class entityClass) { - if (this.isDisabled()) { - return null; - } else { - return (ComponentType)(entityClass == null ? null : this.classToComponentType.get(entityClass)); - } - } - - public static class ItemContainerStateRefSystem extends RefSystem { - private static final Query query = BlockStateModule.get().getComponentType(ItemContainerState.class); - - @Override - public Query getQuery() { - return query; - } - - @Override - public void onEntityAdded( - @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer - ) { - commandBuffer.getExternalData() - .getWorld() - .getChunkStore() - .getStore() - .getResource(BlockModule.BlockStateInfoNeedRebuild.getResourceType()) - .markAsNeedRebuild(); - } - - @Override - public void onEntityRemove( - @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer - ) { - commandBuffer.getExternalData() - .getWorld() - .getChunkStore() - .getStore() - .getResource(BlockModule.BlockStateInfoNeedRebuild.getResourceType()) - .markAsNeedRebuild(); - } - - @Nonnull - @Override - public String toString() { - return "ItemContainerStateRefSystem{}"; - } - } - - public static class LegacyBlockStateHolderSystem extends HolderSystem implements DisableProcessingAssert { - private final ComponentType componentType; - - public LegacyBlockStateHolderSystem(ComponentType componentType) { - this.componentType = componentType; - } - - @Override - public Query getQuery() { - return this.componentType; - } - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - T blockState = holder.getComponent(this.componentType); - switch (reason) { - case REMOVE: - if (blockState instanceof DestroyableBlockState destroyableBlockState) { - destroyableBlockState.onDestroy(); - } - - blockState.unloadFromWorld(); - break; - case UNLOAD: - blockState.onUnload(); - blockState.unloadFromWorld(); - } - } - - @Nonnull - @Override - public String toString() { - return "LegacyBlockStateSystem{componentType=" + this.componentType + "}"; - } - } - - public static class LegacyBlockStateRefSystem extends RefSystem implements DisableProcessingAssert { - private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - private final ComponentType componentType; - - public LegacyBlockStateRefSystem(ComponentType componentType) { - this.componentType = componentType; - } - - @Override - public Query getQuery() { - return this.componentType; - } - - @Override - public void onEntityAdded( - @Nonnull Ref ref, @Nonnull AddReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer - ) { - T blockState = store.getComponent(ref, this.componentType); - int index = blockState.getIndex(); - WorldChunk chunk = blockState.getChunk(); - if (chunk == null) { - Vector3i position = blockState.getBlockPosition(); - int chunkX = MathUtil.floor(position.getX()) >> 5; - int chunkZ = MathUtil.floor(position.getZ()) >> 5; - World world = store.getExternalData().getWorld(); - WorldChunk worldChunk = world.getChunkIfInMemory(ChunkUtil.indexChunk(chunkX, chunkZ)); - if (worldChunk != null && !worldChunk.not(ChunkFlag.INIT)) { - if (worldChunk.not(ChunkFlag.TICKING)) { - commandBuffer.run(_store -> { - Holder holder = _store.removeEntity(ref, RemoveReason.UNLOAD); - worldChunk.getBlockComponentChunk().addEntityHolder(index, holder); - }); - } - - int x = ChunkUtil.xFromBlockInColumn(index); - int y = ChunkUtil.yFromBlockInColumn(index); - int z = ChunkUtil.zFromBlockInColumn(index); - blockState.setPosition(worldChunk, new Vector3i(x, y, z)); - } - } - - blockState.setReference(ref); - if (!blockState.initialized.get()) { - if (!blockState.initialize(blockState.getChunk().getBlockType(blockState.getPosition()))) { - LOGGER.at(Level.WARNING).log("Block State failed initialize: %s, %s, %s", blockState, blockState.getPosition(), chunk); - commandBuffer.removeEntity(ref, RemoveReason.REMOVE); - } else { - blockState.initialized.set(true); - } - } - } - - @Override - public void onEntityRemove( - @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer - ) { - } - - @Nonnull - @Override - public String toString() { - return "LegacyBlockStateSystem{componentType=" + this.componentType + "}"; - } - } - - public static class LegacyLateInitBlockStateSystem extends EntityTickingSystem implements DisableProcessingAssert { - private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - private final ComponentType componentType; - @Nonnull - private final Query query; - - public LegacyLateInitBlockStateSystem(ComponentType componentType) { - this.componentType = componentType; - this.query = Query.and(componentType, BlockModule.BlockStateInfo.getComponentType()); - } - - @Nonnull - @Override - public Query getQuery() { - return this.query; - } - - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } - - @Override - public void tick( - float dt, - int index, - @Nonnull ArchetypeChunk archetypeChunk, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - T blockStateComponent = archetypeChunk.getComponent(index, this.componentType); - - assert blockStateComponent != null; - - BlockModule.BlockStateInfo blockStateInfoComponent = archetypeChunk.getComponent(index, BlockModule.BlockStateInfo.getComponentType()); - - assert blockStateInfoComponent != null; - - try { - if (!blockStateComponent.initialized.get()) { - blockStateComponent.initialized.set(true); - if (blockStateComponent.getReference() == null || !blockStateComponent.getReference().isValid()) { - blockStateComponent.setReference(archetypeChunk.getReferenceTo(index)); - } - - World world = store.getExternalData().getWorld(); - Store chunkStore = world.getChunkStore().getStore(); - WorldChunk worldChunkComponent = chunkStore.getComponent(blockStateInfoComponent.getChunkRef(), WorldChunk.getComponentType()); - - assert worldChunkComponent != null; - - int x = ChunkUtil.xFromBlockInColumn(blockStateInfoComponent.getIndex()); - int y = ChunkUtil.yFromBlockInColumn(blockStateInfoComponent.getIndex()); - int z = ChunkUtil.zFromBlockInColumn(blockStateInfoComponent.getIndex()); - blockStateComponent.setPosition(worldChunkComponent, new Vector3i(x, y, z)); - int blockIndex = worldChunkComponent.getBlock(x, y, z); - BlockType blockType = BlockType.getAssetMap().getAsset(blockIndex); - if (!blockStateComponent.initialize(blockType)) { - LOGGER.at(Level.SEVERE).log("Removing invalid block state %s", blockStateComponent); - commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE); - } - } - } catch (Exception var16) { - LOGGER.at(Level.SEVERE).withCause(var16).log("Exception while re-init BlockState! Removing!! %s", blockStateComponent); - commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE); - } - } - - @Nonnull - @Override - public String toString() { - return "LegacyLateInitBlockStateSystem{componentType=" + this.componentType + "}"; - } - } - - public static class LegacyLoadPacketBlockStateSystem extends ChunkStore.LoadPacketDataQuerySystem { - private final ComponentType componentType; - - public LegacyLoadPacketBlockStateSystem(ComponentType componentType) { - this.componentType = componentType; - } - - @Override - public Query getQuery() { - return this.componentType; - } - - public void fetch( - int index, - @Nonnull ArchetypeChunk archetypeChunk, - Store store, - CommandBuffer commandBuffer, - PlayerRef player, - List results - ) { - SendableBlockState state = (SendableBlockState)BlockState.getBlockState(index, archetypeChunk); - if (state.canPlayerSee(player)) { - state.sendTo(results); - } - } - - @Nonnull - @Override - public String toString() { - return "LegacyLoadPacketBlockStateSystem{componentType=" + this.componentType + "}"; - } - } - - public static class LegacyTickingBlockStateSystem - extends EntityTickingSystem - implements DisableProcessingAssert, - MetricSystem { - private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - private static final MetricsRegistry> METRICS_REGISTRY = new MetricsRegistry>() - .register("ComponentType", o -> o.componentType.getTypeClass().toString(), Codec.STRING); - private final ComponentType componentType; - - public LegacyTickingBlockStateSystem(ComponentType componentType) { - this.componentType = componentType; - } - - @Override - public Query getQuery() { - return this.componentType; - } - - @Override - public void tick( - float dt, - int index, - @Nonnull ArchetypeChunk archetypeChunk, - @Nonnull Store store, - @Nonnull CommandBuffer commandBuffer - ) { - T blockState = archetypeChunk.getComponent(index, this.componentType); - - try { - ((TickableBlockState)blockState).tick(dt, index, archetypeChunk, store, commandBuffer); - } catch (Throwable var8) { - LOGGER.at(Level.SEVERE).withCause(var8).log("Exception while ticking BlockState! Removing!! %s", blockState); - commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE); - } - } - - @Nonnull - @Override - public MetricResults toMetricResults(Store store) { - return METRICS_REGISTRY.toMetricResults(this); - } - - @Nonnull - @Override - public String toString() { - return "LegacyTickingBlockStateSystem{componentType=" + this.componentType + "}"; - } - } - - public static class LegacyUnloadPacketBlockStateSystem extends ChunkStore.UnloadPacketDataQuerySystem { - private final ComponentType componentType; - - public LegacyUnloadPacketBlockStateSystem(ComponentType componentType) { - this.componentType = componentType; - } - - @Override - public Query getQuery() { - return this.componentType; - } - - public void fetch( - int index, - @Nonnull ArchetypeChunk archetypeChunk, - Store store, - CommandBuffer commandBuffer, - PlayerRef player, - List results - ) { - SendableBlockState state = (SendableBlockState)BlockState.getBlockState(index, archetypeChunk); - if (state.canPlayerSee(player)) { - state.unloadFrom(results); - } - } - - @Nonnull - @Override - public String toString() { - return "LegacyUnloadPacketBlockStateSystem{componentType=" + this.componentType + "}"; - } - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateRegistration.java b/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateRegistration.java deleted file mode 100644 index 054ee8e5..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateRegistration.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta; - -import com.hypixel.hytale.registry.Registration; -import java.util.function.BooleanSupplier; -import javax.annotation.Nonnull; - -public class BlockStateRegistration extends Registration { - private final Class blockStateClass; - - public BlockStateRegistration(Class blockStateClass, BooleanSupplier isEnabled, Runnable unregister) { - super(isEnabled, unregister); - this.blockStateClass = blockStateClass; - } - - public BlockStateRegistration(@Nonnull BlockStateRegistration registration, BooleanSupplier isEnabled, Runnable unregister) { - super(isEnabled, unregister); - this.blockStateClass = registration.blockStateClass; - } - - public Class getBlockStateClass() { - return this.blockStateClass; - } - - @Nonnull - @Override - public String toString() { - return "BlockStateRegistration{blockStateClass=" + this.blockStateClass + ", " + super.toString() + "}"; - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateRegistry.java b/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateRegistry.java deleted file mode 100644 index 92f06f28..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/BlockStateRegistry.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.function.consumer.BooleanConsumer; -import com.hypixel.hytale.registry.Registry; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData; -import java.util.List; -import java.util.function.BooleanSupplier; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class BlockStateRegistry extends Registry { - public BlockStateRegistry(@Nonnull List registrations, BooleanSupplier precondition, String preconditionMessage) { - super(registrations, precondition, preconditionMessage, BlockStateRegistration::new); - } - - @Nullable - public BlockStateRegistration registerBlockState(@Nonnull Class clazz, @Nonnull String key, Codec codec) { - this.checkPrecondition(); - return this.register(BlockStateModule.get().registerBlockState(clazz, key, codec)); - } - - @Nullable - public BlockStateRegistration registerBlockState( - @Nonnull Class clazz, @Nonnull String key, Codec codec, Class dataClass, Codec dataCodec - ) { - this.checkPrecondition(); - return this.register(BlockStateModule.get().registerBlockState(clazz, key, codec, dataClass, dataCodec)); - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/BlockMapMarker.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/BlockMapMarker.java index de72544c..8af2371d 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/BlockMapMarker.java +++ b/src/com/hypixel/hytale/server/core/universe/world/meta/state/BlockMapMarker.java @@ -16,8 +16,8 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.universe.world.World; @@ -30,6 +30,7 @@ import com.hypixel.hytale.server.core.universe.world.worldmap.markers.MarkersCol import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class BlockMapMarker implements Component { public static final BuilderCodec CODEC = BuilderCodec.builder(BlockMapMarker.class, BlockMapMarker::new) @@ -80,7 +81,7 @@ public class BlockMapMarker implements Component { for (BlockMapMarkersResource.BlockMapMarkerData markerData : markers.values()) { Vector3i position = markerData.getPosition(); MapMarker marker = new MapMarkerBuilder(markerData.getMarkerId(), markerData.getIcon(), new Transform(position)) - .withCustomName(markerData.getName()) + .withName(Message.translation(markerData.getName())) .build(); collector.add(marker); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/BlockMapMarkersResource.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/BlockMapMarkersResource.java index db52087e..5cdbb171 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/BlockMapMarkersResource.java +++ b/src/com/hypixel/hytale/server/core/universe/world/meta/state/BlockMapMarkersResource.java @@ -7,7 +7,7 @@ import com.hypixel.hytale.codec.codecs.map.MapCodec; import com.hypixel.hytale.component.Resource; import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.math.block.BlockUtil; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; @@ -16,6 +16,7 @@ import java.util.HashMap; import java.util.UUID; import java.util.Map.Entry; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BlockMapMarkersResource implements Resource { public static final BuilderCodec CODEC = BuilderCodec.builder(BlockMapMarkersResource.class, BlockMapMarkersResource::new) @@ -73,7 +74,7 @@ public class BlockMapMarkersResource implements Resource { public static final BuilderCodec CODEC = BuilderCodec.builder( BlockMapMarkersResource.BlockMapMarkerData.class, BlockMapMarkersResource.BlockMapMarkerData::new ) - .append(new KeyedCodec<>("Position", Vector3i.CODEC), (o, v) -> o.position = v, o -> o.position) + .append(new KeyedCodec<>("Position", Vector3iUtil.CODEC), (o, v) -> o.position = v, o -> o.position) .add() .append(new KeyedCodec<>("Name", Codec.STRING), (o, v) -> o.name = v, o -> o.name) .add() diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/BreakValidatedBlockState.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/BreakValidatedBlockState.java deleted file mode 100644 index 2bec2dc3..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/BreakValidatedBlockState.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta.state; - -import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import javax.annotation.Nonnull; - -public interface BreakValidatedBlockState { - boolean canDestroy(@Nonnull Ref var1, @Nonnull ComponentAccessor var2); -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/DestroyableBlockState.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/DestroyableBlockState.java deleted file mode 100644 index b1389f16..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/DestroyableBlockState.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta.state; - -@Deprecated -public interface DestroyableBlockState { - void onDestroy(); -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/ItemContainerBlockState.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/ItemContainerBlockState.java deleted file mode 100644 index b90b9f1b..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/ItemContainerBlockState.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta.state; - -import com.hypixel.hytale.server.core.inventory.container.ItemContainer; - -public interface ItemContainerBlockState { - ItemContainer getItemContainer(); -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/ItemContainerState.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/ItemContainerState.java deleted file mode 100644 index 1f2de237..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/ItemContainerState.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta.state; - -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.KeyedCodec; -import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.component.AddReason; -import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.event.EventPriority; -import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData; -import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerBlockWindow; -import com.hypixel.hytale.server.core.entity.entities.player.windows.WindowManager; -import com.hypixel.hytale.server.core.inventory.ItemStack; -import com.hypixel.hytale.server.core.inventory.container.ItemContainer; -import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; -import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class ItemContainerState extends BlockState implements ItemContainerBlockState, DestroyableBlockState { - public static final Codec CODEC = BuilderCodec.builder(ItemContainerState.class, ItemContainerState::new, BlockState.BASE_CODEC) - .addField(new KeyedCodec<>("Custom", Codec.BOOLEAN), (state, o) -> state.custom = o, state -> state.custom) - .addField(new KeyedCodec<>("AllowViewing", Codec.BOOLEAN), (state, o) -> state.allowViewing = o, state -> state.allowViewing) - .addField(new KeyedCodec<>("Droplist", Codec.STRING), (state, o) -> state.droplist = o, state -> state.droplist) - .addField(new KeyedCodec<>("ItemContainer", SimpleItemContainer.CODEC), (state, o) -> state.itemContainer = o, state -> state.itemContainer) - .build(); - private final Map windows = new ConcurrentHashMap<>(); - protected boolean custom; - protected boolean allowViewing = true; - @Nullable - protected String droplist; - protected SimpleItemContainer itemContainer; - - @Override - public boolean initialize(@Nonnull BlockType blockType) { - if (!super.initialize(blockType)) { - return false; - } else if (this.custom) { - return true; - } else { - short capacity = 20; - if (blockType.getState() instanceof ItemContainerState.ItemContainerStateData itemContainerStateData) { - capacity = itemContainerStateData.getCapacity(); - } - - List remainder = new ObjectArrayList<>(); - this.itemContainer = ItemContainer.ensureContainerCapacity(this.itemContainer, capacity, SimpleItemContainer::new, remainder); - this.itemContainer.registerChangeEvent(EventPriority.LAST, this::onItemChange); - if (!remainder.isEmpty()) { - WorldChunk chunk = this.getChunk(); - World world = chunk.getWorld(); - Store store = world.getEntityStore().getStore(); - HytaleLogger.getLogger() - .at(Level.WARNING) - .withCause(new Throwable()) - .log( - "Dropping %d excess items from item container: %s at world: %s, chunk: %s, block: %s", - remainder.size(), - blockType.getId(), - chunk.getWorld().getName(), - chunk, - this.getPosition() - ); - Vector3i blockPosition = this.getBlockPosition(); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(store, remainder, blockPosition.toVector3d(), Vector3f.ZERO); - store.addEntities(itemEntityHolders, AddReason.SPAWN); - } - - return true; - } - } - - public boolean canOpen(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - return true; - } - - public void onOpen(@Nonnull Ref ref, @Nonnull World world, @Nonnull Store store) { - } - - @Override - public void onDestroy() { - WindowManager.closeAndRemoveAll(this.windows); - WorldChunk chunk = this.getChunk(); - World world = chunk.getWorld(); - Store store = world.getEntityStore().getStore(); - List allItemStacks = this.itemContainer.dropAllItemStacks(); - Vector3d dropPosition = this.getBlockPosition().toVector3d().add(0.5, 0.0, 0.5); - Holder[] itemEntityHolders = ItemComponent.generateItemDrops(store, allItemStacks, dropPosition, Vector3f.ZERO); - if (itemEntityHolders.length > 0) { - world.execute(() -> store.addEntities(itemEntityHolders, AddReason.SPAWN)); - } - } - - public void setCustom(boolean custom) { - this.custom = custom; - this.markNeedsSave(); - } - - public void setAllowViewing(boolean allowViewing) { - this.allowViewing = allowViewing; - this.markNeedsSave(); - } - - public boolean isAllowViewing() { - return this.allowViewing; - } - - public void setItemContainer(SimpleItemContainer itemContainer) { - this.itemContainer = itemContainer; - this.markNeedsSave(); - } - - @Nullable - public String getDroplist() { - return this.droplist; - } - - public void setDroplist(@Nullable String droplist) { - this.droplist = droplist; - this.markNeedsSave(); - } - - @Nonnull - public Map getWindows() { - return this.windows; - } - - @Override - public ItemContainer getItemContainer() { - return this.itemContainer; - } - - public void onItemChange(ItemContainer.ItemContainerChangeEvent event) { - this.markNeedsSave(); - } - - public static class ItemContainerStateData extends StateData { - public static final BuilderCodec CODEC = BuilderCodec.builder( - ItemContainerState.ItemContainerStateData.class, ItemContainerState.ItemContainerStateData::new, StateData.DEFAULT_CODEC - ) - .appendInherited( - new KeyedCodec<>("Capacity", Codec.INTEGER), - (t, i) -> t.capacity = i.shortValue(), - t -> Integer.valueOf(t.capacity), - (o, p) -> o.capacity = p.capacity - ) - .add() - .build(); - private short capacity = 20; - - protected ItemContainerStateData() { - } - - public short getCapacity() { - return this.capacity; - } - - @Nonnull - @Override - public String toString() { - return "ItemContainerStateData{capacity=" + this.capacity + "} " + super.toString(); - } - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/PlacedByBlockState.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/PlacedByBlockState.java deleted file mode 100644 index daeaea78..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/PlacedByBlockState.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta.state; - -import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; -import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import javax.annotation.Nonnull; - -public interface PlacedByBlockState { - void placedBy(@Nonnull Ref var1, @Nonnull String var2, @Nonnull BlockState var3, @Nonnull ComponentAccessor var4); -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/RespawnBlock.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/RespawnBlock.java index b61d0eaa..bf472752 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/RespawnBlock.java +++ b/src/com/hypixel/hytale/server/core/universe/world/meta/state/RespawnBlock.java @@ -15,7 +15,6 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerRespawnPointData; import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerWorldData; @@ -29,6 +28,7 @@ import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class RespawnBlock implements Component { public static final BuilderCodec CODEC = BuilderCodec.builder(RespawnBlock.class, RespawnBlock::new) diff --git a/src/com/hypixel/hytale/server/core/universe/world/meta/state/SendableBlockState.java b/src/com/hypixel/hytale/server/core/universe/world/meta/state/SendableBlockState.java deleted file mode 100644 index 6f22b243..00000000 --- a/src/com/hypixel/hytale/server/core/universe/world/meta/state/SendableBlockState.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.hypixel.hytale.server.core.universe.world.meta.state; - -import com.hypixel.hytale.protocol.ToClientPacket; -import com.hypixel.hytale.server.core.universe.PlayerRef; -import java.util.List; - -@Deprecated -public interface SendableBlockState { - void sendTo(List var1); - - void unloadFrom(List var1); - - default boolean canPlayerSee(PlayerRef player) { - return true; - } -} diff --git a/src/com/hypixel/hytale/server/core/universe/world/path/IPathWaypoint.java b/src/com/hypixel/hytale/server/core/universe/world/path/IPathWaypoint.java index 475eac28..555884b2 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/path/IPathWaypoint.java +++ b/src/com/hypixel/hytale/server/core/universe/world/path/IPathWaypoint.java @@ -1,17 +1,17 @@ package com.hypixel.hytale.server.core.universe.world.path; import com.hypixel.hytale.component.ComponentAccessor; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public interface IPathWaypoint { int getOrder(); Vector3d getWaypointPosition(@Nonnull ComponentAccessor var1); - Vector3f getWaypointRotation(@Nonnull ComponentAccessor var1); + Rotation3f getWaypointRotation(@Nonnull ComponentAccessor var1); double getPauseTime(); diff --git a/src/com/hypixel/hytale/server/core/universe/world/path/SimplePathWaypoint.java b/src/com/hypixel/hytale/server/core/universe/world/path/SimplePathWaypoint.java index 87138196..4533df27 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/path/SimplePathWaypoint.java +++ b/src/com/hypixel/hytale/server/core/universe/world/path/SimplePathWaypoint.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.server.core.universe.world.path; import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SimplePathWaypoint implements IPathWaypoint { private int order; @@ -29,7 +29,7 @@ public class SimplePathWaypoint implements IPathWaypoint { @Nonnull @Override - public Vector3f getWaypointRotation(@Nonnull ComponentAccessor componentAccessor) { + public Rotation3f getWaypointRotation(@Nonnull ComponentAccessor componentAccessor) { return this.transform.getRotation(); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/spawn/FitToHeightMapSpawnProvider.java b/src/com/hypixel/hytale/server/core/universe/world/spawn/FitToHeightMapSpawnProvider.java index 04ce2020..676e6801 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/spawn/FitToHeightMapSpawnProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/spawn/FitToHeightMapSpawnProvider.java @@ -5,11 +5,11 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class FitToHeightMapSpawnProvider implements ISpawnProvider { @Nonnull @@ -35,12 +35,12 @@ public class FitToHeightMapSpawnProvider implements ISpawnProvider { public Transform getSpawnPoint(@Nonnull World world, @Nonnull UUID uuid) { Transform spawnPoint = this.spawnProvider.getSpawnPoint(world, uuid); Vector3d position = spawnPoint.getPosition(); - if (position.getY() < 0.0) { - WorldChunk worldChunk = world.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(position.getX(), position.getZ())); + if (position.y() < 0.0) { + WorldChunk worldChunk = world.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(position.x(), position.z())); if (worldChunk != null) { - int x = MathUtil.floor(position.getX()); - int z = MathUtil.floor(position.getZ()); - position.setY(worldChunk.getHeight(x, z) + 1); + int x = MathUtil.floor(position.x()); + int z = MathUtil.floor(position.z()); + position.y = worldChunk.getHeight(x, z) + 1; } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/spawn/GlobalSpawnProvider.java b/src/com/hypixel/hytale/server/core/universe/world/spawn/GlobalSpawnProvider.java index 587e67bb..2e9b5375 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/spawn/GlobalSpawnProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/spawn/GlobalSpawnProvider.java @@ -3,10 +3,10 @@ package com.hypixel.hytale.server.core.universe.world.spawn; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class GlobalSpawnProvider implements ISpawnProvider { @Nonnull @@ -27,7 +27,7 @@ public class GlobalSpawnProvider implements ISpawnProvider { @Override public Transform getSpawnPoint(@Nonnull World world, @Nonnull UUID uuid) { - return this.spawnPoint.clone(); + return new Transform(this.spawnPoint); } @Nonnull @@ -38,6 +38,6 @@ public class GlobalSpawnProvider implements ISpawnProvider { @Override public boolean isWithinSpawnDistance(@Nonnull Vector3d position, double distance) { - return position.distanceSquaredTo(this.spawnPoint.getPosition()) < distance * distance; + return position.distanceSquared(this.spawnPoint.getPosition()) < distance * distance; } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/spawn/ISpawnProvider.java b/src/com/hypixel/hytale/server/core/universe/world/spawn/ISpawnProvider.java index 747eea6e..8d95bfd5 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/spawn/ISpawnProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/spawn/ISpawnProvider.java @@ -4,13 +4,13 @@ import com.hypixel.hytale.codec.lookup.BuilderCodecMapCodec; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import java.util.UUID; import javax.annotation.Nonnull; +import org.joml.Vector3d; public interface ISpawnProvider { @Nonnull diff --git a/src/com/hypixel/hytale/server/core/universe/world/spawn/IndividualSpawnProvider.java b/src/com/hypixel/hytale/server/core/universe/world/spawn/IndividualSpawnProvider.java index f0aad842..31a751c8 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/spawn/IndividualSpawnProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/spawn/IndividualSpawnProvider.java @@ -5,11 +5,11 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.codecs.array.ArrayCodec; import com.hypixel.hytale.math.util.HashUtil; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class IndividualSpawnProvider implements ISpawnProvider { @Nonnull @@ -39,7 +39,7 @@ public class IndividualSpawnProvider implements ISpawnProvider { @Override public Transform getSpawnPoint(@Nonnull World world, @Nonnull UUID uuid) { - return this.spawnPoints[Math.abs((int)HashUtil.hashUuid(uuid)) % this.spawnPoints.length].clone(); + return new Transform(this.spawnPoints[Math.abs((int)HashUtil.hashUuid(uuid)) % this.spawnPoints.length]); } @Override @@ -57,7 +57,7 @@ public class IndividualSpawnProvider implements ISpawnProvider { double distanceSquared = distance * distance; for (Transform point : this.spawnPoints) { - if (position.distanceSquaredTo(point.getPosition()) < distanceSquared) { + if (position.distanceSquared(point.getPosition()) < distanceSquared) { return true; } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/ChunkStore.java b/src/com/hypixel/hytale/server/core/universe/world/storage/ChunkStore.java index c8c706d8..c0464ccb 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/ChunkStore.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/ChunkStore.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.codec.store.CodecStore; import com.hypixel.hytale.common.util.FormatUtil; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Component; -import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentRegistry; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; @@ -25,6 +24,7 @@ import com.hypixel.hytale.component.system.StoreSystem; import com.hypixel.hytale.component.system.data.EntityDataSystem; import com.hypixel.hytale.event.IEventDispatcher; import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.math.data.Int3ObjectOpenHashMap; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricsRegistry; @@ -37,6 +37,7 @@ import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkFlag; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection; import com.hypixel.hytale.server.core.universe.world.events.ChunkPreLoadProcessEvent; import com.hypixel.hytale.server.core.universe.world.storage.component.ChunkSavingSystems; import com.hypixel.hytale.server.core.universe.world.storage.component.ChunkUnloadingSystem; @@ -95,6 +96,8 @@ public class ChunkStore implements WorldProvider { private final World world; @Nonnull private final Long2ObjectConcurrentHashMap chunks = new Long2ObjectConcurrentHashMap<>(true, ChunkUtil.NOT_FOUND); + private final StampedLock lock = new StampedLock(); + private final Int3ObjectOpenHashMap chunkSections = new Int3ObjectOpenHashMap<>(); private Store store; private Object storageData; @Nullable @@ -266,27 +269,68 @@ public class ChunkStore implements WorldProvider { this.world.getNotificationHandler().updateChunk(worldChunkComponent.getIndex()); } - oldReference = this.store.addEntity(holder, AddReason.SPAWN); - if (oldReference == null) { + ChunkColumn chunkColumn = holder.ensureAndGetComponent(ChunkColumn.getComponentType()); + Ref ref = this.store.addEntity(holder, AddReason.SPAWN); + if (ref == null) { throw new UnsupportedOperationException("Unable to add the chunk to the world!"); } else { - worldChunkComponent.setReference(oldReference); - stamp = chunkState.lock.writeLock(); + Ref[] refs = chunkColumn.getSections(); + Holder[] storedHolders = chunkColumn.takeSectionHolders(); + boolean isTicking = worldChunkComponent.is(ChunkFlag.TICKING); - Ref var17; + for (int i = 0; i < refs.length; i++) { + Holder section; + AddReason reason; + if (storedHolders != null && storedHolders[i] != null) { + section = storedHolders[i]; + reason = worldChunkComponent.is(ChunkFlag.NEWLY_GENERATED) ? AddReason.SPAWN : AddReason.LOAD; + } else { + section = REGISTRY.newHolder(); + reason = AddReason.SPAWN; + } + + section.putComponent(ChunkSection.getComponentType(), new ChunkSection(ref, worldChunkComponent.getX(), i, worldChunkComponent.getZ())); + if (!isTicking) { + section.ensureComponent(REGISTRY.getNonTickingComponentType()); + } else { + section.tryRemoveComponent(REGISTRY.getNonTickingComponentType()); + } + + refs[i] = this.store.addEntity(section, reason); + long stampx = this.lock.writeLock(); + + try { + ChunkStore.ChunkLoadState sectionState = this.chunkSections.get(worldChunkComponent.getX(), i, worldChunkComponent.getZ()); + if (sectionState == null) { + sectionState = new ChunkStore.ChunkLoadState(); + sectionState.reference = refs[i]; + this.chunkSections.put(worldChunkComponent.getX(), i, worldChunkComponent.getZ(), sectionState); + } else { + sectionState.reference = refs[i]; + sectionState.future = null; + } + } finally { + this.lock.unlockWrite(stampx); + } + } + + worldChunkComponent.setReference(ref); + long stampx = chunkState.lock.writeLock(); + + Ref var33; try { - chunkState.reference = oldReference; + chunkState.reference = ref; chunkState.flags = 0; chunkState.future = null; chunkState.throwable = null; chunkState.failedWhen = 0L; chunkState.failedCounter = 0; - var17 = oldReference; + var33 = ref; } finally { - chunkState.lock.unlockWrite(stamp); + chunkState.lock.unlockWrite(stampx); } - return var17; + return var33; } } } @@ -309,6 +353,27 @@ public class ChunkStore implements WorldProvider { } else { this.chunks.remove(index, chunkState); } + + long sectionStamp = this.lock.writeLock(); + + try { + for (int i = 0; i < 10; i++) { + ChunkStore.ChunkLoadState sectionState = this.chunkSections.get(worldChunkComponent.getX(), i, worldChunkComponent.getZ()); + if (sectionState != null) { + if (sectionState.reference != null && sectionState.reference.isValid()) { + throw new IllegalStateException("Section reference was failed to be removed"); + } + + if (sectionState.future != null) { + sectionState.reference = null; + } else { + this.chunkSections.remove(worldChunkComponent.getX(), i, worldChunkComponent.getZ()); + } + } + } + } finally { + this.lock.unlockWrite(sectionStamp); + } } finally { chunkState.lock.unlockRead(stamp); } @@ -341,37 +406,145 @@ public class ChunkStore implements WorldProvider { @Nullable public Ref getChunkSectionReference(int x, int y, int z) { - Ref ref = this.getChunkReference(ChunkUtil.indexChunk(x, z)); - if (ref == null) { - return null; - } else { - ChunkColumn chunkColumnComponent = this.store.getComponent(ref, ChunkColumn.getComponentType()); - return chunkColumnComponent == null ? null : chunkColumnComponent.getSection(y); - } - } + long sectionStamp = this.lock.readLock(); - @Nullable - public Ref getChunkSectionReference(@Nonnull ComponentAccessor commandBuffer, int x, int y, int z) { - Ref ref = this.getChunkReference(ChunkUtil.indexChunk(x, z)); - if (ref == null) { + ChunkStore.ChunkLoadState chunkState; + try { + chunkState = this.chunkSections.get(x, y, z); + } finally { + this.lock.unlockRead(sectionStamp); + } + + if (chunkState == null) { return null; } else { - ChunkColumn chunkColumnComponent = commandBuffer.getComponent(ref, ChunkColumn.getComponentType()); - return chunkColumnComponent == null ? null : chunkColumnComponent.getSection(y); + long stamp = chunkState.lock.tryOptimisticRead(); + Ref reference = chunkState.reference; + if (chunkState.lock.validate(stamp)) { + return reference; + } else { + stamp = chunkState.lock.readLock(); + + Ref var10; + try { + var10 = chunkState.reference; + } finally { + chunkState.lock.unlockRead(stamp); + } + + return var10; + } } } @Nonnull public CompletableFuture> getChunkSectionReferenceAsync(int x, int y, int z) { - return y >= 0 && y < 10 ? this.getChunkReferenceAsync(ChunkUtil.indexChunk(x, z)).thenApplyAsync(ref -> { - if (ref != null && ref.isValid()) { - Store store = ref.getStore(); - ChunkColumn chunkColumnComponent = store.getComponent((Ref)ref, ChunkColumn.getComponentType()); - return chunkColumnComponent == null ? null : chunkColumnComponent.getSection(y); + return this.getChunkSectionReferenceAsync(x, y, z, 0); + } + + @Nonnull + public CompletableFuture> getChunkSectionReferenceAsync(int x, int y, int z, int flags) { + if (y >= 0 && y < 10) { + if ((flags & 3) == 3) { + long sectionStamp = this.lock.readLock(); + + ChunkStore.ChunkLoadState chunkState; + try { + chunkState = this.chunkSections.get(x, y, z); + } finally { + this.lock.unlockRead(sectionStamp); + } + + if (chunkState == null) { + return CompletableFuture.completedFuture(null); + } else { + long stamp = chunkState.lock.tryOptimisticRead(); + Ref ref = chunkState.reference; + if (chunkState.lock.validate(stamp) && ref != null) { + return CompletableFuture.completedFuture(ref); + } else { + stamp = chunkState.lock.readLock(); + + CompletableFuture var40; + try { + if (chunkState.reference != null) { + return CompletableFuture.completedFuture(chunkState.reference); + } + + if (chunkState.future != null) { + return chunkState.future; + } + + var40 = CompletableFuture.completedFuture(null); + } finally { + chunkState.lock.unlockRead(stamp); + } + + return var40; + } + } } else { - return null; + long sectionStamp = this.lock.writeLock(); + + ChunkStore.ChunkLoadState chunkStatex; + try { + chunkStatex = this.chunkSections.get(x, y, z); + if (chunkStatex == null) { + this.chunkSections.put(x, y, z, chunkStatex = new ChunkStore.ChunkLoadState()); + } + } finally { + this.lock.unlockWrite(sectionStamp); + } + + long stamp = chunkStatex.lock.writeLock(); + + CompletableFuture finalChunkState; + try { + if (chunkStatex.reference != null) { + return CompletableFuture.completedFuture(chunkStatex.reference); + } + + if (chunkStatex.future == null) { + chunkStatex.future = this.getChunkReferenceAsync(ChunkUtil.indexChunk(x, z), flags).thenApply(v -> { + long stamp1 = chunkState.lock.readLock(); + + Ref var4x; + try { + var4x = chunkState.reference; + } finally { + chunkState.lock.unlockRead(stamp1); + } + + return var4x; + }); + return chunkStatex.future; + } + + finalChunkState = chunkStatex.future; + } finally { + chunkStatex.lock.unlockWrite(stamp); + } + + return finalChunkState; } - }, this.store.getExternalData().getWorld()) : CompletableFuture.failedFuture(new IndexOutOfBoundsException("Invalid y: " + y)); + } else { + return CompletableFuture.completedFuture(null); + } + } + + @Nullable + public Ref getChunkSectionReferenceAtBlock(int blockX, int blockY, int blockZ) { + return this.getChunkSectionReference(ChunkUtil.chunkCoordinate(blockX), ChunkUtil.chunkCoordinate(blockY), ChunkUtil.chunkCoordinate(blockZ)); + } + + @Nonnull + public CompletableFuture> getChunkSectionReferenceAtBlockAsync(int blockX, int blockY, int blockZ) { + return this.getChunkSectionReferenceAsync(ChunkUtil.chunkCoordinate(blockX), ChunkUtil.chunkCoordinate(blockY), ChunkUtil.chunkCoordinate(blockZ)); + } + + @Nonnull + public CompletableFuture> getChunkSectionReferenceAtBlockAsync(int blockX, int blockY, int blockZ, int flags) { + return this.getChunkSectionReferenceAsync(ChunkUtil.chunkCoordinate(blockX), ChunkUtil.chunkCoordinate(blockY), ChunkUtil.chunkCoordinate(blockZ), flags); } @Nullable @@ -725,6 +898,18 @@ public class ChunkStore implements WorldProvider { } } + public void pauseBackgroundSaving(ChunkSavingSystems.Data data) { + if (this.saver != null) { + this.saver.pauseBackgroundSaving(data); + } + } + + public void resumeBackgroundSaving() { + if (this.saver != null) { + this.saver.resumeBackgroundSaving(); + } + } + static { CodecStore.STATIC.putCodecSupplier(HOLDER_CODEC_KEY, REGISTRY::getEntityCodec); REGISTRY.registerSystem(new ChunkStore.ChunkLoaderSaverSetupSystem()); diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/EntityStore.java b/src/com/hypixel/hytale/server/core/universe/world/storage/EntityStore.java index 0b714512..2e63823a 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/EntityStore.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/EntityStore.java @@ -19,8 +19,8 @@ import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.WorldProvider; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -47,7 +47,7 @@ public class EntityStore implements WorldProvider { @Nonnull private final Map> entitiesByUuid = new ConcurrentHashMap<>(); @Nonnull - private final Int2ObjectMap> networkIdToRef = new Int2ObjectOpenHashMap<>(); + private final Int2ReferenceMap> networkIdToRef = new Int2ReferenceOpenHashMap<>(); public EntityStore(@Nonnull World world) { this.world = world; diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/IChunkSaver.java b/src/com/hypixel/hytale/server/core/universe/world/storage/IChunkSaver.java index a6046ce3..39f12a56 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/IChunkSaver.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/IChunkSaver.java @@ -1,6 +1,7 @@ package com.hypixel.hytale.server.core.universe.world.storage; import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.server.core.universe.world.storage.component.ChunkSavingSystems; import it.unimi.dsi.fastutil.longs.LongSet; import java.io.Closeable; import java.io.IOException; @@ -18,4 +19,10 @@ public interface IChunkSaver extends Closeable { LongSet getIndexes() throws IOException; void flush() throws IOException; + + default void pauseBackgroundSaving(ChunkSavingSystems.Data data) { + } + + default void resumeBackgroundSaving() { + } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/component/ChunkSavingSystems.java b/src/com/hypixel/hytale/server/core/universe/world/storage/component/ChunkSavingSystems.java index e64460e6..848bb153 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/component/ChunkSavingSystems.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/component/ChunkSavingSystems.java @@ -142,7 +142,6 @@ public class ChunkSavingSystems { @Nonnull private final List> chunkSavingFutures = new ObjectArrayList<>(); private float time; - public boolean isSaving = true; @Nonnull private final AtomicInteger savedCount = new AtomicInteger(); @Nonnull @@ -194,6 +193,10 @@ public class ChunkSavingSystems { } } + public void pushSavingFuture(@Nonnull CompletableFuture future) { + this.chunkSavingFutures.add(future); + } + @Nonnull public CompletableFuture waitForSavingChunks() { return CompletableFuture.allOf(this.chunkSavingFutures.toArray(CompletableFuture[]::new)); @@ -210,7 +213,7 @@ public class ChunkSavingSystems { @Override public void tick(float dt, int systemIndex, @Nonnull Store store) { ChunkSavingSystems.Data data = store.getResource(ChunkStore.SAVE_RESOURCE); - if (data.isSaving && store.getExternalData().getWorld().getWorldConfig().canSaveChunks()) { + if (!store.getExternalData().getWorld().isSavingLocked() && store.getExternalData().getWorld().getWorldConfig().canSaveChunks()) { data.chunkSavingFutures.removeIf(CompletableFuture::isDone); if (data.checkTimer(dt)) { store.forEachChunk(ChunkSavingSystems.QUERY, ChunkSavingSystems::tryQueueSync); diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/BackupChunkLoader.java b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/BackupChunkLoader.java new file mode 100644 index 00000000..0507aea0 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/BackupChunkLoader.java @@ -0,0 +1,124 @@ +package com.hypixel.hytale.server.core.universe.world.storage.provider; + +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.IChunkLoader; +import it.unimi.dsi.fastutil.longs.LongSet; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import javax.annotation.Nonnull; + +public class BackupChunkLoader implements IChunkLoader { + private final List loaders = new ArrayList<>(); + private final List fileSystems = new ArrayList<>(); + private final List tempDirs = new ArrayList<>(); + + public BackupChunkLoader(ChunkStore store, List backups) throws IOException { + IChunkStorageProvider baseProvider = store.getWorld().getWorldConfig().getChunkStorageProvider(); + Path worldPath = Universe.get().getWorldsPath(); + + for (Path path : backups) { + Path savePath = store.getWorld().getSavePath(); + String relWorldPath = worldPath.relativize(savePath).toString().replace('\\', '/'); + String expectedChunksPrefix = "worlds/" + relWorldPath + "/chunks/"; + FileSystem fs = FileSystems.newFileSystem(path); + Path worldBackupPath = fs.getPath("worlds", worldPath.relativize(savePath).toString()); + Path loaderPath; + if (Files.exists(worldBackupPath)) { + this.fileSystems.add(fs); + loaderPath = worldBackupPath; + } else { + fs.close(); + Path tempDir = Files.createTempDirectory("hytale-backup-recovery-"); + this.tempDirs.add(tempDir); + Path chunksDir = tempDir.resolve("chunks"); + Files.createDirectory(chunksDir); + + try (ZipFile zipFile = new ZipFile(path.toFile())) { + Enumeration entries = zipFile.entries(); + + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String normalized = entry.getName().replace('\\', '/'); + if (!entry.isDirectory() && normalized.startsWith(expectedChunksPrefix)) { + String fileName = normalized.substring(expectedChunksPrefix.length()); + if (!fileName.contains("/")) { + Path target = chunksDir.resolve(fileName); + + try (InputStream in = zipFile.getInputStream(entry)) { + Files.copy(in, target); + } + } + } + } + } + + loaderPath = tempDir; + } + + IChunkLoader loader = baseProvider.getRecoveryLoader(store.getStore(), loaderPath); + if (loader == null) { + throw new RuntimeException("Recovery of individual chunks from backups not supported by storage type. Please restore instead."); + } + + this.loaders.add(loader); + } + } + + @Nonnull + @Override + public CompletableFuture> loadHolder(int x, int z) { + return this.loadChunkNext(this.loaders.iterator(), x, z); + } + + private CompletableFuture> loadChunkNext(Iterator iterator, int x, int z) { + if (!iterator.hasNext()) { + return CompletableFuture.completedFuture(null); + } else { + IChunkLoader loader = iterator.next(); + return loader.loadHolder(x, z).exceptionallyCompose(t -> this.loadChunkNext(iterator, x, z)); + } + } + + @Nonnull + @Override + public LongSet getIndexes() throws IOException { + return LongSet.of(); + } + + @Override + public void close() throws IOException { + for (IChunkLoader loader : this.loaders) { + loader.close(); + } + + for (FileSystem fs : this.fileSystems) { + fs.close(); + } + + for (Path tempDir : this.tempDirs) { + try (Stream walk = Files.walk(tempDir)) { + walk.sorted(Comparator.reverseOrder()).forEach(p -> { + try { + Files.deleteIfExists(p); + } catch (IOException var2x) { + } + }); + } + } + } +} diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/DefaultChunkStorageProvider.java b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/DefaultChunkStorageProvider.java index 2ce60ef1..140a664c 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/DefaultChunkStorageProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/DefaultChunkStorageProvider.java @@ -6,7 +6,9 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.IChunkLoader; import com.hypixel.hytale.server.core.universe.world.storage.IChunkSaver; import java.io.IOException; +import java.nio.file.Path; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.checkerframework.checker.nullness.compatqual.NonNullDecl; public class DefaultChunkStorageProvider implements IChunkStorageProvider { @@ -46,6 +48,22 @@ public class DefaultChunkStorageProvider implements IChunkStorageProvider)this.provider).getSaver(o, store); } + @Nullable + @Override + public IChunkLoader getRecoveryLoader(@Nonnull Store store, Path backupPath) { + return this.provider.getRecoveryLoader(store, backupPath); + } + + @Override + public void beginRecovery(Path file, Path recoveryPath) throws IOException { + this.provider.beginRecovery(file, recoveryPath); + } + + @Override + public void revertRecovery(Path file, Path recoveryPath) throws IOException { + this.provider.revertRecovery(file, recoveryPath); + } + @Override public boolean isSame(IChunkStorageProvider other) { return other.getClass().equals(this.getClass()) || this.provider.isSame(other); diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IChunkStorageProvider.java b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IChunkStorageProvider.java index c32adab1..e1aa3400 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IChunkStorageProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IChunkStorageProvider.java @@ -7,6 +7,7 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.IChunkLoader; @@ -14,10 +15,12 @@ import com.hypixel.hytale.server.core.universe.world.storage.IChunkSaver; import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongSet; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ForkJoinPool; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public interface IChunkStorageProvider { @Nonnull @@ -38,7 +41,7 @@ public interface IChunkStorageProvider { LongSet chunks = oldLoader.getIndexes(); LongIterator iterator = chunks.iterator(); logger.atInfo().log("Migrating %d chunks", chunks.size()); - HytaleServer.get().reportSingleplayerStatus(String.format("Migrating chunks for %s", world.getName()), 0.0); + HytaleServer.get().reportSingleplayerStatus(Message.translation("client.gameLoadingView.status.migratingChunks").param("name", world.getName()), 0.0); int count = 0; ArrayList> inFlight = new ArrayList<>(); @@ -53,7 +56,8 @@ public interface IChunkStorageProvider { if (++count % 100 == 0) { logger.atInfo().log("Migrated %d/%d chunks", count, chunks.size()); double progress = MathUtil.round((double)count / chunks.size(), 2) * 100.0; - HytaleServer.get().reportSingleplayerStatus(String.format("Migrating chunks for %s", world.getName()), progress); + HytaleServer.get() + .reportSingleplayerStatus(Message.translation("client.gameLoadingView.status.migratingChunks").param("name", world.getName()), progress); } inFlight.removeIf(CompletableFuture::isDone); @@ -81,6 +85,19 @@ public interface IChunkStorageProvider { @Nonnull IChunkSaver getSaver(@Nonnull Data var1, @Nonnull Store var2) throws IOException; + @Nullable + default IChunkLoader getRecoveryLoader(@Nonnull Store store, Path backupPath) { + return null; + } + + default void beginRecovery(Path file, Path recoveryPath) throws IOException { + throw new UnsupportedOperationException(); + } + + default void revertRecovery(Path file, Path recoveryPath) throws IOException { + throw new UnsupportedOperationException(); + } + default boolean isSame(IChunkStorageProvider other) { return other.getClass().equals(this.getClass()); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IndexedStorageChunkStorageProvider.java b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IndexedStorageChunkStorageProvider.java index 2c6e1bc7..3743633a 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IndexedStorageChunkStorageProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/IndexedStorageChunkStorageProvider.java @@ -17,6 +17,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.BufferChunkSaver; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.IChunkLoader; import com.hypixel.hytale.server.core.universe.world.storage.IChunkSaver; +import com.hypixel.hytale.server.core.util.io.FileUtil; import com.hypixel.hytale.sneakythrow.SneakyThrow; import com.hypixel.hytale.storage.IndexedStorageFile; import it.unimi.dsi.fastutil.ints.IntList; @@ -70,7 +71,7 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider @Nonnull public IChunkLoader getLoader(@Nonnull IndexedStorageChunkStorageProvider.IndexedStorageCache cache, @Nonnull Store store) { - return new IndexedStorageChunkStorageProvider.IndexedStorageChunkLoader(store, cache, this.flushOnWrite); + return new IndexedStorageChunkStorageProvider.IndexedStorageChunkLoader(store, cache, this.flushOnWrite, false); } @Nonnull @@ -78,6 +79,29 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider return new IndexedStorageChunkStorageProvider.IndexedStorageChunkSaver(store, cache, this.flushOnWrite); } + @Override + public void beginRecovery(Path file, Path recoveryPath) throws IOException { + FileUtil.atomicMove(file.resolve("chunks"), recoveryPath.resolve("chunks")); + } + + @Override + public void revertRecovery(Path file, Path recoveryPath) throws IOException { + Path chunks = file.resolve("chunks"); + if (Files.exists(chunks)) { + FileUtil.deleteDirectory(chunks); + } + + FileUtil.atomicMove(recoveryPath.resolve("chunks"), chunks); + } + + @Nullable + @Override + public IChunkLoader getRecoveryLoader(@Nonnull Store store, Path backupPath) { + IndexedStorageChunkStorageProvider.IndexedStorageCache cache = new IndexedStorageChunkStorageProvider.IndexedStorageCache(); + cache.path = backupPath.resolve("chunks"); + return new IndexedStorageChunkStorageProvider.IndexedStorageChunkLoader(store, cache, false, true); + } + @Nonnull @Override public String toString() { @@ -199,7 +223,7 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider LongOpenHashSet chunkIndexes = new LongOpenHashSet(); try (Stream stream = Files.list(this.path)) { - stream.forEach(path -> { + stream.forEach(SneakyThrow.sneakyConsumer(path -> { if (!Files.isDirectory(path)) { long regionIndex; try { @@ -210,7 +234,7 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider int regionX = ChunkUtil.xOfChunkIndex(regionIndex); int regionZ = ChunkUtil.zOfChunkIndex(regionIndex); - IndexedStorageFile regionFile = this.getOrTryOpen(regionX, regionZ, true); + IndexedStorageFile regionFile = this.getOrTryOpen(regionX, regionZ, false); if (regionFile != null) { IntList blobIndexes = regionFile.keys(); IntListIterator iterator = blobIndexes.iterator(); @@ -225,7 +249,7 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider } } } - }); + })); } return chunkIndexes; @@ -292,17 +316,22 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider @Nonnull private final IndexedStorageChunkStorageProvider.IndexedStorageCache cache; private final boolean flushOnWrite; + private final boolean ownsCache; public IndexedStorageChunkLoader( - @Nonnull Store store, @Nonnull IndexedStorageChunkStorageProvider.IndexedStorageCache cache, boolean flushOnWrite + @Nonnull Store store, @Nonnull IndexedStorageChunkStorageProvider.IndexedStorageCache cache, boolean flushOnWrite, boolean ownsCache ) { super(store); this.cache = cache; this.flushOnWrite = flushOnWrite; + this.ownsCache = ownsCache; } @Override public void close() throws IOException { + if (this.ownsCache) { + this.cache.close(); + } } @Nonnull diff --git a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/RocksDbChunkStorageProvider.java b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/RocksDbChunkStorageProvider.java index 8adfdb73..41da743a 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/storage/provider/RocksDbChunkStorageProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/storage/provider/RocksDbChunkStorageProvider.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.BufferChunkSaver; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.IChunkLoader; import com.hypixel.hytale.server.core.universe.world.storage.IChunkSaver; +import com.hypixel.hytale.server.core.universe.world.storage.component.ChunkSavingSystems; import com.hypixel.hytale.sneakythrow.SneakyThrow; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; @@ -225,5 +226,19 @@ public class RocksDbChunkStorageProvider implements IChunkStorageProvider this.db.db.pauseBackgroundWork()))); + } + + @Override + public void resumeBackgroundSaving() { + try { + this.db.db.continueBackgroundWork(); + } catch (RocksDBException var2) { + throw SneakyThrow.sneakyThrow(var2); + } + } } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/system/WorldPregenerateSystem.java b/src/com/hypixel/hytale/server/core/universe/world/system/WorldPregenerateSystem.java index 9e7f782d..daa8db40 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/system/WorldPregenerateSystem.java +++ b/src/com/hypixel/hytale/server/core/universe/world/system/WorldPregenerateSystem.java @@ -12,7 +12,7 @@ import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -40,7 +40,7 @@ public class WorldPregenerateSystem extends StoreSystem { int lowZ = MathUtil.floor(region.min.y); int highX = MathUtil.floor(region.max.x); int highZ = MathUtil.floor(region.max.y); - List>> futures = new ObjectArrayList<>(); + List>> futures = new ReferenceArrayList<>(); for (int x = lowX; x <= highX; x += 32) { for (int z = lowZ; z <= highZ; z += 32) { diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedBlockStateChunk.java b/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedBlockStateChunk.java index 73e50f34..d0f61f0b 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedBlockStateChunk.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedBlockStateChunk.java @@ -2,12 +2,11 @@ package com.hypixel.hytale.server.core.universe.world.worldgen; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -23,17 +22,12 @@ public class GeneratedBlockStateChunk { if (state == null) { this.mapping.remove(index); } else { - BlockState blockState = BlockState.getBlockState(state); - if (blockState != null) { - blockState.setPosition(new Vector3i(x, y, z)); - } - this.mapping.put(index, state); } } @Nonnull public BlockComponentChunk toBlockComponentChunk() { - return new BlockComponentChunk(this.mapping, new Int2ObjectOpenHashMap<>()); + return new BlockComponentChunk(this.mapping, new Int2ReferenceOpenHashMap<>()); } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedChunkSection.java b/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedChunkSection.java index ba4244ad..2d1538b8 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedChunkSection.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedChunkSection.java @@ -1,21 +1,28 @@ package com.hypixel.hytale.server.core.universe.world.worldgen; import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.util.NumberUtil; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; +import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.AbstractSectionPalette; import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.EmptySectionPalette; -import com.hypixel.hytale.server.core.universe.world.chunk.section.palette.ISectionPalette; import io.netty.buffer.ByteBuf; -import it.unimi.dsi.fastutil.ints.IntArrays; +import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; import java.util.Arrays; import javax.annotation.Nonnull; public class GeneratedChunkSection { @Nonnull - private final int[] data = new int[32768]; + private final int[] data; @Nonnull - private final int[] temp = new int[32768]; - private ISectionPalette fillers = EmptySectionPalette.INSTANCE; - private ISectionPalette rotations = EmptySectionPalette.INSTANCE; + private final transient Int2ShortOpenHashMap tempIdCounts = new Int2ShortOpenHashMap(); + private AbstractSectionPalette fillers; + private AbstractSectionPalette rotations; + + public GeneratedChunkSection() { + this.data = new int[32768]; + this.fillers = EmptySectionPalette.INSTANCE; + this.rotations = EmptySectionPalette.INSTANCE; + } public int getRotationIndex(int x, int y, int z) { return this.getRotationIndex(ChunkUtil.indexBlock(x, y, z)); @@ -43,19 +50,19 @@ public class GeneratedChunkSection { public void setBlock(int index, int block, int rotation, int filler) { this.data[index] = block; - ISectionPalette.SetResult result = this.fillers.set(index, filler); - if (result == ISectionPalette.SetResult.REQUIRES_PROMOTE) { + AbstractSectionPalette.SetResult result = this.fillers.set(index, filler); + if (result == AbstractSectionPalette.SetResult.REQUIRES_PROMOTE) { this.fillers = this.fillers.promote(); this.fillers.set(index, filler); - } else if (result == ISectionPalette.SetResult.ADDED_OR_REMOVED && this.fillers.shouldDemote()) { + } else if (result == AbstractSectionPalette.SetResult.ADDED_OR_REMOVED && this.fillers.shouldDemote()) { this.fillers = this.fillers.demote(); } result = this.rotations.set(index, rotation); - if (result == ISectionPalette.SetResult.REQUIRES_PROMOTE) { + if (result == AbstractSectionPalette.SetResult.REQUIRES_PROMOTE) { this.rotations = this.rotations.promote(); this.rotations.set(index, rotation); - } else if (result == ISectionPalette.SetResult.ADDED_OR_REMOVED && this.rotations.shouldDemote()) { + } else if (result == AbstractSectionPalette.SetResult.ADDED_OR_REMOVED && this.rotations.shouldDemote()) { this.rotations = this.rotations.demote(); } } @@ -66,6 +73,8 @@ public class GeneratedChunkSection { public void reset() { Arrays.fill(this.data, 0); + this.fillers = EmptySectionPalette.INSTANCE; + this.rotations = EmptySectionPalette.INSTANCE; } public boolean isSolidAir() { @@ -80,17 +89,22 @@ public class GeneratedChunkSection { @Nonnull public BlockSection toChunkSection() { - System.arraycopy(this.data, 0, this.temp, 0, 32768); - IntArrays.unstableSort(this.temp); - int count = 1; + this.tempIdCounts.clear(); + int index = 0; - for (int i = 1; i < 32768; i++) { - if (this.temp[i] != this.temp[i - 1]) { - this.temp[count++] = this.temp[i]; - } + while (index < this.data.length) { + int id = this.data[index]; + int start = index; + + do { + index++; + } while (index < this.data.length && this.data[index] == id); + + short count = (short)(index - start); + this.tempIdCounts.mergeShort(id, count, NumberUtil::sum); } - return new BlockSection(ISectionPalette.from(this.data, this.temp, count), this.fillers, this.rotations); + return new BlockSection(AbstractSectionPalette.from(this.data, this.tempIdCounts), this.fillers, this.rotations); } public void serialize(@Nonnull ByteBuf buf) { diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedEntityChunk.java b/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedEntityChunk.java index 4bf853ba..bcd7e86e 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedEntityChunk.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldgen/GeneratedEntityChunk.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.core.universe.world.worldgen; import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -14,6 +13,7 @@ import java.util.List; import java.util.function.Consumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class GeneratedEntityChunk { private final List entities; @@ -52,7 +52,7 @@ public class GeneratedEntityChunk { assert transformComponent != null; - entry.rotation().rotate(transformComponent.getPosition().subtract(0.5, 0.0, 0.5)); + entry.rotation().rotate(transformComponent.getPosition().sub(0.5, 0.0, 0.5)); transformComponent.getPosition().add(0.5, 0.0, 0.5); HeadRotation headRotationComponent = entityHolder.getComponent(HeadRotation.getComponentType()); if (headRotationComponent != null) { @@ -60,7 +60,8 @@ public class GeneratedEntityChunk { } transformComponent.getRotation().addYaw(-entry.rotation().getYaw()); - transformComponent.getPosition().add(entry.offset()); + Vector3i offset = entry.offset(); + transformComponent.getPosition().add(offset.x, offset.y, offset.z); entityHolder.putComponent(FromWorldGen.getComponentType(), fromWorldGen); entityChunk.storeEntityHolder(entityHolder); } diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/WorldMapManager.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/WorldMapManager.java index 80c8f533..dfca2563 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/WorldMapManager.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/WorldMapManager.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.lookup.CodecMapCodec; import com.hypixel.hytale.common.util.CompletableFutureUtil; -import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; @@ -17,7 +16,10 @@ import com.hypixel.hytale.protocol.packets.player.RemoveMapMarker; import com.hypixel.hytale.protocol.packets.worldmap.CreateUserMarker; import com.hypixel.hytale.protocol.packets.worldmap.MapImage; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; +import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.config.ServerWorldMapConfig; +import com.hypixel.hytale.server.core.config.WorldWorldMapConfig; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerConfigData; import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerWorldData; @@ -104,7 +106,7 @@ public class WorldMapManager extends TickingThread { this.logger.at(Level.INFO).log("Initializing world map generator: %s", generator.toString()); this.generatorLoaded.complete(null); this.generatorLoaded = new CompletableFuture<>(); - this.worldMapSettings = generator.getWorldMapSettings(); + this.applyWorldMapOverrides(generator); this.images.clear(); this.generating.clear(); @@ -126,6 +128,26 @@ public class WorldMapManager extends TickingThread { } } + private void applyWorldMapOverrides(@Nonnull IWorldMap generator) { + WorldMapSettings generatorSettings = generator.getWorldMapSettings(); + int effectiveMin = generatorSettings.getViewRadiusMin(); + int effectiveMax = generatorSettings.getViewRadiusMax(); + WorldWorldMapConfig perWorldConfig = this.world.getWorldConfig().getWorldMapConfig(); + if (perWorldConfig != null) { + effectiveMin = perWorldConfig.getViewRadiusMin(); + effectiveMax = perWorldConfig.getViewRadiusMax(); + } + + ServerWorldMapConfig serverConfig = HytaleServer.get().getConfig().getWorldMapConfig(); + effectiveMin = Math.max(effectiveMin, serverConfig.getViewRadiusMin()); + effectiveMax = Math.min(effectiveMax, serverConfig.getViewRadiusMax()); + if (effectiveMin > effectiveMax) { + effectiveMin = effectiveMax; + } + + this.worldMapSettings = generatorSettings.withViewRadiusLimits(effectiveMin, effectiveMax); + } + @Override protected boolean isIdle() { return this.world.getPlayerCount() == 0; @@ -463,17 +485,16 @@ public class WorldMapManager extends TickingThread { } private void removeMarkerFromOfflinePlayer() { - Universe.get().getPlayerStorage().load(this.player).thenApply(holder -> { - Player player = holder.getComponent(Player.getComponentType()); - PlayerConfigData data = player.getPlayerConfigData(); + Universe.get().getPlayerStorage().update(this.player, holder -> { + Player playerComponent = holder.getComponent(Player.getComponentType()); + PlayerConfigData data = playerComponent.getPlayerConfigData(); String world = this.world; if (world == null) { world = data.getWorld(); } removeMarkerFromData(data, world, this.markerId); - return holder; - }).thenCompose(holder -> Universe.get().getPlayerStorage().save(this.player, (Holder)holder)); + }); } @Nullable diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/WorldMapSettings.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/WorldMapSettings.java index 444f046f..9263baf4 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/WorldMapSettings.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/WorldMapSettings.java @@ -39,6 +39,19 @@ public class WorldMapSettings { return this.imageScale; } + public int getViewRadiusMin() { + return this.viewRadiusMin; + } + + public int getViewRadiusMax() { + return this.viewRadiusMax; + } + + @Nonnull + public WorldMapSettings withViewRadiusLimits(int min, int max) { + return new WorldMapSettings(this.worldMapArea, this.imageScale, this.viewRadiusMultiplier, min, max, this.settingsPacket); + } + @Nonnull public UpdateWorldMapSettings getSettingsPacket() { return this.settingsPacket; diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MapMarkerBuilder.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MapMarkerBuilder.java index 78582f6f..e5a366fb 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MapMarkerBuilder.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MapMarkerBuilder.java @@ -14,7 +14,6 @@ public class MapMarkerBuilder { private final String image; private final Transform transform; private Message name; - private String customName; private List contextMenuItems; private List mapMarkerComponents; @@ -30,7 +29,7 @@ public class MapMarkerBuilder { } public MapMarkerBuilder withCustomName(String customName) { - this.customName = customName; + this.name = Message.raw(customName); return this; } @@ -56,7 +55,6 @@ public class MapMarkerBuilder { return new MapMarker( this.id, this.name == null ? null : this.name.getFormattedMessage(), - this.customName, this.image, PositionUtil.toTransformPacket(this.transform), this.contextMenuItems == null ? null : this.contextMenuItems.toArray(ContextMenuItem[]::new), diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MapMarkerTracker.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MapMarkerTracker.java index 01cca55c..43c312a8 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MapMarkerTracker.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MapMarkerTracker.java @@ -1,6 +1,5 @@ package com.hypixel.hytale.server.core.universe.world.worldmap.markers; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; import com.hypixel.hytale.protocol.packets.worldmap.UpdateWorldMap; @@ -18,6 +17,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class MapMarkerTracker { private final WorldMapTracker worldMapTracker; @@ -111,8 +111,6 @@ public class MapMarkerTracker { return true; } else if (!Objects.equals(oldMarker.name, newMarker.name)) { return true; - } else if (!Objects.equals(oldMarker.customName, newMarker.customName)) { - return true; } else { double yawDistance = Math.abs(oldMarker.transform.orientation.yaw - newMarker.transform.orientation.yaw); if (!(yawDistance > 0.05) && (!this.isSendingSmallMovements() || !(yawDistance > 0.001))) { diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MarkersCollector.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MarkersCollector.java index b7e00006..d6478646 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MarkersCollector.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/MarkersCollector.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.server.core.universe.world.worldmap.markers; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; import com.hypixel.hytale.server.core.universe.PlayerRef; import java.util.function.Predicate; import javax.annotation.Nullable; +import org.joml.Vector3d; public interface MarkersCollector { void add(MapMarker var1); diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/OtherPlayersMarkerProvider.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/OtherPlayersMarkerProvider.java index af320df9..30126a6a 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/OtherPlayersMarkerProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/OtherPlayersMarkerProvider.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.core.universe.world.worldmap.markers.providers; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.packets.worldmap.HeightDeltaIconComponent; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; import com.hypixel.hytale.protocol.packets.worldmap.PlayerMarkerComponent; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.core.universe.world.worldmap.markers.MapMarkerB import com.hypixel.hytale.server.core.universe.world.worldmap.markers.MarkersCollector; import java.util.function.Predicate; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class OtherPlayersMarkerProvider implements WorldMapManager.MarkerProvider { public static final OtherPlayersMarkerProvider INSTANCE = new OtherPlayersMarkerProvider(); @@ -33,7 +33,7 @@ public class OtherPlayersMarkerProvider implements WorldMapManager.MarkerProvide if (!otherPlayer.getUuid().equals(player.getUuid())) { Transform otherPlayerTransform = otherPlayer.getTransform(); Vector3d otherPos = otherPlayerTransform.getPosition(); - if (collector.isInViewDistance(otherPos) && (playerMapFilter == null || !playerMapFilter.test(otherPlayer))) { + if (playerMapFilter == null || !playerMapFilter.test(otherPlayer)) { PlayerMarkerComponent playerMarker = new PlayerMarkerComponent(otherPlayer.getUuid()); HeightDeltaIconComponent heightDeltaComponent = new HeightDeltaIconComponent( playersMapConfig.getIconSwapHeightDelta(), diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/PersonalMarkersProvider.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/PersonalMarkersProvider.java index 4384abc9..89197783 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/PersonalMarkersProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/PersonalMarkersProvider.java @@ -19,7 +19,7 @@ public class PersonalMarkersProvider implements WorldMapManager.MarkerProvider { PlayerWorldData perWorldData = player.getPlayerConfigData().getPerWorldData(world.getName()); for (UserMapMarker userMapMarker : perWorldData.getUserMapMarkers()) { - collector.add(userMapMarker.toProtocolMarker()); + collector.addIgnoreViewDistance(userMapMarker.toProtocolMarker()); } } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/RespawnMarkerProvider.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/RespawnMarkerProvider.java index f7e4ecc1..ecc8dcc5 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/RespawnMarkerProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/RespawnMarkerProvider.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.core.universe.world.worldmap.markers.providers; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; import com.hypixel.hytale.server.core.asset.type.gameplay.WorldMapConfig; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -11,6 +10,7 @@ import com.hypixel.hytale.server.core.universe.world.worldmap.WorldMapManager; import com.hypixel.hytale.server.core.universe.world.worldmap.markers.MapMarkerBuilder; import com.hypixel.hytale.server.core.universe.world.worldmap.markers.MarkersCollector; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class RespawnMarkerProvider implements WorldMapManager.MarkerProvider { public static final RespawnMarkerProvider INSTANCE = new RespawnMarkerProvider(); diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/SharedMarkersProvider.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/SharedMarkersProvider.java index 59387d59..62617104 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/SharedMarkersProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/SharedMarkersProvider.java @@ -19,7 +19,7 @@ public class SharedMarkersProvider implements WorldMapManager.MarkerProvider { WorldMarkersResource worldMarkersResource = world.getChunkStore().getStore().getResource(WorldMarkersResource.getResourceType()); for (UserMapMarker userMapMarker : worldMarkersResource.getUserMapMarkers()) { - collector.add(userMapMarker.toProtocolMarker()); + collector.addIgnoreViewDistance(userMapMarker.toProtocolMarker()); } } } diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/SpawnMarkerProvider.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/SpawnMarkerProvider.java index 4b58a693..6240aa85 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/SpawnMarkerProvider.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/providers/SpawnMarkerProvider.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.core.universe.world.worldmap.markers.providers; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.packets.worldmap.MapMarker; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.gameplay.WorldMapConfig; @@ -11,6 +10,7 @@ import com.hypixel.hytale.server.core.universe.world.worldmap.WorldMapManager; import com.hypixel.hytale.server.core.universe.world.worldmap.markers.MapMarkerBuilder; import com.hypixel.hytale.server.core.universe.world.worldmap.markers.MarkersCollector; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpawnMarkerProvider implements WorldMapManager.MarkerProvider { public static final SpawnMarkerProvider INSTANCE = new SpawnMarkerProvider(); diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/user/UserMarkerValidator.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/user/UserMarkerValidator.java index c08adc8a..28d20ee0 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/user/UserMarkerValidator.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/markers/user/UserMarkerValidator.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.core.universe.world.worldmap.markers.user; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.packets.worldmap.CreateUserMarker; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.gameplay.worldmap.UserMapMarkerConfig; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.universe.world.worldmap.markers.worldstore.WorldMarkersResource; import java.util.Collection; import java.util.UUID; +import org.joml.Vector3d; public final class UserMarkerValidator { private static final int NAME_LENGTH_LIMIT = 24; @@ -71,12 +71,12 @@ public final class UserMarkerValidator { Player player = store.getComponent(ref, Player.getComponentType()); Transform transform = store.getComponent(ref, TransformComponent.getComponentType()).getTransform(); Vector3d playerPosition = transform.getPosition(); - double distanceToMarker = playerPosition.distanceSquaredTo(markerX, playerPosition.y, markerZ); + double distanceToMarker = playerPosition.distanceSquared(markerX, playerPosition.y, markerZ); return distanceToMarker > getMaxRemovalDistanceSquared(player); } private static double getMaxRemovalDistanceSquared(Player player) { - double maxDistance = player.getViewRadius() * 1.5 * 32.0; + int maxDistance = player.getViewRadius() * 2 * 32; return maxDistance * maxDistance; } diff --git a/src/com/hypixel/hytale/server/core/universe/world/worldmap/provider/chunk/ImageBuilder.java b/src/com/hypixel/hytale/server/core/universe/world/worldmap/provider/chunk/ImageBuilder.java index c00c60f4..243128a3 100644 --- a/src/com/hypixel/hytale/server/core/universe/world/worldmap/provider/chunk/ImageBuilder.java +++ b/src/com/hypixel/hytale/server/core/universe/world/worldmap/provider/chunk/ImageBuilder.java @@ -10,18 +10,26 @@ import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.palette.BitFieldArr; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import java.util.Objects; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import javax.annotation.Nullable; -class ImageBuilder { +public class ImageBuilder { + private static volatile boolean quantizationEnabled = true; private final long index; private final World world; + private final int imageWidth; + private final int imageHeight; @Nonnull - private final MapImage image; + private final int[] rawPixels; + @Nullable + private MapImage image; private final int sampleWidth; private final int sampleHeight; private final int blockStepX; @@ -44,15 +52,30 @@ class ImageBuilder { @Nullable private WorldChunk worldChunk; private FluidSection[] fluidSections; + private static final int QUANTIZE_STEP = 8; + private static final int QUANTIZE_HALF = 4; + private static final int[][] BAYER_MATRIX = new int[][]{{0, 8, 2, 10}, {12, 4, 14, 6}, {3, 11, 1, 9}, {15, 7, 13, 5}}; + private static final int GRADIENT_THRESHOLD = 2; + + public static boolean isQuantizationEnabled() { + return quantizationEnabled; + } + + public static boolean toggleQuantization() { + quantizationEnabled = !quantizationEnabled; + return quantizationEnabled; + } public ImageBuilder(long index, int imageWidth, int imageHeight, World world) { this.index = index; this.world = world; - this.image = new MapImage(imageWidth, imageHeight, new int[imageWidth * imageHeight]); - this.sampleWidth = Math.min(32, this.image.width); - this.sampleHeight = Math.min(32, this.image.height); - this.blockStepX = Math.max(1, 32 / this.image.width); - this.blockStepZ = Math.max(1, 32 / this.image.height); + this.imageWidth = imageWidth; + this.imageHeight = imageHeight; + this.rawPixels = new int[imageWidth * imageHeight]; + this.sampleWidth = Math.min(32, imageWidth); + this.sampleHeight = Math.min(32, imageHeight); + this.blockStepX = Math.max(1, 32 / imageWidth); + this.blockStepZ = Math.max(1, 32 / imageHeight); this.heightSamples = new short[this.sampleWidth * this.sampleHeight]; this.tintSamples = new int[this.sampleWidth * this.sampleHeight]; this.blockSamples = new int[this.sampleWidth * this.sampleHeight]; @@ -270,17 +293,17 @@ class ImageBuilder { } } - float imageToSampleRatioWidth = (float)this.sampleWidth / this.image.width; - float imageToSampleRatioHeight = (float)this.sampleHeight / this.image.height; - int blockPixelWidth = Math.max(1, this.image.width / this.sampleWidth); - int blockPixelHeight = Math.max(1, this.image.height / this.sampleHeight); + float imageToSampleRatioWidth = (float)this.sampleWidth / this.imageWidth; + float imageToSampleRatioHeight = (float)this.sampleHeight / this.imageHeight; + int blockPixelWidth = Math.max(1, this.imageWidth / this.sampleWidth); + int blockPixelHeight = Math.max(1, this.imageHeight / this.sampleHeight); for (int iz = 0; iz < this.sampleHeight; iz++) { System.arraycopy(this.heightSamples, iz * this.sampleWidth, this.neighborHeightSamples, (iz + 1) * (this.sampleWidth + 2) + 1, this.sampleWidth); } - for (int ix = 0; ix < this.image.width; ix++) { - for (int iz = 0; iz < this.image.height; iz++) { + for (int ix = 0; ix < this.imageWidth; ix++) { + for (int iz = 0; iz < this.imageHeight; iz++) { int sampleX = Math.min((int)(ix * imageToSampleRatioWidth), this.sampleWidth - 1); int sampleZ = Math.min((int)(iz * imageToSampleRatioHeight), this.sampleHeight - 1); int sampleIndex = sampleZ * this.sampleWidth + sampleX; @@ -320,11 +343,129 @@ class ImageBuilder { } } + this.image = this.encodeToPalette(); return this; } + private static int quantizeChannel(int value) { + return Math.min(255, (value + 4) / 8 * 8); + } + + private static boolean isNearBoundary(int value) { + int distanceFromBoundary = (value + 4) % 8; + return distanceFromBoundary <= 2 || distanceFromBoundary >= 6; + } + + private static int quantizeChannelWithDither(int value, int ditherOffset) { + int adjusted = value + ditherOffset; + adjusted = Math.max(0, Math.min(255, adjusted)); + return Math.min(255, (adjusted + 4) / 8 * 8); + } + + private static int quantizeColor(int argb) { + int r = quantizeChannel(argb >> 24 & 0xFF); + int g = quantizeChannel(argb >> 16 & 0xFF); + int b = quantizeChannel(argb >> 8 & 0xFF); + int a = argb & 0xFF; + return r << 24 | g << 16 | b << 8 | a; + } + + private static boolean colorNearBoundary(int argb) { + int r = argb >> 24 & 0xFF; + int g = argb >> 16 & 0xFF; + int b = argb >> 8 & 0xFF; + return isNearBoundary(r) || isNearBoundary(g) || isNearBoundary(b); + } + + private static int quantizeColorWithDither(int argb, int x, int y) { + int bayerValue = BAYER_MATRIX[y & 3][x & 3]; + int ditherOffset = (bayerValue - 8) * 8 / 16; + int r = quantizeChannelWithDither(argb >> 24 & 0xFF, ditherOffset); + int g = quantizeChannelWithDither(argb >> 16 & 0xFF, ditherOffset); + int b = quantizeChannelWithDither(argb >> 8 & 0xFF, ditherOffset); + int a = argb & 0xFF; + return r << 24 | g << 16 | b << 8 | a; + } + + private boolean isInTransitionZone(int index) { + int centerPixel = this.rawPixels[index]; + int centerQuantized = quantizeColor(centerPixel); + int x = index % this.imageWidth; + int y = index / this.imageWidth; + + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) { + if (dx != 0 || dy != 0) { + int nx = x + dx; + int ny = y + dy; + if (nx >= 0 && nx < this.imageWidth && ny >= 0 && ny < this.imageHeight) { + int neighborPixel = this.rawPixels[ny * this.imageWidth + nx]; + int neighborQuantized = quantizeColor(neighborPixel); + if (neighborQuantized != centerQuantized) { + return true; + } + } + } + } + } + + return false; + } + + @Nonnull + private MapImage encodeToPalette() { + int pixelCount = this.rawPixels.length; + int[] processedPixels = new int[pixelCount]; + IntOpenHashSet uniqueColors = new IntOpenHashSet(); + + for (int i = 0; i < pixelCount; i++) { + int pixel; + if (quantizationEnabled) { + if (this.isInTransitionZone(i)) { + int x = i % this.imageWidth; + int y = i / this.imageWidth; + pixel = quantizeColorWithDither(this.rawPixels[i], x, y); + } else { + pixel = quantizeColor(this.rawPixels[i]); + } + } else { + pixel = this.rawPixels[i]; + } + + processedPixels[i] = pixel; + uniqueColors.add(pixel); + } + + int[] palette = uniqueColors.toIntArray(); + int bitsPerIndex = calculateBitsRequired(palette.length); + Int2IntOpenHashMap colorToIndex = new Int2IntOpenHashMap(palette.length); + + for (int i = 0; i < palette.length; i++) { + colorToIndex.put(palette[i], i); + } + + BitFieldArr indices = new BitFieldArr(bitsPerIndex, pixelCount); + + for (int i = 0; i < pixelCount; i++) { + indices.set(i, colorToIndex.get(processedPixels[i])); + } + + byte[] packedIndices = indices.get(); + return new MapImage(this.imageWidth, this.imageHeight, palette, (byte)bitsPerIndex, packedIndices); + } + + private static int calculateBitsRequired(int colorCount) { + if (colorCount <= 16) { + return 4; + } else if (colorCount <= 256) { + return 8; + } else { + return colorCount <= 4096 ? 12 : 16; + } + } + private void packImageData(int ix, int iz) { - this.image.data[iz * this.image.width + ix] = this.outColor.pack(); + this.rawPixels[iz * this.imageWidth + ix] = this.outColor.pack(); } private static float shadeFromHeights( diff --git a/src/com/hypixel/hytale/server/core/update/command/UpdateCommand.java b/src/com/hypixel/hytale/server/core/update/command/UpdateCommand.java index f86d1472..db616c16 100644 --- a/src/com/hypixel/hytale/server/core/update/command/UpdateCommand.java +++ b/src/com/hypixel/hytale/server/core/update/command/UpdateCommand.java @@ -21,6 +21,7 @@ public class UpdateCommand extends AbstractCommandCollection { this.addSubCommand(new UpdateCancelCommand()); this.addSubCommand(new UpdateStatusCommand()); this.addSubCommand(new UpdatePatchlineCommand()); + this.addSubCommand(new UpdateSetupCommand()); } @Nullable diff --git a/src/com/hypixel/hytale/server/core/update/command/UpdateDownloadCommand.java b/src/com/hypixel/hytale/server/core/update/command/UpdateDownloadCommand.java index 3dacc2ce..dff76e15 100644 --- a/src/com/hypixel/hytale/server/core/update/command/UpdateDownloadCommand.java +++ b/src/com/hypixel/hytale/server/core/update/command/UpdateDownloadCommand.java @@ -35,80 +35,86 @@ public class UpdateDownloadCommand extends AbstractAsyncCommand { if (updateModule != null && updateModule.isDownloadInProgress()) { context.sendMessage(MSG_DOWNLOAD_IN_PROGRESS); return CompletableFuture.completedFuture(null); - } else if (!UpdateService.isValidUpdateLayout() && !this.forceFlag.get(context)) { - context.sendMessage(MSG_INVALID_LAYOUT); - return CompletableFuture.completedFuture(null); } else { - ServerAuthManager authManager = ServerAuthManager.getInstance(); - if (!authManager.hasSessionToken()) { - context.sendMessage(MSG_NOT_AUTHENTICATED); + boolean force = this.forceFlag.get(context); + if (!UpdateService.isValidUpdateLayout() && !force) { + context.sendMessage(MSG_INVALID_LAYOUT); return CompletableFuture.completedFuture(null); } else { - UpdateService updateService = new UpdateService(); - return updateService.checkForUpdate(UpdateService.getEffectivePatchline()) - .thenCompose( - manifest -> { - if (manifest == null) { - context.sendMessage(MSG_CHECK_FAILED); - return CompletableFuture.completedFuture(null); - } else { - if (updateModule != null) { - updateModule.setLatestKnownVersion(manifest); - } - - String currentVersion = ManifestUtil.getImplementationVersion(); - if (currentVersion != null && currentVersion.equals(manifest.version)) { - context.sendMessage(MSG_NO_UPDATE); - return CompletableFuture.completedFuture(null); - } else if (updateModule != null && !updateModule.tryAcquireDownloadLock()) { - context.sendMessage(MSG_DOWNLOAD_IN_PROGRESS); + ServerAuthManager authManager = ServerAuthManager.getInstance(); + if (!authManager.hasSessionToken()) { + context.sendMessage(MSG_NOT_AUTHENTICATED); + return CompletableFuture.completedFuture(null); + } else { + UpdateService updateService = new UpdateService(); + return updateService.checkForUpdate(UpdateService.getEffectivePatchline()) + .thenCompose( + manifest -> { + if (manifest == null) { + context.sendMessage(MSG_CHECK_FAILED); return CompletableFuture.completedFuture(null); } else { - context.sendMessage(Message.translation("server.commands.update.downloading").param("version", manifest.version)); - AtomicInteger lastPercent = new AtomicInteger(-1); - UpdateService.DownloadTask downloadTask = updateService.downloadUpdate( - manifest, - UpdateService.getStagingDir(), - (percent, downloaded, total) -> { - if (updateModule != null) { - updateModule.updateDownloadProgress(downloaded, total); - } - - int rounded = percent / 10 * 10; - if (rounded > lastPercent.get()) { - lastPercent.set(rounded); - context.sendMessage( - Message.translation("server.commands.update.download_progress") - .param("percent", String.valueOf(percent)) - .param("downloaded", FormatUtil.bytesToString(downloaded)) - .param("total", FormatUtil.bytesToString(total)) - ); - } - } - ); - CompletableFuture downloadFuture = downloadTask.future().whenComplete((success, error) -> { - if (updateModule != null) { - updateModule.releaseDownloadLock(); - } - - if (error instanceof CancellationException) { - UpdateService.deleteStagedUpdate(); - } else if (error == null && Boolean.TRUE.equals(success)) { - context.sendMessage(MSG_DOWNLOAD_COMPLETE); - } else { - context.sendMessage(MSG_DOWNLOAD_FAILED); - UpdateService.deleteStagedUpdate(); - } - }); if (updateModule != null) { - updateModule.setActiveDownload(downloadFuture, downloadTask.thread()); + updateModule.setLatestKnownVersion(manifest); } - return downloadFuture.thenApply(v -> null); + String currentVersion = ManifestUtil.getImplementationVersion(); + if (!force && currentVersion != null && currentVersion.equals(manifest.version)) { + context.sendMessage(MSG_NO_UPDATE); + return CompletableFuture.completedFuture(null); + } else if (updateModule != null && !updateModule.tryAcquireDownloadLock()) { + context.sendMessage(MSG_DOWNLOAD_IN_PROGRESS); + return CompletableFuture.completedFuture(null); + } else { + context.sendMessage(Message.translation("server.commands.update.downloading").param("version", manifest.version)); + AtomicInteger lastPercent = new AtomicInteger(-1); + UpdateService.DownloadTask downloadTask = updateService.downloadUpdate( + manifest, + UpdateService.getStagingDir(), + (percent, downloaded, total) -> { + if (updateModule != null) { + updateModule.updateDownloadProgress(downloaded, total); + } + + int rounded = percent / 10 * 10; + if (rounded > lastPercent.get()) { + lastPercent.set(rounded); + context.sendMessage( + Message.translation("server.commands.update.download_progress") + .param("percent", String.valueOf(percent)) + .param("downloaded", FormatUtil.bytesToString(downloaded)) + .param("total", FormatUtil.bytesToString(total)) + ); + } + } + ); + CompletableFuture downloadFuture = downloadTask.future().whenComplete((success, error) -> { + if (updateModule != null) { + updateModule.releaseDownloadLock(); + } + + if (error instanceof CancellationException) { + UpdateService.deleteStagedUpdate(); + } else if (error == null && Boolean.TRUE.equals(success)) { + context.sendMessage(MSG_DOWNLOAD_COMPLETE); + if (!UpdateService.isValidUpdateLayout()) { + context.sendMessage(Message.translation("server.commands.update.download_setup_hint")); + } + } else { + context.sendMessage(MSG_DOWNLOAD_FAILED); + UpdateService.deleteStagedUpdate(); + } + }); + if (updateModule != null) { + updateModule.setActiveDownload(downloadFuture, downloadTask.thread()); + } + + return downloadFuture.thenApply(v -> null); + } } } - } - ); + ); + } } } } diff --git a/src/com/hypixel/hytale/server/core/update/command/UpdateSetupCommand.java b/src/com/hypixel/hytale/server/core/update/command/UpdateSetupCommand.java new file mode 100644 index 00000000..50b24733 --- /dev/null +++ b/src/com/hypixel/hytale/server/core/update/command/UpdateSetupCommand.java @@ -0,0 +1,83 @@ +package com.hypixel.hytale.server.core.update.command; + +import com.hypixel.hytale.common.util.java.ManifestUtil; +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; +import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.logging.Level; +import javax.annotation.Nonnull; + +public class UpdateSetupCommand extends CommandBase { + @Nonnull + private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); + private static final String RESOURCE_START_SH = "update/start.sh"; + private static final String RESOURCE_START_BAT = "update/start.bat"; + private static final String EXPECTED_DIR_NAME = "Server"; + private static final Message MSG_NOT_JAR = Message.translation("server.commands.update.setup.not_jar"); + private static final Message MSG_INVALID_DIRECTORY = Message.translation("server.commands.update.setup.invalid_directory"); + private static final Message MSG_ALREADY_EXIST = Message.translation("server.commands.update.setup.already_exist"); + private static final Message MSG_SETUP_COMPLETE = Message.translation("server.commands.update.setup.complete"); + private static final Message MSG_SETUP_FAILED = Message.translation("server.commands.update.setup.failed"); + private static final Message MSG_NO_ASSETS = Message.translation("server.commands.update.setup.no_assets"); + private final FlagArg forceFlag = this.withFlagArg("force", "server.commands.update.setup.force.desc"); + + public UpdateSetupCommand() { + super("setup", "server.commands.update.setup.desc"); + } + + @Override + protected void executeSync(@Nonnull CommandContext context) { + if (!ManifestUtil.isJar()) { + context.sendMessage(MSG_NOT_JAR); + } else { + boolean force = this.forceFlag.get(context); + Path cwd = Path.of(".").toAbsolutePath().normalize(); + if (!"Server".equals(cwd.getFileName().toString()) && !force) { + context.sendMessage(MSG_INVALID_DIRECTORY); + } else { + Path parent = cwd.getParent(); + Path startSh = parent.resolve("start.sh"); + Path startBat = parent.resolve("start.bat"); + boolean exists = Files.exists(startSh) || Files.exists(startBat); + if (exists && !force) { + context.sendMessage(MSG_ALREADY_EXIST); + } else { + try { + this.extractResource("update/start.sh", startSh); + this.extractResource("update/start.bat", startBat); + if (!System.getProperty("os.name", "").toLowerCase().contains("win")) { + startSh.toFile().setExecutable(true, false); + } + + context.sendMessage(MSG_SETUP_COMPLETE); + LOGGER.at(Level.INFO).log("Wrapper scripts written to %s", parent); + if (!Files.exists(parent.resolve("Assets.zip"))) { + context.sendMessage(MSG_NO_ASSETS); + } + } catch (IOException var9) { + LOGGER.at(Level.SEVERE).withCause(var9).log("Failed to extract wrapper scripts"); + context.sendMessage(MSG_SETUP_FAILED); + } + } + } + } + } + + private void extractResource(@Nonnull String resourcePath, @Nonnull Path target) throws IOException { + try (InputStream in = this.getClass().getClassLoader().getResourceAsStream(resourcePath)) { + if (in == null) { + throw new IOException("Resource not found in JAR: " + resourcePath); + } + + Files.createDirectories(target.getParent()); + Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING); + } + } +} diff --git a/src/com/hypixel/hytale/server/core/util/BsonUtil.java b/src/com/hypixel/hytale/server/core/util/BsonUtil.java index 9ee352a4..677fd848 100644 --- a/src/com/hypixel/hytale/server/core/util/BsonUtil.java +++ b/src/com/hypixel/hytale/server/core/util/BsonUtil.java @@ -13,6 +13,7 @@ import com.hypixel.hytale.server.core.util.io.FileUtil; import com.hypixel.hytale.sneakythrow.SneakyThrow; import io.netty.buffer.ByteBuf; import java.io.BufferedWriter; +import java.io.DataOutputStream; import java.io.IOException; import java.io.StringWriter; import java.nio.ByteBuffer; @@ -76,10 +77,31 @@ public class BsonUtil { return readFromBytes(ByteBufUtil.readByteArray(buf)); } + public static BsonDocument readFromBinaryStream(@Nonnull ByteBuffer buf) { + int length = Short.toUnsignedInt(buf.getShort()); + if (length == 0) { + return null; + } else { + ByteBuffer slice = buf.slice(buf.position(), length); + buf.position(buf.position() + length); + return readFromBuffer(slice); + } + } + public static void writeToBinaryStream(@Nonnull ByteBuf buf, BsonDocument doc) { ByteBufUtil.writeByteArray(buf, writeToBytes(doc)); } + public static void writeToBinaryStream(@Nonnull DataOutputStream out, BsonDocument doc) throws IOException { + byte[] bytes = writeToBytes(doc); + if (bytes.length >= 65535) { + throw new IllegalArgumentException("length is too large"); + } else { + out.writeShort(bytes.length); + out.write(bytes); + } + } + @Nonnull public static CompletableFuture writeDocument(@Nonnull Path file, BsonDocument document) { return writeDocument(file, document, true); diff --git a/src/com/hypixel/hytale/server/core/util/FillerBlockUtil.java b/src/com/hypixel/hytale/server/core/util/FillerBlockUtil.java index 6c4da8e7..b5b97023 100644 --- a/src/com/hypixel/hytale/server/core/util/FillerBlockUtil.java +++ b/src/com/hypixel/hytale/server/core/util/FillerBlockUtil.java @@ -2,11 +2,25 @@ package com.hypixel.hytale.server.core.util; import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap; +import com.hypixel.hytale.common.util.CompletableFutureUtil; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.RemoveReason; +import com.hypixel.hytale.component.Store; import com.hypixel.hytale.function.consumer.TriIntConsumer; import com.hypixel.hytale.function.predicate.TriIntPredicate; import com.hypixel.hytale.math.shape.Box; +import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockOperations; +import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; public class FillerBlockUtil { @@ -21,6 +35,23 @@ public class FillerBlockUtil { } public static void forEachFillerBlock(float threshold, @Nonnull BlockBoundingBoxes.RotatedVariantBoxes blockBoundingBoxes, @Nonnull TriIntConsumer consumer) { + forEachFillerBlock(threshold, 0, blockBoundingBoxes, consumer); + } + + public static void forEachFillerBlock( + float threshold, int expand, @Nonnull BlockBoundingBoxes.RotatedVariantBoxes blockBoundingBoxes, @Nonnull TriIntConsumer consumer + ) { + forEachFillerBlock(threshold, expand, expand, expand, blockBoundingBoxes, consumer); + } + + public static void forEachFillerBlock( + float threshold, + int expandX, + int expandY, + int expandZ, + @Nonnull BlockBoundingBoxes.RotatedVariantBoxes blockBoundingBoxes, + @Nonnull TriIntConsumer consumer + ) { if (!(threshold < 0.0F) && !(threshold >= 1.0F)) { Box boundingBox = blockBoundingBoxes.getBoundingBox(); int minX = (int)boundingBox.min.x; @@ -41,6 +72,12 @@ public class FillerBlockUtil { int maxX = (int)boundingBox.max.x; int maxY = (int)boundingBox.max.y; int maxZ = (int)boundingBox.max.z; + minX -= expandX; + minY -= expandY; + minZ -= expandZ; + maxX += expandX; + maxY += expandY; + maxZ += expandZ; if (boundingBox.max.x - maxX > threshold) { maxX++; } @@ -230,6 +267,265 @@ public class FillerBlockUtil { return result; } + private static void removeBlockEntity(ComponentAccessor accessor, BlockComponentChunk blockComponentChunk, int x, int y, int z) { + int indexInColumn = ChunkUtil.indexBlockInColumn(x, y, z); + if (!(accessor instanceof Store store && (!accessor.getExternalData().getWorld().isInThread() || store.isProcessing()))) { + Ref reference = blockComponentChunk.getEntityReference(indexInColumn); + if (reference != null) { + accessor.removeEntity(reference, ChunkStore.REGISTRY.newHolder(), RemoveReason.REMOVE); + } else { + blockComponentChunk.removeEntityHolder(indexInColumn); + } + } else { + CompletableFutureUtil._catch(CompletableFuture.runAsync(() -> { + Ref referencex = blockComponentChunk.getEntityReference(indexInColumn); + if (referencex != null) { + accessor.removeEntity(referencex, ChunkStore.REGISTRY.newHolder(), RemoveReason.REMOVE); + } else { + blockComponentChunk.removeEntityHolder(indexInColumn); + } + }, accessor.getExternalData().getWorld())); + } + } + + private static void removeFiller( + ComponentAccessor accessor, BlockSection blockSection, int x, int y, int z, FillerBlockUtil.ChangeReason changeReason + ) { + Ref column = accessor.getExternalData().getChunkReference(ChunkUtil.indexChunkFromBlock(x, z)); + if (column != null) { + BlockChunk blockChunk = accessor.getComponent(column, BlockChunk.getComponentType()); + if (blockChunk != null) { + BlockComponentChunk blockComponentChunk = accessor.getComponent(column, BlockComponentChunk.getComponentType()); + if (blockComponentChunk != null) { + short oldHeight = blockChunk.getHeight(x, z); + int oldBlock = blockSection.get(x, y, z); + boolean changed = blockSection.set(x, y, z, 0, 0, 0); + if (changed) { + short newHeight = BlockOperations.updateBlockHeight(blockChunk, 0, BlockType.EMPTY, x, y, z, oldHeight); + if (changeReason != FillerBlockUtil.ChangeReason.NONE) { + BlockOperations.spawnBlockParticles( + accessor.getExternalData(), oldBlock, 0, x, y, z, changeReason == FillerBlockUtil.ChangeReason.BY_PHYSICS + ); + } + + removeBlockEntity(accessor, blockComponentChunk, x, y, z); + accessor.getExternalData() + .getWorld() + .getChunkLighting() + .invalidateLightAtBlock(accessor.getExternalData(), x, y, z, BlockType.EMPTY, oldHeight, newHeight); + } + } + } + } + } + + public static void removeFillerBlocksAt( + @Nonnull ComponentAccessor accessor, + BlockSection blockSection, + int x, + int y, + int z, + int blockId, + int filler, + int rotation, + FillerBlockUtil.ChangeReason changeReason + ) { + BlockType oldBlockType = BlockType.getAssetMap().getAsset(blockId); + if (oldBlockType != null) { + int fx = unpackX(filler); + int fy = unpackY(filler); + int fz = unpackZ(filler); + int baseX = x - fx; + int baseY = y - fy; + int baseZ = z - fz; + BlockBoundingBoxes hitbox = BlockBoundingBoxes.getAssetMap().getAsset(oldBlockType.getHitboxTypeIndex()); + if (hitbox == null) { + hitbox = BlockBoundingBoxes.UNIT_BOX; + } + + forEachFillerBlock(hitbox.get(rotation), (x1, y1, z1) -> { + if (x1 != fx || y1 != fy || z1 != fz) { + int blockX = baseX + x1; + int blockY = baseY + y1; + int blockZ = baseZ + z1; + if (ChunkUtil.isSameChunkSection(x, y, z, blockX, blockY, blockZ)) { + int otherBlockId = blockSection.get(blockX, blockY, blockZ); + if (otherBlockId == blockId) { + removeFiller(accessor, blockSection, blockX, blockY, blockZ, changeReason); + } + } else { + ChunkStore chunkStore = accessor.getExternalData(); + Ref section = chunkStore.getWorld().isInThread() ? chunkStore.getChunkSectionReferenceAtBlock(blockX, blockY, blockZ) : null; + if (section != null) { + BlockSection otherBlockSection = accessor.getComponent(section, BlockSection.getComponentType()); + if (otherBlockSection == null) { + return; + } + + int otherBlockId = otherBlockSection.get(blockX, blockY, blockZ); + if (otherBlockId == blockId) { + removeFiller(accessor, otherBlockSection, blockX, blockY, blockZ, changeReason); + } + } else { + chunkStore.getChunkSectionReferenceAtBlockAsync(blockX, blockY, blockZ).thenAcceptAsync(section1 -> { + BlockSection otherBlockSectionx = section1.getStore().getComponent((Ref)section1, BlockSection.getComponentType()); + if (otherBlockSectionx != null) { + int otherBlockIdx = otherBlockSectionx.get(blockX, blockY, blockZ); + if (otherBlockIdx == blockId) { + removeFiller(accessor, otherBlockSectionx, blockX, blockY, blockZ, changeReason); + } + } + }, accessor.getExternalData().getWorld()); + } + } + } + }); + } + } + + private static void setFiller( + @Nonnull ComponentAccessor accessor, + @Nonnull Ref ref, + @Nonnull BlockSection blockSection, + int x, + int y, + int z, + int blockId, + BlockType blockType, + int filler, + int rotation, + FillerBlockUtil.ChangeReason changeReason + ) { + int oldBlock = blockSection.get(x, y, z); + int oldFiller = blockSection.getFiller(x, y, z); + int oldRotation = blockSection.getRotationIndex(x, y, z); + if (blockSection.set(x, y, z, blockId, rotation, filler)) { + if (oldBlock != 0) { + removeFillerBlocksAt(accessor, blockSection, x, y, z, oldBlock, oldFiller, oldRotation, changeReason); + } + + Ref column = accessor.getExternalData().getChunkReference(ChunkUtil.indexChunkFromBlock(x, z)); + if (column != null) { + BlockChunk blockChunk = accessor.getComponent(column, BlockChunk.getComponentType()); + if (blockChunk != null) { + BlockComponentChunk blockComponentChunk = accessor.getComponent(column, BlockComponentChunk.getComponentType()); + if (blockComponentChunk != null) { + short oldHeight = blockChunk.getHeight(x, z); + short newHeight = BlockOperations.updateBlockHeight(blockChunk, blockId, blockType, x, y, z, oldHeight); + if (changeReason != FillerBlockUtil.ChangeReason.NONE) { + BlockOperations.spawnBlockParticles( + accessor.getExternalData(), oldBlock, blockId, x, y, z, changeReason == FillerBlockUtil.ChangeReason.BY_PHYSICS + ); + } + + accessor.getExternalData() + .getWorld() + .getChunkLighting() + .invalidateLightAtBlock(accessor.getExternalData(), x, y, z, blockType, oldHeight, newHeight); + removeBlockEntity(accessor, blockComponentChunk, x, y, z); + World world = accessor.getExternalData().getWorld(); + if (world.isInThread()) { + if (!blockType.hasSupport()) { + BlockPhysics.clear(accessor, ref, x, y, z); + } else { + BlockPhysics.reset(accessor, ref, x, y, z); + } + } else { + world.execute(() -> { + if (!blockType.hasSupport()) { + BlockPhysics.clear(accessor, ref, x, y, z); + } else { + BlockPhysics.reset(accessor, ref, x, y, z); + } + }); + } + } + } + } + } + } + + public static void setFillerBlocksAt( + @Nonnull ComponentAccessor accessor, + @Nonnull Ref ref, + BlockSection blockSection, + int x, + int y, + int z, + int blockId, + int filler, + int rotation, + FillerBlockUtil.ChangeReason changeReason + ) { + BlockType blockType = BlockType.getAssetMap().getAsset(blockId); + if (blockType != null) { + int fx = unpackX(filler); + int fy = unpackY(filler); + int fz = unpackZ(filler); + int baseX = x - fx; + int baseY = y - fy; + int baseZ = z - fz; + BlockBoundingBoxes hitbox = BlockBoundingBoxes.getAssetMap().getAsset(blockType.getHitboxTypeIndex()); + if (hitbox == null) { + hitbox = BlockBoundingBoxes.UNIT_BOX; + } + + forEachFillerBlock( + hitbox.get(rotation), + (x1, y1, z1) -> { + if (x1 != fx || y1 != fy || z1 != fz) { + int blockX = baseX + x1; + int blockY = baseY + y1; + int blockZ = baseZ + z1; + if (ChunkUtil.isSameChunkSection(x, y, z, blockX, blockY, blockZ)) { + setFiller(accessor, ref, blockSection, blockX, blockY, blockZ, blockId, blockType, pack(x1, y1, z1), rotation, changeReason); + } else { + ChunkStore chunkStore = accessor.getExternalData(); + Ref section = chunkStore.getWorld().isInThread() ? chunkStore.getChunkSectionReferenceAtBlock(blockX, blockY, blockZ) : null; + if (section != null) { + BlockSection otherBlockSection = accessor.getComponent(section, BlockSection.getComponentType()); + if (otherBlockSection == null) { + return; + } + + setFiller(accessor, section, otherBlockSection, blockX, blockY, blockZ, blockId, blockType, pack(x1, y1, z1), rotation, changeReason); + } else { + chunkStore.getChunkSectionReferenceAtBlockAsync(blockX, blockY, blockZ) + .thenAcceptAsync( + section1 -> { + BlockSection otherBlockSectionx = section1.getStore().getComponent((Ref)section1, BlockSection.getComponentType()); + if (otherBlockSectionx != null) { + setFiller( + accessor, + (Ref)section1, + otherBlockSectionx, + blockX, + blockY, + blockZ, + blockId, + blockType, + pack(x1, y1, z1), + rotation, + changeReason + ); + } + }, + accessor.getExternalData().getWorld() + ); + } + } + } + } + ); + } + } + + public static enum ChangeReason { + NONE, + NORMAL, + BY_PHYSICS; + } + public interface FillerFetcher { int getBlock(A var1, B var2, int var3, int var4, int var5); diff --git a/src/com/hypixel/hytale/server/core/util/MessageUtil.java b/src/com/hypixel/hytale/server/core/util/MessageUtil.java index 58c44597..2ca92ba4 100644 --- a/src/com/hypixel/hytale/server/core/util/MessageUtil.java +++ b/src/com/hypixel/hytale/server/core/util/MessageUtil.java @@ -59,6 +59,53 @@ public class MessageUtil { } } + public static boolean containsControlCharacters(@Nonnull String message) { + for (int i = 0; i < message.length(); i++) { + char c = message.charAt(i); + if (c < ' ') { + return true; + } + + if (c == 127) { + return true; + } + + if (c >= 128 && c <= 159) { + return true; + } + } + + return false; + } + + @Nullable + public static String formatMessageToPlainString(@Nullable FormattedMessage msg) { + if (msg == null) { + return null; + } else { + StringBuilder sb = new StringBuilder(); + if (msg.rawText != null) { + sb.append(msg.rawText); + } else if (msg.messageId != null) { + try { + I18nModule i18n = I18nModule.get(); + String message = i18n != null ? i18n.getMessage("en-US", msg.messageId) : null; + sb.append(message != null ? formatText(message, msg.params, msg.messageParams) : msg.messageId); + } catch (Exception var6) { + sb.append(msg.messageId); + } + } + + if (msg.children != null) { + for (FormattedMessage child : msg.children) { + sb.append(formatMessageToPlainString(child)); + } + } + + return sb.toString(); + } + } + @Deprecated public static void sendSuccessReply(@Nonnull PlayerRef playerRef, int token) { sendSuccessReply(playerRef, token, null); @@ -148,7 +195,7 @@ public class MessageUtil { } else if (replacement != null) { String formattedReplacement; formattedReplacement = ""; - label171: + label179: switch (format) { case "upper": if (replacement instanceof StringParamValue s) { @@ -171,7 +218,7 @@ public class MessageUtil { case LongParamValue l -> Long.toString(l.value); default -> ""; }; - break label171; + break label179; case "decimal": case null: default: @@ -183,12 +230,19 @@ public class MessageUtil { case LongParamValue l -> Long.toString(l.value); default -> ""; }; - break label171; + break label179; } case "plural": if (options != null) { Map pluralTexts = parsePluralOptions(options); - int value = Integer.parseInt(replacement.toString()); + + int value = switch (replacement) { + case IntParamValue iv -> iv.value; + case LongParamValue l -> (int)l.value; + case DoubleParamValue d -> (int)d.value; + case StringParamValue s -> Integer.parseInt(s.value); + default -> 0; + }; String category = getPluralCategory(value, "en-US"); String selected; if (pluralTexts.containsKey(category)) { diff --git a/src/com/hypixel/hytale/server/core/util/PositionUtil.java b/src/com/hypixel/hytale/server/core/util/PositionUtil.java index 241b5624..249c8739 100644 --- a/src/com/hypixel/hytale/server/core/util/PositionUtil.java +++ b/src/com/hypixel/hytale/server/core/util/PositionUtil.java @@ -1,18 +1,19 @@ package com.hypixel.hytale.server.core.util; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.protocol.Direction; import com.hypixel.hytale.protocol.Position; import com.hypixel.hytale.protocol.Transform; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PositionUtil { @Nonnull public static Transform toTransformPacket(@Nonnull com.hypixel.hytale.math.vector.Transform transform) { Vector3d position = transform.getPosition(); - Vector3f rotation = transform.getRotation(); + Rotation3f rotation = transform.getRotation(); return new Transform(toPositionPacket(position), toDirectionPacket(rotation)); } @@ -22,8 +23,8 @@ public class PositionUtil { } @Nonnull - public static Direction toDirectionPacket(@Nonnull Vector3f rotation) { - return new Direction(rotation.getYaw(), rotation.getPitch(), rotation.getRoll()); + public static Direction toDirectionPacket(@Nonnull Rotation3f rotation) { + return new Direction(rotation.yaw(), rotation.pitch(), rotation.roll()); } public static com.hypixel.hytale.math.vector.Transform toTransform(@Nullable Transform transform) { @@ -36,8 +37,8 @@ public class PositionUtil { } @Nonnull - public static Vector3f toRotation(@Nonnull Direction orientation) { - return new Vector3f(orientation.pitch, orientation.yaw, orientation.roll); + public static Rotation3f toRotation(@Nonnull Direction orientation) { + return new Rotation3f(orientation.pitch, orientation.yaw, orientation.roll); } public static boolean equals(@Nonnull Vector3d vector, @Nonnull Position position) { @@ -50,14 +51,14 @@ public class PositionUtil { position.z = vector.z; } - public static boolean equals(@Nonnull Vector3f vector, @Nonnull Direction direction) { - return vector.x == direction.pitch && vector.y == direction.yaw && vector.z == direction.roll; + public static boolean equals(@Nonnull Rotation3fc vector, @Nonnull Direction direction) { + return vector.x() == direction.pitch && vector.y() == direction.yaw && vector.z() == direction.roll; } - public static void assign(@Nonnull Direction direction, @Nonnull Vector3f vector) { - direction.pitch = vector.x; - direction.yaw = vector.y; - direction.roll = vector.z; + public static void assign(@Nonnull Direction direction, @Nonnull Rotation3fc vector) { + direction.pitch = vector.x(); + direction.yaw = vector.y(); + direction.roll = vector.z(); } public static void assign(@Nonnull Position position, @Nonnull Position other) { diff --git a/src/com/hypixel/hytale/server/core/util/PrefabUtil.java b/src/com/hypixel/hytale/server/core/util/PrefabUtil.java index aabcfc7f..3e862a85 100644 --- a/src/com/hypixel/hytale/server/core/util/PrefabUtil.java +++ b/src/com/hypixel/hytale/server/core/util/PrefabUtil.java @@ -9,8 +9,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; @@ -36,6 +34,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class PrefabUtil { protected static final String EDITOR_BLOCK = "Editor_Block"; @@ -49,7 +49,7 @@ public class PrefabUtil { double xLength = prefabBuffer.getMaxX() - prefabBuffer.getMinX(); double zLength = prefabBuffer.getMaxZ() - prefabBuffer.getMinZ(); int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength)); - LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.getX(), position.getZ(), prefabRadius); + LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.x(), position.z(), prefabRadius); return prefabBuffer.compare((x, y, z, blockId, rotation, holder, prefabBufferCall) -> { int bx = position.x + x; int by = position.y + y; @@ -72,7 +72,7 @@ public class PrefabUtil { double xLength = prefabBuffer.getMaxX() - prefabBuffer.getMinX(); double zLength = prefabBuffer.getMaxZ() - prefabBuffer.getMinZ(); int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength)); - LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.getX(), position.getZ(), prefabRadius); + LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.x(), position.z(), prefabRadius); return prefabBuffer.compare( (x, y, z, blockId, rotation, holder, prefabBufferCall) -> { if (ignoreOrigin && x == 0 && y == 0 && z == 0) { @@ -141,7 +141,7 @@ public class PrefabUtil { double xLength = buffer.getMaxX() - buffer.getMinX(); double zLength = buffer.getMaxZ() - buffer.getMinZ(); int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength)); - LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.getX(), position.getZ(), prefabRadius); + LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.x(), position.z(), prefabRadius); BlockTypeAssetMap blockTypeMap = BlockType.getAssetMap(); int editorBlock = blockTypeMap.getIndex("Editor_Block"); if (editorBlock == Integer.MIN_VALUE) { @@ -176,7 +176,7 @@ public class PrefabUtil { String blockKey = block.getId(); if (filler != 0) { if (holder != null) { - chunk.setState(bx, by, bz, holder.clone()); + chunk.setState(bx, by, bz, block, blockRotation, holder.clone()); } } else { if (pasteAnchorAsBlock && technicalPaste && x == buffer.getAnchorX() && y == buffer.getAnchorY() && z == buffer.getAnchorZ()) { @@ -209,7 +209,7 @@ public class PrefabUtil { } if (holder != null) { - chunk.setState(bx, by, bz, holder.clone()); + chunk.setState(bx, by, bz, block, blockRotation, holder.clone()); } } }, (x, z, entityWrappers, t) -> { @@ -219,9 +219,9 @@ public class PrefabUtil { Holder entityToAdd = entityWrappers[i].clone(); TransformComponent transformComp = entityToAdd.getComponent(TransformComponent.getComponentType()); if (transformComp != null) { - Vector3d entityPosition = transformComp.getPosition().clone(); + Vector3d entityPosition = new Vector3d(transformComp.getPosition()); rotation.rotate(entityPosition); - Vector3d entityWorldPosition = entityPosition.add(position); + Vector3d entityWorldPosition = entityPosition.add(position.x, position.y, position.z); transformComp = entityToAdd.getComponent(TransformComponent.getComponentType()); if (transformComp != null) { entityPosition = transformComp.getPosition(); @@ -278,7 +278,7 @@ public class PrefabUtil { double xLength = prefabBuffer.getMaxX() - prefabBuffer.getMinX(); double zLength = prefabBuffer.getMaxZ() - prefabBuffer.getMinZ(); int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength)); - LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.getX(), position.getZ(), prefabRadius); + LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, position.x(), position.z(), prefabRadius); BlockTypeAssetMap blockTypeMap = BlockType.getAssetMap(); prefabBuffer.forEach( IPrefabBuffer.iterateAllColumns(), diff --git a/src/com/hypixel/hytale/server/core/util/TargetUtil.java b/src/com/hypixel/hytale/server/core/util/TargetUtil.java index c3af9f3a..21c30dcc 100644 --- a/src/com/hypixel/hytale/server/core/util/TargetUtil.java +++ b/src/com/hypixel/hytale/server/core/util/TargetUtil.java @@ -8,11 +8,8 @@ import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.iterator.BlockIterator; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.modules.collision.CollisionMath; import com.hypixel.hytale.server.core.modules.collision.WorldUtil; import com.hypixel.hytale.server.core.modules.entity.EntityModule; @@ -27,12 +24,14 @@ import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.LinkedList; import java.util.List; import java.util.function.IntPredicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; +import org.joml.Vector3i; public final class TargetUtil { private static final float ENTITY_TARGET_RADIUS = 8.0F; @@ -157,6 +156,57 @@ public final class TargetUtil { return success ? null : new Vector3i(buffer.x, buffer.y, buffer.z); } + @Nullable + public static Vector3i getTargetBlockAvoidLocations( + @Nonnull World world, + @Nonnull IntPredicate blockIdPredicate, + double originX, + double originY, + double originZ, + double directionX, + double directionY, + double directionZ, + double maxDistance, + @Nonnull LinkedList blocksToIgnore, + @Nonnull Vector3d hitPositionOut + ) { + TargetUtil.TargetBuffer buffer = new TargetUtil.TargetBuffer(world); + buffer.updateChunk((int)originX, (int)originZ); + boolean success = BlockIterator.iterate( + originX, originY, originZ, directionX, directionY, directionZ, maxDistance, (x, y, z, px, py, pz, qx, qy, qz, iBuffer) -> { + if (y >= 0 && y < 320) { + iBuffer.updateChunk(x, z); + if (iBuffer.currentBlockChunk == null) { + return false; + } else { + iBuffer.x = x; + iBuffer.y = y; + iBuffer.z = z; + hitPositionOut.x = x + px; + hitPositionOut.y = y + py; + hitPositionOut.z = z + pz; + BlockSection blockSection = iBuffer.currentBlockChunk.getSectionAtBlockY(y); + int blockId = blockSection.get(x, y, z); + if (blockId != 0) { + long packedBlockLocation = BlockUtil.pack(x, y, z); + + for (LongOpenHashSet locations : blocksToIgnore) { + if (locations.contains(packedBlockLocation)) { + return true; + } + } + } + + return !blockIdPredicate.test(blockId); + } + } else { + return false; + } + }, buffer + ); + return success ? null : new Vector3i(buffer.x, buffer.y, buffer.z); + } + @Nullable public static Vector3i getTargetBlock(@Nonnull Ref ref, double maxDistance, @Nonnull ComponentAccessor componentAccessor) { return getTargetBlock(ref, blockId -> blockId != 0, maxDistance, componentAccessor); @@ -200,7 +250,7 @@ public final class TargetUtil { public static List> getAllEntitiesInSphere( @Nonnull Vector3d position, double radius, @Nonnull ComponentAccessor componentAccessor ) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> entitySpatialResource = componentAccessor.getResource(EntityModule.get().getEntitySpatialResourceType()); entitySpatialResource.getSpatialStructure().collect(position, (float)radius, results); SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); @@ -212,7 +262,7 @@ public final class TargetUtil { public static List> getAllEntitiesInCylinder( @Nonnull Vector3d position, double radius, double height, @Nonnull ComponentAccessor componentAccessor ) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> entitySpatialResource = componentAccessor.getResource(EntityModule.get().getEntitySpatialResourceType()); entitySpatialResource.getSpatialStructure().collectCylinder(position, radius, height, results); SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); @@ -224,7 +274,7 @@ public final class TargetUtil { public static List> getAllEntitiesInBox( @Nonnull Vector3d min, @Nonnull Vector3d max, @Nonnull ComponentAccessor componentAccessor ) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> entitySpatialResource = componentAccessor.getResource(EntityModule.get().getEntitySpatialResourceType()); entitySpatialResource.getSpatialStructure().collectBox(min, max, results); SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); @@ -265,7 +315,7 @@ public final class TargetUtil { assert targetTransformComponent != null; - double distance = transformPosition.distanceSquaredTo(targetTransformComponent.getPosition()); + double distance = transformPosition.distanceSquared(targetTransformComponent.getPosition()); if (distance < minDist2) { minDist2 = distance; closest = targetRef; @@ -294,10 +344,8 @@ public final class TargetUtil { assert headRotationComponent != null; Vector3d position = transformComponent.getPosition(); - Vector3f headRotation = headRotationComponent.getRotation(); - return new Transform( - position.getX(), position.getY() + eyeHeight, position.getZ(), headRotation.getPitch(), headRotation.getYaw(), headRotation.getRoll() - ); + Rotation3f headRotation = headRotationComponent.getRotation(); + return new Transform(position.x(), position.y() + eyeHeight, position.z(), headRotation.pitch(), headRotation.yaw(), headRotation.roll()); } private static boolean isHitByRay( @@ -314,7 +362,7 @@ public final class TargetUtil { Box boundingBox = boundingBoxComponent.getBoundingBox(); Vector3d position = transformComponent.getPosition(); Vector2d minMax = new Vector2d(); - return CollisionMath.intersectRayAABB(rayStart, rayDir, position.getX(), position.getY(), position.getZ(), boundingBox, minMax); + return CollisionMath.intersectRayAABB(rayStart, rayDir, position.x(), position.y(), position.z(), boundingBox, minMax); } } diff --git a/src/com/hypixel/hytale/server/core/util/backup/BackupTask.java b/src/com/hypixel/hytale/server/core/util/backup/BackupTask.java index 2f15958c..010efe91 100644 --- a/src/com/hypixel/hytale/server/core/util/backup/BackupTask.java +++ b/src/com/hypixel/hytale/server/core/util/backup/BackupTask.java @@ -14,6 +14,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import javax.annotation.Nonnull; @@ -34,6 +35,7 @@ public class BackupTask { private BackupTask(@Nonnull final Path universeDir, @Nonnull final Path backupDir) { (new Thread("Backup Runner") { { + Objects.requireNonNull(BackupTask.this); this.setDaemon(false); } diff --git a/src/com/hypixel/hytale/server/core/util/backup/BackupUtil.java b/src/com/hypixel/hytale/server/core/util/backup/BackupUtil.java index 16d8453c..3869a8a1 100644 --- a/src/com/hypixel/hytale/server/core/util/backup/BackupUtil.java +++ b/src/com/hypixel/hytale/server/core/util/backup/BackupUtil.java @@ -1,5 +1,6 @@ package com.hypixel.hytale.server.core.util.backup; +import com.hypixel.hytale.common.util.PathUtil; import com.hypixel.hytale.common.util.java.ManifestUtil; import com.hypixel.hytale.protocol.packets.interface_.WorldSavingStatus; import com.hypixel.hytale.server.core.Message; @@ -46,7 +47,7 @@ class BackupUtil { } } - ZipEntry zipEntry = new ZipEntry(sourceDir.relativize(path).toString()); + ZipEntry zipEntry = new ZipEntry(PathUtil.toUnixPathString(sourceDir.relativize(path))); zipEntry.setSize(size); zipEntry.setCompressedSize(size); zipEntry.setCrc(crc.getValue()); diff --git a/src/com/hypixel/hytale/server/core/util/io/ByteBufUtil.java b/src/com/hypixel/hytale/server/core/util/io/ByteBufUtil.java index f8fc63c2..15ce2c7f 100644 --- a/src/com/hypixel/hytale/server/core/util/io/ByteBufUtil.java +++ b/src/com/hypixel/hytale/server/core/util/io/ByteBufUtil.java @@ -3,21 +3,26 @@ package com.hypixel.hytale.server.core.util.io; import com.hypixel.hytale.common.util.BitSetUtil; import com.hypixel.hytale.unsafe.UnsafeUtil; import io.netty.buffer.ByteBuf; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.BitSet; import javax.annotation.Nonnull; public class ByteBufUtil { - private static int MAX_UNSIGNED_SHORT_VALUE = 65535; + public static final int MAX_UNSIGNED_SHORT_VALUE = 65535; public static void writeUTF(@Nonnull ByteBuf buf, @Nonnull String string) { - if (io.netty.buffer.ByteBufUtil.utf8MaxBytes(string) >= MAX_UNSIGNED_SHORT_VALUE) { + if (io.netty.buffer.ByteBufUtil.utf8MaxBytes(string) >= 65535) { throw new IllegalArgumentException("String is too large"); } else { int before = buf.writerIndex(); buf.writeShort(-1); int length = buf.writeCharSequence(string, StandardCharsets.UTF_8); - if (length >= 0 && length < MAX_UNSIGNED_SHORT_VALUE) { + if (length >= 0 && length < 65535) { int after = buf.writerIndex(); buf.writerIndex(before); buf.writeShort(length); @@ -28,6 +33,24 @@ public class ByteBufUtil { } } + public static void writeUTF(@Nonnull DataOutputStream out, @Nonnull String string) throws IOException { + byte[] str = string.getBytes(StandardCharsets.UTF_8); + if (str.length >= 65535) { + throw new IllegalArgumentException("String is too large"); + } else { + out.writeShort(str.length); + out.write(str); + } + } + + @Nonnull + public static String readUTF(@Nonnull ByteBuffer buffer) { + int length = Short.toUnsignedInt(buffer.getShort()); + byte[] bytes = new byte[length]; + buffer.get(bytes); + return new String(bytes, StandardCharsets.UTF_8); + } + @Nonnull public static String readUTF(@Nonnull ByteBuf buf) { int length = buf.readUnsignedShort(); @@ -39,7 +62,7 @@ public class ByteBufUtil { } public static void writeByteArray(@Nonnull ByteBuf buf, byte[] arr, int src, int length) { - if (length > 0 && length < MAX_UNSIGNED_SHORT_VALUE) { + if (length > 0 && length < 65535) { buf.writeShort(length); buf.writeBytes(arr, src, length); } else { @@ -65,27 +88,27 @@ public class ByteBufUtil { return var1; } - public static void writeNumber(@Nonnull ByteBuf buf, int bytes, int value) { + public static void writeNumber(@Nonnull MemorySegment data, int offset, int bytes, int value) { switch (bytes) { case 1: - buf.writeByte(value); + data.set(ValueLayout.JAVA_BYTE, (long)offset, (byte)value); break; case 2: - buf.writeShort(value); + data.set(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset, (short)value); case 3: default: break; case 4: - buf.writeInt(value); + data.set(ValueLayout.JAVA_INT_UNALIGNED, (long)offset, value); } } - public static int readNumber(@Nonnull ByteBuf buf, int bytes) { + public static int readNumber(@Nonnull MemorySegment data, int offset, int bytes) { return switch (bytes) { - case 1 -> buf.readByte() & 255; - case 2 -> buf.readShort() & '\uffff'; + case 1 -> data.get(ValueLayout.JAVA_BYTE, (long)offset) & 255; + case 2 -> data.get(ValueLayout.JAVA_SHORT_UNALIGNED, (long)offset) & '\uffff'; default -> 0; - case 4 -> buf.readInt(); + case 4 -> data.get(ValueLayout.JAVA_INT_UNALIGNED, (long)offset); }; } diff --git a/src/com/hypixel/hytale/server/core/util/thread/TickingThread.java b/src/com/hypixel/hytale/server/core/util/thread/TickingThread.java index 8470db5a..1292c1b7 100644 --- a/src/com/hypixel/hytale/server/core/util/thread/TickingThread.java +++ b/src/com/hypixel/hytale/server/core/util/thread/TickingThread.java @@ -196,13 +196,13 @@ public abstract class TickingThread implements Runnable { } public void debugAssertInTickingThread() { - if (!Thread.currentThread().equals(this.thread) && this.thread != null) { + if (this.thread != null && Thread.currentThread() != this.thread) { throw new AssertionError("Assert not in ticking thread!"); } } public boolean isInThread() { - return Thread.currentThread().equals(this.thread); + return Thread.currentThread() == this.thread; } public boolean isStarted() { diff --git a/src/com/hypixel/hytale/server/flock/Flock.java b/src/com/hypixel/hytale/server/flock/Flock.java index 5522f36c..b6f8d2a3 100644 --- a/src/com/hypixel/hytale/server/flock/Flock.java +++ b/src/com/hypixel/hytale/server/flock/Flock.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.flock.config.FlockAsset; import com.hypixel.hytale.server.npc.util.DamageData; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Flock implements Component { private boolean trace; @@ -89,7 +90,7 @@ public class Flock implements Component { public void onTargetKilled(@Nonnull ComponentAccessor componentAccessor, @Nonnull Ref targetEntityReference) { TransformComponent targetTransformComponent = componentAccessor.getComponent(targetEntityReference, TransformComponent.getComponentType()); if (targetTransformComponent != null) { - this.nextDamageData.onKill(targetEntityReference, targetTransformComponent.getPosition().clone()); + this.nextDamageData.onKill(targetEntityReference, new Vector3d(targetTransformComponent.getPosition())); } } diff --git a/src/com/hypixel/hytale/server/flock/FlockPlugin.java b/src/com/hypixel/hytale/server/flock/FlockPlugin.java index fc616833..cff835c9 100644 --- a/src/com/hypixel/hytale/server/flock/FlockPlugin.java +++ b/src/com/hypixel/hytale/server/flock/FlockPlugin.java @@ -16,8 +16,8 @@ import com.hypixel.hytale.function.consumer.TriConsumer; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.server.core.asset.HytaleAssetStore; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.group.EntityGroup; @@ -52,6 +52,7 @@ import java.util.Map; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3dc; public class FlockPlugin extends JavaPlugin { private static FlockPlugin instance; @@ -146,8 +147,8 @@ public class FlockPlugin extends JavaPlugin { @Nonnull NPCEntity npc, @Nonnull Store store, int roleIndex, - @Nonnull Vector3d position, - Vector3f rotation, + @Nonnull Vector3dc position, + Rotation3fc rotation, @Nullable FlockAsset flockDefinition, TriConsumer, Store> postSpawn ) { @@ -161,8 +162,8 @@ public class FlockPlugin extends JavaPlugin { @Nonnull NPCEntity npc, @Nonnull Store store, int roleIndex, - @Nonnull Vector3d position, - Vector3f rotation, + @Nonnull Vector3dc position, + Rotation3fc rotation, int flockSize, TriConsumer, Store> postSpawn ) { @@ -174,8 +175,8 @@ public class FlockPlugin extends JavaPlugin { @Nonnull Ref npcRef, @Nonnull NPCEntity npc, int roleIndex, - @Nonnull Vector3d position, - Vector3f rotation, + @Nonnull Vector3dc position, + Rotation3fc rotation, int flockSize, FlockAsset flockDefinition, TriConsumer, Store> preAddToWorld, @@ -204,11 +205,11 @@ public class FlockPlugin extends JavaPlugin { assert transformComponent != null; Box boundingBox = boundingBoxComponent.getBoundingBox(); - Vector3f bodyRotation = transformComponent.getRotation(); - double x = position.getX(); - int y = MathUtil.floor(position.getY() + boundingBox.min.y + 1.0E-6); - double z = position.getZ(); - double yaw = bodyRotation.getYaw(); + Rotation3f bodyRotation = transformComponent.getRotation(); + double x = position.x(); + int y = MathUtil.floor(position.y() + boundingBox.min.y + 1.0E-6); + double z = position.z(); + double yaw = bodyRotation.yaw(); boolean randomSpawn = role.isFlockSpawnTypesRandom(); int[] roles = role.getFlockSpawnTypes(); int rolesSize = roles == null ? 0 : roles.length; @@ -227,25 +228,27 @@ public class FlockPlugin extends JavaPlugin { Pair, NPCEntity> memberPair = NPCPlugin.get() .spawnEntity(store, memberRoleIndex, position, rotation, null, preAddToWorld, postSpawn); - Ref memberRef = memberPair.first(); - if (memberRef != null && memberRef.isValid()) { - BoundingBox memberBoundingBoxComponent = store.getComponent(memberRef, BoundingBox.getComponentType()); + if (memberPair != null) { + Ref memberRef = memberPair.first(); + if (memberRef != null && memberRef.isValid()) { + BoundingBox memberBoundingBoxComponent = store.getComponent(memberRef, BoundingBox.getComponentType()); - assert memberBoundingBoxComponent != null; + assert memberBoundingBoxComponent != null; - TransformComponent memberTransformComponent = store.getComponent(memberRef, TransformComponent.getComponentType()); + TransformComponent memberTransformComponent = store.getComponent(memberRef, TransformComponent.getComponentType()); - assert memberTransformComponent != null; + assert memberTransformComponent != null; - HeadRotation memberHeadRotationComponent = store.getComponent(memberRef, HeadRotation.getComponentType()); + HeadRotation memberHeadRotationComponent = store.getComponent(memberRef, HeadRotation.getComponentType()); - assert memberHeadRotationComponent != null; + assert memberHeadRotationComponent != null; - double offsetY = y - memberBoundingBoxComponent.getBoundingBox().min.y; - memberTransformComponent.getRotation().setYaw((float)(yaw + RandomExtra.randomRange((float) (Math.PI / 4), (float) (Math.PI / 4)))); - memberHeadRotationComponent.getRotation().setPitch(0.0F); - memberTransformComponent.getPosition().assign(x + RandomExtra.randomRange(-0.5, 0.5), offsetY, z + RandomExtra.randomRange(-0.5, 0.5)); - FlockMembershipSystems.join(memberRef, flockReference, store); + double offsetY = y - memberBoundingBoxComponent.getBoundingBox().min.y; + memberTransformComponent.getRotation().setYaw((float)(yaw + RandomExtra.randomRange((float) (Math.PI / 4), (float) (Math.PI / 4)))); + memberHeadRotationComponent.getRotation().setPitch(0.0F); + memberTransformComponent.getPosition().set(x + RandomExtra.randomRange(-0.5, 0.5), offsetY, z + RandomExtra.randomRange(-0.5, 0.5)); + FlockMembershipSystems.join(memberRef, flockReference, store); + } } } diff --git a/src/com/hypixel/hytale/server/flock/FlockSystems.java b/src/com/hypixel/hytale/server/flock/FlockSystems.java index 4f659ad0..72fe2d59 100644 --- a/src/com/hypixel/hytale/server/flock/FlockSystems.java +++ b/src/com/hypixel/hytale/server/flock/FlockSystems.java @@ -13,8 +13,6 @@ import com.hypixel.hytale.component.system.EntityEventSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.group.EntityGroup; @@ -28,6 +26,8 @@ import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class FlockSystems { public static class EntityRemoved extends RefSystem { @@ -161,10 +161,8 @@ public class FlockSystems { double leaderRingInnerRadius = leaderRingOuterRadius - 0.08F; double leaderOuterRingRadius = leaderRingOuterRadius + 0.15F; double leaderOuterRingInnerRadius = leaderOuterRingRadius - 0.08F; - DebugUtils.addDisc(world, leaderPos.x, leaderMidY, leaderPos.z, leaderRingOuterRadius, leaderRingInnerRadius, color, 0.8F, 8, 0.1F, false); - DebugUtils.addDisc( - world, leaderPos.x, leaderMidY, leaderPos.z, leaderOuterRingRadius, leaderOuterRingInnerRadius, color, 0.8F, 8, 0.1F, false - ); + DebugUtils.addDisc(world, leaderPos.x, leaderMidY, leaderPos.z, leaderRingOuterRadius, leaderRingInnerRadius, color, 0.8F, 8, 0.1F, 0); + DebugUtils.addDisc(world, leaderPos.x, leaderMidY, leaderPos.z, leaderOuterRingRadius, leaderOuterRingInnerRadius, color, 0.8F, 8, 0.1F, 0); for (Ref memberRef : entityGroup.getMemberList()) { if (memberRef.isValid() && !memberRef.equals(leaderRef)) { @@ -178,7 +176,7 @@ public class FlockSystems { double memberRingOuterRadius = memberWidth / 2.0 + 0.3F; double memberRingInnerRadius = memberRingOuterRadius - 0.08F; DebugUtils.addDisc( - world, memberPos.x, memberMidY, memberPos.z, memberRingOuterRadius, memberRingInnerRadius, color, 0.8F, 8, 0.1F, false + world, memberPos.x, memberMidY, memberPos.z, memberRingOuterRadius, memberRingInnerRadius, color, 0.8F, 8, 0.1F, 0 ); renderConnectingLine( world, @@ -217,7 +215,7 @@ public class FlockSystems { double dirZ = leaderZ - memberZ; double horizontalDist = Math.sqrt(dirX * dirX + dirZ * dirZ); if (horizontalDist < 0.001) { - DebugUtils.addLine(world, memberX, memberY, memberZ, leaderX, leaderY, leaderZ, color, 0.04F, 0.1F, false); + DebugUtils.addLine(world, memberX, memberY, memberZ, leaderX, leaderY, leaderZ, color, 0.04F, 0.1F, 0); } else { double hDirX = dirX / horizontalDist; double hDirZ = dirZ / horizontalDist; @@ -225,7 +223,7 @@ public class FlockSystems { double startZ = memberZ + hDirZ * memberRadius; double endX = leaderX - hDirX * leaderRadius; double endZ = leaderZ - hDirZ * leaderRadius; - DebugUtils.addLine(world, startX, memberY, startZ, endX, leaderY, endZ, color, 0.04F, 0.1F, false); + DebugUtils.addLine(world, startX, memberY, startZ, endX, leaderY, endZ, color, 0.04F, 0.1F, 0); } } } diff --git a/src/com/hypixel/hytale/server/flock/commands/NPCFlockCommand.java b/src/com/hypixel/hytale/server/flock/commands/NPCFlockCommand.java index 42c7aa6b..65c44eef 100644 --- a/src/com/hypixel/hytale/server/flock/commands/NPCFlockCommand.java +++ b/src/com/hypixel/hytale/server/flock/commands/NPCFlockCommand.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; @@ -21,10 +20,11 @@ import com.hypixel.hytale.server.flock.FlockPlugin; import com.hypixel.hytale.server.npc.NPCPlugin; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import java.util.function.BiPredicate; import java.util.function.Predicate; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NPCFlockCommand extends AbstractCommandCollection { private static final double ENTITY_IN_VIEW_DISTANCE = 8.0; @@ -52,13 +52,13 @@ public class NPCFlockCommand extends AbstractCommandCollection { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); - float lookYaw = headRotation.getYaw(); - double x = position.getX(); - double y = position.getY(); - double z = position.getZ(); + Rotation3f headRotation = headRotationComponent.getRotation(); + float lookYaw = headRotation.yaw(); + double x = position.x(); + double y = position.y(); + double z = position.z(); SpatialResource, EntityStore> spatialResource = store.getResource(NPCPlugin.get().getNpcSpatialResource()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialResource.getSpatialStructure().collect(position, 8.0, results); ComponentType npcComponentType = NPCEntity.getComponentType(); @@ -76,8 +76,8 @@ public class NPCFlockCommand extends AbstractCommandCollection { assert entityTransformComponent != null; Vector3d entityPosition = entityTransformComponent.getPosition(); - if (Math.abs(entityPosition.getY() - y) < 2.0 - && NPCPhysicsMath.inViewSector(x, z, lookYaw, (float) (Math.PI / 6), entityPosition.getX(), entityPosition.getZ()) + if (Math.abs(entityPosition.y() - y) < 2.0 + && NPCPhysicsMath.inViewSector(x, z, lookYaw, (float) (Math.PI / 6), entityPosition.x(), entityPosition.z()) && predicate.test(targetRef, targetNpcComponent)) { count++; } @@ -99,13 +99,13 @@ public class NPCFlockCommand extends AbstractCommandCollection { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); - float lookYaw = headRotation.getYaw(); - double x = position.getX(); - double y = position.getY(); - double z = position.getZ(); + Rotation3f headRotation = headRotationComponent.getRotation(); + float lookYaw = headRotation.yaw(); + double x = position.x(); + double y = position.y(); + double z = position.z(); SpatialResource, EntityStore> spatialResource = store.getResource(NPCPlugin.get().getNpcSpatialResource()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialResource.getSpatialStructure().ordered(position, 8.0, results); for (Ref entityRef : results) { @@ -114,8 +114,8 @@ public class NPCFlockCommand extends AbstractCommandCollection { assert entityTransformComponent != null; Vector3d entityPosition = entityTransformComponent.getPosition(); - if (Math.abs(entityPosition.getY() - y) < 2.0 - && NPCPhysicsMath.inViewSector(x, z, lookYaw, (float) (Math.PI / 6), entityPosition.getX(), entityPosition.getZ()) + if (Math.abs(entityPosition.y() - y) < 2.0 + && NPCPhysicsMath.inViewSector(x, z, lookYaw, (float) (Math.PI / 6), entityPosition.x(), entityPosition.z()) && predicate.test(entityRef)) { return true; } diff --git a/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockBeacon.java b/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockBeacon.java index 98f5c522..f36094d2 100644 --- a/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockBeacon.java +++ b/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockBeacon.java @@ -36,7 +36,7 @@ public class ActionFlockBeacon extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return !super.canExecute(ref, role, sensorInfo, dt, store) ? false : store.getArchetype(ref).contains(FLOCK_MEMBERSHIP_COMPONENT_TYPE) @@ -44,7 +44,7 @@ public class ActionFlockBeacon extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); FlockMembership flockMembership = store.getComponent(ref, FLOCK_MEMBERSHIP_COMPONENT_TYPE); Ref flockReference = flockMembership != null ? flockMembership.getFlockRef() : null; diff --git a/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockLeave.java b/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockLeave.java index 3f3918d6..4dacf55d 100644 --- a/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockLeave.java +++ b/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockLeave.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.npc.corecomponents.ActionBase; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionFlockLeave extends ActionBase { public ActionFlockLeave(@Nonnull BuilderActionFlockLeave builderActionFlockLeave) { @@ -17,12 +18,12 @@ public class ActionFlockLeave extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && FlockPlugin.isFlockMember(ref, store); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); store.tryRemoveComponent(ref, FlockMembership.getComponentType()); return true; diff --git a/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockState.java b/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockState.java index 169caff5..ab5fb5ff 100644 --- a/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockState.java +++ b/src/com/hypixel/hytale/server/flock/corecomponents/ActionFlockState.java @@ -24,7 +24,7 @@ public class ActionFlockState extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); role.getStateSupport().flockSetState(ref, this.state, this.subState, store); return true; diff --git a/src/com/hypixel/hytale/server/flock/corecomponents/BodyMotionFlock.java b/src/com/hypixel/hytale/server/flock/corecomponents/BodyMotionFlock.java index 93d59fd7..802a761a 100644 --- a/src/com/hypixel/hytale/server/flock/corecomponents/BodyMotionFlock.java +++ b/src/com/hypixel/hytale/server/flock/corecomponents/BodyMotionFlock.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.flock.corecomponents; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.group.EntityGroup; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; @@ -17,6 +16,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionFlock extends BodyMotionBase { private static final ComponentType FLOCK_MEMBERSHIP_COMPONENT_TYPE = FlockMembership.getComponentType(); @@ -66,37 +66,37 @@ public class BodyMotionFlock extends BodyMotionBase { assert leaderTransformComponent != null; Vector3d position = leaderTransformComponent.getPosition(); - Vector3d toLeader = new Vector3d(position.getX(), position.getY(), position.getZ()); - if (sumOfVelocities.squaredLength() > 1.0E-4) { - desiredSteering.setYaw(PhysicsMath.headingFromDirection(sumOfVelocities.getX(), sumOfVelocities.getZ())); + Vector3d toLeader = new Vector3d(position.x(), position.y(), position.z()); + if (sumOfVelocities.lengthSquared() > 1.0E-4) { + desiredSteering.setYaw(PhysicsMath.headingFromDirection(sumOfVelocities.x(), sumOfVelocities.z())); } else { TransformComponent parentEntityTransformComponent = componentAccessor.getComponent(ref, TRANSFORM_COMPONENT_TYPE); assert parentEntityTransformComponent != null; - desiredSteering.setYaw(parentEntityTransformComponent.getRotation().getYaw()); + desiredSteering.setYaw(parentEntityTransformComponent.getRotation().yaw()); } - sumOfPositions.subtract(position.getX(), position.getY(), position.getZ()).scale(componentSelector); - if (sumOfPositions.squaredLength() > 1.0E-4) { - sumOfPositions.normalize().scale(weightCohesion); + sumOfPositions.sub(position.x(), position.y(), position.z()).mul(componentSelector); + if (sumOfPositions.lengthSquared() > 1.0E-4) { + sumOfPositions.normalize().mul(weightCohesion); } else { - sumOfPositions.assign(0.0); + sumOfPositions.zero(); } - if (sumOfDistances.squaredLength() > 1.0E-4) { - sumOfDistances.normalize().scale(-weightSeparation); + if (sumOfDistances.lengthSquared() > 1.0E-4) { + sumOfDistances.normalize().mul(-weightSeparation); } else { - sumOfDistances.assign(0.0); + sumOfDistances.zero(); } - toLeader.subtract(position.getX(), position.getY(), position.getZ()).scale(componentSelector); - toLeader.normalize().scale(0.5); + toLeader.sub(position.x(), position.y(), position.z()).mul(componentSelector); + toLeader.normalize().mul(0.5); sumOfPositions.add(sumOfDistances).add(toLeader); - if (sumOfPositions.squaredLength() > 1.0E-4) { + if (sumOfPositions.lengthSquared() > 1.0E-4) { sumOfPositions.normalize(); } else { - sumOfPositions.assign(0.0); + sumOfPositions.zero(); } desiredSteering.setTranslation(sumOfPositions); diff --git a/src/com/hypixel/hytale/server/flock/decisionmaker/conditions/FlockSizeCondition.java b/src/com/hypixel/hytale/server/flock/decisionmaker/conditions/FlockSizeCondition.java index e760d416..ad1fe1db 100644 --- a/src/com/hypixel/hytale/server/flock/decisionmaker/conditions/FlockSizeCondition.java +++ b/src/com/hypixel/hytale/server/flock/decisionmaker/conditions/FlockSizeCondition.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.flock.FlockMembership; import com.hypixel.hytale.server.npc.decisionmaker.core.EvaluationContext; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.base.ScaledCurveCondition; -import com.hypixel.hytale.server.npc.entities.NPCEntity; import javax.annotation.Nonnull; public class FlockSizeCondition extends ScaledCurveCondition { @@ -29,7 +28,6 @@ public class FlockSizeCondition extends ScaledCurveCondition { @Nonnull CommandBuffer commandBuffer, EvaluationContext context ) { - NPCEntity self = archetypeChunk.getComponent(selfIndex, NPCEntity.getComponentType()); FlockMembership membership = archetypeChunk.getComponent(selfIndex, FlockMembership.getComponentType()); if (membership == null) { return 1.0; diff --git a/src/com/hypixel/hytale/server/migrations/RenameSpawnMarkerMigration.java b/src/com/hypixel/hytale/server/migrations/RenameSpawnMarkerMigration.java deleted file mode 100644 index f4550bae..00000000 --- a/src/com/hypixel/hytale/server/migrations/RenameSpawnMarkerMigration.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.hypixel.hytale.server.migrations; - -import com.hypixel.hytale.assetstore.AssetValidationResults; -import com.hypixel.hytale.codec.ExtraInfo; -import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.server.core.modules.migrations.EntityMigration; -import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker; -import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity; -import com.hypixel.hytale.sneakythrow.SneakyThrow; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import javax.annotation.Nonnull; - -public class RenameSpawnMarkerMigration extends EntityMigration { - @Nonnull - public static HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - @Nonnull - private Map idMigrations = new HashMap<>(); - - public RenameSpawnMarkerMigration(@Nonnull Path filePath) { - super(SpawnMarkerEntity.class, version -> { - ExtraInfo extraInfo = new ExtraInfo(version, AssetValidationResults::new); - ((AssetValidationResults)extraInfo.getValidationResults()).disableMissingAssetFor(SpawnMarker.class); - return extraInfo; - }); - - List lines; - try { - lines = Files.readAllLines(filePath); - } catch (IOException var8) { - throw SneakyThrow.sneakyThrow(var8); - } - - for (String line : lines) { - String[] split = line.split(":"); - if (split.length == 2) { - String spawnMarkerMigrationId = split[1]; - SpawnMarker spawnMarker = SpawnMarker.getAssetMap().getAsset(spawnMarkerMigrationId); - if (spawnMarker == null) { - LOGGER.at(Level.WARNING).log("SpawnMarker '%s' does not exist!", spawnMarkerMigrationId); - } else { - this.idMigrations.put(split[0], spawnMarker); - } - } - } - } - - protected boolean migrate(@Nonnull SpawnMarkerEntity entity) { - String spawnMarkerId = entity.getSpawnMarkerId(); - SpawnMarker spawnMarker = this.idMigrations.get(spawnMarkerId); - if (spawnMarker == null) { - return false; - } else { - entity.setSpawnMarker(spawnMarker); - return true; - } - } -} diff --git a/src/com/hypixel/hytale/server/npc/NPCPlugin.java b/src/com/hypixel/hytale/server/npc/NPCPlugin.java index 76dbeb29..32f0ee07 100644 --- a/src/com/hypixel/hytale/server/npc/NPCPlugin.java +++ b/src/com/hypixel/hytale/server/npc/NPCPlugin.java @@ -34,16 +34,16 @@ import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.function.consumer.TriConsumer; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.logger.sentry.SkipSentryException; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.Options; import com.hypixel.hytale.server.core.asset.AssetModule; import com.hypixel.hytale.server.core.asset.AssetPackRegisterEvent; import com.hypixel.hytale.server.core.asset.AssetPackUnregisterEvent; -import com.hypixel.hytale.server.core.asset.GenerateSchemaEvent; import com.hypixel.hytale.server.core.asset.HytaleAssetStore; import com.hypixel.hytale.server.core.asset.LoadAssetEvent; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; @@ -60,10 +60,10 @@ import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsSystems; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; -import com.hypixel.hytale.server.core.modules.migrations.MigrationModule; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; +import com.hypixel.hytale.server.core.schema.SchemaGenerator; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.npc.INonPlayerCharacter; import com.hypixel.hytale.server.core.universe.world.path.WorldPathChangedEvent; @@ -71,7 +71,6 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.Config; import com.hypixel.hytale.server.flock.FlockPlugin; import com.hypixel.hytale.server.flock.config.FlockAsset; -import com.hypixel.hytale.server.migrations.RenameSpawnMarkerMigration; import com.hypixel.hytale.server.npc.asset.builder.Builder; import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptor; import com.hypixel.hytale.server.npc.asset.builder.BuilderFactory; @@ -136,6 +135,7 @@ import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.Buil import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterAnd; import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterAttitude; import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterCombat; +import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterEntityEffect; import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterHeightDifference; import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterInsideBlock; import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterInventory; @@ -321,6 +321,7 @@ import java.util.function.Supplier; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3dc; public class NPCPlugin extends JavaPlugin { @Nonnull @@ -363,7 +364,7 @@ public class NPCPlugin extends JavaPlugin { protected boolean autoReload; private AttitudeMap attitudeMap; private ItemAttitudeMap itemAttitudeMap; - private static final Vector3f NULL_ROTATION = new Vector3f(0.0F, 0.0F, 0.0F); + private static final Rotation3f NULL_ROTATION = new Rotation3f(0.0F, 0.0F, 0.0F); public static final short PRIORITY_LOAD_NPC = -8; public static final short PRIORITY_SPAWN_VALIDATION = -7; private final Config config = this.withConfig("NPCModule", NPCPlugin.NPCConfig.CODEC); @@ -441,7 +442,15 @@ public class NPCPlugin extends JavaPlugin { ); eventRegistry.register(AssetPackRegisterEvent.class, event -> this.builderManager.loadBuilders(event.getAssetPack(), false)); eventRegistry.register(AssetPackUnregisterEvent.class, event -> this.builderManager.unloadBuilders(event.getAssetPack())); - eventRegistry.register(GenerateSchemaEvent.class, this::onSchemaGenerate); + SchemaGenerator.registerAssetSchema("NPCRole.json", ctx -> { + Schema schema = this.builderManager.generateSchema(ctx); + schema.setId("NPCRole.json"); + schema.setTitle("NPCRole"); + Schema.HytaleMetadata hytale = schema.getHytale(); + hytale.setPath("NPC/Roles"); + hytale.setExtension(".json"); + return schema; + }, List.of("NPC/Roles/*.json", "NPC/Roles/**/*.json"), null); AssetRegistry.register( ((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder( AttitudeGroup.class, new IndexedLookupTableAssetMap<>(AttitudeGroup[]::new) @@ -482,7 +491,7 @@ public class NPCPlugin extends JavaPlugin { .setCodec(Condition.CODEC)) .setKeyFunction(Condition::getId)) .setReplaceOnRemove(Condition::getAlwaysTrueFor)) - .loadsAfter(ResponseCurve.class, NPCGroup.class, EntityStatType.class)) + .loadsAfter(ResponseCurve.class, NPCGroup.class, EntityStatType.class, EntityEffect.class)) .build() ); this.getEntityRegistry().registerEntity("NPC", NPCEntity.class, NPCEntity::new, NPCEntity.CODEC); @@ -491,7 +500,6 @@ public class NPCPlugin extends JavaPlugin { Interaction.CODEC.register("SpawnNPC", SpawnNPCInteraction.class, SpawnNPCInteraction.CODEC); Interaction.getAssetStore().loadAssets("Hytale:Hytale", List.of(new UseNPCInteraction("*UseNPC"))); RootInteraction.getAssetStore().loadAssets("Hytale:Hytale", List.of(UseNPCInteraction.DEFAULT_ROOT)); - MigrationModule.get().register("spawnMarkers", RenameSpawnMarkerMigration::new); this.setupNPCLoading(); this.blackboardResourceType = entityStoreRegistry.registerResource(Blackboard.class, Blackboard::new); this.combatDataPoolResourceType = entityStoreRegistry.registerResource(CombatViewSystems.CombatDataPool.class, CombatViewSystems.CombatDataPool::new); @@ -531,6 +539,7 @@ public class NPCPlugin extends JavaPlugin { entityStoreRegistry.registerSystem(new CombatViewSystems.EntityRemoved(this.combatDataComponentType, this.combatDataPoolResourceType)); entityStoreRegistry.registerSystem(new CombatViewSystems.Ticking(this.combatDataComponentType, this.combatDataPoolResourceType)); entityStoreRegistry.registerSystem(new NPCSystems.ModelChangeSystem()); + entityStoreRegistry.registerSystem(new NPCSystems.OnNPCAdded()); entityStoreRegistry.registerSystem(new RoleBuilderSystem()); entityStoreRegistry.registerSystem(new BalancingInitialisationSystem()); entityStoreRegistry.registerSystem(new RoleSystems.RoleActivateSystem(npcComponentType)); @@ -592,18 +601,7 @@ public class NPCPlugin extends JavaPlugin { entityStoreRegistry.registerSystem(new NPCSystems.KillFeedDecedentEventSystem()); entityStoreRegistry.registerSystem(new NPCSystems.PrefabPlaceEntityEventSystem()); entityStoreRegistry.registerSystem(new NPCVelocityInstructionSystem()); - this.getEntityStoreRegistry().registerSystem(new NPCPlugin.NPCEntityRegenerateStatsSystem()); - } - - public void onSchemaGenerate(@Nonnull GenerateSchemaEvent event) { - Schema schema = this.builderManager.generateSchema(event.getContext()); - event.addSchema("NPCRole.json", schema); - event.addSchemaLink("NPCRole", List.of("NPC/Roles/*.json", "NPC/Roles/**/*.json"), null); - Schema.HytaleMetadata hytale = schema.getHytale(); - hytale.setPath("NPC/Roles"); - hytale.setExtension(".json"); - schema.setId("NPCRole.json"); - schema.setTitle("NPCRole"); + entityStoreRegistry.registerSystem(new NPCPlugin.NPCEntityRegenerateStatsSystem()); } @Override @@ -844,7 +842,8 @@ public class NPCPlugin extends JavaPlugin { .registerCoreComponentType("And", BuilderEntityFilterAnd::new) .registerCoreComponentType("Or", BuilderEntityFilterOr::new) .registerCoreComponentType("Altitude", BuilderEntityFilterAltitude::new) - .registerCoreComponentType("InsideBlock", BuilderEntityFilterInsideBlock::new); + .registerCoreComponentType("InsideBlock", BuilderEntityFilterInsideBlock::new) + .registerCoreComponentType("EntityEffect", BuilderEntityFilterEntityEffect::new); this.registerCoreComponentType("Attitude", BuilderSensorEntityPrioritiserAttitude::new); NPCPlugin.NPCConfig config = this.config.get(); this.autoReload = config.isAutoReload(); @@ -860,7 +859,7 @@ public class NPCPlugin extends JavaPlugin { @Nullable public Pair, INonPlayerCharacter> spawnNPC( - @Nonnull Store store, @Nonnull String npcType, @Nullable String groupType, @Nonnull Vector3d position, @Nonnull Vector3f rotation + @Nonnull Store store, @Nonnull String npcType, @Nullable String groupType, @Nonnull Vector3dc position, @Nonnull Rotation3fc rotation ) { int roleIndex = this.getIndex(npcType); if (roleIndex < 0) { @@ -1088,8 +1087,8 @@ public class NPCPlugin extends JavaPlugin { public Pair, NPCEntity> spawnEntity( @Nonnull Store store, int roleIndex, - @Nonnull Vector3d position, - @Nullable Vector3f rotation, + @Nonnull Vector3dc position, + @Nullable Rotation3fc rotation, @Nullable Model spawnModel, @Nullable TriConsumer, Store> postSpawn ) { @@ -1100,8 +1099,8 @@ public class NPCPlugin extends JavaPlugin { public Pair, NPCEntity> spawnEntity( @Nonnull Store store, int roleIndex, - @Nonnull Vector3d position, - @Nullable Vector3f rotation, + @Nonnull Vector3dc position, + @Nullable Rotation3fc rotation, @Nullable Model spawnModel, @Nullable TriConsumer, Store> preAddToWorld, @Nullable TriConsumer, Store> postSpawn @@ -1224,12 +1223,12 @@ public class NPCPlugin extends JavaPlugin { ); } - protected void generateDescriptors() { + public void generateDescriptors() { this.getLogger().at(Level.INFO).log("===== Generating descriptors for NPC!"); this.builderDescriptors = this.builderManager.generateDescriptors(); } - protected void saveDescriptors() { + public void saveDescriptors() { this.getLogger().at(Level.INFO).log("===== Saving descriptors for NPC!"); Path path = Path.of("npc_descriptors.json"); BuilderManager.saveDescriptors(this.builderDescriptors, path); diff --git a/src/com/hypixel/hytale/server/npc/asset/builder/BuilderBase.java b/src/com/hypixel/hytale/server/npc/asset/builder/BuilderBase.java index edb3fa20..e320d4fe 100644 --- a/src/com/hypixel/hytale/server/npc/asset/builder/BuilderBase.java +++ b/src/com/hypixel/hytale/server/npc/asset/builder/BuilderBase.java @@ -18,7 +18,6 @@ import com.hypixel.hytale.function.consumer.BooleanConsumer; import com.hypixel.hytale.function.consumer.FloatConsumer; import com.hypixel.hytale.function.consumer.TriConsumer; import com.hypixel.hytale.logger.sentry.SkipSentryException; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.npc.NPCPlugin; import com.hypixel.hytale.server.npc.asset.builder.expression.BuilderExpressionDynamic; import com.hypixel.hytale.server.npc.asset.builder.holder.AssetArrayHolder; @@ -110,6 +109,7 @@ import java.util.logging.Level; import java.util.regex.Pattern; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class BuilderBase implements Builder { private static final Pattern PATTERN = Pattern.compile("\\s*,\\s*"); @@ -3502,6 +3502,28 @@ public abstract class BuilderBase implements Builder { setter.accept(assetList); } + public boolean getOverride( + @Nonnull JsonElement data, + String name, + @Nonnull Consumer setter, + BuilderDescriptorState state, + String shortDescription, + @Nullable String longDescription + ) { + return this.getEnum(data, name, setter, FeatureOverride.class, FeatureOverride.Default, state, shortDescription, longDescription); + } + + public boolean getOverride( + @Nonnull JsonElement data, + String name, + @Nonnull EnumHolder enumHolder, + BuilderDescriptorState state, + String shortDescription, + @Nullable String longDescription + ) { + return this.getEnum(data, name, enumHolder, FeatureOverride.class, FeatureOverride.Default, state, shortDescription, longDescription); + } + protected BuilderDescriptor createDescriptor( @Nonnull Builder builder, String builderName, diff --git a/src/com/hypixel/hytale/server/npc/asset/builder/BuilderManager.java b/src/com/hypixel/hytale/server/npc/asset/builder/BuilderManager.java index 46444888..f5db5cff 100644 --- a/src/com/hypixel/hytale/server/npc/asset/builder/BuilderManager.java +++ b/src/com/hypixel/hytale/server/npc/asset/builder/BuilderManager.java @@ -230,6 +230,10 @@ public class BuilderManager { if (Files.isDirectory(path)) { try { Files.walkFileTree(path, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor() { + { + Objects.requireNonNull(BuilderManager.this); + } + @Nonnull public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) { if (BuilderManager.isJsonFile(file) && !BuilderManager.isIgnoredFile(file)) { @@ -263,6 +267,10 @@ public class BuilderManager { final ObjectArrayList errors = new ObjectArrayList<>(); if (Files.isDirectory(path)) { Files.walkFileTree(path, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor() { + { + Objects.requireNonNull(BuilderManager.this); + } + @Nonnull public FileVisitResult visitFile(@Nonnull Path file, @Nonnull BasicFileAttributes attrs) { if (BuilderManager.isJsonFile(file) && !BuilderManager.isIgnoredFile(file)) { @@ -1160,6 +1168,8 @@ public class BuilderManager { private final boolean includeTests; public BuilderAssetMonitorHandler(AssetPack pack, boolean includeTests) { + Objects.requireNonNull(BuilderManager.this); + super(); this.pack = pack; this.includeTests = includeTests; } diff --git a/src/com/hypixel/hytale/server/npc/asset/builder/BuilderSupport.java b/src/com/hypixel/hytale/server/npc/asset/builder/BuilderSupport.java index 99ee7f04..3b3bf33d 100644 --- a/src/com/hypixel/hytale/server/npc/asset/builder/BuilderSupport.java +++ b/src/com/hypixel/hytale/server/npc/asset/builder/BuilderSupport.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.asset.builder; import com.hypixel.hytale.common.thread.ticking.Tickable; import com.hypixel.hytale.component.Holder; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.blockset.config.BlockSet; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.blackboard.view.event.block.BlockEventType; @@ -41,6 +40,7 @@ import java.util.Objects; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BuilderSupport { private final BuilderManager builderManager; diff --git a/src/com/hypixel/hytale/server/npc/asset/builder/FeatureOverride.java b/src/com/hypixel/hytale/server/npc/asset/builder/FeatureOverride.java new file mode 100644 index 00000000..157e22c1 --- /dev/null +++ b/src/com/hypixel/hytale/server/npc/asset/builder/FeatureOverride.java @@ -0,0 +1,27 @@ +package com.hypixel.hytale.server.npc.asset.builder; + +import java.util.function.Supplier; + +public enum FeatureOverride implements Supplier { + On("Feature always enabled"), + Off("Feature always disabled"), + Default("Default behaviour"); + + private final String description; + + private FeatureOverride(String description) { + this.description = description; + } + + public String get() { + return this.description; + } + + public boolean evaluate(boolean defaultValue) { + return switch (this) { + case On -> true; + case Off -> false; + case Default -> defaultValue; + }; + } +} diff --git a/src/com/hypixel/hytale/server/npc/asset/builder/validators/SubTypeTypeAdapterFactory.java b/src/com/hypixel/hytale/server/npc/asset/builder/validators/SubTypeTypeAdapterFactory.java index e378ba08..d29ecd18 100644 --- a/src/com/hypixel/hytale/server/npc/asset/builder/validators/SubTypeTypeAdapterFactory.java +++ b/src/com/hypixel/hytale/server/npc/asset/builder/validators/SubTypeTypeAdapterFactory.java @@ -11,6 +11,7 @@ import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Map.Entry; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -52,6 +53,10 @@ public class SubTypeTypeAdapterFactory implements TypeAdapterFactory { this.classToName .forEach((aClass, name) -> delegateMap.put((Class)aClass, Map.entry(name, gson.getDelegateAdapter(this, TypeToken.get((Class)aClass))))); return (new TypeAdapter() { + { + Objects.requireNonNull(SubTypeTypeAdapterFactory.this); + } + @Override public void write(JsonWriter out, @Nonnull T value) throws IOException { Entry> entry = delegateMap.get(value.getClass()); diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/BlockRegionView.java b/src/com/hypixel/hytale/server/npc/blackboard/view/BlockRegionView.java index 07dd084b..be6b2cd2 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/BlockRegionView.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/BlockRegionView.java @@ -2,8 +2,8 @@ package com.hypixel.hytale.server.npc.blackboard.view; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class BlockRegionView> implements IBlackboardView { public static final int BITS = 7; @@ -44,8 +44,8 @@ public abstract class BlockRegionView } public static long indexViewFromWorldPosition(@Nonnull Vector3d pos) { - int blackboardX = toRegionalBlackboardCoordinate(MathUtil.floor(pos.getX())); - int blackboardZ = toRegionalBlackboardCoordinate(MathUtil.floor(pos.getZ())); + int blackboardX = toRegionalBlackboardCoordinate(MathUtil.floor(pos.x())); + int blackboardZ = toRegionalBlackboardCoordinate(MathUtil.floor(pos.z())); return indexView(blackboardX, blackboardZ); } diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/BlockRegionViewManager.java b/src/com/hypixel/hytale/server/npc/blackboard/view/BlockRegionViewManager.java index 68809a6d..254788e4 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/BlockRegionViewManager.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/BlockRegionViewManager.java @@ -4,13 +4,13 @@ import com.hypixel.fastutil.longs.Long2ObjectConcurrentHashMap; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.blackboard.Blackboard; import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; import java.util.function.Consumer; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class BlockRegionViewManager> implements IBlackboardViewManager { @Nonnull diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/IBlackboardViewManager.java b/src/com/hypixel/hytale/server/npc/blackboard/view/IBlackboardViewManager.java index 17a87682..96ce55da 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/IBlackboardViewManager.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/IBlackboardViewManager.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.server.npc.blackboard.view; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.blackboard.Blackboard; import java.util.function.Consumer; +import org.joml.Vector3d; public interface IBlackboardViewManager> { View get(Ref var1, Blackboard var2, ComponentAccessor var3); diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/SingletonBlackboardViewManager.java b/src/com/hypixel/hytale/server/npc/blackboard/view/SingletonBlackboardViewManager.java index 310b7e35..8195c9d6 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/SingletonBlackboardViewManager.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/SingletonBlackboardViewManager.java @@ -2,11 +2,11 @@ package com.hypixel.hytale.server.npc.blackboard.view; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.blackboard.Blackboard; import java.util.function.Consumer; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SingletonBlackboardViewManager> implements IBlackboardViewManager { private final View view; diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/blocktype/BlockPositionEntryGenerator.java b/src/com/hypixel/hytale/server/npc/blackboard/view/blocktype/BlockPositionEntryGenerator.java index 930a1453..7dcc610b 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/blocktype/BlockPositionEntryGenerator.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/blocktype/BlockPositionEntryGenerator.java @@ -14,8 +14,6 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntConsumer; import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.BitSet; import java.util.List; @@ -24,7 +22,6 @@ import javax.annotation.Nullable; public class BlockPositionEntryGenerator { private final BlockPositionEntryGenerator.FoundBlockConsumer foundBlockConsumer = new BlockPositionEntryGenerator.FoundBlockConsumer(); - private final IntSet internalIdHolder = new IntOpenHashSet(); @Nonnull public BlockPositionProvider generate( @@ -38,8 +35,7 @@ public class BlockPositionEntryGenerator { } else { ChunkSectionReference chunkSectionPointer = new ChunkSectionReference(chunk, section, sectionIndex); this.foundBlockConsumer.init(chunkSectionPointer, searchedBlockSets); - section.find(unifiedBlocksOfInterest, this.internalIdHolder, this.foundBlockConsumer); - this.internalIdHolder.clear(); + section.find(unifiedBlocksOfInterest, this.foundBlockConsumer); Int2ObjectOpenHashMap> blockData = this.foundBlockConsumer.getBlockData(); this.foundBlockConsumer.release(); return new BlockPositionProvider(searchedBlockSets, blockData, changeCounter); diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/blocktype/BlockTypeView.java b/src/com/hypixel/hytale/server/npc/blackboard/view/blocktype/BlockTypeView.java index c4037d9a..ed13fdde 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/blocktype/BlockTypeView.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/blocktype/BlockTypeView.java @@ -7,7 +7,6 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.blockset.BlockSetModule; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; @@ -30,20 +29,21 @@ import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import java.util.BitSet; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.BiPredicate; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BlockTypeView extends BlockRegionView { private final long index; private final Blackboard blackboard; private final BitSet allBlockSets = new BitSet(); - private final Set> entities = new HashSet<>(); + private final Set> entities = new ReferenceOpenHashSet<>(); private final IntArrayList blockSetAggregate = new IntArrayList(); private final IntArrayList crossViewBlockSetAggregate = new IntArrayList(); private boolean aggregateNeedsRebuild; @@ -317,7 +317,7 @@ public class BlockTypeView extends BlockRegionView { for (int i = 0; i < this.foundBlocks.size(); i++) { IBlockPositionData block = this.foundBlocks.get(i); - double dist2 = entityPos.distanceSquaredTo(block.getXCentre(), block.getYCentre(), block.getZCentre()); + double dist2 = entityPos.distanceSquared(block.getXCentre(), block.getYCentre(), block.getZCentre()); if (dist2 < minDist2) { minDist2 = dist2; data = block; diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/event/EventNotification.java b/src/com/hypixel/hytale/server/npc/blackboard/view/event/EventNotification.java index 062ae159..d4311906 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/event/EventNotification.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/event/EventNotification.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.server.npc.blackboard.view.event; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EventNotification { private final Vector3d position = new Vector3d(); @@ -16,7 +16,7 @@ public class EventNotification { } public void setPosition(double x, double y, double z) { - this.position.assign(x, y, z); + this.position.set(x, y, z); } public Ref getInitiator() { diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/event/EventTypeRegistration.java b/src/com/hypixel/hytale/server/npc/blackboard/view/event/EventTypeRegistration.java index 89295b55..21afb35a 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/event/EventTypeRegistration.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/event/EventTypeRegistration.java @@ -12,7 +12,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.BitSet; import java.util.List; import java.util.function.Consumer; @@ -40,7 +40,7 @@ public class EventTypeRegistration, Notificati while (it.hasNext()) { int set = it.nextInt(); this.eventSets.set(set); - this.entitiesBySet.computeIfAbsent(set, k -> new ObjectArrayList<>()).add(ref); + this.entitiesBySet.computeIfAbsent(set, k -> new ReferenceArrayList<>()).add(ref); } } diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/event/block/BlockEventView.java b/src/com/hypixel/hytale/server/npc/blackboard/view/event/block/BlockEventView.java index 41b4d694..7dedf2d6 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/event/block/BlockEventView.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/event/block/BlockEventView.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.blackboard.view.event.block; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.npc.blackboard.view.event.EventView; import com.hypixel.hytale.server.npc.entities.NPCEntity; import it.unimi.dsi.fastutil.ints.IntSet; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class BlockEventView extends EventView { public BlockEventView(@Nonnull World world) { diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/event/entity/EntityEventView.java b/src/com/hypixel/hytale/server/npc/blackboard/view/event/entity/EntityEventView.java index 93147145..599c0db8 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/event/entity/EntityEventView.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/event/entity/EntityEventView.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.builtin.tagset.config.NPCGroup; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.entity.Entity; @@ -24,6 +23,7 @@ import com.hypixel.hytale.server.npc.blackboard.view.event.EventView; import com.hypixel.hytale.server.npc.entities.NPCEntity; import it.unimi.dsi.fastutil.ints.IntSet; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EntityEventView extends EventView { public EntityEventView(@Nonnull World world) { diff --git a/src/com/hypixel/hytale/server/npc/blackboard/view/resource/ResourceView.java b/src/com/hypixel/hytale/server/npc/blackboard/view/resource/ResourceView.java index a3b12b21..a2645fe7 100644 --- a/src/com/hypixel/hytale/server/npc/blackboard/view/resource/ResourceView.java +++ b/src/com/hypixel/hytale/server/npc/blackboard/view/resource/ResourceView.java @@ -8,14 +8,14 @@ import com.hypixel.hytale.server.npc.blackboard.view.BlockRegionView; import com.hypixel.hytale.server.npc.entities.NPCEntity; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import java.util.HashMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import java.util.Map; import javax.annotation.Nonnull; public class ResourceView extends BlockRegionView { private final long index; private final IntSet[] reservationsBySection = new IntSet[10]; - private final Map, ResourceView.BlockReservation> reservationsByEntity = new HashMap<>(); + private final Map, ResourceView.BlockReservation> reservationsByEntity = new Reference2ObjectOpenHashMap<>(); public ResourceView(long index) { this.index = index; diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCAllCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCAllCommand.java index 295590b4..dce53d0b 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCAllCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCAllCommand.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -23,6 +22,7 @@ import it.unimi.dsi.fastutil.Pair; import java.util.List; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NPCAllCommand extends AbstractPlayerCommand { @Nonnull @@ -54,8 +54,8 @@ public class NPCAllCommand extends AbstractPlayerCommand { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - double px = position.getX() - squareSideLengthHalf; - double pz = position.getZ() - squareSideLengthHalf; + double px = position.x() - squareSideLengthHalf; + double pz = position.z() - squareSideLengthHalf; Vector3d pos = new Vector3d(); for (int index = 0; index < roles.size(); index++) { @@ -66,19 +66,21 @@ public class NPCAllCommand extends AbstractPlayerCommand { double z = pz + distance * (index / columns); double y = NPCPhysicsMath.heightOverGround(world, x, z); if (!(y < 0.0)) { - pos.assign(x, y, z); + pos.set(x, y, z); int roleIndex = npcModule.getIndex(name); if (roleIndex < 0) { throw new IllegalStateException("No such valid role: " + name); } Pair, NPCEntity> npcPair = npcModule.spawnEntity(store, roleIndex, pos, null, null, null); - Ref npcRef = npcPair.first(); + if (npcPair != null) { + Ref npcRef = npcPair.first(); - assert npcRef != null; + assert npcRef != null; - store.putComponent(npcRef, Nameplate.getComponentType(), new Nameplate(name)); - store.ensureComponent(npcRef, Frozen.getComponentType()); + store.putComponent(npcRef, Nameplate.getComponentType(), new Nameplate(name)); + store.ensureComponent(npcRef, Frozen.getComponentType()); + } } } catch (Throwable var33) { playerRef.sendMessage(Message.translation("server.commands.npc.all.failedToSpawn").param("role", name)); diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCBlackboardCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCBlackboardCommand.java index 9d367f3b..ae12b594 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCBlackboardCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCBlackboardCommand.java @@ -6,8 +6,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.AndQuery; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.blockset.config.BlockSet; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -47,6 +45,8 @@ import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; +import org.joml.Vector3i; public class NPCBlackboardCommand extends AbstractCommandCollection { public NPCBlackboardCommand() { diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCCommand.java index 6ca283ee..e419ea41 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCCommand.java @@ -85,5 +85,6 @@ public class NPCCommand extends AbstractCommandCollection { this.addSubCommand(new NPCTestCommand()); this.addSubCommand(new NPCThawCommand()); this.addSubCommand(new NPCMessageCommand()); + this.addSubCommand(new NPCDescriptorsCommand()); } } diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCDescriptorsCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCDescriptorsCommand.java new file mode 100644 index 00000000..03abc5dc --- /dev/null +++ b/src/com/hypixel/hytale/server/npc/commands/NPCDescriptorsCommand.java @@ -0,0 +1,39 @@ +package com.hypixel.hytale.server.npc.commands; + +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandContext; +import com.hypixel.hytale.server.core.command.system.basecommands.AbstractAsyncPlayerCommand; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.npc.NPCPlugin; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Nonnull; + +public class NPCDescriptorsCommand extends AbstractAsyncPlayerCommand { + public NPCDescriptorsCommand() { + super("descriptors", "server.commands.npc.descriptors.desc"); + } + + @Nonnull + @Override + protected CompletableFuture executeAsync( + @Nonnull CommandContext context, @Nonnull Store store, @Nonnull Ref ref, @Nonnull PlayerRef playerRef, @Nonnull World world + ) { + return this.runAsync( + context, + () -> { + NPCPlugin npcPlugin = NPCPlugin.get(); + npcPlugin.generateDescriptors(); + npcPlugin.saveDescriptors(); + context.sendMessage( + Message.translation("server.commands.npc.descriptors.saved").param("path", Path.of("npc_descriptors.json").toAbsolutePath().toString()) + ); + }, + world + ); + } +} diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCGiveCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCGiveCommand.java index c3e347c9..0b54db26 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCGiveCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCGiveCommand.java @@ -30,7 +30,7 @@ public class NPCGiveCommand extends NPCWorldCommandBase { if (item.getArmor() != null) { RoleUtils.setArmor(npc, itemName); } else { - RoleUtils.setItemInHand(npc, itemName); + RoleUtils.setItemInHand(ref, npc, itemName, store); } } @@ -43,7 +43,7 @@ public class NPCGiveCommand extends NPCWorldCommandBase { protected void execute( @Nonnull CommandContext context, @Nonnull NPCEntity npc, @Nonnull World world, @Nonnull Store store, @Nonnull Ref ref ) { - RoleUtils.setItemInHand(npc, null); + RoleUtils.setItemInHand(ref, npc, null, store); } } } diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCMultiSelectCommandBase.java b/src/com/hypixel/hytale/server/npc/commands/NPCMultiSelectCommandBase.java index a17f3482..08168311 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCMultiSelectCommandBase.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCMultiSelectCommandBase.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -16,11 +16,12 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.TargetUtil; import com.hypixel.hytale.server.npc.NPCPlugin; import com.hypixel.hytale.server.npc.entities.NPCEntity; -import java.util.ArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class NPCMultiSelectCommandBase extends NPCWorldCommandBase { protected static final float DEFAULT_CONE_ANGLE = 30.0F; @@ -134,22 +135,22 @@ public abstract class NPCMultiSelectCommandBase extends NPCWorldCommandBase { assert npcEntityComponentType != null; - Vector3d eyePosition; + Vector3d eyePosition = new Vector3d(); if (coneAngleDeg == 0.0F) { Ref ref = TargetUtil.getTargetEntity(playerRef, range, store); if (ref != null && store.getComponent(ref, npcEntityComponentType) != null) { - refs = new ArrayList<>(); + refs = new ReferenceArrayList<>(); refs.add(ref); } - eyePosition = Vector3d.ZERO; + eyePosition.zero(); } else { TransformComponent playerTransform = store.getComponent(playerRef, TransformComponent.getComponentType()); assert playerTransform != null; Transform viewTransform = TargetUtil.getLook(playerRef, store); - eyePosition = viewTransform.getPosition(); + eyePosition.set(viewTransform.getPosition()); Vector3d eyeDirection = viewTransform.getDirection(); assert eyePosition.length() == 1.0; @@ -169,7 +170,7 @@ public abstract class NPCMultiSelectCommandBase extends NPCWorldCommandBase { assert entityTransform != null; - Vector3d direction = Vector3d.directionTo(eyePosition, entityTransform.getPosition()); + Vector3d direction = Vector3dUtil.directionTo(eyePosition, entityTransform.getPosition()); double lengthDirection = direction.length(); return lengthDirection < 1.0E-4 ? true : eyeDirection.dot(direction) < cosineConeAngle * lengthDirection; } @@ -193,7 +194,7 @@ public abstract class NPCMultiSelectCommandBase extends NPCWorldCommandBase { assert npcTransform != null; - double distanceSq = Vector3d.directionTo(eyePosition, npcTransform.getPosition()).squaredLength(); + double distanceSq = eyePosition.distanceSquared(npcTransform.getPosition()); if (distanceSq < nearestDistanceSq) { nearestDistanceSq = distanceSq; nearestRef = ref; diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCPathCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCPathCommand.java index 56bfe7aa..24b0a8d1 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCPathCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCPathCommand.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.builtin.path.waypoint.RelativeWaypointDefinition; import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; @@ -21,6 +20,7 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import java.util.ArrayDeque; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class NPCPathCommand extends AbstractCommandCollection { public NPCPathCommand() { @@ -63,7 +63,7 @@ public class NPCPathCommand extends AbstractCommandCollection { assert headRotationComponent != null; Vector3d position = transformComponent.getPosition(); - Vector3f headRotation = headRotationComponent.getRotation(); + Rotation3f headRotation = headRotationComponent.getRotation(); npc.getPathManager().setTransientPath(TransientPath.buildPath(position, headRotation, instructions, 1.0)); } } diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCRunTestsCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCRunTestsCommand.java index e18204d7..2420af28 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCRunTestsCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCRunTestsCommand.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; @@ -27,11 +26,12 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class NPCRunTestsCommand extends AbstractPlayerCommand { @Nonnull @@ -145,7 +145,7 @@ public class NPCRunTestsCommand extends AbstractPlayerCommand { store.removeComponent(reference, NPCRunTestsCommand.NPCTestData.getComponentType()); } else { Vector3d position; - Vector3f rotation; + Rotation3f rotation; if (npcReference != null) { TransformComponent npcTransformComponent = store.getComponent(npcReference, TransformComponent.getComponentType()); @@ -170,7 +170,7 @@ public class NPCRunTestsCommand extends AbstractPlayerCommand { private static void cleanupNPC(@Nonnull Ref ref, @Nonnull Store store) { Ref flockReference = FlockPlugin.getFlockReference(ref, store); if (flockReference != null) { - ObjectArrayList> members = new ObjectArrayList<>(); + ReferenceArrayList> members = new ReferenceArrayList<>(); EntityGroup entityGroupComponent = store.getComponent(flockReference, EntityGroup.getComponentType()); assert entityGroupComponent != null; @@ -190,7 +190,7 @@ public class NPCRunTestsCommand extends AbstractPlayerCommand { @Nonnull NPCRunTestsCommand.NPCTestData testData, int index, @Nonnull Vector3d position, - @Nullable Vector3f rotation, + @Nullable Rotation3f rotation, @Nonnull Store store ) { Pair, NPCEntity> npcPair = NPCPlugin.get().spawnEntity(store, testData.npcRoles.getInt(index), position, rotation, null, null); diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCSensorStatsCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCSensorStatsCommand.java index 09b378ee..d8757694 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCSensorStatsCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCSensorStatsCommand.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.commands; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; @@ -24,6 +23,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList; import java.util.List; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NPCSensorStatsCommand extends AbstractPlayerCommand { public NPCSensorStatsCommand() { diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCSpawnCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCSpawnCommand.java index 8b262e77..8b42339b 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCSpawnCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCSpawnCommand.java @@ -10,8 +10,8 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.function.consumer.TriConsumer; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.PlayerSkin; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; @@ -55,6 +55,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class NPCSpawnCommand extends AbstractPlayerCommand { private static final double PLAYER_FOOT_POINT_EPSILON = 0.01; @@ -116,7 +117,7 @@ public class NPCSpawnCommand extends AbstractPlayerCommand { assert headRotationComponent != null; - Vector3f playerHeadRotation = headRotationComponent.getRotation(); + Rotation3f playerHeadRotation = headRotationComponent.getRotation(); TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; @@ -131,23 +132,23 @@ public class NPCSpawnCommand extends AbstractPlayerCommand { double radius = this.radiusArg.provided(context) ? this.radiusArg.get(context) : 8.0; String flagsString = this.flagsArg.provided(context) ? this.flagsArg.get(context) : null; EnumSet flags = flagsString != null ? RoleDebugFlags.getFlags(flagsString.split(",")) : RoleDebugFlags.getPreset("none"); - Vector3d velocity = new Vector3d(Vector3d.ZERO); + Vector3d velocity = new Vector3d(Vector3dUtil.ZERO); if (this.speedArg.provided(context)) { - PhysicsMath.vectorFromAngles(playerHeadRotation.getYaw(), playerHeadRotation.getPitch(), velocity); - velocity.setLength(this.speedArg.get(context)); + PhysicsMath.vectorFromAngles(playerHeadRotation.yaw(), playerHeadRotation.pitch(), velocity); + velocity.normalize(this.speedArg.get(context)); } Random random = (Random)(this.nonRandomArg.get(context) ? new Random(0L) : ThreadLocalRandom.current()); Vector3d posOffset = this.posOffsetArg.provided(context) ? this.parseVector3d(context, this.posOffsetArg.get(context)) : null; - Vector3f headRotation = this.headRotationArg.provided(context) ? this.parseVector3f(context, this.headRotationArg.get(context)) : null; + Rotation3f headRotation = this.headRotationArg.provided(context) ? this.parseVector3f(context, this.headRotationArg.get(context)) : null; boolean randomRotation = false; - Vector3f rotation = playerHeadRotation; + Rotation3f rotation = playerHeadRotation; if (this.bodyRotationArg.provided(context)) { rotation = this.parseVector3f(context, this.bodyRotationArg.get(context)); } else if (this.randomRotationArg.get(context)) { randomRotation = true; } else if (this.facingRotationArg.get(context)) { - playerHeadRotation.setY(playerHeadRotation.getY() - (float) Math.PI); + playerHeadRotation.setY(playerHeadRotation.y() - (float) Math.PI); } String flockSizeString = this.flockArg.provided(context) ? this.flockArg.get(context) : "1"; @@ -194,7 +195,7 @@ public class NPCSpawnCommand extends AbstractPlayerCommand { } if (randomRotation) { - rotation = new Vector3f(0.0F, (float)(2.0 * random.nextDouble() * Math.PI), 0.0F); + rotation = new Rotation3f(0.0F, (float)(2.0 * random.nextDouble() * Math.PI), 0.0F); } if (this.scaleArg.provided(context)) { @@ -271,14 +272,14 @@ public class NPCSpawnCommand extends AbstractPlayerCommand { assert npcUuidComponent != null; if (headRotation != null) { - npcHeadRotationComponent.getRotation().assign(headRotation); + npcHeadRotationComponent.getRotation().set(headRotation); store.ensureComponent(npcRef, Frozen.getComponentType()); } Vector3d npcPosition = npcTransformComponent.getPosition(); - double x = npcPosition.getX(); - double y = npcPosition.getY(); - double z = npcPosition.getZ(); + double x = npcPosition.x(); + double y = npcPosition.y(); + double z = npcPosition.z(); if (count > 1) { x += random.nextDouble() * 2.0 * radius - radius; z += random.nextDouble() * 2.0 * radius - radius; @@ -287,9 +288,9 @@ public class NPCSpawnCommand extends AbstractPlayerCommand { y += 0.1; } - npcPosition.assign(x, y, z); + npcPosition.set(x, y, z); npc.saveLeashInformation(npcPosition, npcTransformComponent.getRotation()); - if (!velocity.equals(Vector3d.ZERO)) { + if (!velocity.equals(Vector3dUtil.ZERO)) { npc.getRole().forceVelocity(velocity, null, false); } @@ -308,7 +309,7 @@ public class NPCSpawnCommand extends AbstractPlayerCommand { NPCPlugin.get() .getLogger() .at(Level.INFO) - .log("%s created with id %s at position %s", npc.getRoleName(), npcUuidComponent.getUuid(), Vector3d.formatShortString(npcPosition)); + .log("%s created with id %s at position %s", npc.getRoleName(), npcUuidComponent.getUuid(), Vector3dUtil.formatShortString(npcPosition)); } } catch (IllegalStateException | NullPointerException | IllegalArgumentException var49) { NPCPlugin.get().getLogger().at(Level.WARNING).log("Spawn failed: " + var49.getMessage()); @@ -335,14 +336,14 @@ public class NPCSpawnCommand extends AbstractPlayerCommand { } @Nullable - private Vector3f parseVector3f(@Nonnull CommandContext context, @Nonnull String str) { + private Rotation3f parseVector3f(@Nonnull CommandContext context, @Nonnull String str) { String[] parts = str.split(","); if (parts.length != 3) { context.sendMessage(Message.raw("Invalid Vector3f format: must be three comma-separated floats")); return null; } else { try { - return new Vector3f(Float.parseFloat(parts[0]), Float.parseFloat(parts[1]), Float.parseFloat(parts[2])); + return new Rotation3f(Float.parseFloat(parts[0]), Float.parseFloat(parts[1]), Float.parseFloat(parts[2])); } catch (NumberFormatException var5) { context.sendMessage(Message.raw("Invalid Vector3f format: " + var5.getMessage())); return null; diff --git a/src/com/hypixel/hytale/server/npc/commands/NPCTestCommand.java b/src/com/hypixel/hytale/server/npc/commands/NPCTestCommand.java index 968d9d6e..01c04176 100644 --- a/src/com/hypixel/hytale/server/npc/commands/NPCTestCommand.java +++ b/src/com/hypixel/hytale/server/npc/commands/NPCTestCommand.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -25,6 +24,7 @@ import com.hypixel.hytale.server.npc.util.PositionProbeAir; import com.hypixel.hytale.server.npc.util.PositionProbeWater; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NPCTestCommand extends AbstractCommandCollection { public NPCTestCommand() { diff --git a/src/com/hypixel/hytale/server/npc/components/messaging/EntityEventSupport.java b/src/com/hypixel/hytale/server/npc/components/messaging/EntityEventSupport.java index 07648b01..44ea847d 100644 --- a/src/com/hypixel/hytale/server/npc/components/messaging/EntityEventSupport.java +++ b/src/com/hypixel/hytale/server/npc/components/messaging/EntityEventSupport.java @@ -2,13 +2,13 @@ package com.hypixel.hytale.server.npc.components.messaging; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.flock.FlockMembership; import com.hypixel.hytale.server.npc.blackboard.view.event.EntityEventNotification; import com.hypixel.hytale.server.npc.blackboard.view.event.entity.EntityEventType; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class EntityEventSupport extends EventSupport { public void postMessage( @@ -18,15 +18,15 @@ public abstract class EntityEventSupport extends EventSupport slot.getMaxRangeSquared())) { FlockMembership flockMembership = store.getComponent(parent, FlockMembership.getComponentType()); Ref flockReference = flockMembership != null ? flockMembership.getFlockRef() : null; boolean isSameFlock = flockReference != null && flockReference.equals(notification.getFlockReference()); - if (!slot.isActivated() || distanceSquared < slot.getPosition().distanceSquaredTo(parentEntityPosition) || !slot.isSameFlock() && isSameFlock) { + if (!slot.isActivated() || distanceSquared < slot.getPosition().distanceSquared(parentEntityPosition) || !slot.isSameFlock() && isSameFlock) { slot.activate(x, y, z, notification.getInitiator(), 2.0); slot.setSameFlock(isSameFlock); } @@ -39,7 +39,7 @@ public abstract class EntityEventSupport extends EventSupport target, double age) { super.activate(target, age); - this.position.assign(x, y, z); + this.position.set(x, y, z); } @Nonnull diff --git a/src/com/hypixel/hytale/server/npc/components/messaging/EventSupport.java b/src/com/hypixel/hytale/server/npc/components/messaging/EventSupport.java index 4253db6f..9bf43da2 100644 --- a/src/com/hypixel/hytale/server/npc/components/messaging/EventSupport.java +++ b/src/com/hypixel/hytale/server/npc/components/messaging/EventSupport.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.components.messaging; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.blackboard.view.event.EventNotification; @@ -11,6 +10,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntMap; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class EventSupport, NotificationType extends EventNotification> extends MessageSupport { protected static final double EVENT_AGE = 2.0; @@ -22,12 +22,12 @@ public abstract class EventSupport, Notificati if (slot != null && slot.isEnabled()) { Vector3d parentEntityPosition = store.getComponent(parent, TransformComponent.getComponentType()).getPosition(); Vector3d pos = notification.getPosition(); - double x = pos.getX(); - double y = pos.getY(); - double z = pos.getZ(); - double distanceSquared = parentEntityPosition.distanceSquaredTo(x, y, z); + double x = pos.x(); + double y = pos.y(); + double z = pos.z(); + double distanceSquared = parentEntityPosition.distanceSquared(x, y, z); if (distanceSquared <= slot.getMaxRangeSquared() - && (!slot.isActivated() || distanceSquared < slot.getPosition().distanceSquaredTo(parentEntityPosition))) { + && (!slot.isActivated() || distanceSquared < slot.getPosition().distanceSquared(parentEntityPosition))) { slot.activate(x, y, z, notification.getInitiator(), 2.0); } } @@ -53,7 +53,7 @@ public abstract class EventSupport, Notificati return false; } else { EventMessage event = this.messageSlots[messageIndex]; - return event.getPosition().distanceSquaredTo(parentPosition) < range * range; + return event.getPosition().distanceSquared(parentPosition) < range * range; } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/ActionBase.java b/src/com/hypixel/hytale/server/npc/corecomponents/ActionBase.java index 828a6449..863725ba 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/ActionBase.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/ActionBase.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.server.npc.instructions.Action; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public abstract class ActionBase extends AnnotatedComponentBase implements Action { protected boolean once; @@ -19,12 +20,12 @@ public abstract class ActionBase extends AnnotatedComponentBase implements Actio } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return !this.once || !this.triggered; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { this.setOnce(); return true; } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/BlockTarget.java b/src/com/hypixel/hytale/server/npc/corecomponents/BlockTarget.java index 1f6f9d98..c2d66f80 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/BlockTarget.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/BlockTarget.java @@ -1,15 +1,16 @@ package com.hypixel.hytale.server.npc.corecomponents; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.npc.blackboard.Blackboard; import com.hypixel.hytale.server.npc.blackboard.view.resource.ResourceView; import com.hypixel.hytale.server.npc.entities.NPCEntity; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BlockTarget { - private final Vector3d position = new Vector3d(Vector3d.MIN); + private final Vector3d position = new Vector3d(Vector3dUtil.MIN); private int chunkChangeRevision = -1; private int foundBlockType = Integer.MIN_VALUE; @Nullable @@ -47,7 +48,7 @@ public class BlockTarget { } this.reservationHolder = null; - this.position.assign(Vector3d.MIN); + this.position.set(Vector3dUtil.MIN); this.chunkChangeRevision = -1; this.foundBlockType = Integer.MIN_VALUE; } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/BodyMotionBase.java b/src/com/hypixel/hytale/server/npc/corecomponents/BodyMotionBase.java index 62bdffcb..5125f5f4 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/BodyMotionBase.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/BodyMotionBase.java @@ -2,9 +2,44 @@ package com.hypixel.hytale.server.npc.corecomponents; import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderBodyMotionBase; import com.hypixel.hytale.server.npc.instructions.BodyMotion; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; +import com.hypixel.hytale.server.npc.movement.controllers.ProbeMoveData; +import com.hypixel.hytale.server.npc.role.Role; +import java.util.EnumSet; import javax.annotation.Nonnull; public abstract class BodyMotionBase extends MotionBase implements BodyMotion { public BodyMotionBase(@Nonnull BuilderBodyMotionBase builderMotionBase) { } + + @Nonnull + protected static EnumSet computeEffectiveRelaxedConstraints( + boolean usesLegacyConstraintMode, + @Nonnull EnumSet relaxedConstraints, + boolean isLegacyRelaxedMoveConstraints, + boolean isLegacyAvoidingBlockDamage + ) { + if (!usesLegacyConstraintMode) { + return relaxedConstraints; + } else { + EnumSet effectiveConstraints = isLegacyRelaxedMoveConstraints + ? EnumSet.copyOf(RelaxedConstraint.DEFAULT_WHEN_RELAXED) + : EnumSet.noneOf(RelaxedConstraint.class); + if (!isLegacyAvoidingBlockDamage) { + effectiveConstraints.add(RelaxedConstraint.DAMAGE); + } + + return effectiveConstraints; + } + } + + protected static boolean applyEscapeConstraints(@Nonnull Role role, @Nonnull ProbeMoveData probeMoveData) { + if (!role.couldBreatheCached()) { + probeMoveData.getRelaxedConstraints().add(RelaxedConstraint.BREATHE); + probeMoveData.getRelaxedConstraints().add(RelaxedConstraint.WADE); + return true; + } else { + return false; + } + } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/ISensorEntityPrioritiser.java b/src/com/hypixel/hytale/server/npc/corecomponents/ISensorEntityPrioritiser.java index 420e7ad8..b3efa5fa 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/ISensorEntityPrioritiser.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/ISensorEntityPrioritiser.java @@ -2,12 +2,12 @@ package com.hypixel.hytale.server.npc.corecomponents; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.instructions.RoleStateChange; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.util.IEntityByPriorityFilter; import java.util.List; +import org.joml.Vector3d; public interface ISensorEntityPrioritiser extends RoleStateChange { IEntityByPriorityFilter getNPCPrioritiser(); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/WeightedAction.java b/src/com/hypixel/hytale/server/npc/corecomponents/WeightedAction.java index 76e88149..a9423012 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/WeightedAction.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/WeightedAction.java @@ -17,12 +17,11 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class WeightedAction extends AnnotatedComponentBase implements Action { - @Nullable private final Action action; private final double weight; - public WeightedAction(@Nonnull BuilderWeightedAction builder, @Nonnull BuilderSupport support) { - this.action = builder.getAction(support); + public WeightedAction(@Nonnull BuilderWeightedAction builder, @Nonnull BuilderSupport support, @Nonnull Action action) { + this.action = action; this.weight = builder.getWeight(support); } @@ -31,12 +30,12 @@ public class WeightedAction extends AnnotatedComponentBase implements Action { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return this.action.canExecute(ref, role, sensorInfo, dt, store); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return this.action.execute(ref, role, sensorInfo, dt, store); } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionAppearance.java b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionAppearance.java index b96bca72..f302acc2 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionAppearance.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionAppearance.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionAppearance extends ActionBase { protected final String appearance; @@ -19,12 +20,12 @@ public class ActionAppearance extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && this.appearance != null && !this.appearance.isEmpty(); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType()); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionDisplayName.java b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionDisplayName.java index 5d980a83..12039cf3 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionDisplayName.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionDisplayName.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.Builder import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionDisplayName extends ActionBase { protected final String displayName; @@ -19,12 +20,12 @@ public class ActionDisplayName extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && this.displayName != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); role.getEntitySupport().nominateDisplayName(this.displayName); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionModelAttachment.java b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionModelAttachment.java index 3b052831..12c0f106 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionModelAttachment.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionModelAttachment.java @@ -31,7 +31,7 @@ public class ActionModelAttachment extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); setModelAttachment(ref, this.slot, this.attachment, store); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionPlayAnimation.java b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionPlayAnimation.java index 827d9a5c..06b553bf 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionPlayAnimation.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionPlayAnimation.java @@ -25,7 +25,7 @@ public class ActionPlayAnimation extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType()); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionPlaySound.java b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionPlaySound.java index c754c4ec..a3674224 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionPlaySound.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionPlaySound.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.audiovisual; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -13,6 +12,8 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; public class ActionPlaySound extends ActionBase { protected final int soundEventIndex; @@ -23,7 +24,7 @@ public class ActionPlaySound extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); @@ -34,7 +35,7 @@ public class ActionPlaySound extends ActionBase { assert npcComponent != null; Vector3d position = transformComponent.getPosition(); - SoundUtil.playSoundEvent3d(ref, this.soundEventIndex, position.getX(), position.getY(), position.getZ(), false, store); + SoundUtil.playSoundEvent3d(ref, this.soundEventIndex, position.x(), position.y(), position.z(), false, store); return true; } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionSpawnParticles.java b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionSpawnParticles.java index 83834aa4..f83b9479 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionSpawnParticles.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/ActionSpawnParticles.java @@ -3,9 +3,7 @@ package com.hypixel.hytale.server.npc.corecomponents.audiovisual; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.ModelParticle; -import com.hypixel.hytale.protocol.Vector3f; import com.hypixel.hytale.protocol.packets.entities.SpawnModelParticles; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -17,8 +15,11 @@ import com.hypixel.hytale.server.npc.corecomponents.ActionBase; import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.BuilderActionSpawnParticles; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class ActionSpawnParticles extends ActionBase { protected final String particleSystem; @@ -40,15 +41,15 @@ public class ActionSpawnParticles extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; - Vector3d position = new Vector3d(this.offset).rotateY(transformComponent.getRotation().getYaw()).add(transformComponent.getPosition()); + Vector3d position = new Vector3d(this.offset).rotateY(transformComponent.getRotation().yaw()).add(transformComponent.getPosition()); SpatialResource, EntityStore> playerSpatialResource = store.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(position, this.range, results); NetworkId networkIdComponent = store.getComponent(ref, NetworkId.getComponentType()); if (networkIdComponent == null) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/builders/BuilderActionSpawnParticles.java b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/builders/BuilderActionSpawnParticles.java index ec27cc60..b6377372 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/builders/BuilderActionSpawnParticles.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/audiovisual/builders/BuilderActionSpawnParticles.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders; import com.google.gson.JsonElement; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.asset.builder.holder.AssetHolder; @@ -14,6 +13,7 @@ import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ParticleSyst import com.hypixel.hytale.server.npc.corecomponents.audiovisual.ActionSpawnParticles; import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderActionBase; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BuilderActionSpawnParticles extends BuilderActionBase { protected final AssetHolder particleSystem = new AssetHolder(); @@ -68,7 +68,7 @@ public class BuilderActionSpawnParticles extends BuilderActionBase { } public Vector3d getOffset(BuilderSupport support) { - return createVector3d(this.offset.get(support.getExecutionContext()), Vector3d.ZERO::clone); + return createVector3d(this.offset.get(support.getExecutionContext()), Vector3d::new); } public String getTargetNodeName(BuilderSupport support) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/builders/BuilderActionBase.java b/src/com/hypixel/hytale/server/npc/corecomponents/builders/BuilderActionBase.java index 0389eea0..438d8c24 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/builders/BuilderActionBase.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/builders/BuilderActionBase.java @@ -4,12 +4,14 @@ import com.google.gson.JsonElement; import com.hypixel.hytale.server.npc.asset.builder.Builder; import com.hypixel.hytale.server.npc.asset.builder.BuilderBase; import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; +import com.hypixel.hytale.server.npc.asset.builder.holder.BooleanHolder; import com.hypixel.hytale.server.npc.instructions.Action; import com.hypixel.hytale.server.npc.util.expression.ExecutionContext; import javax.annotation.Nonnull; public abstract class BuilderActionBase extends BuilderBase { protected boolean once; + protected final BooleanHolder enabled = new BooleanHolder(); @Override public boolean canRequireFeature() { @@ -21,6 +23,7 @@ public abstract class BuilderActionBase extends BuilderBase { public Builder readCommonConfig(@Nonnull JsonElement data) { super.readCommonConfig(data); this.getBoolean(data, "Once", v -> this.once = v, false, BuilderDescriptorState.Stable, "Execute only once", null); + this.getBoolean(data, "Enabled", this.enabled, true, BuilderDescriptorState.Stable, "Whether this action should be enabled on the NPC", null); return this; } @@ -32,7 +35,7 @@ public abstract class BuilderActionBase extends BuilderBase { @Override public final boolean isEnabled(ExecutionContext context) { - return true; + return this.enabled.get(context); } public boolean isOnce() { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/builders/BuilderWeightedAction.java b/src/com/hypixel/hytale/server/npc/corecomponents/builders/BuilderWeightedAction.java index 41cf01db..bed5bcc1 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/builders/BuilderWeightedAction.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/builders/BuilderWeightedAction.java @@ -21,9 +21,10 @@ public class BuilderWeightedAction extends BuilderBase { private final BuilderObjectReferenceHelper action = new BuilderObjectReferenceHelper<>(Action.class, this); private final DoubleHolder weight = new DoubleHolder(); - @Nonnull + @Nullable public WeightedAction build(@Nonnull BuilderSupport builderSupport) { - return new WeightedAction(this, builderSupport); + Action action = this.getAction(builderSupport); + return action == null ? null : new WeightedAction(this, builderSupport, action); } @Nonnull diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/combat/ActionAttack.java b/src/com/hypixel/hytale/server/npc/corecomponents/combat/ActionAttack.java index b2f9038c..e41f893b 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/combat/ActionAttack.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/combat/ActionAttack.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.server.npc.corecomponents.combat; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.entity.InteractionChain; import com.hypixel.hytale.server.core.entity.InteractionContext; @@ -94,7 +94,7 @@ public class ActionAttack extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && !role.getCombatSupport().isExecutingAttack(); } @@ -221,8 +221,8 @@ public class ActionAttack extends ActionBase { assert headRotationComponent != null; - Vector3f rotation = aimingData != null && aimingData.getChargeDistance() > 0.0 ? transformComponent.getRotation() : headRotationComponent.getRotation(); - if (this.hasTimeForAiming(dt) && aimingData != null && !aimingData.isOnTarget(rotation.getYaw(), rotation.getPitch(), this.meleeConeAngle)) { + Rotation3f rotation = aimingData != null && aimingData.getChargeDistance() > 0.0 ? transformComponent.getRotation() : headRotationComponent.getRotation(); + if (this.hasTimeForAiming(dt) && aimingData != null && !aimingData.isOnTarget(rotation.yaw(), rotation.pitch(), this.meleeConeAngle)) { aimingData.clearSolution(); return false; } else { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/combat/BodyMotionAimCharge.java b/src/com/hypixel/hytale/server/npc/corecomponents/combat/BodyMotionAimCharge.java index 092cdc46..233e72c7 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/combat/BodyMotionAimCharge.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/combat/BodyMotionAimCharge.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.AimingData; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionAimCharge extends BodyMotionBase { protected static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); @@ -64,20 +64,20 @@ public class BodyMotionAimCharge extends BodyMotionBase { this.aimingData.clearSolution(); return true; } else { - this.direction.subtract(selfPosition); - this.direction.setLength(this.aimingData.getChargeDistance()); - double x = this.direction.getX(); - double y = this.direction.getY(); - double z = this.direction.getZ(); + this.direction.sub(selfPosition); + this.direction.normalize(this.aimingData.getChargeDistance()); + double x = this.direction.x(); + double y = this.direction.y(); + double z = this.direction.z(); float yaw = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)); float pitch = PhysicsMath.pitchFromDirection(x, y, z); desiredSteering.setYaw(yaw); desiredSteering.setPitch(pitch); desiredSteering.setRelativeTurnSpeed(this.relativeTurnSpeed); TransformComponent transformComponent = componentAccessor.getComponent(ref, TRANSFORM_COMPONENT_TYPE); - Vector3f bodyRotation = transformComponent.getRotation(); + Rotation3f bodyRotation = transformComponent.getRotation(); this.aimingData.setOrientation(yaw, pitch); - if (!this.aimingData.isOnTarget(bodyRotation.getYaw(), bodyRotation.getPitch(), this.aimingData.getDesiredHitAngle())) { + if (!this.aimingData.isOnTarget(bodyRotation.yaw(), bodyRotation.pitch(), this.aimingData.getDesiredHitAngle())) { this.aimingData.clearSolution(); return true; } else { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/combat/HeadMotionAim.java b/src/com/hypixel/hytale/server/npc/corecomponents/combat/HeadMotionAim.java index f78efc3a..7157d3a3 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/combat/HeadMotionAim.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/combat/HeadMotionAim.java @@ -5,9 +5,8 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.entity.entities.ProjectileComponent; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; @@ -33,6 +32,8 @@ import java.util.EnumSet; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3f; public class HeadMotionAim extends HeadMotionBase implements DebugSupport.DebugFlagsChangeListener { public static final double MIN_RANGED_AIMING_DISTANCE = 4.0; @@ -84,11 +85,11 @@ public class HeadMotionAim extends HeadMotionBase implements DebugSupport.DebugF if (sensorInfo != null && sensorInfo.hasPosition() && sensorInfo.getPositionProvider() != null) { Transform lookVec = TargetUtil.getLook(ref, componentAccessor); Vector3d lookPosition = lookVec.getPosition(); - Vector3f lookRotation = lookVec.getRotation(); + Rotation3f lookRotation = lookVec.getRotation(); IPositionProvider positionProvider = sensorInfo.getPositionProvider(); positionProvider.providePosition(this.targetPosition); - this.startPosition.assign(lookPosition); - this.relativeVelocity.assign(Vector3d.ZERO); + this.startPosition.set(lookPosition); + this.relativeVelocity.zero(); Ref targetRef = positionProvider.getTarget(); BallisticData ballisticData = this.aimingData.getBallisticData(); Box boundingBox = Box.ZERO; @@ -104,24 +105,24 @@ public class HeadMotionAim extends HeadMotionBase implements DebugSupport.DebugF if (ballisticData != null) { if (this.deflection) { - this.relativeVelocity.assign(targetVelocityComponent.getVelocity()); + this.relativeVelocity.set(targetVelocityComponent.getVelocity()); } } else { - double targetY = this.targetPosition.getY(); - double startY = this.startPosition.getY(); + double targetY = this.targetPosition.y(); + double startY = this.startPosition.y(); double minY = targetY + boundingBox.getMin().y; double maxY = targetY + boundingBox.getMax().y; if (minY > startY) { - this.targetPosition.setY(minY); + this.targetPosition.y = minY; } else if (maxY < startY) { - this.targetPosition.setY(maxY); + this.targetPosition.y = maxY; } else { - this.targetPosition.setY(startY); + this.targetPosition.y = startY; } } } - boolean isNearTarget = this.startPosition.distanceSquaredTo(this.targetPosition) <= 16.0; + boolean isNearTarget = this.startPosition.distanceSquared(this.targetPosition) <= 16.0; if (ballisticData != null) { this.aimingData.setDepthOffset(ballisticData.getDepthShot(), ballisticData.isPitchAdjustShot()); if (!isNearTarget) { @@ -130,12 +131,12 @@ public class HeadMotionAim extends HeadMotionBase implements DebugSupport.DebugF ballisticData.getVerticalCenterShot(), ballisticData.getHorizontalCenterShot(), ballisticData.getDepthShot(), - lookRotation.getYaw(), - lookRotation.getPitch(), + lookRotation.yaw(), + lookRotation.pitch(), this.startOffset ); } else { - this.startOffset.assign(Vector3d.ZERO); + this.startOffset.zero(); } if (targetRef != null && !targetRef.equals(this.lastTargetReference)) { @@ -145,16 +146,16 @@ public class HeadMotionAim extends HeadMotionBase implements DebugSupport.DebugF if (this.aimingData.isHaveAttacked()) { ThreadLocalRandom random = ThreadLocalRandom.current(); - this.spreadOffset.assign(Vector3d.ZERO); - this.targetOffset.assign(Vector3d.ZERO); + this.spreadOffset.zero(); + this.targetOffset.zero(); if (this.spread > 0.0 && random.nextDouble() > this.hitProbability) { - double spread2 = 2.0 * this.spread * this.startPosition.distanceTo(this.targetPosition) / 10.0; - this.spreadOffset.assign(random.nextDouble() - 0.5, random.nextDouble() - 0.5, random.nextDouble() - 0.5).scale(spread2); + double spread2 = 2.0 * this.spread * this.startPosition.distance(this.targetPosition) / 10.0; + this.spreadOffset.set(random.nextDouble() - 0.5, random.nextDouble() - 0.5, random.nextDouble() - 0.5).mul(spread2); } else { double start = 0.1; double end = 0.9; this.targetOffset - .assign( + .set( NPCPhysicsMath.lerp(boundingBox.getMin().x, boundingBox.getMax().x, random.nextDouble(0.1, 0.9)), NPCPhysicsMath.lerp(boundingBox.getMin().y, boundingBox.getMax().y, random.nextDouble(0.1, 0.9)), NPCPhysicsMath.lerp(boundingBox.getMin().z, boundingBox.getMax().z, random.nextDouble(0.1, 0.9)) @@ -171,12 +172,12 @@ public class HeadMotionAim extends HeadMotionBase implements DebugSupport.DebugF this.aimingData.setDepthOffset(0.0, false); } - double x = this.targetPosition.getX() - this.startPosition.getX(); - double y = this.targetPosition.getY() - this.startPosition.getY(); - double z = this.targetPosition.getZ() - this.startPosition.getZ(); + double x = this.targetPosition.x() - this.startPosition.x(); + double y = this.targetPosition.y() - this.startPosition.y(); + double z = this.targetPosition.z() - this.startPosition.z(); if (isNearTarget && ballisticData != null) { - float yaw = lookRotation.getYaw(); - float pitch = lookRotation.getPitch(); + float yaw = lookRotation.yaw(); + float pitch = lookRotation.pitch(); double dotXZ = x * x + z * z; if (dotXZ >= 1.0E-4) { yaw = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)); @@ -202,13 +203,13 @@ public class HeadMotionAim extends HeadMotionBase implements DebugSupport.DebugF this.aimingData.setOrientation(yaw, pitch); this.aimingData.setTarget(targetRef); - } else if (this.aimingData.computeSolution(x, y, z, this.relativeVelocity.getX(), this.relativeVelocity.getY(), this.relativeVelocity.getZ())) { + } else if (this.aimingData.computeSolution(x, y, z, this.relativeVelocity.x(), this.relativeVelocity.y(), this.relativeVelocity.z())) { this.aimingData.setTarget(targetRef); } else { double dotXZx = x * x + z * z; double dotXYZ = dotXZx + y * y; - float yawx = dotXZx >= 1.0E-4 ? PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)) : lookRotation.getYaw(); - float pitchx = dotXYZ >= 1.0E-4 ? PhysicsMath.pitchFromDirection(x, y, z) : lookRotation.getPitch(); + float yawx = dotXZx >= 1.0E-4 ? PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)) : lookRotation.yaw(); + float pitchx = dotXYZ >= 1.0E-4 ? PhysicsMath.pitchFromDirection(x, y, z) : lookRotation.pitch(); this.aimingData.setOrientation(yawx, pitchx); this.aimingData.setTarget(null); } @@ -221,8 +222,10 @@ public class HeadMotionAim extends HeadMotionBase implements DebugSupport.DebugF World world = ref.getStore().getExternalData().getWorld(); DebugUtils.addSphere(world, this.targetPosition, color, 0.5, 0.1F); - if (this.startPosition.distanceTo(this.targetPosition) > 1.0E-4) { - DebugUtils.addArrow(world, this.startPosition, this.targetPosition.clone().subtract(this.startPosition).setLength(1.0), color, 0.1F, true); + if (this.startPosition.distance(this.targetPosition) > 1.0E-4) { + DebugUtils.addArrow( + world, this.startPosition, new Vector3d(this.targetPosition).sub(this.startPosition).normalize(), color, 0.1F, DebugUtils.FLAG_FADE + ); } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/debug/ActionLog.java b/src/com/hypixel/hytale/server/npc/corecomponents/debug/ActionLog.java index d69bc4c2..29f00b7f 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/debug/ActionLog.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/debug/ActionLog.java @@ -12,6 +12,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionLog extends ActionBase { protected final String text; @@ -22,12 +23,12 @@ public class ActionLog extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && this.text != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); NPCPlugin.get().getLogger().at(Level.INFO).atMostEvery(1, TimeUnit.SECONDS).log("[%d]<%s>: %s", ref.getIndex(), role.getRoleName(), this.text); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/debug/BodyMotionTestProbe.java b/src/com/hypixel/hytale/server/npc/corecomponents/debug/BodyMotionTestProbe.java index 20936b3b..ebeac0d7 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/debug/BodyMotionTestProbe.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/debug/BodyMotionTestProbe.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.corecomponents.debug; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -11,18 +10,23 @@ import com.hypixel.hytale.server.npc.corecomponents.BodyMotionBase; import com.hypixel.hytale.server.npc.corecomponents.debug.builders.BuilderBodyMotionTestProbe; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.movement.Steering; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import com.hypixel.hytale.server.npc.movement.controllers.ProbeMoveData; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; +import java.util.EnumSet; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionTestProbe extends BodyMotionBase { protected final double adjustX; protected final double adjustZ; protected final double adjustDistance; protected final float snapAngle; + @Nonnull + protected final EnumSet cachedEffectiveConstraints; protected boolean displayText; protected final Vector3d direction = new Vector3d(); protected final ProbeMoveData probeMoveData = new ProbeMoveData(); @@ -34,8 +38,19 @@ public class BodyMotionTestProbe extends BodyMotionBase { this.adjustZ = builderBodyMotionTestProbe.getAdjustZ(); this.adjustDistance = builderBodyMotionTestProbe.getAdjustDistance(); this.snapAngle = builderBodyMotionTestProbe.getSnapAngle() * (float) (Math.PI / 180.0); - this.probeMoveData.setAvoidingBlockDamage(builderBodyMotionTestProbe.isAvoidingBlockDamage()); - this.probeMoveData.setRelaxedMoveConstraints(builderBodyMotionTestProbe.isRelaxedMoveConstraints()); + boolean isLegacyRelaxedMoveConstraints = builderBodyMotionTestProbe.isLegacyRelaxedMoveConstraints(); + boolean usesLegacyConstraintMode = !builderBodyMotionTestProbe.isRelaxedConstraintsPresent(); + EnumSet relaxedConstraints = builderBodyMotionTestProbe.getRelaxedConstraints(); + boolean isLegacyAvoidingBlockDamage = builderBodyMotionTestProbe.isAvoidingBlockDamage(); + this.cachedEffectiveConstraints = computeEffectiveRelaxedConstraints( + usesLegacyConstraintMode, relaxedConstraints, isLegacyRelaxedMoveConstraints, isLegacyAvoidingBlockDamage + ); + } + + @Nullable + @Override + public EnumSet getRelaxedConstraints() { + return this.cachedEffectiveConstraints; } @Override @@ -74,13 +89,15 @@ public class BodyMotionTestProbe extends BodyMotionBase { @Nonnull ComponentAccessor componentAccessor ) { desiredSteering.clear(); + this.probeMoveData.setRelaxedConstraints(this.cachedEffectiveConstraints); + applyEscapeConstraints(role, this.probeMoveData); if (sensorInfo != null && sensorInfo.getPositionProvider().providePosition(this.direction)) { TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); if (transformComponent == null) { return false; } else { Vector3d position = transformComponent.getPosition(); - this.direction.subtract(position); + this.direction.sub(position); if (!this.displayText) { return true; } else { @@ -90,17 +107,17 @@ public class BodyMotionTestProbe extends BodyMotionBase { } else { if (this.adjustDistance > 0.0) { length = this.adjustDistance; - this.direction.setLength(this.adjustDistance); + this.direction.normalize(this.adjustDistance); } - double x = this.direction.getX(); - double y = this.direction.getY(); - double z = this.direction.getZ(); + double x = this.direction.x(); + double y = this.direction.y(); + double z = this.direction.z(); float yaw = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)); float pitch = PhysicsMath.pitchFromDirection(x, y, z); if (this.snapAngle > 0.0F && this.snapAngle < (float) Math.PI) { yaw = MathUtil.fastRound(yaw / this.snapAngle) * this.snapAngle; - PhysicsMath.vectorFromAngles(yaw, pitch, this.direction).setLength(length); + PhysicsMath.vectorFromAngles(yaw, pitch, this.direction).normalize(length); } desiredSteering.setYaw(yaw); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/debug/builders/BuilderBodyMotionTestProbe.java b/src/com/hypixel/hytale/server/npc/corecomponents/debug/builders/BuilderBodyMotionTestProbe.java index 814df150..ab5089ff 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/debug/builders/BuilderBodyMotionTestProbe.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/debug/builders/BuilderBodyMotionTestProbe.java @@ -5,15 +5,22 @@ import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderBodyMotionBase; import com.hypixel.hytale.server.npc.corecomponents.debug.BodyMotionTestProbe; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; +import java.util.EnumSet; import javax.annotation.Nonnull; public class BuilderBodyMotionTestProbe extends BuilderBodyMotionBase { + private static final String[] AVOID_OR_RELAXED_ATTRIBUTES = new String[]{"AvoidBlockDamage", "RelaxedConstraints"}; + private static final String[] LEGACY_OR_RELAXED_ATTRIBUTES = new String[]{"RelaxedMoveConstraints", "RelaxedConstraints"}; protected double adjustX; protected double adjustZ; protected double adjustDistance; protected float snapAngle; protected boolean isAvoidingBlockDamage; - protected boolean isRelaxedMoveConstraints; + protected boolean isLegacyRelaxedMoveConstraints; + protected EnumSet relaxedConstraints = EnumSet.noneOf(RelaxedConstraint.class); + private final boolean[] avoidOrRelaxedPresent = new boolean[2]; + private final boolean[] legacyOrRelaxedPresent = new boolean[2]; @Nonnull public BodyMotionTestProbe build(BuilderSupport builderSupport) { @@ -55,24 +62,38 @@ public class BuilderBodyMotionTestProbe extends BuilderBodyMotionBase { this.getFloat( data, "SnapAngle", v -> this.snapAngle = v, -1.0F, null, BuilderDescriptorState.Experimental, "Snap angle to multiples of value for debugging", null ); - this.getBoolean( + this.avoidOrRelaxedPresent[0] = this.getBoolean( data, "AvoidBlockDamage", v -> this.isAvoidingBlockDamage = v, true, - BuilderDescriptorState.Stable, + BuilderDescriptorState.Deprecated, "Should avoid environmental damage from blocks", null ); - this.getBoolean( + this.legacyOrRelaxedPresent[0] = this.getBoolean( data, "RelaxedMoveConstraints", - v -> this.isRelaxedMoveConstraints = v, + v -> this.isLegacyRelaxedMoveConstraints = v, false, - BuilderDescriptorState.Stable, + BuilderDescriptorState.Deprecated, "NPC can do movements like wading (depends on motion controller type)", null ); + this.avoidOrRelaxedPresent[1] = this.getEnumSet( + data, + "RelaxedConstraints", + s -> this.relaxedConstraints = (EnumSet)s, + RelaxedConstraint.class, + () -> EnumSet.noneOf(RelaxedConstraint.class), + EnumSet.noneOf(RelaxedConstraint.class), + BuilderDescriptorState.Stable, + "List of constraints to relax for this motion (new mode; empty = no relaxed constraints)", + null + ); + this.legacyOrRelaxedPresent[1] = this.avoidOrRelaxedPresent[1]; + this.validateOneOrNonePresent(AVOID_OR_RELAXED_ATTRIBUTES, this.avoidOrRelaxedPresent); + this.validateOneOrNonePresent(LEGACY_OR_RELAXED_ATTRIBUTES, this.legacyOrRelaxedPresent); return this; } @@ -96,7 +117,16 @@ public class BuilderBodyMotionTestProbe extends BuilderBodyMotionBase { return this.isAvoidingBlockDamage; } - public boolean isRelaxedMoveConstraints() { - return this.isRelaxedMoveConstraints; + public boolean isLegacyRelaxedMoveConstraints() { + return this.isLegacyRelaxedMoveConstraints; + } + + @Nonnull + public EnumSet getRelaxedConstraints() { + return this.relaxedConstraints; + } + + public boolean isRelaxedConstraintsPresent() { + return this.avoidOrRelaxedPresent[1]; } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionBeacon.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionBeacon.java index 713f4d20..f1ee807c 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionBeacon.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionBeacon.java @@ -3,10 +3,7 @@ package com.hypixel.hytale.server.npc.corecomponents.entity; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.matrix.Matrix4d; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; @@ -23,11 +20,14 @@ import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.role.support.PositionCache; import com.hypixel.hytale.server.npc.role.support.WorldSupport; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Matrix4d; +import org.joml.Vector3d; +import org.joml.Vector3f; public class ActionBeacon extends ActionBase { protected final String message; @@ -42,12 +42,12 @@ public class ActionBeacon extends ActionBase { public ActionBeacon(@Nonnull BuilderActionBeacon builderActionBeacon, @Nonnull BuilderSupport support) { super(builderActionBeacon); this.message = builderActionBeacon.getMessage(support); - this.range = builderActionBeacon.getRange(); + this.range = builderActionBeacon.getRange(support); this.targetGroups = builderActionBeacon.getTargetGroups(support); this.targetToSendSlot = builderActionBeacon.getTargetToSendSlot(support); this.expirationTime = builderActionBeacon.getExpirationTime(); this.sendCount = builderActionBeacon.getSendCount(); - this.sendList = this.sendCount > 0 ? new ObjectArrayList<>(this.sendCount) : null; + this.sendList = this.sendCount > 0 ? new ReferenceArrayList<>(this.sendCount) : null; } @Override @@ -56,14 +56,14 @@ public class ActionBeacon extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return !super.canExecute(ref, role, sensorInfo, dt, store) ? false : this.targetToSendSlot == Integer.MIN_VALUE || role.getMarkedEntitySupport().hasMarkedEntityInSlot(this.targetToSendSlot); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); Ref target = this.targetToSendSlot >= 0 ? role.getMarkedEntitySupport().getMarkedEntityRef(this.targetToSendSlot) : ref; PositionCache positionCache = role.getPositionCache(); @@ -127,7 +127,6 @@ public class ActionBeacon extends ActionBase { Vector3f color = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); Matrix4d matrix = new Matrix4d(); matrix.identity(); - Matrix4d tmp = new Matrix4d(); TransformComponent transformComponent = componentAccessor.getComponent(self, TransformComponent.getComponentType()); assert transformComponent != null; @@ -149,14 +148,14 @@ public class ActionBeacon extends ActionBase { Vector3d targetPos = targetTransformComponent.getPosition(); ModelComponent targetModelComponent = componentAccessor.getComponent(targetRef, ModelComponent.getComponentType()); float targetEyeHeight = targetModelComponent != null ? targetModelComponent.getModel().getEyeHeight(targetRef, componentAccessor) : 0.0F; - x -= targetPos.getX(); - y -= targetPos.getY() + targetEyeHeight; - z -= targetPos.getZ(); + x -= targetPos.x(); + y -= targetPos.y() + targetEyeHeight; + z -= targetPos.z(); double angleY = Math.atan2(-z, -x); - matrix.rotateAxis(angleY + (float) (Math.PI / 2), 0.0, 1.0, 0.0, tmp); + matrix.rotate(-(angleY + (float) (Math.PI / 2)), 0.0, 1.0, 0.0); double angleX = Math.atan2(Math.sqrt(x * x + z * z), -y); - matrix.rotateAxis(angleX, 1.0, 0.0, 0.0, tmp); - DebugUtils.addArrow(componentAccessor.getExternalData().getWorld(), matrix, color, pos.distanceTo(targetPos), 5.0F, true); + matrix.rotate(-angleX, 1.0, 0.0, 0.0); + DebugUtils.addArrow(componentAccessor.getExternalData().getWorld(), matrix, color, pos.distance(targetPos), 5.0F, DebugUtils.FLAG_FADE); } BeaconSupport beaconSupportComponent = componentAccessor.getComponent(targetRef, BeaconSupport.getComponentType()); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionIgnoreForAvoidance.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionIgnoreForAvoidance.java index 34b06413..0bf0dd00 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionIgnoreForAvoidance.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionIgnoreForAvoidance.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActio import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionIgnoreForAvoidance extends ActionBase { private final int targetSlot; @@ -19,7 +20,7 @@ public class ActionIgnoreForAvoidance extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { role.getMarkedEntitySupport().setTargetSlotToIgnoreForAvoidance(this.targetSlot); return true; } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionReleaseTarget.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionReleaseTarget.java index 6564cd27..80e0240e 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionReleaseTarget.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionReleaseTarget.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActio import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionReleaseTarget extends ActionBase { protected final int targetSlot; @@ -19,7 +20,7 @@ public class ActionReleaseTarget extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); role.getMarkedEntitySupport().clearMarkedEntity(this.targetSlot); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionSetMarkedTarget.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionSetMarkedTarget.java index d517692e..55b6a2bf 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionSetMarkedTarget.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionSetMarkedTarget.java @@ -7,8 +7,10 @@ import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.corecomponents.ActionBase; import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionSetMarkedTarget; import com.hypixel.hytale.server.npc.role.Role; +import com.hypixel.hytale.server.npc.sensorinfo.IPositionProvider; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionSetMarkedTarget extends ActionBase { protected final int targetSlot; @@ -19,9 +21,11 @@ public class ActionSetMarkedTarget extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); - role.getMarkedEntitySupport().setMarkedEntity(this.targetSlot, sensorInfo.getPositionProvider().getTarget()); + IPositionProvider positionProvider = sensorInfo != null ? sensorInfo.getPositionProvider() : null; + Ref target = positionProvider != null ? positionProvider.getTarget() : null; + role.getMarkedEntitySupport().setMarkedEntity(this.targetSlot, target); return true; } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionSetStat.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionSetStat.java index f08c01cd..0e7ef464 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionSetStat.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/ActionSetStat.java @@ -11,6 +11,7 @@ import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActio import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionSetStat extends ActionBase { protected static final ComponentType STAT_MAP_COMPONENT_TYPE = EntityStatMap.getComponentType(); @@ -26,12 +27,12 @@ public class ActionSetStat extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return !super.canExecute(ref, role, sensorInfo, dt, store) ? false : store.getComponent(ref, STAT_MAP_COMPONENT_TYPE).get(this.stat) != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); EntityStatMap entityStatMapComponent = store.getComponent(ref, STAT_MAP_COMPONENT_TYPE); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/HeadMotionWatch.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/HeadMotionWatch.java index 8b7a8aee..f90e5d4d 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/HeadMotionWatch.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/HeadMotionWatch.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.corecomponents.entity; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.IPositionProvider; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class HeadMotionWatch extends HeadMotionBase { protected static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); @@ -59,9 +59,9 @@ public class HeadMotionWatch extends HeadMotionBase { Model model = modelComponent.getModel(); Vector3d position = transformComponent.getPosition(); - x -= position.getX(); - y -= position.getY() + model.getEyeHeight(); - z -= position.getZ(); + x -= position.x(); + y -= position.y() + model.getEyeHeight(); + z -= position.z(); float yaw = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)); float pitch = PhysicsMath.pitchFromDirection(x, y, z); desiredSteering.clearTranslation(); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorBeacon.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorBeacon.java index c9957b6b..289e000b 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorBeacon.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorBeacon.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.corecomponents.entity; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.NPCPlugin; @@ -16,6 +15,7 @@ import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.sensorinfo.EntityPositionProvider; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorBeacon extends SensorBase { protected static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); @@ -65,7 +65,7 @@ public class SensorBeacon extends SensorBase { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - if (targetPosition.distanceSquaredTo(position) > this.range * this.range) { + if (targetPosition.distanceSquared(position) > this.range * this.range) { this.positionProvider.clear(); return false; } else { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorEntityBase.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorEntityBase.java index afb8fdc5..43b14bad 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorEntityBase.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorEntityBase.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -28,6 +27,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.IEntityByPriorityFilter; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class SensorEntityBase extends SensorWithEntityFilters { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorKill.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorKill.java index 6740fa82..5f7f4fb9 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorKill.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorKill.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.entity; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.corecomponents.SensorBase; @@ -13,6 +12,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.sensorinfo.PositionProvider; import com.hypixel.hytale.server.npc.util.DamageData; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorKill extends SensorBase { protected final int targetSlot; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorTarget.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorTarget.java index 594c99c0..6c5fba99 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorTarget.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/SensorTarget.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.corecomponents.entity; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -14,6 +13,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.EntityPositionProvider; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorTarget extends SensorWithEntityFilters { protected static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); @@ -68,7 +68,7 @@ public class SensorTarget extends SensorWithEntityFilters { return false; } - double squaredDistance = position.distanceSquaredTo(targetTransformComponent.getPosition()); + double squaredDistance = position.distanceSquared(targetTransformComponent.getPosition()); if (squaredDistance > this.range * this.range) { return false; } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/builders/BuilderActionBeacon.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/builders/BuilderActionBeacon.java index fa4a2890..95eeca74 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/builders/BuilderActionBeacon.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/builders/BuilderActionBeacon.java @@ -4,6 +4,7 @@ import com.google.gson.JsonElement; import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.asset.builder.holder.AssetArrayHolder; +import com.hypixel.hytale.server.npc.asset.builder.holder.DoubleHolder; import com.hypixel.hytale.server.npc.asset.builder.holder.StringHolder; import com.hypixel.hytale.server.npc.asset.builder.validators.AssetValidator; import com.hypixel.hytale.server.npc.asset.builder.validators.DoubleOrValidator; @@ -20,7 +21,7 @@ import javax.annotation.Nonnull; public class BuilderActionBeacon extends BuilderActionBase { protected final StringHolder message = new StringHolder(); - protected double range; + protected final DoubleHolder range = new DoubleHolder(); protected final AssetArrayHolder targetGroups = new AssetArrayHolder(); protected final StringHolder targetToSendSlot = new StringHolder(); protected double expirationTime; @@ -59,14 +60,7 @@ public class BuilderActionBeacon extends BuilderActionBase { public BuilderActionBeacon readConfig(@Nonnull JsonElement data) { this.requireString(data, "Message", this.message, StringNotEmptyValidator.get(), BuilderDescriptorState.Experimental, "Message to send to targets", null); this.getDouble( - data, - "Range", - d -> this.range = d, - 64.0, - DoubleSingleValidator.greater0(), - BuilderDescriptorState.Experimental, - "The maximum range to send the message", - null + data, "Range", this.range, 64.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "The maximum range to send the message", null ); this.requireAssetArray( data, @@ -116,8 +110,8 @@ public class BuilderActionBeacon extends BuilderActionBase { return this.message.get(support.getExecutionContext()); } - public double getRange() { - return this.range; + public double getRange(@Nonnull BuilderSupport support) { + return this.range.get(support.getExecutionContext()); } public int[] getTargetGroups(@Nonnull BuilderSupport support) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterEntityEffect.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterEntityEffect.java new file mode 100644 index 00000000..d638034b --- /dev/null +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterEntityEffect.java @@ -0,0 +1,33 @@ +package com.hypixel.hytale.server.npc.corecomponents.entity.filters; + +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; +import com.hypixel.hytale.server.npc.corecomponents.EntityFilterBase; +import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterEntityEffect; +import com.hypixel.hytale.server.npc.role.Role; +import javax.annotation.Nonnull; + +public class EntityFilterEntityEffect extends EntityFilterBase { + public static final int COST = 100; + private static final ComponentType EFFECT_CONTROLLER_COMPONENT_TYPE = EffectControllerComponent.getComponentType(); + private final int entityEffectIndex; + + public EntityFilterEntityEffect(@Nonnull BuilderEntityFilterEntityEffect builder, @Nonnull BuilderSupport support) { + this.entityEffectIndex = builder.getEntityEffectIndex(support); + } + + @Override + public boolean matchesEntity(@Nonnull Ref ref, @Nonnull Ref targetRef, @Nonnull Role role, @Nonnull Store store) { + EffectControllerComponent effectController = store.getComponent(targetRef, EFFECT_CONTROLLER_COMPONENT_TYPE); + return effectController == null ? false : effectController.hasEffect(this.entityEffectIndex); + } + + @Override + public int cost() { + return 100; + } +} diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterHeightDifference.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterHeightDifference.java index 600afd83..24a58603 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterHeightDifference.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterHeightDifference.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -14,6 +13,7 @@ import com.hypixel.hytale.server.npc.corecomponents.EntityFilterBase; import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterHeightDifference; import com.hypixel.hytale.server.npc.role.Role; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EntityFilterHeightDifference extends EntityFilterBase { public static final int COST = 200; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterInsideBlock.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterInsideBlock.java index 43d3186c..908fca0b 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterInsideBlock.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterInsideBlock.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.blockset.BlockSetModule; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.Buil import com.hypixel.hytale.server.npc.role.Role; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class EntityFilterInsideBlock extends EntityFilterBase { public static final int COST = 400; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterInventory.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterInventory.java index cc348efb..c469e0e2 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterInventory.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterInventory.java @@ -2,9 +2,7 @@ package com.hypixel.hytale.server.npc.corecomponents.entity.filters; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.server.core.entity.EntityUtils; -import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -41,9 +39,7 @@ public class EntityFilterInventory extends EntityFilterBase { @Override public boolean matchesEntity(@Nonnull Ref ref, @Nonnull Ref targetRef, @Nonnull Role role, @Nonnull Store store) { - LivingEntity entity = (LivingEntity)EntityUtils.getEntity(targetRef, store); - Inventory inventory = entity.getInventory(); - CombinedItemContainer container = inventory.getCombinedHotbarUtilityConsumableStorage(); + CombinedItemContainer container = InventoryComponent.getCombined(store, targetRef, InventoryComponent.HOTBAR_UTILITY_CONSUMABLE_STORAGE); int count = InventoryHelper.countItems(container, this.items); if (count < this.minCount || count > this.maxCount) { return false; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterSpotsMe.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterSpotsMe.java index 717b7517..c902b193 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterSpotsMe.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterSpotsMe.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -14,6 +13,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import com.hypixel.hytale.server.npc.util.ViewTest; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EntityFilterSpotsMe extends EntityFilterBase { public static final int COST = 400; @@ -63,7 +63,7 @@ public class EntityFilterSpotsMe extends EntityFilterBase { assert headRotationComponent != null; return NPCPhysicsMath.inViewSector( - position.getX(), position.getZ(), headRotationComponent.getRotation().getYaw(), this.viewAngle, targetPosition.getX(), targetPosition.getZ() + position.x(), position.z(), headRotationComponent.getRotation().yaw(), this.viewAngle, targetPosition.x(), targetPosition.z() ); } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterStandingOnBlock.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterStandingOnBlock.java index d655be33..f2661363 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterStandingOnBlock.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterStandingOnBlock.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.blockset.BlockSetModule; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.npc.movement.controllers.MotionController; import com.hypixel.hytale.server.npc.role.Role; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class EntityFilterStandingOnBlock extends EntityFilterBase { public static final int COST = 300; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterViewSector.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterViewSector.java index af3af7e3..4450fba1 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterViewSector.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/EntityFilterViewSector.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.corecomponents.entity.filters; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -13,6 +12,7 @@ import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.Buil import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EntityFilterViewSector extends EntityFilterBase { public static final int COST = 300; @@ -25,23 +25,27 @@ public class EntityFilterViewSector extends EntityFilterBase { @Override public boolean matchesEntity(@Nonnull Ref ref, @Nonnull Ref targetRef, @Nonnull Role role, @Nonnull Store store) { - TransformComponent transformComponent = store.getComponent(ref, TRANSFORM_COMPONENT_TYPE); + if (this.viewCone == 0.0F) { + return true; + } else { + TransformComponent transformComponent = store.getComponent(ref, TRANSFORM_COMPONENT_TYPE); - assert transformComponent != null; + assert transformComponent != null; - Vector3d position = transformComponent.getPosition(); - HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); + Vector3d position = transformComponent.getPosition(); + HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); - assert headRotationComponent != null; + assert headRotationComponent != null; - TransformComponent targetTransformComponent = store.getComponent(targetRef, TRANSFORM_COMPONENT_TYPE); + TransformComponent targetTransformComponent = store.getComponent(targetRef, TRANSFORM_COMPONENT_TYPE); - assert targetTransformComponent != null; + assert targetTransformComponent != null; - Vector3d targetPosition = targetTransformComponent.getPosition(); - return NPCPhysicsMath.inViewSector( - position.getX(), position.getZ(), headRotationComponent.getRotation().getYaw(), this.viewCone, targetPosition.getX(), targetPosition.getZ() - ); + Vector3d targetPosition = targetTransformComponent.getPosition(); + return NPCPhysicsMath.inViewSector( + position.x(), position.z(), headRotationComponent.getRotation().yaw(), this.viewCone, targetPosition.x(), targetPosition.z() + ); + } } @Override diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/builders/BuilderEntityFilterEntityEffect.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/builders/BuilderEntityFilterEntityEffect.java new file mode 100644 index 00000000..ff001138 --- /dev/null +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/builders/BuilderEntityFilterEntityEffect.java @@ -0,0 +1,53 @@ +package com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders; + +import com.google.gson.JsonElement; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; +import com.hypixel.hytale.server.npc.asset.builder.Builder; +import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; +import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; +import com.hypixel.hytale.server.npc.asset.builder.holder.AssetHolder; +import com.hypixel.hytale.server.npc.asset.builder.validators.asset.EntityEffectExistsValidator; +import com.hypixel.hytale.server.npc.corecomponents.IEntityFilter; +import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderEntityFilterBase; +import com.hypixel.hytale.server.npc.corecomponents.entity.filters.EntityFilterEntityEffect; +import javax.annotation.Nonnull; + +public class BuilderEntityFilterEntityEffect extends BuilderEntityFilterBase { + protected final AssetHolder entityEffect = new AssetHolder(); + + @Nonnull + @Override + public String getShortDescription() { + return "Check whether an entity has a specific entity effect"; + } + + @Nonnull + @Override + public String getLongDescription() { + return this.getShortDescription(); + } + + @Nonnull + public IEntityFilter build(@Nonnull BuilderSupport builderSupport) { + return new EntityFilterEntityEffect(this, builderSupport); + } + + @Nonnull + @Override + public BuilderDescriptorState getBuilderDescriptorState() { + return BuilderDescriptorState.Stable; + } + + @Nonnull + @Override + public Builder readConfig(@Nonnull JsonElement data) { + this.requireAsset( + data, "EffectId", this.entityEffect, EntityEffectExistsValidator.required(), BuilderDescriptorState.Stable, "The entity effect to check for.", null + ); + return this; + } + + public int getEntityEffectIndex(@Nonnull BuilderSupport support) { + return EntityEffect.getAssetMap().getIndex(this.entityEffect.get(support.getExecutionContext())); + } +} diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/builders/BuilderEntityFilterViewSector.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/builders/BuilderEntityFilterViewSector.java index fbfb1d75..6fc5dcf4 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/builders/BuilderEntityFilterViewSector.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/filters/builders/BuilderEntityFilterViewSector.java @@ -49,7 +49,7 @@ public class BuilderEntityFilterViewSector extends BuilderEntityFilterBase { 0.0, DoubleRangeValidator.between(0.0, 360.0), BuilderDescriptorState.Stable, - "View sector to test entities in", + "View sector to test entities in (0 is treated as 360)", null ); this.requireContext(InstructionType.Any, ComponentContext.NotSelfEntitySensor); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/prioritisers/SensorEntityPrioritiserAttitude.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/prioritisers/SensorEntityPrioritiserAttitude.java index a8fd9ce7..2202008e 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/prioritisers/SensorEntityPrioritiserAttitude.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/prioritisers/SensorEntityPrioritiserAttitude.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.attitude.Attitude; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -21,13 +20,23 @@ import java.util.Arrays; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SensorEntityPrioritiserAttitude implements ISensorEntityPrioritiser { private static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); private final Attitude[] attitudeByPriority; + private final int[] attitudeToPriority; public SensorEntityPrioritiserAttitude(@Nonnull BuilderSensorEntityPrioritiserAttitude builder, @Nonnull BuilderSupport support) { this.attitudeByPriority = builder.getPrioritisedAttitudes(support); + this.attitudeToPriority = new int[Attitude.VALUES.length]; + Arrays.fill(this.attitudeToPriority, -1); + int itr = 0; + int len = this.attitudeByPriority.length; + + while (itr < len) { + this.attitudeToPriority[this.attitudeByPriority[itr].ordinal()] = itr++; + } } @Override @@ -38,13 +47,13 @@ public class SensorEntityPrioritiserAttitude implements ISensorEntityPrioritiser @Nonnull @Override public IEntityByPriorityFilter getNPCPrioritiser() { - return new SensorEntityPrioritiserAttitude.AttitudePrioritiser(this.attitudeByPriority); + return new SensorEntityPrioritiserAttitude.AttitudePrioritiser(this.attitudeToPriority); } @Nonnull @Override public IEntityByPriorityFilter getPlayerPrioritiser() { - return new SensorEntityPrioritiserAttitude.AttitudePrioritiser(this.attitudeByPriority); + return new SensorEntityPrioritiserAttitude.AttitudePrioritiser(this.attitudeToPriority); } @Nonnull @@ -94,24 +103,24 @@ public class SensorEntityPrioritiserAttitude implements ISensorEntityPrioritiser @Nonnull Ref ref, @Nonnull WorldSupport support, @Nonnull Ref targetRef, @Nonnull Store store ) { Attitude attitude = support.getAttitude(ref, targetRef, store); - - for (int i = 0; i < this.attitudeByPriority.length; i++) { - if (attitude == this.attitudeByPriority[i]) { - return i; - } + int priority = this.attitudeToPriority[attitude.ordinal()]; + if (priority == -1) { + throw new IllegalStateException(String.format("Attitude %s was not specified in the priority list but an NPC with that attitude was picked", attitude)); + } else { + return priority; } - - throw new IllegalStateException(String.format("Attitude %s was not specified in the priority list but an NPC with that attitude was picked", attitude)); } public static class AttitudePrioritiser implements IEntityByPriorityFilter { - private final Attitude[] attitudeByPriority; - private final Ref[] targetsByAttitude = new Ref[Attitude.VALUES.length]; + private final int[] attitudeToPriority; + @Nullable + private Ref highestPriorityTarget; + private int highestPriorityIndex = Integer.MAX_VALUE; @Nullable private WorldSupport support; - public AttitudePrioritiser(Attitude[] attitudeByPriority) { - this.attitudeByPriority = attitudeByPriority; + public AttitudePrioritiser(int[] attitudeToPriority) { + this.attitudeToPriority = attitudeToPriority; } @Override @@ -121,31 +130,27 @@ public class SensorEntityPrioritiserAttitude implements ISensorEntityPrioritiser public boolean test(@Nonnull Ref ref, @Nonnull Ref targetRef, @Nonnull ComponentAccessor componentAccessor) { Attitude attitude = this.support.getAttitude(ref, targetRef, componentAccessor); - if (this.targetsByAttitude[attitude.ordinal()] == null) { - this.targetsByAttitude[attitude.ordinal()] = targetRef; + int attitudeIdx = attitude.ordinal(); + int priority = this.attitudeToPriority[attitudeIdx]; + if (priority != -1 && priority < this.highestPriorityIndex) { + this.highestPriorityIndex = priority; + this.highestPriorityTarget = targetRef; } - return attitude == this.attitudeByPriority[0]; + return this.highestPriorityIndex == 0; } @Nullable @Override public Ref getHighestPriorityTarget() { - for (int i = 0; i < this.attitudeByPriority.length; i++) { - int attitudeIdx = this.attitudeByPriority[i].ordinal(); - Ref target = this.targetsByAttitude[attitudeIdx]; - if (target != null) { - return target; - } - } - - return null; + return this.highestPriorityTarget; } @Override public void cleanup() { this.support = null; - Arrays.fill(this.targetsByAttitude, null); + this.highestPriorityTarget = null; + this.highestPriorityIndex = Integer.MAX_VALUE; } } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/entity/prioritisers/SensorEntityPrioritiserDefault.java b/src/com/hypixel/hytale/server/npc/corecomponents/entity/prioritisers/SensorEntityPrioritiserDefault.java index 2f8e60bb..03fef535 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/entity/prioritisers/SensorEntityPrioritiserDefault.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/entity/prioritisers/SensorEntityPrioritiserDefault.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.corecomponents.IEntityFilter; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.npc.util.IEntityByPriorityFilter; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SensorEntityPrioritiserDefault implements ISensorEntityPrioritiser { private static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/interaction/ActionLockOnInteractionTarget.java b/src/com/hypixel/hytale/server/npc/corecomponents/interaction/ActionLockOnInteractionTarget.java index c577a4e3..23c4002e 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/interaction/ActionLockOnInteractionTarget.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/interaction/ActionLockOnInteractionTarget.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.corecomponents.interaction.builders.Builder import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionLockOnInteractionTarget extends ActionBase { protected final int targetSlot; @@ -19,12 +20,12 @@ public class ActionLockOnInteractionTarget extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && role.getStateSupport().getInteractionIterationTarget() != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); Ref target = role.getStateSupport().getInteractionIterationTarget(); role.getMarkedEntitySupport().setMarkedEntity(this.targetSlot, target); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/interaction/ActionSetInteractable.java b/src/com/hypixel/hytale/server/npc/corecomponents/interaction/ActionSetInteractable.java index 428b477f..61dee0f5 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/interaction/ActionSetInteractable.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/interaction/ActionSetInteractable.java @@ -26,12 +26,12 @@ public class ActionSetInteractable extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && role.getStateSupport().getInteractionIterationTarget() != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); StateSupport stateSupport = role.getStateSupport(); stateSupport.setInteractable(ref, stateSupport.getInteractionIterationTarget(), this.setTo, this.hint, this.showPrompt, store); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/interaction/SensorCanInteract.java b/src/com/hypixel/hytale/server/npc/corecomponents/interaction/SensorCanInteract.java index 1d98f5a0..6e8fedd5 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/interaction/SensorCanInteract.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/interaction/SensorCanInteract.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.corecomponents.interaction; import com.hypixel.hytale.component.Archetype; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.attitude.Attitude; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -17,6 +16,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import java.util.EnumSet; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorCanInteract extends SensorBase { protected final float viewCone; @@ -56,10 +56,8 @@ public class SensorCanInteract extends SensorBase { assert headRotationComponent != null; Vector3d position = transformComponent.getPosition(); - float headRotationYaw = headRotationComponent.getRotation().getYaw(); - if (!NPCPhysicsMath.inViewSector( - position.getX(), position.getZ(), headRotationYaw, this.viewCone, targetPosition.getX(), targetPosition.getZ() - )) { + float headRotationYaw = headRotationComponent.getRotation().yaw(); + if (!NPCPhysicsMath.inViewSector(position.x(), position.z(), headRotationYaw, this.viewCone, targetPosition.x(), targetPosition.z())) { return false; } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionDropItem.java b/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionDropItem.java index fe37bdf0..754fd399 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionDropItem.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionDropItem.java @@ -4,7 +4,8 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.entity.ItemUtils; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; @@ -21,6 +22,8 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.AimingHelper; import com.hypixel.hytale.server.npc.util.InventoryHelper; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.joml.Vector3d; public class ActionDropItem extends ActionWithDelay { protected final String item; @@ -54,12 +57,12 @@ public class ActionDropItem extends ActionWithDelay { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && !this.isDelaying(); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); this.prepareDelay(); this.startDelay(role.getEntitySupport()); @@ -101,10 +104,11 @@ public class ActionDropItem extends ActionWithDelay { if (headRotationComponent != null) { direction = headRotationComponent.getDirection(); } else { - direction = transformComponent.getRotation().toVector3d(); + Rotation3f rotation = transformComponent.getRotation(); + direction = Vector3dUtil.setYawPitch(rotation.yaw(), rotation.pitch(), new Vector3d()); } - this.dropDirection.assign(direction); + this.dropDirection.set(direction); this.dropDirection.rotateY(RandomExtra.randomRange(this.dropSectorStart, this.dropSectorEnd)); if (!AimingHelper.computePitch(distance, height, this.throwSpeed, 32.0, this.pitch)) { throw new IllegalStateException( diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionInventory.java b/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionInventory.java index f5a59fc1..d9c51d5f 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionInventory.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionInventory.java @@ -6,6 +6,7 @@ import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.modules.item.ItemModule; @@ -15,6 +16,7 @@ import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.corecomponents.ActionBase; import com.hypixel.hytale.server.npc.corecomponents.items.builders.BuilderActionInventory; import com.hypixel.hytale.server.npc.role.Role; +import com.hypixel.hytale.server.npc.sensorinfo.IPositionProvider; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.InventoryHelper; import java.util.EnumSet; @@ -24,6 +26,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class ActionInventory extends ActionBase { + @Nonnull private static final EnumSet ITEM_FREE_OPERATIONS = EnumSet.of( ActionInventory.Operation.ClearHeldItem, ActionInventory.Operation.RemoveHeldItem, @@ -54,25 +57,27 @@ public class ActionInventory extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nonnull InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); - Ref entityRef = this.useTarget ? sensorInfo.getPositionProvider().getTarget() : ref; - LivingEntity entity = (LivingEntity)EntityUtils.getEntity(entityRef, store); - if (entity == null) { + IPositionProvider positionProvider = sensorInfo != null ? sensorInfo.getPositionProvider() : null; + Ref targetRef = this.useTarget ? (positionProvider != null ? positionProvider.getTarget() : null) : ref; + if (targetRef == null) { + return false; + } else if (!(EntityUtils.getEntity(targetRef, store) instanceof LivingEntity livingEntity)) { return false; } else { - Inventory inventory = entity.getInventory(); + Inventory var15 = livingEntity.getInventory(); if (this.operation == ActionInventory.Operation.ClearHeldItem) { - InventoryHelper.clearItemInHand(inventory, (byte)-1); + InventoryHelper.clearItemInHand(targetRef, var15, (byte)-1, store); return true; } else if (this.operation == ActionInventory.Operation.RemoveHeldItem) { - InventoryHelper.removeItemInHand(inventory, this.count); + InventoryHelper.removeItemInHand(var15, this.count); return true; } else if (this.operation != ActionInventory.Operation.EquipHotbar || this.item != null && !this.item.isEmpty()) { if (this.operation != ActionInventory.Operation.EquipOffHand || this.item != null && !this.item.isEmpty()) { String itemStackKey = this.item; if (itemStackKey != null && !"Empty".equals(itemStackKey) && !"Unknown".equals(itemStackKey) && ItemModule.exists(itemStackKey)) { - CombinedItemContainer combinedStorage = inventory.getCombinedHotbarFirst(); + CombinedItemContainer combinedStorage = InventoryComponent.getCombined(store, targetRef, InventoryComponent.HOTBAR_FIRST); ItemStack itemStack = new ItemStack(itemStackKey, this.count); switch (this.operation) { case Add: @@ -88,39 +93,39 @@ public class ActionInventory extends ActionBase { case Equip: Item item = itemStack.getItem(); if (item.getArmor() != null) { - InventoryHelper.useArmor(inventory.getArmor(), itemStack); + InventoryHelper.useArmor(var15.getArmor(), itemStack); } else { - InventoryHelper.useItem(inventory, item.getId()); + InventoryHelper.useItem(targetRef, var15, item.getId(), store); } case ClearHeldItem: case RemoveHeldItem: default: break; case SetHotbar: - if (InventoryHelper.checkHotbarSlot(inventory, this.slot)) { - inventory.getHotbar().setItemStackForSlot(this.slot, itemStack); + if (InventoryHelper.checkHotbarSlot(var15, this.slot)) { + var15.getHotbar().setItemStackForSlot(this.slot, itemStack); } break; case EquipHotbar: - if (InventoryHelper.checkHotbarSlot(inventory, this.slot)) { - inventory.getHotbar().setItemStackForSlot(this.slot, itemStack); + if (InventoryHelper.checkHotbarSlot(var15, this.slot)) { + var15.getHotbar().setItemStackForSlot(this.slot, itemStack); } - if (inventory.getActiveHotbarSlot() != this.slot && InventoryHelper.checkHotbarSlot(inventory, this.slot)) { - inventory.setActiveHotbarSlot(this.slot); + if (var15.getActiveHotbarSlot() != this.slot && InventoryHelper.checkHotbarSlot(var15, this.slot)) { + var15.setActiveHotbarSlot(targetRef, this.slot, store); } break; case SetOffHand: - if (InventoryHelper.checkOffHandSlot(inventory, this.slot)) { - inventory.getUtility().setItemStackForSlot(this.slot, itemStack); + if (InventoryHelper.checkOffHandSlot(var15, this.slot)) { + var15.getUtility().setItemStackForSlot(this.slot, itemStack); } break; case EquipOffHand: - if (InventoryHelper.checkOffHandSlot(inventory, this.slot)) { - inventory.getUtility().setItemStackForSlot(this.slot, itemStack); + if (InventoryHelper.checkOffHandSlot(var15, this.slot)) { + var15.getUtility().setItemStackForSlot(this.slot, itemStack); } - InventoryHelper.setOffHandSlot(inventory, this.slot); + InventoryHelper.setOffHandSlot(targetRef, var15, this.slot, store); } return true; @@ -129,14 +134,14 @@ public class ActionInventory extends ActionBase { return true; } } else { - InventoryHelper.setOffHandSlot(inventory, this.slot); + InventoryHelper.setOffHandSlot(targetRef, var15, this.slot, store); return true; } - } else if (inventory.getActiveHotbarSlot() == this.slot) { + } else if (var15.getActiveHotbarSlot() == this.slot) { return true; } else { - if (InventoryHelper.checkHotbarSlot(inventory, this.slot)) { - inventory.setActiveHotbarSlot(this.slot); + if (InventoryHelper.checkHotbarSlot(var15, this.slot)) { + var15.setActiveHotbarSlot(targetRef, this.slot, store); } return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionPickUpItem.java b/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionPickUpItem.java index 3cb6be54..5a6bcbfe 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionPickUpItem.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/items/ActionPickUpItem.java @@ -5,15 +5,14 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; +import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.corecomponents.ActionWithDelay; import com.hypixel.hytale.server.npc.corecomponents.items.builders.BuilderActionPickUpItem; -import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.InventoryHelper; @@ -21,6 +20,7 @@ import java.util.List; import java.util.function.Supplier; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ActionPickUpItem extends ActionWithDelay { protected static final ComponentType ITEM_COMPONENT_TYPE = ItemComponent.getComponentType(); @@ -75,7 +75,7 @@ public class ActionPickUpItem extends ActionWithDelay { assert targetTransformComponent != null; Vector3d targetPosition = targetTransformComponent.getPosition(); - double distanceSquared = selfPosition.distanceSquaredTo(targetPosition); + double distanceSquared = selfPosition.distanceSquared(targetPosition); if (distanceSquared > this.range * this.range) { return false; } @@ -104,17 +104,14 @@ public class ActionPickUpItem extends ActionWithDelay { if (itemRef == null) { return false; } else { - NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType()); - - assert npcComponent != null; - - Inventory inventory = npcComponent.getInventory(); switch (this.storageTarget) { case Hotbar: - ItemComponent.addToItemContainer(store, itemRef, inventory.getCombinedHotbarFirst()); + CombinedItemContainer combinedInventoryHotbarFirst = InventoryComponent.getCombined(store, ref, InventoryComponent.HOTBAR_FIRST); + ItemComponent.addToItemContainer(store, itemRef, combinedInventoryHotbarFirst); break; case Inventory: - ItemComponent.addToItemContainer(store, itemRef, inventory.getCombinedStorageFirst()); + CombinedItemContainer combinedInventoryStorageFirst = InventoryComponent.getCombined(store, ref, InventoryComponent.STORAGE_FIRST); + ItemComponent.addToItemContainer(store, itemRef, combinedInventoryStorageFirst); break; case Destroy: store.removeEntity(itemRef, RemoveReason.REMOVE); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/items/SensorDroppedItem.java b/src/com/hypixel/hytale/server/npc/corecomponents/items/SensorDroppedItem.java index 438ab446..8059342b 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/items/SensorDroppedItem.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/items/SensorDroppedItem.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.attitude.Attitude; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; @@ -23,6 +22,7 @@ import java.util.EnumSet; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SensorDroppedItem extends SensorBase { protected static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); @@ -61,7 +61,7 @@ public class SensorDroppedItem extends SensorBase { assert headRotationComponent != null; - this.heading = headRotationComponent.getRotation().getYaw(); + this.heading = headRotationComponent.getRotation().yaw(); Ref droppedItem = role.getPositionCache() .getClosestDroppedItemInRange( ref, @@ -104,7 +104,7 @@ public class SensorDroppedItem extends SensorBase { assert itemTransformComponent != null; Vector3d itemPosition = itemTransformComponent.getPosition(); - if (!NPCPhysicsMath.inViewSector(selfPosition.getX(), selfPosition.getZ(), this.heading, this.viewCone, itemPosition.getX(), itemPosition.getZ())) { + if (!NPCPhysicsMath.inViewSector(selfPosition.x(), selfPosition.z(), this.heading, this.viewCone, itemPosition.x(), itemPosition.z())) { return false; } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDelayDespawn.java b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDelayDespawn.java index a07eb158..82f6f3db 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDelayDespawn.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDelayDespawn.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionDelayDespawn extends ActionBase { protected final float time; @@ -21,7 +22,7 @@ public class ActionDelayDespawn extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); NPCEntity npcEntity = store.getComponent(ref, NPCEntity.getComponentType()); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDespawn.java b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDespawn.java index 65b66fb8..6c9913a6 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDespawn.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDespawn.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionDespawn extends ActionBase { protected final boolean force; @@ -20,7 +21,7 @@ public class ActionDespawn extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); if (this.force) { store.removeEntity(ref, RemoveReason.REMOVE); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDie.java b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDie.java index 9b072e64..00314cee 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDie.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionDie.java @@ -11,6 +11,7 @@ import com.hypixel.hytale.server.npc.corecomponents.lifecycle.builders.BuilderAc import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionDie extends ActionBase { public ActionDie(@Nonnull BuilderActionDie builder) { @@ -18,7 +19,7 @@ public class ActionDie extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); DeathComponent.tryAddComponent(store, ref, new Damage(Damage.NULL_SOURCE, DamageCause.PHYSICAL, 0.0F)); role.setReachedTerminalAction(true); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionRole.java b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionRole.java index 1913faa4..b62d12b1 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionRole.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionRole.java @@ -39,12 +39,12 @@ public class ActionRole extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && this.roleIndex >= 0; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); if (role.isRoleChangeRequested()) { return false; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionSpawn.java b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionSpawn.java index 708928fe..fbce1509 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionSpawn.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/lifecycle/ActionSpawn.java @@ -3,8 +3,7 @@ package com.hypixel.hytale.server.npc.corecomponents.lifecycle; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; @@ -31,6 +30,7 @@ import it.unimi.dsi.fastutil.Pair; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ActionSpawn extends ActionBase { protected final float spawnDirection; @@ -46,7 +46,7 @@ public class ActionSpawn extends ActionBase { protected final double minDelay; protected final double maxDelay; protected final Vector3d position = new Vector3d(); - protected final Vector3f rotation = new Vector3f(); + protected final Rotation3f rotation = new Rotation3f(); protected final boolean launchAtTarget; protected final boolean pitchHigh; protected final Vector3d targetPosition = new Vector3d(); @@ -171,30 +171,30 @@ public class ActionSpawn extends ActionBase { ModelComponent modelComponent = store.getComponent(ref, ModelComponent.getComponentType()); Vector3d position = transformComponent.getPosition(); - Vector3f bodyRotation = transformComponent.getRotation(); + Rotation3f bodyRotation = transformComponent.getRotation(); double x; double z; double y; if (this.launchAtTarget) { float eyeHeight = modelComponent != null ? modelComponent.getModel().getEyeHeight() : 0.0F; - x = position.getX(); - z = position.getZ(); - y = position.getY() + eyeHeight; + x = position.x(); + z = position.z(); + y = position.y() + eyeHeight; } else { double distance = RandomExtra.randomRange(this.minDistance, this.maxDistance); - float yaw = bodyRotation.getYaw() + this.yaw0; + float yaw = bodyRotation.yaw() + this.yaw0; if (!this.fanOut) { yaw += RandomExtra.randomRange(0.0F, this.spawnAngle); } - x = position.getX() + PhysicsMath.headingX(yaw) * distance; - z = position.getZ() + PhysicsMath.headingZ(yaw) * distance; - y = position.getY(); + x = position.x() + PhysicsMath.headingX(yaw) * distance; + z = position.z() + PhysicsMath.headingZ(yaw) * distance; + y = position.y(); } if (spawningContext.set(world, x, y, z) && spawningContext.canSpawn() == SpawnTestResult.TEST_OK) { - this.position.assign(spawningContext.xSpawn, spawningContext.ySpawn, spawningContext.zSpawn); - this.rotation.assign(bodyRotation); + this.position.set(spawningContext.xSpawn, spawningContext.ySpawn, spawningContext.zSpawn); + this.rotation.set(bodyRotation); FlockAsset flockDefinition = this.flock != null ? FlockAsset.getAssetMap().getAsset(this.flock) : null; Pair, NPCEntity> npcPair = NPCPlugin.get() .spawnEntity(store, this.roleIndex, this.position, this.rotation, spawningContext.getModel(), this::postSpawn); @@ -256,13 +256,13 @@ public class ActionSpawn extends ActionBase { Role role = npcComponent.getRole(); Vector3d position = transformComponent.getPosition(); - this.launchDirection.assign(this.targetPosition).subtract(position).normalize(); - double distance = position.distanceTo(this.targetPosition); + this.launchDirection.set(this.targetPosition).sub(position).normalize(); + double distance = position.distance(this.targetPosition); if (role.getActiveMotionController() instanceof MotionControllerFly flyController) { double endVelocity = flyController.getMinSpeedAfterForceSquared(); double acceleration = -flyController.getDampingDeceleration(); double v0 = Math.sqrt(endVelocity - 2.0 * acceleration * distance); - this.launchDirection.scale(v0); + this.launchDirection.mul(v0); role.forceVelocity(this.launchDirection, null, false); } else { double height = this.targetPosition.y - position.y; @@ -277,7 +277,7 @@ public class ActionSpawn extends ActionBase { } else { float heading = PhysicsMath.headingFromDirection(this.launchDirection.x, this.launchDirection.z); PhysicsMath.vectorFromAngles(heading, this.pitchHigh ? this.pitch[1] : this.pitch[0], this.launchDirection).normalize(); - this.launchDirection.scale(throwSpeed); + this.launchDirection.mul(throwSpeed); role.forceVelocity(this.launchDirection, null, true); } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/ActionCrouch.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/ActionCrouch.java index 539bc5a9..d88f66ac 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/ActionCrouch.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/ActionCrouch.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderActionBase; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionCrouch extends ActionBase { private final boolean crouching; @@ -19,7 +20,7 @@ public class ActionCrouch extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); MovementStatesComponent movementStatesComponent = store.getComponent(ref, MovementStatesComponent.getComponentType()); if (movementStatesComponent != null) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/ActionOverrideAltitude.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/ActionOverrideAltitude.java index 47b9585d..e82e43a4 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/ActionOverrideAltitude.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/ActionOverrideAltitude.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.npc.movement.controllers.MotionControllerFly; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionOverrideAltitude extends ActionBase { private final double[] desiredRange; @@ -20,12 +21,12 @@ public class ActionOverrideAltitude extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && "Fly".equals(role.getActiveMotionController().getType()); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); ((MotionControllerFly)role.getActiveMotionController()).setDesiredAltitudeOverride(this.desiredRange); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFind.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFind.java index f3733bb3..442cf383 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFind.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFind.java @@ -2,8 +2,7 @@ package com.hypixel.hytale.server.npc.corecomponents.movement; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.NPCPlugin; @@ -21,6 +20,7 @@ import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionFind extends BodyMotionFindWithTarget { protected final double distance; @@ -160,7 +160,7 @@ public class BodyMotionFind extends BodyMotionFindWithTarget { @Override public float estimateToGoal(@Nonnull AStarBase aStarBase, @Nonnull Vector3d fromPosition, MotionController motionController) { - return (float)((AStarWithTarget)aStarBase).getTargetPosition().distanceTo(fromPosition); + return (float)((AStarWithTarget)aStarBase).getTargetPosition().distance(fromPosition); } @Override @@ -190,11 +190,11 @@ public class BodyMotionFind extends BodyMotionFindWithTarget { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - Vector3f bodyRotation = transformComponent.getRotation(); - this.tempDirectionVector.assign(this.getLastTargetPosition()).subtract(position); - steering.setYaw(NPCPhysicsMath.headingFromDirection(this.tempDirectionVector.x, this.tempDirectionVector.z, bodyRotation.getYaw())); + Rotation3f bodyRotation = transformComponent.getRotation(); + this.tempDirectionVector.set(this.getLastTargetPosition()).sub(position); + steering.setYaw(NPCPhysicsMath.headingFromDirection(this.tempDirectionVector.x, this.tempDirectionVector.z, bodyRotation.yaw())); steering.setPitch( - NPCPhysicsMath.pitchFromDirection(this.tempDirectionVector.x, this.tempDirectionVector.y, this.tempDirectionVector.z, bodyRotation.getPitch()) + NPCPhysicsMath.pitchFromDirection(this.tempDirectionVector.x, this.tempDirectionVector.y, this.tempDirectionVector.z, bodyRotation.pitch()) ); } @@ -208,7 +208,7 @@ public class BodyMotionFind extends BodyMotionFindWithTarget { if (this.isBoundingBoxesOverlapping(position, targetPosition)) { return true; } else { - Vector3d direction = this.tempDirectionVector.assign(targetPosition).subtract(position); + Vector3d direction = this.tempDirectionVector.set(targetPosition).sub(position); motionController.probeMove(ref, position, direction, this.probeMoveData, componentAccessor); return this.isBoundingBoxesOverlapping(this.probeMoveData.probePosition, targetPosition); } @@ -238,4 +238,15 @@ public class BodyMotionFind extends BodyMotionFindWithTarget { v -= p; return v >= min && v < max; } + + @Override + public double getDesiredTargetDistance() { + return this.distance; + } + + @Nullable + @Override + public Ref getDesiredTargetEntity() { + return this.lastDesiredTargetEntity; + } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFindBase.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFindBase.java index 436bce2d..4aa3acc1 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFindBase.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFindBase.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.NPCPlugin; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.npc.corecomponents.BodyMotionBase; import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionFindBase; import com.hypixel.hytale.server.npc.movement.NavState; import com.hypixel.hytale.server.npc.movement.Steering; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import com.hypixel.hytale.server.npc.movement.controllers.MotionController; import com.hypixel.hytale.server.npc.movement.controllers.ProbeMoveData; import com.hypixel.hytale.server.npc.movement.steeringforces.SteeringForceWithTarget; @@ -24,17 +24,20 @@ import com.hypixel.hytale.server.npc.navigation.AStarEvaluator; import com.hypixel.hytale.server.npc.navigation.AStarNode; import com.hypixel.hytale.server.npc.navigation.AStarNodePoolProvider; import com.hypixel.hytale.server.npc.navigation.AStarNodePoolProviderSimple; +import com.hypixel.hytale.server.npc.navigation.IWaypoint; import com.hypixel.hytale.server.npc.navigation.PathFollower; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.role.RoleDebugFlags; +import com.hypixel.hytale.server.npc.role.support.DebugSupport; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import java.util.EnumSet; import java.util.function.Supplier; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; -public abstract class BodyMotionFindBase extends BodyMotionBase implements AStarEvaluator { +public abstract class BodyMotionFindBase extends BodyMotionBase implements AStarEvaluator, DebugSupport.DebugFlagsChangeListener { protected final int nodesPerTick; protected final boolean useBestPath; protected final double throttleDelayMin; @@ -46,8 +49,6 @@ public abstract class BodyMotionFindBase extends BodyMotion protected final double minPathLength; protected final double minPathLengthSquared; protected final boolean canSkipSteering; - protected final boolean isAvoidingBlockDamage; - protected final boolean isRelaxedMoveConstraints; protected final double desiredAltitudeWeight; protected final boolean dbgStatus; protected final boolean dbgProfile; @@ -72,7 +73,14 @@ public abstract class BodyMotionFindBase extends BodyMotion protected double throttleDelay; protected boolean passedWaypoint; protected boolean wasAvoidingBlockDamage; + protected boolean wasUnableToBreathe; protected boolean dbgDisplayString; + protected boolean visPath; + @Nullable + protected DebugSupport cachedDebugSupport; + @Nonnull + protected final EnumSet cachedEffectiveConstraints; + protected final Vector3d lastSeekVisTarget = new Vector3d(Double.NaN, Double.NaN, Double.NaN); protected StringBuilder debugString; public BodyMotionFindBase(@Nonnull BuilderBodyMotionFindBase builderBodyMotionFindBase, @Nonnull BuilderSupport support, @Nonnull T aStar) { @@ -91,10 +99,15 @@ public abstract class BodyMotionFindBase extends BodyMotion this.throttleDelayMin = throttleDelayRange[0]; this.throttleDelayMax = throttleDelayRange[1]; this.throttleIgnoreCount = builderBodyMotionFindBase.getThrottleIgnoreCount(support); - this.isRelaxedMoveConstraints = builderBodyMotionFindBase.isRelaxedMoveConstraints(support); - this.isAvoidingBlockDamage = builderBodyMotionFindBase.isAvoidingBlockDamage(support); + boolean isLegacyRelaxedMoveConstraints = builderBodyMotionFindBase.isLegacyRelaxedMoveConstraints(support); + boolean usesLegacyConstraintMode = !builderBodyMotionFindBase.isRelaxedConstraintsPresent(); + EnumSet relaxedConstraints = builderBodyMotionFindBase.getRelaxedConstraints(support); + boolean isLegacyAvoidingBlockDamage = builderBodyMotionFindBase.isAvoidingBlockDamage(support); + this.cachedEffectiveConstraints = computeEffectiveRelaxedConstraints( + usesLegacyConstraintMode, relaxedConstraints, isLegacyRelaxedMoveConstraints, isLegacyAvoidingBlockDamage + ); this.desiredAltitudeWeight = builderBodyMotionFindBase.getDesiredAltitudeWeight(support); - this.probeMoveData.setRelaxedMoveConstraints(this.isRelaxedMoveConstraints); + this.probeMoveData.setRelaxedConstraints(this.cachedEffectiveConstraints); this.pathFollower.setPathSmoothing(builderBodyMotionFindBase.getPathSmoothing(support)); this.pathFollower.setRelativeSpeed(builderBodyMotionFindBase.getRelativeSpeed(support)); this.pathFollower.setRelativeSpeedWaypoint(builderBodyMotionFindBase.getRelativeSpeedWaypoint(support)); @@ -120,6 +133,12 @@ public abstract class BodyMotionFindBase extends BodyMotion this.pathFollower.setDebugNodes(this.dbgNodes); } + @Nullable + @Override + public EnumSet getRelaxedConstraints() { + return this.cachedEffectiveConstraints; + } + @Override public void activate(@Nonnull Ref ref, @Nonnull Role role, @Nonnull ComponentAccessor componentAccessor) { TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); @@ -127,16 +146,25 @@ public abstract class BodyMotionFindBase extends BodyMotion assert transformComponent != null; MotionController activeMotionController = role.getActiveMotionController(); + DebugSupport debugSupport = role.getDebugSupport(); this.sharedNodePoolProvider = componentAccessor.getResource(AStarNodePoolProviderSimple.getResourceType()); - this.dbgDisplayString = role.getDebugSupport().getDebugFlags().contains(RoleDebugFlags.Pathfinder); + this.dbgDisplayString = debugSupport.getDebugFlags().contains(RoleDebugFlags.Pathfinder); + this.visPath = debugSupport.isVisPath(); + this.cachedDebugSupport = debugSupport; + debugSupport.registerDebugFlagsListener(this); this.setNavStateInit(activeMotionController); this.wasSteering = false; - this.wasAvoidingBlockDamage = this.isAvoidingBlockDamage && activeMotionController.isAvoidingBlockDamage(); + this.wasAvoidingBlockDamage = !this.cachedEffectiveConstraints.contains(RelaxedConstraint.DAMAGE) && activeMotionController.isAvoidingBlockDamage(); + this.wasUnableToBreathe = !role.couldBreatheCached(); this.aStar.setStartPosition(transformComponent.getPosition()); } @Override public void deactivate(@Nonnull Ref ref, @Nonnull Role role, @Nonnull ComponentAccessor componentAccessor) { + role.getDebugSupport().removeDebugFlagsListener(this); + role.getDebugSupport().clearPathVisualization(); + this.cachedDebugSupport = null; + this.lastSeekVisTarget.x = this.lastSeekVisTarget.y = this.lastSeekVisTarget.z = Double.NaN; this.pathFollower.clearPath(); this.aStar.clearPath(); } @@ -162,12 +190,9 @@ public abstract class BodyMotionFindBase extends BodyMotion this.wasSteering = false; return false; } else { - if (!this.isAvoidingBlockDamage) { - activeMotionController.setAvoidingBlockDamage(false); - } - - activeMotionController.setRelaxedMoveConstraints(this.isRelaxedMoveConstraints); - this.probeMoveData.setAvoidingBlockDamage(activeMotionController.isAvoidingBlockDamage()); + activeMotionController.setRelaxedMoveConstraints(this.cachedEffectiveConstraints); + this.probeMoveData.setRelaxedConstraints(this.cachedEffectiveConstraints); + applyEscapeConstraints(role, this.probeMoveData); if (this.isGoalReached(ref, activeMotionController, position, componentAccessor)) { this.setNavStateAtGoal(role.getActiveMotionController()); this.wasSteering = false; @@ -218,6 +243,15 @@ public abstract class BodyMotionFindBase extends BodyMotion this.setNavStateSteering(role.getActiveMotionController()); this.onSteering(activeMotionController, ref, componentAccessor); this.wasSteering = true; + if (this.visPath && this.cachedDebugSupport != null) { + Vector3d steeringTarget = this.getSteeringTargetPosition(); + if (steeringTarget != null && !steeringTarget.equals(this.lastSeekVisTarget)) { + this.cachedDebugSupport.clearPathVisualization(); + this.cachedDebugSupport.recordPathWaypoint(steeringTarget, true, true, true); + this.lastSeekVisTarget.set(steeringTarget); + } + } + if (this.dbgMotionState) { NPCPlugin.get().getLogger().at(Level.INFO).every(100).log("MotionFindBase: Steering"); } @@ -264,7 +298,7 @@ public abstract class BodyMotionFindBase extends BodyMotion } } - if (this.pathFollower.pathInFinalStage() && activeMotionController.canAct(ref, componentAccessor)) { + if (this.pathFollower.pathInFinalStage() && activeMotionController.canSteer(ref, componentAccessor)) { if (this.pathFollower.getCurrentWaypoint() == null) { if (this.aStar.getPath() != null) { this.setPath(ref, position, activeMotionController, componentAccessor); @@ -308,7 +342,7 @@ public abstract class BodyMotionFindBase extends BodyMotion } else if (this.pathFollower.isWaypointFrozen()) { this.aStar.clearPath(); Vector3d targetPosition = this.pathFollower.getCurrentWaypointPosition(); - if (targetPosition.distanceSquaredTo(position) < 1.0) { + if (targetPosition.distanceSquared(position) < 1.0) { this.pathFollower.setWaypointFrozen(false); if (this.canSkipSteering && this.shouldSkipSteering(ref, activeMotionController, targetPosition, componentAccessor)) { this.startPathFinder(ref, position, role, activeMotionController, componentAccessor); @@ -353,7 +387,7 @@ public abstract class BodyMotionFindBase extends BodyMotion } } - if (activeMotionController.canAct(ref, componentAccessor) && !this.dbgStay) { + if (activeMotionController.canSteer(ref, componentAccessor) && !this.dbgStay) { this.pathFollower.executePath(position, activeMotionController, desiredSteering); if (this.dbgMotionState) { NPCPlugin.get().getLogger().at(Level.INFO).every(100).log("MotionFindBase: Executing path"); @@ -441,7 +475,7 @@ public abstract class BodyMotionFindBase extends BodyMotion this.onNoPathFound(activeMotionController); return false; } else { - double pathLengthSquared = this.aStar.getEndPosition().distanceSquaredTo(this.aStar.getPosition()); + double pathLengthSquared = this.aStar.getEndPosition().distanceSquared(this.aStar.getPosition()); if (pathLengthSquared < this.minPathLengthSquared) { if (this.dbgStatus) { NPCPlugin.get().getLogger().at(Level.INFO).log("Path computation failed. Path to short length=%s", Math.sqrt(pathLengthSquared)); @@ -472,11 +506,18 @@ public abstract class BodyMotionFindBase extends BodyMotion @Nonnull ComponentAccessor componentAccessor ) { if (!this.pathFollower.updateCurrentTarget(position, activeMotionController)) { + if (this.visPath && this.cachedDebugSupport != null) { + this.cachedDebugSupport.clearPathVisualization(); + } + return false; } else { if (this.pathFollower.shouldSmoothPath()) { this.passedWaypoint = true; this.pathFollower.smoothPath(ref, position, activeMotionController, this.probeMoveData, componentAccessor); + if (this.visPath && this.cachedDebugSupport != null) { + this.recordPathVisualization(this.cachedDebugSupport); + } } return true; @@ -501,6 +542,11 @@ public abstract class BodyMotionFindBase extends BodyMotion return false; } + @Nullable + protected Vector3d getSteeringTargetPosition() { + return null; + } + protected boolean scaleSteering( @Nonnull Ref ref, @Nonnull Role role, @@ -531,7 +577,6 @@ public abstract class BodyMotionFindBase extends BodyMotion } } - motionController.requireDepthProbing(); return true; } } @@ -637,6 +682,10 @@ public abstract class BodyMotionFindBase extends BodyMotion AStarNode aStarPath = this.aStar.getPath(); this.pathFollower.setPath(aStarPath, position); this.passedWaypoint = false; + if (this.visPath && this.cachedDebugSupport != null) { + this.recordPathVisualization(this.cachedDebugSupport); + } + this.updatePathFollower(ref, position, activeMotionController, componentAccessor); } @@ -686,7 +735,18 @@ public abstract class BodyMotionFindBase extends BodyMotion return true; } else { - return false; + boolean cannotBreathe = !activeMotionController.getRole().couldBreatheCached(); + if (this.wasUnableToBreathe && !cannotBreathe) { + this.wasUnableToBreathe = false; + if (this.dbgStatus) { + NPCPlugin.get().getLogger().at(Level.INFO).log("Recomputing Path - Recovered breathing"); + } + + return true; + } else { + this.wasUnableToBreathe = cannotBreathe; + return false; + } } } } @@ -700,6 +760,29 @@ public abstract class BodyMotionFindBase extends BodyMotion this.pathFollower.clearPath(); } + @Override + public void onDebugFlagsChanged(EnumSet newFlags) { + boolean wasVisPath = this.visPath; + this.visPath = newFlags.contains(RoleDebugFlags.VisPath); + this.dbgDisplayString = newFlags.contains(RoleDebugFlags.Pathfinder); + if (this.visPath && !wasVisPath && this.cachedDebugSupport != null && this.pathFollower.getCurrentWaypoint() != null) { + this.recordPathVisualization(this.cachedDebugSupport); + } + } + + protected void recordPathVisualization(@Nonnull DebugSupport debugSupport) { + debugSupport.clearPathVisualization(); + this.lastSeekVisTarget.x = this.lastSeekVisTarget.y = this.lastSeekVisTarget.z = Double.NaN; + IWaypoint waypoint = this.pathFollower.getCurrentWaypoint(); + if (waypoint != null) { + for (boolean isCurrentTarget = true; waypoint != null; isCurrentTarget = false) { + IWaypoint nextWaypoint = waypoint.next(); + debugSupport.recordPathWaypoint(waypoint.getPosition(), isCurrentTarget, nextWaypoint == null); + waypoint = nextWaypoint; + } + } + } + public static enum DebugFlags implements Supplier { Opens("Display open nodes each step when computing"), Maps("Display map each step when computing"), diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFindWithTarget.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFindWithTarget.java index f64c0779..96ebd4b0 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFindWithTarget.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionFindWithTarget.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.EntityUtils; import com.hypixel.hytale.server.core.entity.LivingEntity; @@ -27,6 +26,7 @@ import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class BodyMotionFindWithTarget extends BodyMotionFindBase { protected final double minMoveDistanceWait; @@ -51,6 +51,8 @@ public abstract class BodyMotionFindWithTarget extends BodyMotionFindBase lastDesiredTargetEntity; public BodyMotionFindWithTarget(@Nonnull BuilderBodyMotionFindWithTarget builderMotionFindWithTarget, @Nonnull BuilderSupport support) { super(builderMotionFindWithTarget, support, new AStarWithTarget()); @@ -72,8 +74,15 @@ public abstract class BodyMotionFindWithTarget extends BodyMotionFindBase ref, @Nonnull Role role, @Nonnull ComponentAccessor componentAccessor) { + super.deactivate(ref, role, componentAccessor); + this.lastDesiredTargetEntity = null; } @Override @@ -84,6 +93,7 @@ public abstract class BodyMotionFindWithTarget extends BodyMotionFindBase componentAccessor ) { if (this.throttleCount > this.throttleIgnoreCount) { - double distanceSquared = this.getLastTargetPosition().distanceSquaredTo(this.lastPathedPosition); + double distanceSquared = this.getLastTargetPosition().distanceSquared(this.lastPathedPosition); if (distanceSquared < 1.0000000000000002E-10 || this.waitForTargetMovement && distanceSquared < this.minMoveDistanceWaitSquared) { return true; } } this.waitForTargetMovement = false; - this.lastPathedPosition.assign(this.getLastAccessibleTargetPosition(motionController, false, componentAccessor)); + this.lastPathedPosition.set(this.getLastAccessibleTargetPosition(motionController, false, componentAccessor)); return false; } @@ -213,8 +224,8 @@ public abstract class BodyMotionFindWithTarget extends BodyMotionFindBase componentAccessor ) { if (this.cosHalfRecomputeConeAngle < 1.0F) { - this.conePosition.assign(position); - this.coneDirection.assign(this.lastPathedPosition).subtract(position); + this.conePosition.set(position); + this.coneDirection.set(this.lastPathedPosition).sub(position); } return this.aStar @@ -240,7 +251,7 @@ public abstract class BodyMotionFindWithTarget extends BodyMotionFindBase componentAccessor ) { @@ -267,7 +284,7 @@ public abstract class BodyMotionFindWithTarget extends BodyMotionFindBase %s", this.self, this.other); } - this.lastAccessibleTargetPosition.assign(this.lastTargetPosition); + this.lastAccessibleTargetPosition.set(this.lastTargetPosition); motionController.translateToAccessiblePosition(this.lastAccessibleTargetPosition, this.targetBoundingBox, 0.0, 320.0, componentAccessor); this.haveAccessibleTargetPosition = true; this.lastAccessibleTargetPositionIsCurrent = true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionLand.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionLand.java index 8e40e988..1019d62a 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionLand.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionLand.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.movement; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -15,6 +14,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionLand extends BodyMotionFind { protected final double goalLenience; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionLeave.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionLeave.java index cf178fcb..e62664d8 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionLeave.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionLeave.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.movement; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionLeave; @@ -10,6 +9,7 @@ import com.hypixel.hytale.server.npc.movement.controllers.MotionController; import com.hypixel.hytale.server.npc.navigation.AStarBase; import com.hypixel.hytale.server.npc.navigation.AStarNode; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BodyMotionLeave extends BodyMotionFindBase { protected final double distanceSquared; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMaintainDistance.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMaintainDistance.java index e3ee328a..33fba5ee 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMaintainDistance.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMaintainDistance.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -29,6 +28,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionMaintainDistance extends BodyMotionBase { protected static final ComponentType TRANSFORM_COMPONENT_TYPE = TransformComponent.getComponentType(); @@ -47,6 +47,7 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { protected final int maxRangeProviderSlot; protected final int positioningAngleProviderSlot; protected final double[] desiredDistanceRange = new double[2]; + protected double minThresholdDistance; protected double targetDistanceSquared; protected boolean approaching; protected boolean movingAway; @@ -57,6 +58,8 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { protected final SteeringForcePursue seek = new SteeringForcePursue(); protected final Vector3d targetPosition = new Vector3d(); protected final Vector3d toTarget = new Vector3d(); + @Nullable + protected Ref lastTargetEntity; protected DoubleParameterProvider cachedMinRangeProvider; protected DoubleParameterProvider cachedMaxRangeProvider; protected DoubleParameterProvider cachedPositioningAngleProvider; @@ -71,6 +74,7 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { this.moveThreshold = builder.getMoveThreshold(support); double min = Math.max(0.0, this.initialDesiredDistanceRange[0] - this.moveThreshold); double max = this.initialDesiredDistanceRange[1] + this.moveThreshold; + this.minThresholdDistance = min; this.thresholdDistanceRangeSquared = new double[2]; this.thresholdDistanceRangeSquared[0] = min * min; this.thresholdDistanceRangeSquared[1] = max * max; @@ -94,6 +98,7 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { @Nonnull ComponentAccessor componentAccessor ) { desiredSteering.clear(); + this.lastTargetEntity = null; if (!support.getActiveMotionController().matchesType(MotionControllerWalk.class)) { support.setBackingAway(false); return false; @@ -178,6 +183,7 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { if (recalculateMinThreshold) { double min = Math.max(0.0, this.desiredDistanceRange[0] - this.moveThreshold); + this.minThresholdDistance = min; this.thresholdDistanceRangeSquared[0] = min * min; } @@ -189,7 +195,7 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { IPositionProvider positionProvider = sensorInfo.getPositionProvider(); positionProvider.providePosition(this.targetPosition); Vector3d selfPosition = transformComponent.getPosition(); - double distanceSquared = selfPosition.distanceSquaredTo(this.targetPosition); + double distanceSquared = selfPosition.distanceSquared(this.targetPosition); if (forceNewTargetRange) { double targetDistance; if (distanceSquared > this.thresholdDistanceRangeSquared[1]) { @@ -239,10 +245,11 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { desiredSteering.scaleTranslation(this.relativeForwardsSpeed); } - double x = this.targetPosition.getX() - selfPosition.getX(); - double z = this.targetPosition.getZ() - selfPosition.getZ(); + double x = this.targetPosition.x() - selfPosition.x(); + double z = this.targetPosition.z() - selfPosition.z(); float targetYaw = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)); MotionController motionController = support.getActiveMotionController(); + Ref targetRef = positionProvider.getTarget(); if (this.strafingDurationRange[1] > 0.0 || positioningAngle != Double.MAX_VALUE) { if (positioningAngle == Double.MAX_VALUE) { if (!this.tickStrafingDelay(dt)) { @@ -255,37 +262,34 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { this.pauseStrafing = true; } } - } else { - Ref targetRef = positionProvider.getTarget(); - if (targetRef != null) { - TransformComponent targetTransformComponent = componentAccessor.getComponent(targetRef, TRANSFORM_COMPONENT_TYPE); + } else if (targetRef != null) { + TransformComponent targetTransformComponent = componentAccessor.getComponent(targetRef, TRANSFORM_COMPONENT_TYPE); - assert targetTransformComponent != null; + assert targetTransformComponent != null; - float selfYaw = NPCPhysicsMath.lookatHeading(selfPosition, this.targetPosition, transformComponent.getRotation().getYaw()); - float difference = PhysicsMath.normalizeTurnAngle(targetTransformComponent.getRotation().getYaw() - selfYaw - (float)positioningAngle); - if (Math.abs(difference) > 0.08726646F) { - this.strafingDirection = difference > 0.0F ? -1 : 1; - this.pauseStrafing = false; - } else { - this.pauseStrafing = true; - } + float selfYaw = NPCPhysicsMath.lookatHeading(selfPosition, this.targetPosition, transformComponent.getRotation().yaw()); + float difference = PhysicsMath.normalizeTurnAngle(targetTransformComponent.getRotation().yaw() - selfYaw - (float)positioningAngle); + if (Math.abs(difference) > 0.08726646F) { + this.strafingDirection = difference > 0.0F ? -1 : 1; + this.pauseStrafing = false; } else { this.pauseStrafing = true; } + } else { + this.pauseStrafing = true; } if (!this.pauseStrafing) { float angle; if (!desiredSteering.hasTranslation()) { - this.toTarget.add(this.targetPosition).subtract(selfPosition).setY(0.0); + this.toTarget.add(this.targetPosition).sub(selfPosition).y = 0.0; this.toTarget.normalize(); desiredSteering.setTranslation(this.toTarget); Vector3d translation = desiredSteering.getTranslation(); - double newX = translation.getZ() * this.strafingDirection; - double newZ = translation.getX() * -this.strafingDirection; - translation.setX(newX); - translation.setZ(newZ); + double newX = translation.z() * this.strafingDirection; + double newZ = translation.x() * -this.strafingDirection; + translation.x = newX; + translation.z = newZ; desiredSteering.scaleTranslation(this.relativeForwardsSpeed); angle = this.strafingDirection * (float) (Math.PI / 4); } else { @@ -300,7 +304,10 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { } } - motionController.requireDepthProbing(); + if (targetRef != null && targetRef.isValid()) { + this.lastTargetEntity = targetRef; + } + desiredSteering.setYaw(targetYaw); return false; } else { @@ -321,6 +328,18 @@ public class BodyMotionMaintainDistance extends BodyMotionBase { @Override public void deactivate(@Nonnull Ref ref, @Nonnull Role role, @Nonnull ComponentAccessor componentAccessor) { super.deactivate(ref, role, componentAccessor); + this.lastTargetEntity = null; role.setBackingAway(false); } + + @Override + public double getDesiredTargetDistance() { + return this.minThresholdDistance; + } + + @Nullable + @Override + public Ref getDesiredTargetEntity() { + return this.lastTargetEntity; + } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMatchLook.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMatchLook.java index c5b2a468..5e466292 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMatchLook.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMatchLook.java @@ -30,7 +30,7 @@ public class BodyMotionMatchLook extends BodyMotionBase { assert headRotationComponent != null; - float headYaw = headRotationComponent.getRotation().getYaw(); + float headYaw = headRotationComponent.getRotation().yaw(); desiredSteering.setYaw(headYaw); return true; } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMoveAway.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMoveAway.java index aae64d46..ef8354e2 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMoveAway.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionMoveAway.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; @@ -23,6 +22,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionMoveAway extends BodyMotionFindWithTarget { protected final double stopDistance; @@ -34,8 +34,10 @@ public class BodyMotionMoveAway extends BodyMotionFindWithTarget { protected final float erraticJitter; protected final double erraticChangeDurationMultiplier; protected final SteeringForceEvade evade = new SteeringForceEvade(); + private final Vector3d tmpProbeDirection = new Vector3d(); protected float fleeDirection; protected double holdDirectionTimeRemaining; + protected boolean fleeDirectionBlocked; public BodyMotionMoveAway(@Nonnull BuilderBodyMotionMoveAway builderMotionFind, @Nonnull BuilderSupport support) { super(builderMotionFind, support); @@ -58,6 +60,7 @@ public class BodyMotionMoveAway extends BodyMotionFindWithTarget { public void activate(@Nonnull Ref ref, @Nonnull Role role, @Nonnull ComponentAccessor componentAccessor) { super.activate(ref, role, componentAccessor); this.holdDirectionTimeRemaining = 0.0; + this.fleeDirectionBlocked = false; } @Override @@ -96,16 +99,17 @@ public class BodyMotionMoveAway extends BodyMotionFindWithTarget { assert transformComponent != null; Vector3d selfPosition = transformComponent.getPosition(); - Vector3f bodyRotation = transformComponent.getRotation(); + Rotation3f bodyRotation = transformComponent.getRotation(); Vector3d lastTargetPosition = this.getLastTargetPosition(); if (NPCPhysicsMath.inViewSector( - selfPosition.x, selfPosition.z, bodyRotation.getYaw(), this.changeDirectionViewSector, lastTargetPosition.x, lastTargetPosition.z + selfPosition.x, selfPosition.z, bodyRotation.yaw(), this.changeDirectionViewSector, lastTargetPosition.x, lastTargetPosition.z )) { this.holdDirectionTimeRemaining = 0.0; } - if (this.holdDirectionTimeRemaining <= 0.0) { - boolean inErraticRange = selfPosition.distanceSquaredTo(lastTargetPosition) < this.erraticDistanceSquared; + MotionController motionController = role.getActiveMotionController(); + if (this.holdDirectionTimeRemaining <= 0.0 || this.fleeDirectionBlocked) { + boolean inErraticRange = selfPosition.distanceSquared(lastTargetPosition) < this.erraticDistanceSquared; float jitter = inErraticRange ? this.erraticJitter : this.jitterAngle; this.fleeDirection = PhysicsMath.headingFromDirection(selfPosition.x - lastTargetPosition.x, selfPosition.z - lastTargetPosition.z) + RandomExtra.randomRange(-jitter, jitter); @@ -113,13 +117,25 @@ public class BodyMotionMoveAway extends BodyMotionFindWithTarget { if (inErraticRange) { this.holdDirectionTimeRemaining = this.holdDirectionTimeRemaining * this.erraticChangeDurationMultiplier; } + + double dx = Math.sin(this.fleeDirection) * this.stopDistance; + double dz = Math.cos(this.fleeDirection) * this.stopDistance; + this.tmpProbeDirection.set(dx, 0.0, dz); + double moved = motionController.probeMove(ref, selfPosition, this.tmpProbeDirection, this.probeMoveData, componentAccessor); + this.fleeDirectionBlocked = moved < 1.0E-5; + if (!this.fleeDirectionBlocked && this.probeMoveData.edgeBlocked) { + this.holdDirectionTimeRemaining = 0.0; + } } - this.evade.setPositions(selfPosition, lastTargetPosition); - this.evade.setDirectionHint(this.fleeDirection); - MotionController motionController = role.getActiveMotionController(); - double desiredAltitudeWeight = this.desiredAltitudeWeight >= 0.0 ? this.desiredAltitudeWeight : motionController.getDesiredAltitudeWeight(); - return this.scaleSteering(ref, role, this.evade, desiredSteering, desiredAltitudeWeight, componentAccessor); + if (this.fleeDirectionBlocked) { + return false; + } else { + this.evade.setPositions(selfPosition, lastTargetPosition); + this.evade.setDirectionHint(this.fleeDirection); + double desiredAltitudeWeight = this.desiredAltitudeWeight >= 0.0 ? this.desiredAltitudeWeight : motionController.getDesiredAltitudeWeight(); + return this.scaleSteering(ref, role, this.evade, desiredSteering, desiredAltitudeWeight, componentAccessor); + } } @Override diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionTakeOff.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionTakeOff.java index bf65bce8..8a8ce43e 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionTakeOff.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionTakeOff.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.movement; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.corecomponents.BodyMotionBase; @@ -14,6 +13,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionTakeOff extends BodyMotionBase { protected final double jumpSpeed; @@ -43,7 +43,7 @@ public class BodyMotionTakeOff extends BodyMotionBase { role.setActiveMotionController(ref, npcComponent, "Fly", componentAccessor); Vector3d position = transformComponent.getPosition(); - position.setY(transformComponent.getPosition().getY() + 0.1); + position.y = transformComponent.getPosition().y() + 0.1; ((MotionControllerFly)role.getActiveMotionController()).takeOff(ref, this.jumpSpeed, componentAccessor); } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionTeleport.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionTeleport.java index d8f24d6f..d8a8ab12 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionTeleport.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionTeleport.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport; @@ -20,6 +19,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import java.util.function.Supplier; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionTeleport extends BodyMotionBase { public static final int MAX_TRIES = 10; @@ -63,25 +63,25 @@ public class BodyMotionTeleport extends BodyMotionBase { @Nonnull ComponentAccessor componentAccessor ) { if (sensorInfo != null && sensorInfo.getPositionProvider().providePosition(this.target)) { - double dist = this.target.distanceSquaredTo(this.lastTriedTarget); + double dist = this.target.distanceSquared(this.lastTriedTarget); if ((this.tries > 0 || !(dist < 1.0)) && !this.tickCooldown(dt)) { if (dist > 1.0) { this.tries = 10; } - this.lastTriedTarget.assign(this.target); + this.lastTriedTarget.set(this.target); TransformComponent transformComponent = componentAccessor.getComponent(ref, TRANSFORM_COMPONENT_TYPE); assert transformComponent != null; Vector3d selfPosition = transformComponent.getPosition(); - double distance = selfPosition.distanceSquaredTo(this.target); + double distance = selfPosition.distanceSquared(this.target); double maxOffset2 = this.maxOffset * this.maxOffset; if (distance <= maxOffset2 + 1.0E-5) { return false; } else { - this.offsetVector.assign(selfPosition).subtract(this.target).setY(0.0); - this.offsetVector.setLength(RandomExtra.randomRange(this.minOffset, this.maxOffset)); + this.offsetVector.set(selfPosition).sub(this.target).y = 0.0; + this.offsetVector.normalize(RandomExtra.randomRange(this.minOffset, this.maxOffset)); this.offsetVector.rotateY(RandomExtra.randomRange(-this.angle, this.angle)); this.target.add(this.offsetVector); MotionController motionController = role.getActiveMotionController(); @@ -96,27 +96,27 @@ public class BodyMotionTeleport extends BodyMotionBase { && motionController.isValidPosition(this.target, componentAccessor)) { switch (this.orientation) { case Unchanged: { - Vector3f bodyRotation = transformComponent.getRotation(); + Rotation3f bodyRotation = transformComponent.getRotation(); componentAccessor.addComponent(ref, Teleport.getComponentType(), Teleport.createExact(this.target, bodyRotation)); break; } case TowardsTarget: { - double x = this.lastTriedTarget.getX() - this.target.getX(); - double y = this.lastTriedTarget.getY() - this.target.getY(); - double z = this.lastTriedTarget.getZ() - this.target.getZ(); - Vector3f bodyRotation = transformComponent.getRotation(); + double x = this.lastTriedTarget.x() - this.target.x(); + double y = this.lastTriedTarget.y() - this.target.y(); + double z = this.lastTriedTarget.z() - this.target.z(); + Rotation3f bodyRotation = transformComponent.getRotation(); float yaw; float pitch; if (x * x + z * z < 1.0E-5) { - yaw = bodyRotation.getYaw(); - pitch = bodyRotation.getPitch(); + yaw = bodyRotation.yaw(); + pitch = bodyRotation.pitch(); } else { yaw = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(x, z)); pitch = PhysicsMath.pitchFromDirection(x, y, z); } componentAccessor.addComponent( - ref, Teleport.getComponentType(), Teleport.createExact(this.target, new Vector3f(yaw, pitch, bodyRotation.getRoll())) + ref, Teleport.getComponentType(), Teleport.createExact(this.target, new Rotation3f(yaw, pitch, bodyRotation.roll())) ); break; } @@ -130,7 +130,7 @@ public class BodyMotionTeleport extends BodyMotionBase { assert targetTransformComponent != null; - Vector3f bodyRotation = targetTransformComponent.getRotation(); + Rotation3f bodyRotation = targetTransformComponent.getRotation(); componentAccessor.addComponent(ref, Teleport.getComponentType(), Teleport.createExact(this.target, bodyRotation)); } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWander.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWander.java index 81da23ee..54ec29ca 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWander.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWander.java @@ -2,12 +2,12 @@ package com.hypixel.hytale.server.npc.corecomponents.movement; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionWander; import com.hypixel.hytale.server.npc.role.Role; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BodyMotionWander extends BodyMotionWanderBase { public BodyMotionWander(@Nonnull BuilderBodyMotionWander builder, @Nonnull BuilderSupport builderSupport) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderBase.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderBase.java index b7bd9e57..b188c32c 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderBase.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderBase.java @@ -6,8 +6,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; @@ -18,6 +17,7 @@ import com.hypixel.hytale.server.npc.corecomponents.BodyMotionBase; import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionWanderBase; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.movement.Steering; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import com.hypixel.hytale.server.npc.movement.controllers.MotionController; import com.hypixel.hytale.server.npc.movement.controllers.ProbeMoveData; import com.hypixel.hytale.server.npc.movement.steeringforces.SteeringForcePursue; @@ -26,10 +26,12 @@ import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import java.util.Arrays; +import java.util.EnumSet; import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public abstract class BodyMotionWanderBase extends BodyMotionBase { public static final HytaleLogger LOGGER = NPCPlugin.get().getLogger(); @@ -48,8 +50,6 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { protected final double minMoveDistance; protected final double stopDistance; protected final int testsPerTick; - protected final boolean isAvoidingBlockDamage; - protected final boolean isRelaxedMoveConstraints; protected final double desiredAltitudeWeight; protected final byte[] preOrderedDirections = new byte[32]; protected final int insideConeCount; @@ -69,6 +69,9 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { protected double walkDistance; protected int directionIndex; protected double desiredWalkDistance; + protected boolean searchUsesEscapeConstraints; + @Nonnull + protected final EnumSet cachedEffectiveConstraints; protected final double[] walkDistances = new double[32]; protected final byte[] walkDirections = new byte[32]; @@ -86,12 +89,14 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { this.stopDistance = builder.getStopDistance(builderSupport); this.testsPerTick = builder.getTestsPerTick(builderSupport); this.desiredAltitudeWeight = builder.getDesiredAltitudeWeight(builderSupport); - boolean avoidingBlockDamage = builder.isAvoidingBlockDamage(builderSupport); - this.isAvoidingBlockDamage = avoidingBlockDamage; - this.probeMoveData.setAvoidingBlockDamage(avoidingBlockDamage); - boolean relaxedMoveConstraints = builder.isRelaxedMoveConstraints(builderSupport); - this.isRelaxedMoveConstraints = relaxedMoveConstraints; - this.probeMoveData.setRelaxedMoveConstraints(relaxedMoveConstraints); + boolean legacyRelaxedMoveConstraints = builder.isLegacyRelaxedMoveConstraints(builderSupport); + boolean usesLegacyConstraintMode = !builder.isRelaxedConstraintsPresent(); + EnumSet relaxedConstraints = builder.getRelaxedConstraints(builderSupport); + boolean isLegacyAvoidingBlockDamage = builder.isAvoidingBlockDamage(builderSupport); + this.cachedEffectiveConstraints = computeEffectiveRelaxedConstraints( + usesLegacyConstraintMode, relaxedConstraints, legacyRelaxedMoveConstraints, isLegacyAvoidingBlockDamage + ); + this.probeMoveData.setRelaxedConstraints(this.cachedEffectiveConstraints); int count = 0; for (int i = this.minDirection; i <= this.maxDirection; i++) { @@ -100,15 +105,21 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { this.insideConeCount = count; - for (int var7 = 0; var7 < this.minDirection; var7++) { - count = this.addPreOrderedDirection(var7, count); + for (int var9 = 0; var9 < this.minDirection; var9++) { + count = this.addPreOrderedDirection(var9, count); } - for (int var8 = this.maxDirection + 1; var8 <= 16; var8++) { - count = this.addPreOrderedDirection(var8, count); + for (int var10 = this.maxDirection + 1; var10 <= 16; var10++) { + count = this.addPreOrderedDirection(var10, count); } } + @Nullable + @Override + public EnumSet getRelaxedConstraints() { + return this.cachedEffectiveConstraints; + } + @Override public void activate(@Nonnull Ref ref, @Nonnull Role role, @Nonnull ComponentAccessor componentAccessor) { this.debugSteer = role.getDebugSupport().isDebugFlagSet(RoleDebugFlags.MotionControllerSteer); @@ -153,9 +164,9 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { if (this.debugSteer) { LOGGER.at(Level.INFO) .log( - "Wander compute: state=%s canAct=%s blocked=%s walkTime=%s", + "Wander compute: state=%s canSteer=%s blocked=%s walkTime=%s", this.state.toString(), - activeMotionController.canAct(ref, componentAccessor), + activeMotionController.canSteer(ref, componentAccessor), activeMotionController.isObstructed(), this.walkTime ); @@ -173,22 +184,29 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { this.restartSearch(ref, npcComponent, activeMotionController, componentAccessor); } + this.probeMoveData.setRelaxedConstraints(this.cachedEffectiveConstraints); + boolean usesEscapeConstraints = applyEscapeConstraints(role, this.probeMoveData); + if (this.state == BodyMotionWanderBase.State.SEARCHING && this.directionIndex == 0) { + this.searchUsesEscapeConstraints = usesEscapeConstraints; + } else if (this.searchUsesEscapeConstraints && !usesEscapeConstraints) { + this.restartSearch(ref, npcComponent, activeMotionController, componentAccessor); + } + if (activeMotionController.isInProgress()) { if (this.state == BodyMotionWanderBase.State.WALKING) { this.walkTime -= dt; - activeMotionController.setRelaxedMoveConstraints(this.isRelaxedMoveConstraints); - activeMotionController.setAvoidingBlockDamage(this.isAvoidingBlockDamage && activeMotionController.isAvoidingBlockDamage()); + activeMotionController.setRelaxedMoveConstraints(this.cachedEffectiveConstraints); } return true; - } else if (!activeMotionController.canAct(ref, componentAccessor)) { + } else if (!activeMotionController.canSteer(ref, componentAccessor)) { return true; } else { TransformComponent transformComponent = componentAccessor.getComponent(ref, TransformComponent.getComponentType()); assert transformComponent != null; - Vector3f bodyRotation = transformComponent.getRotation(); + Rotation3f bodyRotation = transformComponent.getRotation(); if (activeMotionController.isObstructed() && this.state == BodyMotionWanderBase.State.WALKING) { this.restartSearch(ref, npcComponent, activeMotionController, componentAccessor); if (this.debugSteer) { @@ -198,7 +216,7 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { this.state.toString(), this.directionIndex, this.walkTime, - (180.0F / (float)Math.PI) * bodyRotation.getYaw(), + (180.0F / (float)Math.PI) * bodyRotation.yaw(), (180.0F / (float)Math.PI) * this.walkHeading ); } @@ -239,14 +257,14 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { "Wander: Found move state=%s directionIndex=%s yaw=%s newYaw=%s", this.state.toString(), this.directionIndex, - (180.0F / (float)Math.PI) * bodyRotation.getYaw(), + (180.0F / (float)Math.PI) * bodyRotation.yaw(), (180.0F / (float)Math.PI) * this.walkHeading ); } } if (this.state == BodyMotionWanderBase.State.TURNING) { - float heading = bodyRotation.getYaw(); + float heading = bodyRotation.yaw(); double turnAngle = NPCPhysicsMath.turnAngle(this.walkHeading, heading); if (!(Math.abs(turnAngle) < 0.05235988F)) { desiredSteering.setYaw(this.walkHeading); @@ -269,7 +287,7 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { .log( "Wander: Walk state=%s yaw=%s desiredYaw=%s walkTime=%s", this.state.toString(), - (180.0F / (float)Math.PI) * bodyRotation.getYaw(), + (180.0F / (float)Math.PI) * bodyRotation.yaw(), (180.0F / (float)Math.PI) * this.walkHeading, this.walkTime ); @@ -289,14 +307,15 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { "Wander: Walk done state=%s directionIndex=%s yaw=%s desiredYaw=%s", this.state.toString(), this.directionIndex, - (180.0F / (float)Math.PI) * bodyRotation.getYaw(), + (180.0F / (float)Math.PI) * bodyRotation.yaw(), (180.0F / (float)Math.PI) * this.walkHeading ); } } - activeMotionController.setRelaxedMoveConstraints(this.isRelaxedMoveConstraints); - activeMotionController.setAvoidingBlockDamage(this.isAvoidingBlockDamage && activeMotionController.isAvoidingBlockDamage()); + activeMotionController.setRelaxedMoveConstraints(this.cachedEffectiveConstraints); + this.probeMoveData.setRelaxedConstraints(this.cachedEffectiveConstraints); + applyEscapeConstraints(role, this.probeMoveData); desiredSteering.scaleTranslation(this.relativeSpeed); } @@ -366,6 +385,7 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { @Nullable ComponentAccessor componentAccessor ) { this.state = BodyMotionWanderBase.State.SEARCHING; + this.searchUsesEscapeConstraints = false; float currentHorizontalSpeedMultiplier = npcComponent.getCurrentHorizontalSpeedMultiplier(ref, componentAccessor); this.walkTime = RandomExtra.randomRange(this.minWalkTime, this.maxWalkTime) / currentHorizontalSpeedMultiplier; this.desiredWalkDistance = this.relativeSpeed * activeMotionController.getMaximumSpeed() * this.walkTime; @@ -418,10 +438,13 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { return false; } else { if (constrainDistance < this.desiredWalkDistance) { - this.probeDirection.scale(constrainDistance / this.desiredWalkDistance); + this.probeDirection.mul(constrainDistance / this.desiredWalkDistance); + } + + if (motionController.willReceiveBlockDamage()) { + this.probeMoveData.getRelaxedConstraints().add(RelaxedConstraint.DAMAGE); } - this.probeMoveData.setAvoidingBlockDamage(!motionController.willReceiveBlockDamage()); double moveDistance = motionController.probeMove(ref, this.probePosition, this.probeDirection, this.probeMoveData, componentAccessor); if (moveDistance < 1.0E-5) { return false; @@ -431,12 +454,12 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { return false; } else { if (moveDistance < constrainDistance) { - this.probeDirection.scale(moveDistance / constrainDistance); + this.probeDirection.mul(moveDistance / constrainDistance); } this.walkDistance = moveDistance; this.walkHeading = heading; - this.targetPosition.assign(this.probePosition).add(this.probeDirection); + this.targetPosition.set(this.probePosition).add(this.probeDirection); return true; } } @@ -448,11 +471,11 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { assert transformComponent != null; - this.probePosition.assign(transformComponent.getPosition()); + this.probePosition.set(transformComponent.getPosition()); this.probeDirection.x = PhysicsMath.headingX(heading) * distance; this.probeDirection.y = this.probeDY * distance / this.desiredWalkDistance; this.probeDirection.z = PhysicsMath.headingZ(heading) * distance; - this.targetPosition.assign(this.probePosition).add(this.probeDirection); + this.targetPosition.set(this.probePosition).add(this.probeDirection); } protected float toAngle(@Nonnull Ref ref, int direction, @Nonnull ComponentAccessor componentAccessor) { @@ -460,7 +483,7 @@ public abstract class BodyMotionWanderBase extends BodyMotionBase { assert transformComponent != null; - return PhysicsMath.normalizeAngle(transformComponent.getRotation().getYaw() + direction * (float) (Math.PI / 16) + this.angleOffset); + return PhysicsMath.normalizeAngle(transformComponent.getRotation().yaw() + direction * (float) (Math.PI / 16) + this.angleOffset); } private int addPreOrderedDirection(int direction, int count) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderInCircle.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderInCircle.java index bb76577d..3c3da38b 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderInCircle.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderInCircle.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.movement; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.group.EntityGroup; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; @@ -15,6 +14,8 @@ import com.hypixel.hytale.server.npc.movement.controllers.MotionController; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class BodyMotionWanderInCircle extends BodyMotionWanderBase { protected final double radius; @@ -57,12 +58,12 @@ public class BodyMotionWanderInCircle extends BodyMotionWanderBase { } } } else { - Vector3d n = activeMotionController.getWorldNormal(); - double endDist2 = NPCPhysicsMath.squaredDistProjected(targetPosition.getX(), targetPosition.getY(), targetPosition.getZ(), referencePoint, n); + Vector3dc n = activeMotionController.getWorldNormal(); + double endDist2 = NPCPhysicsMath.squaredDistProjected(targetPosition.x(), targetPosition.y(), targetPosition.z(), referencePoint, n); if (endDist2 <= r2) { return moveDist; } else { - double startDist2 = NPCPhysicsMath.squaredDistProjected(probePosition.getX(), probePosition.getY(), probePosition.getZ(), referencePoint, n); + double startDist2 = NPCPhysicsMath.squaredDistProjected(probePosition.x(), probePosition.y(), probePosition.z(), referencePoint, n); if (startDist2 >= r2) { return endDist2 <= startDist2 ? moveDist : 0.0; } else { @@ -93,12 +94,12 @@ public class BodyMotionWanderInCircle extends BodyMotionWanderBase { assert leaderTransformComponent != null; Vector3d leaderPosition = leaderTransformComponent.getPosition(); - this.referencePoint.assign(leaderPosition.getX(), leaderPosition.getY(), leaderPosition.getZ()); + this.referencePoint.set(leaderPosition.x(), leaderPosition.y(), leaderPosition.z()); return this.referencePoint; } } - this.referencePoint.assign(entityPosition); + this.referencePoint.set(entityPosition); return this.referencePoint; } else { NPCEntity npcComponent = componentAccessor.getComponent(ref, NPCEntity.getComponentType()); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderInRect.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderInRect.java index c3d577c3..76764ebb 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderInRect.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/BodyMotionWanderInRect.java @@ -2,13 +2,13 @@ package com.hypixel.hytale.server.npc.corecomponents.movement; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionWanderInRect; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BodyMotionWanderInRect extends BodyMotionWanderBase { public static final int LEFT = 1; @@ -44,8 +44,8 @@ public class BodyMotionWanderInRect extends BodyMotionWanderBase { assert npcComponent != null; Vector3d leash = npcComponent.getLeashPoint(); - double leashX = leash.getX(); - double leashZ = leash.getZ(); + double leashX = leash.x(); + double leashZ = leash.z(); double endX = targetPosition.x - leashX; double endZ = targetPosition.z - leashZ; int endCode = this.sectorCode(endX, endZ); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/builders/BuilderBodyMotionFindBase.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/builders/BuilderBodyMotionFindBase.java index f672e784..ac3a6c42 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/builders/BuilderBodyMotionFindBase.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/builders/BuilderBodyMotionFindBase.java @@ -6,6 +6,7 @@ import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.asset.builder.holder.BooleanHolder; import com.hypixel.hytale.server.npc.asset.builder.holder.DoubleHolder; +import com.hypixel.hytale.server.npc.asset.builder.holder.EnumSetHolder; import com.hypixel.hytale.server.npc.asset.builder.holder.IntHolder; import com.hypixel.hytale.server.npc.asset.builder.holder.NumberArrayHolder; import com.hypixel.hytale.server.npc.asset.builder.validators.DoubleRangeValidator; @@ -15,11 +16,14 @@ import com.hypixel.hytale.server.npc.asset.builder.validators.IntSingleValidator import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderBodyMotionBase; import com.hypixel.hytale.server.npc.corecomponents.movement.BodyMotionFindBase; import com.hypixel.hytale.server.npc.instructions.BodyMotion; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import java.util.EnumSet; import javax.annotation.Nonnull; public abstract class BuilderBodyMotionFindBase extends BuilderBodyMotionBase implements Builder { protected static final double[] THROTTLE_DELAY = new double[]{3.0, 5.0}; + private static final String[] AVOID_OR_RELAXED_ATTRIBUTES = new String[]{"AvoidBlockDamage", "RelaxedConstraints"}; + private static final String[] LEGACY_OR_RELAXED_ATTRIBUTES = new String[]{"RelaxedMoveConstraints", "RelaxedConstraints"}; @Nonnull protected EnumSet parsedDebugFlags = EnumSet.noneOf(BodyMotionFindBase.DebugFlags.class); protected String debugFlags; @@ -37,7 +41,8 @@ public abstract class BuilderBodyMotionFindBase extends BuilderBodyMotionBase im protected final DoubleHolder rejectionWeight = new DoubleHolder(); protected final DoubleHolder blendHeading = new DoubleHolder(); protected final BooleanHolder isAvoidingBlockDamage = new BooleanHolder(); - protected final BooleanHolder isRelaxedMoveConstraints = new BooleanHolder(); + protected final BooleanHolder isLegacyRelaxedMoveConstraints = new BooleanHolder(); + protected final EnumSetHolder relaxedConstraints = new EnumSetHolder<>(); protected final NumberArrayHolder throttleDelayRangeHolder = new NumberArrayHolder(); protected final IntHolder throttleIgnoreCount = new IntHolder(); protected final BooleanHolder useSteering = new BooleanHolder(); @@ -45,6 +50,8 @@ public abstract class BuilderBodyMotionFindBase extends BuilderBodyMotionBase im protected final BooleanHolder skipSteering = new BooleanHolder(); protected final DoubleHolder minPathLength = new DoubleHolder(); protected final DoubleHolder desiredAltitudeWeight = new DoubleHolder(); + private final boolean[] avoidOrRelaxedPresent = new boolean[2]; + private final boolean[] legacyOrRelaxedPresent = new boolean[2]; protected final boolean enableSteering; public BuilderBodyMotionFindBase() { @@ -111,18 +118,31 @@ public abstract class BuilderBodyMotionFindBase extends BuilderBodyMotionBase im this.getBoolean( data, "BuildOptimisedPath", this.buildOptimisedPath, true, BuilderDescriptorState.Stable, "Try to reduce number of nodes of generated path", null ); - this.getBoolean( - data, "AvoidBlockDamage", this.isAvoidingBlockDamage, true, BuilderDescriptorState.Stable, "Should avoid environmental damage from blocks", null + this.avoidOrRelaxedPresent[0] = this.getBoolean( + data, "AvoidBlockDamage", this.isAvoidingBlockDamage, true, BuilderDescriptorState.Deprecated, "Should avoid environmental damage from blocks", null ); - this.getBoolean( + this.legacyOrRelaxedPresent[0] = this.getBoolean( data, "RelaxedMoveConstraints", - this.isRelaxedMoveConstraints, + this.isLegacyRelaxedMoveConstraints, true, - BuilderDescriptorState.Stable, + BuilderDescriptorState.Deprecated, "NPC can do movements like wading (depends on motion controller type)", null ); + this.avoidOrRelaxedPresent[1] = this.getEnumSet( + data, + "RelaxedConstraints", + this.relaxedConstraints, + RelaxedConstraint.class, + EnumSet.noneOf(RelaxedConstraint.class), + BuilderDescriptorState.Stable, + "List of constraints to relax for this motion (new mode; empty = no relaxed constraints)", + null + ); + this.legacyOrRelaxedPresent[1] = this.avoidOrRelaxedPresent[1]; + this.validateOneOrNonePresent(AVOID_OR_RELAXED_ATTRIBUTES, this.avoidOrRelaxedPresent); + this.validateOneOrNonePresent(LEGACY_OR_RELAXED_ATTRIBUTES, this.legacyOrRelaxedPresent); this.getDouble( data, "BlendHeading", @@ -280,8 +300,17 @@ public abstract class BuilderBodyMotionFindBase extends BuilderBodyMotionBase im return this.isAvoidingBlockDamage.get(support.getExecutionContext()); } - public boolean isRelaxedMoveConstraints(@Nonnull BuilderSupport support) { - return this.isRelaxedMoveConstraints.get(support.getExecutionContext()); + public boolean isLegacyRelaxedMoveConstraints(@Nonnull BuilderSupport support) { + return this.isLegacyRelaxedMoveConstraints.get(support.getExecutionContext()); + } + + @Nonnull + public EnumSet getRelaxedConstraints(@Nonnull BuilderSupport support) { + return this.relaxedConstraints.get(support.getExecutionContext()); + } + + public boolean isRelaxedConstraintsPresent() { + return this.avoidOrRelaxedPresent[1]; } public double[] getThrottleDelayRange(@Nonnull BuilderSupport support) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/movement/builders/BuilderBodyMotionWanderBase.java b/src/com/hypixel/hytale/server/npc/corecomponents/movement/builders/BuilderBodyMotionWanderBase.java index 0071e0fe..fe620224 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/movement/builders/BuilderBodyMotionWanderBase.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/movement/builders/BuilderBodyMotionWanderBase.java @@ -6,6 +6,7 @@ import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.asset.builder.holder.BooleanHolder; import com.hypixel.hytale.server.npc.asset.builder.holder.DoubleHolder; +import com.hypixel.hytale.server.npc.asset.builder.holder.EnumSetHolder; import com.hypixel.hytale.server.npc.asset.builder.holder.FloatHolder; import com.hypixel.hytale.server.npc.asset.builder.holder.IntHolder; import com.hypixel.hytale.server.npc.asset.builder.validators.DoubleRangeValidator; @@ -15,10 +16,14 @@ import com.hypixel.hytale.server.npc.asset.builder.validators.RelationalOperator import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderBodyMotionBase; import com.hypixel.hytale.server.npc.corecomponents.movement.BodyMotionWanderBase; import com.hypixel.hytale.server.npc.instructions.BodyMotion; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; +import java.util.EnumSet; import javax.annotation.Nonnull; import javax.annotation.Nullable; public abstract class BuilderBodyMotionWanderBase extends BuilderBodyMotionBase { + private static final String[] AVOID_OR_RELAXED_ATTRIBUTES = new String[]{"AvoidBlockDamage", "RelaxedConstraints"}; + private static final String[] LEGACY_OR_RELAXED_ATTRIBUTES = new String[]{"RelaxedMoveConstraints", "RelaxedConstraints"}; protected final DoubleHolder minWalkTime = new DoubleHolder(); protected final DoubleHolder maxWalkTime = new DoubleHolder(); protected final FloatHolder minHeadingChange = new FloatHolder(); @@ -28,9 +33,12 @@ public abstract class BuilderBodyMotionWanderBase extends BuilderBodyMotionBase protected final DoubleHolder minMoveDistance = new DoubleHolder(); protected final DoubleHolder stopDistance = new DoubleHolder(); protected final BooleanHolder isAvoidingBlockDamage = new BooleanHolder(); - protected final BooleanHolder isRelaxedMoveConstraints = new BooleanHolder(); + protected final BooleanHolder isLegacyRelaxedMoveConstraints = new BooleanHolder(); + protected final EnumSetHolder relaxedConstraints = new EnumSetHolder<>(); protected final IntHolder testsPerTick = new IntHolder(); protected final DoubleHolder desiredAltitudeWeight = new DoubleHolder(); + private final boolean[] avoidOrRelaxedPresent = new boolean[2]; + private final boolean[] legacyOrRelaxedPresent = new boolean[2]; @Nullable public BodyMotionWanderBase build(@Nonnull BuilderSupport builderSupport) { @@ -115,18 +123,31 @@ public abstract class BuilderBodyMotionWanderBase extends BuilderBodyMotionBase data, "StopDistance", this.stopDistance, 0.5, DoubleSingleValidator.greater0(), BuilderDescriptorState.Stable, "Distance to stop at target", null ); this.getInt(data, "TestsPerTick", this.testsPerTick, 1, IntSingleValidator.greater0(), BuilderDescriptorState.Stable, "Direction tests per tick", null); - this.getBoolean( - data, "AvoidBlockDamage", this.isAvoidingBlockDamage, true, BuilderDescriptorState.Stable, "Should avoid environmental damage from blocks", null + this.avoidOrRelaxedPresent[0] = this.getBoolean( + data, "AvoidBlockDamage", this.isAvoidingBlockDamage, true, BuilderDescriptorState.Deprecated, "Should avoid environmental damage from blocks", null ); - this.getBoolean( + this.legacyOrRelaxedPresent[0] = this.getBoolean( data, "RelaxedMoveConstraints", - this.isRelaxedMoveConstraints, + this.isLegacyRelaxedMoveConstraints, false, - BuilderDescriptorState.Stable, + BuilderDescriptorState.Deprecated, "NPC can do movements like wading (depends on motion controller type)", null ); + this.avoidOrRelaxedPresent[1] = this.getEnumSet( + data, + "RelaxedConstraints", + this.relaxedConstraints, + RelaxedConstraint.class, + EnumSet.noneOf(RelaxedConstraint.class), + BuilderDescriptorState.Stable, + "List of constraints to relax for this motion (new mode; empty = no relaxed constraints)", + null + ); + this.legacyOrRelaxedPresent[1] = this.avoidOrRelaxedPresent[1]; + this.validateOneOrNonePresent(AVOID_OR_RELAXED_ATTRIBUTES, this.avoidOrRelaxedPresent); + this.validateOneOrNonePresent(LEGACY_OR_RELAXED_ATTRIBUTES, this.legacyOrRelaxedPresent); this.getDouble( data, "DesiredAltitudeWeight", @@ -178,8 +199,17 @@ public abstract class BuilderBodyMotionWanderBase extends BuilderBodyMotionBase return this.isAvoidingBlockDamage.get(support.getExecutionContext()); } - public boolean isRelaxedMoveConstraints(@Nonnull BuilderSupport support) { - return this.isRelaxedMoveConstraints.get(support.getExecutionContext()); + public boolean isLegacyRelaxedMoveConstraints(@Nonnull BuilderSupport support) { + return this.isLegacyRelaxedMoveConstraints.get(support.getExecutionContext()); + } + + @Nonnull + public EnumSet getRelaxedConstraints(@Nonnull BuilderSupport support) { + return this.relaxedConstraints.get(support.getExecutionContext()); + } + + public boolean isRelaxedConstraintsPresent() { + return this.avoidOrRelaxedPresent[1]; } public int getTestsPerTick(@Nonnull BuilderSupport support) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionParentState.java b/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionParentState.java index 66e1f9bf..27c9c426 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionParentState.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionParentState.java @@ -11,6 +11,7 @@ import com.hypixel.hytale.server.npc.decisionmaker.stateevaluator.StateEvaluator import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionParentState extends ActionBase { protected final int state; @@ -24,7 +25,7 @@ public class ActionParentState extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); StateEvaluator stateEvaluatorComponent = store.getComponent(ref, StateEvaluator.getComponentType()); if (stateEvaluatorComponent == null || !stateEvaluatorComponent.isActive()) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionState.java b/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionState.java index e176b012..ed4cd930 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionState.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionState.java @@ -11,6 +11,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.ComponentInfo; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionState extends ActionBase { protected final int state; @@ -29,7 +30,7 @@ public class ActionState extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); if (this.componentLocal) { role.getStateSupport().setComponentState(this.componentIndex, this.state); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionToggleStateEvaluator.java b/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionToggleStateEvaluator.java index 8db7b4cb..563cac87 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionToggleStateEvaluator.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/ActionToggleStateEvaluator.java @@ -9,23 +9,24 @@ import com.hypixel.hytale.server.npc.decisionmaker.stateevaluator.StateEvaluator import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionToggleStateEvaluator extends ActionBase { - protected final boolean enable; + protected final boolean on; public ActionToggleStateEvaluator(@Nonnull BuilderActionToggleStateEvaluator builder) { super(builder); - this.enable = builder.isEnable(); + this.on = builder.isOn(); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); StateEvaluator stateEvaluatorComponent = store.getComponent(ref, StateEvaluator.getComponentType()); + if (stateEvaluatorComponent != null) { + stateEvaluatorComponent.setActive(this.on); + } - assert stateEvaluatorComponent != null; - - stateEvaluatorComponent.setActive(this.enable); return true; } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/builders/BuilderActionToggleStateEvaluator.java b/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/builders/BuilderActionToggleStateEvaluator.java index baefaae4..0d739033 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/builders/BuilderActionToggleStateEvaluator.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/statemachine/builders/BuilderActionToggleStateEvaluator.java @@ -8,7 +8,7 @@ import com.hypixel.hytale.server.npc.corecomponents.statemachine.ActionToggleSta import javax.annotation.Nonnull; public class BuilderActionToggleStateEvaluator extends BuilderActionBase { - protected boolean enable; + protected boolean on; @Nonnull public ActionToggleStateEvaluator build(BuilderSupport builderSupport) { @@ -35,7 +35,7 @@ public class BuilderActionToggleStateEvaluator extends BuilderActionBase { @Nonnull public BuilderActionToggleStateEvaluator readConfig(@Nonnull JsonElement data) { - this.requireBoolean(data, "Enabled", b -> this.enable = b, BuilderDescriptorState.Stable, "Whether or not to enable the state evaluator", null); + this.requireBoolean(data, "On", b -> this.on = b, BuilderDescriptorState.Stable, "Whether or not to enable the state evaluator", null); if (!this.isCreatingDescriptor()) { this.stateHelper.setRequiresStateEvaluator(); } @@ -43,7 +43,7 @@ public class BuilderActionToggleStateEvaluator extends BuilderActionBase { return this; } - public boolean isEnable() { - return this.enable; + public boolean isOn() { + return this.on; } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/timer/ActionSetAlarm.java b/src/com/hypixel/hytale/server/npc/corecomponents/timer/ActionSetAlarm.java index 22d9bffe..9f3e9287 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/timer/ActionSetAlarm.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/timer/ActionSetAlarm.java @@ -15,6 +15,7 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAmount; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionSetAlarm extends ActionBase { protected final Alarm alarm; @@ -33,7 +34,7 @@ public class ActionSetAlarm extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); if (this.cancel) { this.alarm.set(ref, null, store); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/timer/ActionTimer.java b/src/com/hypixel/hytale/server/npc/corecomponents/timer/ActionTimer.java index d6fc36b0..ac21e85d 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/timer/ActionTimer.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/timer/ActionTimer.java @@ -16,6 +16,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.Timer; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionTimer extends ActionBase { protected final Timer timer; @@ -76,7 +77,7 @@ public class ActionTimer extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); switch (this.action) { case START: diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionRandom.java b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionRandom.java index 977d94dd..572e4a48 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionRandom.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionRandom.java @@ -42,7 +42,7 @@ public class ActionRandom extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { int length = this.actions.length; if (super.canExecute(ref, role, sensorInfo, dt, store) && length != 0) { if (this.current != null) { @@ -66,7 +66,7 @@ public class ActionRandom extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); if (this.availableActionsCount == 0) { return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionResetInstructions.java b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionResetInstructions.java index 76d9c2fe..c88ce194 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionResetInstructions.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionResetInstructions.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderActi import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionResetInstructions extends ActionBase { protected final int[] instructions; @@ -19,7 +20,7 @@ public class ActionResetInstructions extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); role.addDeferredAction((_ref, _role, _dt, _store) -> this.resetInstructions(_role, _dt)); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionSequence.java b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionSequence.java index 9319a104..3064db42 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionSequence.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionSequence.java @@ -28,12 +28,12 @@ public class ActionSequence extends ActionBase implements IAnnotatedComponentCol } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && this.actions.canExecute(ref, role, sensorInfo, dt, store); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); return this.actions.execute(ref, role, sensorInfo, dt, store); } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionSetFlag.java b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionSetFlag.java index 05dcd8b6..fd3dd17b 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionSetFlag.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionSetFlag.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderActi import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionSetFlag extends ActionBase { protected final int flagIndex; @@ -21,7 +22,7 @@ public class ActionSetFlag extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); role.setFlag(this.flagIndex, this.value); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionTimeout.java b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionTimeout.java index f440655d..d811d2f8 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionTimeout.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/utility/ActionTimeout.java @@ -30,7 +30,7 @@ public class ActionTimeout extends ActionWithDelay implements IAnnotatedComponen } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { if (super.canExecute(ref, role, sensorInfo, dt, store) && (this.action == null || this.action.canExecute(ref, role, sensorInfo, dt, store))) { if (!this.isDelaying() && this.isDelayPrepared()) { this.startDelay(role.getEntitySupport()); @@ -43,7 +43,7 @@ public class ActionTimeout extends ActionWithDelay implements IAnnotatedComponen } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); if (this.action != null) { this.action.execute(ref, role, sensorInfo, dt, store); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/utility/SensorAdjustPosition.java b/src/com/hypixel/hytale/server/npc/corecomponents/utility/SensorAdjustPosition.java index b5df2f14..5199d1c0 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/utility/SensorAdjustPosition.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/utility/SensorAdjustPosition.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.corecomponents.utility; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -21,6 +20,7 @@ import com.hypixel.hytale.server.npc.util.IAnnotatedComponent; import com.hypixel.hytale.server.npc.util.IAnnotatedComponentCollection; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SensorAdjustPosition extends SensorBase implements IAnnotatedComponentCollection { protected final Sensor sensor; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/utility/builders/BuilderSensorAdjustPosition.java b/src/com/hypixel/hytale/server/npc/corecomponents/utility/builders/BuilderSensorAdjustPosition.java index 6fc76a6b..a53ae765 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/utility/builders/BuilderSensorAdjustPosition.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/utility/builders/BuilderSensorAdjustPosition.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.npc.corecomponents.utility.builders; import com.google.gson.JsonElement; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; import com.hypixel.hytale.server.npc.asset.builder.BuilderObjectReferenceHelper; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -17,6 +16,7 @@ import java.util.List; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BuilderSensorAdjustPosition extends BuilderSensorBase { protected final BuilderObjectReferenceHelper sensor = new BuilderObjectReferenceHelper<>(Sensor.class, this); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionMakePath.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionMakePath.java index 707d36e5..f976e275 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionMakePath.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionMakePath.java @@ -28,12 +28,12 @@ public class ActionMakePath extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && !this.built; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionPlaceBlock.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionPlaceBlock.java index 8ebb5028..06794a44 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionPlaceBlock.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionPlaceBlock.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -21,6 +20,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.BlockPlacementHelper; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ActionPlaceBlock extends ActionBase { protected static final ComponentType BOUNDING_BOX_COMPONENT_TYPE = BoundingBox.getComponentType(); @@ -57,10 +57,10 @@ public class ActionPlaceBlock extends ActionBase { maxDistance += hitBox.getBoundingBox().getMaximumExtent(); } - int x = MathUtil.floor(this.target.getX()); - int y = MathUtil.floor(this.target.getY()); - int z = MathUtil.floor(this.target.getZ()); - if (transformComponent.getPosition().distanceSquaredTo(x, y, z) > maxDistance * maxDistance) { + int x = MathUtil.floor(this.target.x()); + int y = MathUtil.floor(this.target.y()); + int z = MathUtil.floor(this.target.z()); + if (transformComponent.getPosition().distanceSquared(x, y, z) > maxDistance * maxDistance) { return false; } else if (sensorInfo instanceof CachedPositionProvider && !((CachedPositionProvider)sensorInfo).isFromCache()) { return true; @@ -77,12 +77,12 @@ public class ActionPlaceBlock extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); World world = store.getExternalData().getWorld(); - WorldChunk chunk = world.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(this.target.getX(), this.target.getZ())); + WorldChunk chunk = world.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(this.target.x(), this.target.z())); chunk.setBlock( - MathUtil.floor(this.target.getX()), MathUtil.floor(this.target.getY()), MathUtil.floor(this.target.getZ()), role.getWorldSupport().getBlockToPlace() + MathUtil.floor(this.target.x()), MathUtil.floor(this.target.y()), MathUtil.floor(this.target.z()), role.getWorldSupport().getBlockToPlace() ); return true; } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetBlockSensors.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetBlockSensors.java index 1d76e985..75453482 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetBlockSensors.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetBlockSensors.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.role.support.WorldSupport; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionResetBlockSensors extends ActionBase { protected final int[] blockSets; @@ -24,7 +25,7 @@ public class ActionResetBlockSensors extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); WorldSupport worldSupport = role.getWorldSupport(); if (this.blockSets.length == 0) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetPath.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetPath.java index 3efc46fc..56666e0a 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetPath.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetPath.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderAction import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionResetPath extends ActionBase { public ActionResetPath(@Nonnull BuilderActionResetPath builder) { @@ -15,7 +16,7 @@ public class ActionResetPath extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); role.getWorldSupport().requestNewPath(); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetSearchRays.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetSearchRays.java index f6e42ac8..65cdb3b5 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetSearchRays.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionResetSearchRays.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.role.support.WorldSupport; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionResetSearchRays extends ActionBase { protected final int[] searchRayIds; @@ -20,7 +21,7 @@ public class ActionResetSearchRays extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); WorldSupport worldSupport = role.getWorldSupport(); if (this.searchRayIds.length == 0) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionSetBlockToPlace.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionSetBlockToPlace.java index 1a29bad8..b9bd6e57 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionSetBlockToPlace.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionSetBlockToPlace.java @@ -10,6 +10,7 @@ import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderAction import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionSetBlockToPlace extends ActionBase { protected final String blockType; @@ -20,12 +21,12 @@ public class ActionSetBlockToPlace extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && BlockType.getAssetMap().getAsset(this.blockType) != null; } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); role.getWorldSupport().setBlockToPlace(this.blockType); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionSetLeashPosition.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionSetLeashPosition.java index e11c1687..889e6873 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionSetLeashPosition.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionSetLeashPosition.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.server.npc.corecomponents.world; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.corecomponents.ActionBase; @@ -50,9 +50,9 @@ public class ActionSetLeashPosition extends ActionBase { assert entityTransformComponent != null; - Vector3f entityBodyRotation = entityTransformComponent.getRotation(); - selfNpcComponent.getLeashPoint().assign(entityTransformComponent.getPosition()); - selfNpcComponent.setLeashPitch(entityBodyRotation.getPitch()); - selfNpcComponent.setLeashHeading(entityBodyRotation.getYaw()); + Rotation3f entityBodyRotation = entityTransformComponent.getRotation(); + selfNpcComponent.getLeashPoint().set(entityTransformComponent.getPosition()); + selfNpcComponent.setLeashPitch(entityBodyRotation.pitch()); + selfNpcComponent.setLeashHeading(entityBodyRotation.yaw()); } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionTriggerSpawners.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionTriggerSpawners.java index 9aee6121..cc18a8d8 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionTriggerSpawners.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/ActionTriggerSpawners.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -13,10 +12,11 @@ import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderAction import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class ActionTriggerSpawners extends ActionBase { protected static final ComponentType SPAWN_MARKER_ENTITY_COMPONENT_TYPE = SpawnMarkerEntity.getComponentType(); @@ -35,7 +35,7 @@ public class ActionTriggerSpawners extends ActionBase { this.range = builder.getRange(support); this.rangeSquared = this.range * this.range; this.count = builder.getCount(support); - this.triggerList = this.count > 0 ? new ObjectArrayList<>(this.count) : null; + this.triggerList = this.count > 0 ? new ReferenceArrayList<>(this.count) : null; } @Override @@ -44,7 +44,7 @@ public class ActionTriggerSpawners extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); this.parentRef = ref; List> spawners = role.getPositionCache().getSpawnMarkerList(); @@ -96,7 +96,7 @@ public class ActionTriggerSpawners extends ActionBase { SpawnMarkerEntity targetMarkerEntityComponent = store.getComponent(targetRef, SPAWN_MARKER_ENTITY_COMPONENT_TYPE); return targetMarkerEntityComponent == null || !targetMarkerEntityComponent.isManualTrigger() - || !(parentPosition.distanceSquaredTo(targetPosition) <= this.rangeSquared) + || !(parentPosition.distanceSquared(targetPosition) <= this.rangeSquared) || this.spawner != null && !this.spawner.equals(targetMarkerEntityComponent.getSpawnMarkerId()) ? null : targetRef; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/BodyMotionPath.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/BodyMotionPath.java index 57a6d44c..4cfcac89 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/BodyMotionPath.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/BodyMotionPath.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.path.IPath; import com.hypixel.hytale.server.core.universe.world.path.IPathWaypoint; @@ -28,6 +28,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BodyMotionPath extends BodyMotionBase { public static final double MIN_GUARD_POINT_WAIT_TIME = 1.0; @@ -71,7 +72,7 @@ public class BodyMotionPath extends BodyMotionBase { protected float observationSector; protected double currentObservationDelay; protected boolean rotating; - protected final Vector3d previousSteeringTranslation = new Vector3d(Vector3d.MIN); + protected final Vector3d previousSteeringTranslation = new Vector3d(Vector3dUtil.MIN); protected int currentViewSegment; public BodyMotionPath(@Nonnull BuilderBodyMotionPath builder, @Nonnull BuilderSupport support) { @@ -119,7 +120,7 @@ public class BodyMotionPath extends BodyMotionBase { @Nonnull ComponentAccessor componentAccessor ) { desiredSteering.clear(); - if (!role.getActiveMotionController().canAct(ref, componentAccessor)) { + if (!role.getActiveMotionController().canSteer(ref, componentAccessor)) { return true; } else { IPathProvider info = sensorInfo.getExtraInfo(IPathProvider.class); @@ -152,7 +153,7 @@ public class BodyMotionPath extends BodyMotionBase { this.currentNodeDelay = 0.0; } - float heading = transformComponent.getRotation().getYaw(); + float heading = transformComponent.getRotation().yaw(); if (this.currentNodeDelay > 0.0) { this.currentNodeDelay -= dt; if (this.observationSector != 0.0F || numWaypoints == 1) { @@ -174,9 +175,9 @@ public class BodyMotionPath extends BodyMotionBase { MotionController activeMotionController = role.getActiveMotionController(); Vector3d componentSelector = activeMotionController.getComponentSelector(); WorldSupport worldSupport = role.getWorldSupport(); - this.currentPosition.assign(position.getX(), position.getY(), position.getZ()); + this.currentPosition.set(position.x(), position.y(), position.z()); int lastIndex = this.currentWaypointIndex; - this.lastWaypointPosition.assign(this.currentWaypointPosition); + this.lastWaypointPosition.set(this.currentWaypointPosition); while (this.closeToPosition(this.currentWaypointPosition, activeMotionController)) { if (this.nextPositionValid || numWaypoints == 1) { @@ -185,7 +186,7 @@ public class BodyMotionPath extends BodyMotionBase { return false; } - this.nodeViewDirection = wayPoint.getWaypointRotation(componentAccessor).getYaw(); + this.nodeViewDirection = wayPoint.getWaypointRotation(componentAccessor).yaw(); this.nodeWaitTime = wayPoint.getPauseTime(); this.observationSector = wayPoint.getObservationAngle() / 2.0F; this.currentViewSegment = 0; @@ -213,20 +214,20 @@ public class BodyMotionPath extends BodyMotionBase { if (!this.nextPositionValid || this.closeToPosition(this.nextPosition, activeMotionController)) { if (this.pathWidth == 0.0) { - this.nextPosition.assign(this.currentWaypointPosition); + this.nextPosition.set(this.currentWaypointPosition); } else { double maxDistance = NPCPhysicsMath.dotProduct( this.currentWaypointPosition, this.lastWaypointPosition, this.currentPosition, componentSelector ); double distance = Math.min(RandomExtra.randomRange(this.minWalkDistance, this.maxWalkDistance), maxDistance); if (distance >= maxDistance - this.nodeWidth) { - this.nextPosition.assign(this.currentWaypointPosition); + this.nextPosition.set(this.currentWaypointPosition); } else { NPCPhysicsMath.orthoComposition( this.lastWaypointPosition, this.currentWaypointPosition, distance, - Vector3d.UP, + Vector3dUtil.UP, RandomExtra.randomRange(-this.pathWidth / 2.0, this.pathWidth / 2.0), this.nextPosition ); @@ -299,7 +300,7 @@ public class BodyMotionPath extends BodyMotionBase { desiredSteering.scaleTranslation(this.currentSpeed); } - this.previousSteeringTranslation.assign(desiredSteering.getTranslation()); + this.previousSteeringTranslation.set(desiredSteering.getTranslation()); return true; } } else { @@ -485,7 +486,7 @@ public class BodyMotionPath extends BodyMotionBase { } this.waypointIndexUpdated(path, componentAccessor); - this.lastWaypointPosition.assign(lastPos.getX(), lastPos.getY(), lastPos.getZ()); + this.lastWaypointPosition.set(lastPos.x(), lastPos.y(), lastPos.z()); return true; } else { return false; @@ -496,7 +497,7 @@ public class BodyMotionPath extends BodyMotionBase { IPathWaypoint pathWaypoint = path.get(this.currentWaypointIndex); if (pathWaypoint != null) { Vector3d pathWaypointPosition = pathWaypoint.getWaypointPosition(componentAccessor); - this.currentWaypointPosition.assign(pathWaypointPosition); + this.currentWaypointPosition.set(pathWaypointPosition); } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/HeadMotionObserve.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/HeadMotionObserve.java index 1050b728..0e493e38 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/HeadMotionObserve.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/HeadMotionObserve.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.Rangef; import com.hypixel.hytale.server.core.asset.type.model.config.camera.CameraSettings; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; @@ -70,14 +70,14 @@ public class HeadMotionObserve extends HeadMotionBase { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); - this.steeringForceRotate.setHeading(headRotation.getYaw()); - this.steeringForceRotate.setDesiredHeading(transformComponent.getRotation().getYaw() + this.targetBodyOffsetYaw); + Rotation3f headRotation = headRotationComponent.getRotation(); + this.steeringForceRotate.setHeading(headRotation.yaw()); + this.steeringForceRotate.setDesiredHeading(transformComponent.getRotation().yaw() + this.targetBodyOffsetYaw); if (this.steeringForceRotate.compute(desiredSteering)) { desiredSteering.setRelativeTurnSpeed(this.relativeTurnSpeed); return true; } else { - desiredSteering.setYaw(transformComponent.getRotation().getYaw() + this.targetBodyOffsetYaw); + desiredSteering.setYaw(transformComponent.getRotation().yaw() + this.targetBodyOffsetYaw); if (this.tickDelay(dt)) { return true; } else { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorBlock.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorBlock.java index 4a958e55..014d195f 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorBlock.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorBlock.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; @@ -25,6 +25,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.sensorinfo.PositionProvider; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorBlock extends SensorBase { protected final double range; @@ -62,13 +63,13 @@ public class SensorBlock extends SensorBase { BlockTarget target = role.getWorldSupport().getCachedBlockTarget(this.blockSet); Vector3d position = target.getPosition(); - if (!position.equals(Vector3d.MIN)) { + if (!position.equals(Vector3dUtil.MIN)) { WorldChunk targetChunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(position.x, position.z)); if (targetChunk != null) { BlockSection section = targetChunk.getBlockChunk().getSectionAtBlockY(MathUtil.floor(position.y)); if (section.getLocalChangeCounter() == target.getChunkChangeRevision() || section.get(MathUtil.floor(position.x), MathUtil.floor(position.y), MathUtil.floor(position.z)) == target.getFoundBlockType()) { - if (!(Math.abs(entityPos.y - position.y) > this.yRange) && !(entityPos.distanceSquaredTo(position) > this.range * this.range)) { + if (!(Math.abs(entityPos.y - position.y) > this.yRange) && !(entityPos.distanceSquared(position) > this.range * this.range)) { this.positionProvider.setTarget(position); return true; } else { @@ -89,7 +90,7 @@ public class SensorBlock extends SensorBase { this.positionProvider.clear(); return false; } else { - position.assign(blockData.getXCentre(), blockData.getYCentre(), blockData.getZCentre()); + position.set(blockData.getXCentre(), blockData.getYCentre(), blockData.getZCentre()); int blockTypeId = blockData.getBlockType(); target.setFoundBlockType(blockTypeId); target.setChunkChangeRevision(blockData.getChunkSection().getLocalChangeCounter()); diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorBlockChange.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorBlockChange.java index a9017062..d3c48a10 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorBlockChange.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorBlockChange.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.world; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -12,6 +11,7 @@ import com.hypixel.hytale.server.npc.components.messaging.PlayerBlockEventSuppor import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorBlockChange; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SensorBlockChange extends SensorEvent { public SensorBlockChange(@Nonnull BuilderSensorBlockChange builder, @Nonnull BuilderSupport support) { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorCanPlace.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorCanPlace.java index de2bd53a..7d368e34 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorCanPlace.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorCanPlace.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox; @@ -20,6 +20,8 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.util.BlockPlacementHelper; import java.util.function.Supplier; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class SensorCanPlace extends SensorBase { protected static final ComponentType BOUNDING_BOX_COMPONENT_TYPE = BoundingBox.getComponentType(); @@ -54,7 +56,7 @@ public class SensorCanPlace extends SensorBase { assert transformComponent != null; World world = store.getExternalData().getWorld(); - float yaw = transformComponent.getRotation().getYaw(); + float yaw = transformComponent.getRotation().yaw(); float piQuarter = (float) (Math.PI / 4); yaw = Math.round(yaw / piQuarter) * piQuarter; this.direction.apply(this.transform, yaw); @@ -74,16 +76,16 @@ public class SensorCanPlace extends SensorBase { double transformZ = zNegative ? -this.transform.z : this.transform.z; double magnitude = Math.sqrt(npcX * npcX * transformX + npcZ * npcZ * transformZ) + Math.sqrt(blockX * blockX * transformX + blockZ * blockZ * transformZ); - this.transform.setLength(magnitude); + this.transform.normalize(magnitude); this.transform.add(xPositive ? 1.0 : 0.0, 0.0, zPositive ? 1.0 : 0.0); } this.offset.apply(this.transform); Vector3d position = transformComponent.getPosition(); - this.transform.add(position.getX(), position.getY(), position.getZ()).floor(); - int x = (int)this.transform.getX(); - int y = (int)this.transform.getY(); - int z = (int)this.transform.getZ(); + this.transform.add(position.x(), position.y(), position.z()).floor(); + int x = (int)this.transform.x(); + int y = (int)this.transform.y(); + int z = (int)this.transform.z(); if (!this.cachedPosition.equals(this.transform)) { this.delay = 0.0; } @@ -95,7 +97,7 @@ public class SensorCanPlace extends SensorBase { if (!((this.delay -= dt) > 0.0)) { this.cachedResult = canPlaceUnitBlock && BlockPlacementHelper.canPlaceBlock(world, placedBlockType, 0, this.allowEmptyMaterials, x, y, z); - this.cachedPosition.assign(this.transform); + this.cachedPosition.set(this.transform); this.delay = this.retryDelay; if (!this.cachedResult) { this.positionProvider.clear(); @@ -132,20 +134,20 @@ public class SensorCanPlace extends SensorBase { } public static enum Direction implements Supplier { - Forward(Vector3d.FORWARD), - Backward(Vector3d.BACKWARD), - Left(Vector3d.LEFT), - Right(Vector3d.RIGHT); + Forward(Vector3dUtil.FORWARD), + Backward(Vector3dUtil.BACKWARD), + Left(Vector3dUtil.LEFT), + Right(Vector3dUtil.RIGHT); - private final Vector3d direction; + private final Vector3dc direction; - private Direction(Vector3d direction) { + private Direction(Vector3dc direction) { this.direction = direction; } @Nonnull public Vector3d apply(@Nonnull Vector3d target, float rotation) { - return target.assign(this.direction).rotateY(rotation); + return target.set(this.direction).rotateY(rotation); } @Nonnull @@ -155,13 +157,13 @@ public class SensorCanPlace extends SensorBase { } public static enum Offset implements Supplier { - HeadPosition(Vector3d.UP), - BodyPosition(Vector3d.ZERO), - FootPosition(Vector3d.DOWN); + HeadPosition(Vector3dUtil.UP), + BodyPosition(Vector3dUtil.ZERO), + FootPosition(Vector3dUtil.DOWN); - private final Vector3d offset; + private final Vector3dc offset; - private Offset(Vector3d offset) { + private Offset(Vector3dc offset) { this.offset = offset; } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorEntityEvent.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorEntityEvent.java index 8668c53d..ddf3ea5a 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorEntityEvent.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorEntityEvent.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.world; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -11,6 +10,7 @@ import com.hypixel.hytale.server.npc.components.messaging.PlayerEntityEventSuppo import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorEntityEvent; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SensorEntityEvent extends SensorEvent { private final boolean flockOnly; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorLeash.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorLeash.java index aa3a9d9a..4193835d 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorLeash.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorLeash.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.corecomponents.world; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -13,6 +12,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.sensorinfo.PositionProvider; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorLeash extends SensorBase { protected final double range; @@ -36,7 +36,7 @@ public class SensorLeash extends SensorBase { NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType()); Vector3d leashPoint = npcComponent.getLeashPoint(); - if (transformComponent.getPosition().distanceSquaredTo(leashPoint) > this.rangeSq) { + if (transformComponent.getPosition().distanceSquared(leashPoint) > this.rangeSq) { this.positionProvider.setTarget(leashPoint); return true; } else { diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorPath.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorPath.java index 413f83e1..9c963e82 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorPath.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorPath.java @@ -11,7 +11,7 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.component.WorldGenId; import com.hypixel.hytale.server.core.universe.world.World; @@ -28,19 +28,20 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.sensorinfo.PathProvider; import com.hypixel.hytale.server.npc.sensorinfo.PositionProvider; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.function.Supplier; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SensorPath extends SensorBase { protected final double range; protected final SensorPath.PathType pathType; - protected final Vector3d closestWaypoint = new Vector3d(Vector3d.MIN); + protected final Vector3d closestWaypoint = new Vector3d(Vector3dUtil.MIN); protected final HashSet disallowedPaths = new HashSet<>(); protected final PathProvider pathProvider = new PathProvider(); protected final PositionProvider positionProvider = new PositionProvider(null, this.pathProvider); @@ -118,7 +119,7 @@ public class SensorPath extends SensorBase { this.positionProvider.clear(); return false; } else { - this.closestWaypoint.assign(Vector3d.MIN); + this.closestWaypoint.set(Vector3dUtil.MIN); this.findClosestWaypoint(path, position, this.closestWaypoint, store); if (!this.isInRange(this.distanceSquared)) { this.pathProvider.clear(); @@ -207,7 +208,7 @@ public class SensorPath extends SensorBase { break; case AnyPrefabPath: SpatialResource, EntityStore> spatialResource = store.getResource(this.prefabPathSpatialResource); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialResource.getSpatialStructure().ordered(position, this.range, results); if (results.isEmpty()) { this.loadStatus = SensorPath.LoadStatus.WAITING; @@ -229,7 +230,7 @@ public class SensorPath extends SensorBase { assert eTransformComponent != null; - double dist2 = position.distanceSquaredTo(eTransformComponent.getPosition()); + double dist2 = position.distanceSquared(eTransformComponent.getPosition()); if (dist2 < nearest2) { nearest2 = dist2; nearestWaypoint = ePatrolPathMarkerEntityComponent; @@ -264,8 +265,8 @@ public class SensorPath extends SensorBase { @Nonnull IPath path, @Nonnull Vector3d position, @Nonnull Vector3d cachedTarget, @Nonnull ComponentAccessor componentAccessor ) { double prevDistanceSquared = this.distanceSquared; - if (!cachedTarget.equals(Vector3d.MIN)) { - double newDistance = position.distanceSquaredTo(cachedTarget); + if (!cachedTarget.equals(Vector3dUtil.MIN)) { + double newDistance = position.distanceSquared(cachedTarget); if (newDistance <= this.distanceSquared) { this.distanceSquared = newDistance; return; @@ -278,10 +279,10 @@ public class SensorPath extends SensorBase { IPathWaypoint pathWaypoint = path.get(i); if (pathWaypoint != null) { Vector3d waypoint = pathWaypoint.getWaypointPosition(componentAccessor); - double distance = position.distanceSquaredTo(waypoint); + double distance = position.distanceSquared(waypoint); if (distance < this.distanceSquared) { this.distanceSquared = distance; - cachedTarget.assign(waypoint); + cachedTarget.set(waypoint); } } } diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorReadPosition.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorReadPosition.java index 081dad5c..e2f28128 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorReadPosition.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorReadPosition.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.server.npc.corecomponents.world; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; @@ -12,6 +12,7 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.sensorinfo.PositionProvider; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorReadPosition extends SensorBase { protected final int slot; @@ -51,7 +52,7 @@ public class SensorReadPosition extends SensorBase { position = role.getMarkedEntitySupport().getStoredPosition(this.slot); } - if (position.equals(Vector3d.MIN)) { + if (position.equals(Vector3dUtil.MIN)) { this.positionProvider.clear(); return false; } else { @@ -59,7 +60,7 @@ public class SensorReadPosition extends SensorBase { assert transformComponent != null; - double dist2 = transformComponent.getPosition().distanceSquaredTo(position); + double dist2 = transformComponent.getPosition().distanceSquared(position); if (!(dist2 > this.range * this.range) && !(dist2 < this.minRange * this.minRange)) { this.positionProvider.setTarget(position); return true; diff --git a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorSearchRay.java b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorSearchRay.java index de922b31..99ce227d 100644 --- a/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorSearchRay.java +++ b/src/com/hypixel/hytale/server/npc/corecomponents/world/SensorSearchRay.java @@ -4,8 +4,8 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; @@ -20,6 +20,7 @@ import com.hypixel.hytale.server.npc.sensorinfo.InfoProvider; import com.hypixel.hytale.server.npc.sensorinfo.PositionProvider; import com.hypixel.hytale.server.npc.util.RayBlockHitTest; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SensorSearchRay extends SensorBase { protected final int id; @@ -63,9 +64,9 @@ public class SensorSearchRay extends SensorBase { assert headRotationComponent != null; Vector3d position = transformComponent.getPosition(); - Vector3f headRotation = headRotationComponent.getRotation(); + Rotation3f headRotation = headRotationComponent.getRotation(); Vector3d cachedPosition = role.getWorldSupport().getCachedSearchRayPosition(this.id); - if (!cachedPosition.equals(Vector3d.MIN)) { + if (!cachedPosition.equals(Vector3dUtil.MIN)) { WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(cachedPosition.x, cachedPosition.z)); if (chunk != null) { BlockSection section = chunk.getBlockChunk().getSectionAtBlockY(MathUtil.floor(cachedPosition.y)); @@ -74,34 +75,34 @@ public class SensorSearchRay extends SensorBase { return true; } - cachedPosition.assign(Vector3d.MIN); + cachedPosition.set(Vector3dUtil.MIN); this.positionProvider.clear(); } } else if ((this.throttleTimeRemaining -= dt) > 0.0 - && Math.abs(headRotation.getYaw() - this.lastCheckedYaw) <= this.minRetestAngle - && position.distanceSquaredTo(this.lastCheckedPosition) <= this.minRetestMoveSquared) { + && Math.abs(headRotation.yaw() - this.lastCheckedYaw) <= this.minRetestAngle + && position.distanceSquared(this.lastCheckedPosition) <= this.minRetestMoveSquared) { this.positionProvider.clear(); return false; } RayBlockHitTest blockRaySearch = RayBlockHitTest.THREAD_LOCAL.get(); if (!blockRaySearch.init(ref, this.blockSet, this.angle, store)) { - cachedPosition.assign(Vector3d.MIN); + cachedPosition.set(Vector3dUtil.MIN); this.positionProvider.clear(); blockRaySearch.clear(); return false; } else { - this.lastCheckedPosition.assign(position); - this.lastCheckedYaw = headRotation.getYaw(); + this.lastCheckedPosition.set(position); + this.lastCheckedYaw = headRotation.yaw(); this.throttleTimeRemaining = this.throttleTime; boolean result = blockRaySearch.run(this.range); if (result) { this.lastBlockRevision = blockRaySearch.getLastBlockRevision(); Vector3d targetPosition = blockRaySearch.getHitPosition(); - cachedPosition.assign(targetPosition.x + 0.5, targetPosition.y + 0.5, targetPosition.z + 0.5); + cachedPosition.set(targetPosition.x + 0.5, targetPosition.y + 0.5, targetPosition.z + 0.5); this.positionProvider.setTarget(cachedPosition); } else { - cachedPosition.assign(Vector3d.MIN); + cachedPosition.set(Vector3dUtil.MIN); this.positionProvider.clear(); } diff --git a/src/com/hypixel/hytale/server/npc/decisionmaker/core/Evaluator.java b/src/com/hypixel/hytale/server/npc/decisionmaker/core/Evaluator.java index e4367d03..f4d9ac3d 100644 --- a/src/com/hypixel/hytale/server/npc/decisionmaker/core/Evaluator.java +++ b/src/com/hypixel/hytale/server/npc/decisionmaker/core/Evaluator.java @@ -11,6 +11,7 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; @@ -45,17 +46,12 @@ public abstract class Evaluator { public Evaluator.OptionHolder evaluate( int index, @Nonnull ArchetypeChunk archetypeChunk, CommandBuffer commandBuffer, @Nonnull EvaluationContext context ) { - NPCEntity npcComponent = archetypeChunk.getComponent(index, NPCEntity.getComponentType()); - - assert npcComponent != null; - - UUIDComponent uuidComponent = archetypeChunk.getComponent(index, UUIDComponent.getComponentType()); - - assert uuidComponent != null; - + NPCEntity npcComponent = null; + UUIDComponent uuidComponent = null; Evaluator.OptionHolder bestOption = null; double minimumWeight = context.getMinimumWeightCoefficient(); int nonMatchingIndex = this.options.size(); + HytaleLogger.Api logContext = LOGGER.at(Level.FINE); for (int i = 0; i < this.options.size(); i++) { Evaluator.OptionHolder optionHolder = this.options.get(i); @@ -65,8 +61,19 @@ public abstract class Evaluator { } double utility = optionHolder.calculateUtility(index, archetypeChunk, commandBuffer, context); - HytaleLogger.Api logContext = LOGGER.at(Level.FINE); if (logContext.isEnabled()) { + if (npcComponent == null) { + npcComponent = archetypeChunk.getComponent(index, NPCEntity.getComponentType()); + + assert npcComponent != null; + } + + if (uuidComponent == null) { + uuidComponent = archetypeChunk.getComponent(index, UUIDComponent.getComponentType()); + + assert uuidComponent != null; + } + logContext.log("%s with uuid %s: Scored option %s at %s", npcComponent.getRoleName(), uuidComponent.getUuid(), optionHolder.option, utility); } @@ -115,6 +122,8 @@ public abstract class Evaluator { protected double utility; public OptionHolder(OptionType option) { + Objects.requireNonNull(Evaluator.this); + super(); this.option = option; } diff --git a/src/com/hypixel/hytale/server/npc/decisionmaker/core/Option.java b/src/com/hypixel/hytale/server/npc/decisionmaker/core/Option.java index 08c5855e..467a96d6 100644 --- a/src/com/hypixel/hytale/server/npc/decisionmaker/core/Option.java +++ b/src/com/hypixel/hytale/server/npc/decisionmaker/core/Option.java @@ -96,22 +96,28 @@ public abstract class Option { CommandBuffer commandBuffer, @Nonnull EvaluationContext context ) { - NPCEntity npcComponent = archetypeChunk.getComponent(selfIndex, NPCEntity.getComponentType()); - - assert npcComponent != null; - - UUIDComponent uuidComponent = archetypeChunk.getComponent(selfIndex, UUIDComponent.getComponentType()); - - assert uuidComponent != null; - + NPCEntity npcComponent = null; + UUIDComponent uuidComponent = null; double compensationFactor = 1.0 - 1.0 / this.sortedConditions.length; double result = 1.0; + HytaleLogger.Api logContext = Evaluator.LOGGER.at(Level.FINE); for (Option.ConditionReference reference : this.sortedConditions) { Condition condition = reference.get(); double score = condition.calculateUtility(selfIndex, archetypeChunk, target, commandBuffer, context); - HytaleLogger.Api logContext = Evaluator.LOGGER.at(Level.FINE); if (logContext.isEnabled()) { + if (npcComponent == null) { + npcComponent = archetypeChunk.getComponent(selfIndex, NPCEntity.getComponentType()); + + assert npcComponent != null; + } + + if (uuidComponent == null) { + uuidComponent = archetypeChunk.getComponent(selfIndex, UUIDComponent.getComponentType()); + + assert uuidComponent != null; + } + logContext.log("%s with uuid %s: Scored condition %s at %s", npcComponent.getRoleName(), uuidComponent.getUuid(), condition, score); } diff --git a/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/SelfHasEffectCondition.java b/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/SelfHasEffectCondition.java new file mode 100644 index 00000000..4afdfbe7 --- /dev/null +++ b/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/SelfHasEffectCondition.java @@ -0,0 +1,62 @@ +package com.hypixel.hytale.server.npc.decisionmaker.core.conditions; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; +import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.npc.decisionmaker.core.EvaluationContext; +import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.base.SimpleCondition; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class SelfHasEffectCondition extends SimpleCondition { + public static final BuilderCodec CODEC = BuilderCodec.builder( + SelfHasEffectCondition.class, SelfHasEffectCondition::new, ABSTRACT_CODEC + ) + .documentation("A simple boolean condition that returns whether the NPC has a specific active entity effect.") + .appendInherited( + new KeyedCodec<>("EffectId", Codec.STRING), + (condition, s) -> condition.entityEffectId = s, + condition -> condition.entityEffectId, + (condition, parent) -> condition.entityEffectId = parent.entityEffectId + ) + .addValidator(EntityEffect.VALIDATOR_CACHE.getValidator()) + .documentation("The entity effect to check for.") + .add() + .afterDecode(condition -> { + if (condition.entityEffectId != null) { + condition.entityEffectIndex = EntityEffect.getAssetMap().getIndex(condition.entityEffectId); + } + }) + .build(); + @Nullable + private String entityEffectId; + private int entityEffectIndex; + + protected SelfHasEffectCondition() { + } + + @Override + protected boolean evaluate( + int selfIndex, + @Nonnull ArchetypeChunk archetypeChunk, + Ref target, + @Nonnull CommandBuffer commandBuffer, + EvaluationContext context + ) { + Ref selfRef = archetypeChunk.getReferenceTo(selfIndex); + EffectControllerComponent effectController = commandBuffer.getComponent(selfRef, EffectControllerComponent.getComponentType()); + return effectController == null ? false : effectController.hasEffect(this.entityEffectIndex); + } + + @Nonnull + @Override + public String toString() { + return "SelfHasEffectCondition{entityEffectId='" + this.entityEffectId + "'} " + super.toString(); + } +} diff --git a/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/TargetDistanceCondition.java b/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/TargetDistanceCondition.java index 4318f3bc..b34ce4b0 100644 --- a/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/TargetDistanceCondition.java +++ b/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/TargetDistanceCondition.java @@ -5,13 +5,13 @@ import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.decisionmaker.core.EvaluationContext; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.base.ScaledCurveCondition; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class TargetDistanceCondition extends ScaledCurveCondition { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -41,7 +41,7 @@ public class TargetDistanceCondition extends ScaledCurveCondition { assert targetTransformComponent != null; Vector3d targetPos = targetTransformComponent.getPosition(); - return selfPos.distanceTo(targetPos); + return selfPos.distance(targetPos); } else { return Double.MAX_VALUE; } diff --git a/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/TargetHasEffectCondition.java b/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/TargetHasEffectCondition.java new file mode 100644 index 00000000..ceb10933 --- /dev/null +++ b/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/TargetHasEffectCondition.java @@ -0,0 +1,65 @@ +package com.hypixel.hytale.server.npc.decisionmaker.core.conditions; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; +import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.npc.decisionmaker.core.EvaluationContext; +import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.base.SimpleCondition; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class TargetHasEffectCondition extends SimpleCondition { + public static final BuilderCodec CODEC = BuilderCodec.builder( + TargetHasEffectCondition.class, TargetHasEffectCondition::new, ABSTRACT_CODEC + ) + .documentation("A simple boolean condition that returns whether the target entity has a specific active entity effect.") + .appendInherited( + new KeyedCodec<>("EffectId", Codec.STRING), + (condition, s) -> condition.entityEffectId = s, + condition -> condition.entityEffectId, + (condition, parent) -> condition.entityEffectId = parent.entityEffectId + ) + .addValidator(EntityEffect.VALIDATOR_CACHE.getValidator()) + .documentation("The entity effect to check for.") + .add() + .afterDecode(condition -> { + if (condition.entityEffectId != null) { + condition.entityEffectIndex = EntityEffect.getAssetMap().getIndex(condition.entityEffectId); + } + }) + .build(); + @Nullable + private String entityEffectId; + private int entityEffectIndex; + + protected TargetHasEffectCondition() { + } + + @Override + protected boolean evaluate( + int selfIndex, + ArchetypeChunk archetypeChunk, + @Nullable Ref target, + @Nonnull CommandBuffer commandBuffer, + EvaluationContext context + ) { + if (target != null && target.isValid()) { + EffectControllerComponent effectController = commandBuffer.getComponent(target, EffectControllerComponent.getComponentType()); + return effectController == null ? false : effectController.hasEffect(this.entityEffectIndex); + } else { + return false; + } + } + + @Nonnull + @Override + public String toString() { + return "TargetHasEffectCondition{entityEffectId='" + this.entityEffectId + "'} " + super.toString(); + } +} diff --git a/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/base/Condition.java b/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/base/Condition.java index 534f86d4..cbcd1908 100644 --- a/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/base/Condition.java +++ b/src/com/hypixel/hytale/server/npc/decisionmaker/core/conditions/base/Condition.java @@ -23,9 +23,11 @@ import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.IsInStateCond import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.LineOfSightCondition; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.NearbyCountCondition; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.RandomiserCondition; +import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.SelfHasEffectCondition; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.SelfStatAbsoluteCondition; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.SelfStatPercentageCondition; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.TargetDistanceCondition; +import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.TargetHasEffectCondition; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.TargetMovementStateCondition; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.TargetStatAbsoluteCondition; import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.TargetStatPercentageCondition; @@ -126,6 +128,8 @@ public abstract class Condition implements JsonAssetWithMap implements Component { @@ -136,6 +137,7 @@ public class StateEvaluator extends Evaluator implements Component< public class SelfOptionHolder extends Evaluator.OptionHolder { public SelfOptionHolder(StateOption option) { + Objects.requireNonNull(StateEvaluator.this); super(option); } diff --git a/src/com/hypixel/hytale/server/npc/entities/NPCEntity.java b/src/com/hypixel/hytale/server/npc/entities/NPCEntity.java index 675a83a3..9d05c52c 100644 --- a/src/com/hypixel/hytale/server/npc/entities/NPCEntity.java +++ b/src/com/hypixel/hytale/server/npc/entities/NPCEntity.java @@ -8,8 +8,8 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3fc; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.entityeffect.config.ApplicationEffects; @@ -22,7 +22,6 @@ import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.ActiveAnimationComponent; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; @@ -63,6 +62,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class NPCEntity extends LivingEntity implements INonPlayerCharacter { public static final BuilderCodec CODEC = BuilderCodec.builder(NPCEntity.class, NPCEntity::new, LivingEntity.CODEC) @@ -91,8 +92,8 @@ public class NPCEntity extends LivingEntity implements INonPlayerCharacter { .addField(new KeyedCodec<>("WorldgenId", Codec.INTEGER), (npcEntity, i) -> npcEntity.worldgenId = i, npcEntity -> npcEntity.worldgenId) .append(new KeyedCodec<>("PathManager", PathManager.CODEC), (npcEntity, manager) -> npcEntity.pathManager = manager, npcEntity -> npcEntity.pathManager) .add() - .addField(new KeyedCodec<>("LeashPos", Vector3d.CODEC), (npcEntity, v) -> { - npcEntity.leashPoint.assign(v); + .addField(new KeyedCodec<>("LeashPos", Vector3dUtil.CODEC), (npcEntity, v) -> { + npcEntity.leashPoint.set(v); npcEntity.hasLeashPosition = true; }, npcEntity -> npcEntity.requiresLeashPosition() ? npcEntity.leashPoint : null) .addField( @@ -173,10 +174,8 @@ public class NPCEntity extends LivingEntity implements INonPlayerCharacter { return this.alarmStore; } - @Nonnull - @Override - protected Inventory createDefaultInventory() { - return new Inventory((short)0, Inventory.DEFAULT_ARMOR_CAPACITY, (short)3, (short)0, (short)0); + public void setAlarmStore(@Nonnull AlarmStore alarmStore) { + this.alarmStore = alarmStore; } @Nullable @@ -189,7 +188,7 @@ public class NPCEntity extends LivingEntity implements INonPlayerCharacter { } public void storeTickStartPosition(@Nonnull Vector3d position) { - this.oldPosition.assign(position); + this.oldPosition.set(position); } public boolean tickDespawnAnimationRemainingSeconds(float dt) { @@ -304,10 +303,10 @@ public class NPCEntity extends LivingEntity implements INonPlayerCharacter { this.role.setMarkedTarget(targetSlot, target); } - public void saveLeashInformation(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { - this.leashPoint.assign(position); - this.leashHeading = rotation.getYaw(); - this.leashPitch = rotation.getPitch(); + public void saveLeashInformation(@Nonnull Vector3dc position, @Nonnull Rotation3fc rotation) { + this.leashPoint.set(position); + this.leashHeading = rotation.yaw(); + this.leashPitch = rotation.pitch(); this.saveLeashBlockType(); } @@ -323,7 +322,7 @@ public class NPCEntity extends LivingEntity implements INonPlayerCharacter { } public void setLeashPoint(@Nonnull Vector3d leashPoint) { - this.leashPoint.assign(leashPoint); + this.leashPoint.set(leashPoint); } public float getLeashHeading() { @@ -563,10 +562,6 @@ public class NPCEntity extends LivingEntity implements INonPlayerCharacter { return this.spawnInstant; } - public void setInventorySize(int hotbarCapacity, int inventoryCapacity, int offHandCapacity) { - this.setInventory(new Inventory((short)inventoryCapacity, Inventory.DEFAULT_ARMOR_CAPACITY, (short)hotbarCapacity, (short)offHandCapacity, (short)0)); - } - @Deprecated(forRemoval = true) public int getLegacyWorldgenId() { return this.worldgenId; diff --git a/src/com/hypixel/hytale/server/npc/instructions/ActionList.java b/src/com/hypixel/hytale/server/npc/instructions/ActionList.java index 36abb826..852925b3 100644 --- a/src/com/hypixel/hytale/server/npc/instructions/ActionList.java +++ b/src/com/hypixel/hytale/server/npc/instructions/ActionList.java @@ -39,7 +39,7 @@ public class ActionList { this.atomic = atomic; } - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { if (this.actions.length == 0) { return false; } else if (this.blocking) { @@ -63,7 +63,7 @@ public class ActionList { } } - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { if (this.blocking) { Action action = this.actions[this.actionIndex]; if (!action.canExecute(ref, role, sensorInfo, dt, store)) { diff --git a/src/com/hypixel/hytale/server/npc/instructions/BodyMotion.java b/src/com/hypixel/hytale/server/npc/instructions/BodyMotion.java index 093230fc..6129dda7 100644 --- a/src/com/hypixel/hytale/server/npc/instructions/BodyMotion.java +++ b/src/com/hypixel/hytale/server/npc/instructions/BodyMotion.java @@ -1,5 +1,9 @@ package com.hypixel.hytale.server.npc.instructions; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; +import java.util.EnumSet; import javax.annotation.Nullable; public interface BodyMotion extends Motion { @@ -7,4 +11,18 @@ public interface BodyMotion extends Motion { default BodyMotion getSteeringMotion() { return this; } + + default double getDesiredTargetDistance() { + return Double.MAX_VALUE; + } + + @Nullable + default Ref getDesiredTargetEntity() { + return null; + } + + @Nullable + default EnumSet getRelaxedConstraints() { + return null; + } } diff --git a/src/com/hypixel/hytale/server/npc/interactions/SpawnNPCInteraction.java b/src/com/hypixel/hytale/server/npc/interactions/SpawnNPCInteraction.java index a8c651fa..ed91b8ef 100644 --- a/src/com/hypixel/hytale/server/npc/interactions/SpawnNPCInteraction.java +++ b/src/com/hypixel/hytale/server/npc/interactions/SpawnNPCInteraction.java @@ -13,9 +13,8 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple; @@ -34,6 +33,8 @@ import com.hypixel.hytale.server.npc.validators.NPCRoleValidator; import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class SpawnNPCInteraction extends SimpleBlockInteraction { @Nonnull @@ -57,8 +58,8 @@ public class SpawnNPCInteraction extends SimpleBlockInteraction { .documentation("A weighted list of entity IDs from which an entity will be selected for spawning. Supersedes any provided EntityId.") .add() .append( - new KeyedCodec<>("SpawnOffset", Vector3d.CODEC), - (spawnNPCInteraction, s) -> spawnNPCInteraction.spawnOffset.assign(s), + new KeyedCodec<>("SpawnOffset", Vector3dUtil.CODEC), + (spawnNPCInteraction, s) -> spawnNPCInteraction.spawnOffset.set(s), spawnNPCInteraction -> spawnNPCInteraction.spawnOffset ) .documentation("The offset to apply to the spawn position of the NPC, relative to the block's rotation and center.") @@ -122,25 +123,31 @@ public class SpawnNPCInteraction extends SimpleBlockInteraction { BlockType blockType = worldChunkComponent.getBlockType(targetBlock.x, targetBlock.y, targetBlock.z); if (blockType == null) { - return new SpawnNPCInteraction.SpawnData(this.spawnOffset.clone().add(targetBlock).add(0.5, 0.5, 0.5), Vector3f.ZERO); + return new SpawnNPCInteraction.SpawnData( + new Vector3d(this.spawnOffset).add(targetBlock.x, targetBlock.y, targetBlock.z).add(0.5, 0.5, 0.5), new Rotation3f(Rotation3f.IDENTITY) + ); } else { BlockChunk blockChunkComponent = chunkStore.getStore().getComponent(chunkRef, BlockChunk.getComponentType()); if (blockChunkComponent == null) { - return new SpawnNPCInteraction.SpawnData(this.spawnOffset.clone().add(targetBlock).add(0.5, 0.5, 0.5), Vector3f.ZERO); + return new SpawnNPCInteraction.SpawnData( + new Vector3d(this.spawnOffset).add(targetBlock.x, targetBlock.y, targetBlock.z).add(0.5, 0.5, 0.5), new Rotation3f(Rotation3f.IDENTITY) + ); } else { BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.y); int rotationIndex = section.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z); RotationTuple rotationTuple = RotationTuple.get(rotationIndex); - Vector3d position = rotationTuple.rotate(this.spawnOffset); + Vector3d position = rotationTuple.rotatedVector(this.spawnOffset); Vector3d blockCenter = new Vector3d(); blockType.getBlockCenter(rotationIndex, blockCenter); - position.add(blockCenter).add(targetBlock); - Vector3f rotation = new Vector3f(0.0F, (float)(rotationTuple.yaw().getRadians() + Math.toRadians(this.spawnYawOffset)), 0.0F); + position.add(blockCenter).add(targetBlock.x, targetBlock.y, targetBlock.z); + Rotation3f rotation = new Rotation3f(0.0F, (float)(rotationTuple.yaw().getRadians() + Math.toRadians(this.spawnYawOffset)), 0.0F); return new SpawnNPCInteraction.SpawnData(position, rotation); } } } else { - return new SpawnNPCInteraction.SpawnData(this.spawnOffset.clone().add(targetBlock).add(0.5, 0.5, 0.5), Vector3f.ZERO); + return new SpawnNPCInteraction.SpawnData( + new Vector3d(this.spawnOffset).add(targetBlock.x, targetBlock.y, targetBlock.z).add(0.5, 0.5, 0.5), new Rotation3f(Rotation3f.IDENTITY) + ); } } @@ -172,7 +179,7 @@ public class SpawnNPCInteraction extends SimpleBlockInteraction { } } - private record SpawnData(@Nonnull Vector3d position, @Nonnull Vector3f rotation) { + private record SpawnData(@Nonnull Vector3d position, @Nonnull Rotation3f rotation) { } protected static class WeightedNPCSpawn implements IWeightedElement { diff --git a/src/com/hypixel/hytale/server/npc/interactions/UseNPCInteraction.java b/src/com/hypixel/hytale/server/npc/interactions/UseNPCInteraction.java index 6608ef0a..d4da3dab 100644 --- a/src/com/hypixel/hytale/server/npc/interactions/UseNPCInteraction.java +++ b/src/com/hypixel/hytale/server/npc/interactions/UseNPCInteraction.java @@ -8,10 +8,10 @@ import com.hypixel.hytale.protocol.InteractionState; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.entity.InteractionContext; -import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.blackboard.Blackboard; import com.hypixel.hytale.server.npc.blackboard.view.interaction.InteractionView; @@ -40,8 +40,8 @@ public class UseNPCInteraction extends SimpleInstantInteraction { protected final void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) { Ref ref = context.getEntity(); CommandBuffer commandBuffer = context.getCommandBuffer(); - Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType()); - if (playerComponent == null) { + PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType()); + if (playerRefComponent == null) { HytaleLogger.getLogger().at(Level.INFO).log("UseNPCInteraction requires a Player but was used for: %s", ref); context.getState().state = InteractionState.Failed; } else { @@ -58,10 +58,10 @@ public class UseNPCInteraction extends SimpleInstantInteraction { } else { InteractionView interactionView = commandBuffer.getResource(Blackboard.getResourceType()).getView(InteractionView.class, 0L); if (interactionView.getReservationStatus(targetRef, ref, commandBuffer) == ReservationStatus.RESERVED_OTHER) { - playerComponent.sendMessage(Message.translation("server.npc.npc.isBusy").param("roleName", npcComponent.getRoleName())); + playerRefComponent.sendMessage(Message.translation("server.npc.npc.isBusy").param("roleName", npcComponent.getRoleName())); context.getState().state = InteractionState.Failed; } else { - npcComponent.getRole().getStateSupport().addInteraction(playerComponent); + npcComponent.getRole().getStateSupport().addInteraction(playerRefComponent.getReference()); } } } diff --git a/src/com/hypixel/hytale/server/npc/metadata/CapturedNPCMetadata.java b/src/com/hypixel/hytale/server/npc/metadata/CapturedNPCMetadata.java index 30ccc2f2..967b9a0b 100644 --- a/src/com/hypixel/hytale/server/npc/metadata/CapturedNPCMetadata.java +++ b/src/com/hypixel/hytale/server/npc/metadata/CapturedNPCMetadata.java @@ -3,6 +3,8 @@ package com.hypixel.hytale.server.npc.metadata; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import com.hypixel.hytale.server.npc.storage.AlarmStore; +import javax.annotation.Nullable; public class CapturedNPCMetadata { public static final String KEY = "CapturedEntity"; @@ -25,11 +27,20 @@ public class CapturedNPCMetadata { (meta, parent) -> meta.fullItemIcon = parent.fullItemIcon ) .add() + .appendInherited( + new KeyedCodec<>("AlarmStore", AlarmStore.CODEC), + (meta, store) -> meta.alarmStore = store, + meta -> meta.alarmStore, + (meta, parent) -> meta.alarmStore = parent.alarmStore + ) + .add() .build(); public static final KeyedCodec KEYED_CODEC = new KeyedCodec<>("CapturedEntity", CODEC); private String iconPath; private String npcNameKey; private String fullItemIcon; + @Nullable + private AlarmStore alarmStore; public String getIconPath() { return this.iconPath; @@ -43,6 +54,11 @@ public class CapturedNPCMetadata { return this.fullItemIcon; } + @Nullable + public AlarmStore getAlarmStore() { + return this.alarmStore; + } + public void setIconPath(String iconPath) { this.iconPath = iconPath; } @@ -54,4 +70,8 @@ public class CapturedNPCMetadata { public void setFullItemIcon(String fullItemIcon) { this.fullItemIcon = fullItemIcon; } + + public void setAlarmStore(@Nullable AlarmStore alarmStore) { + this.alarmStore = alarmStore; + } } diff --git a/src/com/hypixel/hytale/server/npc/movement/GroupSteeringAccumulator.java b/src/com/hypixel/hytale/server/npc/movement/GroupSteeringAccumulator.java index 39172209..34cc3d00 100644 --- a/src/com/hypixel/hytale/server/npc/movement/GroupSteeringAccumulator.java +++ b/src/com/hypixel/hytale/server/npc/movement/GroupSteeringAccumulator.java @@ -3,14 +3,16 @@ package com.hypixel.hytale.server.npc.movement; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.random.RandomExtra; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.component.Velocity; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class GroupSteeringAccumulator { @Nonnull @@ -26,10 +28,11 @@ public class GroupSteeringAccumulator { private double xViewDirection; private double yViewDirection; private double zViewDirection; - private Vector3d componentSelector = Vector3d.ALL_ONES; + private final Vector3d componentSelector = new Vector3d(Vector3dUtil.ALL_ONES); private double maxRangeSquared = Double.MAX_VALUE; private double maxDistance = Double.MAX_VALUE; private float collisionViewHalfAngleCosine = 1.0F; + private boolean normalizeDistances; public void begin(double x, double y, double z, double xViewDirection, double yViewDirection, double zViewDirection) { this.x = x; @@ -38,9 +41,9 @@ public class GroupSteeringAccumulator { this.xViewDirection = xViewDirection; this.yViewDirection = yViewDirection; this.zViewDirection = zViewDirection; - this.sumOfDistances.assign(0.0); - this.sumOfPositions.assign(0.0); - this.sumOfVelocities.assign(0.0); + this.sumOfDistances.zero(); + this.sumOfPositions.zero(); + this.sumOfVelocities.zero(); this.count = 0; } @@ -49,7 +52,7 @@ public class GroupSteeringAccumulator { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); + Rotation3f headRotation = headRotationComponent.getRotation(); NPCPhysicsMath.getViewDirection(headRotation, this.temp); this.temp.normalize(); TransformComponent transformComponent = componentAccessor.getComponent(ref, TRANSFORM_COMPONENT_TYPE); @@ -57,7 +60,7 @@ public class GroupSteeringAccumulator { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - this.begin(position.getX(), position.getY(), position.getZ(), this.temp.x, this.temp.y, this.temp.z); + this.begin(position.x(), position.y(), position.z(), this.temp.x, this.temp.y, this.temp.z); } public void processEntity(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { @@ -71,9 +74,9 @@ public class GroupSteeringAccumulator { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - double xPosition = position.getX(); - double yPosition = position.getY(); - double zPosition = position.getZ(); + double xPosition = position.x(); + double yPosition = position.y(); + double zPosition = position.z(); double dx = xPosition - this.x; double dy = yPosition - this.y; double dz = zPosition - this.z; @@ -103,34 +106,66 @@ public class GroupSteeringAccumulator { assert velocityComponent != null; Vector3d velocity = velocityComponent.getVelocity(); - double dx = position.getX() - this.x; - double dy = position.getY() - this.y; - double dz = position.getZ() - this.z; + double dx = position.x() - this.x; + double dy = position.y() - this.y; + double dz = position.z() - this.z; double d = NPCPhysicsMath.dotProduct(dx, dy, dz, this.componentSelector); if (d < this.maxRangeSquared && NPCPhysicsMath.isInViewCone(this.xViewDirection, this.yViewDirection, this.zViewDirection, this.collisionViewHalfAngleCosine, dx, dy, dz)) { - d = 1.0 - Math.sqrt(d) / this.maxDistance; + double length; + if (!this.normalizeDistances) { + length = Math.sqrt(d); + } else { + if (d < 1.0E-12) { + do { + dx = RandomExtra.randomRange(-1.0, 1.0); + dy = RandomExtra.randomRange(-1.0, 1.0); + dz = RandomExtra.randomRange(-1.0, 1.0); + d = NPCPhysicsMath.dotProduct(dx, dy, dz); + } while (d < 1.0E-12); + + double scale = 1.0E-6 / Math.sqrt(d); + dx *= scale; + dy *= scale; + dz *= scale; + d = 1.0E-12; + } + + length = Math.sqrt(d); + double inverseLength = 1.0 / length; + dx *= inverseLength; + dy *= inverseLength; + dz *= inverseLength; + } + + d = 1.0 - length / this.maxDistance; double w = Math.pow(d, distanceWeight); this.sumOfDistances.add(dx * w, dy * w, dz * w); w = Math.pow(d, positionWeight); - this.sumOfPositions.addScaled(position, w); + this.sumOfPositions.fma(w, position); w = Math.pow(d, velocityWeight); - this.sumOfVelocities.addScaled(velocity, w); + this.sumOfVelocities.fma(w, velocity); this.count++; } } public void end() { if (this.count > 0) { - double scale = 1.0 / this.count; - this.sumOfDistances.scale(scale).scale(this.componentSelector); - this.sumOfPositions.scale(scale).scale(this.componentSelector); - this.sumOfVelocities.scale(scale).scale(this.componentSelector); + if (this.normalizeDistances) { + if (this.sumOfDistances.lengthSquared() >= 1.0) { + this.sumOfDistances.normalize(); + } + } else { + double scale = 1.0 / this.count; + this.sumOfDistances.mul(scale).mul(this.componentSelector); + this.sumOfPositions.mul(scale).mul(this.componentSelector); + this.sumOfVelocities.mul(scale).mul(this.componentSelector); + } } } public void setComponentSelector(Vector3d componentSelector) { - this.componentSelector = componentSelector; + this.componentSelector.set(componentSelector); } public void setMaxRange(double maxRange) { @@ -138,6 +173,10 @@ public class GroupSteeringAccumulator { this.maxDistance = maxRange; } + public void setNormalizeDistances(boolean normalizeDistances) { + this.normalizeDistances = normalizeDistances; + } + public void setViewConeHalfAngleCosine(float collisionViewHalfAngleCosine) { this.collisionViewHalfAngleCosine = collisionViewHalfAngleCosine; } diff --git a/src/com/hypixel/hytale/server/npc/movement/Steering.java b/src/com/hypixel/hytale/server/npc/movement/Steering.java index 578c1b0e..fade8b0a 100644 --- a/src/com/hypixel/hytale/server/npc/movement/Steering.java +++ b/src/com/hypixel/hytale/server/npc/movement/Steering.java @@ -1,15 +1,20 @@ package com.hypixel.hytale.server.npc.movement; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class Steering { + public static final double DIRECTION_VECTOR_MIN_LENGTH_SQUARED = 0.1; public static final Steering NULL = new Steering().clear(); private final Vector3d translation = new Vector3d(); private double maxDistance = Double.MAX_VALUE; private Vector3d maxDistanceComponentSelector; private boolean hasTranslation; + private final Rotation3f directionHint = new Rotation3f(); + private boolean hasDirectionHint; private float yaw; private boolean hasYaw; private float pitch; @@ -28,10 +33,12 @@ public class Steering { @Nonnull public Steering assign(@Nonnull Steering other) { - this.translation.assign(other.translation); + this.translation.set(other.translation); this.maxDistance = other.maxDistance; this.maxDistanceComponentSelector = other.maxDistanceComponentSelector; this.hasTranslation = other.hasTranslation; + this.directionHint.set(other.directionHint); + this.hasDirectionHint = other.hasDirectionHint; this.yaw = other.yaw; this.hasYaw = other.hasYaw; this.pitch = other.pitch; @@ -65,6 +72,10 @@ public class Steering { return false; } else if (!this.translation.equals(steering.translation)) { return false; + } else if (this.hasDirectionHint != steering.hasDirectionHint) { + return false; + } else if (!this.directionHint.equals(steering.directionHint)) { + return false; } else if (Double.compare(steering.relativeTurnSpeed, this.relativeTurnSpeed) != 0) { return false; } else { @@ -84,6 +95,8 @@ public class Steering { result = 31 * result + (int)(temp ^ temp >>> 32); result = 31 * result + this.maxDistanceComponentSelector.hashCode(); result = 31 * result + (this.hasTranslation ? 1 : 0); + result = 31 * result + this.directionHint.hashCode(); + result = 31 * result + (this.hasDirectionHint ? 1 : 0); result = 31 * result + (this.yaw != 0.0F ? Float.floatToIntBits(this.yaw) : 0); result = 31 * result + (this.hasYaw ? 1 : 0); result = 31 * result + (this.pitch != 0.0F ? Float.floatToIntBits(this.pitch) : 0); @@ -94,9 +107,11 @@ public class Steering { @Nonnull public Steering clearTranslation() { - this.translation.assign(Vector3d.ZERO); + this.translation.zero(); this.maxDistance = Double.MAX_VALUE; this.hasTranslation = false; + this.directionHint.set(Rotation3f.ZERO); + this.hasDirectionHint = false; return this; } @@ -152,33 +167,33 @@ public class Steering { @Nonnull public Steering setTranslation(@Nonnull Vector3d translation) { this.hasTranslation = true; - this.translation.assign(translation); + this.translation.set(translation); return this; } @Nonnull public Steering setTranslation(double x, double y, double z) { this.hasTranslation = true; - this.translation.assign(x, y, z); + this.translation.set(x, y, z); return this; } @Nonnull public Steering setTranslationRelativeSpeed(double relativeSpeed) { - this.translation.setLength(relativeSpeed); + this.translation.normalize(relativeSpeed); return this; } @Nonnull public Steering scaleTranslation(double speedFactor) { - this.translation.scale(speedFactor); + this.translation.mul(speedFactor); return this; } @Nonnull public Steering ensureMinTranslation(double relativeSpeed) { - if (this.translation.squaredLength() < relativeSpeed * relativeSpeed && this.translation.squaredLength() > 0.0) { - this.translation.setLength(relativeSpeed); + if (this.translation.lengthSquared() < relativeSpeed * relativeSpeed && this.translation.lengthSquared() > 0.0) { + this.translation.normalize(relativeSpeed); } return this; @@ -264,18 +279,78 @@ public class Steering { return this.hasTranslation; } + @Nonnull + public Rotation3f getDirectionHint() { + return this.directionHint; + } + + public boolean hasDirectionHint() { + return this.hasDirectionHint; + } + + @Nonnull + public Steering setDirectionHint(@Nonnull Vector3d direction, @Nonnull Rotation3f rotation, boolean forceOverwrite) { + if (!forceOverwrite && this.hasDirectionHint) { + return this; + } else if (direction.lengthSquared() < 0.1) { + this.directionHint.set(rotation); + this.hasDirectionHint = true; + return this; + } else { + this.directionHint.setYaw(PhysicsMath.headingFromDirection(direction.x, direction.z)); + this.directionHint.setPitch(PhysicsMath.pitchFromDirection(direction.x, direction.y, direction.z)); + this.directionHint.setRoll(rotation.roll()); + this.hasDirectionHint = true; + return this; + } + } + + @Nonnull + public Steering setDirectionHint(@Nonnull Rotation3f rotation) { + return this.setDirectionHint(rotation, false); + } + + @Nonnull + public Steering setDirectionHint(@Nonnull Rotation3f rotation, boolean forceOverwrite) { + return this.setDirectionHint(this.translation, rotation, forceOverwrite); + } + public boolean hasYaw() { return this.hasYaw; } + public boolean hasYawOrDirection() { + return this.hasYaw || this.hasDirectionHint; + } + + public float getYawOrDirection() { + return this.hasYaw ? this.yaw : this.directionHint.yaw(); + } + public boolean hasPitch() { return this.hasPitch; } + public boolean hasPitchOrDirection() { + return this.hasPitch || this.hasDirectionHint; + } + + public float getPitchOrDirection() { + return this.hasPitch ? this.pitch : this.directionHint.pitch(); + } + public boolean hasRoll() { return this.hasRoll; } + public boolean hasRollOrDirection() { + return this.hasRoll || this.hasDirectionHint; + } + + public float getRollOrDirection() { + return this.hasRoll ? this.roll : this.directionHint.roll(); + } + public double getSpeed() { return !this.hasTranslation() ? 0.0 : this.translation.length(); } diff --git a/src/com/hypixel/hytale/server/npc/movement/constraints/RelaxedConstraint.java b/src/com/hypixel/hytale/server/npc/movement/constraints/RelaxedConstraint.java new file mode 100644 index 00000000..f250e364 --- /dev/null +++ b/src/com/hypixel/hytale/server/npc/movement/constraints/RelaxedConstraint.java @@ -0,0 +1,25 @@ +package com.hypixel.hytale.server.npc.movement.constraints; + +import java.util.Set; +import java.util.function.Supplier; +import javax.annotation.Nonnull; + +public enum RelaxedConstraint implements Supplier { + WADE("Wade"), + DAMAGE("Damage"), + DROP("Drop"), + BREATHE("Breathe"), + FENCE("Fence"); + + private final String displayName; + @Nonnull + public static final Set DEFAULT_WHEN_RELAXED = Set.of(WADE); + + private RelaxedConstraint(String displayName) { + this.displayName = displayName; + } + + public String get() { + return this.displayName; + } +} diff --git a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionController.java b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionController.java index 650287f5..ae837353 100644 --- a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionController.java +++ b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionController.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.server.npc.movement.controllers; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.MovementStates; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; @@ -15,12 +15,15 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.movement.MovementState; import com.hypixel.hytale.server.npc.movement.NavState; import com.hypixel.hytale.server.npc.movement.Steering; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.role.support.DebugSupport; import java.util.EnumSet; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; public interface MotionController extends DebugSupport.DebugFlagsChangeListener { String getType(); @@ -72,7 +75,10 @@ public interface MotionController extends DebugSupport.DebugFlagsChangeListener boolean isValidPosition(Vector3d var1, ComponentAccessor var2); - boolean canAct(@Nonnull Ref var1, @Nonnull ComponentAccessor var2); + boolean canSteer(@Nonnull Ref var1, @Nonnull ComponentAccessor var2); + + @Nullable + String canSteerFailReason(@Nonnull Ref var1, @Nonnull ComponentAccessor var2); boolean isInProgress(); @@ -94,9 +100,9 @@ public interface MotionController extends DebugSupport.DebugFlagsChangeListener double getCurrentTurnRadius(); - double waypointDistance(Vector3d var1, Vector3d var2); + double waypointDistance(Vector3dc var1, Vector3dc var2); - double waypointDistanceSquared(Vector3d var1, Vector3d var2); + double waypointDistanceSquared(Vector3dc var1, Vector3dc var2); double waypointDistance(@Nonnull Ref var1, Vector3d var2, @Nonnull ComponentAccessor var3); @@ -116,37 +122,34 @@ public interface MotionController extends DebugSupport.DebugFlagsChangeListener boolean is2D(); - Vector3d getWorldNormal(); + Vector3dc getWorldNormal(); - Vector3d getWorldAntiNormal(); + Vector3dc getWorldAntiNormal(); void addForce(@Nonnull Vector3d var1, @Nullable VelocityConfig var2); Vector3d getForce(); - void forceVelocity(@Nonnull Vector3d var1, @Nullable VelocityConfig var2, boolean var3); + void forceVelocity(@Nonnull Vector3dc var1, @Nullable VelocityConfig var2, boolean var3); MotionController.VerticalRange getDesiredVerticalRange(@Nonnull Ref var1, @Nonnull ComponentAccessor var2); double getWanderVerticalMovementRatio(); - void setAvoidingBlockDamage(boolean var1); - boolean isAvoidingBlockDamage(); boolean willReceiveBlockDamage(); void requirePreciseMovement(Vector3d var1); - void requireDepthProbing(); - void enableHeadingBlending(double var1, Vector3d var3, double var4); void enableHeadingBlending(); - void setRelaxedMoveConstraints(boolean var1); + void setRelaxedMoveConstraints(@Nonnull EnumSet var1); - boolean isRelaxedMoveConstraints(); + @Nonnull + EnumSet getRelaxedConstraints(); NavState getNavState(); @@ -178,7 +181,7 @@ public interface MotionController extends DebugSupport.DebugFlagsChangeListener } default double getSquaredDistance(@Nonnull Vector3d p1, @Nonnull Vector3d p2, boolean useProjectedDistance) { - return useProjectedDistance ? this.waypointDistanceSquared(p1, p2) : p1.distanceSquaredTo(p2); + return useProjectedDistance ? this.waypointDistanceSquared(p1, p2) : p1.distanceSquared(p2); } void updatePhysicsValues(PhysicsValues var1); @@ -190,32 +193,36 @@ public interface MotionController extends DebugSupport.DebugFlagsChangeListener if (!.$assertionsDisabled && movementStatesComponent == null) { throw new AssertionError(); } else { - Velocity velocityComponent = componentAccessor.getComponent(ref, Velocity.getComponentType()); - if (!.$assertionsDisabled && velocityComponent == null) { - throw new AssertionError(); - } else { - MovementStates states = movementStatesComponent.getMovementStates(); + MovementStates states = movementStatesComponent.getMovementStates(); - return switch (state) { - case CLIMBING -> states.climbing; - case FALLING -> states.falling; - case CROUCHING -> states.crouching; - case FLYING -> states.flying; - case JUMPING -> states.jumping; - case SPRINTING -> states.sprinting; - case RUNNING -> states.running; - case IDLE -> velocityComponent.getVelocity().closeToZero(0.001); - case WALKING -> !velocityComponent.getVelocity().closeToZero(0.001) - && !states.falling - && !states.climbing - && !states.flying - && !states.running - && !states.sprinting - && !states.jumping - && !states.crouching; - case ANY -> true; - }; - } + return switch (state) { + case CLIMBING -> states.climbing; + case FALLING -> states.falling; + case CROUCHING -> states.crouching; + case FLYING -> states.flying; + case JUMPING -> states.jumping; + case SPRINTING -> states.sprinting; + case RUNNING -> states.running; + case IDLE, WALKING -> { + Velocity velocityComponent = componentAccessor.getComponent(ref, Velocity.getComponentType()); + if (!.$assertionsDisabled && velocityComponent == null) { + throw new AssertionError(); + } + + boolean isIdle = Vector3dUtil.closeToZero(velocityComponent.getVelocity(), 0.001); + yield state == MovementState.IDLE + ? isIdle + : !isIdle + && !states.falling + && !states.climbing + && !states.flying + && !states.running + && !states.sprinting + && !states.jumping + && !states.crouching; + } + case ANY -> true; + }; } } @@ -233,7 +240,7 @@ public interface MotionController extends DebugSupport.DebugFlagsChangeListener public double min; public double max; - public void assign(double current, double min, double max) { + public void set(double current, double min, double max) { this.current = current; this.min = min; this.max = max; diff --git a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerBase.java b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerBase.java index 2063de61..081720c4 100644 --- a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerBase.java +++ b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerBase.java @@ -6,8 +6,8 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.MovementSettings; import com.hypixel.hytale.protocol.MovementStates; @@ -44,10 +44,12 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.movement.MotionKind; import com.hypixel.hytale.server.npc.movement.NavState; import com.hypixel.hytale.server.npc.movement.Steering; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import com.hypixel.hytale.server.npc.movement.controllers.builders.BuilderMotionControllerBase; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; +import com.hypixel.hytale.server.npc.util.VisHelper; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.EnumSet; import java.util.List; @@ -56,12 +58,14 @@ import java.util.function.BiPredicate; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; public abstract class MotionControllerBase implements MotionController { public static final double FORCE_SCALE = 5.0; public static final double BISECT_DIST = 0.05; public static final double FILTER_COEFFICIENT = 0.7; - public static final double DOT_PRODUCT_EPSILON = 0.001; + public static final double DOT_PRODUCT_EPSILON = 0.1; public static final double DEFAULT_BLOCK_DRAG = 0.82; protected static final HytaleLogger LOGGER = NPCPlugin.get().getLogger(); public static final boolean DEBUG_APPLIED_FORCES = false; @@ -105,20 +109,19 @@ public abstract class MotionControllerBase implements MotionController { protected double throttleDuration; protected double targetDeltaSquared; protected boolean recomputePath; - protected final Vector3d worldNormal = Vector3d.UP; - protected final Vector3d worldAntiNormal = Vector3d.DOWN; + protected final Vector3dc worldNormal = Vector3dUtil.UP; + protected final Vector3dc worldAntiNormal = Vector3dUtil.DOWN; protected final Vector3d componentSelector = new Vector3d(1.0, 0.0, 1.0); protected final Vector3d planarComponentSelector = new Vector3d(1.0, 0.0, 1.0); protected boolean enableTriggers = true; protected boolean enableBlockDamage = true; protected boolean isReceivingBlockDamage; - protected boolean isAvoidingBlockDamage = true; protected boolean requiresPreciseMovement; - protected boolean requiresDepthProbing; protected boolean havePreciseMovementTarget; @Nonnull protected Vector3d preciseMovementTarget = new Vector3d(); - protected boolean isRelaxedMoveConstraints; + @Nonnull + protected EnumSet relaxedConstraints = EnumSet.noneOf(RelaxedConstraint.class); protected boolean isBlendingHeading; protected double blendHeading; protected boolean haveBlendHeadingPosition; @@ -200,11 +203,11 @@ public abstract class MotionControllerBase implements MotionController { assert transformComponent != null; - Vector3f bodyRotation = transformComponent.getRotation(); - this.position.assign(transformComponent.getPosition()); - this.yaw = bodyRotation.getY(); - this.pitch = bodyRotation.getPitch(); - this.roll = bodyRotation.getRoll(); + Rotation3f bodyRotation = transformComponent.getRotation(); + this.position.set(transformComponent.getPosition()); + this.yaw = bodyRotation.y(); + this.pitch = bodyRotation.pitch(); + this.roll = bodyRotation.roll(); this.adjustReadPosition(ref, componentAccessor); this.postReadPosition(ref, componentAccessor); } @@ -218,7 +221,7 @@ public abstract class MotionControllerBase implements MotionController { assert transformComponent != null; this.adjustWritePosition(ref, dt, componentAccessor); - Vector3f bodyRotation = transformComponent.getRotation(); + Rotation3f bodyRotation = transformComponent.getRotation(); bodyRotation.setYaw(this.yaw); bodyRotation.setPitch(this.pitch); bodyRotation.setRoll(this.roll); @@ -240,16 +243,16 @@ public abstract class MotionControllerBase implements MotionController { public boolean touchesWater(boolean defaultValue, @Nonnull ComponentAccessor componentAccessor) { World world = componentAccessor.getExternalData().getWorld(); ChunkStore chunkStore = world.getChunkStore(); - long chunkIndex = ChunkUtil.indexChunkFromBlock(this.position.getX(), this.position.getZ()); + long chunkIndex = ChunkUtil.indexChunkFromBlock(this.position.x(), this.position.z()); Ref chunkRef = chunkStore.getChunkReference(chunkIndex); if (chunkRef != null && chunkRef.isValid()) { WorldChunk worldChunkComponent = chunkStore.getStore().getComponent(chunkRef, WorldChunk.getComponentType()); assert worldChunkComponent != null; - int blockX = MathUtil.floor(this.position.getX()); - int blockY = MathUtil.floor(this.position.getY() + this.collisionBoundingBox.min.y); - int blockZ = MathUtil.floor(this.position.getZ()); + int blockX = MathUtil.floor(this.position.x()); + int blockY = MathUtil.floor(this.position.y() + this.collisionBoundingBox.min.y); + int blockZ = MathUtil.floor(this.position.z()); int fluidId = worldChunkComponent.getFluidId(blockX, blockY, blockZ); return fluidId != 0; } else { @@ -270,11 +273,11 @@ public abstract class MotionControllerBase implements MotionController { movementStates.swimJumping = false; movementStates.inFluid = this.touchesWater(movementStates.inFluid, componentAccessor); movementStates.onGround = this.role.isOnGround(); - double speed = this.waypointDistance(Vector3d.ZERO, velocity); + double speed = this.waypointDistance(Vector3dUtil.ZERO, velocity); speed = 0.7 * this.previousSpeed + 0.30000000000000004 * speed; this.previousSpeed = speed; this.fastMotionKind = this.isFastMotionKind(speed); - this.idleMotionKind = steering.getTranslation().equals(Vector3d.ZERO); + this.idleMotionKind = steering.getTranslation().equals(Vector3dUtil.ZERO); this.horizontalIdleKind = this.isHorizontalIdle(speed); if (this.motionKind != this.lastMovementStateUpdatedMotionKind || lastFastMotion != this.fastMotionKind @@ -416,13 +419,14 @@ public abstract class MotionControllerBase implements MotionController { double v = t > 0.0 ? l / t : 0.0; LOGGER.at(Level.INFO) .log( - "== Steer %s = t =%.4f dt=%.4f h =%.4f l =%.4f v =%.4f motion=%s", + "== Steer %s = t =%.4f dt=%.4f h =%.4f l =%.4f v =%.4f obstr=%s motion=%s", this.getType(), interval, t, (180.0F / (float)Math.PI) * this.yaw, l, v, + this.isObstructed ? "yes" : "no", role.getSteeringMotionName() ); return st; @@ -445,105 +449,124 @@ public abstract class MotionControllerBase implements MotionController { assert npcComponent != null; this.effectHorizontalSpeedMultiplier = npcComponent.getCurrentHorizontalSpeedMultiplier(ref, componentAccessor); - this.setAvoidingBlockDamage(this.isAvoidingBlockDamage && !this.isReceivingBlockDamage); - this.translation.assign(0.0); + this.applyRuntimeRelaxedConstraints(!role.couldBreatheCached()); + this.translation.zero(); this.cachedMovementBlocked = this.isMovementBlocked(ref, componentAccessor); this.computeMove(ref, role, bodySteering, interval, this.translation, componentAccessor); - if (this.debugModeValidateMath && !NPCPhysicsMath.isValid(this.translation)) { - throw new IllegalArgumentException(String.valueOf(this.translation)); - } else { - if (this.translation.squaredLength() > 1000000.0) { - if (this.debugModeValidateMath) { - LOGGER.at(Level.WARNING) - .log("NPC with role %s has abnormal high speed! (Distance=%s, MotionController=%s)", role.getRoleName(), this.translation.length(), this.type); - } - - this.translation.assign(Vector3d.ZERO); - } - - this.executeMove(ref, role, interval, this.translation, componentAccessor); - this.postExecuteMove(); - this.clearRequirePreciseMovement(); - this.clearRequireDepthProbing(); - this.clearBlendHeading(); - this.setAvoidingBlockDamage(!this.isReceivingBlockDamage); - this.setRelaxedMoveConstraints(false); - float maxBodyRotation = (float)(interval * this.getCurrentMaxBodyRotationSpeed() * bodySteering.getRelativeTurnSpeed()); - float maxHeadRotation = (float)(interval * this.maxHeadRotationSpeed * headSteering.getRelativeTurnSpeed() * this.effectHorizontalSpeedMultiplier); - this.calculateYaw(ref, bodySteering, headSteering, maxHeadRotation, maxBodyRotation, componentAccessor); - this.calculatePitch(ref, bodySteering, headSteering, maxHeadRotation, componentAccessor); - this.calculateRoll(bodySteering, headSteering); - this.moveEntity(ref, interval, componentAccessor); - HeadRotation headRotationComponent = componentAccessor.getComponent(ref, HeadRotation.getComponentType()); - - assert headRotationComponent != null; - - Vector3f headRotation = headRotationComponent.getRotation(); - headRotation.setYaw(headSteering.getYaw()); - headRotation.setPitch(headSteering.getPitch()); - headRotation.setRoll(headSteering.getRoll()); - if (!this.forceVelocity.equals(Vector3d.ZERO) && !this.ignoreDamping) { - double movementThresholdSquared = 1.0000000000000002E-10; - if (this.forceVelocity.squaredLength() >= movementThresholdSquared) { - this.dampForceVelocity(this.forceVelocity, this.forceVelocityDamping, interval, componentAccessor); - } else { - this.forceVelocity.assign(Vector3d.ZERO); - } - } - - double clientTps = 60.0; - int serverTps = world.getTps(); - double rate = clientTps / serverTps; - boolean dampenY = this.shouldDampenAppliedVelocitiesY(); - boolean useGroundResistance = this.shouldAlwaysUseGroundResistance() || this.onGround(); - - for (int i = 0; i < this.appliedVelocities.size(); i++) { - MotionControllerBase.AppliedVelocity entry = this.appliedVelocities.get(i); - float min; - float max; - if (useGroundResistance) { - min = entry.config.getGroundResistance(); - max = entry.config.getGroundResistanceMax(); - } else { - min = entry.config.getAirResistance(); - max = entry.config.getAirResistanceMax(); - } - - float resistance = min; - if (max >= 0.0F) { - resistance = switch (entry.config.getStyle()) { - case Linear -> { - float len = (float)entry.velocity.length(); - if (len < entry.config.getThreshold()) { - float mul = len / entry.config.getThreshold(); - yield min * mul + max * (1.0F - mul); - } else { - yield min; - } - } - case Exp -> { - float len = (float)entry.velocity.squaredLength(); - if (len < entry.config.getThreshold() * entry.config.getThreshold()) { - float mul = len / (entry.config.getThreshold() * entry.config.getThreshold()); - yield min * mul + max * (1.0F - mul); - } else { - yield min; - } - } - }; - } - - double resistanceScale = Math.pow(resistance, rate); - entry.velocity.x *= resistanceScale; - entry.velocity.z *= resistanceScale; - if (dampenY) { - entry.velocity.y *= resistanceScale; - } - } - - this.appliedVelocities.removeIf(v -> v.velocity.squaredLength() < 0.001); - return interval; + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "== Move %s = t =%.4f pos=%s motion=%s obstr=%s", + this.type, + Vector3dUtil.formatShortString(this.position), + role.getSteeringMotionName(), + this.isObstructed ? "yes" : "no" + ); } + + if (this.debugModeValidateMath) { + if (!NPCPhysicsMath.isValid(this.translation)) { + throw new IllegalArgumentException(String.valueOf(this.translation)); + } + + if (this.translation.lengthSquared() > 1000000.0) { + throw new IllegalStateException( + String.format( + "NPC with role %s has abnormal high speed! (Distance=%s, MotionController=%s)", role.getRoleName(), this.translation.length(), this.type + ) + ); + } + } else if (this.translation.lengthSquared() > 1000000.0) { + this.translation.zero(); + } + + this.applyRuntimeRelaxedConstraints(!role.couldBreatheCached()); + this.executeMove(ref, role, interval, this.translation, componentAccessor); + if (role.getDebugSupport().isDebugFlagSet(RoleDebugFlags.VisTranslation)) { + VisHelper.renderDebugVectorTo(this.position, this.translation, VisHelper.DEBUG_COLOR_STEERING_PRE, world); + } + + this.postExecuteMove(); + this.clearRequirePreciseMovement(); + this.clearBlendHeading(); + boolean cannotBreathe = !role.couldBreatheCached(); + this.relaxedConstraints.clear(); + this.applyRuntimeRelaxedConstraints(cannotBreathe); + float maxBodyRotation = (float)(interval * this.getCurrentMaxBodyRotationSpeed() * bodySteering.getRelativeTurnSpeed()); + float maxHeadRotation = (float)(interval * this.maxHeadRotationSpeed * headSteering.getRelativeTurnSpeed() * this.effectHorizontalSpeedMultiplier); + this.calculateYaw(ref, bodySteering, headSteering, maxHeadRotation, maxBodyRotation, componentAccessor); + this.calculatePitch(ref, bodySteering, headSteering, maxHeadRotation, componentAccessor); + this.calculateRoll(bodySteering, headSteering); + this.moveEntity(ref, interval, componentAccessor); + HeadRotation headRotationComponent = componentAccessor.getComponent(ref, HeadRotation.getComponentType()); + + assert headRotationComponent != null; + + Rotation3f headRotation = headRotationComponent.getRotation(); + headRotation.setYaw(headSteering.getYaw()); + headRotation.setPitch(headSteering.getPitch()); + headRotation.setRoll(headSteering.getRoll()); + if (!this.forceVelocity.equals(Vector3dUtil.ZERO) && !this.ignoreDamping) { + double movementThresholdSquared = 1.0000000000000002E-10; + if (this.forceVelocity.lengthSquared() >= movementThresholdSquared) { + this.dampForceVelocity(this.forceVelocity, this.forceVelocityDamping, interval, componentAccessor); + } else { + this.forceVelocity.zero(); + } + } + + double clientTps = 60.0; + int serverTps = world.getTps(); + double rate = clientTps / serverTps; + boolean dampenY = this.shouldDampenAppliedVelocitiesY(); + boolean useGroundResistance = this.shouldAlwaysUseGroundResistance() || this.onGround(); + + for (int i = 0; i < this.appliedVelocities.size(); i++) { + MotionControllerBase.AppliedVelocity entry = this.appliedVelocities.get(i); + float min; + float max; + if (useGroundResistance) { + min = entry.config.getGroundResistance(); + max = entry.config.getGroundResistanceMax(); + } else { + min = entry.config.getAirResistance(); + max = entry.config.getAirResistanceMax(); + } + + float resistance = min; + if (max >= 0.0F) { + resistance = switch (entry.config.getStyle()) { + case Linear -> { + float len = (float)entry.velocity.length(); + if (len < entry.config.getThreshold()) { + float mul = len / entry.config.getThreshold(); + yield min * mul + max * (1.0F - mul); + } else { + yield min; + } + } + case Exp -> { + float len = (float)entry.velocity.lengthSquared(); + if (len < entry.config.getThreshold() * entry.config.getThreshold()) { + float mul = len / (entry.config.getThreshold() * entry.config.getThreshold()); + yield min * mul + max * (1.0F - mul); + } else { + yield min; + } + } + }; + } + + double resistanceScale = Math.pow(resistance, rate); + entry.velocity.x *= resistanceScale; + entry.velocity.z *= resistanceScale; + if (dampenY) { + entry.velocity.y *= resistanceScale; + } + } + + this.appliedVelocities.removeIf(v -> v.velocity.lengthSquared() < 0.001); + return interval; } protected boolean shouldDampenAppliedVelocitiesY() { @@ -562,9 +585,9 @@ public abstract class MotionControllerBase implements MotionController { float maxBodyRotation, @Nonnull ComponentAccessor componentAccessor ) { - if (bodySteering.hasYaw()) { - this.yaw = bodySteering.getYaw(); - } else if (NPCPhysicsMath.dotProduct(this.translation.x, 0.0, this.translation.z) > 0.001) { + if (bodySteering.hasYawOrDirection()) { + this.yaw = bodySteering.getYawOrDirection(); + } else if (NPCPhysicsMath.dotProduct(this.translation.x, 0.0, this.translation.z) > 0.1) { this.yaw = PhysicsMath.headingFromDirection(this.translation.x, this.translation.z); } @@ -581,8 +604,8 @@ public abstract class MotionControllerBase implements MotionController { assert modelComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); - float currentYaw = headRotation.getYaw(); + Rotation3f headRotation = headRotationComponent.getRotation(); + float currentYaw = headRotation.yaw(); float targetYaw = headSteering.getYaw(); float turnAngle = MathUtil.clamp(NPCPhysicsMath.turnAngle(currentYaw, targetYaw), -maxHeadRotation, maxHeadRotation); headSteering.setYaw(PhysicsMath.normalizeTurnAngle(currentYaw + turnAngle)); @@ -625,8 +648,8 @@ public abstract class MotionControllerBase implements MotionController { assert transformComponent != null; - Vector3f bodyRotation = transformComponent.getRotation(); - float currentBodyYaw = bodyRotation.getYaw(); + Rotation3f bodyRotation = transformComponent.getRotation(); + float currentBodyYaw = bodyRotation.yaw(); float targetBodyYaw = MathUtil.wrapAngle(this.yaw + yawOffset); float bodyTurnAngle = MathUtil.clamp(NPCPhysicsMath.turnAngle(currentBodyYaw, targetBodyYaw), -maxBodyRotation, maxBodyRotation); return MathUtil.wrapAngle(this.yaw + bodyTurnAngle); @@ -639,9 +662,9 @@ public abstract class MotionControllerBase implements MotionController { float maxHeadRotation, @Nonnull ComponentAccessor componentAccessor ) { - if (bodySteering.hasPitch()) { - this.pitch = bodySteering.getPitch(); - } else if (NPCPhysicsMath.dotProduct(this.translation.x, this.translation.y, this.translation.z) > 0.001) { + if (bodySteering.hasPitchOrDirection()) { + this.pitch = bodySteering.getPitchOrDirection(); + } else if (NPCPhysicsMath.dotProduct(this.translation.x, this.translation.y, this.translation.z) > 0.1) { this.pitch = PhysicsMath.pitchFromDirection(this.translation.x, this.translation.y, this.translation.z); } @@ -654,8 +677,8 @@ public abstract class MotionControllerBase implements MotionController { assert headRotationComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); - float currentPitch = headRotation.getPitch(); + Rotation3f headRotation = headRotationComponent.getRotation(); + float currentPitch = headRotation.pitch(); float targetPitch = headSteering.getPitch(); float turnAngle = MathUtil.clamp(NPCPhysicsMath.turnAngle(currentPitch, targetPitch), -maxHeadRotation, maxHeadRotation); headSteering.setPitch(PhysicsMath.normalizeTurnAngle(currentPitch + turnAngle)); @@ -827,12 +850,23 @@ public abstract class MotionControllerBase implements MotionController { return speed == 0.0; } + protected boolean isForcePushed() { + return !this.forceVelocity.equals(Vector3dUtil.ZERO) || !this.appliedVelocities.isEmpty(); + } + @Override - public boolean canAct(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - return this.isAlive(ref, componentAccessor) - && this.role.couldBreatheCached() - && this.forceVelocity.equals(Vector3d.ZERO) - && this.appliedVelocities.isEmpty(); + public boolean canSteer(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { + return this.isAlive(ref, componentAccessor) && !this.isForcePushed(); + } + + @Nullable + @Override + public String canSteerFailReason(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { + if (!this.isAlive(ref, componentAccessor)) { + return "DEAD"; + } else { + return this.isForcePushed() ? "EXT_FORCE" : null; + } } public boolean isMovementBlocked(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { @@ -876,29 +910,51 @@ public abstract class MotionControllerBase implements MotionController { ) { double validDistance = 0.0; double invalidDistance = 1.0; - this.bisectValidPosition.assign(validPosition); - this.bisectInvalidPosition.assign(invalidPosition); + this.bisectValidPosition.set(validPosition); + this.bisectInvalidPosition.set(invalidPosition); maxDistance *= maxDistance; double validWeight = 0.1; double invalidWeight = 0.9; - while (this.bisectValidPosition.distanceSquaredTo(this.bisectInvalidPosition) > maxDistance) { + while (this.bisectValidPosition.distanceSquared(this.bisectInvalidPosition) > maxDistance) { double distance = validWeight * validDistance + invalidWeight * invalidDistance; result.x = validWeight * this.bisectValidPosition.x + invalidWeight * this.bisectInvalidPosition.x; result.y = validWeight * this.bisectValidPosition.y + invalidWeight * this.bisectInvalidPosition.y; result.z = validWeight * this.bisectValidPosition.z + invalidWeight * this.bisectInvalidPosition.z; if (validate.test(t, result)) { validDistance = distance; - this.bisectValidPosition.assign(result); + this.bisectValidPosition.set(result); + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + " Bisect valid: d=[%f %f] w=[%f %f] pos=%s", + distance, + invalidDistance, + validWeight, + invalidWeight, + Vector3dUtil.formatShortString(result) + ); + } } else { invalidDistance = distance; - this.bisectInvalidPosition.assign(result); + this.bisectInvalidPosition.set(result); validWeight = 0.5; invalidWeight = 0.5; + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + " Bisect invalid: d=[%f %f] w=[%f %f] pos=%s", + validDistance, + distance, + validWeight, + invalidWeight, + Vector3dUtil.formatShortString(result) + ); + } } } - result.assign(this.bisectValidPosition); + result.set(this.bisectValidPosition); return validDistance; } @@ -916,24 +972,24 @@ public abstract class MotionControllerBase implements MotionController { } else { double horzMul = 0.18000000000000005 * this.movementSettings.velocityResistance; this.forceVelocity.add(force.x * scale * horzMul, force.y * scale, force.z * scale * horzMul); - this.appliedForce.assign(this.forceVelocity); + this.appliedForce.set(this.forceVelocity); this.ignoreDamping = false; } } @Override - public void forceVelocity(@Nonnull Vector3d velocity, @Nullable VelocityConfig velocityConfig, boolean ignoreDamping) { + public void forceVelocity(@Nonnull Vector3dc velocity, @Nullable VelocityConfig velocityConfig, boolean ignoreDamping) { if (!SplitVelocity.SHOULD_MODIFY_VELOCITY && velocityConfig != null) { this.appliedVelocities.clear(); - this.appliedVelocities.add(new MotionControllerBase.AppliedVelocity(velocity.clone(), velocityConfig)); + this.appliedVelocities.add(new MotionControllerBase.AppliedVelocity(new Vector3d(velocity), velocityConfig)); } else { - this.forceVelocity.assign(velocity); + this.forceVelocity.set(velocity); this.ignoreDamping = ignoreDamping; } } public void clearForce() { - this.forceVelocity.assign(Vector3d.ZERO); + this.forceVelocity.zero(); } protected void dumpCollisionResults() { @@ -945,7 +1001,7 @@ public abstract class MotionControllerBase implements MotionController { LOGGER.at(Level.INFO) .log( "CollRes: pos=%s yaw=%f count=%d %s", - Vector3d.formatShortString(this.position), + Vector3dUtil.formatShortString(this.position), (180.0F / (float)Math.PI) * this.yaw, this.collisionResult.getBlockCollisionCount(), slideString @@ -971,8 +1027,8 @@ public abstract class MotionControllerBase implements MotionController { cd.y, cd.z, cd.collisionStart, - Vector3d.formatShortString(cd.collisionNormal), - Vector3d.formatShortString(cd.collisionPoint), + Vector3dUtil.formatShortString(cd.collisionNormal), + Vector3dUtil.formatShortString(cd.collisionPoint), materialName, typeName, hitboxName, @@ -996,13 +1052,19 @@ public abstract class MotionControllerBase implements MotionController { } @Override - public void setAvoidingBlockDamage(boolean avoid) { - this.isAvoidingBlockDamage = avoid; + public boolean isAvoidingBlockDamage() { + return !this.relaxedConstraints.contains(RelaxedConstraint.DAMAGE); } - @Override - public boolean isAvoidingBlockDamage() { - return this.isAvoidingBlockDamage; + protected void applyRuntimeRelaxedConstraints(boolean cannotBreathe) { + if (this.isReceivingBlockDamage) { + this.relaxedConstraints.add(RelaxedConstraint.DAMAGE); + } + + if (cannotBreathe) { + this.relaxedConstraints.add(RelaxedConstraint.BREATHE); + this.relaxedConstraints.add(RelaxedConstraint.WADE); + } } public void processTriggers( @@ -1015,8 +1077,8 @@ public abstract class MotionControllerBase implements MotionController { int count = collisionResult.getTriggerBlocks().size(); if (count != 0) { if (this.enableTriggers) { - this.beforeTriggerForce.assign(this.getForce()); - this.beforeTriggerPosition.assign(this.position); + this.beforeTriggerForce.set(this.getForce()); + this.beforeTriggerPosition.set(this.position); } this.moveEntity(ref, 0.0, componentAccessor); @@ -1104,29 +1166,29 @@ public abstract class MotionControllerBase implements MotionController { @Override public void setComponentSelector(@Nonnull Vector3d componentSelector) { - this.componentSelector.assign(componentSelector); + this.componentSelector.set(componentSelector); } @Override - public Vector3d getWorldNormal() { + public Vector3dc getWorldNormal() { return this.worldNormal; } @Override - public Vector3d getWorldAntiNormal() { + public Vector3dc getWorldAntiNormal() { return this.worldAntiNormal; } @Override - public double waypointDistance(@Nonnull Vector3d p, @Nonnull Vector3d q) { + public double waypointDistance(@Nonnull Vector3dc p, @Nonnull Vector3dc q) { return Math.sqrt(this.waypointDistanceSquared(p, q)); } @Override - public double waypointDistanceSquared(@Nonnull Vector3d p, @Nonnull Vector3d q) { - double dx = (p.x - q.x) * this.getComponentSelector().x; - double dy = (p.y - q.y) * this.getComponentSelector().y; - double dz = (p.z - q.z) * this.getComponentSelector().z; + public double waypointDistanceSquared(@Nonnull Vector3dc p, @Nonnull Vector3dc q) { + double dx = (p.x() - q.x()) * this.getComponentSelector().x; + double dy = (p.y() - q.y()) * this.getComponentSelector().y; + double dz = (p.z() - q.z()) * this.getComponentSelector().z; return dx * dx + dy * dy + dz * dz; } @@ -1142,9 +1204,9 @@ public abstract class MotionControllerBase implements MotionController { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - double dx = (p.x - position.getX()) * this.getComponentSelector().x; - double dy = (p.y - position.getY()) * this.getComponentSelector().y; - double dz = (p.z - position.getZ()) * this.getComponentSelector().z; + double dx = (p.x - position.x()) * this.getComponentSelector().x; + double dy = (p.y - position.y()) * this.getComponentSelector().y; + double dz = (p.z - position.z()) * this.getComponentSelector().z; return dx * dx + dy * dy + dz * dz; } @@ -1221,7 +1283,7 @@ public abstract class MotionControllerBase implements MotionController { this.requiresPreciseMovement = true; this.havePreciseMovementTarget = positionHint != null; if (this.havePreciseMovementTarget) { - this.preciseMovementTarget.assign(positionHint); + this.preciseMovementTarget.set(positionHint); } } @@ -1234,26 +1296,13 @@ public abstract class MotionControllerBase implements MotionController { return this.requiresPreciseMovement; } - @Override - public void requireDepthProbing() { - this.requiresDepthProbing = true; - } - - public void clearRequireDepthProbing() { - this.requiresDepthProbing = false; - } - - public boolean isRequiresDepthProbing() { - return this.requiresDepthProbing; - } - @Override public void enableHeadingBlending(double heading, @Nullable Vector3d targetPosition, double blendLevel) { this.isBlendingHeading = true; this.blendHeading = heading; this.haveBlendHeadingPosition = targetPosition != null; if (this.haveBlendHeadingPosition) { - this.blendHeadingPosition.assign(targetPosition); + this.blendHeadingPosition.set(targetPosition); } this.blendLevelAtTargetPosition = blendLevel; @@ -1270,13 +1319,15 @@ public abstract class MotionControllerBase implements MotionController { } @Override - public void setRelaxedMoveConstraints(boolean relax) { - this.isRelaxedMoveConstraints = relax; + public void setRelaxedMoveConstraints(@Nonnull EnumSet constraints) { + this.relaxedConstraints.clear(); + this.relaxedConstraints.addAll(constraints); } + @Nonnull @Override - public boolean isRelaxedMoveConstraints() { - return this.isRelaxedMoveConstraints; + public EnumSet getRelaxedConstraints() { + return this.relaxedConstraints; } @Override diff --git a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerDive.java b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerDive.java index e1d71e5a..da83f73d 100644 --- a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerDive.java +++ b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerDive.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.modules.collision.BlockCollisionData; @@ -24,6 +24,7 @@ import java.util.EnumSet; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class MotionControllerDive extends MotionControllerBase { public static final String TYPE = "Dive"; @@ -62,7 +63,7 @@ public class MotionControllerDive extends MotionControllerBase { public MotionControllerDive(@Nonnull BuilderSupport builderSupport, @Nonnull BuilderMotionControllerDive builder) { super(builderSupport, builder); this.setGravity(builder.getGravity()); - this.componentSelector.assign(1.0, 1.0, 1.0); + this.componentSelector.set(1.0, 1.0, 1.0); this.maxVerticalSpeed = builder.getMaxVerticalSpeed(); this.acceleration = builder.getAcceleration(); this.maxSinkSpeed = builder.getMaxSinkSpeed(); @@ -120,7 +121,7 @@ public class MotionControllerDive extends MotionControllerBase { maxY = y; } - this.verticalRange.assign(y, minY, maxY); + this.verticalRange.set(y, minY, maxY); return this.verticalRange; } @@ -143,12 +144,12 @@ public class MotionControllerDive extends MotionControllerBase { if (this.collisionWithSolid) { this.moveSpeed = 0.0; this.climbSpeed = 0.0; - this.forceVelocity.assign(Vector3d.ZERO); + this.forceVelocity.zero(); this.appliedVelocities.clear(); } - if (this.canAct(ref, componentAccessor)) { - this.tempDirection.assign(dir.x, 0.0, dir.z); + if (this.canSteer(ref, componentAccessor)) { + this.tempDirection.set(dir.x, 0.0, dir.z); double maxVerticalSpeed = this.maxVerticalSpeed * this.effectHorizontalSpeedMultiplier; double hSpeed = this.tempDirection.length() * this.getMaximumSpeed(); double vSpeed = dir.y * maxVerticalSpeed; @@ -164,16 +165,16 @@ public class MotionControllerDive extends MotionControllerBase { newHeading = PhysicsMath.normalizeTurnAngle(PhysicsMath.headingFromDirection(dir.x, dir.z)); newPitch = PhysicsMath.normalizeTurnAngle(PhysicsMath.pitchFromDirection(dir.x, dir.y, dir.z)); } else { - translation.assign(Vector3d.ZERO); - if (steering.hasYaw()) { - newHeading = steering.getYaw(); + translation.zero(); + if (steering.hasYawOrDirection()) { + newHeading = steering.getYawOrDirection(); } else { newHeading = heading; } steering.clearYaw(); - if (steering.hasPitch()) { - newPitch = steering.getPitch(); + if (steering.hasPitchOrDirection()) { + newPitch = steering.getPitchOrDirection(); } else { newPitch = pitch; } @@ -235,21 +236,21 @@ public class MotionControllerDive extends MotionControllerBase { if (!this.isAlive(ref, componentAccessor)) { steering.setYaw(this.getYaw()); steering.setPitch(onGround ? 0.0F : this.getPitch()); - this.forceVelocity.assign(Vector3d.ZERO); + this.forceVelocity.zero(); this.appliedVelocities.clear(); this.moveSpeed = 0.0; this.climbSpeed = 0.0; if (onGround) { - translation.assign(Vector3d.ZERO); + translation.zero(); } else { Velocity velocityComponent = componentAccessor.getComponent(ref, Velocity.getComponentType()); double sinkSpeed = velocityComponent.getVelocity().y; sinkSpeed = NPCPhysicsMath.gravityDrag(sinkSpeed, 5.0 * this.gravity, dt, maxSpeed); - translation.assign(0.0, sinkSpeed, 0.0).scale(dt); + translation.set(0.0, sinkSpeed, 0.0).mul(dt); } return dt; - } else if (this.forceVelocity.equals(Vector3d.ZERO) && this.appliedVelocities.isEmpty()) { + } else if (this.forceVelocity.equals(Vector3dUtil.ZERO) && this.appliedVelocities.isEmpty()) { if (!steering.hasYaw()) { steering.setYaw(heading); } @@ -265,7 +266,7 @@ public class MotionControllerDive extends MotionControllerBase { this.moveSpeed = 0.0; this.climbSpeed = 0.0; if (!this.isObstructed()) { - translation.assign(this.forceVelocity); + translation.set(this.forceVelocity); for (int i = 0; i < this.appliedVelocities.size(); i++) { MotionControllerBase.AppliedVelocity entry = this.appliedVelocities.get(i); @@ -280,13 +281,13 @@ public class MotionControllerDive extends MotionControllerBase { translation.add(entry.velocity); } } else { - translation.assign(Vector3d.ZERO); + translation.zero(); this.appliedVelocities.clear(); - this.forceVelocity.assign(Vector3d.ZERO); + this.forceVelocity.zero(); } translation.y = NPCPhysicsMath.gravityDrag(this.forceVelocity.y, 5.0 * this.gravity, dt, maxSpeed); - translation.scale(dt); + translation.mul(dt); steering.setYaw(this.getYaw()); steering.setPitch(this.getPitch()); return dt; @@ -308,7 +309,7 @@ public class MotionControllerDive extends MotionControllerBase { translation.x = moveSpeed * dt * PhysicsMath.headingX(heading); translation.z = moveSpeed * dt * PhysicsMath.headingZ(heading); translation.y = climbSpeed * dt; - translation.clipToZero(this.getEpsilonSpeed()); + Vector3dUtil.clipToZero(translation, this.getEpsilonSpeed()); } private boolean isNearZero(float angle) { @@ -332,8 +333,8 @@ public class MotionControllerDive extends MotionControllerBase { if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { throw new IllegalStateException("Invalid position"); } else { - boolean canAct = this.canAct(ref, componentAccessor); - this.collisionResult.setCollisionByMaterial(canAct ? 5 : 4); + boolean canSteer = this.canSteer(ref, componentAccessor); + this.collisionResult.setCollisionByMaterial(canSteer ? 5 : 4); if (this.debugModeBlockCollisions) { this.collisionResult.setLogger(LOGGER); } @@ -348,7 +349,7 @@ public class MotionControllerDive extends MotionControllerBase { this.dumpCollisionResults(); } - this.lastValidPosition.assign(this.position); + this.lastValidPosition.set(this.position); this.isObstructed = false; this.collisionWithSolid = false; BlockCollisionData collision = this.collisionResult.getFirstBlockCollision(); @@ -356,15 +357,15 @@ public class MotionControllerDive extends MotionControllerBase { double time = dt; this.position.add(translation); if (!this.moveProbe.probePosition(ref, this.collisionBoundingBox, this.position, this.collisionResult, this.swimDepth, componentAccessor) - || canAct && !this.moveProbe.isInWater()) { + || canSteer && !this.moveProbe.isInWater()) { time = this.bisect(ref, this.lastValidPosition, 0.0, this.position, dt, this.position, componentAccessor); this.isObstructed = true; if (this.debugModeMove) { LOGGER.at(Level.INFO) - .log("Move - Dive: No Collision, Bisect pos=%s, blocked=%s", Vector3d.formatShortString(this.position), this.isObstructed); + .log("Move - Dive: No Collision, Bisect pos=%s, blocked=%s", Vector3dUtil.formatShortString(this.position), this.isObstructed); } } else if (this.debugModeMove) { - LOGGER.at(Level.INFO).log("Move - Dive: No collision, pos=%s, blocked=%s", Vector3d.formatShortString(this.position), this.isObstructed); + LOGGER.at(Level.INFO).log("Move - Dive: No collision, pos=%s, blocked=%s", Vector3dUtil.formatShortString(this.position), this.isObstructed); } if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { @@ -377,18 +378,18 @@ public class MotionControllerDive extends MotionControllerBase { throw new IllegalStateException("Invalid position"); } else { double collisionStart = collision.collisionStart; - this.position.assign(collision.collisionPoint); + this.position.set(collision.collisionPoint); this.isObstructed = true; this.collisionWithSolid = collision.blockMaterial == BlockMaterial.Solid; if (!this.moveProbe.probePosition(ref, this.collisionBoundingBox, this.position, this.collisionResult, this.swimDepth, componentAccessor) - || canAct && !this.moveProbe.isInWater()) { + || canSteer && !this.moveProbe.isInWater()) { collisionStart = this.bisect(ref, this.lastValidPosition, 0.0, this.position, collisionStart, this.position, componentAccessor); if (this.debugModeMove) { LOGGER.at(Level.INFO) .log( "Move - Dive: Collision with solid=%s, Bisect pos=%s, blocked=%s", this.collisionWithSolid, - Vector3d.formatShortString(this.position), + Vector3dUtil.formatShortString(this.position), this.isObstructed ); } @@ -397,7 +398,7 @@ public class MotionControllerDive extends MotionControllerBase { .log( "Move - Dive: Collision with solid=%s, pos=%s, blocked=%s", this.collisionWithSolid, - Vector3d.formatShortString(this.position), + Vector3dUtil.formatShortString(this.position), this.isObstructed ); } @@ -423,8 +424,8 @@ public class MotionControllerDive extends MotionControllerBase { } @Override - public boolean canAct(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - return super.canAct(ref, componentAccessor) && this.moveProbe.isInWater(); + public boolean canSteer(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { + return super.canSteer(ref, componentAccessor) && this.moveProbe.isInWater(); } @Override @@ -499,7 +500,12 @@ public class MotionControllerDive extends MotionControllerBase { CollisionModule.findCollisions(this.collisionBoundingBox, probePosition, probeMovement, this.collisionResult, componentAccessor); if (this.debugModeMove) { LOGGER.at(Level.INFO) - .log("Probe Step: pos=%s mov=%s left=%s", Vector3d.formatShortString(probePosition), Vector3d.formatShortString(probeMovement), maxDistance); + .log( + "Probe Step: pos=%s mov=%s left=%s", + Vector3dUtil.formatShortString(probePosition), + Vector3dUtil.formatShortString(probeMovement), + maxDistance + ); } if (this.debugModeCollisions) { @@ -507,17 +513,17 @@ public class MotionControllerDive extends MotionControllerBase { } BlockCollisionData collision = this.collisionResult.getFirstBlockCollision(); - this.tempPosition.assign(probePosition); + this.tempPosition.set(probePosition); if (collision == null) { probePosition.add(probeMovement); - probeMovement.assign(Vector3d.ZERO); + probeMovement.zero(); double distanceTravelled; if (this.probeMoveProbe.probePosition(ref, this.collisionBoundingBox, probePosition, this.collisionResult, this.swimDepth, componentAccessor) && this.probeMoveProbe.isInWater()) { distanceTravelled = maxDistance; if (this.debugModeMove) { LOGGER.at(Level.INFO) - .log("Probe - Dive: No Collision, valid pos=%s, distanceLeft=%s", Vector3d.formatShortString(probePosition), maxDistance - maxDistance); + .log("Probe - Dive: No Collision, valid pos=%s, distanceLeft=%s", Vector3dUtil.formatShortString(probePosition), maxDistance - maxDistance); } } else { distanceTravelled = this.bisect(ref, this.tempPosition, 0.0, probePosition, maxDistance, probePosition, componentAccessor); @@ -525,7 +531,7 @@ public class MotionControllerDive extends MotionControllerBase { LOGGER.at(Level.INFO) .log( "Probe - Dive: No Collision, Bisect pos=%s, distanceLeft=%s", - Vector3d.formatShortString(probePosition), + Vector3dUtil.formatShortString(probePosition), maxDistance - distanceTravelled ); } @@ -548,7 +554,7 @@ public class MotionControllerDive extends MotionControllerBase { } else { double collisionStart = collision.collisionStart; double distanceTravelledx = maxDistance * collisionStart; - probePosition.assign(collision.collisionPoint); + probePosition.set(collision.collisionPoint); if (!this.probeMoveProbe.probePosition(ref, this.collisionBoundingBox, probePosition, this.collisionResult, this.swimDepth, componentAccessor) || !this.probeMoveProbe.isInWater()) { distanceTravelledx = this.bisect(ref, this.tempPosition, 0.0, probePosition, distanceTravelledx, probePosition, componentAccessor); @@ -556,7 +562,7 @@ public class MotionControllerDive extends MotionControllerBase { LOGGER.at(Level.INFO) .log( "Probe - Dive: Collision, Bisect pos=%s, distanceLeft=%s, collision start=%s", - Vector3d.formatShortString(probePosition), + Vector3dUtil.formatShortString(probePosition), maxDistance - distanceTravelledx, collisionStart ); @@ -565,7 +571,7 @@ public class MotionControllerDive extends MotionControllerBase { LOGGER.at(Level.INFO) .log( "Probe - Dive: Collision, valid pos=%s, distanceLeft=%s, collision start=%s", - Vector3d.formatShortString(probePosition), + Vector3dUtil.formatShortString(probePosition), maxDistance - distanceTravelledx, collisionStart ); @@ -652,7 +658,7 @@ public class MotionControllerDive extends MotionControllerBase { @Override public boolean estimateVelocity(Steering steering, @Nonnull Vector3d velocityOut) { - velocityOut.assign(Vector3d.ZERO); + velocityOut.zero(); return false; } @@ -666,8 +672,8 @@ public class MotionControllerDive extends MotionControllerBase { protected void dampForceVelocity( @Nonnull Vector3d forceVelocity, double forceVelocityDamping, double interval, ComponentAccessor componentAccessor ) { - if (forceVelocity.squaredLength() < this.minSpeedAfterForceSquared) { - forceVelocity.assign(Vector3d.ZERO); + if (forceVelocity.lengthSquared() < this.minSpeedAfterForceSquared) { + forceVelocity.zero(); } else { NPCPhysicsMath.deccelerateToStop(forceVelocity, this.getDampingDeceleration(), interval); } @@ -678,13 +684,13 @@ public class MotionControllerDive extends MotionControllerBase { return 0.5; } else { return swimDepth < 0.0 - ? NPCPhysicsMath.lerp(eyeHeight, boundingBox.getMin().getY(), -swimDepth) - : NPCPhysicsMath.lerp(eyeHeight, boundingBox.getMax().getY(), swimDepth); + ? NPCPhysicsMath.lerp(eyeHeight, boundingBox.getMin().y(), -swimDepth) + : NPCPhysicsMath.lerp(eyeHeight, boundingBox.getMax().y(), swimDepth); } } public static double relativeSwimDepthToHeight(double swimDepth, @Nullable Box boundingBox, float eyeHeight) { - return boundingBox == null ? 0.5 : relativeSwimDepthToBoundingBox(swimDepth, boundingBox, eyeHeight) - boundingBox.getMin().getY(); + return boundingBox == null ? 0.5 : relativeSwimDepthToBoundingBox(swimDepth, boundingBox, eyeHeight) - boundingBox.getMin().y(); } public static double relativeSwimDepthToHeight( diff --git a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerFly.java b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerFly.java index 9f4a885f..4e674972 100644 --- a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerFly.java +++ b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerFly.java @@ -6,7 +6,7 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.collision.BlockCollisionData; import com.hypixel.hytale.server.core.modules.collision.CollisionModule; import com.hypixel.hytale.server.core.modules.collision.WorldUtil; @@ -27,6 +27,7 @@ import com.hypixel.hytale.server.npc.util.PositionProbeAir; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class MotionControllerFly extends MotionControllerBase { public static final String TYPE = "Fly"; @@ -70,7 +71,7 @@ public class MotionControllerFly extends MotionControllerBase { public MotionControllerFly(@Nonnull BuilderSupport builderSupport, @Nonnull BuilderMotionControllerFly builder) { super(builderSupport, builder); this.setGravity(builder.getGravity()); - this.componentSelector.assign(1.0, 1.0, 1.0); + this.componentSelector.set(1.0, 1.0, 1.0); this.minAirSpeed = builder.getMinAirSpeed(); this.maxClimbSpeed = builder.getMaxClimbSpeed(); this.maxSinkSpeed = builder.getMaxSinkSpeed(); @@ -115,20 +116,20 @@ public class MotionControllerFly extends MotionControllerBase { this.moveProbe.probePosition(ref, this.collisionBoundingBox, this.position, this.collisionResult, componentAccessor); this.currentRelativeSpeed = steering.getSpeed(); if (!this.isAlive(ref, componentAccessor)) { - this.forceVelocity.assign(Vector3d.ZERO); + this.forceVelocity.zero(); this.appliedVelocities.clear(); } double maxFallSpeed = this.moveProbe.isInWater() ? this.maxSinkSpeedFluid : this.maxFallSpeed; boolean onGround = this.onGround(); - if (this.forceVelocity.equals(Vector3d.ZERO) && this.appliedVelocities.isEmpty()) { - if (NPCPhysicsMath.near(this.lastVelocity, Vector3d.ZERO)) { + if (this.forceVelocity.equals(Vector3dUtil.ZERO) && this.appliedVelocities.isEmpty()) { + if (NPCPhysicsMath.near(this.lastVelocity, Vector3dUtil.ZERO)) { PhysicsMath.vectorFromAngles(this.getYaw(), this.getPitch(), this.lastVelocity); this.lastSpeed = 0.0; } - if (this.canAct(ref, componentAccessor)) { - translation.assign(steering.getTranslation()); + if (this.canSteer(ref, componentAccessor)) { + translation.set(steering.getTranslation()); double steeringSpeed = steering.hasTranslation() ? translation.length() : 0.0; float yaw = PhysicsMath.normalizeAngle(this.getYaw()); float pitch = PhysicsMath.normalizeTurnAngle(this.getPitch()); @@ -141,8 +142,8 @@ public class MotionControllerFly extends MotionControllerBase { expYaw = PhysicsMath.headingFromDirection(dirX, dirZ); expPitch = TrigMathUtil.atan2(translation.y, Math.sqrt(dotXZ)); } else { - expYaw = steering.hasYaw() ? steering.getYaw() : yaw; - expPitch = steering.hasPitch() ? steering.getPitch() : (this.autoLevel ? 0.0F : pitch); + expYaw = steering.hasYawOrDirection() ? steering.getYawOrDirection() : yaw; + expPitch = steering.hasPitchOrDirection() ? steering.getPitchOrDirection() : (this.autoLevel ? 0.0F : pitch); } steering.clearYaw(); @@ -179,14 +180,14 @@ public class MotionControllerFly extends MotionControllerBase { steering.setPitch(newPitch); steering.setRoll(constrainedRoll); if (steeringSpeed == 0.0) { - translation.assign(Vector3d.ZERO); + translation.zero(); } else { - translation.scale(steeringSpeed * this.effectHorizontalSpeedMultiplier); + translation.mul(steeringSpeed * this.effectHorizontalSpeedMultiplier); } - this.lastVelocity.assign(translation); + this.lastVelocity.set(translation); this.lastSpeed = steeringSpeed; - translation.scale(dt); + translation.mul(dt); if (this.debugModeValidateMath && !NPCPhysicsMath.isValid(translation)) { throw new IllegalArgumentException(String.valueOf(translation)); } @@ -196,7 +197,7 @@ public class MotionControllerFly extends MotionControllerBase { steering.setRoll(this.getRoll()); if (onGround) { this.setMotionKind(MotionKind.STANDING); - this.lastVelocity.assign(Vector3d.ZERO); + this.lastVelocity.zero(); this.lastSpeed = 0.0; return dt; } @@ -219,9 +220,9 @@ public class MotionControllerFly extends MotionControllerBase { translation.z = 0.0; } - this.lastVelocity.assign(translation); + this.lastVelocity.set(translation); this.lastSpeed = this.lastVelocity.length(); - translation.scale(dt); + translation.mul(dt); if (this.debugModeValidateMath && !NPCPhysicsMath.isValid(translation)) { throw new IllegalArgumentException(String.valueOf(translation)); } @@ -237,7 +238,7 @@ public class MotionControllerFly extends MotionControllerBase { steering.setPitch(this.getPitch()); steering.setRoll(this.getRoll()); if (!this.isObstructed()) { - translation.assign(this.forceVelocity); + translation.set(this.forceVelocity); for (int i = 0; i < this.appliedVelocities.size(); i++) { MotionControllerBase.AppliedVelocity entry = this.appliedVelocities.get(i); @@ -252,18 +253,18 @@ public class MotionControllerFly extends MotionControllerBase { translation.add(entry.velocity); } } else { - translation.assign(Vector3d.ZERO); + translation.zero(); this.appliedVelocities.clear(); - this.forceVelocity.assign(Vector3d.ZERO); + this.forceVelocity.zero(); } if (!onGround) { translation.y = NPCPhysicsMath.accelerateDrag(translation.y, -this.gravity, dt, maxFallSpeed); } - this.lastVelocity.assign(translation); + this.lastVelocity.set(translation); this.lastSpeed = this.lastVelocity.length(); - translation.scale(dt); + translation.mul(dt); if (this.debugModeValidateMath && !NPCPhysicsMath.isValid(translation)) { throw new IllegalArgumentException(String.valueOf(translation)); } else { @@ -304,14 +305,14 @@ public class MotionControllerFly extends MotionControllerBase { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - int ix = MathUtil.floor(position.getX()); - int iz = MathUtil.floor(position.getZ()); + int ix = MathUtil.floor(position.x()); + int iz = MathUtil.floor(position.z()); if (ix == this.lastVerticalPositionX && iz == this.lastVerticalPositionZ) { return this.verticalRange; } else { this.lastVerticalPositionX = ix; this.lastVerticalPositionZ = iz; - double y = position.getY(); + double y = position.y(); World world = componentAccessor.getExternalData().getWorld(); ChunkStore chunkStore = world.getChunkStore(); long chunkIndex = ChunkUtil.indexChunkFromBlock(ix, iz); @@ -344,14 +345,14 @@ public class MotionControllerFly extends MotionControllerBase { maxY = y; } - this.verticalRange.assign(y, minY, maxY); + this.verticalRange.set(y, minY, maxY); return this.verticalRange; } else { - this.verticalRange.assign(y, y, y); + this.verticalRange.set(y, y, y); return this.verticalRange; } } else { - this.verticalRange.assign(y, y, y); + this.verticalRange.set(y, y, y); return this.verticalRange; } } @@ -372,15 +373,15 @@ public class MotionControllerFly extends MotionControllerBase { ) { boolean probeOnly = probeMoveData != null; boolean saveSegments = probeOnly && probeMoveData.startProbing(); - boolean canAct = probeOnly || this.canAct(ref, componentAccessor); + boolean canSteer = probeOnly || this.canSteer(ref, componentAccessor); String debugPrefix = probeOnly ? "Probe" : "Move"; if (this.debugModeMove) { LOGGER.at(Level.INFO) .log( "%s - Fly: Execute pos=%s vel=%s onGround=%s blocked=%s ", debugPrefix, - Vector3d.formatShortString(position), - Vector3d.formatShortString(translation), + Vector3dUtil.formatShortString(position), + Vector3dUtil.formatShortString(translation), this.onGround(), this.isObstructed ); @@ -400,7 +401,7 @@ public class MotionControllerFly extends MotionControllerBase { } } - this.collisionResult.setCollisionByMaterial(canAct ? 6 : 4); + this.collisionResult.setCollisionByMaterial(canSteer ? 6 : 4); CollisionModule.get(); CollisionModule.findCollisions(this.collisionBoundingBox, position, translation, this.collisionResult, componentAccessor); if (this.debugModeBlockCollisions) { @@ -412,7 +413,7 @@ public class MotionControllerFly extends MotionControllerBase { } BlockCollisionData collision = this.collisionResult.getFirstBlockCollision(); - this.lastValidPosition.assign(position); + this.lastValidPosition.set(position); double distanceFactor; if (collision == null) { position.add(translation); @@ -422,8 +423,8 @@ public class MotionControllerFly extends MotionControllerBase { .log( "%s - Fly: No collision pos=%s vel=%s onGround=%s blocked=%s ", debugPrefix, - Vector3d.formatShortString(position), - Vector3d.formatShortString(translation), + Vector3dUtil.formatShortString(position), + Vector3dUtil.formatShortString(translation), this.onGround(), this.isObstructed ); @@ -433,7 +434,7 @@ public class MotionControllerFly extends MotionControllerBase { throw new IllegalStateException("Invalid position"); } } else { - position.assign(collision.collisionPoint); + position.set(collision.collisionPoint); distanceFactor = collision.collisionStart; if (!probeOnly) { this.isObstructed = true; @@ -444,9 +445,9 @@ public class MotionControllerFly extends MotionControllerBase { .log( "%s - Fly: Collision pos=%s collStart=%s vel=%s onGround=%s blocked=%s ", debugPrefix, - Vector3d.formatShortString(position), + Vector3dUtil.formatShortString(position), distanceFactor, - Vector3d.formatShortString(translation), + Vector3dUtil.formatShortString(translation), this.onGround(), this.isObstructed ); @@ -468,7 +469,9 @@ public class MotionControllerFly extends MotionControllerBase { distanceFactor *= adjust; if (this.debugModeMove) { LOGGER.at(Level.INFO) - .log("%s - Fly: Bisect step pos=%s distanceFactor=%s adjust=%s", debugPrefix, Vector3d.formatShortString(position), distanceFactor, adjust); + .log( + "%s - Fly: Bisect step pos=%s distanceFactor=%s adjust=%s", debugPrefix, Vector3dUtil.formatShortString(position), distanceFactor, adjust + ); } } @@ -499,7 +502,7 @@ public class MotionControllerFly extends MotionControllerBase { if (scale < 1.0) { dt *= scale; this.lastSpeed *= scale; - this.lastVelocity.scale(scale); + this.lastVelocity.mul(scale); } return dt; @@ -518,8 +521,8 @@ public class MotionControllerFly extends MotionControllerBase { protected void dampForceVelocity( @Nonnull Vector3d forceVelocity, double forceVelocityDamping, double interval, ComponentAccessor componentAccessor ) { - if (forceVelocity.squaredLength() < this.minSpeedAfterForceSquared) { - forceVelocity.assign(Vector3d.ZERO); + if (forceVelocity.lengthSquared() < this.minSpeedAfterForceSquared) { + forceVelocity.zero(); } else { NPCPhysicsMath.deccelerateToStop(forceVelocity, this.getDampingDeceleration(), interval); } @@ -540,8 +543,8 @@ public class MotionControllerFly extends MotionControllerBase { } @Override - public boolean canAct(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - return super.canAct(ref, componentAccessor) && this.moveProbe.isInAir() && this.effectHorizontalSpeedMultiplier != 0.0; + public boolean canSteer(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { + return super.canSteer(ref, componentAccessor) && this.moveProbe.isInAir() && this.effectHorizontalSpeedMultiplier != 0.0; } @Override @@ -611,7 +614,7 @@ public class MotionControllerFly extends MotionControllerBase { @Override public boolean estimateVelocity(Steering steering, @Nonnull Vector3d velocityOut) { - velocityOut.assign(Vector3d.ZERO); + velocityOut.zero(); return false; } @@ -629,7 +632,7 @@ public class MotionControllerFly extends MotionControllerBase { assert transformComponent != null; - PhysicsMath.vectorFromAngles(transformComponent.getRotation().getYaw(), (float) (Math.PI / 4), this.lastVelocity); + PhysicsMath.vectorFromAngles(transformComponent.getRotation().yaw(), (float) (Math.PI / 4), this.lastVelocity); this.lastSpeed = speed; } diff --git a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerWalk.java b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerWalk.java index 10c5aa92..6743e8a1 100644 --- a/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerWalk.java +++ b/src/com/hypixel/hytale/server/npc/movement/controllers/MotionControllerWalk.java @@ -8,9 +8,8 @@ import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.protocol.MovementStates; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; @@ -41,6 +40,7 @@ import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.movement.MotionKind; import com.hypixel.hytale.server.npc.movement.Steering; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import com.hypixel.hytale.server.npc.movement.controllers.builders.BuilderMotionControllerWalk; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; @@ -49,6 +49,9 @@ import java.util.function.Supplier; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class MotionControllerWalk extends MotionControllerBase { public static final String TYPE = "Walk"; @@ -56,7 +59,8 @@ public class MotionControllerWalk extends MotionControllerBase { public static final double CLIMB_FORWARD_DISTANCE_SQUARED = 0.010000000000000002; public static final double JUMP_FORWARD_DISTANCE = 0.5; public static final double ONE_PLUS_THRESHOLD = 1.00001; - public static final double DROP_MIN_STOP_DIST = 0.001; + public static final double MIN_SAFETY_DISTANCE = 0.05; + private static final double EDGE_MARGIN_BBOX_FACTOR = 0.1; protected static final EnumSet STATE_CAN_HOVER = EnumSet.of(MotionKind.MOVING, MotionKind.STANDING); protected static final EnumSet VALID_MOTIONS = EnumSet.of( MotionKind.ASCENDING, MotionKind.DESCENDING, MotionKind.DROPPING, MotionKind.STANDING, MotionKind.MOVING @@ -84,6 +88,7 @@ public class MotionControllerWalk extends MotionControllerBase { protected final double climbSpeedPow; protected final double climbSpeedConst; protected final double maxDropHeight; + protected final double maxDropHeightRelaxed; protected final double minDescentAnimationHeight; protected final double descendFlatness; protected final double descendSpeedCompensation; @@ -136,6 +141,7 @@ public class MotionControllerWalk extends MotionControllerBase { protected final Vector3d tmpMovePosition = new Vector3d(); protected final CollisionResult tmpResults = new CollisionResult(); protected final Vector2d tmpClimbHeightResults = new Vector2d(); + private final EnumSet effectiveMoveConstraints = EnumSet.noneOf(RelaxedConstraint.class); public MotionControllerWalk(@Nonnull BuilderMotionControllerWalk builder, @Nonnull BuilderSupport builderSupport) { super(builderSupport, builder); @@ -164,6 +170,7 @@ public class MotionControllerWalk extends MotionControllerBase { this.descendFlatness = builder.getDescendForwardAmount(builderSupport); this.descendSpeedCompensation = builder.getDescendSpeedCompensation(builderSupport); this.maxDropHeight = builder.getMaxDropHeight(builderSupport); + this.maxDropHeightRelaxed = this.maxDropHeight + builder.getMaxDropHeightRelaxedAdjustment(builderSupport); this.fenceBlockSet = builder.getFenceBlockSet(); this.minHover = builder.getMinHover(); this.maxHover = builder.getMaxHover(); @@ -201,7 +208,7 @@ public class MotionControllerWalk extends MotionControllerBase { assert transformComponent != null; - this.verticalRange.assign(transformComponent.getPosition().getY(), 0.0, 320.0); + this.verticalRange.set(transformComponent.getPosition().y(), 0.0, 320.0); return this.verticalRange; } @@ -395,9 +402,28 @@ public class MotionControllerWalk extends MotionControllerBase { return motionKind == MotionKind.ASCENDING || motionKind == MotionKind.DESCENDING || motionKind == MotionKind.DROPPING; } + private boolean isOnSolidGround() { + return this.onGround && this.belowBlockType != null && this.belowBlockType.getMaterial() == BlockMaterial.Solid; + } + @Override - public boolean canAct(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { - return super.canAct(ref, componentAccessor) && this.onGround && this.belowBlockType != null && this.belowBlockType.getMaterial() == BlockMaterial.Solid; + public boolean canSteer(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { + return super.canSteer(ref, componentAccessor) && this.isOnSolidGround(); + } + + @Nullable + @Override + public String canSteerFailReason(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { + String reason = super.canSteerFailReason(ref, componentAccessor); + if (reason != null) { + return reason; + } else if (!this.onGround) { + return "OFF_GROUND"; + } else if (this.belowBlockType == null) { + return "NO_BLOCK_BELOW"; + } else { + return this.belowBlockType.getMaterial() != BlockMaterial.Solid ? "NO_SOLID_BLOCK_BELOW" : null; + } } @Override @@ -423,13 +449,13 @@ public class MotionControllerWalk extends MotionControllerBase { @Override public void constrainRotations(Role role, @Nonnull TransformComponent transform) { - Vector3f rotation = transform.getRotation(); + Rotation3f rotation = transform.getRotation(); rotation.setPitch(0.0F); rotation.setRoll(0.0F); } @Override - public void forceVelocity(@Nonnull Vector3d velocity, VelocityConfig velocityConfig, boolean ignoreDamping) { + public void forceVelocity(@Nonnull Vector3dc velocity, VelocityConfig velocityConfig, boolean ignoreDamping) { super.forceVelocity(velocity, velocityConfig, ignoreDamping); this.onGround = false; } @@ -507,10 +533,10 @@ public class MotionControllerWalk extends MotionControllerBase { @Override public boolean estimateVelocity(@Nonnull Steering steering, @Nonnull Vector3d velocityOut) { if (steering.hasTranslation()) { - velocityOut.assign(steering.getTranslation()).scale(this.getCurrentSpeed()); + velocityOut.set(steering.getTranslation()).mul(this.getCurrentSpeed()); return true; } else { - velocityOut.assign(Vector3d.ZERO); + velocityOut.zero(); return false; } } @@ -528,7 +554,7 @@ public class MotionControllerWalk extends MotionControllerBase { public void postReadPosition(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { if (!this.footingPosition.equals(this.position)) { this.footingBlocksValid = false; - this.footingPosition.assign(this.position); + this.footingPosition.set(this.position); } World world = componentAccessor.getExternalData().getWorld(); @@ -554,7 +580,7 @@ public class MotionControllerWalk extends MotionControllerBase { int blockIndex = 0; this.footingBlocksValid = true; - label260: + label270: for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunk(chunkX, chunkZ)); @@ -563,7 +589,7 @@ public class MotionControllerWalk extends MotionControllerBase { this.belowBlockType = null; this.belowBlockTypeId = 0; this.onGround = false; - break label260; + break label270; } int minX = chunkX == minChunkX ? ChunkUtil.localCoordinate(minBlockX) : 0; @@ -594,9 +620,19 @@ public class MotionControllerWalk extends MotionControllerBase { this.belowBlockTypeId = 0; BoxBlockIntersectionEvaluator boxBlockIntersectionEvaluator = this.collisionResult.getBoxBlockIntersection(); boxBlockIntersectionEvaluator.setBox(this.collisionBoundingBox, this.footingPosition); + Vector3d worldUp = boxBlockIntersectionEvaluator.getWorldUp(); + int horizontalOverlapMask; + if (worldUp.y != 0.0) { + horizontalOverlapMask = 40; + } else if (worldUp.x != 0.0) { + horizontalOverlapMask = 48; + } else { + horizontalOverlapMask = 24; + } + blockIndex = 0; - label227: + label237: for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { int chunkMinBlockX = ChunkUtil.minBlock(chunkX); @@ -632,18 +668,18 @@ public class MotionControllerWalk extends MotionControllerBase { if (rotatedBoxes.hasDetailBoxes()) { for (Box detailBox : rotatedBoxes.getDetailBoxes()) { code = boxBlockIntersectionEvaluator.intersectBoxComputeOnGround(detailBox, x, y, z); - if (!CollisionMath.isDisjoint(code) && boxBlockIntersectionEvaluator.isOnGround()) { + if (boxBlockIntersectionEvaluator.isOnGround() && (code & horizontalOverlapMask) == horizontalOverlapMask) { this.belowBlockType = blockType; this.belowBlockTypeId = blockId; this.onGround = true; - break label227; + break label237; } } - } else if (boxBlockIntersectionEvaluator.isOnGround()) { + } else if (boxBlockIntersectionEvaluator.isOnGround() && (code & horizontalOverlapMask) == horizontalOverlapMask) { this.belowBlockType = blockType; this.belowBlockTypeId = blockId; this.onGround = true; - break label227; + break label237; } } } @@ -784,20 +820,20 @@ public class MotionControllerWalk extends MotionControllerBase { public int translateToAccessiblePosition( @Nonnull Vector3d position, double minYValue, double maxYValue, @Nonnull ComponentAccessor componentAccessor ) { - if (position.getY() < 0.0) { + if (position.y() < 0.0) { return -1; } else { World world = componentAccessor.getExternalData().getWorld(); - long chunkIndex = ChunkUtil.indexChunkFromBlock(position.getX(), position.getZ()); + long chunkIndex = ChunkUtil.indexChunkFromBlock(position.x(), position.z()); WorldChunk chunk = world.getChunkIfInMemory(chunkIndex); if (chunk == null) { return -1; } else { BlockChunk blockChunk = chunk.getBlockChunk(); BlockTypeAssetMap assetMap = BlockType.getAssetMap(); - int x = MathUtil.floor(position.getX()); - int y = MathUtil.floor(position.getY()); - int z = MathUtil.floor(position.getZ()); + int x = MathUtil.floor(position.x()); + int y = MathUtil.floor(position.y()); + int z = MathUtil.floor(position.z()); if (y < 320) { int blockId = chunk.getBlock(x, y, z); if (blockId != 0) { @@ -836,7 +872,7 @@ public class MotionControllerWalk extends MotionControllerBase { if (top > maxYValue) { return 0; } else { - position.setY(top); + position.y = top; return 1; } } @@ -879,7 +915,7 @@ public class MotionControllerWalk extends MotionControllerBase { return 0; } - position.setY(topx); + position.y = topx; return 1; } } else if (blockType.getMaterial() == BlockMaterial.Solid) { @@ -890,7 +926,7 @@ public class MotionControllerWalk extends MotionControllerBase { return 0; } - position.setY(topx); + position.y = topx; return 1; } } @@ -928,305 +964,30 @@ public class MotionControllerWalk extends MotionControllerBase { double maxVerticalSpeed = this.inWater() ? this.maxVerticalSpeedFluid : this.maxVerticalSpeed; double maxHorizontalSpeed = this.getMaximumSpeed(); boolean isDead = !this.isAlive(ref, componentAccessor); - if (isDead) { + if (isDead && !this.isForcePushed()) { this.climbUpDistance = 0.0; this.currentClimbForwardDistance = 0.0; this.maxClimbForwardDistance = 0.0; - this.forceVelocity.assign(Vector3d.ZERO); - this.appliedVelocities.clear(); steering.setYaw(this.getYaw()); if (this.onGround) { - translation.assign(Vector3d.ZERO); + translation.zero(); steering.setPitch(0.0F); } else { steering.setPitch(this.getPitch()); Velocity velocityComponent = componentAccessor.getComponent(ref, Velocity.getComponentType()); Vector3d velocity = velocityComponent.getVelocity(); - translation.assign(velocity); + translation.set(velocity); } translation.y = this.computeNewFallSpeed(dt, translation.y); - translation.scale(dt); + translation.mul(dt); this.validateTranslation(translation, "Death"); return dt; - } else if (this.forceVelocity.equals(Vector3d.ZERO) && this.appliedVelocities.isEmpty()) { - if (this.cachedMovementBlocked) { - return dt; - } else { - float heading = this.getYaw(); - if (this.getMotionKind() == MotionKind.ASCENDING) { - if (this.isRequiresPreciseMovement() && this.havePreciseMovementTarget) { - double maxDistance = this.waypointDistance(this.preciseMovementTarget, this.position) + this.currentClimbForwardDistance; - if (maxDistance < this.maxClimbForwardDistance) { - this.maxClimbForwardDistance = maxDistance; - } - } - - double distance = dt * this.climbSpeed; - if (this.jumping) { - if (this.climbUpDistance > 0.0) { - distance *= Math.max(Math.pow(this.climbUpDistance * this.climbUpDistance * this.jumpForce, this.jumpBlending), 1.0); - this.climbUpDistance = this.computeClimbMove(this.climbUpDirection, this.climbUpDistance, distance, translation); - } else if (this.jumpDropHeight > 0.0) { - double jumpDiff = this.currentJumpHeight - this.jumpDropHeight; - distance *= Math.max(Math.pow(jumpDiff * jumpDiff * this.jumpDescentSteepness, this.jumpDescentBlending), 1.0); - this.jumpDropHeight = this.computeClimbMove(this.jumpDropDirection, this.jumpDropHeight, distance, translation); - } else { - this.setMotionKind(MotionKind.DROPPING); - } - - double heightAboveBlock = this.currentJumpHeight - this.climbUpDistance - translation.y; - double moveDistance = 0.0; - if (heightAboveBlock > this.jumpBlockHeight) { - double percentage; - if (this.climbUpDistance > 0.0) { - percentage = (heightAboveBlock - this.jumpBlockHeight) / heightAboveBlock / 2.0; - } else { - percentage = 0.5 + (1.0 - this.jumpDropHeight / (this.currentJumpHeight - this.jumpBlockHeight)) / 2.0; - } - - double expectedDistance = this.maxClimbForwardDistance * percentage; - if (expectedDistance > this.maxClimbForwardDistance) { - expectedDistance = this.maxClimbForwardDistance; - } - - moveDistance = expectedDistance - this.currentClimbForwardDistance; - if (moveDistance < 0.0) { - moveDistance = 0.0; - } - - this.currentClimbForwardDistance = expectedDistance; - translation.add(this.climbForwardDirection.x * moveDistance, 0.0, this.climbForwardDirection.z * moveDistance); - } - - if (this.isBlendingHeading) { - heading = this.computeBlendHeading( - heading, - NPCPhysicsMath.headingFromDirection(this.climbForwardDirection.x, this.climbForwardDirection.z, heading), - dt, - moveDistance / dt, - steering.getRelativeTurnSpeed() - ); - } - - this.lockOrientation(steering, translation, heading); - this.fallSpeed = 0.0; - this.onGround = false; - this.validateTranslation(translation, "AscendingJump"); - return dt; - } else { - double moveDistancex; - if (this.climbUpDistance > 0.0) { - double prevDistance = this.climbUpDistance; - this.climbUpDistance = this.computeClimbMove(this.climbUpDirection, prevDistance, distance, translation); - moveDistancex = prevDistance - this.climbUpDistance; - } else if (this.currentClimbForwardDistance < this.maxClimbForwardDistance) { - double remaining = this.maxClimbForwardDistance - this.currentClimbForwardDistance; - if (distance < remaining) { - double newRemaining = remaining - distance; - moveDistancex = newRemaining <= 1.0E-5 ? remaining : distance; - } else { - moveDistancex = remaining; - } - - this.currentClimbForwardDistance += moveDistancex; - translation.assign(this.climbForwardDirection).scale(moveDistancex); - this.onGround = false; - } else { - this.setMotionKind(MotionKind.DROPPING); - moveDistancex = 0.0; - } - - if (this.isBlendingHeading) { - heading = this.computeBlendHeading( - heading, - NPCPhysicsMath.headingFromDirection(this.climbForwardDirection.x, this.climbForwardDirection.z, heading), - dt, - moveDistancex / dt, - steering.getRelativeTurnSpeed() - ); - } - - this.lockOrientation(steering, translation, heading); - this.fallSpeed = 0.0; - this.onGround = false; - this.validateSpeeds(ref, "Ascending", componentAccessor); - this.validateTranslation(translation, "Ascending"); - return dt; - } - } else if (this.getMotionKind() == MotionKind.DESCENDING) { - double maxForwardSpeed = this.currentRelativeSpeed * maxHorizontalSpeed; - if (this.isRequiresPreciseMovement() && this.havePreciseMovementTarget) { - double maxForwardDistance = this.waypointDistance(this.position, this.preciseMovementTarget); - if (maxForwardDistance < maxForwardSpeed * dt) { - maxForwardSpeed = maxForwardDistance / dt; - } - } - - this.fallSpeed = MathUtil.maxValue(0.0, NPCPhysicsMath.accelerateDragCapped(this.fallSpeed, 5.0 * this.gravity, dt, maxVerticalSpeed)); - this.moveSpeed = MathUtil.clamp(maxForwardSpeed, 0.0, this.moveSpeed + dt * this.acceleration); - if (this.moveSpeed > maxHorizontalSpeed) { - this.moveSpeed = maxHorizontalSpeed; - } - - if (this.moveSpeed <= 0.3 * this.maxHorizontalSpeed) { - this.setMotionKind(MotionKind.DROPPING); - } - - double vertical = this.climbForwardDirection.y; - if (this.predictedFallHeight > 0.0) { - this.totalDropDistance = this.totalDropDistance + Math.abs(this.climbForwardDirection.y * this.moveSpeed * dt); - double scaledDiff = Math.min(this.totalDropDistance / this.predictedFallHeight, 1.0); - vertical *= Math.pow(this.descentSteepness, this.descentBlending) * Math.pow(scaledDiff, this.descentBlending); - } - - translation.assign(this.climbForwardDirection.x, vertical, this.climbForwardDirection.z); - translation.scale(this.moveSpeed * dt); - translation.clipToZero(this.getEpsilonSpeed()); - if (this.isBlendingHeading) { - heading = this.computeBlendHeading( - heading, - NPCPhysicsMath.headingFromDirection(this.climbForwardDirection.x, this.climbForwardDirection.z, heading), - dt, - this.moveSpeed, - steering.getRelativeTurnSpeed() - ); - } - - this.lockOrientation(steering, translation, heading); - this.validateSpeeds(ref, "Descending", componentAccessor); - this.validateTranslation(translation, "Descending"); - return dt; - } else { - this.validateSpeeds(ref, "Enter Walk/Drop", componentAccessor); - boolean canAct = this.canAct(ref, componentAccessor); - boolean isBlendResting = false; - float moveHeading; - if (canAct) { - moveHeading = NPCPhysicsMath.headingFromDirection(direction.x, direction.z, heading); - if (this.isRequiresPreciseMovement() && !this.isBlendingHeading) { - float turnAngle = NPCPhysicsMath.turnAngle(heading, moveHeading); - float epsilon = this.getEpsilonAngle(); - if (turnAngle < -epsilon || turnAngle > epsilon) { - float maxRotation = (float)MathUtil.clamp( - dt * this.getCurrentMaxBodyRotationSpeed() * steering.getRelativeTurnSpeed(), 0.0, (float) (Math.PI / 2) - ); - turnAngle = MathUtil.clamp(turnAngle, -maxRotation, maxRotation); - heading = PhysicsMath.normalizeTurnAngle(heading + turnAngle); - translation.assign(0.0, 0.0, 0.0); - steering.setYaw(heading); - this.isFullyRotated = false; - return dt; - } - - heading = moveHeading; - } - - this.moveSpeed = MathUtil.clamp(this.currentRelativeSpeed * maxHorizontalSpeed, 0.0, this.moveSpeed + dt * this.acceleration); - if (this.fallSpeed > 0.0) { - this.fallSpeed = 0.0; - } - - if (this.moveSpeed < this.getEpsilonSpeed()) { - this.moveSpeed = 0.0; - } else if (this.moveSpeed < this.minHorizontalSpeed) { - this.moveSpeed = this.minHorizontalSpeed; - } - - if (this.isBlendingHeading) { - if (this.blendRestTurnAngle > 0.0F) { - float turnAngle = this.computeBlendTurnAngle(heading, moveHeading); - if (Math.abs(turnAngle) > this.blendRestTurnAngle) { - isBlendResting = true; - } - - heading = this.computeBlendHeading(heading, moveHeading, dt, this.moveSpeed, turnAngle, steering.getRelativeTurnSpeed()); - } else { - heading = this.computeBlendHeading(heading, moveHeading, dt, this.moveSpeed, steering.getRelativeTurnSpeed()); - } - - steering.setYaw(heading); - this.isFullyRotated = true; - } else if (steering.hasYaw()) { - heading = this.computeHeading(steering.getYaw(), steering.getRelativeTurnSpeed(), heading, dt, false, false); - steering.setYaw(heading); - } else if (this.moveSpeed != 0.0) { - heading = this.computeHeading(moveHeading, steering.getRelativeTurnSpeed(), heading, dt, true, true); - moveHeading = heading; - steering.setYaw(heading); - } - - if (this.debugModeSteer) { - LOGGER.at(Level.INFO) - .log( - "=== Compute = t =%.4f v =%.4f h =%.4f mh=%.4f", - dt, - this.moveSpeed, - (180.0F / (float)Math.PI) * heading, - (180.0F / (float)Math.PI) * moveHeading - ); - } - } else { - Velocity velocityComponent = componentAccessor.getComponent(ref, Velocity.getComponentType()); - Vector3d velocity = velocityComponent.getVelocity(); - moveHeading = NPCPhysicsMath.headingFromDirection(velocity.x, velocity.z, heading); - if (!steering.hasYaw()) { - steering.setYaw(heading); - } - - if (this.maxHover > 0.0 && this.floatsDown) { - this.fallSpeed = this.climbSpeedConst; - if (this.fallSpeed != 0.0 && this.climbSpeedMult != 0.0) { - double prevFallSpeed = this.fallSpeed; - double deltaFallSpeed = this.climbSpeedMult * Math.pow(prevFallSpeed, this.climbSpeedPow); - this.fallSpeed += deltaFallSpeed; - } - } else { - double prevFallSpeed = this.fallSpeed; - this.fallSpeed = -NPCPhysicsMath.gravityDrag(-prevFallSpeed, 5.0 * this.gravity, dt, maxVerticalSpeed); - } - } - - if (this.moveSpeed > maxHorizontalSpeed) { - this.moveSpeed = maxHorizontalSpeed; - } - - if (this.fallSpeed > maxVerticalSpeed) { - this.fallSpeed = maxVerticalSpeed; - } else if (this.fallSpeed < -maxVerticalSpeed) { - this.fallSpeed = -maxVerticalSpeed; - } - - double appliedSpeed = this.moveSpeed; - if (isBlendResting) { - double maxSpeed = this.blendRestRelativeSpeed * this.getMaximumSpeed(); - if (appliedSpeed > maxSpeed) { - appliedSpeed = maxSpeed; - } - } - - translation.x = appliedSpeed * dt * PhysicsMath.headingX(moveHeading); - translation.z = appliedSpeed * dt * PhysicsMath.headingZ(moveHeading); - translation.y = -this.fallSpeed * dt; - double maxDistance = steering.getMaxDistance(); - if (this.canAct(ref, componentAccessor) && maxDistance < Double.MAX_VALUE && maxDistance > 0.0) { - double lenSquared = NPCPhysicsMath.dotProduct(translation.x, translation.y, translation.z, this.getComponentSelector()); - double len = Math.sqrt(lenSquared); - if (len > maxDistance) { - translation.scale(maxDistance / len); - } - } - - this.validateSpeeds(ref, canAct ? "Moving" : "Falling", componentAccessor); - this.validateTranslation(translation, canAct ? "Moving" : "Falling"); - return dt; - } - } - } else { + } else if (this.isForcePushed()) { this.climbUpDistance = 0.0; this.currentClimbForwardDistance = 0.0; this.maxClimbForwardDistance = 0.0; - translation.assign(this.forceVelocity); + translation.set(this.forceVelocity); for (int i = 0; i < this.appliedVelocities.size(); i++) { MotionControllerBase.AppliedVelocity entry = this.appliedVelocities.get(i); @@ -1241,7 +1002,7 @@ public class MotionControllerWalk extends MotionControllerBase { translation.add(entry.velocity); } - translation.scale(dt); + translation.mul(dt); if (!this.onGround || !(this.forceVelocity.y < 0.0)) { this.forceVelocity.y = this.computeNewFallSpeed(dt, this.forceVelocity.y); } else if (translation.y < 0.0) { @@ -1249,7 +1010,7 @@ public class MotionControllerWalk extends MotionControllerBase { this.forceVelocity.y = 0.0; } - if (!this.appliedForce.equals(Vector3d.ZERO)) { + if (!this.appliedForce.equals(Vector3dUtil.ZERO)) { if (this.moveSpeed > 0.0) { float headingX = PhysicsMath.headingX(this.getYaw()); float headingZ = PhysicsMath.headingZ(this.getYaw()); @@ -1269,21 +1030,293 @@ public class MotionControllerWalk extends MotionControllerBase { npcComponent.setHoverHeight(translation.y <= 0.0 ? this.minHoverDrop : this.maxHover); } - this.appliedForce.assign(Vector3d.ZERO); + this.appliedForce.zero(); } if (this.onGround && this.ignoreDamping) { double speed = this.forceVelocity.length() - dt * this.inertia * this.acceleration * 5.0; if (speed > 0.0) { - this.forceVelocity.setLength(speed); + this.forceVelocity.normalize(speed); } else { - this.forceVelocity.assign(Vector3d.ZERO); + this.forceVelocity.zero(); } } steering.setYaw(this.getYaw()); this.validateTranslation(translation, "ExtForce"); return dt; + } else if (this.cachedMovementBlocked) { + return dt; + } else { + float heading = this.getYaw(); + if (this.getMotionKind() == MotionKind.ASCENDING) { + if (this.isRequiresPreciseMovement() && this.havePreciseMovementTarget) { + double maxDistance = this.waypointDistance(this.preciseMovementTarget, this.position) + this.currentClimbForwardDistance; + if (maxDistance < this.maxClimbForwardDistance) { + this.maxClimbForwardDistance = maxDistance; + } + } + + double distance = dt * this.climbSpeed; + if (this.jumping) { + if (this.climbUpDistance > 0.0) { + distance *= Math.max(Math.pow(this.climbUpDistance * this.climbUpDistance * this.jumpForce, this.jumpBlending), 1.0); + this.climbUpDistance = this.computeClimbMove(this.climbUpDirection, this.climbUpDistance, distance, translation); + } else if (this.jumpDropHeight > 0.0) { + double jumpDiff = this.currentJumpHeight - this.jumpDropHeight; + distance *= Math.max(Math.pow(jumpDiff * jumpDiff * this.jumpDescentSteepness, this.jumpDescentBlending), 1.0); + this.jumpDropHeight = this.computeClimbMove(this.jumpDropDirection, this.jumpDropHeight, distance, translation); + } else { + this.setMotionKind(MotionKind.DROPPING); + } + + double heightAboveBlock = this.currentJumpHeight - this.climbUpDistance - translation.y; + double moveDistance = 0.0; + if (heightAboveBlock > this.jumpBlockHeight) { + double percentage; + if (this.climbUpDistance > 0.0) { + percentage = (heightAboveBlock - this.jumpBlockHeight) / heightAboveBlock / 2.0; + } else { + percentage = 0.5 + (1.0 - this.jumpDropHeight / (this.currentJumpHeight - this.jumpBlockHeight)) / 2.0; + } + + double expectedDistance = this.maxClimbForwardDistance * percentage; + if (expectedDistance > this.maxClimbForwardDistance) { + expectedDistance = this.maxClimbForwardDistance; + } + + moveDistance = expectedDistance - this.currentClimbForwardDistance; + if (moveDistance < 0.0) { + moveDistance = 0.0; + } + + this.currentClimbForwardDistance = expectedDistance; + translation.add(this.climbForwardDirection.x * moveDistance, 0.0, this.climbForwardDirection.z * moveDistance); + } + + if (this.isBlendingHeading) { + heading = this.computeBlendHeading( + heading, + NPCPhysicsMath.headingFromDirection(this.climbForwardDirection.x, this.climbForwardDirection.z, heading), + dt, + moveDistance / dt, + steering.getRelativeTurnSpeed() + ); + } + + this.lockOrientation(steering, translation, heading); + this.fallSpeed = 0.0; + this.onGround = false; + this.validateTranslation(translation, "AscendingJump"); + return dt; + } else { + double moveDistancex; + if (this.climbUpDistance > 0.0) { + double prevDistance = this.climbUpDistance; + this.climbUpDistance = this.computeClimbMove(this.climbUpDirection, prevDistance, distance, translation); + moveDistancex = prevDistance - this.climbUpDistance; + } else if (this.currentClimbForwardDistance < this.maxClimbForwardDistance) { + double remaining = this.maxClimbForwardDistance - this.currentClimbForwardDistance; + if (distance < remaining) { + double newRemaining = remaining - distance; + moveDistancex = newRemaining <= 1.0E-5 ? remaining : distance; + } else { + moveDistancex = remaining; + } + + this.currentClimbForwardDistance += moveDistancex; + translation.set(this.climbForwardDirection).mul(moveDistancex); + this.onGround = false; + } else { + this.setMotionKind(MotionKind.DROPPING); + moveDistancex = 0.0; + } + + if (this.isBlendingHeading) { + heading = this.computeBlendHeading( + heading, + NPCPhysicsMath.headingFromDirection(this.climbForwardDirection.x, this.climbForwardDirection.z, heading), + dt, + moveDistancex / dt, + steering.getRelativeTurnSpeed() + ); + } + + this.lockOrientation(steering, translation, heading); + this.fallSpeed = 0.0; + this.onGround = false; + this.validateSpeeds(ref, "Ascending", componentAccessor); + this.validateTranslation(translation, "Ascending"); + return dt; + } + } else if (this.getMotionKind() == MotionKind.DESCENDING) { + double maxForwardSpeed = this.currentRelativeSpeed * maxHorizontalSpeed; + if (this.isRequiresPreciseMovement() && this.havePreciseMovementTarget) { + double maxForwardDistance = this.waypointDistance(this.position, this.preciseMovementTarget); + if (maxForwardDistance < maxForwardSpeed * dt) { + maxForwardSpeed = maxForwardDistance / dt; + } + } + + this.fallSpeed = MathUtil.maxValue(0.0, NPCPhysicsMath.accelerateDragCapped(this.fallSpeed, 5.0 * this.gravity, dt, maxVerticalSpeed)); + this.moveSpeed = MathUtil.clamp(maxForwardSpeed, 0.0, this.moveSpeed + dt * this.acceleration); + if (this.moveSpeed > maxHorizontalSpeed) { + this.moveSpeed = maxHorizontalSpeed; + } + + if (this.moveSpeed <= 0.3 * this.maxHorizontalSpeed) { + this.setMotionKind(MotionKind.DROPPING); + } + + double vertical = this.climbForwardDirection.y; + if (this.predictedFallHeight > 0.0) { + this.totalDropDistance = this.totalDropDistance + Math.abs(this.climbForwardDirection.y * this.moveSpeed * dt); + double scaledDiff = Math.min(this.totalDropDistance / this.predictedFallHeight, 1.0); + vertical *= Math.pow(this.descentSteepness, this.descentBlending) * Math.pow(scaledDiff, this.descentBlending); + } + + translation.set(this.climbForwardDirection.x, vertical, this.climbForwardDirection.z); + translation.mul(this.moveSpeed * dt); + Vector3dUtil.clipToZero(translation, this.getEpsilonSpeed()); + if (this.isBlendingHeading) { + heading = this.computeBlendHeading( + heading, + NPCPhysicsMath.headingFromDirection(this.climbForwardDirection.x, this.climbForwardDirection.z, heading), + dt, + this.moveSpeed, + steering.getRelativeTurnSpeed() + ); + } + + this.lockOrientation(steering, translation, heading); + this.validateSpeeds(ref, "Descending", componentAccessor); + this.validateTranslation(translation, "Descending"); + return dt; + } else { + this.validateSpeeds(ref, "Enter Walk/Drop", componentAccessor); + boolean canSteer = this.canSteer(ref, componentAccessor); + boolean isBlendResting = false; + float moveHeading; + if (canSteer) { + moveHeading = NPCPhysicsMath.headingFromDirection(direction.x, direction.z, heading); + if (this.isRequiresPreciseMovement() && !this.isBlendingHeading) { + float turnAngle = NPCPhysicsMath.turnAngle(heading, moveHeading); + float epsilon = this.getEpsilonAngle(); + if (turnAngle < -epsilon || turnAngle > epsilon) { + float maxRotation = (float)MathUtil.clamp( + dt * this.getCurrentMaxBodyRotationSpeed() * steering.getRelativeTurnSpeed(), 0.0, (float) (Math.PI / 2) + ); + turnAngle = MathUtil.clamp(turnAngle, -maxRotation, maxRotation); + heading = PhysicsMath.normalizeTurnAngle(heading + turnAngle); + translation.set(0.0, 0.0, 0.0); + steering.setYaw(heading); + this.isFullyRotated = false; + return dt; + } + + heading = moveHeading; + } + + this.moveSpeed = MathUtil.clamp(this.currentRelativeSpeed * maxHorizontalSpeed, 0.0, this.moveSpeed + dt * this.acceleration); + if (this.fallSpeed > 0.0) { + this.fallSpeed = 0.0; + } + + if (this.moveSpeed < this.getEpsilonSpeed()) { + this.moveSpeed = 0.0; + } else if (this.moveSpeed < this.minHorizontalSpeed) { + this.moveSpeed = this.minHorizontalSpeed; + } + + if (this.isBlendingHeading) { + if (this.blendRestTurnAngle > 0.0F) { + float turnAngle = this.computeBlendTurnAngle(heading, moveHeading); + if (Math.abs(turnAngle) > this.blendRestTurnAngle) { + isBlendResting = true; + } + + heading = this.computeBlendHeading(heading, moveHeading, dt, this.moveSpeed, turnAngle, steering.getRelativeTurnSpeed()); + } else { + heading = this.computeBlendHeading(heading, moveHeading, dt, this.moveSpeed, steering.getRelativeTurnSpeed()); + } + + steering.setYaw(heading); + this.isFullyRotated = true; + } else if (steering.hasYawOrDirection()) { + float yaw = steering.getYawOrDirection(); + heading = this.computeHeading(yaw, steering.getRelativeTurnSpeed(), heading, dt, false, false); + steering.setYaw(heading); + } else if (this.moveSpeed != 0.0) { + heading = this.computeHeading(moveHeading, steering.getRelativeTurnSpeed(), heading, dt, true, true); + moveHeading = heading; + steering.setYaw(heading); + } + + if (this.debugModeSteer) { + LOGGER.at(Level.INFO) + .log( + "=== Compute = t =%.4f v =%.4f h =%.4f mh=%.4f", + dt, + this.moveSpeed, + (180.0F / (float)Math.PI) * heading, + (180.0F / (float)Math.PI) * moveHeading + ); + } + } else { + Velocity velocityComponent = componentAccessor.getComponent(ref, Velocity.getComponentType()); + Vector3d velocity = velocityComponent.getVelocity(); + moveHeading = NPCPhysicsMath.headingFromDirection(velocity.x, velocity.z, heading); + if (!steering.hasYawOrDirection()) { + steering.setYaw(heading); + } + + if (this.maxHover > 0.0 && this.floatsDown) { + this.fallSpeed = this.climbSpeedConst; + if (this.fallSpeed != 0.0 && this.climbSpeedMult != 0.0) { + double prevFallSpeed = this.fallSpeed; + double deltaFallSpeed = this.climbSpeedMult * Math.pow(prevFallSpeed, this.climbSpeedPow); + this.fallSpeed += deltaFallSpeed; + } + } else { + double prevFallSpeed = this.fallSpeed; + this.fallSpeed = -NPCPhysicsMath.gravityDrag(-prevFallSpeed, 5.0 * this.gravity, dt, maxVerticalSpeed); + } + } + + if (this.moveSpeed > maxHorizontalSpeed) { + this.moveSpeed = maxHorizontalSpeed; + } + + if (this.fallSpeed > maxVerticalSpeed) { + this.fallSpeed = maxVerticalSpeed; + } else if (this.fallSpeed < -maxVerticalSpeed) { + this.fallSpeed = -maxVerticalSpeed; + } + + double appliedSpeed = this.moveSpeed; + if (isBlendResting) { + double maxSpeed = this.blendRestRelativeSpeed * this.getMaximumSpeed(); + if (appliedSpeed > maxSpeed) { + appliedSpeed = maxSpeed; + } + } + + translation.x = appliedSpeed * dt * PhysicsMath.headingX(moveHeading); + translation.z = appliedSpeed * dt * PhysicsMath.headingZ(moveHeading); + translation.y = -this.fallSpeed * dt; + double maxDistance = steering.getMaxDistance(); + if (this.canSteer(ref, componentAccessor) && maxDistance < Double.MAX_VALUE && maxDistance > 0.0) { + double lenSquared = NPCPhysicsMath.dotProduct(translation.x, translation.y, translation.z, this.getComponentSelector()); + double len = Math.sqrt(lenSquared); + if (len > maxDistance) { + translation.mul(maxDistance / len); + } + } + + this.validateSpeeds(ref, canSteer ? "Moving" : "Falling", componentAccessor); + this.validateTranslation(translation, canSteer ? "Moving" : "Falling"); + return dt; + } } } @@ -1307,33 +1340,16 @@ public class MotionControllerWalk extends MotionControllerBase { return fallSpeed; } - @Override - protected double executeMove( - @Nonnull Ref ref, @Nonnull Role role, double dt, @Nonnull Vector3d translation, @Nonnull ComponentAccessor componentAccessor + private boolean findMoveCollisions( + int walkingMaterials, boolean avoidDamage, @Nonnull Vector3d translation, @Nonnull ComponentAccessor componentAccessor ) { - if (this.debugModeMove) { - LOGGER.at(Level.INFO) - .log( - "Move: Execute pos=%s vel=%s onGround=%s blocked=%s canAct=%s avdDmg=%s relax=%s", - Vector3d.formatShortString(this.position), - Vector3d.formatShortString(translation), - this.onGround, - this.isObstructed, - this.canAct(ref, componentAccessor), - this.isAvoidingBlockDamage(), - this.isRelaxedMoveConstraints() - ); - } - - this.collisionResult.setCollisionByMaterial(4, this.isRelaxedMoveConstraints ? 13 : 5); + this.collisionResult.setCollisionByMaterial(4, walkingMaterials); this.resetObstructedFlags(); if (this.debugModeBlockCollisions) { this.collisionResult.setLogger(LOGGER); } - boolean avoidingBlockDamage = this.isAvoidingBlockDamage() && this.canAct(ref, componentAccessor); - boolean relaxMoveConstraints = this.isRelaxedMoveConstraints() || !this.canAct(ref, componentAccessor); - boolean oldState = this.collisionResult.setDamageBlocking(avoidingBlockDamage); + boolean oldState = this.collisionResult.setDamageBlocking(avoidDamage); boolean shortMove = !CollisionModule.findCollisions(this.collisionBoundingBox, this.position, translation, false, this.collisionResult, componentAccessor); this.collisionResult.setDamageBlocking(oldState); if (this.debugModeBlockCollisions) { @@ -1344,90 +1360,62 @@ public class MotionControllerWalk extends MotionControllerBase { this.dumpCollisionResults(); } - BlockCollisionData collision = this.getFirstCollision(this.collisionResult, avoidingBlockDamage); - double startSlide; - double endSlide; - if (this.collisionResult.isSliding) { - startSlide = this.collisionResult.slideStart; - endSlide = this.collisionResult.slideEnd; - if (this.onGround) { - collision = this.discardIgnorableSlideCollisions(this.collisionResult, collision, avoidingBlockDamage); - } - } else { - startSlide = Double.MAX_VALUE; - endSlide = Double.MAX_VALUE; + return shortMove; + } + + private double finishPhysicsCollision( + @Nonnull Ref ref, double dt, double triggerScale, @Nonnull ComponentAccessor componentAccessor + ) { + this.processTriggers(ref, this.collisionResult, triggerScale, componentAccessor); + dt *= triggerScale; + if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + LOGGER.at(Level.WARNING).log("Move: Physics moved to invalid position pos=%s/%s/%s", this.position.x, this.position.y, this.position.z); } - boolean tryClimb = false; - boolean needsRotation = this.isRequiresPreciseMovement() && !this.isFullyRotated; - this.lastValidPosition.assign(this.position); - if (collision == null) { - boolean wasOnGround = this.onGround; - double triggerScale; - if (wasOnGround && !(endSlide >= 1.0)) { - boolean canAct = this.canAct(ref, componentAccessor); - this.onGround = false; - if (canAct) { - if (needsRotation) { - this.onGround = true; - this.isObstructed = false; - endSlide = this.shortenSlide(translation, endSlide); - } else if (this.isRequiresDepthProbing()) { - this.tmpMovePosition.assign(this.position).addScaled(translation, endSlide); - if (this.isDropBlocked(this.tmpMovePosition, this.maxDropHeight, false, avoidingBlockDamage, this.isRelaxedMoveConstraints, componentAccessor) - ) - { - endSlide = this.shortenSlide(translation, endSlide); - this.isObstructed = true; - this.onGround = true; - } - } - } + return dt; + } - this.position.addScaled(translation, endSlide); - triggerScale = endSlide; - dt *= endSlide; + private double executePhysicsMove( + @Nonnull Ref ref, double dt, @Nonnull Vector3d translation, @Nonnull ComponentAccessor componentAccessor + ) { + this.findMoveCollisions(13, false, translation, componentAccessor); + BlockCollisionData collision = this.collisionResult.getFirstBlockCollision(); + this.lastValidPosition.set(this.position); + boolean wasOnGround = this.onGround; + if (collision == null) { + double startSlide; + double endSlide; + if (this.collisionResult.isSliding) { + startSlide = this.collisionResult.slideStart; + endSlide = this.collisionResult.slideEnd; } else { - this.position.add(translation); - this.onGround = startSlide <= 1.0 && endSlide >= 1.0; - triggerScale = 1.0; + startSlide = Double.MAX_VALUE; + endSlide = Double.MAX_VALUE; } - if (this.getMotionKind() != MotionKind.ASCENDING) { - if (!this.onGround) { - if (this.initiateDescend(translation, wasOnGround, "No collision", componentAccessor)) { - this.position.assign(this.lastValidPosition); - this.moveSpeed = 0.0; - triggerScale = 0.0; - } - } else { - this.setMotionKind( - !this.isObstructed && NPCPhysicsMath.projectedLengthSquared(translation, this.getComponentSelector()) > 0.0 - ? MotionKind.MOVING - : MotionKind.STANDING - ); + this.position.add(translation); + this.onGround = startSlide <= 1.0 && endSlide >= 1.0; + double triggerScale = 1.0; + if (this.onGround) { + this.setMotionKind(MotionKind.STANDING); + } else { + this.setMotionKind(MotionKind.DROPPING); + if (wasOnGround) { + this.fallStartHeight = this.position.y; + this.fallSpeed = 0.0; } } if (!this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { - if (this.getMotionKind() == MotionKind.DESCENDING) { - this.setMotionKind(MotionKind.DROPPING); - } - double scale = this.bisect(this.lastValidPosition, this.position, this.position, componentAccessor); triggerScale *= scale; if (this.debugModeMove) { LOGGER.at(Level.INFO) .log( - "Move: No collision, bisect onGround was/is=%s/%s slide=%s/%s scale=%s newpos=%s state=%s blocked=%s", - wasOnGround, - this.onGround, - startSlide, - endSlide, + "Move: Physics no-collision bisect scale=%s newpos=%s state=%s", scale, - Vector3d.formatShortString(this.position), - this.getMotionKind(), - this.isObstructed + Vector3dUtil.formatShortString(this.position), + this.getMotionKind() ); } @@ -1437,131 +1425,75 @@ public class MotionControllerWalk extends MotionControllerBase { } else if (this.debugModeMove) { LOGGER.at(Level.INFO) .log( - "Move: No collision onGround was/is=%s/%s slide=%s/%s newpos=%s state=%s", - wasOnGround, + "Move: Physics no-collision onGround=%s newpos=%s state=%s", this.onGround, - startSlide, - endSlide, - Vector3d.formatShortString(this.position), + Vector3dUtil.formatShortString(this.position), this.getMotionKind() ); } this.processTriggers(ref, this.collisionResult, triggerScale, componentAccessor); - } else { if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { - throw new IllegalStateException("Invalid position"); + LOGGER.at(Level.WARNING).log("Move: Physics moved to invalid position pos=%s/%s/%s", this.position.x, this.position.y, this.position.z); } + return dt; + } else { double triggerScalex = collision.collisionStart; - this.position.assign(collision.collisionPoint); - Vector3d remainingTranslation = this.lastValidPosition.clone().add(translation).subtract(collision.collisionPoint); - if (!remainingTranslation.equals(Vector3d.ZERO)) { - double t = remainingTranslation.dot(collision.collisionNormal); - remainingTranslation.addScaled(collision.collisionNormal, -t); - this.position.add(remainingTranslation); + this.position.set(collision.collisionPoint); + this.tmpMovePosition.set(this.lastValidPosition).add(translation).sub(collision.collisionPoint); + if (!this.tmpMovePosition.equals(Vector3dUtil.ZERO)) { + double t = this.tmpMovePosition.dot(collision.collisionNormal); + this.tmpMovePosition.fma(-t, collision.collisionNormal); + this.position.add(this.tmpMovePosition); } if (!this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { double scalex = this.bisect(this.lastValidPosition, this.position, this.position, componentAccessor); triggerScalex *= scalex; if (this.debugModeMove) { - LOGGER.at(Level.INFO).log("Move: Collision bisect=%s triggerScale=%s", scalex, triggerScalex); + LOGGER.at(Level.INFO).log("Move: Physics collision bisect=%s triggerScale=%s", scalex, triggerScalex); } } - if (collision.collisionNormal.equals(this.getWorldNormal())) { - if (this.onGround - && this.isRelaxedMoveConstraints - && !this.isDropBlocked(this.position, this.maxDropHeight, false, avoidingBlockDamage, this.isRelaxedMoveConstraints, componentAccessor)) { - this.initiateDescend(translation, true, "Collision", componentAccessor); - this.onGround = false; - } else { - if (this.onGround) { - this.isObstructed = true; - if (avoidingBlockDamage && collision.willDamage) { - triggerScalex = this.shortenMovement(triggerScalex); - } - } else if (this.getMotionKind() == MotionKind.DROPPING) { - double fallHeight = this.fallStartHeight - this.position.y; - if (fallHeight >= this.maxDropHeight) { - this.moveSpeed = 0.0; - } else if (fallHeight > this.maxClimbHeight) { - this.moveSpeed = this.moveSpeed * ((fallHeight - this.maxClimbHeight) / (this.maxDropHeight - this.maxClimbHeight)); - this.validateSpeeds(ref, "Collision on Ground", componentAccessor); - } - } - + if (!this.isForcePushed()) { + if (collision.collisionNormal.equals(this.getWorldNormal())) { this.setMotionKind(MotionKind.STANDING); - if (this.debugModeMove) { - LOGGER.at(Level.INFO) - .log( - "Move: Collision Up onGround=%s/%s blocked=%s newpos=%s state=%s", - this.onGround, - true, - this.isObstructed, - Vector3d.formatShortString(this.position), - this.getMotionKind() - ); - } - if (!this.onGround) { this.onGround = true; this.postReadPosition(ref, componentAccessor); } - this.onGround = true; - } - } else if (this.forceVelocity.equals(Vector3d.ZERO) && this.appliedVelocities.isEmpty()) { - if (collision.collisionNormal.equals(this.getWorldAntiNormal())) { + this.fallSpeed = 0.0; + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: Physics ground collision onGround=%s newpos=%s state=%s", + this.onGround, + Vector3dUtil.formatShortString(this.position), + this.getMotionKind() + ); + } + + return this.finishPhysicsCollision(ref, dt, triggerScalex, componentAccessor); + } else if (collision.collisionNormal.equals(this.getWorldAntiNormal())) { this.fallSpeed = 0.0; this.setMotionKind(MotionKind.DROPPING); if (this.debugModeMove) { LOGGER.at(Level.INFO) - .log( - "Move: No ext force, collision down, clear vert speed newpos=%s state=%s", - Vector3d.formatShortString(this.position), - this.getMotionKind() - ); + .log("Move: Physics ceiling collision newpos=%s state=%s", Vector3dUtil.formatShortString(this.position), this.getMotionKind()); } + + return this.finishPhysicsCollision(ref, dt, triggerScalex, componentAccessor); } else { + this.isObstructed = true; this.setMotionKind(MotionKind.STANDING); - if (!shortMove - && !needsRotation - && this.canAct(ref, componentAccessor) - && collision.blockType != null - && this.isClimbable(collision.blockType, collision.fluid, avoidingBlockDamage)) { - tryClimb = this.tryClimb(translation, avoidingBlockDamage, relaxMoveConstraints, componentAccessor); - if (this.debugModeMove) { - LOGGER.at(Level.INFO) - .log( - "Move: No ext force, collision horz, try climb h=%s succ=%s newpos(succ|fail)=%s|%s state=%s", - this.climbUpDistance, - tryClimb, - Vector3d.formatShortString(this.tmpMovePosition), - Vector3d.formatShortString(this.position), - this.getMotionKind() - ); - } - } else { - tryClimb = false; - if (avoidingBlockDamage && collision.willDamage) { - triggerScalex = this.shortenMovement(triggerScalex); - } - - if (this.debugModeMove) { - LOGGER.at(Level.INFO) - .log( - "Move: No ext force, collision horz, don't try climb onGround %s, block %s newpos=%s state=%s", - this.onGround, - collision.blockType != null, - Vector3d.formatShortString(this.position), - this.getMotionKind() - ); - } + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log("Move: Physics wall collision newpos=%s state=%s", Vector3dUtil.formatShortString(this.position), this.getMotionKind()); } - this.isObstructed = this.isFullyRotated && !tryClimb && !shortMove; + return this.finishPhysicsCollision(ref, dt, triggerScalex, componentAccessor); } } else { if (this.ignoreDamping) { @@ -1583,57 +1515,387 @@ public class MotionControllerWalk extends MotionControllerBase { this.setMotionKind(MotionKind.DROPPING); if (collision.collisionNormal.equals(this.getWorldAntiNormal())) { this.fallSpeed = 0.0; - if (this.debugModeMove) { - LOGGER.at(Level.INFO) - .log( - "Move: Ext force, collision down, clear force and vert speed newpos=%s state=%s", - Vector3d.formatShortString(this.position), - this.getMotionKind() - ); - } - } else if (this.debugModeMove) { - LOGGER.at(Level.INFO) - .log("Move: Ext force, collision not down, clear force newpos=%s state=%s", Vector3d.formatShortString(this.position), this.getMotionKind()); } - } - this.processTriggers(ref, this.collisionResult, triggerScalex, componentAccessor); - dt *= triggerScalex; - } + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: Physics force-push collision normal=%s onGround=%s newpos=%s state=%s", + collision.collisionNormal, + this.onGround, + Vector3dUtil.formatShortString(this.position), + this.getMotionKind() + ); + } - if (tryClimb && !this.isProcessTriggersHasMoved()) { - this.setMotionKind(MotionKind.ASCENDING); - this.climbUpDirection.assign(this.getWorldNormal()); - this.climbForwardDirection.assign(translation).normalize(); - this.climbSpeed = this.computeClimbSpeed(this.moveSpeed); - this.onGround = false; - if (this.debugModeMove) { - LOGGER.at(Level.INFO) - .log( - "Move: No ext force, collision horz, start climbing h=%s forw=%s state=%s", - this.climbUpDistance, - this.maxClimbForwardDistance, - this.getMotionKind() - ); - } - - if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { - throw new IllegalStateException("Invalid position"); + return this.finishPhysicsCollision(ref, dt, triggerScalex, componentAccessor); } } + } - if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { - LOGGER.at(Level.WARNING) + @Override + protected double executeMove( + @Nonnull Ref ref, @Nonnull Role role, double dt, @Nonnull Vector3d translation, @Nonnull ComponentAccessor componentAccessor + ) { + if (this.debugModeMove) { + LOGGER.at(Level.INFO) .log( - "Move: Walked on invalid position pos=%s/%s/%s overlaps=%s", - this.position.x, - this.position.y, - this.position.z, - this.collisionResult.getBlockCollisionCount() + "Move: Execute pos=%s vel=%s onGround=%s blocked=%s solidGround=%s avdDmg=%s relax=%s", + Vector3dUtil.formatShortString(this.position), + Vector3dUtil.formatShortString(translation), + this.onGround, + this.isObstructed, + this.isOnSolidGround(), + this.isAvoidingBlockDamage(), + !this.getRelaxedConstraints().isEmpty() ); } - return dt; + if (this.isAlive(ref, componentAccessor) && !this.isForcePushed()) { + this.effectiveMoveConstraints.clear(); + this.effectiveMoveConstraints.addAll(this.relaxedConstraints); + if (!this.isOnSolidGround()) { + this.effectiveMoveConstraints.add(RelaxedConstraint.DAMAGE); + this.effectiveMoveConstraints.add(RelaxedConstraint.WADE); + } + + if (this.inWater) { + this.effectiveMoveConstraints.add(RelaxedConstraint.WADE); + } + + if (!role.couldBreatheCached()) { + this.effectiveMoveConstraints.add(RelaxedConstraint.BREATHE); + } + + boolean avoidDamage = !this.effectiveMoveConstraints.contains(RelaxedConstraint.DAMAGE); + boolean allowWade = this.effectiveMoveConstraints.contains(RelaxedConstraint.WADE); + double effectiveDropHeight = this.effectiveMaxDropHeight(this.effectiveMoveConstraints); + boolean shortMove = this.findMoveCollisions(allowWade ? 13 : 5, avoidDamage, translation, componentAccessor); + BlockCollisionData collision = this.collisionResult.getFirstBlockCollision(); + double startSlide; + double endSlide; + if (this.collisionResult.isSliding) { + startSlide = this.collisionResult.slideStart; + endSlide = this.collisionResult.slideEnd; + if (this.onGround) { + collision = this.discardIgnorableSlideCollisions(this.collisionResult, collision, this.effectiveMoveConstraints); + } + } else { + startSlide = Double.MAX_VALUE; + endSlide = Double.MAX_VALUE; + } + + boolean tryClimb = false; + boolean needsRotation = this.isRequiresPreciseMovement() && !this.isFullyRotated; + this.lastValidPosition.set(this.position); + boolean wasOnGround = this.onGround; + if (collision == null) { + double triggerScale; + if (wasOnGround && !(endSlide >= 1.0)) { + if (this.isOnSolidGround() && needsRotation) { + this.isObstructed = false; + endSlide = this.shortenSlide(translation, endSlide); + } else { + this.onGround = false; + this.tmpMovePosition.set(this.position).fma(endSlide, translation); + if (this.isDropBlocked(this.tmpMovePosition, effectiveDropHeight, false, this.effectiveMoveConstraints, componentAccessor)) { + ChunkStore chunkStore = componentAccessor.getExternalData().getWorld().getChunkStore(); + if (this.isValidWalkPosition( + chunkStore, this.lastValidPosition.x, this.lastValidPosition.y, this.lastValidPosition.z, this.effectiveMoveConstraints + )) { + endSlide = this.shortenSlide(translation, endSlide); + this.isObstructed = true; + this.onGround = true; + } + } + } + + this.position.fma(endSlide, translation); + triggerScale = endSlide; + dt *= endSlide; + if (this.onGround && this.validateGroundPosition(effectiveDropHeight, this.effectiveMoveConstraints, componentAccessor)) { + triggerScale = 0.0; + } + } else { + this.position.add(translation); + this.onGround = startSlide <= 1.0 && endSlide >= 1.0; + triggerScale = 1.0; + if (wasOnGround && this.onGround) { + ChunkStore chunkStore = componentAccessor.getExternalData().getWorld().getChunkStore(); + if (!this.isValidWalkPosition(chunkStore, this.position.x, this.position.y, this.position.z, this.effectiveMoveConstraints) + && this.isValidWalkPosition( + chunkStore, this.lastValidPosition.x, this.lastValidPosition.y, this.lastValidPosition.z, this.effectiveMoveConstraints + )) { + double scale = this.bisect( + this.lastValidPosition, + this.position, + this, + (_this, pos) -> _this.isValidWalkPosition(chunkStore, pos.x, pos.y, pos.z, this.effectiveMoveConstraints), + this.position + ); + triggerScale *= scale; + if (scale == 0.0) { + this.isObstructed = true; + } + } + } + } + + if (this.getMotionKind() != MotionKind.ASCENDING) { + if (this.onGround) { + this.setMotionKind( + !this.isObstructed && NPCPhysicsMath.projectedLengthSquared(translation, this.getComponentSelector()) > 0.0 + ? MotionKind.MOVING + : MotionKind.STANDING + ); + } else if (this.initiateDescend(translation, wasOnGround, "No collision", componentAccessor)) { + this.position.set(this.lastValidPosition); + this.moveSpeed = 0.0; + triggerScale = 0.0; + } + } + + if (!this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + if (this.getMotionKind() == MotionKind.DESCENDING) { + this.setMotionKind(MotionKind.DROPPING); + } + + double scale = this.bisect(this.lastValidPosition, this.position, this.position, componentAccessor); + triggerScale *= scale; + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: No collision, bisect onGround was/is=%s/%s slide=%s/%s scale=%s newpos=%s state=%s blocked=%s", + wasOnGround, + this.onGround, + startSlide, + endSlide, + scale, + Vector3dUtil.formatShortString(this.position), + this.getMotionKind(), + this.isObstructed + ); + } + + if (scale == 0.0) { + this.isObstructed = true; + } + } else if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: No collision onGround was/is=%s/%s slide=%s/%s newpos=%s state=%s", + wasOnGround, + this.onGround, + startSlide, + endSlide, + Vector3dUtil.formatShortString(this.position), + this.getMotionKind() + ); + } + + this.processTriggers(ref, this.collisionResult, triggerScale, componentAccessor); + if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + LOGGER.at(Level.WARNING) + .log( + "Move: Walked on invalid position pos=%s/%s/%s overlaps=%s", + this.position.x, + this.position.y, + this.position.z, + this.collisionResult.getBlockCollisionCount() + ); + } + + return dt; + } else if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + throw new IllegalStateException("Invalid position"); + } else { + double triggerScalex = collision.collisionStart; + this.position.set(collision.collisionPoint); + this.tmpMovePosition.set(this.lastValidPosition).add(translation).sub(collision.collisionPoint); + if (!this.tmpMovePosition.equals(Vector3dUtil.ZERO)) { + double t = this.tmpMovePosition.dot(collision.collisionNormal); + this.tmpMovePosition.fma(-t, collision.collisionNormal); + this.position.add(this.tmpMovePosition); + } + + if (!this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + double scalex = this.bisect(this.lastValidPosition, this.position, this.position, componentAccessor); + triggerScalex *= scalex; + if (this.debugModeMove) { + LOGGER.at(Level.INFO).log("Move: Collision bisect=%s triggerScale=%s", scalex, triggerScalex); + } + } + + if (collision.collisionNormal.equals(this.getWorldNormal())) { + if (this.onGround && !this.isDropBlocked(this.position, effectiveDropHeight, false, this.effectiveMoveConstraints, componentAccessor)) { + this.initiateDescend(translation, true, "Collision", componentAccessor); + this.onGround = false; + } else { + if (this.onGround) { + this.isObstructed = true; + if (avoidDamage && collision.willDamage) { + triggerScalex = this.shortenMovement(triggerScalex); + } + } else if (this.getMotionKind() == MotionKind.DROPPING) { + double fallHeight = this.fallStartHeight - this.position.y; + if (fallHeight >= this.maxDropHeight) { + this.moveSpeed = 0.0; + } else if (fallHeight > this.maxClimbHeight) { + this.moveSpeed = this.moveSpeed * ((fallHeight - this.maxClimbHeight) / (this.maxDropHeight - this.maxClimbHeight)); + this.validateSpeeds(ref, "Collision on Ground", componentAccessor); + } + } + + this.setMotionKind(MotionKind.STANDING); + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: Collision Up onGround is/was=%s/%s blocked=%s newpos=%s state=%s", + this.onGround, + wasOnGround, + this.isObstructed, + Vector3dUtil.formatShortString(this.position), + this.getMotionKind() + ); + } + + if (!this.onGround) { + this.onGround = true; + this.postReadPosition(ref, componentAccessor); + } + } + + if (this.onGround && this.validateGroundPosition(effectiveDropHeight, this.effectiveMoveConstraints, componentAccessor)) { + triggerScalex = 0.0; + } + + this.processTriggers(ref, this.collisionResult, triggerScalex, componentAccessor); + dt *= triggerScalex; + if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + LOGGER.at(Level.WARNING) + .log( + "Move: Walked on invalid position pos=%s/%s/%s overlaps=%s", + this.position.x, + this.position.y, + this.position.z, + this.collisionResult.getBlockCollisionCount() + ); + } + + return dt; + } else if (collision.collisionNormal.equals(this.getWorldAntiNormal())) { + this.fallSpeed = 0.0; + this.setMotionKind(MotionKind.DROPPING); + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: No ext force, collision down, clear vert speed newpos=%s state=%s", + Vector3dUtil.formatShortString(this.position), + this.getMotionKind() + ); + } + + if (this.onGround && this.validateGroundPosition(effectiveDropHeight, this.effectiveMoveConstraints, componentAccessor)) { + triggerScalex = 0.0; + } + + this.processTriggers(ref, this.collisionResult, triggerScalex, componentAccessor); + dt *= triggerScalex; + if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + LOGGER.at(Level.WARNING) + .log( + "Move: Walked on invalid position pos=%s/%s/%s overlaps=%s", + this.position.x, + this.position.y, + this.position.z, + this.collisionResult.getBlockCollisionCount() + ); + } + + return dt; + } else { + this.setMotionKind(MotionKind.STANDING); + if (!shortMove + && !needsRotation + && this.isOnSolidGround() + && collision.blockType != null + && this.isClimbable(collision.blockType, collision.fluid, this.effectiveMoveConstraints)) { + tryClimb = this.tryClimb(translation, this.effectiveMoveConstraints, componentAccessor); + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: No ext force, collision horz, try climb h=%s succ=%s newpos(succ|fail)=%s|%s state=%s", + this.climbUpDistance, + tryClimb, + Vector3dUtil.formatShortString(this.tmpMovePosition), + Vector3dUtil.formatShortString(this.position), + this.getMotionKind() + ); + } + } else { + tryClimb = false; + if (avoidDamage && collision.willDamage) { + triggerScalex = this.shortenMovement(triggerScalex); + } + + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: No ext force, collision horz, don't try climb onGround %s, block %s newpos=%s state=%s", + this.onGround, + collision.blockType != null, + Vector3dUtil.formatShortString(this.position), + this.getMotionKind() + ); + } + } + + this.isObstructed = this.isFullyRotated && !tryClimb && !shortMove; + if (this.onGround && this.validateGroundPosition(effectiveDropHeight, this.effectiveMoveConstraints, componentAccessor)) { + tryClimb = false; + triggerScalex = 0.0; + } + + this.processTriggers(ref, this.collisionResult, triggerScalex, componentAccessor); + dt *= triggerScalex; + if (tryClimb && !this.isProcessTriggersHasMoved()) { + this.setMotionKind(MotionKind.ASCENDING); + this.climbUpDirection.set(this.getWorldNormal()); + this.climbForwardDirection.set(translation).normalize(); + this.climbSpeed = this.computeClimbSpeed(this.moveSpeed); + this.onGround = false; + if (this.debugModeMove) { + LOGGER.at(Level.INFO) + .log( + "Move: No ext force, collision horz, start climbing h=%s forw=%s state=%s", + this.climbUpDistance, + this.maxClimbForwardDistance, + this.getMotionKind() + ); + } + + if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + throw new IllegalStateException("Invalid position"); + } + } + + if (this.debugModeValidatePositions && !this.isValidPosition(this.position, this.collisionResult, componentAccessor)) { + LOGGER.at(Level.WARNING) + .log( + "Move: Walked on invalid position pos=%s/%s/%s overlaps=%s", + this.position.x, + this.position.y, + this.position.z, + this.collisionResult.getBlockCollisionCount() + ); + } + + return dt; + } + } + } else { + return this.executePhysicsMove(ref, dt, translation, componentAccessor); + } } @Override @@ -1645,7 +1907,11 @@ public class MotionControllerWalk extends MotionControllerBase { if (distanceLeftSquared == 0.0) { return 0.0; } else { - this.collisionResult.setCollisionByMaterial(4, probeMoveData.isRelaxedMoveConstraints ? 13 : 5); + EnumSet probeConstraints = probeMoveData.getRelaxedConstraints(); + boolean avoidDamage = !probeConstraints.contains(RelaxedConstraint.DAMAGE); + boolean allowWade = probeConstraints.contains(RelaxedConstraint.WADE); + double effectiveDropHeight = this.effectiveMaxDropHeight(probeConstraints); + this.collisionResult.setCollisionByMaterial(4, allowWade ? 13 : 5); Vector3d probePosition = probeMoveData.probePosition; Vector3d initialPosition = probeMoveData.initialPosition; Vector3d targetPosition = probeMoveData.targetPosition; @@ -1658,21 +1924,20 @@ public class MotionControllerWalk extends MotionControllerBase { onGround = collisionModule.validatePosition(world, this.collisionBoundingBox, probePosition, this.collisionResult) == 1; } - directionComponentSelector.assign(this.getComponentSelector()); - probeMovement.scale(directionComponentSelector); - boolean stopOnDamageBlocks = probeMoveData.isAvoidingBlockDamage; - boolean relaxedMoveConstraints = probeMoveData.isRelaxedMoveConstraints; + directionComponentSelector.set(this.getComponentSelector()); + probeMovement.mul(directionComponentSelector); if (saveSegments) { probeMoveData.addStartSegment(initialPosition, true); } if (!onGround) { - if (this.isDropBlocked(probePosition, this.maxDropHeight, true, stopOnDamageBlocks, relaxedMoveConstraints, componentAccessor)) { + if (this.isDropBlocked(probePosition, effectiveDropHeight, true, probeConstraints, componentAccessor)) { if (saveSegments) { probeMoveData.addBlockedDropSegment(probePosition, this.waypointDistance(initialPosition, probePosition)); return probeMoveData.getLastDistance(); } + probeMoveData.edgeBlocked = true; return this.waypointDistance(initialPosition, probePosition); } @@ -1686,7 +1951,7 @@ public class MotionControllerWalk extends MotionControllerBase { this.collisionResult.setLogger(LOGGER); } - boolean oldState = this.collisionResult.setDamageBlocking(stopOnDamageBlocks); + boolean oldState = this.collisionResult.setDamageBlocking(avoidDamage); boolean shortMove = !CollisionModule.findCollisions( this.collisionBoundingBox, probePosition, probeMovement, this.collisionResult, componentAccessor ); @@ -1699,8 +1964,8 @@ public class MotionControllerWalk extends MotionControllerBase { LOGGER.at(Level.INFO) .log( "Probe Step: pos=%s mov=%s left=%s", - Vector3d.formatShortString(probePosition), - Vector3d.formatShortString(probeMovement), + Vector3dUtil.formatShortString(probePosition), + Vector3dUtil.formatShortString(probeMovement), Math.sqrt(distanceLeftSquared) ); } @@ -1709,18 +1974,18 @@ public class MotionControllerWalk extends MotionControllerBase { this.dumpCollisionResults(); } - BlockCollisionData collision = this.getFirstCollision(this.collisionResult, stopOnDamageBlocks); + BlockCollisionData collision = this.collisionResult.getFirstBlockCollision(); double endSlide = this.collisionResult.isSliding ? this.collisionResult.slideEnd : Double.MAX_VALUE; if (this.collisionResult.isSliding) { - collision = this.discardIgnorableSlideCollisions(this.collisionResult, collision, stopOnDamageBlocks); + collision = this.discardIgnorableSlideCollisions(this.collisionResult, collision, probeConstraints); } if (collision != null && collision.collisionStart <= endSlide) { - if (stopOnDamageBlocks && collision.willDamage) { + if (avoidDamage && collision.willDamage) { this.shortenMovement(probePosition, collision.collisionPoint, probePosition); distanceLeftSquared = 0.0; } else { - probePosition.assign(collision.collisionPoint); + probePosition.set(collision.collisionPoint); distanceLeftSquared = this.updateMovementVector(probePosition, probeMovement, targetPosition, directionComponentSelector); } @@ -1742,7 +2007,7 @@ public class MotionControllerWalk extends MotionControllerBase { int blockId = collision.blockId; if (collision.blockType == null || distanceLeftSquared < 0.010000000000000002 - || !this.isClimbable(collision.blockType, collision.fluid, stopOnDamageBlocks)) { + || !this.isClimbable(collision.blockType, collision.fluid, probeConstraints)) { if (saveSegments) { probeMoveData.changeSegmentToBlockedWall(); } @@ -1753,15 +2018,7 @@ public class MotionControllerWalk extends MotionControllerBase { double climbHeight = shortMove ? 0.0 : this.computeClimbHeight( - probePosition, - probeMovement, - this.maxClimbHeight, - 0.1, - null, - this.tmpClimbHeightResults, - stopOnDamageBlocks, - relaxedMoveConstraints, - componentAccessor + probePosition, probeMovement, this.maxClimbHeight, 0.1, null, this.tmpClimbHeightResults, probeConstraints, componentAccessor ); if (climbHeight <= 0.0) { if (saveSegments) { @@ -1771,7 +2028,7 @@ public class MotionControllerWalk extends MotionControllerBase { return this.waypointDistance(initialPosition, probePosition); } - probePosition.addScaled(this.getWorldNormal(), climbHeight); + probePosition.fma(climbHeight, this.getWorldNormal()); if (saveSegments) { probeMoveData.addClimbSegment(probePosition, this.waypointDistance(initialPosition, probePosition), blockId); } @@ -1780,7 +2037,22 @@ public class MotionControllerWalk extends MotionControllerBase { } else { if (endSlide >= 1.0) { probePosition.add(probeMovement); - probeMovement.assign(Vector3d.ZERO); + if (!allowWade) { + ChunkStore chunkStore = componentAccessor.getExternalData().getWorld().getChunkStore(); + if (!this.isValidWalkPosition(chunkStore, probePosition.x, probePosition.y, probePosition.z, probeConstraints) + && this.isValidWalkPosition(chunkStore, initialPosition.x, initialPosition.y, initialPosition.z, probeConstraints)) { + probePosition.sub(probeMovement); + if (saveSegments) { + probeMoveData.addBlockedGroundSegment( + probePosition, this.waypointDistance(initialPosition, probePosition), this.getWorldNormal(), -1 + ); + } + + return this.waypointDistance(initialPosition, probePosition); + } + } + + probeMovement.set(Vector3dUtil.ZERO); double distance = this.waypointDistance(initialPosition, probePosition); if (saveSegments) { probeMoveData.addMoveSegment(probePosition, true, distance); @@ -1789,19 +2061,23 @@ public class MotionControllerWalk extends MotionControllerBase { return distance; } - probePosition.addScaled(probeMovement, endSlide); + probePosition.fma(endSlide, probeMovement); if (saveSegments) { probeMoveData.addHitEdgeSegment(probePosition, this.waypointDistance(initialPosition, probePosition)); } - if (this.isDropBlocked(probePosition, this.maxDropHeight, true, stopOnDamageBlocks, relaxedMoveConstraints, componentAccessor)) { - probeMovement.assign(targetPosition).subtract(probePosition).scale(directionComponentSelector); - if (saveSegments) { - probeMoveData.changeSegmentToBlockedEdge(); - return probeMoveData.getLastDistance(); - } + if (this.isDropBlocked(probePosition, effectiveDropHeight, true, probeConstraints, componentAccessor)) { + ChunkStore chunkStore = componentAccessor.getExternalData().getWorld().getChunkStore(); + if (allowWade || this.isValidWalkPosition(chunkStore, initialPosition.x, initialPosition.y, initialPosition.z, probeConstraints)) { + probeMovement.set(targetPosition).sub(probePosition).mul(directionComponentSelector); + if (saveSegments) { + probeMoveData.changeSegmentToBlockedEdge(); + return probeMoveData.getLastDistance(); + } - return this.waypointDistance(initialPosition, probePosition); + probeMoveData.edgeBlocked = true; + return this.waypointDistance(initialPosition, probePosition); + } } if (saveSegments) { @@ -1908,18 +2184,19 @@ public class MotionControllerWalk extends MotionControllerBase { protected double updateMovementVector( @Nonnull Vector3d probePosition, @Nonnull Vector3d probeMovement, @Nonnull Vector3d targetPosition, @Nonnull Vector3d directionComponentSelector ) { - probeMovement.assign(targetPosition).subtract(probePosition).scale(directionComponentSelector); + probeMovement.set(targetPosition).sub(probePosition).mul(directionComponentSelector); return this.waypointDistanceSquared(probePosition, targetPosition); } @Nullable private BlockCollisionData discardIgnorableSlideCollisions( - @Nonnull CollisionResult collisionResult, @Nullable BlockCollisionData startCollision, boolean acknowledgeDamage + @Nonnull CollisionResult collisionResult, @Nullable BlockCollisionData startCollision, @Nonnull EnumSet constraints ) { + boolean avoidDamage = !constraints.contains(RelaxedConstraint.DAMAGE); double endSlide = collisionResult.slideEnd; while (startCollision != null) { - if (acknowledgeDamage && startCollision.willDamage) { + if (avoidDamage && startCollision.willDamage) { return startCollision; } @@ -1934,26 +2211,46 @@ public class MotionControllerWalk extends MotionControllerBase { return null; } - @Nullable - private BlockCollisionData getFirstCollision(@Nonnull CollisionResult collisionResult, boolean acknowledgeDamage) { - return collisionResult.getFirstBlockCollision(); - } - private double bisect( @Nonnull Vector3d validPosition, @Nonnull Vector3d invalidPosition, @Nonnull Vector3d result, @Nonnull ComponentAccessor componentAccessor ) { if (!this.isValidPosition(validPosition, this.collisionResult, componentAccessor)) { - result.assign(invalidPosition); + result.set(invalidPosition); return 1.0; } else { return this.bisect(validPosition, invalidPosition, this, (_this, pos) -> _this.isValidPosition(pos, _this.collisionResult, componentAccessor), result); } } + private boolean validateGroundPosition( + double effectiveDropHeight, @Nonnull EnumSet effectiveMoveConstraints, @Nonnull ComponentAccessor componentAccessor + ) { + if (this.isDropBlocked(this.position, effectiveDropHeight, false, effectiveMoveConstraints, componentAccessor)) { + this.position.set(this.lastValidPosition); + this.isObstructed = true; + return true; + } else { + ChunkStore chunkStore = componentAccessor.getExternalData().getWorld().getChunkStore(); + if (!this.isValidWalkPosition(chunkStore, this.position.x, this.position.y, this.position.z, effectiveMoveConstraints) + && this.isValidWalkPosition(chunkStore, this.lastValidPosition.x, this.lastValidPosition.y, this.lastValidPosition.z, effectiveMoveConstraints)) { + this.position.set(this.lastValidPosition); + this.isObstructed = true; + return true; + } else { + return false; + } + } + } + private double shortenSlide(@Nonnull Vector3d translation, double endSlide) { + Vector3d cs = this.getComponentSelector(); + double hwX = (this.collisionBoundingBox.max.x - this.collisionBoundingBox.min.x) * 0.1 * cs.x; + double hwY = (this.collisionBoundingBox.max.y - this.collisionBoundingBox.min.y) * 0.1 * cs.y; + double hwZ = (this.collisionBoundingBox.max.z - this.collisionBoundingBox.min.z) * 0.1 * cs.z; + double edgeMargin = Math.max(Math.max(hwX, Math.max(hwY, hwZ)), 0.05); double moveLength = translation.length() * endSlide; - if (moveLength > 0.001) { - endSlide = endSlide * (moveLength - 0.001) / moveLength; + if (moveLength > edgeMargin) { + endSlide = endSlide * (moveLength - edgeMargin) / moveLength; } else { endSlide = 0.0; } @@ -1962,11 +2259,11 @@ public class MotionControllerWalk extends MotionControllerBase { } private double shortenMovement(@Nonnull Vector3d start, @Nonnull Vector3d end, @Nonnull Vector3d result) { - double moveLength = end.distanceTo(start); - if (moveLength <= 0.001) { + double moveLength = end.distance(start); + if (moveLength <= 0.05) { return 0.0; } else { - moveLength = (moveLength - 0.001) / moveLength; + moveLength = (moveLength - 0.05) / moveLength; NPCPhysicsMath.lerp(start, end, moveLength, result); return moveLength; } @@ -1984,7 +2281,7 @@ public class MotionControllerWalk extends MotionControllerBase { private void validateTranslation(@Nonnull Vector3d translation, String kind) { if (this.debugModeValidateMath) { - boolean b = NPCPhysicsMath.isValid(translation) && translation.squaredLength() < 1000000.0; + boolean b = NPCPhysicsMath.isValid(translation) && translation.lengthSquared() < 1000000.0; if (!b) { throw new IllegalStateException( String.format( @@ -1993,12 +2290,12 @@ public class MotionControllerWalk extends MotionControllerBase { translation.toString(), this.moveSpeed, this.fallSpeed, - Vector3d.formatShortString(this.position) + Vector3dUtil.formatShortString(this.position) ) ); } - } else if (translation.squaredLength() > 1000000.0) { - translation.assign(Vector3d.ZERO); + } else if (translation.lengthSquared() > 1000000.0) { + translation.zero(); } } @@ -2009,13 +2306,13 @@ public class MotionControllerWalk extends MotionControllerBase { { throw new IllegalStateException( String.format( - "Walk - Invalid speed path=%s, moveSpeed=%s, fallSpeed=%s, onGround=%s canAct=%s, pos=%s, motionKind=%s", + "Walk - Invalid speed path=%s, moveSpeed=%s, fallSpeed=%s, onGround=%s canSteer=%s, pos=%s, motionKind=%s", kind, this.moveSpeed, this.fallSpeed, this.onGround, - this.canAct(ref, componentAccessor), - Vector3d.formatShortString(this.position), + this.canSteer(ref, componentAccessor), + Vector3dUtil.formatShortString(this.position), this.getMotionKind() ) ); @@ -2074,12 +2371,12 @@ public class MotionControllerWalk extends MotionControllerBase { } } - translation.assign(climbDirection).scale(distance); + translation.set(climbDirection).mul(distance); return climbDistance; } private void computeDescendDirection(@Nonnull Vector3d translation) { - this.climbForwardDirection.assign(this.getWorldAntiNormal()); + this.climbForwardDirection.set(this.getWorldAntiNormal()); if (!(this.descendFlatness <= 0.0)) { double forwardDistance = NPCPhysicsMath.dotProduct(translation.x, 0.0, translation.z); if (!(forwardDistance <= 1.0E-12)) { @@ -2091,7 +2388,7 @@ public class MotionControllerWalk extends MotionControllerBase { double newForwardDistance = Math.sqrt(NPCPhysicsMath.dotProduct(this.climbForwardDirection.x, 0.0, this.climbForwardDirection.z)); if (newForwardDistance > 1.0E-6) { double compensation = 1.0 + (1.0 / newForwardDistance - 1.0) * this.descendSpeedCompensation; - this.climbForwardDirection.scale(compensation); + this.climbForwardDirection.mul(compensation); } } } @@ -2107,7 +2404,7 @@ public class MotionControllerWalk extends MotionControllerBase { } private boolean tryClimb( - @Nonnull Vector3d translation, boolean avoidingBlockDamage, boolean relaxMoveConstraints, @Nonnull ComponentAccessor componentAccessor + @Nonnull Vector3d translation, @Nonnull EnumSet constraints, @Nonnull ComponentAccessor componentAccessor ) { boolean canJump = this.jumpHeight > 0.0; this.currentClimbForwardDistance = 0.0; @@ -2119,8 +2416,7 @@ public class MotionControllerWalk extends MotionControllerBase { this.maxClimbForwardDistance, null, this.tmpClimbHeightResults, - avoidingBlockDamage, - relaxMoveConstraints, + constraints, componentAccessor ); double targetJumpHeight = this.climbUpDistance + this.jumpHeight; @@ -2131,14 +2427,14 @@ public class MotionControllerWalk extends MotionControllerBase { this.currentJumpHeight = targetJumpHeight; this.jumpDropHeight = this.currentJumpHeight - this.climbUpDistance; this.jumpBlockHeight = this.climbUpDistance; - this.jumpDropDirection.assign(this.getWorldAntiNormal()); + this.jumpDropDirection.set(this.getWorldAntiNormal()); double baseClimbUpDistance = this.climbUpDistance; this.climbUpDistance = this.currentJumpHeight; - this.tmpClimbMovement.assign(translation).setLength(0.4); - this.tmpMovePosition.assign(this.position).add(0.0, baseClimbUpDistance, 0.0); - double forwardMax = this.maxMoveFactor(this.tmpMovePosition, this.tmpClimbMovement, avoidingBlockDamage, componentAccessor); + this.tmpClimbMovement.set(translation).normalize(0.4); + this.tmpMovePosition.set(this.position).add(0.0, baseClimbUpDistance, 0.0); + double forwardMax = this.maxMoveFactor(this.tmpMovePosition, this.tmpClimbMovement, constraints, componentAccessor); this.maxClimbForwardDistance += forwardMax * 0.4; - this.tmpMovePosition.addScaled(this.tmpClimbMovement, forwardMax); + this.tmpMovePosition.fma(forwardMax, this.tmpClimbMovement); this.jumping = this.maxClimbForwardDistance >= this.minJumpDistance; } else { this.jumping = false; @@ -2154,37 +2450,37 @@ public class MotionControllerWalk extends MotionControllerBase { double forward, @Nullable Vector3d targetPosition, @Nonnull Vector2d results, - boolean acknowledgeDamage, - boolean relaxMoveConstraints, + @Nonnull EnumSet constraints, @Nonnull ComponentAccessor componentAccessor ) { World world = componentAccessor.getExternalData().getWorld(); ChunkStore chunkStore = world.getChunkStore(); - this.tmpResults.setCollisionByMaterial(4, relaxMoveConstraints ? 13 : 5); - results.assign(0.0); - Vector3d worldNormal = this.getWorldNormal(); - this.tmpClimbMovement.assign(worldNormal).scale(height); - double scale = this.maxMoveFactor(position, this.tmpClimbMovement, acknowledgeDamage, componentAccessor); + boolean allowWade = constraints.contains(RelaxedConstraint.WADE); + this.tmpResults.setCollisionByMaterial(4, allowWade ? 13 : 5); + results.zero(); + Vector3dc worldNormal = this.getWorldNormal(); + this.tmpClimbMovement.set(worldNormal).mul(height); + double scale = this.maxMoveFactor(position, this.tmpClimbMovement, constraints, componentAccessor); height *= scale; if (height == 0.0) { return 0.0; } else { - this.tmpClimbMovement.assign(direction).setLength(forward); - this.tmpClimbPosition.assign(position).add(this.tmpClimbMovement); - this.tmpClimbMovement.assign(worldNormal).scale(height); + this.tmpClimbMovement.set(direction).normalize(forward); + this.tmpClimbPosition.set(position).add(this.tmpClimbMovement); + this.tmpClimbMovement.set(worldNormal).mul(height); boolean saveComputeOverlaps = this.tmpResults.isComputeOverlaps(); this.tmpResults.setComputeOverlaps(true); CollisionModule.get(); CollisionModule.findCollisions(this.collisionBoundingBox, this.tmpClimbPosition, this.tmpClimbMovement, false, this.tmpResults, componentAccessor); this.tmpResults.setComputeOverlaps(saveComputeOverlaps); BlockCollisionData collisionData = this.tmpResults.getFirstBlockCollision(); - Vector3d worldAntiNormal = this.getWorldAntiNormal(); + Vector3dc worldAntiNormal = this.getWorldAntiNormal(); double top = 0.0; double high; for (high = 1.0; collisionData != null; collisionData = this.tmpResults.forgetFirstBlockCollision()) { BlockType blockType = collisionData.blockType; - if (blockType == null || !this.isClimbable(blockType, collisionData.fluid, acknowledgeDamage)) { + if (blockType == null || !this.isClimbable(blockType, collisionData.fluid, constraints)) { break; } @@ -2206,17 +2502,14 @@ public class MotionControllerWalk extends MotionControllerBase { if (top == 0.0) { return 0.0; } else { - this.tmpClimbPosition.addScaled(this.tmpClimbMovement, top); - relaxMoveConstraints |= this.role.isBreathesInAir(); - if (!this.isValidWalkPosition( - chunkStore, this.tmpClimbPosition.x, this.tmpClimbPosition.y, this.tmpClimbPosition.z, acknowledgeDamage, relaxMoveConstraints - )) { + this.tmpClimbPosition.fma(top, this.tmpClimbMovement); + if (!this.isValidWalkPosition(chunkStore, this.tmpClimbPosition.x, this.tmpClimbPosition.y, this.tmpClimbPosition.z, constraints)) { return 0.0; } else { double bottom = height * top; - results.assign(bottom, height * high); + results.set(bottom, height * high); if (targetPosition != null) { - targetPosition.assign(this.tmpClimbPosition); + targetPosition.set(this.tmpClimbPosition); } return bottom; @@ -2225,16 +2518,20 @@ public class MotionControllerWalk extends MotionControllerBase { } } + private double effectiveMaxDropHeight(@Nonnull EnumSet constraints) { + return constraints.contains(RelaxedConstraint.DROP) ? this.maxDropHeightRelaxed : this.maxDropHeight; + } + private boolean isDropBlocked( @Nonnull Vector3d position, double maxDropHeight, boolean updatePosition, - boolean acknowledgeDamage, - boolean relaxedMoveConstraints, + @Nonnull EnumSet constraints, @Nonnull ComponentAccessor componentAccessor ) { World world = componentAccessor.getExternalData().getWorld(); ChunkStore chunkStore = world.getChunkStore(); + boolean avoidDamage = !constraints.contains(RelaxedConstraint.DAMAGE); if (this.debugModeValidatePositions && !this.isValidPosition(position, this.tmpResults, componentAccessor)) { throw new IllegalStateException("Invalid position"); } else { @@ -2242,8 +2539,8 @@ public class MotionControllerWalk extends MotionControllerBase { if (collision == null) { return true; } else { - this.tmpClimbPosition.assign(collision.collisionPoint); - if (acknowledgeDamage) { + this.tmpClimbPosition.set(collision.collisionPoint); + if (avoidDamage) { double collisionStart = collision.collisionStart; do { @@ -2259,10 +2556,7 @@ public class MotionControllerWalk extends MotionControllerBase { } while (collision != null && collision.collisionStart <= collisionStart); } - relaxedMoveConstraints |= this.role.isBreathesInWater(); - if (!this.isValidWalkPosition( - chunkStore, this.tmpClimbPosition.x, this.tmpClimbPosition.y, this.tmpClimbPosition.z, acknowledgeDamage, relaxedMoveConstraints - )) { + if (!this.isValidWalkPosition(chunkStore, this.tmpClimbPosition.x, this.tmpClimbPosition.y, this.tmpClimbPosition.z, constraints)) { if (this.debugModeMove) { LOGGER.at(Level.FINE).log("Drop INV %.2f/%.2f/%.2f", this.tmpClimbPosition.x, this.tmpClimbPosition.y, this.tmpClimbPosition.z); } @@ -2274,7 +2568,7 @@ public class MotionControllerWalk extends MotionControllerBase { } if (updatePosition) { - position.assign(this.tmpClimbPosition); + position.set(this.tmpClimbPosition); } return false; @@ -2293,7 +2587,7 @@ public class MotionControllerWalk extends MotionControllerBase { @Nonnull Vector3d position, double maxTestDistance, @Nonnull ComponentAccessor componentAccessor ) { this.tmpResults.setCollisionByMaterial(4); - this.tmpClimbMovement.assign(this.getWorldAntiNormal()).scale(maxTestDistance); + this.tmpClimbMovement.set(this.getWorldAntiNormal()).mul(maxTestDistance); CollisionModule.get(); CollisionModule.findCollisions(this.collisionBoundingBox, position, this.tmpClimbMovement, this.tmpResults, componentAccessor); BlockCollisionData collision = this.tmpResults.getFirstBlockCollision(); @@ -2337,9 +2631,11 @@ public class MotionControllerWalk extends MotionControllerBase { return collision; } - private boolean isClimbable(@Nonnull BlockType blockType, @Nonnull Fluid fluid, boolean avoidDamageBlocks) { - return (blockType.getDamageToEntities() <= 0 && fluid.getDamageToEntities() <= 0 || !avoidDamageBlocks) - && (this.fenceBlockSet == Integer.MIN_VALUE || !BlockSetModule.getInstance().blockInSet(this.fenceBlockSet, blockType)); + private boolean isClimbable(@Nonnull BlockType blockType, @Nonnull Fluid fluid, @Nonnull EnumSet constraints) { + boolean allowDamage = constraints.contains(RelaxedConstraint.DAMAGE); + boolean allowFence = constraints.contains(RelaxedConstraint.FENCE); + return (allowDamage || blockType.getDamageToEntities() <= 0 && fluid.getDamageToEntities() <= 0) + && (allowFence || this.fenceBlockSet == Integer.MIN_VALUE || !BlockSetModule.getInstance().blockInSet(this.fenceBlockSet, blockType)); } private boolean isValidWalkPosition( @@ -2348,10 +2644,11 @@ public class MotionControllerWalk extends MotionControllerBase { double x, double y, double z, - boolean acknowledgeDamage, - boolean relaxedMoveConstraints + @Nonnull EnumSet constraints ) { - if (acknowledgeDamage) { + boolean checkBreathing = !constraints.contains(RelaxedConstraint.BREATHE); + boolean allowWade = constraints.contains(RelaxedConstraint.WADE); + if (checkBreathing) { long packed = WorldUtil.getPackedMaterialAndFluidAtPosition(chunkRef, chunkStore, x, y + this.breathingDepth, z); BlockMaterial material = BlockMaterial.VALUES[MathUtil.unpackLeft(packed)]; int fluidId = MathUtil.unpackRight(packed); @@ -2364,7 +2661,7 @@ public class MotionControllerWalk extends MotionControllerBase { } } - if (!relaxedMoveConstraints) { + if (!allowWade) { long packedx = WorldUtil.getPackedMaterialAndFluidAtPosition(chunkRef, chunkStore, x, y + this.constraintDepth, z); BlockMaterial materialx = BlockMaterial.VALUES[MathUtil.unpackLeft(packedx)]; int fluidIdx = MathUtil.unpackRight(packedx); @@ -2376,24 +2673,25 @@ public class MotionControllerWalk extends MotionControllerBase { return true; } - private boolean isValidWalkPosition(@Nonnull ChunkStore chunkStore, double x, double y, double z, boolean acknowledgeDamage, boolean relaxedMoveConstraints) { + private boolean isValidWalkPosition(@Nonnull ChunkStore chunkStore, double x, double y, double z, @Nonnull EnumSet constraints) { long chunkIndex = ChunkUtil.indexChunkFromBlock(x, z); Ref chunkRef = chunkStore.getChunkReference(chunkIndex); - return chunkRef != null && chunkRef.isValid() - ? this.isValidWalkPosition(chunkRef, chunkStore.getStore(), x, y, z, acknowledgeDamage, relaxedMoveConstraints) - : false; + return chunkRef != null && chunkRef.isValid() ? this.isValidWalkPosition(chunkRef, chunkStore.getStore(), x, y, z, constraints) : false; } private double maxMoveFactor( - @Nonnull Vector3d position, @Nonnull Vector3d velocity, boolean acknowledgeDamage, @Nonnull ComponentAccessor componentAccessor + @Nonnull Vector3d position, + @Nonnull Vector3d velocity, + @Nonnull EnumSet constraints, + @Nonnull ComponentAccessor componentAccessor ) { CollisionModule.get(); CollisionModule.findCollisions(this.collisionBoundingBox, position, velocity, this.tmpResults, componentAccessor); BlockCollisionData collision; if (velocity.y != 0.0) { - collision = this.getFirstCollision(this.tmpResults, acknowledgeDamage); + collision = this.tmpResults.getFirstBlockCollision(); } else { - collision = this.discardIgnorableSlideCollisions(this.tmpResults, this.tmpResults.getFirstBlockCollision(), acknowledgeDamage); + collision = this.discardIgnorableSlideCollisions(this.tmpResults, this.tmpResults.getFirstBlockCollision(), constraints); } return collision == null ? 1.0 : MathUtil.clamp(collision.collisionStart, 0.0, 1.0); diff --git a/src/com/hypixel/hytale/server/npc/movement/controllers/ProbeMoveData.java b/src/com/hypixel/hytale/server/npc/movement/controllers/ProbeMoveData.java index 0ce5f76d..9085dad5 100644 --- a/src/com/hypixel/hytale/server/npc/movement/controllers/ProbeMoveData.java +++ b/src/com/hypixel/hytale/server/npc/movement/controllers/ProbeMoveData.java @@ -2,12 +2,15 @@ package com.hypixel.hytale.server.npc.movement.controllers; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import java.util.Arrays; +import java.util.EnumSet; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class ProbeMoveData { @Nonnull @@ -20,9 +23,8 @@ public class ProbeMoveData { public final Vector3d targetPosition; @Nonnull public final Vector3d directionComponentSelector; - public boolean isAvoidingBlockDamage = true; - public boolean isRelaxedMoveConstraints = false; - public boolean onGround; + private final EnumSet relaxedConstraints = EnumSet.noneOf(RelaxedConstraint.class); + public boolean edgeBlocked; public boolean isSavingSegments = false; public int segmentCount = 0; @Nullable @@ -47,40 +49,34 @@ public class ProbeMoveData { } } - public boolean isAvoidingBlockDamage() { - return this.isAvoidingBlockDamage; + @Nonnull + public EnumSet getRelaxedConstraints() { + return this.relaxedConstraints; } - public void setAvoidingBlockDamage(boolean avoid) { - this.isAvoidingBlockDamage = avoid; - } - - public boolean isRelaxedMoveConstraints() { - return this.isRelaxedMoveConstraints; - } - - public void setRelaxedMoveConstraints(boolean relaxedMoveConstraints) { - this.isRelaxedMoveConstraints = relaxedMoveConstraints; + public void setRelaxedConstraints(@Nonnull EnumSet constraints) { + this.relaxedConstraints.clear(); + this.relaxedConstraints.addAll(constraints); } @Nonnull public ProbeMoveData setPosition(@Nonnull Vector3d position) { - this.probePosition.assign(position); - this.initialPosition.assign(position); + this.probePosition.set(position); + this.initialPosition.set(position); return this; } @Nonnull public ProbeMoveData setDirection(@Nonnull Vector3d direction) { - this.probeDirection.assign(direction); - this.targetPosition.assign(this.probePosition).add(this.probeDirection); + this.probeDirection.set(direction); + this.targetPosition.set(this.probePosition).add(this.probeDirection); return this; } @Nonnull public ProbeMoveData setTargetPosition(@Nonnull Vector3d targetPosition) { - this.targetPosition.assign(targetPosition); - this.probeDirection.assign(targetPosition).subtract(this.probePosition); + this.targetPosition.set(targetPosition); + this.probeDirection.set(targetPosition).sub(this.probePosition); return this; } @@ -129,7 +125,7 @@ public class ProbeMoveData { if (this.segmentCount < 2) { return false; } else if (distance <= 0.0) { - result.assign(this.segments[0].position); + result.set(this.segments[0].position); return true; } else { int index = 1; @@ -145,20 +141,21 @@ public class ProbeMoveData { } if (segment.distance <= distance) { - result.assign(segment.position); + result.set(segment.position); return true; } else if (segment.type.canInterpolate()) { double lambda = (distance - prevSegment.distance) / (segment.distance - prevSegment.distance); NPCPhysicsMath.lerp(prevSegment.position, segment.position, lambda, result); return true; } else { - result.assign(prevSegment.position); + result.set(prevSegment.position); return true; } } } public boolean startProbing() { + this.edgeBlocked = false; if (this.isSavingSegments) { this.segmentCount = 0; } @@ -166,35 +163,35 @@ public class ProbeMoveData { return this.isSavingSegments; } - public void addStartSegment(@Nonnull Vector3d position, boolean onGround) { + public void addStartSegment(@Nonnull Vector3dc position, boolean onGround) { this.newSegment().initAsStartSegment(position, onGround); } - public void addEndSegment(@Nonnull Vector3d position, boolean onGround, double distance) { + public void addEndSegment(@Nonnull Vector3dc position, boolean onGround, double distance) { this.newSegment().initAsEndSegment(position, onGround, distance); } - public void addBlockedGroundSegment(@Nonnull Vector3d position, double distance, @Nonnull Vector3d normal, int blockId) { + public void addBlockedGroundSegment(@Nonnull Vector3dc position, double distance, @Nonnull Vector3dc normal, int blockId) { this.newSegment().initAsBlockedGroundSegment(position, distance, normal, blockId); } - public void addHitGroundSegment(@Nonnull Vector3d position, double distance, @Nonnull Vector3d normal, int blockId) { + public void addHitGroundSegment(@Nonnull Vector3dc position, double distance, @Nonnull Vector3dc normal, int blockId) { this.newSegment().initAsHitGroundSegment(position, distance, normal, blockId); } - public void addHitWallSegment(@Nonnull Vector3d position, boolean onGround, double distance, @Nonnull Vector3d normal, int blockId) { + public void addHitWallSegment(@Nonnull Vector3dc position, boolean onGround, double distance, @Nonnull Vector3dc normal, int blockId) { this.newSegment().initAsHitWallSegment(position, onGround, distance, normal, blockId); } - public void addMoveSegment(@Nonnull Vector3d position, boolean onGround, double distance) { + public void addMoveSegment(@Nonnull Vector3dc position, boolean onGround, double distance) { this.newSegment().initAsMoveSegment(position, onGround, distance); } - public void addClimbSegment(@Nonnull Vector3d position, double distance, int blockId) { + public void addClimbSegment(@Nonnull Vector3dc position, double distance, int blockId) { this.newSegment().initAsClimbSegment(position, distance, blockId); } - public void addHitEdgeSegment(@Nonnull Vector3d position, double distance) { + public void addHitEdgeSegment(@Nonnull Vector3dc position, double distance) { this.newSegment().initAsHitEdgeSegment(position, distance); } @@ -203,6 +200,7 @@ public class ProbeMoveData { } public void addBlockedDropSegment(@Nonnull Vector3d position, double distance) { + this.edgeBlocked = true; this.newSegment().initAsBlockedDropSegment(position, distance); } @@ -211,6 +209,7 @@ public class ProbeMoveData { } public void changeSegmentToBlockedEdge() { + this.edgeBlocked = true; this.segments[this.segmentCount - 1].type = ProbeMoveData.Segment.Type.BLOCKED_EDGE; } @@ -238,91 +237,91 @@ public class ProbeMoveData { public boolean onGround; public int blockId; - public void initAsStartSegment(@Nonnull Vector3d position, boolean onGround) { + public void initAsStartSegment(@Nonnull Vector3dc position, boolean onGround) { this.type = ProbeMoveData.Segment.Type.START; - this.position.assign(position); - this.normal.assign(Vector3d.ZERO); + this.position.set(position); + this.normal.zero(); this.distance = 0.0; this.onGround = onGround; this.blockId = Integer.MIN_VALUE; } - public void initAsEndSegment(@Nonnull Vector3d position, boolean onGround, double distance) { + public void initAsEndSegment(@Nonnull Vector3dc position, boolean onGround, double distance) { this.type = ProbeMoveData.Segment.Type.END; - this.position.assign(position); - this.normal.assign(Vector3d.ZERO); + this.position.set(position); + this.normal.zero(); this.distance = distance; this.onGround = onGround; this.blockId = Integer.MIN_VALUE; } - public void initAsBlockedGroundSegment(@Nonnull Vector3d position, double distance, @Nonnull Vector3d normal, int blockId) { + public void initAsBlockedGroundSegment(@Nonnull Vector3dc position, double distance, @Nonnull Vector3dc normal, int blockId) { this.type = ProbeMoveData.Segment.Type.BLOCKED_GROUND; - this.position.assign(position); - this.normal.assign(normal); + this.position.set(position); + this.normal.set(normal); this.distance = distance; this.onGround = true; this.blockId = blockId; } - public void initAsHitGroundSegment(@Nonnull Vector3d position, double distance, @Nonnull Vector3d normal, int blockId) { + public void initAsHitGroundSegment(@Nonnull Vector3dc position, double distance, @Nonnull Vector3dc normal, int blockId) { this.type = ProbeMoveData.Segment.Type.HIT_GROUND; - this.position.assign(position); - this.normal.assign(normal); + this.position.set(position); + this.normal.set(normal); this.distance = distance; this.onGround = true; this.blockId = blockId; } - public void initAsHitWallSegment(@Nonnull Vector3d position, boolean onGround, double distance, @Nonnull Vector3d normal, int blockId) { + public void initAsHitWallSegment(@Nonnull Vector3dc position, boolean onGround, double distance, @Nonnull Vector3dc normal, int blockId) { this.type = ProbeMoveData.Segment.Type.HIT_WALL; - this.position.assign(position); - this.normal.assign(normal); + this.position.set(position); + this.normal.set(normal); this.distance = distance; this.onGround = onGround; this.blockId = blockId; } - public void initAsClimbSegment(@Nonnull Vector3d position, double distance, int blockId) { + public void initAsClimbSegment(@Nonnull Vector3dc position, double distance, int blockId) { this.type = ProbeMoveData.Segment.Type.CLIMB; - this.position.assign(position); - this.normal.assign(Vector3d.ZERO); + this.position.set(position); + this.normal.zero(); this.distance = distance; this.onGround = true; this.blockId = blockId; } - public void initAsMoveSegment(@Nonnull Vector3d position, boolean onGround, double distance) { + public void initAsMoveSegment(@Nonnull Vector3dc position, boolean onGround, double distance) { this.type = ProbeMoveData.Segment.Type.MOVE; - this.position.assign(position); - this.normal.assign(Vector3d.ZERO); + this.position.set(position); + this.normal.zero(); this.distance = distance; this.onGround = onGround; this.blockId = Integer.MIN_VALUE; } - public void initAsDropSegment(@Nonnull Vector3d position, double distance) { + public void initAsDropSegment(@Nonnull Vector3dc position, double distance) { this.type = ProbeMoveData.Segment.Type.DROP; - this.position.assign(position); - this.normal.assign(Vector3d.ZERO); + this.position.set(position); + this.normal.zero(); this.distance = distance; this.onGround = true; this.blockId = Integer.MIN_VALUE; } - public void initAsBlockedDropSegment(@Nonnull Vector3d position, double distance) { + public void initAsBlockedDropSegment(@Nonnull Vector3dc position, double distance) { this.type = ProbeMoveData.Segment.Type.BLOCKED_DROP; - this.position.assign(position); - this.normal.assign(Vector3d.ZERO); + this.position.set(position); + this.normal.zero(); this.distance = distance; this.onGround = false; this.blockId = Integer.MIN_VALUE; } - public void initAsHitEdgeSegment(@Nonnull Vector3d position, double distance) { + public void initAsHitEdgeSegment(@Nonnull Vector3dc position, double distance) { this.type = ProbeMoveData.Segment.Type.HIT_EDGE; - this.position.assign(position); - this.normal.assign(Vector3d.ZERO); + this.position.set(position); + this.normal.zero(); this.distance = distance; this.onGround = true; this.blockId = Integer.MIN_VALUE; diff --git a/src/com/hypixel/hytale/server/npc/movement/controllers/builders/BuilderMotionControllerWalk.java b/src/com/hypixel/hytale/server/npc/movement/controllers/builders/BuilderMotionControllerWalk.java index 765cc25c..5e02d142 100644 --- a/src/com/hypixel/hytale/server/npc/movement/controllers/builders/BuilderMotionControllerWalk.java +++ b/src/com/hypixel/hytale/server/npc/movement/controllers/builders/BuilderMotionControllerWalk.java @@ -47,6 +47,7 @@ public class BuilderMotionControllerWalk extends BuilderMotionControllerBase { private final DoubleHolder descentSteepness = new DoubleHolder(); private final DoubleHolder descentBlending = new DoubleHolder(); private final DoubleHolder maxDropHeight = new DoubleHolder(); + private final DoubleHolder maxDropHeightRelaxedAdjustment = new DoubleHolder(); private double maxVerticalSpeedFluid; private final NumberArrayHolder jumpRange = new NumberArrayHolder(); private double minHover; @@ -343,6 +344,16 @@ public class BuilderMotionControllerWalk extends BuilderMotionControllerBase { "Maximum height NPC considers drop safe", null ); + this.getDouble( + data, + "MaxDropHeightRelaxedAdjustment", + this.maxDropHeightRelaxedAdjustment, + 3.0, + DoubleSingleValidator.greaterEqual0(), + BuilderDescriptorState.WorkInProgress, + "Additional drop height when DROP constraint is relaxed", + null + ); this.getAsset( data, "FenceBlockSet", @@ -509,6 +520,10 @@ public class BuilderMotionControllerWalk extends BuilderMotionControllerBase { return this.maxDropHeight.get(support.getExecutionContext()); } + public double getMaxDropHeightRelaxedAdjustment(@Nonnull BuilderSupport support) { + return this.maxDropHeightRelaxedAdjustment.get(support.getExecutionContext()); + } + public int getFenceBlockSet() { int index = BlockSet.getAssetMap().getIndex(this.fenceBlockSet); if (index == Integer.MIN_VALUE) { diff --git a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceAvoidCollision.java b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceAvoidCollision.java index 7a3d5932..4a4c2318 100644 --- a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceAvoidCollision.java +++ b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceAvoidCollision.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.server.npc.movement.steeringforces; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.component.Velocity; @@ -16,6 +16,7 @@ import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SteeringForceAvoidCollision extends SteeringForceWithGroup { private final Vector3d selfVelocity = new Vector3d(); @@ -68,7 +69,7 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { this.selfReference = ref; this.otherReference = null; if (velocity != null) { - this.selfVelocity.assign(velocity); + this.selfVelocity.set(velocity); } else { this.setVelocityFromEntity(this.selfReference, componentAccessor); } @@ -86,7 +87,7 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { this.otherReference = null; this.canSlowDown = true; this.overlap = false; - double velocitySquared = this.selfVelocity.squaredLength(); + double velocitySquared = this.selfVelocity.lengthSquared(); if (velocitySquared > 0.001) { this.velocity = Math.sqrt(velocitySquared); this.maxTime = this.maxDistance / velocitySquared; @@ -98,7 +99,7 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { @Override public boolean compute(@Nonnull Steering output) { - this.lastSteeringDirection.assign(Vector3d.ZERO); + this.lastSteeringDirection.zero(); if (this.velocity == 0.0) { return false; } else { @@ -123,21 +124,21 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { NPCPlugin.get().getLogger().at(Level.INFO).log("--> Avoidance slowdown=%s dist=%s maxDist=%s", s, distance, this.maxDistance); } } else { - this.tempPos.assign(this.colliderPosition).subtract(this.selfPosition); + this.tempPos.set(this.colliderPosition).sub(this.selfPosition); NPCPhysicsMath.rejection(this.selfVelocity, this.tempPos, this.tempVel); this.tempVel.negate(); - if (this.tempVel.squaredLength() < 0.001) { - this.selfVelocity.cross(Vector3d.UP, this.tempVel); - if (this.tempVel.squaredLength() < 0.001) { - this.selfVelocity.cross(Vector3d.RIGHT, this.tempVel); + if (this.tempVel.lengthSquared() < 0.001) { + this.selfVelocity.cross(Vector3dUtil.UP, this.tempVel); + if (this.tempVel.lengthSquared() < 0.001) { + this.selfVelocity.cross(Vector3dUtil.RIGHT, this.tempVel); } } double s = Math.pow(1.0 - distance / this.maxDistance, 1.0 / this.falloff); - this.tempVel.setLength(l * s * this.strength).scale(this.componentSelector); - this.lastSteeringDirection.assign(this.tempVel); + this.tempVel.normalize(l * s * this.strength).mul(this.componentSelector); + this.lastSteeringDirection.set(this.tempVel); output.scaleTranslation(1.0 - s); - output.getTranslation().add(this.tempVel).setLength(l); + output.getTranslation().add(this.tempVel).normalize(l); if (this.debug) { NPCPlugin.get().getLogger().at(Level.INFO).log("--> Avoidance dist=%.2f l=%.2f s=%.2f maxDist=%.2f", distance, l, s, this.maxDistance); } @@ -174,8 +175,8 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { assert uuidComponent != null; UUID uuid = uuidComponent.getUuid(); - this.tempPos.assign(position); - boolean departing = this.tempVel.assign(this.tempPos).subtract(this.selfPosition).dot(this.selfVelocity) <= 0.0; + this.tempPos.set(position); + boolean departing = this.tempVel.set(this.tempPos).sub(this.selfPosition).dot(this.selfVelocity) <= 0.0; if (departing) { if (this.debug) { NPCPlugin.get().getLogger().at(Level.INFO).log("Avoidance add: Entity %s - Moving away, ignoring", uuid); @@ -183,7 +184,7 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { } else { double entityRadius = NPCPhysicsMath.collisionSphereRadius(ref, commandBuffer); double sumRadius = this.selfRadius + entityRadius; - this.overlap = this.selfPosition.distanceSquaredTo(this.tempPos) <= sumRadius * sumRadius; + this.overlap = this.selfPosition.distanceSquared(this.tempPos) <= sumRadius * sumRadius; if (this.overlap) { this.collisionTime = 0.0; this.canSlowDown = true; @@ -194,7 +195,7 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { Velocity velocityComponent = commandBuffer.getComponent(ref, Velocity.getComponentType()); velocityComponent.assignVelocityTo(this.tempVel); int solutions = NPCPhysicsMath.intersectSweptSpheresFootpoint( - this.selfPosition, this.selfVelocity, this.selfRadius, this.tempPos, this.tempVel, entityRadius, Vector3d.ALL_ONES, this.tempTime + this.selfPosition, this.selfVelocity, this.selfRadius, this.tempPos, this.tempVel, entityRadius, Vector3dUtil.ALL_ONES, this.tempTime ); if (this.debug && solutions > 0) { NPCPlugin.get() @@ -232,7 +233,7 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { ); } - this.colliderPosition.assign(position); + this.colliderPosition.set(position); this.collisionTime = this.tempTime[0]; this.otherReference = ref; this.canSlowDown = !antiParallel && this.otherReference.getIndex() < this.selfReference.getIndex(); @@ -260,7 +261,7 @@ public class SteeringForceAvoidCollision extends SteeringForceWithGroup { } public void setSelfVelocity(@Nonnull Vector3d selfVelocity) { - this.selfVelocity.assign(selfVelocity); + this.selfVelocity.set(selfVelocity); } @Nonnull diff --git a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceEvade.java b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceEvade.java index 2b1d2841..f89e2197 100644 --- a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceEvade.java +++ b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceEvade.java @@ -41,8 +41,8 @@ public class SteeringForceEvade extends SteeringForceWithTarget { @Override public boolean compute(@Nonnull Steering output) { if (super.compute(output)) { - output.setTranslation(this.selfPosition).getTranslation().subtract(this.targetPosition); - double distanceSquared = output.getTranslation().squaredLength(); + output.setTranslation(this.selfPosition).getTranslation().sub(this.targetPosition); + double distanceSquared = output.getTranslation().lengthSquared(); if (distanceSquared >= this.squaredStopDistance) { output.clear(); return false; @@ -58,7 +58,7 @@ public class SteeringForceEvade extends SteeringForceWithTarget { if (!(distanceSquared < this.squaredSlowdownDistance) && this.distanceDelta != 0.0) { double scale = Math.pow((this.stopDistance - Math.sqrt(distanceSquared)) / this.distanceDelta, 1.0 / this.falloff); - output.getTranslation().setLength(scale); + output.getTranslation().normalize(scale); return true; } else { output.getTranslation().normalize(); diff --git a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForcePursue.java b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForcePursue.java index 40b547c1..e21bcd9e 100644 --- a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForcePursue.java +++ b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForcePursue.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.server.npc.movement.steeringforces; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.npc.movement.Steering; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SteeringForcePursue extends SteeringForceWithTarget { private double stopDistance; @@ -34,20 +34,20 @@ public class SteeringForcePursue extends SteeringForceWithTarget { if (super.compute(output)) { output.setTranslation(this.targetPosition); Vector3d translation = output.getTranslation(); - translation.subtract(this.selfPosition); - double distanceSquared = translation.squaredLength(); + translation.sub(this.selfPosition); + double distanceSquared = translation.lengthSquared(); if (distanceSquared <= this.squaredStopDistance) { output.clear(); return false; } else { double distance = Math.sqrt(distanceSquared); if (distanceSquared >= this.squaredSlowdownDistance) { - translation.scale(1.0 / distance); + translation.mul(1.0 / distance); output.clearRotation(); return true; } else { double scale = Math.pow((distance - this.stopDistance) / this.distanceDelta, this.invFalloff); - translation.setLength(scale); + translation.normalize(scale); output.clearRotation(); return true; } diff --git a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceRotate.java b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceRotate.java index 7e6609b9..384cd37b 100644 --- a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceRotate.java +++ b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceRotate.java @@ -39,7 +39,7 @@ public class SteeringForceRotate implements SteeringForce { assert transformComponent != null; - this.heading = transformComponent.getRotation().getYaw(); + this.heading = transformComponent.getRotation().yaw(); } public void setTolerance(double tolerance) { diff --git a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWander.java b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWander.java index 8f36fb75..b7870fc5 100644 --- a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWander.java +++ b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWander.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.server.npc.movement.steeringforces; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import com.hypixel.hytale.server.npc.movement.Steering; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SteeringForceWander implements SteeringForce { private double time; diff --git a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWithGroup.java b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWithGroup.java index ea3de00a..36da73b1 100644 --- a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWithGroup.java +++ b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWithGroup.java @@ -3,10 +3,10 @@ package com.hypixel.hytale.server.npc.movement.steeringforces; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.movement.Steering; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class SteeringForceWithGroup implements SteeringForce { @Nonnull @@ -14,7 +14,7 @@ public abstract class SteeringForceWithGroup implements SteeringForce { protected Vector3d componentSelector; public void setSelf(@Nonnull Ref ref, @Nonnull Vector3d position, @Nonnull ComponentAccessor componentAccessor) { - this.selfPosition.assign(position.getX(), position.getY(), position.getZ()); + this.selfPosition.set(position.x(), position.y(), position.z()); } public void setComponentSelector(Vector3d componentSelector) { diff --git a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWithTarget.java b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWithTarget.java index 3478ec88..182499e5 100644 --- a/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWithTarget.java +++ b/src/com/hypixel/hytale/server/npc/movement/steeringforces/SteeringForceWithTarget.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.server.npc.movement.steeringforces; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.npc.movement.Steering; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class SteeringForceWithTarget implements SteeringForce { protected final Vector3d selfPosition = new Vector3d(); @@ -15,7 +15,7 @@ public abstract class SteeringForceWithTarget implements SteeringForce { } public void setSelfPosition(@Nonnull Vector3d selfPosition) { - this.selfPosition.assign(selfPosition); + this.selfPosition.set(selfPosition); } @Nonnull @@ -24,11 +24,11 @@ public abstract class SteeringForceWithTarget implements SteeringForce { } public void setTargetPosition(@Nonnull Vector3d targetPosition) { - this.targetPosition.assign(targetPosition); + this.targetPosition.set(targetPosition); } public void setTargetPosition(double x, double y, double z) { - this.targetPosition.assign(x, y, z); + this.targetPosition.set(x, y, z); } public void setPositions(@Nonnull Vector3d self, @Nonnull Vector3d target) { @@ -37,7 +37,7 @@ public abstract class SteeringForceWithTarget implements SteeringForce { } public void setSelfPosition(double x, double y, double z) { - this.selfPosition.assign(x, y, z); + this.selfPosition.set(x, y, z); } public void setComponentSelector(Vector3d componentSelector) { @@ -46,8 +46,8 @@ public abstract class SteeringForceWithTarget implements SteeringForce { @Override public boolean compute(Steering output) { - this.selfPosition.scale(this.componentSelector); - this.targetPosition.scale(this.componentSelector); + this.selfPosition.mul(this.componentSelector); + this.targetPosition.mul(this.componentSelector); return true; } } diff --git a/src/com/hypixel/hytale/server/npc/navigation/AStarBase.java b/src/com/hypixel/hytale/server/npc/navigation/AStarBase.java index 3d0e5d18..6f654a1e 100644 --- a/src/com/hypixel/hytale/server/npc/navigation/AStarBase.java +++ b/src/com/hypixel/hytale/server/npc/navigation/AStarBase.java @@ -7,8 +7,8 @@ import com.hypixel.hytale.function.function.ToFloatFunction; import com.hypixel.hytale.function.predicate.BiFloatPredicate; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.npc.movement.constraints.RelaxedConstraint; import com.hypixel.hytale.server.npc.movement.controllers.MotionController; import com.hypixel.hytale.server.npc.movement.controllers.ProbeMoveData; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; @@ -17,9 +17,11 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectIterator; +import java.util.EnumSet; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class AStarBase { public static final double FULL_STEP_THRESHOLD = 0.9999999; @@ -35,8 +37,7 @@ public class AStarBase { protected int totalNodesLimit = 400; protected boolean canMoveDiagonal = true; protected boolean optimizedBuildPath = true; - protected boolean isAvoidingBlockDamage; - protected boolean isRelaxedMoveConstraints; + protected EnumSet relaxedConstraints = EnumSet.noneOf(RelaxedConstraint.class); protected final Vector3d startPosition = new Vector3d(); protected AStarEvaluator evaluator; protected double positionToIndexOffsetX; @@ -86,7 +87,7 @@ public class AStarBase { } public void setStartPosition(@Nonnull Vector3d position) { - this.startPosition.assign(position); + this.startPosition.set(position); } @Nonnull @@ -166,9 +167,9 @@ public class AStarBase { this.clearPath(); this.iterations = 0; this.evaluator = evaluator; - this.startPosition.assign(start); - this.isAvoidingBlockDamage = probeMoveData.isAvoidingBlockDamage; - this.isRelaxedMoveConstraints = probeMoveData.isRelaxedMoveConstraints; + this.startPosition.set(start); + this.relaxedConstraints.clear(); + this.relaxedConstraints.addAll(probeMoveData.getRelaxedConstraints()); long startBlockX = MathUtil.fastFloor(this.startPosition.x); long startBlockY = MathUtil.fastFloor(this.startPosition.y); long startBlockZ = MathUtil.fastFloor(this.startPosition.z); @@ -194,7 +195,7 @@ public class AStarBase { || !this.searchDirectionsWorldNormal.equals(motionController.getWorldNormal())) { this.searchDirectionIsDiagonalMoves = this.canMoveDiagonal; this.searchDirectionIs2D = this.is2D; - this.searchDirectionsWorldNormal.assign(motionController.getWorldNormal()); + this.searchDirectionsWorldNormal.set(motionController.getWorldNormal()); int searchDirectionCount = this.is2D ? (this.canMoveDiagonal ? 8 : 4) : (this.canMoveDiagonal ? 26 : 6); this.searchDirections = new Vector3d[searchDirectionCount]; this.searchDirectionDistances = new double[searchDirectionCount]; @@ -225,10 +226,10 @@ public class AStarBase { for (int i = 0; i < this.searchDirections.length - 1; i++) { if (this.inverseSearchDirections[i] == -1) { - this.tempDirectionVector.assign(this.searchDirections[i]).negate(); + this.tempDirectionVector.set(this.searchDirections[i]).negate(); for (int j = i + 1; j < this.searchDirections.length; j++) { - if (this.searchDirections[j].equals(this.tempDirectionVector)) { + if (this.searchDirections[j].equals(this.tempDirectionVector, 0.0)) { this.inverseSearchDirections[i] = j; this.inverseSearchDirections[j] = i; break; @@ -274,7 +275,7 @@ public class AStarBase { probeMoveData.setSaveSegments(false); this.tempPositionVector - .assign(this.projectedX ? start.x : startBlockX + 0.5, this.projectedY ? start.y : startBlockY + 0.5, this.projectedZ ? start.z : startBlockZ + 0.5); + .set(this.projectedX ? start.x : startBlockX + 0.5, this.projectedY ? start.y : startBlockY + 0.5, this.projectedZ ? start.z : startBlockZ + 0.5); Vector3d position = this.canAdvance(ref, this.startPosition, this.tempPositionVector, motionController, probeMoveData, componentAccessor); if (position != null) { this.addStartNode(this.startPosition, position, motionController); @@ -288,7 +289,7 @@ public class AStarBase { for (double xx = this.projectedX ? 0.0 : 0.5; xx >= 0.0; xx -= 0.5) { for (double yx = this.projectedY ? 0.0 : 0.5; yx >= 0.0; yx -= 0.5) { for (double zx = this.projectedZ ? 0.0 : 0.5; zx >= 0.0; zx -= 0.5) { - this.tempDirectionVector.assign(xx, yx, zx).add(this.tempPositionVector); + this.tempDirectionVector.set(xx, yx, zx).add(this.tempPositionVector); position = this.canAdvance(ref, this.startPosition, this.tempDirectionVector, motionController, probeMoveData, componentAccessor); if (position != null) { this.addStartNode(this.startPosition, position, motionController); @@ -308,7 +309,7 @@ public class AStarBase { for (double xx = startX; xx <= endX; xx += 0.5) { for (double yx = startY; yx <= endY; yx += 0.5) { for (double zxx = startZ; zxx <= endZ; zxx += 0.5) { - this.tempDirectionVector.assign(xx, yx, zxx); + this.tempDirectionVector.set(xx, yx, zxx); position = this.canAdvance(ref, this.startPosition, this.tempDirectionVector, motionController, probeMoveData, componentAccessor); if (position != null) { this.addStartNode(this.startPosition, position, motionController); @@ -333,8 +334,7 @@ public class AStarBase { if (this.progress != AStarBase.Progress.COMPUTING) { return this.progress; } else { - probeMoveData.isAvoidingBlockDamage = this.isAvoidingBlockDamage; - probeMoveData.isRelaxedMoveConstraints = this.isRelaxedMoveConstraints; + probeMoveData.setRelaxedConstraints(this.relaxedConstraints); while (!this.openNodes.isEmpty() && nodesToProcess-- > 0) { int idx = this.openNodes.size() - 1; @@ -420,7 +420,7 @@ public class AStarBase { } public float buildFurthestPath() { - AStarNode node = this.buildBestPath(n -> (float)n.getPosition().distanceSquaredTo(this.startPosition), (oldV, v) -> v > oldV, 0.0F); + AStarNode node = this.buildBestPath(n -> (float)n.getPosition().distanceSquared(this.startPosition), (oldV, v) -> v > oldV, 0.0F); return node == null ? 0.0F : node.getTravelCost(); } @@ -634,7 +634,7 @@ public class AStarBase { if (endNode == null) { this.path = null; } else { - this.pathEnd.assign(endNode.getPosition()); + this.pathEnd.set(endNode.getPosition()); if (this.optimizedBuildPath) { AStarNode node = endNode; int length = 1; diff --git a/src/com/hypixel/hytale/server/npc/navigation/AStarEvaluator.java b/src/com/hypixel/hytale/server/npc/navigation/AStarEvaluator.java index a385fdc3..20ca3fae 100644 --- a/src/com/hypixel/hytale/server/npc/navigation/AStarEvaluator.java +++ b/src/com/hypixel/hytale/server/npc/navigation/AStarEvaluator.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.server.npc.navigation; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.movement.controllers.MotionController; +import org.joml.Vector3d; public interface AStarEvaluator { boolean isGoalReached(Ref var1, AStarBase var2, AStarNode var3, MotionController var4, ComponentAccessor var5); diff --git a/src/com/hypixel/hytale/server/npc/navigation/AStarNode.java b/src/com/hypixel/hytale/server/npc/navigation/AStarNode.java index edc5d72c..0012eecc 100644 --- a/src/com/hypixel/hytale/server/npc/navigation/AStarNode.java +++ b/src/com/hypixel/hytale/server/npc/navigation/AStarNode.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.server.npc.navigation; -import com.hypixel.hytale.math.vector.Vector3d; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class AStarNode implements IWaypoint { public static final AStarNode ENTRY_NODE_TAG = new AStarNode(0); @@ -118,7 +118,7 @@ public class AStarNode implements IWaypoint { @Nonnull public AStarNode initAsStartNode(@Nonnull Vector3d position, long positionIndex, float cost, float estimateCost) { - this.position.assign(position); + this.position.set(position); this.positionIndex = positionIndex; this.open = true; this.estimateToGoal = estimateCost; @@ -142,7 +142,7 @@ public class AStarNode implements IWaypoint { float travelCost, float estimateCost ) { - this.position.assign(position); + this.position.set(position); this.positionIndex = positionIndex; this.open = true; this.estimateToGoal = estimateCost; @@ -159,7 +159,7 @@ public class AStarNode implements IWaypoint { @Nonnull public AStarNode initAsInvalid(@Nonnull Vector3d position, long positionIndex) { - this.position.assign(position); + this.position.set(position); this.positionIndex = positionIndex; this.open = false; this.estimateToGoal = Float.MAX_VALUE; diff --git a/src/com/hypixel/hytale/server/npc/navigation/AStarWithTarget.java b/src/com/hypixel/hytale/server/npc/navigation/AStarWithTarget.java index 5350765b..fdb56cb7 100644 --- a/src/com/hypixel/hytale/server/npc/navigation/AStarWithTarget.java +++ b/src/com/hypixel/hytale/server/npc/navigation/AStarWithTarget.java @@ -3,11 +3,11 @@ package com.hypixel.hytale.server.npc.navigation; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.movement.controllers.MotionController; import com.hypixel.hytale.server.npc.movement.controllers.ProbeMoveData; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class AStarWithTarget extends AStarBase { @Nonnull @@ -39,7 +39,7 @@ public class AStarWithTarget extends AStarBase { @Nonnull ComponentAccessor componentAccessor ) { AStarBase.Progress progress = super.initComputePath(ref, start, evaluator, motionController, probeMoveData, nodePoolProvider, componentAccessor); - this.targetPosition.assign(end); + this.targetPosition.set(end); this.targetPositionIndex = this.positionToIndex(this.targetPosition); return progress; } diff --git a/src/com/hypixel/hytale/server/npc/navigation/IWaypoint.java b/src/com/hypixel/hytale/server/npc/navigation/IWaypoint.java index aa54e557..e5113e84 100644 --- a/src/com/hypixel/hytale/server/npc/navigation/IWaypoint.java +++ b/src/com/hypixel/hytale/server/npc/navigation/IWaypoint.java @@ -1,7 +1,7 @@ package com.hypixel.hytale.server.npc.navigation; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nullable; +import org.joml.Vector3d; public interface IWaypoint { int getLength(); diff --git a/src/com/hypixel/hytale/server/npc/navigation/PathFollower.java b/src/com/hypixel/hytale/server/npc/navigation/PathFollower.java index d31bf781..8f6aa8cf 100644 --- a/src/com/hypixel/hytale/server/npc/navigation/PathFollower.java +++ b/src/com/hypixel/hytale/server/npc/navigation/PathFollower.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.server.npc.navigation; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.NPCPlugin; @@ -13,6 +13,7 @@ import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PathFollower { @Nullable @@ -100,7 +101,7 @@ public class PathFollower { public void setPath(IWaypoint firstWaypoint, @Nonnull Vector3d startPosition) { this.currentWaypoint = firstWaypoint; - this.lastWaypointPosition.assign(startPosition); + this.lastWaypointPosition.set(startPosition); this.currentWaypointDistanceSquared = Double.MAX_VALUE; this.shouldSmoothPath = true; this.isWaypointFrozen = false; @@ -133,7 +134,7 @@ public class PathFollower { if (this.currentWaypoint == this.frozenWaypoint) { return true; } else { - this.frozenWaypoint.position.assign(this.currentWaypoint.getPosition()); + this.frozenWaypoint.position.set(this.currentWaypoint.getPosition()); this.currentWaypoint = this.frozenWaypoint; this.isWaypointFrozen = true; return true; @@ -152,31 +153,31 @@ public class PathFollower { public void executePath(@Nonnull Vector3d currentPosition, @Nonnull MotionController activeMotionController, @Nonnull Steering desiredSteering) { Vector3d target = this.getCurrentWaypointPosition(); if (target != null) { - this.tempVector.assign(target).subtract(currentPosition); + this.tempVector.set(target).sub(currentPosition); double length = this.tempVector.length(); desiredSteering.setMaxDistance(length); if (length > this.waypointRadius) { - this.direction.assign(this.tempVector); + this.direction.set(this.tempVector); this.computeRejection(currentPosition, target, activeMotionController); - this.direction.subtract(this.rejection); - desiredSteering.setTranslation(this.direction.scale(this.relativeSpeed / length)); + this.direction.sub(this.rejection); + desiredSteering.setTranslation(this.direction.mul(this.relativeSpeed / length)); } else { if (length > 0.1) { - this.direction.assign(this.tempVector); + this.direction.set(this.tempVector); } - desiredSteering.setTranslation(this.direction.scale(this.relativeSpeedWaypoint / length)); + desiredSteering.setTranslation(this.direction.mul(this.relativeSpeedWaypoint / length)); } } } public void computeRejection(@Nonnull Vector3d currentPosition, @Nonnull Vector3d target, @Nonnull MotionController activeMotionController) { - this.tempPath.assign(target).subtract(this.lastWaypointPosition).scale(activeMotionController.getComponentSelector()); - this.tempVector.assign(currentPosition).subtract(this.lastWaypointPosition).scale(activeMotionController.getComponentSelector()); - double dotDD = this.tempPath.squaredLength(); + this.tempPath.set(target).sub(this.lastWaypointPosition).mul(activeMotionController.getComponentSelector()); + this.tempVector.set(currentPosition).sub(this.lastWaypointPosition).mul(activeMotionController.getComponentSelector()); + double dotDD = this.tempPath.lengthSquared(); double dotDP = this.tempPath.dot(this.tempVector); - this.projection.assign(this.tempPath).scale(dotDP / dotDD); - this.rejection.assign(this.tempVector).subtract(this.projection).scale(this.rejectionWeight); + this.projection.set(this.tempPath).mul(dotDP / dotDD); + this.rejection.set(this.tempVector).sub(this.projection).mul(this.rejectionWeight); } public boolean updateCurrentTarget(@Nonnull Vector3d entityPosition, @Nonnull MotionController motionController) { @@ -205,13 +206,13 @@ public class PathFollower { !reachedWaypoint, Math.sqrt(distanceSquared), projectionLength, - Vector3d.formatShortString(entityPosition), - Vector3d.formatShortString(waypointPosition) + Vector3dUtil.formatShortString(entityPosition), + Vector3dUtil.formatShortString(waypointPosition) ); } if (reachedWaypoint) { - this.lastWaypointPosition.assign(waypointPosition); + this.lastWaypointPosition.set(waypointPosition); this.currentWaypoint = this.currentWaypoint.next(); if (this.currentWaypoint == null) { this.isWaypointFrozen = false; @@ -238,7 +239,7 @@ public class PathFollower { if (nextWaypoint == null) { return true; } else { - this.tempVector.assign(nextWaypoint.getPosition()).subtract(waypointPosition); + this.tempVector.set(nextWaypoint.getPosition()).sub(waypointPosition); distanceSquared = NPCPhysicsMath.projectedLengthSquared(this.tempVector, motionController.getComponentSelector()); if (distanceSquared < 0.001) { return true; @@ -300,9 +301,9 @@ public class PathFollower { "=== New Target len=%s skipped=%s pos=%s tgt=%s dist=%s", l, startLength - l, - Vector3d.formatShortString(position), - Vector3d.formatShortString(startNode.getPosition()), - position.distanceTo(startNode.getPosition()) + Vector3dUtil.formatShortString(position), + Vector3dUtil.formatShortString(startNode.getPosition()), + position.distance(startNode.getPosition()) ); } diff --git a/src/com/hypixel/hytale/server/npc/pages/EntitySpawnPage.java b/src/com/hypixel/hytale/server/npc/pages/EntitySpawnPage.java index 482a35b2..3fb8d19f 100644 --- a/src/com/hypixel/hytale/server/npc/pages/EntitySpawnPage.java +++ b/src/com/hypixel/hytale/server/npc/pages/EntitySpawnPage.java @@ -10,10 +10,8 @@ import com.hypixel.hytale.component.NonSerialized; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.Page; @@ -64,6 +62,8 @@ import java.util.Map.Entry; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class EntitySpawnPage extends InteractiveCustomUIPage { private static final String COMMON_TEXT_BUTTON_DOCUMENT = "Common/TextButton.ui"; @@ -98,7 +98,7 @@ public class EntitySpawnPage extends InteractiveCustomUIPage modelPreview; private Vector3d position; - private Vector3f rotation; + private Rotation3f rotation; private float currentRotationOffset = 0.0F; private float currentScale = 1.0F; private float lastPreviewScale = 1.0F; @@ -179,10 +179,10 @@ public class EntitySpawnPage extends InteractiveCustomUIPage= 1 && count <= 100) { this.clearPreview(store); - Vector3f spawnRotation = this.rotation.clone(); - spawnRotation.setYaw(this.rotation.getYaw() + this.currentRotationOffset); + Rotation3f spawnRotation = new Rotation3f(this.rotation); + spawnRotation.setYaw(this.rotation.yaw() + this.currentRotationOffset); for (int i = 0; i < count; i++) { NPCPlugin.get().spawnNPC(store, this.selectedNpcRole, null, this.position, spawnRotation); @@ -319,8 +319,8 @@ public class EntitySpawnPage extends InteractiveCustomUIPage holder = store.getRegistry().newHolder(); @@ -546,8 +546,8 @@ public class EntitySpawnPage extends InteractiveCustomUIPage blockId != 0, aheadPosition.x, aheadPosition.y + 0.5, aheadPosition.z, 0.0, -1.0, 0.0, 3.0 @@ -691,9 +691,9 @@ public class EntitySpawnPage extends InteractiveCustomUIPage> ignoredEntitiesForAvoidance = new HashSet<>(); + protected final Vector3d separationTempDistanceVector = new Vector3d(); + protected final Vector3d separationTempSteeringVector = new Vector3d(); + protected final Set> ignoredEntitiesForAvoidance = new ReferenceOpenHashSet<>(); protected final double entityAvoidanceStrength; protected final Role.AvoidanceMode avoidanceMode; protected final boolean isAvoidingEntities; + protected final Role.SeparationMode separationMode; + protected final boolean useOrientationHint; + protected final boolean alwaysApplySeparation; + protected final boolean normalizeDistances; protected final double separationDistance; protected final double separationWeight; protected final double separationDistanceTarget; protected final double separationNearRadiusTarget; protected final double separationFarRadiusTarget; protected final boolean applySeparation; + protected final double separationSafeDistanceMultiplier; + protected final double separationLegacySteeringStrength; + protected final double separationPushSteeringStrength; + protected final double separationPushDistanceWeightDefault; + protected final double separationPushDistanceWeightTarget; + protected final double separationPushDistanceWeightAttacker; + protected final double separationPushSpeedScale; protected final Vector3d lastSeparationSteering = new Vector3d(); + protected int separationSummedCount; + protected final Vector3d separationSummedDistances = new Vector3d(); @Nullable protected final float[] headPitchAngleRange; protected final boolean stayInEnvironment; @@ -124,6 +148,8 @@ public class Role implements IAnnotatedComponentCollection { @Nullable protected final String[] offHandItems; protected final double deathAnimationTime; + protected final String deathParticles; + protected final boolean dropDeathItemsInstantly; protected final float despawnAnimationTime; protected final String dropListId; @Nullable @@ -143,7 +169,6 @@ public class Role implements IAnnotatedComponentCollection { protected int roleIndex; protected String roleName; protected String appearance; - protected boolean isActivated; @Nonnull protected Map motionControllers = new HashMap<>(); protected MotionController activeMotionController; @@ -171,6 +196,7 @@ public class Role implements IAnnotatedComponentCollection { protected final float spawnLockTime; protected final String nameTranslationKey; protected boolean backingAway; + protected boolean deathItemsDropped; public Role(@Nonnull BuilderRole builder, @Nonnull BuilderSupport builderSupport) { NPCEntity npcComponent = builderSupport.getEntity(); @@ -204,18 +230,29 @@ public class Role implements IAnnotatedComponentCollection { this.positionCache.setOpaqueBlockSet(builder.getOpaqueBlockSet()); this.dropListId = builder.getDropListId(builderSupport); this.isAvoidingEntities = builder.isAvoidingEntities(); - this.avoidanceMode = builder.getAvoidanceMode(); + this.avoidanceMode = builder.getAvoidanceMode(builderSupport); this.collisionProbeDistance = builder.getCollisionDistance(); this.collisionForceFalloff = builder.getCollisionForceFalloff(); this.collisionRadius = builder.getCollisionRadius(); this.collisionViewAngle = builder.getCollisionViewAngle(); this.collisionViewHalfAngleCosine = TrigMathUtil.cos(this.collisionViewAngle / 2.0F); + this.separationMode = builder.getSeparationMode(builderSupport); this.separationDistance = builder.getSeparationDistance(builderSupport); this.separationWeight = builder.getSeparationWeight(builderSupport); this.separationDistanceTarget = builder.getSeparationDistanceTarget(builderSupport); this.separationNearRadiusTarget = builder.getSeparationNearRadiusTarget(builderSupport); this.separationFarRadiusTarget = builder.getSeparationFarRadiusTarget(builderSupport); this.applySeparation = builder.isApplySeparation(builderSupport); + this.separationSafeDistanceMultiplier = builder.getSeparationSafeDistanceMultiplier(builderSupport); + this.separationLegacySteeringStrength = builder.getSeparationLegacySteeringStrength(builderSupport); + this.separationPushSteeringStrength = builder.getSeparationPushSteeringStrength(builderSupport); + this.separationPushDistanceWeightDefault = builder.getSeparationPushDistanceWeightDefault(builderSupport); + this.separationPushDistanceWeightTarget = builder.getSeparationPushDistanceWeightTarget(builderSupport); + this.separationPushDistanceWeightAttacker = builder.getSeparationPushDistanceWeightAttacker(builderSupport); + this.separationPushSpeedScale = builder.getSeparationPushSpeedScale(builderSupport); + this.useOrientationHint = builder.getOverrideUseOrientationHint(builderSupport).evaluate(this.separationMode != Role.SeparationMode.Legacy); + this.alwaysApplySeparation = builder.getOverrideAlwaysSeparate(builderSupport).evaluate(this.separationMode != Role.SeparationMode.Legacy); + this.normalizeDistances = builder.getOverrideNormalizeDistances(builderSupport).evaluate(this.separationMode != Role.SeparationMode.Legacy); if (builder.isOverridingHeadPitchAngle(builderSupport)) { this.headPitchAngleRange = builder.getHeadPitchAngleRange(builderSupport); } else { @@ -237,7 +274,9 @@ public class Role implements IAnnotatedComponentCollection { this.breathesInAir = builder.isBreathesInAir(builderSupport); this.breathesInWater = builder.isBreathesInWater(builderSupport); this.pickupDropOnDeath = builder.isPickupDropOnDeath(); - this.deathAnimationTime = builder.getDeathAnimationTime(); + this.deathAnimationTime = builder.getDeathAnimationTime(builderSupport); + this.deathParticles = builder.getDeathParticles(builderSupport); + this.dropDeathItemsInstantly = builder.isDropDeathItemsInstantly(builderSupport); this.deathInteraction = builder.getDeathInteraction(builderSupport); this.despawnAnimationTime = builder.getDespawnAnimationTime(); this.inventorySlots = builder.getInventorySlots(); @@ -399,7 +438,7 @@ public class Role implements IAnnotatedComponentCollection { stateTransitions.spawned(this); } - this.initialiseInventories(npcComponent); + this.initialiseInventories(npcComponent, holder); } public void unloaded() { @@ -632,14 +671,19 @@ public class Role implements IAnnotatedComponentCollection { boolean isDead = store.getArchetype(ref).contains(DeathComponent.getComponentType()); if (isDead) { - if (this.deathInstruction != null) { + if (this.deathInstruction != null && this.deathInstruction.matches(ref, this, tickTime, store)) { this.deathInstruction.execute(ref, this, tickTime, store); } } else { if (this.interactionInstruction != null) { this.positionCache.forEachPlayer((d, _playerRef, _this, _selfRef, _store) -> { _this.stateSupport.setInteractionIterationTarget(_playerRef); - _this.interactionInstruction.execute(_selfRef, _this, d, _store); + + assert _this.interactionInstruction != null; + + if (_this.interactionInstruction.matches(_selfRef, _this, d, _store)) { + _this.interactionInstruction.execute(_selfRef, _this, d, _store); + } }, this, ref, store, tickTime, store); this.stateSupport.setInteractionIterationTarget(null); this.entitySupport.clearTargetPlayerActiveTasks(); @@ -698,19 +742,65 @@ public class Role implements IAnnotatedComponentCollection { public void blendSeparation( @Nonnull Ref selfRef, @Nonnull Vector3d position, + @Nonnull Rotation3f rotation, @Nonnull Steering steering, @Nonnull ComponentType transformComponentType, @Nonnull CommandBuffer commandBuffer ) { - this.lastSeparationSteering.assign(Vector3d.ZERO); - double maxRange = this.separationDistance; + this.lastSeparationSteering.zero(); Ref targetRef = this.markedEntitySupport.getTargetReferenceToIgnoreForAvoidance(); - if (targetRef != null && targetRef.isValid()) { - TransformComponent targetTransformComponent = commandBuffer.getComponent(targetRef, transformComponentType); + Ref ignoredTargetRef = targetRef != null && targetRef.isValid() ? targetRef : null; + this.separationSummedDistances.zero(); + this.separationSummedCount = 0; + switch (this.separationMode) { + case Legacy: + this.computeSummedDistanceLegacy(selfRef, position, transformComponentType, commandBuffer, ignoredTargetRef, this.separationMode); + break; + case Push: + this.computeSummedDistancePush(selfRef, position, transformComponentType, commandBuffer, ignoredTargetRef); + break; + default: + return; + } + + if (this.debugSupport.isDebugFlagSet(RoleDebugFlags.VisSeparationSummed)) { + World world = commandBuffer.getExternalData().getWorld(); + Vector3d direction = new Vector3d(this.separationSummedDistances); + VisHelper.renderDebugVector(position, direction, DebugUtils.COLOR_BLACK, world); + } + + if (this.separationSummedCount != 0) { + if (!(this.separationSummedDistances.lengthSquared() < 0.010000000000000002)) { + switch (this.separationMode) { + case Legacy: + this.scaleSummedDistanceLegacy(rotation, steering); + break; + case Push: + this.scaleSummedDistancesPush(position, rotation, steering, commandBuffer); + } + + if (this.useOrientationHint) { + steering.setDirectionHint(rotation); + } + } + } + } + + private void computeSummedDistanceLegacy( + @Nonnull Ref selfRef, + @Nonnull Vector3d position, + @Nonnull ComponentType transformComponentType, + @Nonnull CommandBuffer commandBuffer, + @Nullable Ref ignoredTargetRef, + Role.SeparationMode separationMode + ) { + double maxRange = this.separationDistance; + if (ignoredTargetRef != null && ignoredTargetRef.isValid()) { + TransformComponent targetTransformComponent = commandBuffer.getComponent(ignoredTargetRef, transformComponentType); assert targetTransformComponent != null; - double distance = targetTransformComponent.getPosition().distanceSquaredTo(position); + double distance = targetTransformComponent.getPosition().distanceSquared(position); if (distance <= this.separationNearRadiusTarget * this.separationNearRadiusTarget) { maxRange = this.separationDistanceTarget; } else if (distance < this.separationFarRadiusTarget * this.separationFarRadiusTarget) { @@ -722,6 +812,7 @@ public class Role implements IAnnotatedComponentCollection { this.groupSteeringAccumulator.setComponentSelector(this.activeMotionController.getComponentSelector()); this.groupSteeringAccumulator.setMaxRange(maxRange); this.groupSteeringAccumulator.setViewConeHalfAngleCosine(this.collisionViewHalfAngleCosine); + this.groupSteeringAccumulator.setNormalizeDistances(this.normalizeDistances); this.groupSteeringAccumulator.begin(selfRef, commandBuffer); this.positionCache .forEachEntityInAvoidanceRange( @@ -732,19 +823,160 @@ public class Role implements IAnnotatedComponentCollection { commandBuffer ); this.groupSteeringAccumulator.end(); - if (this.groupSteeringAccumulator.getCount() > 0) { - Vector3d sumOfDistances = this.groupSteeringAccumulator.getSumOfDistances(); - if (sumOfDistances.squaredLength() > 1.0000000000000002E-10) { - double speed = steering.getSpeed(); - this.separation.assign(sumOfDistances).setLength(-0.5); - this.lastSeparationSteering.assign(this.separation); - if (speed > 0.0) { - this.separation.add(steering.getTranslation()); - this.separation.setLength(speed); - } + this.separationSummedDistances.set(this.groupSteeringAccumulator.getSumOfDistances()); + this.separationSummedCount = this.groupSteeringAccumulator.getCount(); + } - steering.setTranslation(this.separation); + private void scaleSummedDistanceLegacy(@Nonnull Rotation3f rotation, @Nonnull Steering steering) { + double speed = steering.getSpeed(); + this.separationTempDistanceVector.set(this.separationSummedDistances).normalize(-this.separationLegacySteeringStrength); + if (speed > 0.0) { + this.separationTempDistanceVector.add(steering.getTranslation()); + this.separationTempDistanceVector.normalize(speed); + } else if (this.alwaysApplySeparation) { + this.separationTempDistanceVector.add(steering.getTranslation()); + } + + this.lastSeparationSteering.set(this.separationTempDistanceVector).sub(steering.getTranslation()); + steering.setTranslation(this.separationTempDistanceVector); + } + + private void computeSummedDistancePush( + @Nonnull Ref selfRef, + @Nonnull Vector3d position, + @Nonnull ComponentType transformComponentType, + @Nonnull CommandBuffer commandBuffer, + @Nullable Ref ignoredTargetRef + ) { + double x = position.x(); + double y = position.y(); + double z = position.z(); + BodyMotion bodyMotion = this.getLastBodySteeringMotion(); + Ref desiredTargetEntity = bodyMotion != null ? bodyMotion.getDesiredTargetEntity() : null; + Ref motionTarget = desiredTargetEntity != null && desiredTargetEntity.isValid() ? desiredTargetEntity : null; + double targetDistance = motionTarget != null ? bodyMotion.getDesiredTargetDistance() : Double.MAX_VALUE; + double safeTargetDistance = targetDistance * this.separationSafeDistanceMultiplier; + boolean needSwitchDistance = safeTargetDistance < this.separationDistance; + double separationDistanceSquared = this.separationDistance * this.separationDistance; + Vector3d motionTargetPosition; + if (motionTarget != null) { + TransformComponent transformComponent = commandBuffer.getComponent(motionTarget, transformComponentType); + + assert transformComponent != null; + + motionTargetPosition = transformComponent.getPosition(); + } else { + motionTargetPosition = null; + } + + this.positionCache + .forEachEntityInAvoidanceRange( + this.ignoredEntitiesForAvoidance, + (ref, componentSelector, _role, componentAccessor) -> { + if (selfRef != ref) { + if (ignoredTargetRef != ref) { + TransformComponent transformComponentx = componentAccessor.getComponent(ref, transformComponentType); + + assert transformComponentx != null; + + Vector3d otherPosition = transformComponentx.getPosition(); + double maxRange = this.separationDistance; + double distanceWeight = this.separationPushDistanceWeightDefault; + if (needSwitchDistance) { + if (ref == motionTarget) { + double distanceSquared = NPCPhysicsMath.distanceSquaredWithSelector(motionTargetPosition, position, componentSelector); + if (distanceSquared <= separationDistanceSquared) { + maxRange = Math.max(Math.sqrt(distanceSquared) * this.separationSafeDistanceMultiplier, safeTargetDistance); + distanceWeight = this.separationPushDistanceWeightTarget; + } + } else if (motionTargetPosition != null + && NPCPhysicsMath.distanceSquaredWithSelector(otherPosition, motionTargetPosition, componentSelector) <= separationDistanceSquared) { + maxRange = safeTargetDistance; + distanceWeight = this.separationPushDistanceWeightAttacker; + } + } + + double dx = (otherPosition.x() - x) * componentSelector.x; + double dy = (otherPosition.y() - y) * componentSelector.y; + double dz = (otherPosition.z() - z) * componentSelector.z; + double d = NPCPhysicsMath.dotProduct(dx, dy, dz); + if (!(d > maxRange * maxRange)) { + double distance; + if (d < 1.0E-6) { + while (true) { + dx = RandomExtra.randomRange(-1.0, 1.0) * componentSelector.x; + dy = RandomExtra.randomRange(-1.0, 1.0) * componentSelector.y; + dz = RandomExtra.randomRange(-1.0, 1.0) * componentSelector.z; + d = NPCPhysicsMath.dotProduct(dx, dy, dz); + if (!(d < 1.0E-6)) { + double norm = 0.001 / Math.sqrt(d); + dx *= norm; + dy *= norm; + dz *= norm; + distance = 0.001; + break; + } + } + } else { + distance = Math.sqrt(d); + } + + d = distance / maxRange; + d = 1.0 - Math.pow(d, distanceWeight); + d /= distance; + dx *= d; + dy *= d; + dz *= d; + if (this.debugSupport.isDebugFlagSet(RoleDebugFlags.VisSeparationTargets)) { + World world = commandBuffer.getExternalData().getWorld(); + VisHelper.renderDebugSphere( + otherPosition, maxRange, maxRange == this.separationDistance ? DebugUtils.COLOR_WHITE : DebugUtils.COLOR_CYAN, world + ); + Vector3d direction = new Vector3d(dx, dy, dz); + VisHelper.renderDebugVector(position, direction, DebugUtils.COLOR_YELLOW, world); + } + + this.separationSummedDistances.add(dx, dy, dz); + this.separationSummedCount++; + } + } + } + }, + this.activeMotionController.getComponentSelector(), + this, + commandBuffer + ); + } + + private void scaleSummedDistancesPush( + @Nonnull Vector3d position, @Nonnull Rotation3f rotation, @Nonnull Steering steering, @Nonnull CommandBuffer commandBuffer + ) { + this.separationTempDistanceVector.set(this.separationSummedDistances).mul(this.activeMotionController.getComponentSelector()); + double separationSquaredLength = this.separationTempDistanceVector.lengthSquared(); + if (separationSquaredLength > 1.0) { + this.separationTempDistanceVector.normalize(); + } + + this.separationTempDistanceVector.mul(-this.separationPushSteeringStrength); + this.separationTempSteeringVector.set(steering.getTranslation()).mul(this.activeMotionController.getComponentSelector()); + double speedSquared = this.separationTempSteeringVector.lengthSquared(); + if (speedSquared < 1.0000000000000002E-10) { + if (!this.alwaysApplySeparation) { + return; } + + steering.setTranslation(this.separationTempDistanceVector); + this.lastSeparationSteering.set(this.separationTempDistanceVector); + } else { + double speed = Math.pow(speedSquared, this.separationPushSpeedScale * 0.5); + this.separationTempDistanceVector.add(this.separationTempSteeringVector).normalize(speed).sub(this.separationTempSteeringVector); + this.separationTempSteeringVector.set(steering.getTranslation()).add(this.separationTempDistanceVector); + if (this.separationTempSteeringVector.lengthSquared() > 1.0) { + this.separationTempSteeringVector.normalize(); + } + + steering.setTranslation(this.separationTempSteeringVector); + this.lastSeparationSteering.set(this.separationTempDistanceVector); } } @@ -754,7 +986,11 @@ public class Role implements IAnnotatedComponentCollection { } public void blendAvoidance( - @Nonnull Ref ref, @Nonnull Vector3d position, @Nonnull Steering steering, @Nonnull CommandBuffer commandBuffer + @Nonnull Ref ref, + @Nonnull Vector3d position, + @Nonnull Rotation3f rotation, + @Nonnull Steering steering, + @Nonnull CommandBuffer commandBuffer ) { this.steeringForceAvoidCollision.setDebug(this.debugSupport.isDebugRoleSteering()); this.steeringForceAvoidCollision.setAvoidanceMode(this.getAvoidanceMode()); @@ -859,7 +1095,7 @@ public class Role implements IAnnotatedComponentCollection { public void processSetVelocityInstruction(@Nonnull Vector3d velocity, @Nullable VelocityConfig velocityConfig) { if (this.activeMotionController != null) { - this.activeMotionController.forceVelocity(Vector3d.ZERO, null, false); + this.activeMotionController.forceVelocity(Vector3dUtil.ZERO, null, false); this.activeMotionController.addForce(velocity, velocityConfig); } } @@ -966,6 +1202,23 @@ public class Role implements IAnnotatedComponentCollection { return this.deathAnimationTime; } + @Nullable + public String getDeathParticles() { + return this.deathParticles; + } + + public boolean isDropDeathItemsInstantly() { + return this.dropDeathItemsInstantly; + } + + public boolean hasDroppedDeathItems() { + return this.deathItemsDropped; + } + + public void setDeathItemsDropped() { + this.deathItemsDropped = true; + } + @Nullable public String getDeathInteraction() { return this.deathInteraction; @@ -1027,18 +1280,26 @@ public class Role implements IAnnotatedComponentCollection { @Nullable public String getSteeringMotionName() { + BodyMotion motion = this.getLastBodySteeringMotion(); + return motion == null ? null : motion.getClass().getSimpleName(); + } + + @Nullable + public BodyMotion getLastBodySteeringMotion() { if (this.lastBodyMotionStep == null) { return null; } else { BodyMotion motion = this.lastBodyMotionStep.getBodyMotion(); - if (motion != null) { - motion = motion.getSteeringMotion(); - } - - return motion == null ? null : motion.getClass().getSimpleName(); + return motion == null ? null : motion.getSteeringMotion(); } } + @Nullable + public EnumSet getSteeringRelaxedConstraints() { + BodyMotion motion = this.getLastBodySteeringMotion(); + return motion == null ? null : motion.getRelaxedConstraints(); + } + @Override public int componentCount() { return 1; @@ -1074,7 +1335,7 @@ public class Role implements IAnnotatedComponentCollection { return this.roleName; } - private void initialiseInventories(@Nonnull NPCEntity npcComponent) { + private void initialiseInventories(@Nonnull NPCEntity npcComponent, @Nonnull Holder holder) { List inventoryItems = null; if (this.inventoryContentsDropList != null) { ItemModule itemModule = ItemModule.get(); @@ -1085,7 +1346,21 @@ public class Role implements IAnnotatedComponentCollection { int inventorySlots = inventoryItems != null && inventoryItems.size() > this.inventorySlots ? inventoryItems.size() : this.inventorySlots; if (inventorySlots > 0 || this.hotbarSlots > 3 || this.offHandSlots > 0) { - npcComponent.setInventorySize(this.hotbarSlots, inventorySlots, this.offHandSlots); + ObjectArrayList remainder = new ObjectArrayList<>(); + InventoryComponent.Hotbar hotbar = holder.getComponent(InventoryComponent.Hotbar.getComponentType()); + if (hotbar != null) { + hotbar.ensureCapacity((short)this.hotbarSlots, remainder); + } + + InventoryComponent.Utility utility = holder.getComponent(InventoryComponent.Utility.getComponentType()); + if (utility != null) { + utility.ensureCapacity((short)this.offHandSlots, remainder); + } + + InventoryComponent.Storage storage = holder.getComponent(InventoryComponent.Storage.getComponentType()); + if (storage != null) { + storage.ensureCapacity((short)inventorySlots, remainder); + } } if (inventoryItems != null) { @@ -1096,6 +1371,52 @@ public class Role implements IAnnotatedComponentCollection { } } + this.initialiseItemsAndArmor(npcComponent); + if (this.defaultOffHandSlot >= 0) { + InventoryHelper.setOffHandSlot(holder, npcComponent.getInventory(), this.defaultOffHandSlot); + } + } + + private void initialiseInventories(@Nonnull NPCEntity npcComponent, @Nonnull ComponentAccessor accessor, @Nonnull Ref ref) { + List inventoryItems = null; + if (this.inventoryContentsDropList != null) { + ItemModule itemModule = ItemModule.get(); + if (itemModule.isEnabled()) { + inventoryItems = itemModule.getRandomItemDrops(this.inventoryContentsDropList); + } + } + + int inventorySlots = inventoryItems != null && inventoryItems.size() > this.inventorySlots ? inventoryItems.size() : this.inventorySlots; + if (inventorySlots > 0 || this.hotbarSlots > 3 || this.offHandSlots > 0) { + ObjectArrayList remainder = new ObjectArrayList<>(); + InventoryComponent.Hotbar hotbar = accessor.getComponent(ref, InventoryComponent.Hotbar.getComponentType()); + if (hotbar != null) { + hotbar.ensureCapacity((short)this.hotbarSlots, remainder); + } + + InventoryComponent.Utility utility = accessor.getComponent(ref, InventoryComponent.Utility.getComponentType()); + if (utility != null) { + utility.ensureCapacity((short)this.offHandSlots, remainder); + } + + InventoryComponent.Storage storage = accessor.getComponent(ref, InventoryComponent.Storage.getComponentType()); + if (storage != null) { + storage.ensureCapacity((short)inventorySlots, remainder); + } + } + + if (inventoryItems != null) { + ItemContainer inventory = npcComponent.getInventory().getStorage(); + + for (ItemStack item : inventoryItems) { + inventory.addItemStack(item); + } + } + + this.initialiseInventories(ref, npcComponent, accessor); + } + + private void initialiseItemsAndArmor(@Nonnull NPCEntity npcComponent) { if (this.hotbarItems != null && this.hotbarItems.length > 0 && npcComponent.getInventory().getHotbar().isEmpty()) { Inventory inventory = npcComponent.getInventory(); ItemContainer hotbar = inventory.getHotbar(); @@ -1120,13 +1441,16 @@ public class Role implements IAnnotatedComponentCollection { RoleUtils.setOffHandItems(npcComponent, this.offHandItems); } - if (this.defaultOffHandSlot >= 0) { - InventoryHelper.setOffHandSlot(npcComponent.getInventory(), this.defaultOffHandSlot); - } - this.setArmor(npcComponent, this.armor); } + private void initialiseInventories(@Nonnull Ref ref, @Nonnull NPCEntity npcComponent, @Nonnull ComponentAccessor componentAccessor) { + this.initialiseItemsAndArmor(npcComponent); + if (this.defaultOffHandSlot >= 0) { + InventoryHelper.setOffHandSlot(ref, npcComponent.getInventory(), this.defaultOffHandSlot, componentAccessor); + } + } + public boolean isCorpseStaysInFlock() { return this.corpseStaysInFlock; } @@ -1135,7 +1459,7 @@ public class Role implements IAnnotatedComponentCollection { @Nonnull Ref ref, @Nonnull NPCEntity npcComponent, @Nonnull ComponentAccessor componentAccessor ) { this.entitySupport.pickRandomDisplayName(ref, true, componentAccessor); - this.initialiseInventories(npcComponent); + this.initialiseInventories(npcComponent, componentAccessor, ref); } public RoleStats getRoleStats() { @@ -1164,4 +1488,19 @@ public class Role implements IAnnotatedComponentCollection { public interface DeferredAction { boolean tick(@Nonnull Ref var1, @Nonnull Role var2, double var3, @Nonnull Store var5); } + + public static enum SeparationMode implements Supplier { + Legacy("Flock like separation force"), + Push("Push away from all neighbours and also applied when no other steering happens"); + + private final String description; + + private SeparationMode(String description) { + this.description = description; + } + + public String get() { + return this.description; + } + } } diff --git a/src/com/hypixel/hytale/server/npc/role/RoleDebugDisplay.java b/src/com/hypixel/hytale/server/npc/role/RoleDebugDisplay.java index fa35da9b..f3ac60d7 100644 --- a/src/com/hypixel/hytale/server/npc/role/RoleDebugDisplay.java +++ b/src/com/hypixel/hytale/server/npc/role/RoleDebugDisplay.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.protocol.MovementStates; import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; @@ -33,6 +32,7 @@ import java.time.temporal.ChronoField; import java.util.EnumSet; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class RoleDebugDisplay { protected boolean debugDisplayState; @@ -160,9 +160,9 @@ public class RoleDebugDisplay { assert blockChunkComponent != null; Vector3d position = transformComponent.getPosition(); - int x = MathUtil.floor(position.getX()); - int y = MathUtil.floor(position.getY()); - int z = MathUtil.floor(position.getZ()); + int x = MathUtil.floor(position.x()); + int y = MathUtil.floor(position.y()); + int z = MathUtil.floor(position.z()); double sunlightFactor = worldTimeResource.getSunlightFactor(); this.debugDisplay .append(" LL:") diff --git a/src/com/hypixel/hytale/server/npc/role/RoleDebugFlags.java b/src/com/hypixel/hytale/server/npc/role/RoleDebugFlags.java index 64e07737..32107921 100644 --- a/src/com/hypixel/hytale/server/npc/role/RoleDebugFlags.java +++ b/src/com/hypixel/hytale/server/npc/role/RoleDebugFlags.java @@ -36,12 +36,19 @@ public enum RoleDebugFlags implements Supplier { DisplayName("Display the role name for this entity"), ValidateMath("Validate (some) math computations in movement"), VisAvoidance("Visualize avoidance vectors"), + VisSeparationSummed("Visualize summed separation distance vector"), VisSeparation("Visualize separation vector"), + VisSeparationTargets("Visualize separation targets"), + VisOrientation("Visualize orientation hint provided by avoidance and separation"), VisAiming("Visualize aiming"), VisMarkedTargets("Visualize arrows to marked targets"), VisSensorRanges("Visualize entity sensor detection ranges"), VisLeashPosition("Visualize NPC leash position"), + VisSteeringPre("Visualize motion steering pre separation/avoidance"), + VisSteeringPost("Visualize motion steering post separation/avoidance"), + VisTranslation("Visualize current translation vector"), VisFlock("Visualize flock member connections"), + VisPath("Visualize pathfinding waypoints"), BeaconMessages("Enable debugging of beacon message sending and receiving"); private static final RoleDebugFlags.RoleDebugPreset[] presets = new RoleDebugFlags.RoleDebugPreset[]{ @@ -51,7 +58,10 @@ public enum RoleDebugFlags implements Supplier { new RoleDebugFlags.RoleDebugPreset("steer", EnumSet.of(MotionControllerMove, MotionControllerSteer, Collisions)), new RoleDebugFlags.RoleDebugPreset("valid", EnumSet.of(MotionControllerMove, MotionControllerSteer, Collisions, ValidatePositions)), new RoleDebugFlags.RoleDebugPreset("block", EnumSet.of(MotionControllerMove, MotionControllerSteer, Collisions, BlockCollisions)), - new RoleDebugFlags.RoleDebugPreset("visDist", EnumSet.of(VisAvoidance, VisSeparation)), + new RoleDebugFlags.RoleDebugPreset( + "visDist", EnumSet.of(VisAvoidance, VisSeparation, VisSteeringPre, VisSteeringPost, VisSeparationTargets, VisSeparationSummed) + ), + new RoleDebugFlags.RoleDebugPreset("visMove", EnumSet.of(VisSteeringPost, VisTranslation)), new RoleDebugFlags.RoleDebugPreset("visSensorInfo", EnumSet.of(VisMarkedTargets, VisSensorRanges)), new RoleDebugFlags.RoleDebugPreset( "display", diff --git a/src/com/hypixel/hytale/server/npc/role/RoleUtils.java b/src/com/hypixel/hytale/server/npc/role/RoleUtils.java index 4af2b3de..5fb71eb9 100644 --- a/src/com/hypixel/hytale/server/npc/role/RoleUtils.java +++ b/src/com/hypixel/hytale/server/npc/role/RoleUtils.java @@ -1,6 +1,9 @@ package com.hypixel.hytale.server.npc.role; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.NPCPlugin; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.util.InventoryHelper; @@ -25,8 +28,10 @@ public class RoleUtils { } } - public static void setItemInHand(@Nonnull NPCEntity npcComponent, @Nullable String itemInHand) { - if (!InventoryHelper.useItem(npcComponent.getInventory(), itemInHand)) { + public static void setItemInHand( + @Nonnull Ref ref, @Nonnull NPCEntity npcComponent, @Nullable String itemInHand, @Nonnull ComponentAccessor componentAccessor + ) { + if (!InventoryHelper.useItem(ref, npcComponent.getInventory(), itemInHand, componentAccessor)) { NPCPlugin.get().getLogger().at(Level.WARNING).log("NPC of type '%s': Failed to use item '%s'", npcComponent.getRoleName(), itemInHand); } } diff --git a/src/com/hypixel/hytale/server/npc/role/SpawnEffect.java b/src/com/hypixel/hytale/server/npc/role/SpawnEffect.java index 432239e6..f6526f29 100644 --- a/src/com/hypixel/hytale/server/npc/role/SpawnEffect.java +++ b/src/com/hypixel/hytale/server/npc/role/SpawnEffect.java @@ -1,36 +1,72 @@ package com.hypixel.hytale.server.npc.role; import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.protocol.packets.entities.SpawnModelParticles; +import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle; import com.hypixel.hytale.server.core.modules.entity.EntityModule; +import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; +import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.world.ParticleUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; -import it.unimi.dsi.fastutil.objects.ObjectList; +import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; +import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; public interface SpawnEffect { - String getSpawnParticles(); + String getSpawnParticles(@Nonnull BuilderSupport var1); - Vector3d getSpawnParticleOffset(); + Vector3d getSpawnParticleOffset(@Nonnull BuilderSupport var1); + + String getSpawnParticleTargetNode(@Nonnull BuilderSupport var1); + + boolean isSpawnParticleDetached(@Nonnull BuilderSupport var1); double getSpawnViewDistance(); - default void spawnEffect(@Nonnull Vector3d position, @Nonnull Vector3f rotation, @Nonnull ComponentAccessor componentAccessor) { - String particles = this.getSpawnParticles(); + default void spawnEffect( + @Nonnull Holder holder, + @Nonnull BuilderSupport support, + @Nonnull Vector3d position, + @Nonnull Rotation3f rotation, + @Nonnull ComponentAccessor componentAccessor + ) { + String particles = this.getSpawnParticles(support); if (particles != null && !particles.isEmpty()) { Vector3d spawnPosition = new Vector3d(0.0, 0.0, 0.0); - if (this.getSpawnParticleOffset() != null) { - spawnPosition.assign(this.getSpawnParticleOffset()); + Vector3d offset = this.getSpawnParticleOffset(support); + if (offset != null) { + spawnPosition.set(offset); } - spawnPosition.rotateY(rotation.getYaw()).add(position); + spawnPosition.rotateY(rotation.yaw()).add(position); SpatialResource, EntityStore> playerSpatialResource = componentAccessor.getResource(EntityModule.get().getPlayerSpatialResourceType()); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); playerSpatialResource.getSpatialStructure().collect(spawnPosition, this.getSpawnViewDistance(), results); ParticleUtil.spawnParticleEffect(particles, spawnPosition, results, componentAccessor); + ModelParticle particle = new ModelParticle(); + particle.setSystemId(particles); + particle.setPositionOffset(new Vector3f((float)spawnPosition.x, (float)spawnPosition.y, (float)spawnPosition.z)); + particle.setTargetNodeName(this.getSpawnParticleTargetNode(support)); + particle.setDetachedFromModel(this.isSpawnParticleDetached(support)); + NetworkId networkIdComponent = holder.getComponent(NetworkId.getComponentType()); + if (networkIdComponent != null) { + SpawnModelParticles packet = new SpawnModelParticles( + networkIdComponent.getId(), new com.hypixel.hytale.protocol.ModelParticle[]{particle.toPacket()} + ); + + for (Ref playerRef : results) { + PlayerRef playerRefComponent = componentAccessor.getComponent(playerRef, PlayerRef.getComponentType()); + if (playerRefComponent != null) { + playerRefComponent.getPacketHandler().write(packet); + } + } + } } } } diff --git a/src/com/hypixel/hytale/server/npc/role/builders/BuilderRole.java b/src/com/hypixel/hytale/server/npc/role/builders/BuilderRole.java index 7841380e..e2df4281 100644 --- a/src/com/hypixel/hytale/server/npc/role/builders/BuilderRole.java +++ b/src/com/hypixel/hytale/server/npc/role/builders/BuilderRole.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.npc.role.builders; import com.google.gson.JsonElement; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.attitude.Attitude; import com.hypixel.hytale.server.core.asset.type.blockset.config.BlockSet; import com.hypixel.hytale.server.npc.NPCPlugin; @@ -17,6 +16,7 @@ import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.asset.builder.BuilderTemplateInteractionVars; import com.hypixel.hytale.server.npc.asset.builder.BuilderValidationHelper; import com.hypixel.hytale.server.npc.asset.builder.FeatureEvaluatorHelper; +import com.hypixel.hytale.server.npc.asset.builder.FeatureOverride; import com.hypixel.hytale.server.npc.asset.builder.InstructionContextHelper; import com.hypixel.hytale.server.npc.asset.builder.InstructionType; import com.hypixel.hytale.server.npc.asset.builder.SpawnableWithModelBuilder; @@ -46,6 +46,7 @@ import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ItemAttitude import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ItemDropListExistsValidator; import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ItemExistsValidator; import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ModelExistsValidator; +import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ParticleSystemExistsValidator; import com.hypixel.hytale.server.npc.asset.builder.validators.asset.RootInteractionValidator; import com.hypixel.hytale.server.npc.config.AttitudeGroup; import com.hypixel.hytale.server.npc.config.ItemAttitudeGroup; @@ -74,6 +75,7 @@ import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BuilderRole extends SpawnableWithModelBuilder implements SpawnEffect { protected static final double[] DEFAULT_HEAD_PITCH_RANGE = new double[]{-89.0, 89.0}; @@ -102,6 +104,19 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw protected final DoubleHolder separationNearRadiusTarget = new DoubleHolder(); protected final DoubleHolder separationFarRadiusTarget = new DoubleHolder(); protected final BooleanHolder applySeparation = new BooleanHolder(); + protected final BooleanHolder avoidanceFallCheck = new BooleanHolder(); + protected final EnumHolder avoidanceMode = new EnumHolder<>(); + protected final EnumHolder separationMode = new EnumHolder<>(); + protected final EnumHolder featureOverrideOrientationHint = new EnumHolder<>(); + protected final EnumHolder featureOverrideAlwaysSeparate = new EnumHolder<>(); + protected final EnumHolder featureOverrideNormalizeDistances = new EnumHolder<>(); + protected final DoubleHolder separationSafeDistanceMultiplier = new DoubleHolder(); + protected final DoubleHolder separationLegacySteeringStrength = new DoubleHolder(); + protected final DoubleHolder separationPushSteeringStrength = new DoubleHolder(); + protected final DoubleHolder separationPushDistanceWeightDefault = new DoubleHolder(); + protected final DoubleHolder separationPushDistanceWeightTarget = new DoubleHolder(); + protected final DoubleHolder separationPushDistanceWeightAttacker = new DoubleHolder(); + protected final DoubleHolder separationPushSpeedScale = new DoubleHolder(); protected boolean stayInEnvironment; protected String allowedEnvironments; protected final StringArrayHolder flockSpawnTypes = new StringArrayHolder(); @@ -123,15 +138,18 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw protected final IntHolder defaultOffHandSlot = new IntHolder(); protected boolean pickupDropOnDeath; protected String[] armor; - protected double deathAnimationTime; + protected final DoubleHolder deathAnimationTime = new DoubleHolder(); + protected final AssetHolder deathParticles = new AssetHolder(); + protected final BooleanHolder dropDeathItemsInstantly = new BooleanHolder(); protected float despawnAnimationTime; @Nonnull protected AssetHolder deathInteraction = new AssetHolder(); - protected Role.AvoidanceMode avoidanceMode; protected boolean disableDamageFlock; protected final AssetArrayHolder disableDamageGroups = new AssetArrayHolder(); - protected String spawnParticles; - protected double[] spawnParticleOffset; + protected final AssetHolder spawnParticles = new AssetHolder(); + protected final NumberArrayHolder spawnParticleOffset = new NumberArrayHolder(); + protected final StringHolder spawnParticlesTargetNode = new StringHolder(); + protected final BooleanHolder spawnParticlesDetached = new BooleanHolder(); protected double spawnViewDistance; protected int inventorySlots; protected int hotbarSlots; @@ -474,17 +492,130 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw null ); this.getBoolean(data, "ApplyAvoidance", b -> this.applyAvoidance = b, false, BuilderDescriptorState.Experimental, "Apply avoidance steering force", null); + this.getBoolean( + data, + "AvoidanceFallCheck", + this.avoidanceFallCheck, + false, + BuilderDescriptorState.Deprecated, + "Deprecated no-op retained for backwards compatibility", + null + ); this.getBoolean(data, "ApplySeparation", this.applySeparation, false, BuilderDescriptorState.Experimental, "Apply separation steering force", null); this.getEnum( data, "AvoidanceMode", - e -> this.avoidanceMode = e, + this.avoidanceMode, Role.AvoidanceMode.class, Role.AvoidanceMode.Any, BuilderDescriptorState.Experimental, "Abilities to use for avoidance", null ); + this.getEnum( + data, + "SeparationMode", + this.separationMode, + Role.SeparationMode.class, + Role.SeparationMode.Legacy, + BuilderDescriptorState.Experimental, + "Mode for separation calculation", + null + ); + this.getDouble( + data, + "SeparationPushSafeDistanceMultiplier", + this.separationSafeDistanceMultiplier, + 0.8, + DoubleRangeValidator.between01(), + BuilderDescriptorState.Experimental, + "Push Mode: Multiplier to reduce target distance to avoid NPC jittering close to target", + null + ); + this.getDouble( + data, + "SeparationLegacySteeringStrength", + this.separationLegacySteeringStrength, + 0.5, + DoubleSingleValidator.greater0(), + BuilderDescriptorState.Experimental, + "Legacy mode: Separation steering strength", + null + ); + this.getDouble( + data, + "SeparationPushSteeringStrength", + this.separationPushSteeringStrength, + 0.8, + DoubleSingleValidator.greater0(), + BuilderDescriptorState.Experimental, + "Push mode: Separation steering strength", + null + ); + this.getDouble( + data, + "SeparationPushDistanceWeightDefault", + this.separationPushDistanceWeightDefault, + 1.0, + DoubleSingleValidator.greater0(), + BuilderDescriptorState.Experimental, + "Push mode: falloff exponent for separation distance", + null + ); + this.getDouble( + data, + "SeparationPushDistanceWeightTarget", + this.separationPushDistanceWeightTarget, + 8.0, + DoubleSingleValidator.greater0(), + BuilderDescriptorState.Experimental, + "Push mode: falloff exponent when NPC is close to motion target", + null + ); + this.getDouble( + data, + "SeparationPushDistanceWeightAttacker", + this.separationPushDistanceWeightAttacker, + 8.0, + DoubleSingleValidator.greater0(), + BuilderDescriptorState.Experimental, + "Push mode: falloff exponent towards other potential attackers when close to motion target", + null + ); + this.getDouble( + data, + "SeparationPushSpeedScale", + this.separationPushSpeedScale, + 0.5, + DoubleRangeValidator.fromExclToIncl(0.0, 1.0), + BuilderDescriptorState.Experimental, + "Push mode: scale to increase smaller speeds for separation", + null + ); + this.getOverride( + data, + "SeparationOverrideOrientation", + this.featureOverrideOrientationHint, + BuilderDescriptorState.Experimental, + "Whether to override orientation of NPC when separating", + null + ); + this.getOverride( + data, + "SeparationOverrideAlwaysSeparate", + this.featureOverrideAlwaysSeparate, + BuilderDescriptorState.Experimental, + "Whether to separate even if the NPC would rest", + null + ); + this.getOverride( + data, + "SeparationOverrideNormalizeDistances", + this.featureOverrideNormalizeDistances, + BuilderDescriptorState.Experimental, + "Whether to normalize distances when summing distances", + null + ); this.getDouble( data, "EntityAvoidanceStrength", @@ -622,13 +753,32 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw this.getDouble( data, "DeathAnimationTime", - d -> this.deathAnimationTime = d, - 5.0, + this.deathAnimationTime, + 1.5, DoubleSingleValidator.greaterEqual0(), BuilderDescriptorState.Experimental, "How long to let the death animation play before removing", null ); + this.getAsset( + data, + "DeathParticles", + this.deathParticles, + "Effect_Death", + ParticleSystemExistsValidator.withConfig(AssetValidator.CanBeEmpty), + BuilderDescriptorState.Stable, + "Particles to play when the NPC corpse is removed.", + null + ); + this.getBoolean( + data, + "DropDeathItemsInstantly", + this.dropDeathItemsInstantly, + false, + BuilderDescriptorState.Stable, + "Whether or not to drop death items instantly on death, or when the body disappears.", + null + ); this.getAsset( data, "DeathInteraction", @@ -649,19 +799,38 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw "How long to let the despawn animation play before removing", null ); - this.getString( - data, "SpawnParticles", v -> this.spawnParticles = v, null, null, BuilderDescriptorState.Experimental, "Particle system when spawning", null + this.getAsset( + data, + "SpawnParticles", + this.spawnParticles, + null, + ParticleSystemExistsValidator.withConfig(AssetValidator.CanBeEmpty), + BuilderDescriptorState.Experimental, + "Particle system when spawning", + null ); this.getVector3d( data, "SpawnParticlesOffset", - v -> this.spawnParticleOffset = v, + this.spawnParticleOffset, null, null, BuilderDescriptorState.Experimental, "Displacement from foot point to spawn relative to NPC heading", null ); + this.getString( + data, "SpawnParticlesTargetNode", this.spawnParticlesTargetNode, null, null, BuilderDescriptorState.Stable, "Target node for spawn particles", null + ); + this.getBoolean( + data, + "SpawnParticlesDetached", + this.spawnParticlesDetached, + true, + BuilderDescriptorState.Stable, + "Whether spawn particles should be attached to the model or not", + null + ); this.getDouble( data, "SpawnViewDistance", @@ -932,13 +1101,23 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw } @Override - public String getSpawnParticles() { - return this.spawnParticles; + public String getSpawnParticles(@Nonnull BuilderSupport support) { + return this.spawnParticles.get(support.getExecutionContext()); } @Override - public Vector3d getSpawnParticleOffset() { - return createVector3d(this.spawnParticleOffset, Vector3d.ZERO::clone); + public Vector3d getSpawnParticleOffset(@Nonnull BuilderSupport support) { + return createVector3d(this.spawnParticleOffset.get(support.getExecutionContext()), Vector3d::new); + } + + @Override + public String getSpawnParticleTargetNode(@Nonnull BuilderSupport support) { + return this.spawnParticlesTargetNode.get(support.getExecutionContext()); + } + + @Override + public boolean isSpawnParticleDetached(@Nonnull BuilderSupport support) { + return this.spawnParticlesDetached.get(support.getExecutionContext()); } @Override @@ -1043,8 +1222,52 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw return this.applyAvoidance; } - public Role.AvoidanceMode getAvoidanceMode() { - return this.avoidanceMode; + public Role.AvoidanceMode getAvoidanceMode(BuilderSupport builderSupport) { + return this.avoidanceMode.get(builderSupport.getExecutionContext()); + } + + public Role.SeparationMode getSeparationMode(BuilderSupport builderSupport) { + return this.separationMode.get(builderSupport.getExecutionContext()); + } + + public double getSeparationSafeDistanceMultiplier(BuilderSupport builderSupport) { + return this.separationSafeDistanceMultiplier.get(builderSupport.getExecutionContext()); + } + + public double getSeparationLegacySteeringStrength(BuilderSupport builderSupport) { + return this.separationLegacySteeringStrength.get(builderSupport.getExecutionContext()); + } + + public double getSeparationPushSteeringStrength(BuilderSupport builderSupport) { + return this.separationPushSteeringStrength.get(builderSupport.getExecutionContext()); + } + + public double getSeparationPushDistanceWeightDefault(BuilderSupport builderSupport) { + return this.separationPushDistanceWeightDefault.get(builderSupport.getExecutionContext()); + } + + public double getSeparationPushDistanceWeightTarget(BuilderSupport builderSupport) { + return this.separationPushDistanceWeightTarget.get(builderSupport.getExecutionContext()); + } + + public double getSeparationPushDistanceWeightAttacker(BuilderSupport builderSupport) { + return this.separationPushDistanceWeightAttacker.get(builderSupport.getExecutionContext()); + } + + public double getSeparationPushSpeedScale(BuilderSupport builderSupport) { + return this.separationPushSpeedScale.get(builderSupport.getExecutionContext()); + } + + public FeatureOverride getOverrideUseOrientationHint(BuilderSupport builderSupport) { + return this.featureOverrideOrientationHint.get(builderSupport.getExecutionContext()); + } + + public FeatureOverride getOverrideAlwaysSeparate(BuilderSupport builderSupport) { + return this.featureOverrideAlwaysSeparate.get(builderSupport.getExecutionContext()); + } + + public FeatureOverride getOverrideNormalizeDistances(BuilderSupport builderSupport) { + return this.featureOverrideNormalizeDistances.get(builderSupport.getExecutionContext()); } public double getCollisionRadius() { @@ -1075,6 +1298,10 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw return this.applySeparation.get(support.getExecutionContext()); } + public boolean isAvoidanceFallCheck(BuilderSupport builderSupport) { + return this.avoidanceFallCheck.get(builderSupport.getExecutionContext()); + } + public boolean isStayingInEnvironment() { return this.stayInEnvironment; } @@ -1144,8 +1371,16 @@ public class BuilderRole extends SpawnableWithModelBuilder implements Spaw return this.combatConfig.build(support.getExecutionContext()); } - public double getDeathAnimationTime() { - return this.deathAnimationTime; + public double getDeathAnimationTime(@Nonnull BuilderSupport support) { + return this.deathAnimationTime.get(support.getExecutionContext()); + } + + public String getDeathParticles(@Nonnull BuilderSupport support) { + return this.deathParticles.get(support.getExecutionContext()); + } + + public boolean isDropDeathItemsInstantly(@Nonnull BuilderSupport support) { + return this.dropDeathItemsInstantly.get(support.getExecutionContext()); } @Nullable diff --git a/src/com/hypixel/hytale/server/npc/role/support/DebugSupport.java b/src/com/hypixel/hytale/server/npc/role/support/DebugSupport.java index a753b451..a9dff1d3 100644 --- a/src/com/hypixel/hytale/server/npc/role/support/DebugSupport.java +++ b/src/com/hypixel/hytale/server/npc/role/support/DebugSupport.java @@ -7,13 +7,14 @@ import com.hypixel.hytale.server.npc.instructions.Sensor; import com.hypixel.hytale.server.npc.role.RoleDebugDisplay; import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.role.builders.BuilderRole; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import java.util.ArrayList; import java.util.EnumSet; -import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class DebugSupport { protected final NPCEntity parent; @@ -37,6 +38,9 @@ public class DebugSupport { protected List sensorVisDataList; @Nullable protected Map, List> entityVisDataMap; + protected boolean visPath; + @Nullable + protected List pathVisDataList; public DebugSupport(NPCEntity parent, @Nonnull BuilderRole builder) { this.parent = parent; @@ -76,7 +80,7 @@ public class DebugSupport { return this.debugMotionSteering; } - public void setDisplayCustomString(String displayCustomString) { + public void setDisplayCustomString(@Nullable String displayCustomString) { this.displayCustomString = displayCustomString; } @@ -87,7 +91,7 @@ public class DebugSupport { return ret; } - public void setDisplayPathfinderString(String displayPathfinderString) { + public void setDisplayPathfinderString(@Nullable String displayPathfinderString) { this.displayPathfinderString = displayPathfinderString; } @@ -129,6 +133,7 @@ public class DebugSupport { this.traceSuccess = this.isDebugFlagSet(RoleDebugFlags.TraceSuccess); this.traceSensorFails = this.isDebugFlagSet(RoleDebugFlags.TraceSensorFailures); this.visSensorRanges = this.isDebugFlagSet(RoleDebugFlags.VisSensorRanges); + this.visPath = this.isDebugFlagSet(RoleDebugFlags.VisPath); this.debugDisplay = RoleDebugDisplay.create(this.debugFlags, this.debugDisplay); } @@ -175,7 +180,7 @@ public class DebugSupport { public void recordEntityCheck(@Nonnull Ref entityRef, int sensorColorIndex, boolean matched) { if (this.entityVisDataMap == null) { - this.entityVisDataMap = new HashMap<>(); + this.entityVisDataMap = new Reference2ObjectOpenHashMap<>(); } this.entityVisDataMap.computeIfAbsent(entityRef, k -> new ArrayList<>()).add(new DebugSupport.EntityVisData(sensorColorIndex, matched)); @@ -201,6 +206,37 @@ public class DebugSupport { } } + public boolean isVisPath() { + return this.visPath; + } + + public void clearPathVisualization() { + if (this.pathVisDataList != null) { + this.pathVisDataList.clear(); + } + } + + public void recordPathWaypoint(@Nonnull Vector3d position, boolean isCurrentTarget, boolean isEndNode) { + this.recordPathWaypoint(position, isCurrentTarget, isEndNode, false); + } + + public void recordPathWaypoint(@Nonnull Vector3d position, boolean isCurrentTarget, boolean isEndNode, boolean isSeekTarget) { + if (this.pathVisDataList == null) { + this.pathVisDataList = new ArrayList<>(); + } + + this.pathVisDataList.add(new DebugSupport.PathWaypointVisData(new Vector3d(position), isCurrentTarget, isEndNode, isSeekTarget)); + } + + @Nullable + public List getPathVisData() { + return this.pathVisDataList; + } + + public boolean hasPathVisData() { + return this.pathVisDataList != null && !this.pathVisDataList.isEmpty(); + } + public interface DebugFlagsChangeListener { void onDebugFlagsChanged(EnumSet var1); } @@ -208,6 +244,9 @@ public class DebugSupport { public record EntityVisData(int sensorColorIndex, boolean matched) { } + public record PathWaypointVisData(Vector3d position, boolean isCurrentTarget, boolean isEndNode, boolean isSeekTarget) { + } + public record SensorVisData(double range, double minRange, int colorIndex, double viewAngle) { } } diff --git a/src/com/hypixel/hytale/server/npc/role/support/EntityList.java b/src/com/hypixel/hytale/server/npc/role/support/EntityList.java index b27324ed..3490c404 100644 --- a/src/com/hypixel/hytale/server/npc/role/support/EntityList.java +++ b/src/com/hypixel/hytale/server/npc/role/support/EntityList.java @@ -13,7 +13,6 @@ import com.hypixel.hytale.function.consumer.TriConsumer; import com.hypixel.hytale.function.predicate.QuadObjectDoublePredicate; import com.hypixel.hytale.function.predicate.QuadPredicate; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.components.SortBufferProviderResource; @@ -25,6 +24,7 @@ import java.util.function.BiPredicate; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class EntityList extends BucketList> { protected static final int BUCKET_COUNT = 6; @@ -141,7 +141,7 @@ public class EntityList extends BucketList> { assert transformComponent != null; - double distance = parentPosition.distanceSquaredTo(transformComponent.getPosition()); + double distance = parentPosition.distanceSquared(transformComponent.getPosition()); this.add(ref, distance); } } diff --git a/src/com/hypixel/hytale/server/npc/role/support/MarkedEntitySupport.java b/src/com/hypixel/hytale/server/npc/role/support/MarkedEntitySupport.java index 10a0ec1a..856724b1 100644 --- a/src/com/hypixel/hytale/server/npc/role/support/MarkedEntitySupport.java +++ b/src/com/hypixel/hytale/server/npc/role/support/MarkedEntitySupport.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.role.support; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.group.EntityGroup; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.flock.FlockPlugin; @@ -14,6 +13,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class MarkedEntitySupport { public static final String DEFAULT_TARGET_SLOT = "LockedTarget"; @@ -54,6 +54,7 @@ public class MarkedEntitySupport { } this.storedPositions = support.allocatePositionSlots(); + this.targetSlotToIgnoreForAvoidance = this.defaultTargetSlot; } public void clearMarkedEntity(int targetSlot) { @@ -122,13 +123,12 @@ public class MarkedEntitySupport { } public void setTargetSlotToIgnoreForAvoidance(int targetSlotToIgnoreForAvoidance) { - this.targetSlotToIgnoreForAvoidance = targetSlotToIgnoreForAvoidance; + this.targetSlotToIgnoreForAvoidance = targetSlotToIgnoreForAvoidance >= 0 ? targetSlotToIgnoreForAvoidance : this.defaultTargetSlot; } @Nullable public Ref getTargetReferenceToIgnoreForAvoidance() { - int slot = this.targetSlotToIgnoreForAvoidance >= 0 ? this.targetSlotToIgnoreForAvoidance : this.defaultTargetSlot; - return slot < 0 ? null : this.getMarkedEntityRef(slot); + return this.targetSlotToIgnoreForAvoidance < 0 ? null : this.getMarkedEntityRef(this.targetSlotToIgnoreForAvoidance); } public String getSlotName(int slot) { diff --git a/src/com/hypixel/hytale/server/npc/role/support/PositionCache.java b/src/com/hypixel/hytale/server/npc/role/support/PositionCache.java index 50cfbd03..712cbd88 100644 --- a/src/com/hypixel/hytale/server/npc/role/support/PositionCache.java +++ b/src/com/hypixel/hytale/server/npc/role/support/PositionCache.java @@ -14,8 +14,6 @@ import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.Opacity; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -36,9 +34,10 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.util.NPCPhysicsMath; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.Object2ByteMap; -import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.Reference2ByteMap; +import it.unimi.dsi.fastutil.objects.Reference2ByteOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import java.util.Objects; import java.util.Set; @@ -47,6 +46,8 @@ import java.util.function.Consumer; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; public class PositionCache { public static final BiPredicate, ComponentAccessor> IS_VALID_PLAYER = (ref, componentAccessor) -> { @@ -84,12 +85,12 @@ public class PositionCache { protected EntityList players; protected EntityList npcs; protected final List> externalRegistrations = new ObjectArrayList<>(); - private final List> droppedItems = new ObjectArrayList<>(); - private final List> spawnMarkers = new ObjectArrayList<>(); - private final List> spawnBeacons = new ObjectArrayList<>(); - private final Object2ByteMap> lineOfSightCache = new Object2ByteOpenHashMap<>(); - private final Object2ByteMap> inverseLineOfSightCache = new Object2ByteOpenHashMap<>(); - private final Object2ByteMap> friendlyFireCache = new Object2ByteOpenHashMap<>(); + private final List> droppedItems = new ReferenceArrayList<>(); + private final List> spawnMarkers = new ReferenceArrayList<>(); + private final List> spawnBeacons = new ReferenceArrayList<>(); + private final Reference2ByteMap> lineOfSightCache = new Reference2ByteOpenHashMap<>(); + private final Reference2ByteMap> inverseLineOfSightCache = new Reference2ByteOpenHashMap<>(); + private final Reference2ByteMap> friendlyFireCache = new Reference2ByteOpenHashMap<>(); protected final PositionCache.LineOfSightBuffer lineOfSightComputeBuffer = new PositionCache.LineOfSightBuffer(); protected final PositionCache.LineOfSightEntityBuffer lineOfSightEntityComputeBuffer = new PositionCache.LineOfSightEntityBuffer(); private float cacheTTL = 0.09F; @@ -297,7 +298,7 @@ public class PositionCache { assert itemEntityTransformComponent != null; - double squaredDistance = itemEntityTransformComponent.getPosition().distanceSquaredTo(position); + double squaredDistance = itemEntityTransformComponent.getPosition().distanceSquared(position); if (!(squaredDistance < minRange)) { if (squaredDistance >= maxRange) { break; @@ -508,17 +509,17 @@ public class PositionCache { Vector3d position = transformComponent.getPosition(); ModelComponent modelComponent = componentAccessor.getComponent(ref, MODEL_COMPONENT_TYPE); float eyeHeight = modelComponent != null ? modelComponent.getModel().getEyeHeight() : 0.0F; - double sx = position.getX(); - double sy = position.getY() + eyeHeight; - double sz = position.getZ(); + double sx = position.x(); + double sy = position.y() + eyeHeight; + double sz = position.z(); TransformComponent targetTransformComponent = componentAccessor.getComponent(targetRef, TRANSFORM_COMPONENT_TYPE); assert targetTransformComponent != null; Vector3d targetPosition = targetTransformComponent.getPosition(); - double tx = targetPosition.getX(); - double ty = targetPosition.getY(); - double tz = targetPosition.getZ(); + double tx = targetPosition.x(); + double ty = targetPosition.y(); + double tz = targetPosition.z(); ModelComponent targetModelComponent = componentAccessor.getComponent(targetRef, MODEL_COMPONENT_TYPE); if (targetModelComponent != null) { return predicate.test(sx, sy, sz, tx, ty + targetModelComponent.getModel().getEyeHeight(), tz, t, componentAccessor); @@ -529,9 +530,9 @@ public class PositionCache { BoundingBox boundingBoxComponent = componentAccessor.getComponent(targetRef, BOUNDING_BOX_COMPONENT_TYPE); if (boundingBoxComponent != null) { Box boundingBox = boundingBoxComponent.getBoundingBox(); - ox = (boundingBox.getMax().getX() + boundingBox.getMin().getX()) / 2.0; - oy = (boundingBox.getMax().getY() + boundingBox.getMin().getY()) / 2.0; - oz = (boundingBox.getMax().getZ() + boundingBox.getMin().getZ()) / 2.0; + ox = (boundingBox.getMax().x() + boundingBox.getMin().x()) / 2.0; + oy = (boundingBox.getMax().y() + boundingBox.getMin().y()) / 2.0; + oz = (boundingBox.getMax().z() + boundingBox.getMin().z()) / 2.0; } return predicate.test(sx, sy, sz, tx + ox, ty + oy, tz + oz, t, componentAccessor); @@ -552,7 +553,7 @@ public class PositionCache { assert targetTransformComponent != null; - if (transformComponent.getPosition().distanceSquaredTo(targetTransformComponent.getPosition()) <= 1.0E-12) { + if (transformComponent.getPosition().distanceSquared(targetTransformComponent.getPosition()) <= 1.0E-12) { return true; } else { World world = componentAccessor.getExternalData().getWorld(); @@ -679,9 +680,9 @@ public class PositionCache { targetRef, (sx, sy, sz, tx, ty, tz, _this, accessor) -> { PositionCache.LineOfSightEntityBuffer buffer = _this.lineOfSightEntityComputeBuffer; - buffer.pos.assign(sx, sy, sz); - buffer.dir.assign(tx - sx, ty - sy, tz - sz); - double squaredLength = buffer.dir.squaredLength(); + buffer.pos.set(sx, sy, sz); + buffer.dir.set(tx - sx, ty - sy, tz - sz); + double squaredLength = buffer.dir.lengthSquared(); return squaredLength < 1.0E-6 ? false : _this.players @@ -756,9 +757,9 @@ public class PositionCache { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - double px = position.getX(); - double py = position.getY(); - double pz = position.getZ(); + double px = position.x(); + double py = position.y(); + double pz = position.z(); double dx = px - pos.x; double dy = py - pos.y; double dz = pz - pos.z; diff --git a/src/com/hypixel/hytale/server/npc/role/support/StateSupport.java b/src/com/hypixel/hytale/server/npc/role/support/StateSupport.java index ff0e6870..52e5e7fc 100644 --- a/src/com/hypixel/hytale/server/npc/role/support/StateSupport.java +++ b/src/com/hypixel/hytale/server/npc/role/support/StateSupport.java @@ -23,8 +23,9 @@ import com.hypixel.hytale.server.npc.statetransition.StateTransitionController; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import java.util.BitSet; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; @@ -80,9 +81,9 @@ public class StateSupport { } if (builderSupport.isTrackInteractions()) { - this.interactedPlayers = new HashSet<>(); - this.interactablePlayers = new HashSet<>(); - this.contextualInteractions = new HashMap<>(); + this.interactedPlayers = new ReferenceOpenHashSet<>(); + this.interactablePlayers = new ReferenceOpenHashSet<>(); + this.contextualInteractions = new Reference2ObjectOpenHashMap<>(); } if (this.busyStates != null) { @@ -284,8 +285,8 @@ public class StateSupport { return contextualInteraction == null ? false : contextualInteraction.equals(context); } - public void addInteraction(@Nonnull Player player) { - this.interactedPlayers.add(player.getReference()); + public void addInteraction(@Nonnull Ref playerReference) { + this.interactedPlayers.add(playerReference); } public boolean consumeInteraction(@Nonnull Ref playerReference) { diff --git a/src/com/hypixel/hytale/server/npc/role/support/WorldSupport.java b/src/com/hypixel/hytale/server/npc/role/support/WorldSupport.java index 010e6d3c..3bcf147d 100644 --- a/src/com/hypixel/hytale/server/npc/role/support/WorldSupport.java +++ b/src/com/hypixel/hytale/server/npc/role/support/WorldSupport.java @@ -8,7 +8,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.asset.type.attitude.Attitude; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.inventory.ItemStack; @@ -35,6 +35,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; import it.unimi.dsi.fastutil.objects.ObjectIterator; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class WorldSupport { public static final double ATTITUDE_CACHE_CLEAR_FREQUENCY = 0.1; @@ -133,12 +134,12 @@ public class WorldSupport { } public void resetCachedSearchRayPosition(int id) { - this.searchRayCachedPositions[id].assign(Vector3d.MIN); + this.searchRayCachedPositions[id].set(Vector3dUtil.MIN); } public void resetAllCachedSearchRayPositions() { for (Vector3d cachedPosition : this.searchRayCachedPositions) { - cachedPosition.assign(Vector3d.MIN); + cachedPosition.set(Vector3dUtil.MIN); } } diff --git a/src/com/hypixel/hytale/server/npc/sensorinfo/IPositionProvider.java b/src/com/hypixel/hytale/server/npc/sensorinfo/IPositionProvider.java index 0310bc4c..afdba23a 100644 --- a/src/com/hypixel/hytale/server/npc/sensorinfo/IPositionProvider.java +++ b/src/com/hypixel/hytale/server/npc/sensorinfo/IPositionProvider.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.server.npc.sensorinfo; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nullable; +import org.joml.Vector3d; public interface IPositionProvider { boolean hasPosition(); diff --git a/src/com/hypixel/hytale/server/npc/sensorinfo/PositionProvider.java b/src/com/hypixel/hytale/server/npc/sensorinfo/PositionProvider.java index f12959e1..184d8227 100644 --- a/src/com/hypixel/hytale/server/npc/sensorinfo/PositionProvider.java +++ b/src/com/hypixel/hytale/server/npc/sensorinfo/PositionProvider.java @@ -2,13 +2,13 @@ package com.hypixel.hytale.server.npc.sensorinfo; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.sensorinfo.parameterproviders.ParameterProvider; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PositionProvider extends InfoProviderBase implements IPositionProvider { protected double x = 2.147483647E9; @@ -68,7 +68,7 @@ public class PositionProvider extends InfoProviderBase implements IPositionProvi if (!this.hasPosition()) { return false; } else { - result.assign(this.x, this.y, this.z); + result.set(this.x, this.y, this.z); return true; } } diff --git a/src/com/hypixel/hytale/server/npc/statetransition/StateTransitionController.java b/src/com/hypixel/hytale/server/npc/statetransition/StateTransitionController.java index f8943930..062b65e4 100644 --- a/src/com/hypixel/hytale/server/npc/statetransition/StateTransitionController.java +++ b/src/com/hypixel/hytale/server/npc/statetransition/StateTransitionController.java @@ -181,7 +181,9 @@ public class StateTransitionController { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute( + @Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store + ) { if (this.currentIndex >= this.actionLists.size()) { this.currentIndex = 0; } @@ -190,7 +192,7 @@ public class StateTransitionController { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { StateTransitionController.PrioritisedActionList actionList = this.actionLists.get(this.currentIndex); if (!actionList.actionList.canExecute(ref, role, sensorInfo, dt, store)) { return false; @@ -286,12 +288,14 @@ public class StateTransitionController { private record PrioritisedActionList(int priority, ActionList actionList) implements StateTransitionController.IActionListHolder { @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute( + @Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store + ) { return this.actionList.canExecute(ref, role, sensorInfo, dt, store); } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return this.actionList.execute(ref, role, sensorInfo, dt, store); } diff --git a/src/com/hypixel/hytale/server/npc/systems/AvoidanceSystem.java b/src/com/hypixel/hytale/server/npc/systems/AvoidanceSystem.java index a93b4535..58db240a 100644 --- a/src/com/hypixel/hytale/server/npc/systems/AvoidanceSystem.java +++ b/src/com/hypixel/hytale/server/npc/systems/AvoidanceSystem.java @@ -10,27 +10,22 @@ import com.hypixel.hytale.component.dependency.Order; import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.physics.util.PhysicsMath; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.role.support.DebugSupport; +import com.hypixel.hytale.server.npc.util.VisHelper; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class AvoidanceSystem extends SteppableTickingSystem { - public static final Vector3f DEBUG_COLOR_STEERING_POST = new Vector3f(0.0F, 1.0F, 0.0F); - public static final Vector3f DEBUG_COLOR_STEERING_PRE = new Vector3f(1.0F, 0.0F, 0.0F); - public static final Vector3f DEBUG_COLOR_AVOIDANCE = new Vector3f(1.0F, 1.0F, 1.0F); - public static final Vector3f DEBUG_COLOR_SEPARATION = new Vector3f(0.0F, 0.0F, 1.0F); - public static final double DEBUG_MIN_VECTOR_DRAW_LENGTH_SQUARED = 0.01; - public static final double DEBUG_VECTORS_SCALE = 4.0; - public static final float DEBUG_VECTORS_TIME = 0.05F; @Nonnull private final ComponentType componentType; @Nonnull @@ -77,60 +72,57 @@ public class AvoidanceSystem extends SteppableTickingSystem { assert npcComponent != null; Role role = npcComponent.getRole(); - if (role.isAvoidingEntities() || role.isApplySeparation()) { - Ref target = role.getMarkedEntitySupport().getTargetReferenceToIgnoreForAvoidance(); - if (target != null && target.isValid()) { - role.getIgnoredEntitiesForAvoidance().add(target); + if (role != null) { + if (role.isAvoidingEntities() || role.isApplySeparation()) { + Ref target = role.getMarkedEntitySupport().getTargetReferenceToIgnoreForAvoidance(); + if (target != null && target.isValid()) { + role.getIgnoredEntitiesForAvoidance().add(target); + } } - } - if (!role.getActiveMotionController().isObstructed()) { DebugSupport debugSupport = role.getDebugSupport(); - boolean debugVisAvoidance = debugSupport.isDebugFlagSet(RoleDebugFlags.VisAvoidance); - boolean debugVisSeparation = debugSupport.isDebugFlagSet(RoleDebugFlags.VisSeparation); - Vector3d preBlendSteering = !debugVisSeparation && !debugVisAvoidance ? null : role.getBodySteering().getTranslation().clone(); - boolean renderSteering = false; - TransformComponent transformComponent = archetypeChunk.getComponent(index, this.transformComponentType); + boolean debugVisSteeringPre = debugSupport.isDebugFlagSet(RoleDebugFlags.VisSteeringPre); + Vector3d preBlendSteering = debugVisSteeringPre ? new Vector3d(role.getBodySteering().getTranslation()) : null; + if (!role.getActiveMotionController().isObstructed()) { + TransformComponent transformComponent = archetypeChunk.getComponent(index, this.transformComponentType); - assert transformComponent != null; + assert transformComponent != null; - Vector3d position = transformComponent.getPosition(); - World world = commandBuffer.getExternalData().getWorld(); - if (role.isAvoidingEntities()) { - role.blendAvoidance(npcRef, position, role.getBodySteering(), commandBuffer); - if (debugVisAvoidance) { - renderSteering = true; - renderDebugSteeringVector(position, role.getLastAvoidanceSteering(), DEBUG_COLOR_AVOIDANCE, world); + Vector3d position = transformComponent.getPosition(); + World world = commandBuffer.getExternalData().getWorld(); + Rotation3f rotation = transformComponent.getRotation(); + boolean debugVisAvoidance = debugSupport.isDebugFlagSet(RoleDebugFlags.VisAvoidance); + boolean debugVisSeparation = debugSupport.isDebugFlagSet(RoleDebugFlags.VisSeparation); + boolean debugVisOrientation = debugSupport.isDebugFlagSet(RoleDebugFlags.VisOrientation) && (debugVisAvoidance || debugVisSeparation); + if (role.isAvoidingEntities()) { + role.blendAvoidance(npcRef, position, rotation, role.getBodySteering(), commandBuffer); + if (debugVisAvoidance) { + VisHelper.renderDebugVector(position, role.getLastAvoidanceSteering(), VisHelper.DEBUG_COLOR_AVOIDANCE, world); + } + } + + if (role.isApplySeparation()) { + role.blendSeparation(npcRef, position, rotation, role.getBodySteering(), this.transformComponentType, commandBuffer); + if (debugVisSeparation) { + VisHelper.renderDebugVector(position, role.getLastAvoidanceSteering(), VisHelper.DEBUG_COLOR_SEPARATION, world); + } + } + + if (debugVisOrientation && role.getBodySteering().hasDirectionHint()) { + Vector3d hint = new Vector3d(); + PhysicsMath.vectorFromAngles(role.getBodySteering().getDirectionHint().yaw(), role.getBodySteering().getDirectionHint().pitch(), hint); + hint.mul(0.25); + VisHelper.renderDebugVector(position, hint, DebugUtils.COLOR_CYAN, world); + } + + if (debugVisSteeringPre) { + VisHelper.renderDebugVectorTo(position, preBlendSteering, VisHelper.DEBUG_COLOR_STEERING_PRE, world); + } + + if (debugSupport.isDebugFlagSet(RoleDebugFlags.VisSteeringPost)) { + VisHelper.renderDebugVector(position, role.getBodySteering().getTranslation(), VisHelper.DEBUG_COLOR_STEERING_POST, world); } } - - if (role.isApplySeparation()) { - role.blendSeparation(archetypeChunk.getReferenceTo(index), position, role.getBodySteering(), this.transformComponentType, commandBuffer); - if (debugVisSeparation) { - renderSteering = true; - renderDebugSteeringVector(position, role.getLastSeparationSteering(), DEBUG_COLOR_SEPARATION, world); - } - } - - if (renderSteering) { - renderDebugSteeringVectorInverse(position, preBlendSteering, DEBUG_COLOR_STEERING_PRE, world); - renderDebugSteeringVector(position, role.getBodySteering().getTranslation(), DEBUG_COLOR_STEERING_POST, world); - } - } - } - - private static void renderDebugSteeringVector(@Nonnull Vector3d position, @Nonnull Vector3d direction, @Nonnull Vector3f color, @Nonnull World world) { - if (!(direction.squaredLength() < 0.01)) { - Vector3d scaledDir = direction.clone().scale(4.0); - DebugUtils.addArrow(world, position, scaledDir, color, 0.05F, false); - } - } - - private static void renderDebugSteeringVectorInverse(@Nonnull Vector3d position, @Nonnull Vector3d direction, @Nonnull Vector3f color, @Nonnull World world) { - if (!(direction.squaredLength() < 0.01)) { - Vector3d scaledDir = direction.clone().scale(4.0); - Vector3d start = position.clone().subtract(scaledDir); - DebugUtils.addArrow(world, start, scaledDir, color, 0.05F, false); } } } diff --git a/src/com/hypixel/hytale/server/npc/systems/ComputeVelocitySystem.java b/src/com/hypixel/hytale/server/npc/systems/ComputeVelocitySystem.java index 862fc2e3..10473a7d 100644 --- a/src/com/hypixel/hytale/server/npc/systems/ComputeVelocitySystem.java +++ b/src/com/hypixel/hytale/server/npc/systems/ComputeVelocitySystem.java @@ -7,13 +7,13 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.dependency.Dependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.physics.component.Velocity; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.entities.NPCEntity; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class ComputeVelocitySystem extends SteppableTickingSystem { @Nonnull @@ -71,9 +71,9 @@ public class ComputeVelocitySystem extends SteppableTickingSystem { Vector3d position = transformComponent.getPosition(); Vector3d oldPosition = npcComponent.getOldPosition(); - double x = (position.getX() - oldPosition.getX()) / dt; - double y = (position.getY() - oldPosition.getY()) / dt; - double z = (position.getZ() - oldPosition.getZ()) / dt; + double x = (position.x() - oldPosition.x()) / dt; + double y = (position.y() - oldPosition.y()) / dt; + double z = (position.z() - oldPosition.z()) / dt; velocityComponent.set(x, y, z); } diff --git a/src/com/hypixel/hytale/server/npc/systems/NPCDamageSystems.java b/src/com/hypixel/hytale/server/npc/systems/NPCDamageSystems.java index 8d898fd2..7d19f3d5 100644 --- a/src/com/hypixel/hytale/server/npc/systems/NPCDamageSystems.java +++ b/src/com/hypixel/hytale/server/npc/systems/NPCDamageSystems.java @@ -10,10 +10,13 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.SystemGroup; +import com.hypixel.hytale.component.dependency.Dependency; +import com.hypixel.hytale.component.dependency.Order; +import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.asset.type.gameplay.DeathConfig; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -26,6 +29,7 @@ import com.hypixel.hytale.server.core.modules.entity.damage.DamageEventSystem; import com.hypixel.hytale.server.core.modules.entity.damage.DamageModule; import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems; +import com.hypixel.hytale.server.core.modules.entity.damage.DeferredCorpseRemoval; import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; import com.hypixel.hytale.server.core.modules.entity.player.PlayerSettings; import com.hypixel.hytale.server.core.modules.item.ItemModule; @@ -38,8 +42,10 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.npc.util.DamageData; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; +import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class NPCDamageSystems { public static class DamageDealtSystem extends DamageEventSystem { @@ -160,10 +166,18 @@ public class NPCDamageSystems { } } - public static class DropDeathItems extends DeathSystems.OnDeathSystem { + public static class DropDeathItems extends EntityTickingSystem { @Nonnull private static final Query QUERY = Query.and( - NPCEntity.getComponentType(), TransformComponent.getComponentType(), HeadRotation.getComponentType(), Query.not(Player.getComponentType()) + NPCEntity.getComponentType(), + TransformComponent.getComponentType(), + HeadRotation.getComponentType(), + Query.not(Player.getComponentType()), + DeathComponent.getComponentType() + ); + @Nonnull + private static final Set> DEPENDENCIES = Set.of( + new SystemDependency<>(Order.AFTER, DeathSystems.TickCorpseRemoval.class), new SystemDependency<>(Order.BEFORE, DeathSystems.CorpseRemoval.class) ); @Nonnull @@ -172,45 +186,70 @@ public class NPCDamageSystems { return QUERY; } - public void onComponentAdded( - @Nonnull Ref ref, @Nonnull DeathComponent component, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + @Nonnull + @Override + public Set> getDependencies() { + return DEPENDENCIES; + } + + @Override + public void tick( + float dt, + int index, + @Nonnull ArchetypeChunk archetypeChunk, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer ) { - if (component.getItemsLossMode() == DeathConfig.ItemsLossMode.ALL) { - NPCEntity npcComponent = commandBuffer.getComponent(ref, NPCEntity.getComponentType()); + DeathComponent deathComponent = archetypeChunk.getComponent(index, DeathComponent.getComponentType()); + + assert deathComponent != null; + + if (deathComponent.getItemsLossMode() == DeathConfig.ItemsLossMode.ALL) { + NPCEntity npcComponent = archetypeChunk.getComponent(index, NPCEntity.getComponentType()); assert npcComponent != null; Role role = npcComponent.getRole(); if (role != null) { - List itemsToDrop = new ObjectArrayList<>(); - if (role.isPickupDropOnDeath()) { - Inventory inventory = npcComponent.getInventory(); - itemsToDrop.addAll(inventory.getStorage().dropAllItemStacks()); - } - - String dropListId = role.getDropListId(); - if (dropListId != null) { - ItemModule itemModule = ItemModule.get(); - if (itemModule.isEnabled()) { - List randomItemsToDrop = itemModule.getRandomItemDrops(dropListId); - itemsToDrop.addAll(randomItemsToDrop); + if (!role.hasDroppedDeathItems()) { + if (!role.isDropDeathItemsInstantly()) { + DeferredCorpseRemoval deferredRemoval = archetypeChunk.getComponent(index, DeferredCorpseRemoval.getComponentType()); + if (deferredRemoval != null && !deferredRemoval.shouldRemove()) { + return; + } } - } - if (!itemsToDrop.isEmpty()) { - TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType()); + role.setDeathItemsDropped(); + List itemsToDrop = new ObjectArrayList<>(); + if (role.isPickupDropOnDeath()) { + Inventory inventory = npcComponent.getInventory(); + itemsToDrop.addAll(inventory.getStorage().dropAllItemStacks()); + } - assert transformComponent != null; + String dropListId = role.getDropListId(); + if (dropListId != null) { + ItemModule itemModule = ItemModule.get(); + if (itemModule.isEnabled()) { + List randomItemsToDrop = itemModule.getRandomItemDrops(dropListId); + itemsToDrop.addAll(randomItemsToDrop); + } + } - Vector3d position = transformComponent.getPosition(); - HeadRotation headRotationComponent = store.getComponent(ref, HeadRotation.getComponentType()); + if (!itemsToDrop.isEmpty()) { + TransformComponent transformComponent = archetypeChunk.getComponent(index, TransformComponent.getComponentType()); - assert headRotationComponent != null; + assert transformComponent != null; - Vector3f headRotation = headRotationComponent.getRotation(); - Vector3d dropPosition = position.clone().add(0.0, 1.0, 0.0); - Holder[] drops = ItemComponent.generateItemDrops(store, itemsToDrop, dropPosition, headRotation.clone()); - commandBuffer.addEntities(drops, AddReason.SPAWN); + Vector3d position = transformComponent.getPosition(); + HeadRotation headRotationComponent = archetypeChunk.getComponent(index, HeadRotation.getComponentType()); + + assert headRotationComponent != null; + + Rotation3f headRotation = headRotationComponent.getRotation(); + Vector3d dropPosition = new Vector3d(position).add(0.0, 1.0, 0.0); + Holder[] drops = ItemComponent.generateItemDrops(store, itemsToDrop, dropPosition, new Rotation3f(headRotation)); + commandBuffer.addEntities(drops, AddReason.SPAWN); + } } } } diff --git a/src/com/hypixel/hytale/server/npc/systems/NPCDeathSystems.java b/src/com/hypixel/hytale/server/npc/systems/NPCDeathSystems.java index 9ce0aa73..7e2e5e0a 100644 --- a/src/com/hypixel/hytale/server/npc/systems/NPCDeathSystems.java +++ b/src/com/hypixel/hytale/server/npc/systems/NPCDeathSystems.java @@ -7,7 +7,6 @@ import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.AllLegacyLivingEntityTypesQuery; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.npc.blackboard.view.event.entity.EntityEventTyp import com.hypixel.hytale.server.npc.blackboard.view.event.entity.EntityEventView; import com.hypixel.hytale.server.npc.entities.NPCEntity; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NPCDeathSystems { public static class EntityViewSystem extends DeathSystems.OnDeathSystem { @@ -91,7 +91,7 @@ public class NPCDeathSystems { assert transformComponent != null; - sourceNpcComponent.getDamageData().onKill(ref, transformComponent.getPosition().clone()); + sourceNpcComponent.getDamageData().onKill(ref, new Vector3d(transformComponent.getPosition())); } } } diff --git a/src/com/hypixel/hytale/server/npc/systems/NPCPreTickSystem.java b/src/com/hypixel/hytale/server/npc/systems/NPCPreTickSystem.java index a5d66329..66a4ede5 100644 --- a/src/com/hypixel/hytale/server/npc/systems/NPCPreTickSystem.java +++ b/src/com/hypixel/hytale/server/npc/systems/NPCPreTickSystem.java @@ -12,7 +12,6 @@ import com.hypixel.hytale.component.dependency.Order; import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -24,6 +23,7 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.spawning.SpawningPlugin; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NPCPreTickSystem extends SteppableTickingSystem { private static final float DEFAULT_DESPAWN_CHECK_DELAY = 30.0F; diff --git a/src/com/hypixel/hytale/server/npc/systems/NPCSpatialSystem.java b/src/com/hypixel/hytale/server/npc/systems/NPCSpatialSystem.java index 2b6e42c0..ce7dbfec 100644 --- a/src/com/hypixel/hytale/server/npc/systems/NPCSpatialSystem.java +++ b/src/com/hypixel/hytale/server/npc/systems/NPCSpatialSystem.java @@ -8,11 +8,11 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.entities.NPCEntity; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NPCSpatialSystem extends SpatialSystem { public static final Query QUERY = Archetype.of(NPCEntity.getComponentType(), TransformComponent.getComponentType()); diff --git a/src/com/hypixel/hytale/server/npc/systems/NPCSystems.java b/src/com/hypixel/hytale/server/npc/systems/NPCSystems.java index 5340b55d..b1e1452f 100644 --- a/src/com/hypixel/hytale/server/npc/systems/NPCSystems.java +++ b/src/com/hypixel/hytale/server/npc/systems/NPCSystems.java @@ -19,13 +19,14 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.WorldEventSystem; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.entity.Frozen; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent; import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab; @@ -144,10 +145,10 @@ public class NPCSystems { assert transformComponent != null; - npcComponent.getLeashPoint().assign(transformComponent.getPosition()); - Vector3f bodyRotation = transformComponent.getRotation(); - npcComponent.setLeashHeading(bodyRotation.getYaw()); - npcComponent.setLeashPitch(bodyRotation.getPitch()); + npcComponent.getLeashPoint().set(transformComponent.getPosition()); + Rotation3f bodyRotation = transformComponent.getRotation(); + npcComponent.setLeashHeading(bodyRotation.yaw()); + npcComponent.setLeashPitch(bodyRotation.pitch()); npcComponent.setSpawnInstant(worldTimeResource.getGameTime()); npcComponent.getRole().onLoadFromWorldGenOrPrefab(ref, npcComponent, commandBuffer); if (fromWorldGen) { @@ -468,13 +469,53 @@ public class NPCSystems { assert npcComponent != null; Role role = npcComponent.getRole(); - double deathAnimationTime = role.getDeathAnimationTime(); - if (deathAnimationTime > 0.0) { - commandBuffer.addComponent(ref, this.deferredCorpseRemovalComponentType, new DeferredCorpseRemoval(deathAnimationTime)); + if (role != null) { + double deathAnimationTime = role.getDeathAnimationTime(); + if (deathAnimationTime > 0.0) { + commandBuffer.addComponent(ref, this.deferredCorpseRemovalComponentType, new DeferredCorpseRemoval(deathAnimationTime, role.getDeathParticles())); + } } } } + public static class OnNPCAdded extends HolderSystem { + @Override + public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { + NPCEntity npc = holder.getComponent(NPCEntity.getComponentType()); + + assert npc != null; + + npc.getInventory().migrateToComponents(holder); + if (!holder.getArchetype().contains(InventoryComponent.Storage.getComponentType())) { + holder.addComponent(InventoryComponent.Storage.getComponentType(), new InventoryComponent.Storage((short)0)); + } + + if (!holder.getArchetype().contains(InventoryComponent.Armor.getComponentType())) { + holder.addComponent(InventoryComponent.Armor.getComponentType(), new InventoryComponent.Armor(InventoryComponent.DEFAULT_ARMOR_CAPACITY)); + } + + if (!holder.getArchetype().contains(InventoryComponent.Hotbar.getComponentType())) { + holder.addComponent(InventoryComponent.Hotbar.getComponentType(), new InventoryComponent.Hotbar((short)3)); + } + + if (!holder.getArchetype().contains(InventoryComponent.Utility.getComponentType())) { + holder.addComponent(InventoryComponent.Utility.getComponentType(), new InventoryComponent.Utility((short)0)); + } + + npc.getInventory().backwardsCompatHook(holder); + } + + @Override + public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { + } + + @Nullable + @Override + public Query getQuery() { + return NPCEntity.getComponentType(); + } + } + public static class OnTeleportSystem extends RefChangeSystem { @Nonnull private final ComponentType npcComponentType = NPCEntity.getComponentType(); diff --git a/src/com/hypixel/hytale/server/npc/systems/NPCVelocityInstructionSystem.java b/src/com/hypixel/hytale/server/npc/systems/NPCVelocityInstructionSystem.java index cf59b272..b491bce0 100644 --- a/src/com/hypixel/hytale/server/npc/systems/NPCVelocityInstructionSystem.java +++ b/src/com/hypixel/hytale/server/npc/systems/NPCVelocityInstructionSystem.java @@ -9,7 +9,6 @@ import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.dependency.SystemTypeDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.debug.DebugUtils; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import java.util.Set; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class NPCVelocityInstructionSystem extends EntityTickingSystem { @Nonnull diff --git a/src/com/hypixel/hytale/server/npc/systems/NewSpawnStartTickingSystem.java b/src/com/hypixel/hytale/server/npc/systems/NewSpawnStartTickingSystem.java index f1b3fff7..f630633c 100644 --- a/src/com/hypixel/hytale/server/npc/systems/NewSpawnStartTickingSystem.java +++ b/src/com/hypixel/hytale/server/npc/systems/NewSpawnStartTickingSystem.java @@ -12,8 +12,8 @@ import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.system.tick.TickingSystem; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.NPCPlugin; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceList; import java.util.Set; import javax.annotation.Nonnull; @@ -38,7 +38,7 @@ public class NewSpawnStartTickingSystem extends TickingSystem { @Override public void tick(float dt, int systemIndex, @Nonnull Store store) { - ObjectList> queue = store.getResource(this.queueResourceType).queue; + ReferenceList> queue = store.getResource(this.queueResourceType).queue; if (!queue.isEmpty()) { for (Ref reference : queue) { if (reference.isValid()) { @@ -57,7 +57,7 @@ public class NewSpawnStartTickingSystem extends TickingSystem { public static class QueueResource implements Resource { @Nonnull - private final ObjectList> queue = new ObjectArrayList<>(); + private final ReferenceList> queue = new ReferenceArrayList<>(); @Nonnull public static ResourceType getResourceType() { diff --git a/src/com/hypixel/hytale/server/npc/systems/PositionCacheSystems.java b/src/com/hypixel/hytale/server/npc/systems/PositionCacheSystems.java index 53b5963d..ae25203c 100644 --- a/src/com/hypixel/hytale/server/npc/systems/PositionCacheSystems.java +++ b/src/com/hypixel/hytale/server/npc/systems/PositionCacheSystems.java @@ -19,11 +19,8 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefChangeSystem; -import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.protocol.BlockMaterial; -import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.modules.entity.EntityModule; +import com.hypixel.hytale.server.core.modules.entity.component.CachedStatsComponent; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.system.PlayerSpatialSystem; @@ -43,6 +40,7 @@ import java.util.Set; import java.util.function.Consumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class PositionCacheSystems { public static void initialisePositionCache(@Nonnull Role role, @Nullable StateEvaluator stateEvaluator, double flockInfluenceRange) { @@ -247,7 +245,7 @@ public class PositionCacheSystems { this.playerSpatialResource = EntityModule.get().getPlayerSpatialResourceType(); this.npcSpatialResource = npcSpatialResource; this.itemSpatialResource = EntityModule.get().getItemSpatialResourceType(); - this.query = Query.and(npcComponentType, this.transformComponentType, this.modelComponentType); + this.query = Query.and(npcComponentType, this.transformComponentType, this.modelComponentType, CachedStatsComponent.getComponentType()); } @Nonnull @@ -283,10 +281,11 @@ public class PositionCacheSystems { Role role = npcComponent.getRole(); PositionCache positionCache = role.getPositionCache(); positionCache.setBenchmarking(NPCPlugin.get().isBenchmarkingSensorSupport()); - long packed = LivingEntity.getPackedMaterialAndFluidAtBreathingHeight(ref, commandBuffer); - BlockMaterial material = BlockMaterial.VALUES[MathUtil.unpackLeft(packed)]; - int fluidId = MathUtil.unpackRight(packed); - positionCache.setCouldBreathe(role.canBreathe(material, fluidId)); + CachedStatsComponent cachedStats = archetypeChunk.getComponent(index, CachedStatsComponent.getComponentType()); + + assert cachedStats != null; + + positionCache.setCouldBreathe(cachedStats.isCanBreathe()); if (positionCache.tickPositionCacheNextUpdate(dt)) { positionCache.resetPositionCacheNextUpdate(); TransformComponent transformComponent = archetypeChunk.getComponent(index, this.transformComponentType); diff --git a/src/com/hypixel/hytale/server/npc/systems/RoleBuilderSystem.java b/src/com/hypixel/hytale/server/npc/systems/RoleBuilderSystem.java index cbc217c1..a78a9569 100644 --- a/src/com/hypixel/hytale/server/npc/systems/RoleBuilderSystem.java +++ b/src/com/hypixel/hytale/server/npc/systems/RoleBuilderSystem.java @@ -20,12 +20,14 @@ import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; import com.hypixel.hytale.server.core.modules.entity.component.ActiveAnimationComponent; +import com.hypixel.hytale.server.core.modules.entity.component.CachedStatsComponent; import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent; import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab; import com.hypixel.hytale.server.core.modules.entity.component.Invulnerable; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.entity.system.ModelSystems; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsSystems; import com.hypixel.hytale.server.core.modules.interaction.Interactions; import com.hypixel.hytale.server.core.modules.physics.systems.PhysicsValuesAddSystem; @@ -73,7 +75,10 @@ public class RoleBuilderSystem extends HolderSystem { public RoleBuilderSystem() { this.npcComponentType = NPCEntity.getComponentType(); this.dependencies = Set.of( - new SystemDependency<>(Order.AFTER, EntityStatsSystems.Setup.class), new SystemDependency<>(Order.AFTER, PhysicsValuesAddSystem.class) + new SystemDependency<>(Order.AFTER, EntityStatsSystems.Setup.class), + new SystemDependency<>(Order.AFTER, PhysicsValuesAddSystem.class), + new SystemDependency<>(Order.AFTER, NPCSystems.OnNPCAdded.class), + new SystemDependency<>(Order.BEFORE, ModelSystems.ModelSpawned.class) ); this.query = Archetype.of(this.npcComponentType, this.transformComponentType); } @@ -226,6 +231,7 @@ public class RoleBuilderSystem extends HolderSystem { holder.ensureComponent(EffectControllerComponent.getComponentType()); holder.ensureComponent(ActiveAnimationComponent.getComponentType()); + holder.ensureComponent(CachedStatsComponent.getComponentType()); boolean fromPrefab = holder.getArchetype().contains(FromPrefab.getComponentType()); boolean spawnedOrPrefab = reason.equals(AddReason.SPAWN) || fromPrefab; if (spawnedOrPrefab) { @@ -258,7 +264,7 @@ public class RoleBuilderSystem extends HolderSystem { assert transformComponent != null; - spawnEffect.spawnEffect(transformComponent.getPosition(), transformComponent.getRotation(), store); + spawnEffect.spawnEffect(holder, builderSupport, transformComponent.getPosition(), transformComponent.getRotation(), store); } } } diff --git a/src/com/hypixel/hytale/server/npc/systems/RoleSystems.java b/src/com/hypixel/hytale/server/npc/systems/RoleSystems.java index 17d44512..d9619d94 100644 --- a/src/com/hypixel/hytale/server/npc/systems/RoleSystems.java +++ b/src/com/hypixel/hytale/server/npc/systems/RoleSystems.java @@ -15,11 +15,8 @@ import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.component.system.tick.TickingSystem; -import com.hypixel.hytale.math.matrix.Matrix4d; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.Frozen; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -47,16 +44,19 @@ import com.hypixel.hytale.server.npc.role.RoleDebugFlags; import com.hypixel.hytale.server.npc.role.support.DebugSupport; import com.hypixel.hytale.server.npc.role.support.EntitySupport; import com.hypixel.hytale.server.npc.role.support.MarkedEntitySupport; -import java.util.ArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Matrix4d; +import org.joml.Vector3d; +import org.joml.Vector3f; public class RoleSystems { - private static final ThreadLocal>> ENTITY_LIST = ThreadLocal.withInitial(ArrayList::new); + private static final ThreadLocal>> ENTITY_LIST = ThreadLocal.withInitial(ReferenceArrayList::new); public static class BehaviourTickSystem extends TickingSystem { @Nonnull @@ -357,6 +357,12 @@ public class RoleSystems { private static final float NPC_RING_THICKNESS = 0.1F; private static final float NPC_RING_OFFSET = 0.1F; private static final float LEASH_LINE_THICKNESS = 0.05F; + private static final double PATH_WAYPOINT_SPHERE_SIZE = 0.25; + private static final double PATH_CURRENT_TARGET_SPHERE_SIZE = 0.35; + private static final double PATH_END_NODE_SPHERE_SIZE = 0.4; + private static final double PATH_SPHERE_Y_OFFSET = 0.5; + private static final double PATH_LINE_THICKNESS = 0.05; + private static final double PATH_NPC_LINE_THICKNESS = 0.08; @Nonnull private final ComponentType npcComponentType; @Nonnull @@ -410,7 +416,8 @@ public class RoleSystems { boolean hasSensorVis = debugSupport.hasSensorVisData(); boolean hasLeashVis = debugSupport.isDebugFlagSet(RoleDebugFlags.VisLeashPosition); - if (hasSensorVis || hasLeashVis) { + boolean hasPathVis = debugSupport.isVisPath() && debugSupport.hasPathVisData(); + if (hasSensorVis || hasLeashVis || hasPathVis) { Ref npcRef = archetypeChunk.getReferenceTo(index); TransformComponent transformComponent = archetypeChunk.getComponent(index, TransformComponent.getComponentType()); @@ -428,6 +435,10 @@ public class RoleSystems { if (hasLeashVis) { renderLeashPositionVisualization(npcComponent, npcRef, transformComponent, boundingBoxComponent, world); } + + if (hasPathVis) { + renderPathVisualization(debugSupport, transformComponent, boundingBoxComponent, world); + } } } } @@ -451,7 +462,7 @@ public class RoleSystems { targetEyePosition.x - npcEyePosition.x, targetEyePosition.y - npcEyePosition.y, targetEyePosition.z - npcEyePosition.z ); Vector3f color = DebugUtils.INDEXED_COLORS[slotIndex % DebugUtils.INDEXED_COLORS.length]; - DebugUtils.addArrow(world, npcEyePosition, direction, color, 0.1F, false); + DebugUtils.addArrow(world, npcEyePosition, direction, color, 0.1F, 0); } } } @@ -469,7 +480,7 @@ public class RoleSystems { Vector3d npcPosition = transformComponent.getPosition(); double npcMidHeight = boundingBoxComponent.getBoundingBox().max.y / 2.0; HeadRotation headRotation = commandBuffer.getComponent(npcRef, HeadRotation.getComponentType()); - double heading = headRotation != null ? headRotation.getRotation().getYaw() : transformComponent.getRotation().getYaw(); + double heading = headRotation != null ? headRotation.getRotation().yaw() : transformComponent.getRotation().yaw(); sensorDataList.sort((a, b) -> Double.compare(b.range(), a.range())); double discStackOffset = 0.1; @@ -491,10 +502,10 @@ public class RoleSystems { color, 0.4F, 0.1F, - false + 0 ); } else { - DebugUtils.addDisc(world, npcPosition.x, height, npcPosition.z, sensorData.range(), sensorData.minRange(), color, 0.4F, 0.1F, false); + DebugUtils.addDisc(world, npcPosition.x, height, npcPosition.z, sensorData.range(), sensorData.minRange(), color, 0.4F, 0.1F, 0); } } @@ -549,7 +560,7 @@ public class RoleSystems { DebugUtils.COLOR_GRAY, 0.03, 0.1F, - false + 0 ); } } @@ -599,11 +610,11 @@ public class RoleSystems { double npcEdgeX = npcPosition.x - hDirX * npcRingOuterRadius * cosPitch; double npcEdgeY = npcMidY - sinPitch * npcRingOuterRadius; double npcEdgeZ = npcPosition.z - hDirZ * npcRingOuterRadius * cosPitch; - DebugUtils.addLine(world, leashEdgeX, leashEdgeY, leashEdgeZ, npcEdgeX, npcEdgeY, npcEdgeZ, color, 0.05F, 0.1F, false); + DebugUtils.addLine(world, leashEdgeX, leashEdgeY, leashEdgeZ, npcEdgeX, npcEdgeY, npcEdgeZ, color, 0.05F, 0.1F, 0); } else { - DebugUtils.addDisc(world, leashPoint.x, leashPoint.y, leashPoint.z, 0.5, 0.4F, color, 0.8F, 0.1F, false); - DebugUtils.addDisc(world, npcPosition.x, npcMidY, npcPosition.z, npcRingOuterRadius, npcRingInnerRadius, color, 0.8F, 0.1F, false); - DebugUtils.addLine(world, leashPoint.x, leashPoint.y, leashPoint.z, npcPosition.x, npcMidY, npcPosition.z, color, 0.05F, 0.1F, false); + DebugUtils.addDisc(world, leashPoint.x, leashPoint.y, leashPoint.z, 0.5, 0.4F, color, 0.8F, 0.1F, 0); + DebugUtils.addDisc(world, npcPosition.x, npcMidY, npcPosition.z, npcRingOuterRadius, npcRingInnerRadius, color, 0.8F, 0.1F, 0); + DebugUtils.addLine(world, leashPoint.x, leashPoint.y, leashPoint.z, npcPosition.x, npcMidY, npcPosition.z, color, 0.05F, 0.1F, 0); } } } @@ -622,10 +633,73 @@ public class RoleSystems { Matrix4d matrix = new Matrix4d(); matrix.identity(); matrix.translate(x, y, z); - Matrix4d tmp = new Matrix4d(); - matrix.rotateAxis(yawAngle, 0.0, 1.0, 0.0, tmp); - matrix.rotateAxis(pitchAngle, 0.0, 0.0, 1.0, tmp); - DebugUtils.addDisc(world, matrix, outerRadius, innerRadius, color, 0.8F, 0.1F, false); + matrix.rotate(-yawAngle, 0.0, 1.0, 0.0); + matrix.rotate(-pitchAngle, 0.0, 0.0, 1.0); + DebugUtils.addDisc(world, matrix, outerRadius, innerRadius, color, 0.8F, 0.1F, 0); + } + + private static void renderPathVisualization( + @Nonnull DebugSupport debugSupport, @Nonnull TransformComponent transformComponent, @Nonnull BoundingBox boundingBoxComponent, @Nonnull World world + ) { + List pathData = debugSupport.getPathVisData(); + if (pathData != null && !pathData.isEmpty()) { + Vector3d npcPosition = transformComponent.getPosition(); + double npcMidHeight = boundingBoxComponent.getBoundingBox().middleY(); + double prevX = 0.0; + double prevY = 0.0; + double prevZ = 0.0; + boolean hasPrev = false; + double targetX = 0.0; + double targetY = 0.0; + double targetZ = 0.0; + boolean hasTarget = false; + boolean isSeekTarget = false; + + for (DebugSupport.PathWaypointVisData waypoint : pathData) { + Vector3d waypointPos = waypoint.position(); + double offsetY = waypointPos.y + 0.5; + Vector3f color; + double sphereSize; + if (waypoint.isEndNode()) { + color = DebugUtils.COLOR_MAGENTA; + sphereSize = 0.4; + if (waypoint.isCurrentTarget()) { + targetX = waypointPos.x; + targetY = offsetY; + targetZ = waypointPos.z; + hasTarget = true; + isSeekTarget = waypoint.isSeekTarget(); + } + } else if (waypoint.isCurrentTarget()) { + color = DebugUtils.COLOR_CYAN; + sphereSize = 0.35; + targetX = waypointPos.x; + targetY = offsetY; + targetZ = waypointPos.z; + hasTarget = true; + isSeekTarget = waypoint.isSeekTarget(); + } else { + color = DebugUtils.COLOR_LIME; + sphereSize = 0.25; + } + + DebugUtils.addSphere(world, waypointPos.x, offsetY, waypointPos.z, color, sphereSize, 0.1F); + if (hasPrev) { + DebugUtils.addLine(world, prevX, prevY, prevZ, waypointPos.x, offsetY, waypointPos.z, DebugUtils.COLOR_GRAY, 0.05, 0.1F, 0); + } + + prevX = waypointPos.x; + prevY = offsetY; + prevZ = waypointPos.z; + hasPrev = true; + } + + if (hasTarget) { + double npcMidY = npcPosition.y + npcMidHeight; + Vector3f lineColor = isSeekTarget ? DebugUtils.COLOR_RED : DebugUtils.COLOR_YELLOW; + DebugUtils.addLine(world, npcPosition.x, npcMidY, npcPosition.z, targetX, targetY, targetZ, lineColor, 0.08, 0.1F, 0); + } + } } } } diff --git a/src/com/hypixel/hytale/server/npc/systems/SpawnReferenceSystems.java b/src/com/hypixel/hytale/server/npc/systems/SpawnReferenceSystems.java index af30bc89..a06a7db0 100644 --- a/src/com/hypixel/hytale/server/npc/systems/SpawnReferenceSystems.java +++ b/src/com/hypixel/hytale/server/npc/systems/SpawnReferenceSystems.java @@ -229,19 +229,63 @@ public class SpawnReferenceSystems { UUID uuid = uuidComponent.getUuid(); int spawnCount = spawnMarkerComponent.decrementAndGetSpawnCount(); + if (spawnCount < 0) { + SpawningPlugin.get() + .getLogger() + .at(Level.WARNING) + .log("Marker %s spawn count went negative (%d) while removing NPC %s", spawnMarkerRef, spawnCount, uuid); + spawnCount = 0; + spawnMarkerComponent.setSpawnCount(0); + } + SpawnMarker cachedMarker = spawnMarkerComponent.getCachedMarker(); - if (spawnCount > 0 && cachedMarker.getDeactivationDistance() > 0.0) { - InvalidatablePersistentRef[] newReferences = new InvalidatablePersistentRef[spawnCount]; - int pos = 0; + if (cachedMarker.getDeactivationDistance() > 0.0) { InvalidatablePersistentRef[] npcReferences = spawnMarkerComponent.getNpcReferences(); + int remaining = 0; for (InvalidatablePersistentRef npcRef : npcReferences) { - if (!npcRef.getUuid().equals(uuid)) { - newReferences[pos++] = npcRef; + if (!uuid.equals(npcRef.getUuid())) { + remaining++; + } + } + + InvalidatablePersistentRef[] newReferences = new InvalidatablePersistentRef[remaining]; + int pos = 0; + + for (InvalidatablePersistentRef npcRefx : npcReferences) { + if (!uuid.equals(npcRefx.getUuid())) { + newReferences[pos++] = npcRefx; } } spawnMarkerComponent.setNpcReferences(newReferences); + if (remaining == npcReferences.length) { + SpawningPlugin.get() + .getLogger() + .at(Level.WARNING) + .log( + "Marker %s removed NPC %s that was not present in marker references (spawnCount=%d, refs=%d)", + spawnMarkerRef, + uuid, + spawnCount, + npcReferences.length + ); + } + + if (spawnCount != remaining) { + SpawningPlugin.get() + .getLogger() + .at(Level.WARNING) + .log( + "Marker %s spawn count/reference mismatch while removing NPC %s (spawnCount=%d, refsAfter=%d)", + spawnMarkerRef, + uuid, + spawnCount, + remaining + ); + spawnCount = remaining; + spawnMarkerComponent.setSpawnCount(remaining); + } } if (spawnCount <= 0 && !cachedMarker.isRealtimeRespawn()) { diff --git a/src/com/hypixel/hytale/server/npc/systems/SteeringSystem.java b/src/com/hypixel/hytale/server/npc/systems/SteeringSystem.java index 091b1046..0db1aaba 100644 --- a/src/com/hypixel/hytale/server/npc/systems/SteeringSystem.java +++ b/src/com/hypixel/hytale/server/npc/systems/SteeringSystem.java @@ -10,7 +10,6 @@ import com.hypixel.hytale.component.dependency.Dependency; import com.hypixel.hytale.component.dependency.Order; import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.knockback.KnockbackSystems; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.system.TransformSystems; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.npc.role.Role; import java.util.Set; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SteeringSystem extends SteppableTickingSystem { @Nonnull @@ -81,12 +81,12 @@ public class SteeringSystem extends SteppableTickingSystem { try { if (role.getDebugSupport().isDebugMotionSteering()) { Vector3d position = transformComponent.getPosition(); - double x = position.getX(); - double z = position.getZ(); - float yaw = transformComponent.getRotation().getYaw(); + double x = position.x(); + double z = position.z(); + float yaw = transformComponent.getRotation().yaw(); role.getActiveMotionController().steer(ref, role, role.getBodySteering(), role.getHeadSteering(), dt, commandBuffer); - x = position.getX() - x; - z = position.getZ() - z; + x = position.x() - x; + z = position.z() - z; double l = Math.sqrt(x * x + z * z); double v = l / dt; double vx = x / dt; diff --git a/src/com/hypixel/hytale/server/npc/util/DamageData.java b/src/com/hypixel/hytale/server/npc/util/DamageData.java index a57ddf38..54e724ca 100644 --- a/src/com/hypixel/hytale/server/npc/util/DamageData.java +++ b/src/com/hypixel/hytale/server/npc/util/DamageData.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.npc.util; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.damage.Damage; @@ -11,15 +10,18 @@ import com.hypixel.hytale.server.core.modules.entity.player.PlayerSettings; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import it.unimi.dsi.fastutil.objects.Object2DoubleMap; import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap; -import java.util.HashMap; +import it.unimi.dsi.fastutil.objects.Reference2DoubleMap; +import it.unimi.dsi.fastutil.objects.Reference2DoubleOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class DamageData { - private final Map, Vector3d> kills = new HashMap<>(); - private final Object2DoubleMap> damageInflicted = new Object2DoubleOpenHashMap<>(); - private final Object2DoubleMap> damageSuffered = new Object2DoubleOpenHashMap<>(); + private final Map, Vector3d> kills = new Reference2ObjectOpenHashMap<>(); + private final Reference2DoubleMap> damageInflicted = new Reference2DoubleOpenHashMap<>(); + private final Reference2DoubleMap> damageSuffered = new Reference2DoubleOpenHashMap<>(); private final Object2DoubleMap damageByCause = new Object2DoubleOpenHashMap<>(); private double maxDamageSuffered; private double maxDamageInflicted; diff --git a/src/com/hypixel/hytale/server/npc/util/InventoryHelper.java b/src/com/hypixel/hytale/server/npc/util/InventoryHelper.java index b94056ee..8ea1a94d 100644 --- a/src/com/hypixel/hytale/server/npc/util/InventoryHelper.java +++ b/src/com/hypixel/hytale/server/npc/util/InventoryHelper.java @@ -1,14 +1,19 @@ package com.hypixel.hytale.server.npc.util; import com.hypixel.hytale.common.util.StringUtil; +import com.hypixel.hytale.component.ComponentAccessor; +import com.hypixel.hytale.component.Holder; +import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.asset.type.item.config.ItemArmor; import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList; import com.hypixel.hytale.server.core.inventory.Inventory; +import com.hypixel.hytale.server.core.inventory.InventoryComponent; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.modules.item.ItemModule; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.NPCPlugin; import java.util.List; import java.util.logging.Level; @@ -110,11 +115,12 @@ public class InventoryHelper { return -1; } - public static short findInventorySlotWithItem(@Nonnull Inventory inventory, String name) { - CombinedItemContainer container = inventory.getCombinedHotbarFirst(); + public static short findInventorySlotWithItem(@Nonnull Ref ref, @Nonnull String name, @Nonnull ComponentAccessor componentAccessor) { + CombinedItemContainer combinedContainer = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); - for (short i = 0; i < container.getCapacity(); i++) { - if (matchesItem(name, container.getItemStack(i))) { + for (short i = 0; i < combinedContainer.getCapacity(); i++) { + ItemStack itemStack = combinedContainer.getItemStack(i); + if (itemStack != null && matchesItem(name, itemStack)) { return i; } } @@ -122,11 +128,14 @@ public class InventoryHelper { return -1; } - public static short findInventorySlotWithItem(@Nonnull Inventory inventory, List name) { - CombinedItemContainer container = inventory.getCombinedHotbarFirst(); + public static short findInventorySlotWithItem( + @Nonnull Ref ref, @Nonnull List name, @Nonnull ComponentAccessor componentAccessor + ) { + CombinedItemContainer combinedContainer = InventoryComponent.getCombined(componentAccessor, ref, InventoryComponent.HOTBAR_FIRST); - for (short i = 0; i < container.getCapacity(); i++) { - if (matchesItem(name, container.getItemStack(i))) { + for (short i = 0; i < combinedContainer.getCapacity(); i++) { + ItemStack itemStack = combinedContainer.getItemStack(i); + if (itemStack != null && matchesItem(name, itemStack)) { return i; } } @@ -138,9 +147,9 @@ public class InventoryHelper { int count = 0; for (short i = 0; i < container.getCapacity(); i++) { - ItemStack item = container.getItemStack(i); - if (matchesItem(name, item)) { - count += item.getQuantity(); + ItemStack itemStack = container.getItemStack(i); + if (itemStack != null && matchesItem(name, itemStack)) { + count += itemStack.getQuantity(); } } @@ -172,26 +181,28 @@ public class InventoryHelper { return matchesItem(name, inventory.getItemInHand()); } - public static boolean containsItem(@Nonnull Inventory inventory, String name) { - return findInventorySlotWithItem(inventory, name) != -1; + public static boolean containsItem(@Nonnull Ref ref, String name, @Nonnull ComponentAccessor componentAccessor) { + return findInventorySlotWithItem(ref, name, componentAccessor) != -1; } - public static boolean containsItem(@Nonnull Inventory inventory, List name) { - return findInventorySlotWithItem(inventory, name) != -1; + public static boolean containsItem(@Nonnull Ref ref, List name, @Nonnull ComponentAccessor componentAccessor) { + return findInventorySlotWithItem(ref, name, componentAccessor) != -1; } - public static boolean clearItemInHand(@Nonnull Inventory inventory, byte slotHint) { + public static boolean clearItemInHand( + @Nonnull Ref ref, @Nonnull Inventory inventory, byte slotHint, @Nonnull ComponentAccessor componentAccessor + ) { if (ItemStack.isEmpty(inventory.getItemInHand())) { return true; } else { byte slot = findHotbarEmptySlot(inventory); if (slot >= 0) { - inventory.setActiveHotbarSlot(slot); + inventory.setActiveHotbarSlot(ref, slot, componentAccessor); return true; } else { slot = slotHint != -1 ? slotHint : 0; inventory.getHotbar().removeItemStackFromSlot(slot); - inventory.setActiveHotbarSlot(slot); + inventory.setActiveHotbarSlot(ref, slot, componentAccessor); return true; } } @@ -226,18 +237,30 @@ public class InventoryHelper { } } - public static void setHotbarSlot(@Nonnull Inventory inventory, byte slot) { + public static void setHotbarSlot( + @Nonnull Ref ref, @Nonnull Inventory inventory, byte slot, @Nonnull ComponentAccessor componentAccessor + ) { if (inventory.getActiveHotbarSlot() != slot) { if (checkHotbarSlot(inventory, slot)) { - inventory.setActiveHotbarSlot(slot); + inventory.setActiveHotbarSlot(ref, slot, componentAccessor); } } } - public static void setOffHandSlot(@Nonnull Inventory inventory, byte slot) { + public static void setOffHandSlot( + @Nonnull Ref ref, @Nonnull Inventory inventory, byte slot, @Nonnull ComponentAccessor componentAccessor + ) { if (inventory.getActiveUtilitySlot() != slot) { if (checkOffHandSlot(inventory, slot)) { - inventory.setActiveUtilitySlot(slot); + inventory.setActiveUtilitySlot(ref, slot, componentAccessor); + } + } + } + + public static void setOffHandSlot(@Nonnull Holder holder, @Nonnull Inventory inventory, byte slot) { + if (inventory.getActiveUtilitySlot() != slot) { + if (checkOffHandSlot(inventory, slot)) { + inventory.setActiveUtilitySlot(holder, slot); } } } @@ -274,7 +297,13 @@ public class InventoryHelper { } } - public static boolean useItem(@Nonnull Inventory inventory, @Nullable String name, byte slotHint) { + public static boolean useItem( + @Nonnull Ref ref, + @Nonnull Inventory inventory, + @Nullable String name, + byte slotHint, + @Nonnull ComponentAccessor componentAccessor + ) { if (name == null || name.isEmpty() || !itemKeyExists(name)) { return false; } else if (holdsItem(inventory, name)) { @@ -282,7 +311,7 @@ public class InventoryHelper { } else { byte slot = findHotbarSlotWithItem(inventory, name); if (slot >= 0) { - inventory.setActiveHotbarSlot(slot); + inventory.setActiveHotbarSlot(ref, slot, componentAccessor); return true; } else { if (slotHint == -1) { @@ -294,7 +323,7 @@ public class InventoryHelper { } inventory.getHotbar().setItemStackForSlot(slotHint, createItem(name)); - inventory.setActiveHotbarSlot(slotHint); + inventory.setActiveHotbarSlot(ref, slotHint, componentAccessor); return true; } } @@ -305,8 +334,12 @@ public class InventoryHelper { return !itemKeyExists(name) ? null : new ItemStack(name, 1); } - public static boolean useItem(@Nonnull Inventory inventory, @Nullable String name) { - return name != null && !name.isEmpty() ? useItem(inventory, name, (byte)-1) : clearItemInHand(inventory, (byte)-1); + public static boolean useItem( + @Nonnull Ref ref, @Nonnull Inventory inventory, @Nullable String name, @Nonnull ComponentAccessor componentAccessor + ) { + return name != null && !name.isEmpty() + ? useItem(ref, inventory, name, (byte)-1, componentAccessor) + : clearItemInHand(ref, inventory, (byte)-1, componentAccessor); } public static boolean useArmor(@Nonnull ItemContainer armorInventory, @Nullable String armorItem) { diff --git a/src/com/hypixel/hytale/server/npc/util/NPCPhysicsMath.java b/src/com/hypixel/hytale/server/npc/util/NPCPhysicsMath.java index bb8e2f1c..4ae9153c 100644 --- a/src/com/hypixel/hytale/server/npc/util/NPCPhysicsMath.java +++ b/src/com/hypixel/hytale/server/npc/util/NPCPhysicsMath.java @@ -6,8 +6,7 @@ import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -19,6 +18,8 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.entities.NPCEntity; import com.hypixel.hytale.server.npc.role.Role; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class NPCPhysicsMath { public static final double EPSILON_LENGTH = 1.0E-6; @@ -27,12 +28,12 @@ public class NPCPhysicsMath { private NPCPhysicsMath() { } - public static boolean near(@Nonnull Vector3d v, @Nonnull Vector3d w) { + public static boolean near(@Nonnull Vector3dc v, @Nonnull Vector3dc w) { return near(v, w, 1.0E-12); } - public static boolean near(@Nonnull Vector3d v, @Nonnull Vector3d w, double epsilonLength) { - return v.distanceSquaredTo(w) <= epsilonLength; + public static boolean near(@Nonnull Vector3dc v, @Nonnull Vector3dc w, double epsilonLength) { + return v.distanceSquared(w) <= epsilonLength; } public static boolean near(double v, double w) { @@ -54,8 +55,8 @@ public class NPCPhysicsMath { } @Nonnull - public static Vector3d getViewDirection(@Nonnull Vector3f lookDirection, @Nonnull Vector3d outDirection) { - return PhysicsMath.vectorFromAngles(lookDirection.getYaw(), lookDirection.getPitch(), outDirection); + public static Vector3d getViewDirection(@Nonnull Rotation3f rotation, @Nonnull Vector3d outDirection) { + return PhysicsMath.vectorFromAngles(rotation.yaw(), rotation.pitch(), outDirection); } public static double cosAngleBetweenVectors(@Nonnull Vector3d v, @Nonnull Vector3d w) { @@ -254,8 +255,8 @@ public class NPCPhysicsMath { public static int intersectLineSphere( @Nonnull Vector3d center, double radius, @Nonnull Vector3d p, @Nonnull Vector3d q, @Nonnull Vector3d x1, @Nonnull Vector3d x2, boolean segmentOnly ) { - x1.assign(q).subtract(p); - x2.assign(p).subtract(center); + x1.set(q).sub(p); + x2.set(p).sub(center); double a = x1.dot(x1); double b = 2.0 * x1.dot(x2); double c = x2.dot(x2) - radius * radius; @@ -264,7 +265,7 @@ public class NPCPhysicsMath { if (k != 0.0) { return 0; } else { - x1.assign(p); + x1.set(p); return 1; } } else if (k < 0.0) { @@ -286,9 +287,9 @@ public class NPCPhysicsMath { } if (d1 >= 0.0) { - x1.assign(p).addScaled(x2, d1); + x1.set(p).fma(d1, x2); if (d2 <= 1.0) { - x2.scale(d2).add(p); + x2.mul(d2).add(p); return 2; } @@ -296,18 +297,18 @@ public class NPCPhysicsMath { } if (d2 >= 0.0 && d2 <= 1.0) { - x1.assign(p).addScaled(x2, d1); + x1.set(p).fma(d1, x2); return 1; } } - x1.assign(p).addScaled(x2, d1); - x2.scale(d2).add(p); + x1.set(p).fma(d1, x2); + x2.mul(d2).add(p); return 2; } else { double dx = -b / (2.0 * a); if (!segmentOnly || !(dx < 0.0) && !(dx > 1.0)) { - x1.assign(p).addScaled(x2, dx); + x1.set(p).fma(dx, x2); return 1; } else { return 0; @@ -324,8 +325,8 @@ public class NPCPhysicsMath { @Nonnull Vector3d t2, @Nonnull Vector3d componentSelector ) { - t1.assign(q).subtract(p).scale(componentSelector); - t2.assign(p).subtract(center).scale(componentSelector); + t1.set(q).sub(p).mul(componentSelector); + t2.set(p).sub(center).mul(componentSelector); double a = t1.dot(t1); double b = 2.0 * t1.dot(t2); double c = t2.dot(t2) - radius * radius; @@ -405,7 +406,7 @@ public class NPCPhysicsMath { } public static void lerpDistance(@Nonnull Vector3d start, @Nonnull Vector3d end, double distance, @Nonnull Vector3d result) { - lerp(start, end, distance / start.distanceTo(end), result); + lerp(start, end, distance / start.distance(end), result); } public static void lerp(@Nonnull Vector3d start, @Nonnull Vector3d end, double lambda, @Nonnull Vector3d result) { @@ -420,11 +421,11 @@ public class NPCPhysicsMath { } public static void offsetVector(@Nonnull Vector3d start, double dx, double dy, double dz, double lambda, @Nonnull Vector3d result) { - result.assign(start.x + lambda * dx, start.y + lambda * dy, start.z + lambda * dz); + result.set(start.x + lambda * dx, start.y + lambda * dy, start.z + lambda * dz); } public static void offsetVector(double sx, double sy, double sz, double dx, double dy, double dz, double lambda, @Nonnull Vector3d result) { - result.assign(sx + lambda * dx, sy + lambda * dy, sz + lambda * dz); + result.set(sx + lambda * dx, sy + lambda * dy, sz + lambda * dz); } public static void orthoComposition(@Nonnull Vector3d start, @Nonnull Vector3d end, @Nonnull Vector3d ortho, double distance, @Nonnull Vector3d result) { @@ -438,16 +439,16 @@ public class NPCPhysicsMath { } public static void orthoComposition( - @Nonnull Vector3d start, @Nonnull Vector3d end, double distanceStart, @Nonnull Vector3d ortho, double distance, @Nonnull Vector3d result + @Nonnull Vector3dc start, @Nonnull Vector3dc end, double distanceStart, @Nonnull Vector3dc ortho, double distance, @Nonnull Vector3d result ) { - double dx = end.x - start.x; - double dy = end.y - start.y; - double dz = end.z - start.z; - double ox = dy * ortho.z - dz * ortho.y; - double oy = dz * ortho.x - dx * ortho.z; - double oz = dx * ortho.y - dy * ortho.x; + double dx = end.x() - start.x(); + double dy = end.y() - start.y(); + double dz = end.z() - start.z(); + double ox = dy * ortho.z() - dz * ortho.y(); + double oy = dz * ortho.x() - dx * ortho.z(); + double oz = dx * ortho.y() - dy * ortho.x(); double lambda = distanceStart / length(dx, dy, dz); - offsetVector(start.x + lambda * dx, start.y + lambda * dy, start.z + lambda * dz, ox, oy, oz, distance / length(ox, oy, oz), result); + offsetVector(start.x() + lambda * dx, start.y() + lambda * dy, start.z() + lambda * dz, ox, oy, oz, distance / length(ox, oy, oz), result); } public static float lookatHeading(@Nonnull Vector3d self, @Nonnull Vector3d pointOfInterest, float headingHint) { @@ -544,6 +545,21 @@ public class NPCPhysicsMath { return Math.sqrt(projectedLengthSquared(v, componentSelector)); } + public static double dotProductWithSelector(@Nonnull Vector3d p, @Nonnull Vector3d q, @Nonnull Vector3d componentSelector) { + return dotProduct(p.x, p.y, p.z, q.x, q.y, q.z, componentSelector); + } + + public static double distanceWithSelector(@Nonnull Vector3d p, @Nonnull Vector3d q, @Nonnull Vector3d componentSelector) { + return Math.sqrt(distanceSquaredWithSelector(p, q, componentSelector)); + } + + public static double distanceSquaredWithSelector(@Nonnull Vector3d p, @Nonnull Vector3d q, @Nonnull Vector3d componentSelector) { + double dx = (p.x - q.x) * componentSelector.x; + double dy = (p.y - q.y) * componentSelector.y; + double dz = (p.z - q.z) * componentSelector.z; + return dx * dx + dy * dy + dz * dz; + } + public static int intersectSweptSpheres( @Nonnull Vector3d p1, @Nonnull Vector3d velocity1, @@ -565,7 +581,7 @@ public class NPCPhysicsMath { @Nonnull Vector3d p2, @Nonnull Vector3d velocity2, double radius2, - @Nonnull Vector3d componentSelector, + @Nonnull Vector3dc componentSelector, double[] results ) { return intersectSweptSpheres( @@ -601,15 +617,15 @@ public class NPCPhysicsMath { double velocity2y, double velocity2z, double radius, - @Nonnull Vector3d componentSelector, + @Nonnull Vector3dc componentSelector, double[] results ) { - double px = (p2x - p1x) * componentSelector.x; - double py = (p2y - p1y) * componentSelector.y; - double pz = (p2z - p1z) * componentSelector.z; - double vx = (velocity2x - velocity1x) * componentSelector.x; - double vy = (velocity2y - velocity1y) * componentSelector.y; - double vz = (velocity2z - velocity1z) * componentSelector.z; + double px = (p2x - p1x) * componentSelector.x(); + double py = (p2y - p1y) * componentSelector.y(); + double pz = (p2z - p1z) * componentSelector.z(); + double vx = (velocity2x - velocity1x) * componentSelector.x(); + double vy = (velocity2y - velocity1y) * componentSelector.y(); + double vz = (velocity2z - velocity1z) * componentSelector.z(); double a = dotProduct(vx, vy, vz); double b = 2.0 * dotProduct(px, py, pz, vx, vy, vz); double c = dotProduct(px, py, pz) - radius * radius; @@ -679,19 +695,21 @@ public class NPCPhysicsMath { } } - public static double rayCircleIntersect(@Nonnull Vector3d start, @Nonnull Vector3d end, @Nonnull Vector3d center, double radius, @Nonnull Vector3d normal) { - if (normal.x == 0.0) { - return rayCircleIntersect(start.y - center.y, start.z - center.z, end.y - start.y, end.z - start.z, radius); + public static double rayCircleIntersect( + @Nonnull Vector3dc start, @Nonnull Vector3dc end, @Nonnull Vector3dc center, double radius, @Nonnull Vector3dc normal + ) { + if (normal.x() == 0.0) { + return rayCircleIntersect(start.y() - center.y(), start.z() - center.z(), end.y() - start.y(), end.z() - start.z(), radius); } else { - return normal.y == 0.0 - ? rayCircleIntersect(start.x - center.x, start.z - center.z, end.x - start.x, end.z - start.z, radius) - : rayCircleIntersect(start.x - center.x, start.y - center.y, end.x - start.x, end.y - start.y, radius); + return normal.y() == 0.0 + ? rayCircleIntersect(start.x() - center.x(), start.z() - center.z(), end.x() - start.x(), end.z() - start.z(), radius) + : rayCircleIntersect(start.x() - center.x(), start.y() - center.y(), end.x() - start.x(), end.y() - start.y(), radius); } } @Nonnull public static Vector3d projection(@Nonnull Vector3d v, @Nonnull Vector3d p, @Nonnull Vector3d result) { - result.assign(v).scale(p.dot(v) / v.dot(v)); + result.set(v).mul(p.dot(v) / v.dot(v)); return result; } @@ -704,7 +722,7 @@ public class NPCPhysicsMath { @Nonnull public static Vector3d subtractVector(@Nonnull Vector3d p, @Nonnull Vector3d q, @Nonnull Vector3d result) { - return result.assign(p.x - q.x, p.y - q.y, p.z - q.z); + return result.set(p.x - q.x, p.y - q.y, p.z - q.z); } @Nonnull @@ -714,7 +732,7 @@ public class NPCPhysicsMath { @Nonnull public static Vector3d projection(@Nonnull Vector3d base, @Nonnull Vector3d v, @Nonnull Vector3d p, @Nonnull Vector3d result) { - subtractVector(v, base, result).scale(dotProduct(base, p, v) / dotProduct(base, v, v)); + subtractVector(v, base, result).mul(dotProduct(base, p, v) / dotProduct(base, v, v)); return result; } @@ -733,23 +751,23 @@ public class NPCPhysicsMath { return v; } - public static double squaredDistProjected(double px, double py, double pz, @Nonnull Vector3d q, @Nonnull Vector3d normal) { + public static double squaredDistProjected(double px, double py, double pz, @Nonnull Vector3d q, @Nonnull Vector3dc normal) { return squaredDistProjected(px, py, pz, q.x, q.y, q.z, normal); } - public static double squaredDistProjected(double px, double py, double pz, double qx, double qy, double qz, @Nonnull Vector3d normal) { + public static double squaredDistProjected(double px, double py, double pz, double qx, double qy, double qz, @Nonnull Vector3dc normal) { double d2 = 0.0; - if (normal.x == 0.0) { + if (normal.x() == 0.0) { double d = qx - px; d2 += d * d; } - if (normal.y == 0.0) { + if (normal.y() == 0.0) { double d = qy - py; d2 += d * d; } - if (normal.z == 0.0) { + if (normal.z() == 0.0) { double d = qz - pz; d2 += d * d; } @@ -787,7 +805,7 @@ public class NPCPhysicsMath { double s2 = y - d; double v = Math.sqrt(s1 / gravity); float phi = TrigMathUtil.atan(-v * v / (gravity * x)); - velocity.assign(dx, TrigMathUtil.sin(phi) * x, dz).setLength(v); + velocity.set(dx, TrigMathUtil.sin(phi) * x, dz).normalize(v); return v; } diff --git a/src/com/hypixel/hytale/server/npc/util/PositionProbeAir.java b/src/com/hypixel/hytale/server/npc/util/PositionProbeAir.java index 2430d96d..7a251828 100644 --- a/src/com/hypixel/hytale/server/npc/util/PositionProbeAir.java +++ b/src/com/hypixel/hytale/server/npc/util/PositionProbeAir.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.npc.util; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.modules.collision.BoxBlockIntersectionEvaluator; import com.hypixel.hytale.server.core.modules.collision.CollisionConfig; @@ -11,6 +10,7 @@ import com.hypixel.hytale.server.core.modules.collision.CollisionMath; import com.hypixel.hytale.server.core.modules.collision.CollisionResult; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PositionProbeAir extends PositionProbeBase { protected boolean inAir; diff --git a/src/com/hypixel/hytale/server/npc/util/PositionProbeBase.java b/src/com/hypixel/hytale/server/npc/util/PositionProbeBase.java index 0ce62c1f..19d61da8 100644 --- a/src/com/hypixel/hytale/server/npc/util/PositionProbeBase.java +++ b/src/com/hypixel/hytale/server/npc/util/PositionProbeBase.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.shape.Box; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.collision.BoxBlockIntersectionEvaluator; import com.hypixel.hytale.server.core.modules.collision.CollisionFilter; import com.hypixel.hytale.server.core.modules.collision.CollisionModule; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PositionProbeBase { private static final int lastWaterCheckDistanceMinSquared = 25; diff --git a/src/com/hypixel/hytale/server/npc/util/PositionProbeWater.java b/src/com/hypixel/hytale/server/npc/util/PositionProbeWater.java index 7913147a..e548d1dc 100644 --- a/src/com/hypixel/hytale/server/npc/util/PositionProbeWater.java +++ b/src/com/hypixel/hytale/server/npc/util/PositionProbeWater.java @@ -3,13 +3,13 @@ package com.hypixel.hytale.server.npc.util; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.shape.Box; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.collision.BoxBlockIntersectionEvaluator; import com.hypixel.hytale.server.core.modules.collision.CollisionConfig; import com.hypixel.hytale.server.core.modules.collision.CollisionMath; import com.hypixel.hytale.server.core.modules.collision.CollisionResult; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PositionProbeWater extends PositionProbeBase { private double ySwim; diff --git a/src/com/hypixel/hytale/server/npc/util/RayBlockHitTest.java b/src/com/hypixel/hytale/server/npc/util/RayBlockHitTest.java index 94349bd8..e0e55ff4 100644 --- a/src/com/hypixel/hytale/server/npc/util/RayBlockHitTest.java +++ b/src/com/hypixel/hytale/server/npc/util/RayBlockHitTest.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.math.iterator.BlockIterator; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.modules.blockset.BlockSetModule; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; @@ -14,6 +14,7 @@ import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class RayBlockHitTest implements BlockIterator.BlockIteratorProcedure { public static final ThreadLocal THREAD_LOCAL = ThreadLocal.withInitial(RayBlockHitTest::new); @@ -24,7 +25,7 @@ public class RayBlockHitTest implements BlockIterator.BlockIteratorProcedure { private final Vector3d origin = new Vector3d(); private final Vector3d direction = new Vector3d(); private int blockSet; - private final Vector3d hitPosition = new Vector3d(Vector3d.MIN); + private final Vector3d hitPosition = new Vector3d(Vector3dUtil.MIN); private short lastBlockRevision; @Override @@ -32,7 +33,7 @@ public class RayBlockHitTest implements BlockIterator.BlockIteratorProcedure { if (!ChunkUtil.isInsideChunk(this.chunk.getX(), this.chunk.getZ(), x, z)) { this.chunk = this.world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(x, z)); if (this.chunk == null) { - this.hitPosition.assign(Vector3d.MIN); + this.hitPosition.set(Vector3dUtil.MIN); return false; } } @@ -45,7 +46,7 @@ public class RayBlockHitTest implements BlockIterator.BlockIteratorProcedure { } else { this.lastBlockRevision = this.chunk.getBlockChunk().getSectionAtBlockY(y).getLocalChangeCounter(); if (BlockSetModule.getInstance().blockInSet(this.blockSet, blockId)) { - this.hitPosition.assign(x, y, z); + this.hitPosition.set(x, y, z); } return false; @@ -76,27 +77,27 @@ public class RayBlockHitTest implements BlockIterator.BlockIteratorProcedure { World world = componentAccessor.getExternalData().getWorld(); this.blockSet = blockSet; - this.origin.assign(transformComponent.getPosition()); + this.origin.set(transformComponent.getPosition()); this.origin.y = this.origin.y + modelComponent.getModel().getEyeHeight(); this.world = world; this.chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(this.origin.x, this.origin.z)); if (this.chunk == null) { return false; } else { - float yaw = headRotationComponent.getRotation().getYaw(); - this.direction.assign(yaw, pitch); + float yaw = headRotationComponent.getRotation().yaw(); + Vector3dUtil.setYawPitch(yaw, pitch, this.direction); return true; } } public boolean run(double range) { BlockIterator.iterate(this.origin, this.direction, range, this); - return !this.hitPosition.equals(Vector3d.MIN); + return !this.hitPosition.equals(Vector3dUtil.MIN); } public void clear() { this.world = null; this.chunk = null; - this.hitPosition.assign(Vector3d.MIN); + this.hitPosition.set(Vector3dUtil.MIN); } } diff --git a/src/com/hypixel/hytale/server/npc/util/VisHelper.java b/src/com/hypixel/hytale/server/npc/util/VisHelper.java new file mode 100644 index 00000000..51f9deb7 --- /dev/null +++ b/src/com/hypixel/hytale/server/npc/util/VisHelper.java @@ -0,0 +1,58 @@ +package com.hypixel.hytale.server.npc.util; + +import com.hypixel.hytale.server.core.modules.debug.DebugUtils; +import com.hypixel.hytale.server.core.universe.world.World; +import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3f; + +public class VisHelper { + public static final Vector3f DEBUG_COLOR_STEERING_POST = DebugUtils.COLOR_GREEN; + public static final Vector3f DEBUG_COLOR_STEERING_PRE = DebugUtils.COLOR_RED; + public static final Vector3f DEBUG_COLOR_AVOIDANCE = DebugUtils.COLOR_WHITE; + public static final Vector3f DEBUG_COLOR_SEPARATION = DebugUtils.COLOR_BLUE; + public static final double DEBUG_MIN_VECTOR_DRAW_LENGTH_SQUARED = 0.01; + public static final double DEBUG_VECTORS_SCALE = 4.0; + public static final float DEBUG_VECTORS_TIME = 0.05F; + public static final float DEBUG_TRANSPARENT = 0.24000001F; + public static final double DEBUG_SPHERE_SCALE = 1.0; + + public static void renderDebugVector(@Nonnull Vector3d position, @Nonnull Vector3d direction, @Nonnull Vector3f color, @Nonnull World world) { + renderDebugVector(position, direction, color, 0.24000001F, world); + } + + public static void renderDebugVector(@Nonnull Vector3d position, @Nonnull Vector3d direction, @Nonnull Vector3f color, float opacity, @Nonnull World world) { + if (!(direction.lengthSquared() < 0.01)) { + Vector3d scaledDir = new Vector3d(direction).mul(4.0); + DebugUtils.addArrow(world, position, scaledDir, color, opacity, 0.05F, 0); + } + } + + public static void renderDebugVectorTo(@Nonnull Vector3d position, @Nonnull Vector3d direction, @Nonnull Vector3f color, @Nonnull World world) { + renderDebugVectorTo(position, direction, color, 0.24000001F, world); + } + + public static void renderDebugVectorTo(@Nonnull Vector3d position, @Nonnull Vector3d direction, @Nonnull Vector3f color, float opacity, @Nonnull World world) { + if (!(direction.lengthSquared() < 0.01)) { + Vector3d scaledDir = new Vector3d(direction).mul(4.0); + Vector3d start = new Vector3d(position).sub(scaledDir); + DebugUtils.addArrow(world, start, scaledDir, color, opacity, 0.05F, 0); + } + } + + public static void renderDebugSphere(@Nonnull Vector3d position, @Nonnull Vector3f color, @Nonnull World world) { + renderDebugSphere(position, color, 0.24000001F, world); + } + + public static void renderDebugSphere(@Nonnull Vector3d position, @Nonnull Vector3f color, float opacity, @Nonnull World world) { + DebugUtils.addSphere(world, position, color, opacity, 1.0, 0.05F); + } + + public static void renderDebugSphere(@Nonnull Vector3d position, double radius, @Nonnull Vector3f color, @Nonnull World world) { + renderDebugSphere(position, radius, color, 0.24000001F, world); + } + + public static void renderDebugSphere(@Nonnull Vector3d position, double radius, @Nonnull Vector3f color, float opacity, @Nonnull World world) { + DebugUtils.addSphere(world, position, color, opacity, radius, 0.05F); + } +} diff --git a/src/com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperand.java b/src/com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperand.java index 616a37a6..e3c4fcd7 100644 --- a/src/com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperand.java +++ b/src/com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperand.java @@ -36,187 +36,34 @@ public abstract class ASTOperand extends AST { } @Nonnull - private static ASTOperand createFromScopeConstant(@Nonnull Token param0, int param1, @Nonnull Scope param2, String param3) { - // $VF: Couldn't be decompiled - // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) - // java.lang.IllegalStateException: Invalid switch case set: [[const(0)], [const(1)], [const(2)], [const(3)], [const(4)], [const(5)], [const(6)], [const(null), null]] for selector of type Lcom/hypixel/hytale/server/npc/util/expression/ValueType; - // at org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchHeadExprent.checkExprTypeBounds(SwitchHeadExprent.java:66) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.checkTypeExpr(VarTypeProcessor.java:140) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.checkTypeExprent(VarTypeProcessor.java:126) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.lambda$processVarTypes$2(VarTypeProcessor.java:114) - // at org.jetbrains.java.decompiler.modules.decompiler.flow.DirectGraph.iterateExprents(DirectGraph.java:107) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.processVarTypes(VarTypeProcessor.java:114) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.calculateVarTypes(VarTypeProcessor.java:44) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsProcessor.setVarVersions(VarVersionsProcessor.java:68) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor.setVarVersions(VarProcessor.java:47) - // at org.jetbrains.java.decompiler.main.rels.MethodProcessor.codeToJava(MethodProcessor.java:302) - // - // Bytecode: - // 00: aload 2 - // 01: aload 3 - // 02: invokeinterface com/hypixel/hytale/server/npc/util/expression/Scope.getType (Ljava/lang/String;)Lcom/hypixel/hytale/server/npc/util/expression/ValueType; 2 - // 07: astore 4 - // 09: aload 4 - // 0b: astore 5 - // 0d: bipush 0 - // 0e: istore 6 - // 10: aload 5 - // 12: iload 6 - // 14: invokedynamic typeSwitch (Ljava/lang/Object;I)I bsm=java/lang/runtime/SwitchBootstraps.typeSwitch (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc; ] - // 19: tableswitch 143 -1 6 143 47 61 75 89 103 117 131 - // 48: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandNumber - // 4b: dup - // 4c: aload 0 - // 4d: iload 1 - // 4e: aload 2 - // 4f: aload 3 - // 50: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandNumber. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;ILcom/hypixel/hytale/server/npc/util/expression/Scope;Ljava/lang/String;)V - // 53: goto ba - // 56: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandString - // 59: dup - // 5a: aload 0 - // 5b: iload 1 - // 5c: aload 2 - // 5d: aload 3 - // 5e: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandString. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;ILcom/hypixel/hytale/server/npc/util/expression/Scope;Ljava/lang/String;)V - // 61: goto ba - // 64: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandBoolean - // 67: dup - // 68: aload 0 - // 69: iload 1 - // 6a: aload 2 - // 6b: aload 3 - // 6c: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandBoolean. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;ILcom/hypixel/hytale/server/npc/util/expression/Scope;Ljava/lang/String;)V - // 6f: goto ba - // 72: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandNumberArray - // 75: dup - // 76: aload 0 - // 77: iload 1 - // 78: aload 2 - // 79: aload 3 - // 7a: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandNumberArray. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;ILcom/hypixel/hytale/server/npc/util/expression/Scope;Ljava/lang/String;)V - // 7d: goto ba - // 80: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandStringArray - // 83: dup - // 84: aload 0 - // 85: iload 1 - // 86: aload 2 - // 87: aload 3 - // 88: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandStringArray. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;ILcom/hypixel/hytale/server/npc/util/expression/Scope;Ljava/lang/String;)V - // 8b: goto ba - // 8e: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandBooleanArray - // 91: dup - // 92: aload 0 - // 93: iload 1 - // 94: aload 2 - // 95: aload 3 - // 96: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandBooleanArray. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;ILcom/hypixel/hytale/server/npc/util/expression/Scope;Ljava/lang/String;)V - // 99: goto ba - // 9c: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandEmptyArray - // 9f: dup - // a0: aload 0 - // a1: iload 1 - // a2: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandEmptyArray. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;I)V - // a5: goto ba - // a8: new java/lang/IllegalStateException - // ab: dup - // ac: aload 4 - // ae: invokestatic java/lang/String.valueOf (Ljava/lang/Object;)Ljava/lang/String; - // b1: invokedynamic makeConcatWithConstants (Ljava/lang/String;)Ljava/lang/String; bsm=java/lang/invoke/StringConcatFactory.makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ "Illegal constant type encountered\u0001" ] - // b6: invokespecial java/lang/IllegalStateException. (Ljava/lang/String;)V - // b9: athrow - // ba: areturn + private static ASTOperand createFromScopeConstant(@Nonnull Token token, int tokenPosition, @Nonnull Scope scope, String identifier) { + ValueType type = scope.getType(identifier); + + return (ASTOperand)(switch (type) { + case NUMBER -> new ASTOperandNumber(token, tokenPosition, scope, identifier); + case STRING -> new ASTOperandString(token, tokenPosition, scope, identifier); + case BOOLEAN -> new ASTOperandBoolean(token, tokenPosition, scope, identifier); + case NUMBER_ARRAY -> new ASTOperandNumberArray(token, tokenPosition, scope, identifier); + case STRING_ARRAY -> new ASTOperandStringArray(token, tokenPosition, scope, identifier); + case BOOLEAN_ARRAY -> new ASTOperandBooleanArray(token, tokenPosition, scope, identifier); + case EMPTY_ARRAY -> new ASTOperandEmptyArray(token, tokenPosition); + case null, default -> throw new IllegalStateException("Illegal constant type encountered" + type); + }); } @Nonnull - public static ASTOperand createFromOperand(@Nonnull Token param0, int param1, @Nonnull ExecutionContext.Operand param2) { - // $VF: Couldn't be decompiled - // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) - // java.lang.IllegalStateException: Invalid switch case set: [[const(0)], [const(1)], [const(2)], [const(3)], [const(4)], [const(5)], [const(6)], [const(null), null]] for selector of type Lcom/hypixel/hytale/server/npc/util/expression/ValueType; - // at org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchHeadExprent.checkExprTypeBounds(SwitchHeadExprent.java:66) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.checkTypeExpr(VarTypeProcessor.java:140) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.checkTypeExprent(VarTypeProcessor.java:126) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.lambda$processVarTypes$2(VarTypeProcessor.java:114) - // at org.jetbrains.java.decompiler.modules.decompiler.flow.DirectGraph.iterateExprents(DirectGraph.java:107) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.processVarTypes(VarTypeProcessor.java:114) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor.calculateVarTypes(VarTypeProcessor.java:44) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsProcessor.setVarVersions(VarVersionsProcessor.java:68) - // at org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor.setVarVersions(VarProcessor.java:47) - // at org.jetbrains.java.decompiler.main.rels.MethodProcessor.codeToJava(MethodProcessor.java:302) - // - // Bytecode: - // 00: aload 2 - // 01: getfield com/hypixel/hytale/server/npc/util/expression/ExecutionContext$Operand.type Lcom/hypixel/hytale/server/npc/util/expression/ValueType; - // 04: astore 3 - // 05: aload 3 - // 06: astore 4 - // 08: bipush 0 - // 09: istore 5 - // 0b: aload 4 - // 0d: iload 5 - // 0f: invokedynamic typeSwitch (Ljava/lang/Object;I)I bsm=java/lang/runtime/SwitchBootstraps.typeSwitch (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc;, null.invoke Ljava/lang/Enum$EnumDesc; ] - // 14: tableswitch 156 -1 6 156 48 64 80 96 112 128 144 - // 44: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandNumber - // 47: dup - // 48: aload 0 - // 49: iload 1 - // 4a: aload 2 - // 4b: getfield com/hypixel/hytale/server/npc/util/expression/ExecutionContext$Operand.number D - // 4e: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandNumber. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;ID)V - // 51: goto c1 - // 54: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandString - // 57: dup - // 58: aload 0 - // 59: iload 1 - // 5a: aload 2 - // 5b: getfield com/hypixel/hytale/server/npc/util/expression/ExecutionContext$Operand.string Ljava/lang/String; - // 5e: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandString. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;ILjava/lang/String;)V - // 61: goto c1 - // 64: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandBoolean - // 67: dup - // 68: aload 0 - // 69: iload 1 - // 6a: aload 2 - // 6b: getfield com/hypixel/hytale/server/npc/util/expression/ExecutionContext$Operand.bool Z - // 6e: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandBoolean. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;IZ)V - // 71: goto c1 - // 74: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandNumberArray - // 77: dup - // 78: aload 0 - // 79: iload 1 - // 7a: aload 2 - // 7b: getfield com/hypixel/hytale/server/npc/util/expression/ExecutionContext$Operand.numberArray [D - // 7e: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandNumberArray. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;I[D)V - // 81: goto c1 - // 84: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandStringArray - // 87: dup - // 88: aload 0 - // 89: iload 1 - // 8a: aload 2 - // 8b: getfield com/hypixel/hytale/server/npc/util/expression/ExecutionContext$Operand.stringArray [Ljava/lang/String; - // 8e: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandStringArray. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;I[Ljava/lang/String;)V - // 91: goto c1 - // 94: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandBooleanArray - // 97: dup - // 98: aload 0 - // 99: iload 1 - // 9a: aload 2 - // 9b: getfield com/hypixel/hytale/server/npc/util/expression/ExecutionContext$Operand.boolArray [Z - // 9e: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandBooleanArray. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;I[Z)V - // a1: goto c1 - // a4: new com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandEmptyArray - // a7: dup - // a8: aload 0 - // a9: iload 1 - // aa: invokespecial com/hypixel/hytale/server/npc/util/expression/compile/ast/ASTOperandEmptyArray. (Lcom/hypixel/hytale/server/npc/util/expression/compile/Token;I)V - // ad: goto c1 - // b0: new java/lang/IllegalStateException - // b3: dup - // b4: aload 3 - // b5: invokestatic java/lang/String.valueOf (Ljava/lang/Object;)Ljava/lang/String; - // b8: invokedynamic makeConcatWithConstants (Ljava/lang/String;)Ljava/lang/String; bsm=java/lang/invoke/StringConcatFactory.makeConcatWithConstants (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; args=[ "Illegal operand type encountered\u0001" ] - // bd: invokespecial java/lang/IllegalStateException. (Ljava/lang/String;)V - // c0: athrow - // c1: areturn + public static ASTOperand createFromOperand(@Nonnull Token token, int tokenPosition, @Nonnull ExecutionContext.Operand operand) { + ValueType type = operand.type; + + return (ASTOperand)(switch (type) { + case NUMBER -> new ASTOperandNumber(token, tokenPosition, operand.number); + case STRING -> new ASTOperandString(token, tokenPosition, operand.string); + case BOOLEAN -> new ASTOperandBoolean(token, tokenPosition, operand.bool); + case NUMBER_ARRAY -> new ASTOperandNumberArray(token, tokenPosition, operand.numberArray); + case STRING_ARRAY -> new ASTOperandStringArray(token, tokenPosition, operand.stringArray); + case BOOLEAN_ARRAY -> new ASTOperandBooleanArray(token, tokenPosition, operand.boolArray); + case EMPTY_ARRAY -> new ASTOperandEmptyArray(token, tokenPosition); + case null, default -> throw new IllegalStateException("Illegal operand type encountered" + type); + }); } } diff --git a/src/com/hypixel/hytale/server/spawning/SpawningContext.java b/src/com/hypixel/hytale/server/spawning/SpawningContext.java index cf75add4..342f401a 100644 --- a/src/com/hypixel/hytale/server/spawning/SpawningContext.java +++ b/src/com/hypixel/hytale/server/spawning/SpawningContext.java @@ -5,8 +5,7 @@ import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; @@ -26,6 +25,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SpawningContext { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @@ -526,7 +526,7 @@ public class SpawningContext { if (this.spawnModel == null) { return false; } else { - this.position.assign(this.xSpawn, this.ySpawn, this.zSpawn); + this.position.set(this.xSpawn, this.ySpawn, this.zSpawn); return CollisionModule.get() .validatePosition( this.world, @@ -576,8 +576,8 @@ public class SpawningContext { } @Nonnull - public Vector3f newRotation() { - return new Vector3f((float)this.pitch, (float)this.yaw, (float)this.roll); + public Rotation3f newRotation() { + return new Rotation3f((float)this.pitch, (float)this.yaw, (float)this.roll); } @Nonnull diff --git a/src/com/hypixel/hytale/server/spawning/SpawningPlugin.java b/src/com/hypixel/hytale/server/spawning/SpawningPlugin.java index d9351f40..2ab5478d 100644 --- a/src/com/hypixel/hytale/server/spawning/SpawningPlugin.java +++ b/src/com/hypixel/hytale/server/spawning/SpawningPlugin.java @@ -14,17 +14,12 @@ import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.common.util.FormatUtil; import com.hypixel.hytale.component.AddReason; -import com.hypixel.hytale.component.Archetype; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.component.data.unknown.UnknownComponents; -import com.hypixel.hytale.component.dependency.Dependency; -import com.hypixel.hytale.component.dependency.RootDependency; -import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.KDTree; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.logger.HytaleLogger; @@ -37,14 +32,8 @@ import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; import com.hypixel.hytale.server.core.asset.type.responsecurve.config.ResponseCurve; -import com.hypixel.hytale.server.core.entity.Entity; -import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; -import com.hypixel.hytale.server.core.modules.entity.AllLegacyEntityTypesQuery; import com.hypixel.hytale.server.core.modules.entity.EntityModule; -import com.hypixel.hytale.server.core.modules.entity.component.HiddenFromAdventurePlayers; -import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; @@ -52,7 +41,6 @@ import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.Config; @@ -74,8 +62,8 @@ import com.hypixel.hytale.server.spawning.beacons.InitialBeaconDelay; import com.hypixel.hytale.server.spawning.beacons.LegacySpawnBeaconEntity; import com.hypixel.hytale.server.spawning.beacons.SpawnBeacon; import com.hypixel.hytale.server.spawning.beacons.SpawnBeaconSystems; +import com.hypixel.hytale.server.spawning.blockstates.SpawnMarkerBlock; import com.hypixel.hytale.server.spawning.blockstates.SpawnMarkerBlockReference; -import com.hypixel.hytale.server.spawning.blockstates.SpawnMarkerBlockState; import com.hypixel.hytale.server.spawning.blockstates.SpawnMarkerBlockStateSystems; import com.hypixel.hytale.server.spawning.commands.SpawnCommand; import com.hypixel.hytale.server.spawning.corecomponents.builders.BuilderActionTriggerSpawnBeacon; @@ -130,7 +118,6 @@ import java.util.Map.Entry; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.bson.BsonDocument; public class SpawningPlugin extends JavaPlugin { private static final String DEFAULT_SPAWN_MARKER_MODEL = "NPC_Spawn_Marker"; @@ -138,6 +125,7 @@ public class SpawningPlugin extends JavaPlugin { private static final float OVERPOPULATION_RATIO = 0.25F; private static final int OVERPOPULATION_GROUP_BUFFER = 4; private static SpawningPlugin instance; + private ComponentType spawnMarkerBlockComponentType; private Model spawnMarkerModel; private double localSpawnControllerJoinDelay; private int tickColumnBudget; @@ -235,10 +223,7 @@ public class SpawningPlugin extends JavaPlugin { .build() ); NPCPlugin.get().registerCoreComponentType("TriggerSpawnBeacon", BuilderActionTriggerSpawnBeacon::new); - BlockStateModule.get() - .registerBlockState( - SpawnMarkerBlockState.class, "SpawnMarkerBlock", SpawnMarkerBlockState.CODEC, SpawnMarkerBlockState.Data.class, SpawnMarkerBlockState.Data.CODEC - ); + this.spawnMarkerBlockComponentType = this.getChunkStoreRegistry().registerComponent(SpawnMarkerBlock.class, "SpawnMarkerBlock", SpawnMarkerBlock.CODEC); this.spawnMarkerComponentType = this.getEntityStoreRegistry().registerComponent(SpawnMarkerEntity.class, "SpawnMarkerComponent", SpawnMarkerEntity.CODEC); this.localSpawnControllerComponentType = this.getEntityStoreRegistry().registerComponent(LocalSpawnController.class, LocalSpawnController::new); this.worldSpawnDataResourceType = this.getEntityStoreRegistry().registerResource(WorldSpawnData.class, WorldSpawnData::new); @@ -336,7 +321,6 @@ public class SpawningPlugin extends JavaPlugin { ); this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.SpawnJobTick(legacySpawnBeaconComponentType, this.initialBeaconDelayComponentType)); this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.LoadTimeDelay(this.initialBeaconDelayComponentType)); - this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.LegacyEntityMigration()); this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.EnsureNetworkSendable()); this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.CacheMarker(this.spawnMarkerComponentType)); this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.EntityAdded(this.spawnMarkerComponentType)); @@ -379,14 +363,10 @@ public class SpawningPlugin extends JavaPlugin { .registerSystem( new ChunkSpawningSystems.TickingState(this.worldSpawnDataResourceType, this.chunkSpawnDataComponentType, this.chunkSpawnedNPCDataComponentType) ); - ComponentType spawnMarkerBlockStateComponentType = BlockStateModule.get() - .getComponentType(SpawnMarkerBlockState.class); - this.getChunkStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.AddOrRemove(spawnMarkerBlockStateComponentType)); - this.getChunkStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.TickHeartbeat(spawnMarkerBlockStateComponentType)); + this.getChunkStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.AddOrRemove(this.spawnMarkerBlockComponentType)); + this.getChunkStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.TickHeartbeat(this.spawnMarkerBlockComponentType)); this.getEntityStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.SpawnMarkerAddedFromExternal(this.spawnMarkerBlockReferenceComponentType)); this.getEntityStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.SpawnMarkerTickHeartbeat(this.spawnMarkerBlockReferenceComponentType)); - this.getEntityStoreRegistry().registerSystem(new EntityModule.HiddenFromPlayerMigrationSystem(this.spawnSuppressorComponentType), true); - this.getEntityStoreRegistry().registerSystem(new SpawningPlugin.LegacySpawnSuppressorEntityMigration()); Interaction.CODEC.register("TriggerSpawnMarkers", TriggerSpawnMarkersInteraction.class, TriggerSpawnMarkersInteraction.CODEC); } @@ -498,6 +478,10 @@ public class SpawningPlugin extends JavaPlugin { return this.spawnMarkerBlockReferenceComponentType; } + public ComponentType getSpawnMarkerBlockComponentType() { + return this.spawnMarkerBlockComponentType; + } + public boolean shouldNPCDespawn( @Nonnull Store store, @Nonnull NPCEntity npcComponent, @Nonnull WorldTimeResource timeManager, int configuration, boolean beaconSpawn ) { @@ -902,58 +886,6 @@ public class SpawningPlugin extends JavaPlugin { ); } - @Deprecated(forRemoval = true) - public static class LegacySpawnSuppressorEntityMigration extends EntityModule.MigrationSystem { - private final ComponentType persistentModelComponentType = PersistentModel.getComponentType(); - private final ComponentType nameplateComponentType = Nameplate.getComponentType(); - private final ComponentType uuidComponentType = UUIDComponent.getComponentType(); - private final ComponentType> unknownComponentsComponentType = EntityStore.REGISTRY.getUnknownComponentType(); - private final Query query = Query.and(this.unknownComponentsComponentType, Query.not(AllLegacyEntityTypesQuery.INSTANCE)); - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - UnknownComponents unknownComponent = holder.getComponent(this.unknownComponentsComponentType); - - assert unknownComponent != null; - - Map unknownComponents = unknownComponent.getUnknownComponents(); - BsonDocument spawnSuppressor = unknownComponents.remove("SpawnSuppressor"); - if (spawnSuppressor != null) { - Archetype archetype = holder.getArchetype(); - if (!archetype.contains(this.persistentModelComponentType)) { - Model.ModelReference modelReference = Entity.MODEL.get(spawnSuppressor).get(); - holder.addComponent(this.persistentModelComponentType, new PersistentModel(modelReference)); - } - - if (!archetype.contains(this.nameplateComponentType)) { - holder.addComponent(this.nameplateComponentType, new Nameplate(Entity.DISPLAY_NAME.get(spawnSuppressor).get())); - } - - if (!archetype.contains(this.uuidComponentType)) { - holder.addComponent(this.uuidComponentType, new UUIDComponent(Entity.UUID.get(spawnSuppressor).get())); - } - - holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType()); - } - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return this.query; - } - - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } - } - public static class NPCSpawningConfig { @Nonnull public static final BuilderCodec CODEC = BuilderCodec.builder( diff --git a/src/com/hypixel/hytale/server/spawning/beacons/LegacySpawnBeaconEntity.java b/src/com/hypixel/hytale/server/spawning/beacons/LegacySpawnBeaconEntity.java index f14178ed..5d87e7ba 100644 --- a/src/com/hypixel/hytale/server/spawning/beacons/LegacySpawnBeaconEntity.java +++ b/src/com/hypixel/hytale/server/spawning/beacons/LegacySpawnBeaconEntity.java @@ -11,8 +11,7 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3fc; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; @@ -48,6 +47,8 @@ import java.time.Instant; import java.util.UUID; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class LegacySpawnBeaconEntity extends Entity { @Nonnull @@ -318,8 +319,8 @@ public class LegacySpawnBeaconEntity extends Entity { @Nonnull public static Pair, LegacySpawnBeaconEntity> create( @Nonnull BeaconSpawnWrapper spawnWrapper, - @Nonnull Vector3d position, - @Nonnull Vector3f rotation, + @Nonnull Vector3dc position, + @Nonnull Rotation3fc rotation, @Nonnull ComponentAccessor componentAccessor ) { Holder holder = createHolder(spawnWrapper, position, rotation); @@ -328,7 +329,7 @@ public class LegacySpawnBeaconEntity extends Entity { return Pair.of(ref, legacySpawnBeaconComponent); } - public static Holder createHolder(@Nonnull BeaconSpawnWrapper spawnWrapper, @Nonnull Vector3d position, @Nonnull Vector3f rotation) { + public static Holder createHolder(@Nonnull BeaconSpawnWrapper spawnWrapper, @Nonnull Vector3dc position, @Nonnull Rotation3fc rotation) { LegacySpawnBeaconEntity entity = new LegacySpawnBeaconEntity(); entity.setSpawnConfiguration(spawnWrapper); BeaconNPCSpawn spawn = spawnWrapper.getSpawn(); diff --git a/src/com/hypixel/hytale/server/spawning/beacons/SpawnBeacon.java b/src/com/hypixel/hytale/server/spawning/beacons/SpawnBeacon.java index fc7bdf88..f02aebfc 100644 --- a/src/com/hypixel/hytale/server/spawning/beacons/SpawnBeacon.java +++ b/src/com/hypixel/hytale/server/spawning/beacons/SpawnBeacon.java @@ -8,8 +8,7 @@ import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.UUIDComponent; @@ -37,6 +36,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SpawnBeacon extends Entity { public static final BuilderCodec CODEC = BuilderCodec.builder(SpawnBeacon.class, SpawnBeacon::new, Entity.CODEC) @@ -123,7 +123,7 @@ public class SpawnBeacon extends Entity { this.spawningContext.releaseFull(); } else { Vector3d position = this.spawningContext.newPosition(); - Vector3f rotation = this.spawningContext.newRotation(); + Rotation3f rotation = this.spawningContext.newRotation(); FlockAsset flockDefinition = roleSpawnParameters.getFlockDefinition(); int flockSize = flockDefinition != null ? flockDefinition.pickFlockSize() : 1; diff --git a/src/com/hypixel/hytale/server/spawning/beacons/SpawnBeaconSystems.java b/src/com/hypixel/hytale/server/spawning/beacons/SpawnBeaconSystems.java index ad259952..5d0ca8bc 100644 --- a/src/com/hypixel/hytale/server/spawning/beacons/SpawnBeaconSystems.java +++ b/src/com/hypixel/hytale/server/spawning/beacons/SpawnBeaconSystems.java @@ -21,8 +21,7 @@ import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.random.RandomExtra; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.asset.type.responsecurve.ScaledXYResponseCurve; import com.hypixel.hytale.server.core.entity.Frozen; import com.hypixel.hytale.server.core.entity.UUIDComponent; @@ -51,9 +50,8 @@ import com.hypixel.hytale.server.spawning.util.FloodFillEntryPoolProviderSimple; import com.hypixel.hytale.server.spawning.util.FloodFillPositionSelector; import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper; import it.unimi.dsi.fastutil.Pair; -import it.unimi.dsi.fastutil.objects.Object2DoubleMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.Reference2DoubleMap; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -62,6 +60,7 @@ import java.util.UUID; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SpawnBeaconSystems { public static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @@ -222,7 +221,7 @@ public class SpawnBeaconSystems { BeaconSpawnController spawnController = legacySpawnBeaconComponent.getSpawnController(); List> spawnedEntities = spawnController.getSpawnedEntities(); if (!spawnedEntities.isEmpty()) { - Object2DoubleMap> entityTimeoutCounter = spawnController.getEntityTimeoutCounter(); + Reference2DoubleMap> entityTimeoutCounter = spawnController.getEntityTimeoutCounter(); boolean despawnNPCsIfIdle = spawnController.isDespawnNPCsIfIdle(); double beaconRadiusSquared = spawnController.getBeaconRadiusSquared(); double despawnNPCAfterTimeout = spawnController.getDespawnNPCAfterTimeout(); @@ -243,7 +242,7 @@ public class SpawnBeaconSystems { assert spawnedEntityTransformComponent != null; Vector3d npcPosition = spawnedEntityTransformComponent.getPosition(); - double beaconDistance = npcPosition.distanceSquaredTo(position); + double beaconDistance = npcPosition.distanceSquared(position); if ((despawnNPCsIfIdle && !hasTarget || beaconDistance > beaconRadiusSquared) && !role.getStateSupport().isInBusyState()) { double timeout = entityTimeoutCounter.mergeDouble(spawnedEntityReference, (double)dt, Double::sum); if (timeout >= despawnNPCAfterTimeout) { @@ -263,13 +262,13 @@ public class SpawnBeaconSystems { if (!isReadyToRespawn(legacySpawnBeaconComponent, timeManager)) { validatedEntityList.clear(); } else { - int y = MathUtil.floor(position.getY()); + int y = MathUtil.floor(position.y()); BeaconSpawnWrapper spawnWrapper = legacySpawnBeaconComponent.getSpawnWrapper(); int[] yRange = spawnWrapper.getSpawn().getYRange(); double minY = y + yRange[0]; double maxY = y + yRange[1]; SpatialResource, EntityStore> spatialResource = store.getResource(this.playerSpatialResource); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialResource.getSpatialStructure().collect(position, spawnWrapper.getBeaconRadius(), results); List playersInRegion = spawnController.getPlayersInRegion(); @@ -284,7 +283,7 @@ public class SpawnBeaconSystems { assert resultTransformComponent != null; - double yPos = resultTransformComponent.getPosition().getY(); + double yPos = resultTransformComponent.getPosition().y(); if (!(yPos < minY) && !(yPos > maxY) && !commandBuffer.getArchetype(result).contains(this.deathComponentComponentType)) { playersInRegion.add(resultPlayerComponent); } @@ -306,7 +305,7 @@ public class SpawnBeaconSystems { assert playerTransformComponent != null; Vector3d playerPos = playerTransformComponent.getPosition(); - if (playerPos.distanceSquaredTo(position) <= spawnController.getSpawnRadiusSquared()) { + if (playerPos.distanceSquared(position) <= spawnController.getSpawnRadiusSquared()) { playersInSpawnRange = true; break; } @@ -729,7 +728,7 @@ public class SpawnBeaconSystems { ) { SpawningContext spawningContext = spawnJob.getSpawningContext(); Vector3d position = spawningContext.newPosition(); - Vector3f rotation = spawningContext.newRotation(); + Rotation3f rotation = spawningContext.newRotation(); int roleIndex = spawnJob.getRoleIndex(); commandBuffer.run( _store -> { diff --git a/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockState.java b/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlock.java similarity index 57% rename from src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockState.java rename to src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlock.java index bceeec64..bc03fe50 100644 --- a/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockState.java +++ b/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlock.java @@ -4,24 +4,48 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.validation.Validators; -import com.hypixel.hytale.math.vector.Vector3i; -import com.hypixel.hytale.server.core.asset.type.blocktype.config.StateData; +import com.hypixel.hytale.component.Component; +import com.hypixel.hytale.component.ComponentType; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.entity.reference.PersistentRef; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.spawning.SpawningPlugin; import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker; +import javax.annotation.Nullable; +import org.joml.Vector3i; -public class SpawnMarkerBlockState extends BlockState { - public static final Codec CODEC = BuilderCodec.builder(SpawnMarkerBlockState.class, SpawnMarkerBlockState::new, BlockState.BASE_CODEC) +public class SpawnMarkerBlock implements Component { + public static final BuilderCodec CODEC = BuilderCodec.builder(SpawnMarkerBlock.class, SpawnMarkerBlock::new) .append(new KeyedCodec<>("MarkerReference", PersistentRef.CODEC), (spawn, o) -> spawn.spawnMarkerReference = o, spawn -> spawn.spawnMarkerReference) .add() + .append(new KeyedCodec<>("Config", SpawnMarkerBlock.Data.CODEC), (spawn, o) -> spawn.config = o, spawn -> spawn.config) + .add() .build(); private PersistentRef spawnMarkerReference; private float markerLostTimeout = 30.0F; + @Nullable + private SpawnMarkerBlock.Data config; + + public static ComponentType getComponentType() { + return SpawningPlugin.get().getSpawnMarkerBlockComponentType(); + } + + public SpawnMarkerBlock() { + } + + public SpawnMarkerBlock(PersistentRef spawnMarkerReference) { + this.spawnMarkerReference = spawnMarkerReference; + } public PersistentRef getSpawnMarkerReference() { return this.spawnMarkerReference; } + @Nullable + public SpawnMarkerBlock.Data getConfig() { + return this.config; + } + public void setSpawnMarkerReference(PersistentRef spawnMarkerReference) { this.spawnMarkerReference = spawnMarkerReference; } @@ -34,10 +58,14 @@ public class SpawnMarkerBlockState extends BlockState { return (this.markerLostTimeout -= dt) <= 0.0F; } - public static class Data extends StateData { - public static final BuilderCodec CODEC = BuilderCodec.builder( - SpawnMarkerBlockState.Data.class, SpawnMarkerBlockState.Data::new, StateData.DEFAULT_CODEC - ) + @Nullable + @Override + public Component clone() { + return new SpawnMarkerBlock(this.spawnMarkerReference != null ? new PersistentRef(this.spawnMarkerReference.getUuid()) : null); + } + + public static class Data { + public static final BuilderCodec CODEC = BuilderCodec.builder(SpawnMarkerBlock.Data.class, SpawnMarkerBlock.Data::new) .appendInherited( new KeyedCodec<>("SpawnMarker", Codec.STRING), (spawn, s) -> spawn.spawnMarker = s, @@ -49,7 +77,7 @@ public class SpawnMarkerBlockState extends BlockState { .addValidatorLate(() -> SpawnMarker.VALIDATOR_CACHE.getValidator().late()) .add() .appendInherited( - new KeyedCodec<>("MarkerOffset", Vector3i.CODEC), + new KeyedCodec<>("MarkerOffset", Vector3iUtil.CODEC), (spawn, o) -> spawn.markerOffset = o, spawn -> spawn.markerOffset, (spawn, parent) -> spawn.markerOffset = parent.markerOffset diff --git a/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockReference.java b/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockReference.java index 4eb243ce..3a593bec 100644 --- a/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockReference.java +++ b/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockReference.java @@ -4,14 +4,15 @@ import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Vector3iUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.SpawningPlugin; import javax.annotation.Nonnull; +import org.joml.Vector3i; public class SpawnMarkerBlockReference implements Component { public static final BuilderCodec CODEC = BuilderCodec.builder(SpawnMarkerBlockReference.class, SpawnMarkerBlockReference::new) - .append(new KeyedCodec<>("BlockPosition", Vector3i.CODEC), (reference, o) -> reference.blockPosition = o, reference -> reference.blockPosition) + .append(new KeyedCodec<>("BlockPosition", Vector3iUtil.CODEC), (reference, o) -> reference.blockPosition = o, reference -> reference.blockPosition) .add() .build(); private Vector3i blockPosition; diff --git a/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockStateSystems.java b/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockStateSystems.java index d78eb6f3..08c2a5fe 100644 --- a/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockStateSystems.java +++ b/src/com/hypixel/hytale/server/spawning/blockstates/SpawnMarkerBlockStateSystems.java @@ -13,13 +13,14 @@ import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3iUtil; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; import com.hypixel.hytale.server.core.entity.reference.PersistentRef; +import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab; import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen; import com.hypixel.hytale.server.core.modules.entity.component.HiddenFromAdventurePlayers; @@ -27,59 +28,83 @@ import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; -import com.hypixel.hytale.server.core.universe.world.meta.BlockState; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker; import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; +import org.joml.Vector3i; public class SpawnMarkerBlockStateSystems { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); private static void createMarker( - @Nonnull Ref ref, @Nonnull SpawnMarkerBlockState state, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer + @Nonnull Ref ref, + @Nonnull SpawnMarkerBlock state, + @Nonnull BlockModule.BlockStateInfo info, + @Nonnull Store store, + @Nonnull CommandBuffer commandBuffer ) { - if (state.getBlockType().getState() instanceof SpawnMarkerBlockState.Data stateData) { - SpawnMarker marker = SpawnMarker.getAssetMap().getAsset(stateData.getSpawnMarker()); - if (marker == null) { - LOGGER.at(Level.SEVERE).log(String.format("Marker %s does not exist!", stateData.getSpawnMarker())); - commandBuffer.removeEntity(ref, RemoveReason.REMOVE); - } else { - Vector3i pos = state.getBlockPosition(); - Vector3i offset = stateData.getMarkerOffset(); - if (offset != null) { - pos.add(offset); - } + if (info.getChunkRef().isValid()) { + BlockChunk blockChunk = commandBuffer.getComponent(info.getChunkRef(), BlockChunk.getComponentType()); + if (blockChunk != null) { + int bx = ChunkUtil.xFromBlockInColumn(info.getIndex()); + int by = ChunkUtil.yFromBlockInColumn(info.getIndex()); + int bz = ChunkUtil.zFromBlockInColumn(info.getIndex()); + BlockType blockType = BlockType.getAssetMap().getAsset(blockChunk.getBlock(bx, by, bz)); + if (blockType != null && blockType.getBlockEntity() != null) { + SpawnMarkerBlock configType = blockType.getBlockEntity().getComponent(SpawnMarkerBlock.getComponentType()); + if (configType != null) { + SpawnMarkerBlock.Data data = configType.getConfig(); + if (data != null) { + SpawnMarker marker = SpawnMarker.getAssetMap().getAsset(data.getSpawnMarker()); + if (marker == null) { + LOGGER.at(Level.SEVERE).log(String.format("Marker %s does not exist!", data.getSpawnMarker())); + commandBuffer.removeEntity(ref, RemoveReason.REMOVE); + } else { + Vector3i pos = new Vector3i( + ChunkUtil.worldCoordFromLocalCoord(blockChunk.getX(), bx), by, ChunkUtil.worldCoordFromLocalCoord(blockChunk.getZ(), bz) + ); + Vector3i blockPos = new Vector3i(pos); + Vector3i offset = data.getMarkerOffset(); + if (offset != null) { + pos.add(offset); + } - SpawnMarkerEntity spawnMarker = new SpawnMarkerEntity(); - spawnMarker.setSpawnMarker(marker); - Holder holder = EntityStore.REGISTRY.newHolder(); - holder.addComponent(SpawnMarkerEntity.getComponentType(), spawnMarker); - holder.addComponent(SpawnMarkerBlockReference.getComponentType(), new SpawnMarkerBlockReference(state.getBlockPosition())); - Vector3d markerPos = pos.toVector3d(); - markerPos.add(0.5, 0.0, 0.5); - holder.addComponent(Nameplate.getComponentType(), new Nameplate(marker.getId())); - holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(markerPos, Vector3f.ZERO)); - UUIDComponent uuidComponent = holder.ensureAndGetComponent(UUIDComponent.getComponentType()); - Model model = SpawnMarkerEntity.getModel(marker); - holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model)); - holder.addComponent(PersistentModel.getComponentType(), new PersistentModel(model.toReference())); - holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType()); - Ref markerRef = store.addEntity(holder, AddReason.SPAWN); - PersistentRef persistentRef = new PersistentRef(); - persistentRef.setEntity(markerRef, uuidComponent.getUuid()); - state.setSpawnMarkerReference(persistentRef); + SpawnMarkerEntity spawnMarker = new SpawnMarkerEntity(); + spawnMarker.setSpawnMarker(marker); + Holder holder = EntityStore.REGISTRY.newHolder(); + holder.addComponent(SpawnMarkerEntity.getComponentType(), spawnMarker); + holder.addComponent(SpawnMarkerBlockReference.getComponentType(), new SpawnMarkerBlockReference(blockPos)); + Vector3d markerPos = Vector3iUtil.toVector3d(pos); + markerPos.add(0.5, 0.0, 0.5); + holder.addComponent(Nameplate.getComponentType(), new Nameplate(marker.getId())); + holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(markerPos, Rotation3f.IDENTITY)); + UUIDComponent uuidComponent = holder.ensureAndGetComponent(UUIDComponent.getComponentType()); + Model model = SpawnMarkerEntity.getModel(marker); + holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model)); + holder.addComponent(PersistentModel.getComponentType(), new PersistentModel(model.toReference())); + holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType()); + Ref markerRef = store.addEntity(holder, AddReason.SPAWN); + PersistentRef persistentRef = new PersistentRef(); + persistentRef.setEntity(markerRef, uuidComponent.getUuid()); + state.setSpawnMarkerReference(persistentRef); + } + } + } + } } } } public static class AddOrRemove extends RefSystem { - private final ComponentType componentType; + private final ComponentType componentType; - public AddOrRemove(ComponentType componentType) { + public AddOrRemove(ComponentType componentType) { this.componentType = componentType; } @@ -99,7 +124,7 @@ public class SpawnMarkerBlockStateSystems { @Nonnull Ref ref, @Nonnull RemoveReason reason, @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { if (reason == RemoveReason.REMOVE) { - SpawnMarkerBlockState state = store.getComponent(ref, this.componentType); + SpawnMarkerBlock state = store.getComponent(ref, this.componentType); PersistentRef markerReference = state.getSpawnMarkerReference(); if (markerReference == null) { return; @@ -174,8 +199,8 @@ public class SpawnMarkerBlockStateSystems { Vector3i pos = marker.getBlockPosition(); WorldChunk chunk = store.getExternalData().getWorld().getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(pos.x, pos.z)); if (chunk != null) { - BlockState state = chunk.getState(pos.x, pos.y, pos.z); - if (!(state instanceof SpawnMarkerBlockState)) { + Ref blockEntityRef = chunk.getBlockComponentEntity(pos.x, pos.y, pos.z); + if (blockEntityRef != null && blockEntityRef.getStore().getComponent(blockEntityRef, SpawnMarkerBlock.getComponentType()) == null) { Ref ref = archetypeChunk.getReferenceTo(index); commandBuffer.removeEntity(ref, RemoveReason.REMOVE); SpawnMarkerBlockStateSystems.LOGGER.at(Level.SEVERE).log("Removing block spawn marker due to blockstate mismatch: %s", ref); @@ -191,10 +216,13 @@ public class SpawnMarkerBlockStateSystems { } public static class TickHeartbeat extends EntityTickingSystem { - private final ComponentType componentType; + private final ComponentType componentType; + private final ComponentType blockStateInfoComponentType = BlockModule.BlockStateInfo.getComponentType(); + private final Query query; - public TickHeartbeat(ComponentType componentType) { + public TickHeartbeat(ComponentType componentType) { this.componentType = componentType; + this.query = Query.and(componentType, this.blockStateInfoComponentType); } @Override @@ -204,7 +232,7 @@ public class SpawnMarkerBlockStateSystems { @Override public Query getQuery() { - return this.componentType; + return this.query; } @Override @@ -215,10 +243,17 @@ public class SpawnMarkerBlockStateSystems { @Nonnull Store store, @Nonnull CommandBuffer commandBuffer ) { - SpawnMarkerBlockState state = archetypeChunk.getComponent(index, this.componentType); + SpawnMarkerBlock state = archetypeChunk.getComponent(index, this.componentType); + + assert state != null; + + BlockModule.BlockStateInfo info = archetypeChunk.getComponent(index, this.blockStateInfoComponentType); + + assert info != null; + if (state.getSpawnMarkerReference() == null) { Ref ref = archetypeChunk.getReferenceTo(index); - SpawnMarkerBlockStateSystems.createMarker(ref, state, store.getExternalData().getWorld().getEntityStore().getStore(), commandBuffer); + SpawnMarkerBlockStateSystems.createMarker(ref, state, info, store.getExternalData().getWorld().getEntityStore().getStore(), commandBuffer); } if (state.getSpawnMarkerReference().getEntity(store.getExternalData().getWorld().getEntityStore().getStore()) != null) { @@ -226,7 +261,7 @@ public class SpawnMarkerBlockStateSystems { } else if (state.tickMarkerLostTimeout(dt)) { Ref ref = archetypeChunk.getReferenceTo(index); SpawnMarkerBlockStateSystems.LOGGER.at(Level.SEVERE).log("Creating new spawn marker due to desync with entity: %s", ref); - SpawnMarkerBlockStateSystems.createMarker(ref, state, store.getExternalData().getWorld().getEntityStore().getStore(), commandBuffer); + SpawnMarkerBlockStateSystems.createMarker(ref, state, info, store.getExternalData().getWorld().getEntityStore().getStore(), commandBuffer); } } } diff --git a/src/com/hypixel/hytale/server/spawning/commands/SpawnBeaconsCommand.java b/src/com/hypixel/hytale/server/spawning/commands/SpawnBeaconsCommand.java index 6aa628d2..4f0f7a6a 100644 --- a/src/com/hypixel/hytale/server/spawning/commands/SpawnBeaconsCommand.java +++ b/src/com/hypixel/hytale/server/spawning/commands/SpawnBeaconsCommand.java @@ -4,8 +4,7 @@ import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; @@ -31,8 +30,9 @@ import com.hypixel.hytale.server.spawning.beacons.LegacySpawnBeaconEntity; import com.hypixel.hytale.server.spawning.beacons.SpawnBeacon; import com.hypixel.hytale.server.spawning.util.FloodFillPositionSelector; import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper; -import it.unimi.dsi.fastutil.objects.ObjectList; +import java.util.List; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpawnBeaconsCommand extends AbstractCommandCollection { private static final AssetArgumentType BEACON_SPAWN_ASSET_TYPE = new AssetArgumentType( @@ -65,7 +65,7 @@ public class SpawnBeaconsCommand extends AbstractCommandCollection { assert transformComponent != null; - Vector3f rotation = transformComponent.getRotation(); + Rotation3f rotation = transformComponent.getRotation(); Vector3d position = transformComponent.getPosition(); BeaconNPCSpawn beacon = this.beaconArg.get(context); BeaconSpawnWrapper wrapper = SpawningPlugin.get().getBeaconSpawnWrapper(BeaconNPCSpawn.getAssetMap().getIndex(beacon.getId())); @@ -114,9 +114,7 @@ public class SpawnBeaconsCommand extends AbstractCommandCollection { } @Override - protected void execute( - @Nonnull CommandContext context, @Nonnull ObjectList> entities, @Nonnull World world, @Nonnull Store store - ) { + protected void execute(@Nonnull CommandContext context, @Nonnull List> entities, @Nonnull World world, @Nonnull Store store) { if (entities.isEmpty()) { context.sendMessage(MESSAGE_COMMANDS_SPAWNING_BEACONS_TRIGGER_NO_BEACONS); } else { diff --git a/src/com/hypixel/hytale/server/spawning/commands/SpawnStatsCommand.java b/src/com/hypixel/hytale/server/spawning/commands/SpawnStatsCommand.java index eda4cc0d..3de1367f 100644 --- a/src/com/hypixel/hytale/server/spawning/commands/SpawnStatsCommand.java +++ b/src/com/hypixel/hytale/server/spawning/commands/SpawnStatsCommand.java @@ -52,7 +52,7 @@ public class SpawnStatsCommand extends AbstractWorldCommand { String name = Environment.getAssetMap().getAsset(environmentIndex).getId(); Store chunkStore = world.getChunkStore().getStore(); double[] chunkExpected = new double[]{0.0}; - worldEnvironmentSpawnData.getChunkRefSet() + worldEnvironmentSpawnData.getChunkRefList() .forEach( ref -> { ChunkEnvironmentSpawnData chunkEnvironmentSpawnData = chunkStore.getComponent( diff --git a/src/com/hypixel/hytale/server/spawning/commands/SpawnSuppressionCommand.java b/src/com/hypixel/hytale/server/spawning/commands/SpawnSuppressionCommand.java index 9dc1026b..5e819a60 100644 --- a/src/com/hypixel/hytale/server/spawning/commands/SpawnSuppressionCommand.java +++ b/src/com/hypixel/hytale/server/spawning/commands/SpawnSuppressionCommand.java @@ -4,7 +4,7 @@ import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.command.system.CommandContext; @@ -89,7 +89,7 @@ public class SpawnSuppressionCommand extends AbstractCommandCollection { assert transformComponent != null; SpawnSuppression spawnSuppression = this.suppressionArg.get(context); - Vector3f rotation = transformComponent.getRotation(); + Rotation3f rotation = transformComponent.getRotation(); Holder holder = EntityStore.REGISTRY.newHolder(); holder.addComponent(SpawnSuppressionComponent.getComponentType(), new SpawnSuppressionComponent(spawnSuppression.getId())); holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(transformComponent.getPosition(), rotation)); diff --git a/src/com/hypixel/hytale/server/spawning/controllers/BeaconSpawnController.java b/src/com/hypixel/hytale/server/spawning/controllers/BeaconSpawnController.java index add4859b..47d6984e 100644 --- a/src/com/hypixel/hytale/server/spawning/controllers/BeaconSpawnController.java +++ b/src/com/hypixel/hytale/server/spawning/controllers/BeaconSpawnController.java @@ -15,11 +15,12 @@ import com.hypixel.hytale.server.spawning.jobs.NPCBeaconSpawnJob; import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.Object2DoubleMap; -import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.Reference2DoubleMap; +import it.unimi.dsi.fastutil.objects.Reference2DoubleOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.time.Duration; import java.util.Comparator; import java.util.List; @@ -36,11 +37,11 @@ public class BeaconSpawnController extends SpawnController { public static final double ROUNDING_BREAK_POINT = 0.25; @Nonnull private final Ref ownerRef; - private final List> spawnedEntities = new ObjectArrayList<>(); + private final List> spawnedEntities = new ReferenceArrayList<>(); private final List playersInRegion = new ObjectArrayList<>(); private int nextPlayerIndex = 0; private final Object2IntMap entitiesPerPlayer = new Object2IntOpenHashMap<>(); - private final Object2DoubleMap> entityTimeoutCounter = new Object2DoubleOpenHashMap<>(); + private final Reference2DoubleMap> entityTimeoutCounter = new Reference2DoubleOpenHashMap<>(); private final IntSet unspawnableRoles = new IntOpenHashSet(); private final Comparator threatComparator = Comparator.comparingInt(playerRef -> this.entitiesPerPlayer.getOrDefault(playerRef.getUuid(), 0)); private int baseMaxTotalSpawns; @@ -186,7 +187,7 @@ public class BeaconSpawnController extends SpawnController { this.nextPlayerIndex = nextPlayerIndex; } - public Object2DoubleMap> getEntityTimeoutCounter() { + public Reference2DoubleMap> getEntityTimeoutCounter() { return this.entityTimeoutCounter; } diff --git a/src/com/hypixel/hytale/server/spawning/corecomponents/ActionTriggerSpawnBeacon.java b/src/com/hypixel/hytale/server/spawning/corecomponents/ActionTriggerSpawnBeacon.java index 9aa595aa..fa98a78c 100644 --- a/src/com/hypixel/hytale/server/spawning/corecomponents/ActionTriggerSpawnBeacon.java +++ b/src/com/hypixel/hytale/server/spawning/corecomponents/ActionTriggerSpawnBeacon.java @@ -12,6 +12,7 @@ import com.hypixel.hytale.server.spawning.corecomponents.builders.BuilderActionT import com.hypixel.hytale.server.spawning.util.FloodFillPositionSelector; import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class ActionTriggerSpawnBeacon extends ActionBase { protected final int beaconId; @@ -26,7 +27,7 @@ public class ActionTriggerSpawnBeacon extends ActionBase { } @Override - public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean canExecute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { return super.canExecute(ref, role, sensorInfo, dt, store) && (this.targetSlot == Integer.MIN_VALUE || role.getMarkedEntitySupport().hasMarkedEntityInSlot(this.targetSlot)); } @@ -37,7 +38,7 @@ public class ActionTriggerSpawnBeacon extends ActionBase { } @Override - public boolean execute(@Nonnull Ref ref, @Nonnull Role role, InfoProvider sensorInfo, double dt, @Nonnull Store store) { + public boolean execute(@Nonnull Ref ref, @Nonnull Role role, @Nullable InfoProvider sensorInfo, double dt, @Nonnull Store store) { super.execute(ref, role, sensorInfo, dt, store); for (Ref spawnBeaconRef : role.getPositionCache().getSpawnBeaconList()) { diff --git a/src/com/hypixel/hytale/server/spawning/interactions/TriggerSpawnMarkersInteraction.java b/src/com/hypixel/hytale/server/spawning/interactions/TriggerSpawnMarkersInteraction.java index b03b6ceb..b4bad67d 100644 --- a/src/com/hypixel/hytale/server/spawning/interactions/TriggerSpawnMarkersInteraction.java +++ b/src/com/hypixel/hytale/server/spawning/interactions/TriggerSpawnMarkersInteraction.java @@ -8,7 +8,6 @@ import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.math.random.RandomExtra; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -18,8 +17,9 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.SpawningPlugin; import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker; import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class TriggerSpawnMarkersInteraction extends SimpleInstantInteraction { public static final BuilderCodec CODEC = BuilderCodec.builder( @@ -50,7 +50,7 @@ public class TriggerSpawnMarkersInteraction extends SimpleInstantInteraction { Ref self = context.getEntity(); Vector3d position = commandBuffer.getComponent(self, TransformComponent.getComponentType()).getPosition(); SpatialResource, EntityStore> spatialResource = commandBuffer.getResource(SpawningPlugin.get().getSpawnMarkerSpatialResource()); - ObjectArrayList> spawners = new ObjectArrayList<>(); + ReferenceArrayList> spawners = new ReferenceArrayList<>(); spatialResource.getSpatialStructure().collect(position, (int)this.range + 1, spawners); if (this.count == 0) { for (int i = 0; i < spawners.size(); i++) { @@ -64,7 +64,7 @@ public class TriggerSpawnMarkersInteraction extends SimpleInstantInteraction { } } } else { - ObjectArrayList> triggerList = new ObjectArrayList<>(); + ReferenceArrayList> triggerList = new ReferenceArrayList<>(); RandomExtra.reservoirSample( spawners, (reference, _this, _commandBuffer) -> _this.filterMarker(reference, position, _commandBuffer), @@ -97,7 +97,7 @@ public class TriggerSpawnMarkersInteraction extends SimpleInstantInteraction { SpawnMarkerEntity targetMarkerEntityComponent = commandBuffer.getComponent(targetRef, SpawnMarkerEntity.getComponentType()); return targetMarkerEntityComponent == null || !targetMarkerEntityComponent.isManualTrigger() - || !(position.distanceSquaredTo(targetPosition) <= this.rangeSquared) + || !(position.distanceSquared(targetPosition) <= this.rangeSquared) || this.markerType != null && !this.markerType.equals(targetMarkerEntityComponent.getSpawnMarkerId()) ? null : targetRef; diff --git a/src/com/hypixel/hytale/server/spawning/local/LocalSpawnControllerSystem.java b/src/com/hypixel/hytale/server/spawning/local/LocalSpawnControllerSystem.java index 0b72699a..807f2fc8 100644 --- a/src/com/hypixel/hytale/server/spawning/local/LocalSpawnControllerSystem.java +++ b/src/com/hypixel/hytale/server/spawning/local/LocalSpawnControllerSystem.java @@ -11,7 +11,6 @@ import com.hypixel.hytale.component.system.tick.TickingSystem; import com.hypixel.hytale.function.function.TriIntObjectDoubleToByteFunction; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; @@ -29,10 +28,10 @@ import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.Object2ByteMap; import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; import java.util.logging.Level; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class LocalSpawnControllerSystem extends TickingSystem { public static final double RUN_FREQUENCY_SECONDS = 5.0; @@ -83,7 +82,7 @@ public class LocalSpawnControllerSystem extends TickingSystem { if (!controllers.isEmpty()) { World world = store.getExternalData().getWorld(); List pendingSpawns = localSpawnState.getLocalPendingSpawns(); - ObjectList> existingBeacons = SpatialResource.getThreadLocalReferenceList(); + List> existingBeacons = SpatialResource.getThreadLocalReferenceList(); for (int index = 0; index < controllers.size(); index++) { Ref reference = controllers.get(index); @@ -135,16 +134,16 @@ public class LocalSpawnControllerSystem extends TickingSystem { Vector3d position = transformComponent.getPosition(); double largestDistanceSquared = largestDistance * largestDistance; int yDistance = Math.abs(lowestY) + Math.abs(highestY); - int y = MathUtil.floor(position.getY()); + int y = MathUtil.floor(position.y()); int minY = Math.max(0, y - yDistance); int maxY = Math.min(319, y + yDistance); SpatialResource, EntityStore> spatialResource = store.getResource(this.beaconSpatialComponent); spatialResource.getSpatialStructure().ordered(position, largestDistance, existingBeacons); WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType()); double sunlightFactor = worldTimeResource.getSunlightFactor(); - int xPos = MathUtil.floor(position.getX()); - int yPos = MathUtil.floor(position.getY()); - int zPos = MathUtil.floor(position.getZ()); + int xPos = MathUtil.floor(position.x()); + int yPos = MathUtil.floor(position.y()); + int zPos = MathUtil.floor(position.z()); Object2ByteOpenHashMap averageLightValues = new Object2ByteOpenHashMap<>(); averageLightValues.defaultReturnValue((byte)-1); @@ -162,7 +161,7 @@ public class LocalSpawnControllerSystem extends TickingSystem { assert existingBeaconTransformComponent != null; - double existingY = existingBeaconTransformComponent.getPosition().getY(); + double existingY = existingBeaconTransformComponent.getPosition().y(); if (!(existingY > maxY) && !(existingY < minY)) { int existingBeaconIndex = existingBeaconComponent.getSpawnWrapper().getSpawnIndex(); if (existingBeaconIndex == possibleBeacon.getSpawnIndex()) { @@ -179,7 +178,7 @@ public class LocalSpawnControllerSystem extends TickingSystem { assert pendingTransformComponent != null; Vector3d pendingPosition = pendingTransformComponent.getPosition(); - double pendingY = pendingPosition.getY(); + double pendingY = pendingPosition.y(); if (!(pendingY > maxY) && !(pendingY < minY)) { double xDiff = position.x - pendingPosition.x; double zDiff = position.z - pendingPosition.z; diff --git a/src/com/hypixel/hytale/server/spawning/local/LocalSpawnState.java b/src/com/hypixel/hytale/server/spawning/local/LocalSpawnState.java index 1851881f..41f8d2d5 100644 --- a/src/com/hypixel/hytale/server/spawning/local/LocalSpawnState.java +++ b/src/com/hypixel/hytale/server/spawning/local/LocalSpawnState.java @@ -7,11 +7,12 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.SpawningPlugin; import com.hypixel.hytale.server.spawning.beacons.LegacySpawnBeaconEntity; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import javax.annotation.Nonnull; public class LocalSpawnState implements Resource { - private final List> localControllerList = new ObjectArrayList<>(); + private final List> localControllerList = new ReferenceArrayList<>(); private final List localPendingSpawns = new ObjectArrayList<>(); private boolean forceTriggerControllers; diff --git a/src/com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity.java b/src/com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity.java index e21c60dd..95944831 100644 --- a/src/com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity.java +++ b/src/com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity.java @@ -12,8 +12,8 @@ import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.function.consumer.TriConsumer; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.asset.type.model.config.Model; import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset; import com.hypixel.hytale.server.core.entity.UUIDComponent; @@ -38,7 +38,6 @@ import com.hypixel.hytale.server.spawning.SpawningPlugin; import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.time.Duration; import java.time.Instant; import java.util.HashSet; @@ -49,6 +48,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SpawnMarkerEntity implements Component { private static final double SPAWN_LOST_TIMEOUT = 35.0; @@ -95,8 +95,8 @@ public class SpawnMarkerEntity implements Component { spawnMarkerEntity -> spawnMarkerEntity.storedFlock ) .addField( - new KeyedCodec<>("SpawnPosition", Vector3d.CODEC), - (spawnMarkerEntity, v) -> spawnMarkerEntity.spawnPosition.assign(v), + new KeyedCodec<>("SpawnPosition", Vector3dUtil.CODEC), + (spawnMarkerEntity, v) -> spawnMarkerEntity.spawnPosition.set(v), spawnMarkerEntity -> spawnMarkerEntity.storedFlock == null ? null : spawnMarkerEntity.spawnPosition ) .build(); @@ -303,7 +303,7 @@ public class SpawnMarkerEntity implements Component { this.fail(ref, uuid, roleName, position, store, SpawnMarkerEntity.FailReason.FAILED_ROLE_VALIDATION); return false; } else { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> spatialResource = store.getResource(EntityModule.get().getPlayerSpatialResourceType()); spatialResource.getSpatialStructure().collect(position, marker.getExclusionRadius(), results); boolean hasPlayersInRange = !results.isEmpty(); @@ -329,8 +329,8 @@ public class SpawnMarkerEntity implements Component { this.fail(ref, uuid, roleName, position, store, SpawnMarkerEntity.FailReason.NO_ROOM); return false; } else { - this.spawnPosition.assign(this.context.xSpawn, this.context.ySpawn, this.context.zSpawn); - if (this.spawnPosition.distanceSquaredTo(position) > marker.getMaxDropHeightSquared()) { + this.spawnPosition.set(this.context.xSpawn, this.context.ySpawn, this.context.zSpawn); + if (this.spawnPosition.distanceSquared(position) > marker.getMaxDropHeightSquared()) { SpawningPlugin.get() .getLogger() .at(Level.FINE) @@ -346,7 +346,7 @@ public class SpawnMarkerEntity implements Component { int worldGenId = worldGenIdComponent != null ? worldGenIdComponent.getWorldGenId() : 0; _store.putComponent(_ref, WorldGenId.getComponentType(), new WorldGenId(worldGenId)); }; - Vector3f rotation = transformComponent.getRotation(); + Rotation3f rotation = transformComponent.getRotation(); Pair, NPCEntity> npcPair = npcModule.spawnEntity( store, roleIndex, this.spawnPosition, rotation, null, postSpawn ); @@ -492,7 +492,7 @@ public class SpawnMarkerEntity implements Component { spawnMarker.spawnCount = this.spawnCount; spawnMarker.suppressedBy = this.suppressedBy != null ? new HashSet<>(this.suppressedBy) : null; spawnMarker.failedSpawns = this.failedSpawns; - spawnMarker.spawnPosition.assign(this.spawnPosition); + spawnMarker.spawnPosition.set(this.spawnPosition); spawnMarker.npcReferences = this.npcReferences; spawnMarker.storedFlock = this.storedFlock != null ? this.storedFlock.clone() : null; spawnMarker.timeToDeactivation = this.timeToDeactivation; @@ -512,7 +512,7 @@ public class SpawnMarkerEntity implements Component { spawnMarker.spawnAfter = this.spawnAfter; spawnMarker.npcReferences = this.npcReferences; spawnMarker.storedFlock = this.storedFlock != null ? this.storedFlock.cloneSerializable() : null; - spawnMarker.spawnPosition.assign(this.spawnPosition); + spawnMarker.spawnPosition.set(this.spawnPosition); return spawnMarker; } diff --git a/src/com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerSystems.java b/src/com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerSystems.java index bee99084..455e798c 100644 --- a/src/com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerSystems.java +++ b/src/com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerSystems.java @@ -1,7 +1,5 @@ package com.hypixel.hytale.server.spawning.spawnmarkers; -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Archetype; import com.hypixel.hytale.component.ArchetypeChunk; @@ -13,11 +11,9 @@ import com.hypixel.hytale.component.RemoveReason; import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.SystemGroup; -import com.hypixel.hytale.component.data.unknown.UnknownComponents; import com.hypixel.hytale.component.dependency.Dependency; import com.hypixel.hytale.component.dependency.Order; import com.hypixel.hytale.component.dependency.OrderPriority; -import com.hypixel.hytale.component.dependency.RootDependency; import com.hypixel.hytale.component.dependency.SystemDependency; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; @@ -25,24 +21,17 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.protocol.AnimationSlot; -import com.hypixel.hytale.server.core.asset.type.model.config.Model; -import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.UUIDComponent; -import com.hypixel.hytale.server.core.entity.nameplate.Nameplate; import com.hypixel.hytale.server.core.entity.reference.InvalidatablePersistentRef; import com.hypixel.hytale.server.core.entity.reference.PersistentRefCount; -import com.hypixel.hytale.server.core.modules.entity.AllLegacyEntityTypesQuery; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab; import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen; import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; -import com.hypixel.hytale.server.core.modules.entity.component.HiddenFromAdventurePlayers; import com.hypixel.hytale.server.core.modules.entity.component.Intangible; import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent; -import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.component.WorldGenId; import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; @@ -59,14 +48,12 @@ import com.hypixel.hytale.server.npc.role.Role; import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.bson.BsonDocument; +import org.joml.Vector3d; public class SpawnMarkerSystems { @Nonnull @@ -294,73 +281,6 @@ public class SpawnMarkerSystems { } } - @Deprecated(forRemoval = true) - public static class LegacyEntityMigration extends EntityModule.MigrationSystem { - @Nonnull - private final ComponentType persistentModelComponentType = PersistentModel.getComponentType(); - @Nonnull - private final ComponentType nameplateComponentType = Nameplate.getComponentType(); - @Nonnull - private final ComponentType uuidComponentType = UUIDComponent.getComponentType(); - @Nonnull - private final ComponentType> unknownComponentsComponentType = EntityStore.REGISTRY.getUnknownComponentType(); - @Nonnull - private final Query query = Query.and(this.unknownComponentsComponentType, Query.not(AllLegacyEntityTypesQuery.INSTANCE)); - - @Override - public void onEntityAdd(@Nonnull Holder holder, @Nonnull AddReason reason, @Nonnull Store store) { - UnknownComponents unknownComponentsComponent = holder.getComponent(this.unknownComponentsComponentType); - - assert unknownComponentsComponent != null; - - Map unknownComponents = unknownComponentsComponent.getUnknownComponents(); - BsonDocument spawnMarker = unknownComponents.remove("SpawnMarker"); - if (spawnMarker != null) { - Archetype archetype = holder.getArchetype(); - - assert archetype != null; - - if (!archetype.contains(this.persistentModelComponentType)) { - Model.ModelReference modelReference = Entity.MODEL.get(spawnMarker).get(); - holder.addComponent(this.persistentModelComponentType, new PersistentModel(modelReference)); - } - - if (!archetype.contains(this.nameplateComponentType)) { - holder.addComponent(this.nameplateComponentType, new Nameplate(Entity.DISPLAY_NAME.get(spawnMarker).get())); - } - - if (!archetype.contains(this.uuidComponentType)) { - holder.addComponent(this.uuidComponentType, new UUIDComponent(Entity.UUID.get(spawnMarker).get())); - } - - holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType()); - int worldGenId = Codec.INTEGER.decode(spawnMarker.get("WorldgenId")); - if (worldGenId != 0) { - holder.addComponent(WorldGenId.getComponentType(), new WorldGenId(worldGenId)); - } - - SpawnMarkerEntity marker = SpawnMarkerEntity.CODEC.decode(spawnMarker, new ExtraInfo(5)); - holder.addComponent(SpawnMarkerEntity.getComponentType(), marker); - } - } - - @Override - public void onEntityRemoved(@Nonnull Holder holder, @Nonnull RemoveReason reason, @Nonnull Store store) { - } - - @Nonnull - @Override - public Query getQuery() { - return this.query; - } - - @Nonnull - @Override - public Set> getDependencies() { - return RootDependency.firstSet(); - } - } - public static class Ticking extends EntityTickingSystem { @Nonnull private final ComponentType spawnMarkerEntityComponentType; @@ -432,7 +352,7 @@ public class SpawnMarkerSystems { StoredFlock storedFlock = spawnMarkerEntityComponent.getStoredFlock(); if (storedFlock != null) { SpatialResource, EntityStore> spatialResource = store.getResource(this.playerSpatialComponent); - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); spatialResource.getSpatialStructure().collect(transformComponent.getPosition(), cachedMarker.getDeactivationDistance(), results); boolean hasPlayersInRange = !results.isEmpty(); if (!hasPlayersInRange) { @@ -494,7 +414,7 @@ public class SpawnMarkerSystems { Ref ref = archetypeChunk.getReferenceTo(index); commandBuffer.run( _store -> { - ObjectList> tempStorageList = SpatialResource.getThreadLocalReferenceList(); + List> tempStorageList = SpatialResource.getThreadLocalReferenceList(); for (InvalidatablePersistentRef referencex : npcReferences) { Ref npcRef = referencex.getEntity(_store); @@ -520,11 +440,11 @@ public class SpawnMarkerSystems { if (storedFlock.hasStoredNPCs()) { commandBuffer.run(_store -> { - ObjectList> tempStorageList = SpatialResource.getThreadLocalReferenceList(); + List> tempStorageList = SpatialResource.getThreadLocalReferenceList(); storedFlock.restoreNPCs(tempStorageList, _store); spawnMarkerEntityComponent.setSpawnCount(tempStorageList.size()); Vector3d position = spawnMarkerEntityComponent.getSpawnPosition(); - Vector3f rotation = transformComponent.getRotation(); + Rotation3f rotation = transformComponent.getRotation(); InvalidatablePersistentRef[] npcReferencesx = new InvalidatablePersistentRef[tempStorageList.size()]; int ix = 0; @@ -545,8 +465,8 @@ public class SpawnMarkerSystems { InvalidatablePersistentRef referencex = new InvalidatablePersistentRef(); referencex.setEntity(refx, _store); npcReferencesx[ix] = referencex; - npcTransform.getPosition().assign(position); - npcTransform.getRotation().assign(rotation); + npcTransform.getPosition().set(position); + npcTransform.getRotation().set(rotation); npcHeadRotation.setRotation(rotation); npcComponentx.playAnimation(refx, AnimationSlot.Status, null, commandBuffer); } diff --git a/src/com/hypixel/hytale/server/spawning/suppression/SpawnSuppressorEntry.java b/src/com/hypixel/hytale/server/spawning/suppression/SpawnSuppressorEntry.java index cac364d9..097095a8 100644 --- a/src/com/hypixel/hytale/server/spawning/suppression/SpawnSuppressorEntry.java +++ b/src/com/hypixel/hytale/server/spawning/suppression/SpawnSuppressorEntry.java @@ -3,11 +3,12 @@ package com.hypixel.hytale.server.spawning.suppression; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; +import org.joml.Vector3d; public class SpawnSuppressorEntry { public static final BuilderCodec CODEC = BuilderCodec.builder(SpawnSuppressorEntry.class, SpawnSuppressorEntry::new) - .append(new KeyedCodec<>("Position", Vector3d.CODEC), (entry, v) -> entry.position = v, entry -> entry.position) + .append(new KeyedCodec<>("Position", Vector3dUtil.CODEC), (entry, v) -> entry.position = v, entry -> entry.position) .add() .append(new KeyedCodec<>("Suppression", Codec.STRING), (entry, s) -> entry.suppressionId = s, entry -> entry.suppressionId) .add() diff --git a/src/com/hypixel/hytale/server/spawning/suppression/component/ChunkSuppressionQueue.java b/src/com/hypixel/hytale/server/spawning/suppression/component/ChunkSuppressionQueue.java index 7302c890..f520a271 100644 --- a/src/com/hypixel/hytale/server/spawning/suppression/component/ChunkSuppressionQueue.java +++ b/src/com/hypixel/hytale/server/spawning/suppression/component/ChunkSuppressionQueue.java @@ -6,6 +6,7 @@ import com.hypixel.hytale.component.ResourceType; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.spawning.SpawningPlugin; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -13,7 +14,7 @@ import javax.annotation.Nonnull; public class ChunkSuppressionQueue implements Resource { private final List, ChunkSuppressionEntry>> toAdd = new ObjectArrayList<>(); - private final List> toRemove = new ObjectArrayList<>(); + private final List> toRemove = new ReferenceArrayList<>(); public static ResourceType getResourceType() { return SpawningPlugin.get().getChunkSuppressionQueueResourceType(); diff --git a/src/com/hypixel/hytale/server/spawning/suppression/system/SpawnMarkerSuppressionSystem.java b/src/com/hypixel/hytale/server/spawning/suppression/system/SpawnMarkerSuppressionSystem.java index 68125538..ee7a03d7 100644 --- a/src/com/hypixel/hytale/server/spawning/suppression/system/SpawnMarkerSuppressionSystem.java +++ b/src/com/hypixel/hytale/server/spawning/suppression/system/SpawnMarkerSuppressionSystem.java @@ -55,7 +55,7 @@ public class SpawnMarkerSuppressionSystem extends RefSystem { } else if (suppression.isSuppressSpawnMarkers()) { double radius = suppression.getRadius(); double radiusSquared = radius * radius; - if (transform.getPosition().distanceSquaredTo(entry.getPosition()) <= radiusSquared) { + if (transform.getPosition().distanceSquared(entry.getPosition()) <= radiusSquared) { marker.suppress(id); SpawningPlugin.get().getLogger().at(Level.FINEST).log("Suppressing spawn marker %s on add/load", uuid.getUuid()); } diff --git a/src/com/hypixel/hytale/server/spawning/suppression/system/SpawnSuppressionSystems.java b/src/com/hypixel/hytale/server/spawning/suppression/system/SpawnSuppressionSystems.java index 4e72b6cf..07c502a6 100644 --- a/src/com/hypixel/hytale/server/spawning/suppression/system/SpawnSuppressionSystems.java +++ b/src/com/hypixel/hytale/server/spawning/suppression/system/SpawnSuppressionSystems.java @@ -25,7 +25,6 @@ import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.modules.entity.EntityModule; import com.hypixel.hytale.server.core.modules.entity.component.FromPrefab; @@ -48,7 +47,6 @@ import com.hypixel.hytale.server.spawning.suppression.component.SpawnSuppression import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -58,6 +56,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class SpawnSuppressionSystems { private static void suppressSpawns( @@ -126,7 +125,7 @@ public class SpawnSuppressionSystems { } if (suppression.isSuppressSpawnMarkers()) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> spatialResource = store.getResource(SpawningPlugin.get().getSpawnMarkerSpatialResource()); spatialResource.getSpatialStructure().collect(position, radius, results); @@ -335,7 +334,7 @@ public class SpawnSuppressionSystems { boolean fromExternal = archetype.contains(FromPrefab.getComponentType()) || archetype.contains(FromWorldGen.getComponentType()); if (reason == AddReason.SPAWN || fromExternal) { SpawnSuppressionController suppressionController = store.getResource(this.spawnSuppressionControllerResourceType); - SpawnSuppressorEntry entry = new SpawnSuppressorEntry(suppressor.getSpawnSuppression(), transform.getPosition().clone()); + SpawnSuppressorEntry entry = new SpawnSuppressorEntry(suppressor.getSpawnSuppression(), new Vector3d(transform.getPosition())); UUID uuid = uuidComponent.getUuid(); SpawnSuppressorEntry prev = suppressionController.getSpawnSuppressorMap().put(uuid, entry); if (prev != null) { @@ -388,41 +387,43 @@ public class SpawnSuppressionSystems { long chunkIndex = ChunkUtil.indexChunk(chunkX, chunkZ); ChunkSuppressionEntry chunkEntry = null; ChunkSuppressionEntry oldEntry = chunkSuppressionMap.get(chunkIndex); - if (oldEntry.containsOnly(uuid)) { - chunkSuppressionMap.remove(chunkIndex); - } else { - List oldSpans = oldEntry.getSuppressionSpans(); - ObjectArrayList suppressedSpans = new ObjectArrayList<>(); - - for (ChunkSuppressionEntry.SuppressionSpan span : oldSpans) { - if (!span.getSuppressorId().equals(uuid)) { - suppressedSpans.add(span); - } - } - - chunkEntry = new ChunkSuppressionEntry(suppressedSpans); - chunkSuppressionMap.put(chunkIndex, chunkEntry); - } - - Ref chunkReference = chunkComponentStore.getChunkReference(chunkIndex); - if (chunkReference != null) { - ChunkSuppressionQueue chunkSuppressionQueue = chunkStore.getResource(this.chunkSuppressionQueueResourceType); - if (chunkEntry == null) { - chunkSuppressionQueue.queueForRemove(chunkReference); + if (oldEntry != null) { + if (oldEntry.containsOnly(uuid)) { + chunkSuppressionMap.remove(chunkIndex); } else { - chunkSuppressionQueue.queueForAdd(chunkReference, chunkEntry); + List oldSpans = oldEntry.getSuppressionSpans(); + ObjectArrayList suppressedSpans = new ObjectArrayList<>(); + + for (ChunkSuppressionEntry.SuppressionSpan span : oldSpans) { + if (!span.getSuppressorId().equals(uuid)) { + suppressedSpans.add(span); + } + } + + chunkEntry = new ChunkSuppressionEntry(suppressedSpans); + chunkSuppressionMap.put(chunkIndex, chunkEntry); } - SpawningPlugin.get() - .getLogger() - .at(Level.FINEST) - .log("Queuing removal of suppression from chunk index %s, %s", chunkIndex, suppressionId); + Ref chunkReference = chunkComponentStore.getChunkReference(chunkIndex); + if (chunkReference != null) { + ChunkSuppressionQueue chunkSuppressionQueue = chunkStore.getResource(this.chunkSuppressionQueueResourceType); + if (chunkEntry == null) { + chunkSuppressionQueue.queueForRemove(chunkReference); + } else { + chunkSuppressionQueue.queueForAdd(chunkReference, chunkEntry); + } + + SpawningPlugin.get() + .getLogger() + .at(Level.FINEST) + .log("Queuing removal of suppression from chunk index %s, %s", chunkIndex, suppressionId); + } } } } if (suppression.isSuppressSpawnMarkers()) { - ObjectList> results = SpatialResource.getThreadLocalReferenceList(); + List> results = SpatialResource.getThreadLocalReferenceList(); SpatialResource, EntityStore> spatialResource = store.getResource(this.spawnMarkerSpatialResourceType); spatialResource.getSpatialStructure().collect(position, radius, results); diff --git a/src/com/hypixel/hytale/server/spawning/systems/BeaconSpatialSystem.java b/src/com/hypixel/hytale/server/spawning/systems/BeaconSpatialSystem.java index 375a49ad..0722fa59 100644 --- a/src/com/hypixel/hytale/server/spawning/systems/BeaconSpatialSystem.java +++ b/src/com/hypixel/hytale/server/spawning/systems/BeaconSpatialSystem.java @@ -7,12 +7,12 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.beacons.SpawnBeacon; import com.hypixel.hytale.server.spawning.util.FloodFillPositionSelector; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class BeaconSpatialSystem extends SpatialSystem { private static final Query QUERY = Query.and( diff --git a/src/com/hypixel/hytale/server/spawning/systems/LegacyBeaconSpatialSystem.java b/src/com/hypixel/hytale/server/spawning/systems/LegacyBeaconSpatialSystem.java index c3ed4490..5043028f 100644 --- a/src/com/hypixel/hytale/server/spawning/systems/LegacyBeaconSpatialSystem.java +++ b/src/com/hypixel/hytale/server/spawning/systems/LegacyBeaconSpatialSystem.java @@ -8,11 +8,11 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.beacons.LegacySpawnBeaconEntity; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class LegacyBeaconSpatialSystem extends SpatialSystem { private static final Archetype QUERY = Archetype.of(LegacySpawnBeaconEntity.getComponentType(), TransformComponent.getComponentType()); diff --git a/src/com/hypixel/hytale/server/spawning/systems/SpawnMarkerSpatialSystem.java b/src/com/hypixel/hytale/server/spawning/systems/SpawnMarkerSpatialSystem.java index 0de80317..8c973342 100644 --- a/src/com/hypixel/hytale/server/spawning/systems/SpawnMarkerSpatialSystem.java +++ b/src/com/hypixel/hytale/server/spawning/systems/SpawnMarkerSpatialSystem.java @@ -8,11 +8,11 @@ import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.spatial.SpatialResource; import com.hypixel.hytale.component.spatial.SpatialSystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class SpawnMarkerSpatialSystem extends SpatialSystem { private static final Archetype QUERY = Archetype.of(SpawnMarkerEntity.getComponentType(), TransformComponent.getComponentType()); diff --git a/src/com/hypixel/hytale/server/spawning/util/FloodFillPositionSelector.java b/src/com/hypixel/hytale/server/spawning/util/FloodFillPositionSelector.java index ec79b6b8..ae2b6adc 100644 --- a/src/com/hypixel/hytale/server/spawning/util/FloodFillPositionSelector.java +++ b/src/com/hypixel/hytale/server/spawning/util/FloodFillPositionSelector.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.BlockMaterial; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; @@ -45,6 +43,8 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3i; public class FloodFillPositionSelector implements Component { private static final int MAX_SPAWN_POSITIONS_HINT = 30; @@ -151,7 +151,7 @@ public class FloodFillPositionSelector implements Component { for (int i = 0; i < positions.size(); i++) { FloodFillPositionSelector.WeightedPosition entry = positions.get(i); - double distance = playerPosition.distanceSquaredTo(entry.position); + double distance = playerPosition.distanceSquared(entry.position.x, entry.position.y, entry.position.z); entry.weight = distance < minDistanceFromPlayerSquared ? 0.0 : Math.max(0.0, targetDistanceFromPlayerSquared - Math.abs(distance - targetDistanceFromPlayerSquared)); @@ -242,9 +242,9 @@ public class FloodFillPositionSelector implements Component { public void buildPositionCache(@Nonnull Vector3d origin, @Nonnull FloodFillEntryPoolSimple pool) { int sizeHalf = this.size / 2; - int worldX = MathUtil.floor(origin.getX()); - int worldY = MathUtil.floor(origin.getY()); - int worldZ = MathUtil.floor(origin.getZ()); + int worldX = MathUtil.floor(origin.x()); + int worldY = MathUtil.floor(origin.y()); + int worldZ = MathUtil.floor(origin.z()); this.chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(this.world, worldX, worldZ, sizeHalf); this.chunk = this.chunkAccessor.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(worldX, worldZ)); if (this.chunk != null) { diff --git a/src/com/hypixel/hytale/server/spawning/util/LightRangePredicate.java b/src/com/hypixel/hytale/server/spawning/util/LightRangePredicate.java index fe5d9e7f..c5be7140 100644 --- a/src/com/hypixel/hytale/server/spawning/util/LightRangePredicate.java +++ b/src/com/hypixel/hytale/server/spawning/util/LightRangePredicate.java @@ -3,7 +3,6 @@ package com.hypixel.hytale.server.spawning.util; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; @@ -12,6 +11,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.spawning.assets.spawns.LightType; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class LightRangePredicate { private byte lightValueMin; @@ -138,9 +138,9 @@ public class LightRangePredicate { } public boolean test(@Nonnull World world, @Nonnull Vector3d position, @Nonnull ComponentAccessor componentAccessor) { - int x = MathUtil.floor(position.getX()); - int y = MathUtil.floor(position.getY()); - int z = MathUtil.floor(position.getZ()); + int x = MathUtil.floor(position.x()); + int y = MathUtil.floor(position.y()); + int z = MathUtil.floor(position.z()); WorldTimeResource worldTimeResource = componentAccessor.getResource(WorldTimeResource.getResourceType()); WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(x, z)); return chunk != null && this.test(chunk.getBlockChunk(), x, y, z, worldTimeResource.getSunlightFactor()); diff --git a/src/com/hypixel/hytale/server/spawning/world/WorldEnvironmentSpawnData.java b/src/com/hypixel/hytale/server/spawning/world/WorldEnvironmentSpawnData.java index 167f2260..686c2cd2 100644 --- a/src/com/hypixel/hytale/server/spawning/world/WorldEnvironmentSpawnData.java +++ b/src/com/hypixel/hytale/server/spawning/world/WorldEnvironmentSpawnData.java @@ -18,8 +18,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; import it.unimi.dsi.fastutil.objects.Object2IntMap; -import java.util.HashSet; -import java.util.Set; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -36,12 +36,12 @@ public class WorldEnvironmentSpawnData { @Nonnull private final Int2ObjectMap npcStatMap; @Nonnull - private final Set> chunkRefSet; + private final List> chunkRefList; public WorldEnvironmentSpawnData(int environmentIndex, double density) { this.environmentIndex = environmentIndex; this.npcStatMap = new Int2ObjectOpenHashMap<>(); - this.chunkRefSet = new HashSet<>(); + this.chunkRefList = new ReferenceArrayList<>(); this.density = density; this.fullyPopulated = true; } @@ -96,8 +96,8 @@ public class WorldEnvironmentSpawnData { } @Nonnull - public Set> getChunkRefSet() { - return this.chunkRefSet; + public List> getChunkRefList() { + return this.chunkRefList; } public void adjustSegmentCount(int delta) { @@ -113,7 +113,7 @@ public class WorldEnvironmentSpawnData { this.density = density; this.expectedNPCs = this.segmentCount * density / 1024.0; - for (Ref chunkRef : this.chunkRefSet) { + for (Ref chunkRef : this.chunkRefList) { store.getComponent(chunkRef, ChunkSpawnData.getComponentType()).getEnvironmentSpawnData(this.environmentIndex).updateDensity(density); } } @@ -206,13 +206,13 @@ public class WorldEnvironmentSpawnData { public void removeChunk(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { WorldTimeResource worldTimeResource = componentAccessor.getResource(WorldTimeResource.getResourceType()); - this.chunkRefSet.remove(ref); + this.chunkRefList.remove(ref); this.updateExpectedNPCs(worldTimeResource.getMoonPhase()); } public void addChunk(@Nonnull Ref ref, @Nonnull ComponentAccessor componentAccessor) { WorldTimeResource worldTimeResource = componentAccessor.getResource(WorldTimeResource.getResourceType()); - this.chunkRefSet.add(ref); + this.chunkRefList.add(ref); this.fullyPopulated = false; this.updateExpectedNPCs(worldTimeResource.getMoonPhase()); this.resetUnspawnable(); diff --git a/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawnJobSystems.java b/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawnJobSystems.java index 29081016..ad8144e8 100644 --- a/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawnJobSystems.java +++ b/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawnJobSystems.java @@ -17,8 +17,7 @@ import com.hypixel.hytale.component.system.HolderSystem; import com.hypixel.hytale.component.system.RefChangeSystem; import com.hypixel.hytale.component.system.tick.EntityTickingSystem; import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.core.entity.Frozen; import com.hypixel.hytale.server.core.universe.world.World; @@ -50,6 +49,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class WorldSpawnJobSystems { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @@ -240,7 +240,7 @@ public class WorldSpawnJobSystems { NPCPlugin npcModule = NPCPlugin.get(); SpawningContext spawningContext = spawnJobData.getSpawningContext(); Vector3d position = spawningContext.newPosition(); - Vector3f rotation = spawningContext.newRotation(); + Rotation3f rotation = spawningContext.newRotation(); int roleIndex = spawnJobData.getRoleIndex(); try { diff --git a/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawnTrackingSystem.java b/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawnTrackingSystem.java index 08846dbe..564f4a8c 100644 --- a/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawnTrackingSystem.java +++ b/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawnTrackingSystem.java @@ -13,7 +13,6 @@ import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.iterator.SpiralIterator; import com.hypixel.hytale.math.util.ChunkUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; @@ -29,10 +28,11 @@ import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnData; import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnedNPCData; import com.hypixel.hytale.server.spawning.world.component.WorldSpawnData; import java.util.Iterator; -import java.util.Set; +import java.util.List; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class WorldSpawnTrackingSystem extends RefSystem { private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); @@ -85,8 +85,8 @@ public class WorldSpawnTrackingSystem extends RefSystem { ChunkStore chunkStore = world.getChunkStore(); Store chunkComponentStore = chunkStore.getStore(); Vector3d position = store.getComponent(ref, this.transformComponentType).getPosition(); - int originX = ChunkUtil.chunkCoordinate(position.getX()); - int originZ = ChunkUtil.chunkCoordinate(position.getZ()); + int originX = ChunkUtil.chunkCoordinate(position.x()); + int originZ = ChunkUtil.chunkCoordinate(position.z()); Ref chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunk(originX, originZ)); double count = trackNewNPC( chunkRef, environmentIndex, 1.0, this.chunkSpawnDataComponentType, this.chunkSpawnedNPCDataComponentType, chunkComponentStore @@ -131,7 +131,7 @@ public class WorldSpawnTrackingSystem extends RefSystem { ); } - Set> chunkOptions = worldSpawnData.getWorldEnvironmentSpawnData(environmentIndex).getChunkRefSet(); + List> chunkOptions = worldSpawnData.getWorldEnvironmentSpawnData(environmentIndex).getChunkRefList(); Iterator> iterator = chunkOptions.iterator(); while (iterator.hasNext() && count > 0.0) { @@ -190,8 +190,8 @@ public class WorldSpawnTrackingSystem extends RefSystem { assert transformComponent != null; Vector3d position = transformComponent.getPosition(); - int originX = ChunkUtil.chunkCoordinate(position.getX()); - int originZ = ChunkUtil.chunkCoordinate(position.getZ()); + int originX = ChunkUtil.chunkCoordinate(position.x()); + int originZ = ChunkUtil.chunkCoordinate(position.z()); Ref chunkRef = chunkStore.getChunkReference(ChunkUtil.indexChunk(originX, originZ)); double count = untrackRemovedNPC( chunkRef, environmentIndex, 1.0, this.chunkSpawnDataComponentType, this.chunkSpawnedNPCDataComponentType, chunkComponentStore @@ -226,7 +226,7 @@ public class WorldSpawnTrackingSystem extends RefSystem { ); } - Set> chunkOptions = worldSpawnData.getWorldEnvironmentSpawnData(environmentIndex).getChunkRefSet(); + List> chunkOptions = worldSpawnData.getWorldEnvironmentSpawnData(environmentIndex).getChunkRefList(); Iterator> iterator = chunkOptions.iterator(); while (iterator.hasNext() && count > 0.0) { diff --git a/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawningSystem.java b/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawningSystem.java index f2283af2..f6a0fb72 100644 --- a/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawningSystem.java +++ b/src/com/hypixel/hytale/server/spawning/world/system/WorldSpawningSystem.java @@ -27,7 +27,7 @@ import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnedNPCData; import com.hypixel.hytale.server.spawning.world.component.SpawnJobData; import com.hypixel.hytale.server.spawning.world.component.WorldSpawnData; import java.time.Duration; -import java.util.Set; +import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.logging.Level; import javax.annotation.Nonnull; @@ -242,49 +242,49 @@ public class WorldSpawningSystem extends TickingSystem { ) { int roleIndex = stat.getRoleIndex(); boolean wasFullyPopulated = spawnData.isFullyPopulated(); - Set> chunkRefSet = spawnData.getChunkRefSet(); + List> chunkRefSet = spawnData.getChunkRefList(); int environmentIndex = spawnData.getEnvironmentIndex(); double weight = 0.0; boolean spawnable = false; boolean fullyPopulated = true; if (wasFullyPopulated) { for (Ref chunkRef : chunkRefSet) { - ChunkSpawnData chunkSpawnDataComponent = store.getComponent(chunkRef, this.chunkSpawnDataComponentType); - - assert chunkSpawnDataComponent != null; - - ChunkSpawnedNPCData chunkSpawnedNPCDataComponent = store.getComponent(chunkRef, this.chunkSpawnedNPCDataComponentType); - - assert chunkSpawnedNPCDataComponent != null; - - ChunkEnvironmentSpawnData chunkEnvironmentSpawnData = chunkSpawnDataComponent.getEnvironmentSpawnData(environmentIndex); - fullyPopulated = fullyPopulated - && chunkEnvironmentSpawnData.isFullyPopulated(chunkSpawnedNPCDataComponent.getEnvironmentSpawnCount(environmentIndex)); - if (chunkEnvironmentSpawnData.isRoleSpawnable(roleIndex)) { - spawnable = true; - weight += store.getComponent(chunkRef, this.spawnJobDataComponentType) == null && !getAndUpdateSpawnCooldown(chunkSpawnDataComponent) - ? 1.0 - : 0.0; + if (chunkRef.isValid()) { + ChunkSpawnData chunkSpawnDataComponent = store.getComponent(chunkRef, this.chunkSpawnDataComponentType); + if (chunkSpawnDataComponent != null) { + ChunkSpawnedNPCData chunkSpawnedNPCDataComponent = store.getComponent(chunkRef, this.chunkSpawnedNPCDataComponentType); + if (chunkSpawnedNPCDataComponent != null) { + ChunkEnvironmentSpawnData chunkEnvironmentSpawnData = chunkSpawnDataComponent.getEnvironmentSpawnData(environmentIndex); + fullyPopulated = fullyPopulated + && chunkEnvironmentSpawnData.isFullyPopulated(chunkSpawnedNPCDataComponent.getEnvironmentSpawnCount(environmentIndex)); + if (chunkEnvironmentSpawnData.isRoleSpawnable(roleIndex)) { + spawnable = true; + weight += store.getComponent(chunkRef, this.spawnJobDataComponentType) == null && !getAndUpdateSpawnCooldown(chunkSpawnDataComponent) + ? 1.0 + : 0.0; + } + } + } } } } else { - for (Ref chunkRef : chunkRefSet) { - ChunkSpawnData chunkSpawnDataComponentx = store.getComponent(chunkRef, this.chunkSpawnDataComponentType); - - assert chunkSpawnDataComponentx != null; - - ChunkSpawnedNPCData chunkSpawnedNPCDataComponentx = store.getComponent(chunkRef, this.chunkSpawnedNPCDataComponentType); - - assert chunkSpawnedNPCDataComponentx != null; - - ChunkEnvironmentSpawnData chunkEnvironmentSpawnData = chunkSpawnDataComponentx.getEnvironmentSpawnData(environmentIndex); - double spawnCount = chunkSpawnedNPCDataComponentx.getEnvironmentSpawnCount(environmentIndex); - fullyPopulated = fullyPopulated && chunkEnvironmentSpawnData.isFullyPopulated(spawnCount); - if (chunkEnvironmentSpawnData.isRoleSpawnable(roleIndex)) { - spawnable = true; - weight += store.getComponent(chunkRef, this.spawnJobDataComponentType) == null && !getAndUpdateSpawnCooldown(chunkSpawnDataComponentx) - ? chunkEnvironmentSpawnData.getWeight(spawnCount) - : 0.0; + for (Ref chunkRefx : chunkRefSet) { + if (chunkRefx.isValid()) { + ChunkSpawnData chunkSpawnDataComponent = store.getComponent(chunkRefx, this.chunkSpawnDataComponentType); + if (chunkSpawnDataComponent != null) { + ChunkSpawnedNPCData chunkSpawnedNPCDataComponent = store.getComponent(chunkRefx, this.chunkSpawnedNPCDataComponentType); + if (chunkSpawnedNPCDataComponent != null) { + ChunkEnvironmentSpawnData chunkEnvironmentSpawnData = chunkSpawnDataComponent.getEnvironmentSpawnData(environmentIndex); + double spawnCount = chunkSpawnedNPCDataComponent.getEnvironmentSpawnCount(environmentIndex); + fullyPopulated = fullyPopulated && chunkEnvironmentSpawnData.isFullyPopulated(spawnCount); + if (chunkEnvironmentSpawnData.isRoleSpawnable(roleIndex)) { + spawnable = true; + weight += store.getComponent(chunkRefx, this.spawnJobDataComponentType) == null && !getAndUpdateSpawnCooldown(chunkSpawnDataComponent) + ? chunkEnvironmentSpawnData.getWeight(spawnCount) + : 0.0; + } + } + } } } } @@ -293,35 +293,39 @@ public class WorldSpawningSystem extends TickingSystem { if (spawnable) { return RandomExtra.randomWeightedElement( chunkRefSet, - (chunkRefx, index) -> { - ChunkSpawnData chunkSpawnDataComponent = store.getComponent(chunkRefx, this.chunkSpawnDataComponentType); - - assert chunkSpawnDataComponent != null; - - ChunkEnvironmentSpawnData chunkEnvironmentSpawnDatax = chunkSpawnDataComponent.getEnvironmentSpawnData(environmentIndex); - return chunkEnvironmentSpawnDatax.isRoleSpawnable(index); + (chunkRefxx, index) -> { + ChunkSpawnData chunkSpawnDataComponent = store.getComponent(chunkRefxx, this.chunkSpawnDataComponentType); + if (chunkSpawnDataComponent == null) { + return false; + } else { + ChunkEnvironmentSpawnData chunkEnvironmentSpawnDatax = chunkSpawnDataComponent.getEnvironmentSpawnData(environmentIndex); + return chunkEnvironmentSpawnDatax.isRoleSpawnable(index); + } }, wasFullyPopulated - ? (chunkRefx, index) -> { - ChunkSpawnData spawnChunkDataComponent = store.getComponent(chunkRefx, this.chunkSpawnDataComponentType); - - assert spawnChunkDataComponent != null; - - return store.getComponent(chunkRefx, this.spawnJobDataComponentType) == null && !spawnChunkDataComponent.isOnSpawnCooldown() ? 1.0 : 0.0; + ? (chunkRefxx, index) -> { + ChunkSpawnData spawnChunkDataComponent = store.getComponent(chunkRefxx, this.chunkSpawnDataComponentType); + if (spawnChunkDataComponent == null) { + return 0.0; + } else { + return store.getComponent(chunkRefxx, this.spawnJobDataComponentType) == null && !spawnChunkDataComponent.isOnSpawnCooldown() ? 1.0 : 0.0; + } } - : (chunkRefx, index) -> { - ChunkSpawnData chunkSpawnDataComponent = store.getComponent(chunkRefx, this.chunkSpawnDataComponentType); - - assert chunkSpawnDataComponent != null; - - ChunkSpawnedNPCData chunkSpawnedNPCDataComponentx = store.getComponent(chunkRefx, this.chunkSpawnedNPCDataComponentType); - - assert chunkSpawnedNPCDataComponentx != null; - - ChunkEnvironmentSpawnData chunkEnvironmentSpawnDatax = chunkSpawnDataComponent.getEnvironmentSpawnData(environmentIndex); - return store.getComponent(chunkRefx, this.spawnJobDataComponentType) == null && !chunkSpawnDataComponent.isOnSpawnCooldown() - ? chunkEnvironmentSpawnDatax.getWeight(chunkSpawnedNPCDataComponentx.getEnvironmentSpawnCount(environmentIndex)) - : 0.0; + : (chunkRefxx, index) -> { + ChunkSpawnData chunkSpawnDataComponent = store.getComponent(chunkRefxx, this.chunkSpawnDataComponentType); + if (chunkSpawnDataComponent == null) { + return 0.0; + } else { + ChunkSpawnedNPCData chunkSpawnedNPCDataComponentx = store.getComponent(chunkRefxx, this.chunkSpawnedNPCDataComponentType); + if (chunkSpawnedNPCDataComponentx == null) { + return 0.0; + } else { + ChunkEnvironmentSpawnData chunkEnvironmentSpawnDatax = chunkSpawnDataComponent.getEnvironmentSpawnData(environmentIndex); + return store.getComponent(chunkRefxx, this.spawnJobDataComponentType) == null && !chunkSpawnDataComponent.isOnSpawnCooldown() + ? chunkEnvironmentSpawnDatax.getWeight(chunkSpawnedNPCDataComponentx.getEnvironmentSpawnCount(environmentIndex)) + : 0.0; + } + } }, weight, roleIndex diff --git a/src/com/hypixel/hytale/server/worldgen/BiomeDataSystem.java b/src/com/hypixel/hytale/server/worldgen/BiomeDataSystem.java index 0d7ae959..63fb832a 100644 --- a/src/com/hypixel/hytale/server/worldgen/BiomeDataSystem.java +++ b/src/com/hypixel/hytale/server/worldgen/BiomeDataSystem.java @@ -7,7 +7,6 @@ import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.query.Query; import com.hypixel.hytale.component.system.tick.DelayedEntitySystem; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.world.World; @@ -21,6 +20,7 @@ import com.hypixel.hytale.server.worldgen.zone.Zone; import com.hypixel.hytale.server.worldgen.zone.ZoneDiscoveryConfig; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class BiomeDataSystem extends DelayedEntitySystem { public BiomeDataSystem() { @@ -50,8 +50,8 @@ public class BiomeDataSystem extends DelayedEntitySystem { Vector3d position = transformComponent.getPosition(); int seed = (int)world.getWorldConfig().getSeed(); - int x = (int)position.getX(); - int z = (int)position.getZ(); + int x = (int)position.x(); + int z = (int)position.z(); ZoneBiomeResult result = generator.getZoneBiomeResultAt(seed, x, z); Biome biome = result.getBiome(); Zone zone = result.getZoneResult().getZone(); diff --git a/src/com/hypixel/hytale/server/worldgen/ChunkGeneratorResource.java b/src/com/hypixel/hytale/server/worldgen/ChunkGeneratorResource.java index f9a2b4d7..9022774b 100644 --- a/src/com/hypixel/hytale/server/worldgen/ChunkGeneratorResource.java +++ b/src/com/hypixel/hytale/server/worldgen/ChunkGeneratorResource.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.worldgen; import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector2d; import com.hypixel.hytale.procedurallib.logic.ResultBuffer; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import com.hypixel.hytale.server.worldgen.cache.CoordinateCache; @@ -22,6 +21,7 @@ import it.unimi.dsi.fastutil.ints.IntList; import java.util.Random; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class ChunkGeneratorResource { @Nonnull @@ -30,9 +30,7 @@ public class ChunkGeneratorResource { public final Random random2; @Nonnull public final IntList coverArray; - public final TimeoutCache prefabs = new TimeoutCache<>( - 30L, TimeUnit.SECONDS, this::getPrefab, (key, value) -> value.release() - ); + public final TimeoutCache prefabs = new TimeoutCache<>(30L, TimeUnit.SECONDS, this::getPrefab, (key, value) -> {}); @Nonnull public final BlockPriorityChunk priorityChunk; @Nonnull diff --git a/src/com/hypixel/hytale/server/worldgen/cave/CaveGenerator.java b/src/com/hypixel/hytale/server/worldgen/cave/CaveGenerator.java index ee123f81..f06960ad 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/CaveGenerator.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/CaveGenerator.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.server.worldgen.cave; import com.hypixel.hytale.math.util.FastRandom; import com.hypixel.hytale.math.util.HashUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.procedurallib.condition.ConstantIntCondition; import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; @@ -23,6 +23,7 @@ import com.hypixel.hytale.server.worldgen.util.condition.flag.Int2FlagsCondition import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CaveGenerator { private final CaveType[] caveTypes; @@ -157,12 +158,12 @@ public class CaveGenerator { protected Vector3d getChildOrigin(@Nonnull CaveNode parentNode, @Nullable PrefabRotation parentRotation, @Nonnull CaveNodeType.CaveNodeChildEntry childEntry) { Vector3d vector = parentNode.getEnd(); Vector3d anchor = childEntry.getAnchor(); - if (anchor == Vector3d.ZERO) { + if (anchor.equals(Vector3dUtil.ZERO)) { return vector; } else { - vector.assign(anchor); + vector.set(anchor); if (parentRotation != null && parentRotation != PrefabRotation.ROTATION_0) { - vector.subtract(0.5, 0.5, 0.5); + vector.sub(0.5, 0.5, 0.5); parentRotation.rotate(vector); vector.add(0.5, 0.5, 0.5); } @@ -226,6 +227,8 @@ public class CaveGenerator { @Nonnull CavePrefabContainer.CavePrefabEntry entry, @Nonnull Random random ) { + assert caveNode.getShape().hasGeometry() : "Cannot generate cave-prefab inside an invalid shape"; + CavePrefabContainer.CavePrefabEntry.CavePrefabConfig config = entry.getConfig(); int iterations = config.getIterations(random.nextDouble()); @@ -261,8 +264,8 @@ public class CaveGenerator { } else if (mask == CaveBiomeMaskFlags.DEFAULT_DENY) { return 0; } else { - int x = MathUtil.floor(vec.getX()); - int z = MathUtil.floor(vec.getZ()); + int x = MathUtil.floor(vec.x()); + int z = MathUtil.floor(vec.z()); ZoneBiomeResult biomeResult = chunkGenerator.getZoneBiomeResultAt(seed, x, z); return mask.eval(biomeResult.getBiome().getId()); } diff --git a/src/com/hypixel/hytale/server/worldgen/cave/CaveNodeType.java b/src/com/hypixel/hytale/server/worldgen/cave/CaveNodeType.java index 50f7f7d9..be9b7e1a 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/CaveNodeType.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/CaveNodeType.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.worldgen.cave; import com.hypixel.hytale.common.map.IWeightedMap; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.condition.IBlockFluidCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateRndCondition; @@ -12,12 +11,16 @@ import com.hypixel.hytale.server.worldgen.cave.prefab.CavePrefabContainer; import com.hypixel.hytale.server.worldgen.cave.shape.CaveNodeShape; import com.hypixel.hytale.server.worldgen.cave.shape.CaveNodeShapeEnum; import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CaveNodeType { public static final CaveNodeType[] EMPTY_ARRAY = new CaveNodeType[0]; + public static final ListPool COVER_POOL = new ListPool<>(10, CaveNodeType.CaveNodeCoverEntry.EMPTY_ARRAY); + public static final ListPool CHILD_POOL = new ListPool<>(10, CaveNodeType.CaveNodeChildEntry.EMPTY_ARRAY); @Nonnull private final String name; @Nullable diff --git a/src/com/hypixel/hytale/server/worldgen/cave/CaveType.java b/src/com/hypixel/hytale/server/worldgen/cave/CaveType.java index 18a9e465..9b575c46 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/CaveType.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/CaveType.java @@ -8,12 +8,14 @@ import com.hypixel.hytale.procedurallib.property.NoiseProperty; import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; import com.hypixel.hytale.procedurallib.supplier.IFloatRange; import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import com.hypixel.hytale.server.worldgen.util.ListPool; import com.hypixel.hytale.server.worldgen.util.condition.BlockMaskCondition; import com.hypixel.hytale.server.worldgen.util.condition.flag.Int2FlagsCondition; import java.util.Random; import javax.annotation.Nonnull; public class CaveType { + public static final ListPool ENTRY_POOL = new ListPool<>(10, new CaveType[0]); protected final String name; protected final CaveNodeType entryNodeType; protected final IFloatRange yaw; diff --git a/src/com/hypixel/hytale/server/worldgen/cave/element/CaveNode.java b/src/com/hypixel/hytale/server/worldgen/cave/element/CaveNode.java index 6a034f8f..bdc119cf 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/element/CaveNode.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/element/CaveNode.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.worldgen.cave.element; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; import com.hypixel.hytale.server.worldgen.cave.shape.CaveNodeShape; import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; @@ -13,6 +12,7 @@ import java.util.List; import java.util.function.LongConsumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CaveNode implements CaveElement { private final CaveNodeType caveNodeType; diff --git a/src/com/hypixel/hytale/server/worldgen/cave/prefab/CavePrefabContainer.java b/src/com/hypixel/hytale/server/worldgen/cave/prefab/CavePrefabContainer.java index e31b0fbc..df24ff5e 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/prefab/CavePrefabContainer.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/prefab/CavePrefabContainer.java @@ -12,12 +12,14 @@ import com.hypixel.hytale.server.worldgen.biome.Biome; import com.hypixel.hytale.server.worldgen.cave.CavePrefabPlacement; import com.hypixel.hytale.server.worldgen.cave.element.CaveNode; import com.hypixel.hytale.server.worldgen.loader.WorldGenPrefabSupplier; +import com.hypixel.hytale.server.worldgen.util.ListPool; import com.hypixel.hytale.server.worldgen.util.condition.BlockMaskCondition; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class CavePrefabContainer { + public static final ListPool ENTRY_POOL = new ListPool<>(10, new CavePrefabContainer.CavePrefabEntry[0]); protected final CavePrefabContainer.CavePrefabEntry[] entries; public CavePrefabContainer(CavePrefabContainer.CavePrefabEntry[] entries) { diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShape.java index 4880c96e..c2029036 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShape.java @@ -1,11 +1,11 @@ package com.hypixel.hytale.server.worldgen.cave.shape; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.worldgen.cave.Cave; import com.hypixel.hytale.server.worldgen.cave.element.CaveNode; import com.hypixel.hytale.server.worldgen.chunk.ChunkGeneratorExecution; import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; import java.util.Random; +import org.joml.Vector3d; public interface CaveNodeShape { Vector3d getStart(); @@ -25,6 +25,6 @@ public interface CaveNodeShape { void populateChunk(int var1, ChunkGeneratorExecution var2, Cave var3, CaveNode var4, Random var5); default boolean hasGeometry() { - return true; + return this.getBounds().isValid(); } } diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShapeEnum.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShapeEnum.java index 2605825e..d42caee8 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShapeEnum.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShapeEnum.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.server.worldgen.cave.shape; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; import com.hypixel.hytale.server.worldgen.cave.CaveType; import com.hypixel.hytale.server.worldgen.cave.element.CaveNode; import java.util.Random; +import org.joml.Vector3d; public enum CaveNodeShapeEnum { PIPE, diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShapeUtils.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShapeUtils.java index 992b8242..1e973312 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShapeUtils.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/CaveNodeShapeUtils.java @@ -2,7 +2,7 @@ package com.hypixel.hytale.server.worldgen.cave.shape; import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.function.function.BiDoubleToDoubleFunction; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.procedurallib.condition.ConstantBlockFluidCondition; import com.hypixel.hytale.procedurallib.condition.IBlockFluidCondition; import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class CaveNodeShapeUtils { public static final BiDoubleToDoubleFunction LEFT = (l, r) -> l; @@ -31,7 +32,7 @@ public class CaveNodeShapeUtils { double x = bounds.fractionX(tx); double y = bounds.fractionY(ty); double z = bounds.fractionZ(tz); - return vector.assign(x, y, z); + return vector.set(x, y, z); } @Nonnull @@ -39,7 +40,7 @@ public class CaveNodeShapeUtils { double x = o.x + v.x * t; double y = o.y + v.y * t; double z = o.z + v.z * t; - return vector.assign(x, y, z); + return vector.set(x, y, z); } @Nonnull @@ -72,11 +73,11 @@ public class CaveNodeShapeUtils { @Nonnull public static Vector3d getOffset(@Nullable CaveNode parent, @Nonnull CaveNodeType.CaveNodeChildEntry childEntry) { Vector3d offset = childEntry.getOffset(); - if (offset == Vector3d.ZERO) { + if (offset == Vector3dUtil.ZERO) { return offset; } else { if (parent != null && parent.getShape() instanceof PrefabCaveNodeShape) { - offset = offset.clone(); + offset = new Vector3d(offset); ((PrefabCaveNodeShape)parent.getShape()).getPrefabRotation().rotate(offset); } @@ -225,7 +226,7 @@ public class CaveNodeShapeUtils { ) { double len2 = tx * tx + ty * ty + tz * tz; if (len2 == 0.0) { - return vector.assign(x, y, z); + return vector.set(x, y, z); } else { double invLen = Math.sqrt(1.0 / len2); double dx = Math.abs(tx) * rx * invLen; @@ -234,7 +235,7 @@ public class CaveNodeShapeUtils { x += dx * tx; y += dy * ty; z += dz * tz; - return vector.assign(x, y, z); + return vector.set(x, y, z); } } } diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/CylinderCaveNodeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/CylinderCaveNodeShape.java index eb66f1e2..d50b40f0 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/CylinderCaveNodeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/CylinderCaveNodeShape.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.worldgen.cave.shape; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; import com.hypixel.hytale.server.worldgen.cave.CaveType; @@ -10,6 +9,7 @@ import com.hypixel.hytale.server.worldgen.cave.element.CaveNode; import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class CylinderCaveNodeShape extends AbstractCaveNodeShape implements IWorldBounds { private final CaveType caveType; @@ -45,7 +45,7 @@ public class CylinderCaveNodeShape extends AbstractCaveNodeShape implements IWor @Nonnull @Override public Vector3d getStart() { - return this.o.clone(); + return new Vector3d(this.o); } @Nonnull @@ -214,7 +214,7 @@ public class CylinderCaveNodeShape extends AbstractCaveNodeShape implements IWor Vector3d direction = new Vector3d( TrigMathUtil.sin(pitch) * TrigMathUtil.cos(yaw), TrigMathUtil.cos(pitch), TrigMathUtil.sin(pitch) * TrigMathUtil.sin(yaw) ) - .scale(l); + .mul(l); double radius1; if (this.inheritParentRadius) { radius1 = CaveNodeShapeUtils.getEndRadius(parentNode, this.radius, random); diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/DistortedCaveNodeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/DistortedCaveNodeShape.java index f2d4f796..af92f9ac 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/DistortedCaveNodeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/DistortedCaveNodeShape.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.logic.GeneralNoise; import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; public class DistortedCaveNodeShape implements CaveNodeShape { private final CaveType caveType; @@ -58,11 +58,6 @@ public class DistortedCaveNodeShape implements CaveNodeShape { return this.shape; } - @Override - public boolean hasGeometry() { - return this.shape.hasGeometry(); - } - @Override public boolean shouldReplace(int seed, double x, double z, int y) { double t = this.shape.getProjection(x, z); @@ -319,11 +314,11 @@ public class DistortedCaveNodeShape implements CaveNodeShape { @Nonnull private static Vector3d getDirection(double yaw, double pitch, double length) { if (length == 0.0) { - return Vector3d.ZERO; + return new Vector3d(); } else { pitch = AbstractDistortedShape.clampPitch(pitch); return new Vector3d(TrigMathUtil.sin(pitch) * TrigMathUtil.cos(yaw), TrigMathUtil.cos(pitch), TrigMathUtil.sin(pitch) * TrigMathUtil.sin(yaw)) - .scale(length); + .mul(length); } } diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/EllipsoidCaveNodeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/EllipsoidCaveNodeShape.java index 707e5a59..ab5dcfa2 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/EllipsoidCaveNodeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/EllipsoidCaveNodeShape.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.worldgen.cave.shape; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; import com.hypixel.hytale.server.worldgen.cave.CaveType; @@ -9,6 +8,7 @@ import com.hypixel.hytale.server.worldgen.cave.element.CaveNode; import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EllipsoidCaveNodeShape extends AbstractCaveNodeShape implements IWorldBounds { private final CaveType caveType; @@ -41,7 +41,7 @@ public class EllipsoidCaveNodeShape extends AbstractCaveNodeShape implements IWo @Nonnull @Override public Vector3d getStart() { - return this.o.clone(); + return new Vector3d(this.o); } @Nonnull diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/EmptyLineCaveNodeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/EmptyLineCaveNodeShape.java index d53a1d68..92dde693 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/EmptyLineCaveNodeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/EmptyLineCaveNodeShape.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.worldgen.cave.shape; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; import com.hypixel.hytale.server.worldgen.cave.CaveType; @@ -9,6 +8,7 @@ import com.hypixel.hytale.server.worldgen.cave.element.CaveNode; import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class EmptyLineCaveNodeShape extends AbstractCaveNodeShape implements IWorldBounds { private final Vector3d o; @@ -22,7 +22,7 @@ public class EmptyLineCaveNodeShape extends AbstractCaveNodeShape implements IWo @Nonnull @Override public Vector3d getStart() { - return this.o.clone(); + return new Vector3d(this.o); } @Nonnull @@ -118,7 +118,7 @@ public class EmptyLineCaveNodeShape extends AbstractCaveNodeShape implements IWo Vector3d direction = new Vector3d( TrigMathUtil.sin(pitch) * TrigMathUtil.cos(yaw), TrigMathUtil.cos(pitch), TrigMathUtil.sin(pitch) * TrigMathUtil.sin(yaw) ) - .scale(l); + .mul(l); return new EmptyLineCaveNodeShape(origin, direction); } } diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/PipeCaveNodeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/PipeCaveNodeShape.java index a1f295b0..770adef0 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/PipeCaveNodeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/PipeCaveNodeShape.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.worldgen.cave.shape; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.supplier.IDoubleRange; import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; import com.hypixel.hytale.server.worldgen.cave.CaveType; @@ -10,6 +9,7 @@ import com.hypixel.hytale.server.worldgen.cave.element.CaveNode; import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class PipeCaveNodeShape extends AbstractCaveNodeShape implements IWorldBounds { private final CaveType caveType; @@ -45,7 +45,7 @@ public class PipeCaveNodeShape extends AbstractCaveNodeShape implements IWorldBo @Nonnull @Override public Vector3d getStart() { - return this.o.clone(); + return new Vector3d(this.o); } @Nonnull @@ -228,7 +228,7 @@ public class PipeCaveNodeShape extends AbstractCaveNodeShape implements IWorldBo Vector3d direction = new Vector3d( TrigMathUtil.sin(pitch) * TrigMathUtil.cos(yaw), TrigMathUtil.cos(pitch), TrigMathUtil.sin(pitch) * TrigMathUtil.sin(yaw) ) - .scale(l); + .mul(l); double radius1; if (this.inheritParentRadius) { radius1 = CaveNodeShapeUtils.getEndRadius(parentNode, this.radius, random); diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/PrefabCaveNodeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/PrefabCaveNodeShape.java index 38fe57c3..3101abab 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/PrefabCaveNodeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/PrefabCaveNodeShape.java @@ -3,7 +3,7 @@ package com.hypixel.hytale.server.worldgen.cave.shape; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.HashUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3dUtil; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import com.hypixel.hytale.server.worldgen.cave.Cave; @@ -21,12 +21,14 @@ import java.util.List; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3d; +import org.joml.Vector3dc; public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds { private final CaveType caveType; @Nonnull - private final Vector3d o; - private final Vector3d e; + private final Vector3dc o; + private final Vector3dc e; @Nonnull private final WorldGenPrefabSupplier prefabSupplier; @Nonnull @@ -41,8 +43,8 @@ public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds { public PrefabCaveNodeShape( CaveType caveType, - @Nonnull Vector3d o, - Vector3d e, + @Nonnull Vector3dc o, + Vector3dc e, @Nonnull WorldGenPrefabSupplier prefabSupplier, @Nonnull PrefabRotation rotation, BlockMaskCondition configuration @@ -55,12 +57,12 @@ public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds { this.configuration = configuration; IPrefabBuffer prefab = prefabSupplier.get(); IChunkBounds bounds = prefabSupplier.getBounds(prefab); - this.lowBoundX = MathUtil.floor(o.x + bounds.getLowBoundX(rotation)); - this.lowBoundY = MathUtil.floor(o.y + prefab.getMinY()); - this.lowBoundZ = MathUtil.floor(o.z + bounds.getLowBoundZ(rotation)); - this.highBoundX = MathUtil.ceil(o.x + bounds.getHighBoundX(rotation)); - this.highBoundY = MathUtil.ceil(o.y + prefab.getMaxY()); - this.highBoundZ = MathUtil.ceil(o.z + bounds.getHighBoundZ(rotation)); + this.lowBoundX = MathUtil.floor(o.x() + bounds.getLowBoundX(rotation)); + this.lowBoundY = MathUtil.floor(o.y() + prefab.getMinY()); + this.lowBoundZ = MathUtil.floor(o.z() + bounds.getLowBoundZ(rotation)); + this.highBoundX = MathUtil.ceil(o.x() + bounds.getHighBoundX(rotation)); + this.highBoundY = MathUtil.ceil(o.y() + prefab.getMaxY()); + this.highBoundZ = MathUtil.ceil(o.z() + bounds.getHighBoundZ(rotation)); } public CaveType getCaveType() { @@ -73,20 +75,20 @@ public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds { } @Nonnull - public Vector3d getO() { + public Vector3dc getO() { return this.o; } @Nonnull @Override public Vector3d getStart() { - return this.o.clone(); + return new Vector3d(this.o); } @Nonnull @Override public Vector3d getEnd() { - return this.o.clone().add(this.e); + return new Vector3d(this.o).add(this.e); } @Nonnull @@ -96,7 +98,7 @@ public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds { double x = MathUtil.floor(anchor.x) + 0.5; double y = MathUtil.floor(anchor.y) + 0.5; double z = MathUtil.floor(anchor.z) + 0.5; - return anchor.assign(x, y, z); + return anchor.set(x, y, z); } @Nonnull @@ -142,26 +144,26 @@ public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds { @Override public double getFloorPosition(int seed, double x, double z) { - x -= this.o.x; - z -= this.o.z; + x -= this.o.x(); + z -= this.o.z(); return this.prefabSupplier.get().getMinYAt(this.rotation, (int)x, (int)z); } @Override public double getCeilingPosition(int seed, double x, double z) { - x -= this.o.x; - z -= this.o.z; + x -= this.o.x(); + z -= this.o.z(); return this.prefabSupplier.get().getMaxYAt(this.rotation, (int)x, (int)z); } @Override public void populateChunk(int seed, @Nonnull ChunkGeneratorExecution execution, @Nonnull Cave cave, @Nonnull CaveNode node, Random random) { - int x = MathUtil.floor(this.o.x); - int y = MathUtil.floor(this.o.y); - int z = MathUtil.floor(this.o.z); + int x = MathUtil.floor(this.o.x()); + int y = MathUtil.floor(this.o.y()); + int z = MathUtil.floor(this.o.z()); int cx = x - ChunkUtil.minBlock(execution.getX()); int cz = z - ChunkUtil.minBlock(execution.getZ()); - long externalSeed = HashUtil.hash(Double.doubleToLongBits(this.o.x), Double.doubleToLongBits(this.o.z)) * 1406794441L; + long externalSeed = HashUtil.hash(Double.doubleToLongBits(this.o.x()), Double.doubleToLongBits(this.o.z())) * 1406794441L; PrefabPasteUtil.PrefabPasteBuffer buffer = ChunkGenerator.getResource().prefabBuffer; buffer.setSeed(seed, externalSeed); buffer.execution = execution; @@ -228,9 +230,9 @@ public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds { WorldGenPrefabSupplier prefab = this.prefabs.get(random.nextInt(this.prefabs.size())); if (parentNode == null) { PrefabRotation rotation = PrefabRotation.VALUES[random.nextInt(PrefabRotation.VALUES.length)]; - return new PrefabCaveNodeShape(caveType, origin, Vector3d.ZERO, prefab, rotation, this.configuration); + return new PrefabCaveNodeShape(caveType, origin, Vector3dUtil.ZERO, prefab, rotation, this.configuration); } else { - Vector3d offset = childEntry.getOffset().clone(); + Vector3d offset = new Vector3d(childEntry.getOffset()); PrefabRotation rotation = childEntry.getRotation(random); if (parentNode.getShape() instanceof PrefabCaveNodeShape parentShape) { PrefabRotation parentRotation = parentShape.getPrefabRotation(); @@ -239,7 +241,7 @@ public class PrefabCaveNodeShape implements CaveNodeShape, IWorldBounds { } origin.add(offset); - return new PrefabCaveNodeShape(caveType, origin, Vector3d.ZERO, prefab, rotation, this.configuration); + return new PrefabCaveNodeShape(caveType, origin, Vector3dUtil.ZERO, prefab, rotation, this.configuration); } } } diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/TetrahedronCaveNodeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/TetrahedronCaveNodeShape.java index 61f91604..0fda7490 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/TetrahedronCaveNodeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/TetrahedronCaveNodeShape.java @@ -1,12 +1,12 @@ package com.hypixel.hytale.server.worldgen.cave.shape; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.worldgen.cave.CaveNodeType; import com.hypixel.hytale.server.worldgen.cave.CaveType; import com.hypixel.hytale.server.worldgen.cave.element.CaveNode; import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Vector3d; @Deprecated public class TetrahedronCaveNodeShape extends AbstractCaveNodeShape implements IWorldBounds { @@ -38,12 +38,12 @@ public class TetrahedronCaveNodeShape extends AbstractCaveNodeShape implements I this.a = new Vector3d(10.0, 0.0, 0.0); this.b = new Vector3d(0.0, 10.0, 0.0); this.c = new Vector3d(0.0, 0.0, 10.0); - this.n1 = this.c.cross(this.b); - this.n2 = this.b.cross(this.a); - this.n3 = this.a.cross(this.c); - Vector3d ba = this.a.clone().subtract(this.b); - Vector3d bc = this.c.clone().subtract(this.b); - this.n4 = bc.cross(ba); + this.n1 = this.c.cross(this.b, new Vector3d()); + this.n2 = this.b.cross(this.a, new Vector3d()); + this.n3 = this.a.cross(this.c, new Vector3d()); + Vector3d ba = new Vector3d(this.a).sub(this.b); + Vector3d bc = new Vector3d(this.c).sub(this.b); + this.n4 = bc.cross(ba, new Vector3d()); this.lowBoundX = (int)(o.x - 10.0); this.lowBoundY = (int)(o.y - 10.0); this.lowBoundZ = (int)(o.z - 10.0); @@ -55,13 +55,13 @@ public class TetrahedronCaveNodeShape extends AbstractCaveNodeShape implements I @Nonnull @Override public Vector3d getStart() { - return this.o.clone(); + return new Vector3d(this.o); } @Nonnull @Override public Vector3d getEnd() { - return this.o.clone().add(this.c); + return new Vector3d(this.o).add(this.c); } @Nonnull diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedBody.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedBody.java index bfcc4499..c547fa90 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedBody.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedBody.java @@ -2,11 +2,11 @@ package com.hypixel.hytale.server.worldgen.cave.shape.distorted; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.logic.GeneralNoise; import com.hypixel.hytale.procedurallib.random.CoordinateRotator; import com.hypixel.hytale.server.worldgen.cave.CaveType; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class AbstractDistortedBody extends AbstractDistortedShape { @Nonnull diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedExtrusion.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedExtrusion.java index b5c84e9c..8193f570 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedExtrusion.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedExtrusion.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.server.worldgen.cave.shape.distorted; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.logic.GeneralNoise; import com.hypixel.hytale.server.worldgen.cave.CaveType; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class AbstractDistortedExtrusion extends AbstractDistortedShape { protected final GeneralNoise.InterpolationFunction interpolation; diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedShape.java index 622113bf..45030cca 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/AbstractDistortedShape.java @@ -1,8 +1,8 @@ package com.hypixel.hytale.server.worldgen.cave.shape.distorted; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import javax.annotation.Nonnull; +import org.joml.Vector3d; public abstract class AbstractDistortedShape implements DistortedShape { private static final double PITCH_MIN = Math.toRadians(5.0); diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedCylinderShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedCylinderShape.java index 6cbd58b8..2aa81c69 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedCylinderShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedCylinderShape.java @@ -2,10 +2,10 @@ package com.hypixel.hytale.server.worldgen.cave.shape.distorted; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.logic.GeneralNoise; import com.hypixel.hytale.server.worldgen.cave.shape.CaveNodeShapeUtils; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DistortedCylinderShape extends AbstractDistortedExtrusion { protected static final double PITCH_COMPENSATION_MIN = 1.0; @@ -74,7 +74,7 @@ public class DistortedCylinderShape extends AbstractDistortedExtrusion { @Nonnull @Override public Vector3d getStart() { - return this.o.clone(); + return new Vector3d(this.o); } @Nonnull diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedEllipsoidShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedEllipsoidShape.java index c5dab3db..4d934785 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedEllipsoidShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedEllipsoidShape.java @@ -1,10 +1,10 @@ package com.hypixel.hytale.server.worldgen.cave.shape.distorted; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.logic.GeneralNoise; import com.hypixel.hytale.server.worldgen.cave.CaveType; import com.hypixel.hytale.server.worldgen.cave.shape.CaveNodeShapeUtils; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DistortedEllipsoidShape extends AbstractDistortedBody { private final double radiusX; diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedPipeShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedPipeShape.java index 16635852..c335e6e8 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedPipeShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedPipeShape.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.server.worldgen.cave.shape.distorted; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.logic.GeneralNoise; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class DistortedPipeShape extends DistortedCylinderShape { private final double compensation; diff --git a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedShape.java b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedShape.java index db9a7c17..1d427dda 100644 --- a/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedShape.java +++ b/src/com/hypixel/hytale/server/worldgen/cave/shape/distorted/DistortedShape.java @@ -1,9 +1,9 @@ package com.hypixel.hytale.server.worldgen.cave.shape.distorted; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.logic.GeneralNoise; import com.hypixel.hytale.server.worldgen.cave.CaveType; import com.hypixel.hytale.server.worldgen.util.bounds.IWorldBounds; +import org.joml.Vector3d; public interface DistortedShape extends IWorldBounds { Vector3d getStart(); @@ -12,10 +12,6 @@ public interface DistortedShape extends IWorldBounds { Vector3d getAnchor(Vector3d var1, double var2, double var4, double var6); - default boolean hasGeometry() { - return this.getHighBoundX() > this.getLowBoundX() && this.getHighBoundY() > this.getLowBoundY() && this.getHighBoundZ() > this.getLowBoundZ(); - } - double getProjection(double var1, double var3); boolean isValidProjection(double var1); diff --git a/src/com/hypixel/hytale/server/worldgen/chunk/ChunkGenerator.java b/src/com/hypixel/hytale/server/worldgen/chunk/ChunkGenerator.java index 1fd56a36..8246abed 100644 --- a/src/com/hypixel/hytale/server/worldgen/chunk/ChunkGenerator.java +++ b/src/com/hypixel/hytale/server/worldgen/chunk/ChunkGenerator.java @@ -2,12 +2,10 @@ package com.hypixel.hytale.server.worldgen.chunk; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.logger.sentry.SkipSentryException; +import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.MathUtil; +import com.hypixel.hytale.math.vector.Rotation3f; import com.hypixel.hytale.math.vector.Transform; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3f; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricResults; import com.hypixel.hytale.procedurallib.condition.IHeightThresholdInterpreter; @@ -35,6 +33,7 @@ import com.hypixel.hytale.server.worldgen.cave.Cave; import com.hypixel.hytale.server.worldgen.cave.CaveGenerator; import com.hypixel.hytale.server.worldgen.cave.CaveType; import com.hypixel.hytale.server.worldgen.container.FadeContainer; +import com.hypixel.hytale.server.worldgen.container.PrefabContainer; import com.hypixel.hytale.server.worldgen.container.UniquePrefabContainer; import com.hypixel.hytale.server.worldgen.map.GeneratorChunkWorldMap; import com.hypixel.hytale.server.worldgen.prefab.PrefabLoadingCache; @@ -62,9 +61,14 @@ import java.util.function.Supplier; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; +import org.joml.Vector3d; +import org.joml.Vector3i; public class ChunkGenerator implements IBenchmarkableWorldGen, ValidatableWorldGen, MetricProvider, IWorldMapProvider { public static final int TINT_INTERPOLATION_RADIUS = 4; + private static final int CHUNK_BOUNDS_PADDING = 16; + private static final CompletableFuture NO_CHUNK = CompletableFuture.completedFuture(null); private static final ThreadLocal THREAD_LOCAL = ThreadLocal.withInitial(ChunkGeneratorResource::new); public static final int POOL_SIZE = Math.max(2, MathUtil.fastCeil(Runtime.getRuntime().availableProcessors() * 0.75F)); @Nonnull @@ -86,6 +90,8 @@ public class ChunkGenerator implements IBenchmarkableWorldGen, ValidatableWorldG @Nonnull private final Supplier generatedChunkSupplier; private final Path dataFolder; + private final int minChunkCoord; + private final int maxChunkCoord; public ChunkGenerator(ZonePatternProvider zonePatternProvider, Path dataFolder) { this.dataFolder = dataFolder; @@ -110,6 +116,9 @@ public class ChunkGenerator implements IBenchmarkableWorldGen, ValidatableWorldG this.prefabLoadingCache = new PrefabLoadingCache(); this.generatedChunkSupplier = GeneratedChunk::new; this.benchmark = new ChunkWorldgenBenchmark(); + int extents = getLargestFeatureChunkExtents(zonePatternProvider, 16); + this.minChunkCoord = -67108864 + extents; + this.maxChunkCoord = 67108863 - extents; } public ZonePatternProvider getZonePatternProvider() { @@ -136,10 +145,10 @@ public class ChunkGenerator implements IBenchmarkableWorldGen, ValidatableWorldG if (entry.isSpawnLocation()) { Vector3i position = entry.getPosition(); Vector3d spawnPosition = new Vector3d(entry.getSpawnOffset()); - Vector3f spawnRotation = new Vector3f(Vector3f.ZERO); + Rotation3f spawnRotation = new Rotation3f(); entry.getRotation().rotate(spawnPosition); spawnRotation.addYaw(-entry.getRotation().getYaw()); - list.add(new Transform(spawnPosition.add(position).add(0.5, 0.0, 0.5), spawnRotation)); + list.add(new Transform(spawnPosition.add(position.x, position.y, position.z).add(0.5, 0.0, 0.5), spawnRotation)); } } @@ -209,7 +218,7 @@ public class ChunkGenerator implements IBenchmarkableWorldGen, ValidatableWorldG @Nonnull @Override public CompletableFuture generate(int seed, long index, int x, int z, @Nullable LongPredicate stillNeeded) { - return CompletableFuture.supplyAsync(() -> { + return this.isChunkOutsideGeneratableArea(x, z) ? NO_CHUNK : CompletableFuture.supplyAsync(() -> { if (stillNeeded != null && !stillNeeded.test(index)) { return null; } else { @@ -434,6 +443,10 @@ public class ChunkGenerator implements IBenchmarkableWorldGen, ValidatableWorldG return THREAD_LOCAL.get(); } + public boolean isChunkOutsideGeneratableArea(int x, int z) { + return x < this.minChunkCoord || x > this.maxChunkCoord || z < this.minChunkCoord || z > this.maxChunkCoord; + } + @Override public boolean validate() { return !ValidationUtil.isInvalid(this.zonePatternProvider, this.executor); @@ -465,4 +478,26 @@ public class ChunkGenerator implements IBenchmarkableWorldGen, ValidatableWorldG public String toString() { return this.toString(true, true); } + + private static int getLargestFeatureChunkExtents(@Nonnull ZonePatternProvider zonePatternProvider, int padding) { + double max = 0.0; + + for (Zone zone : zonePatternProvider.getZones()) { + if (zone.caveGenerator() != null) { + for (CaveType cave : zone.caveGenerator().getCaveTypes()) { + max = Math.max(max, cave.getMaximumSize()); + } + } + + for (Biome biome : zone.biomePatternGenerator().getBiomes()) { + if (biome.getPrefabContainer() != null) { + for (PrefabContainer.PrefabContainerEntry entry : biome.getPrefabContainer().getEntries()) { + max = Math.max(max, (double)entry.getPrefabPatternGenerator().getMaxSize()); + } + } + } + } + + return ChunkUtil.chunkCoordinate(MathUtil.ceil(max)) + padding; + } } diff --git a/src/com/hypixel/hytale/server/worldgen/chunk/populator/PrefabPopulator.java b/src/com/hypixel/hytale/server/worldgen/chunk/populator/PrefabPopulator.java index 8625559c..d899839d 100644 --- a/src/com/hypixel/hytale/server/worldgen/chunk/populator/PrefabPopulator.java +++ b/src/com/hypixel/hytale/server/worldgen/chunk/populator/PrefabPopulator.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.FastRandom; import com.hypixel.hytale.math.util.HashUtil; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.procedurallib.condition.ConstantIntCondition; import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateRndCondition; import com.hypixel.hytale.procedurallib.condition.IBlockFluidCondition; @@ -40,6 +39,7 @@ import java.util.Objects; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PrefabPopulator { private static final UniquePrefabContainer.UniquePrefabEntry[] EMPTY_UNIQUE_PREFABS = new UniquePrefabContainer.UniquePrefabEntry[0]; @@ -186,9 +186,9 @@ public class PrefabPopulator { Vector3i v = entry.getPosition(); generatePrefabAt( seed, - v.getX(), - v.getZ(), - v.getY(), + v.x(), + v.z(), + v.y(), execution, entry.getPrefabSupplier(), entry.getConfiguration(), @@ -305,8 +305,8 @@ public class PrefabPopulator { for (UniquePrefabContainer.UniquePrefabEntry unique : uniquePrefabs) { if (priority < unique.getCategory().priority()) { - long dx = x - unique.getPosition().getX(); - long dz = z - unique.getPosition().getZ(); + long dx = x - unique.getPosition().x(); + long dz = z - unique.getPosition().z(); if (dx * dx + dz * dz <= radius2) { return true; } diff --git a/src/com/hypixel/hytale/server/worldgen/climate/ClimateSearch.java b/src/com/hypixel/hytale/server/worldgen/climate/ClimateSearch.java index 801e2e01..a687122e 100644 --- a/src/com/hypixel/hytale/server/worldgen/climate/ClimateSearch.java +++ b/src/com/hypixel/hytale/server/worldgen/climate/ClimateSearch.java @@ -2,9 +2,9 @@ package com.hypixel.hytale.server.worldgen.climate; import com.hypixel.hytale.math.util.FastRandom; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector2i; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ClimateSearch { public static final int STEP_SIZE = 100; @@ -35,7 +35,7 @@ public class ClimateSearch { int y = cy + (int)(Math.sin(t) * r); double score = collect(seed, x, y, noise, graph, rule); if (score > bestScore) { - bestPosition.assign(x, y); + bestPosition.set(x, y); bestScore = score; } } diff --git a/src/com/hypixel/hytale/server/worldgen/climate/UniqueClimateGenerator.java b/src/com/hypixel/hytale/server/worldgen/climate/UniqueClimateGenerator.java index 373083fc..9f59ebc6 100644 --- a/src/com/hypixel/hytale/server/worldgen/climate/UniqueClimateGenerator.java +++ b/src/com/hypixel/hytale/server/worldgen/climate/UniqueClimateGenerator.java @@ -1,6 +1,5 @@ package com.hypixel.hytale.server.worldgen.climate; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.worldgen.util.LogUtil; import com.hypixel.hytale.server.worldgen.zone.Zone; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -10,6 +9,7 @@ import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; public class UniqueClimateGenerator { public static final UniqueClimateGenerator EMPTY = new UniqueClimateGenerator( diff --git a/src/com/hypixel/hytale/server/worldgen/container/CoverContainer.java b/src/com/hypixel/hytale/server/worldgen/container/CoverContainer.java index ffcf6935..7ae93688 100644 --- a/src/com/hypixel/hytale/server/worldgen/container/CoverContainer.java +++ b/src/com/hypixel/hytale/server/worldgen/container/CoverContainer.java @@ -5,11 +5,13 @@ import com.hypixel.hytale.procedurallib.condition.IBlockFluidCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateRndCondition; import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class CoverContainer { + public static final ListPool ENTY_POOL = new ListPool<>(10, new CoverContainer.CoverContainerEntry[0]); protected final CoverContainer.CoverContainerEntry[] entries; public CoverContainer(CoverContainer.CoverContainerEntry[] entries) { diff --git a/src/com/hypixel/hytale/server/worldgen/container/EnvironmentContainer.java b/src/com/hypixel/hytale/server/worldgen/container/EnvironmentContainer.java index f9cc2f18..2a63bad0 100644 --- a/src/com/hypixel/hytale/server/worldgen/container/EnvironmentContainer.java +++ b/src/com/hypixel/hytale/server/worldgen/container/EnvironmentContainer.java @@ -4,10 +4,14 @@ import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.property.NoiseProperty; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.util.Arrays; import javax.annotation.Nonnull; public class EnvironmentContainer { + public static final ListPool ENTRY_POOL = new ListPool<>( + 10, new EnvironmentContainer.EnvironmentContainerEntry[0] + ); protected final EnvironmentContainer.DefaultEnvironmentContainerEntry defaultEntry; protected final EnvironmentContainer.EnvironmentContainerEntry[] entries; diff --git a/src/com/hypixel/hytale/server/worldgen/container/LayerContainer.java b/src/com/hypixel/hytale/server/worldgen/container/LayerContainer.java index 88c3edd7..830b5133 100644 --- a/src/com/hypixel/hytale/server/worldgen/container/LayerContainer.java +++ b/src/com/hypixel/hytale/server/worldgen/container/LayerContainer.java @@ -4,12 +4,15 @@ import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.supplier.IDoubleCoordinateSupplier; import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import com.hypixel.hytale.server.worldgen.util.ListPool; import com.hypixel.hytale.server.worldgen.util.NoiseBlockArray; import java.util.Arrays; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class LayerContainer { + public static final ListPool STATIC_POOL = new ListPool<>(10, new LayerContainer.StaticLayer[0]); + public static final ListPool DYNAMIC_POOL = new ListPool<>(10, new LayerContainer.DynamicLayer[0]); @Nonnull protected final BlockFluidEntry filling; protected final int fillingEnvironment; diff --git a/src/com/hypixel/hytale/server/worldgen/container/PrefabContainer.java b/src/com/hypixel/hytale/server/worldgen/container/PrefabContainer.java index 33a44fc3..e22776ce 100644 --- a/src/com/hypixel/hytale/server/worldgen/container/PrefabContainer.java +++ b/src/com/hypixel/hytale/server/worldgen/container/PrefabContainer.java @@ -4,11 +4,13 @@ import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.server.worldgen.loader.WorldGenPrefabSupplier; import com.hypixel.hytale.server.worldgen.prefab.PrefabPatternGenerator; +import com.hypixel.hytale.server.worldgen.util.ListPool; import com.hypixel.hytale.server.worldgen.util.bounds.IChunkBounds; import java.util.Arrays; import javax.annotation.Nonnull; public class PrefabContainer { + public static final ListPool ENTRY_POOL = new ListPool<>(10, new PrefabContainer.PrefabContainerEntry[0]); private final PrefabContainer.PrefabContainerEntry[] entries; private final int maxSize; diff --git a/src/com/hypixel/hytale/server/worldgen/container/TintContainer.java b/src/com/hypixel/hytale/server/worldgen/container/TintContainer.java index 332d4954..a38f3244 100644 --- a/src/com/hypixel/hytale/server/worldgen/container/TintContainer.java +++ b/src/com/hypixel/hytale/server/worldgen/container/TintContainer.java @@ -4,10 +4,12 @@ import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.property.NoiseProperty; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.util.List; import javax.annotation.Nonnull; public class TintContainer { + public static final ListPool ENTRY_POOL = new ListPool<>(10, new TintContainer.TintContainerEntry[0]); private final TintContainer.DefaultTintContainerEntry defaultEntry; private final List entries; diff --git a/src/com/hypixel/hytale/server/worldgen/container/UniquePrefabContainer.java b/src/com/hypixel/hytale/server/worldgen/container/UniquePrefabContainer.java index 17140912..ca11d6f3 100644 --- a/src/com/hypixel/hytale/server/worldgen/container/UniquePrefabContainer.java +++ b/src/com/hypixel/hytale/server/worldgen/container/UniquePrefabContainer.java @@ -1,9 +1,6 @@ package com.hypixel.hytale.server.worldgen.container; import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3d; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.prefab.PrefabRotation; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import com.hypixel.hytale.server.worldgen.chunk.ChunkGenerator; @@ -18,6 +15,9 @@ import java.util.Objects; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; +import org.joml.Vector3d; +import org.joml.Vector3i; public class UniquePrefabContainer { protected final int seedOffset; diff --git a/src/com/hypixel/hytale/server/worldgen/container/WaterContainer.java b/src/com/hypixel/hytale/server/worldgen/container/WaterContainer.java index 31d56a80..e6442c2d 100644 --- a/src/com/hypixel/hytale/server/worldgen/container/WaterContainer.java +++ b/src/com/hypixel/hytale/server/worldgen/container/WaterContainer.java @@ -3,9 +3,11 @@ package com.hypixel.hytale.server.worldgen.container; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.supplier.IDoubleCoordinateSupplier; +import com.hypixel.hytale.server.worldgen.util.ListPool; import javax.annotation.Nonnull; public class WaterContainer { + public static final ListPool ENTRY_POOL = new ListPool<>(10, new WaterContainer.Entry[0]); public static final int NO_WATER_AT_COORDINATED = Integer.MIN_VALUE; private final WaterContainer.Entry[] entries; diff --git a/src/com/hypixel/hytale/server/worldgen/loader/ChunkGeneratorJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/ChunkGeneratorJsonLoader.java index 23a94a14..a9e2d3a3 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/ChunkGeneratorJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/ChunkGeneratorJsonLoader.java @@ -4,6 +4,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.hypixel.hytale.assetstore.AssetPack; +import com.hypixel.hytale.builtin.worldgen.modifier.EventHandler; import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.common.map.WeightedMap; import com.hypixel.hytale.common.semver.Semver; @@ -11,7 +12,6 @@ import com.hypixel.hytale.common.util.ArrayUtil; import com.hypixel.hytale.common.util.PathUtil; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.math.util.FastRandom; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.procedurallib.file.FileIO; import com.hypixel.hytale.procedurallib.json.JsonLoader; import com.hypixel.hytale.procedurallib.json.Loader; @@ -32,6 +32,7 @@ import java.nio.file.Path; import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; +import org.joml.Vector2i; public class ChunkGeneratorJsonLoader extends Loader { @Nonnull @@ -54,8 +55,11 @@ public class ChunkGeneratorJsonLoader extends Loader { public static final AssetLoader IMAGE_LOADER = new AssetLoader() { @@ -67,8 +67,8 @@ public class MaskProviderJsonLoader extends JsonLoader(this.seed, this.dataFolder, this.json).load(), new PixelProvider(mask), - (double)this.zoomSize.getX() / mask.getWidth(), - (double)this.zoomSize.getY() / mask.getHeight(), + (double)this.zoomSize.x() / mask.getWidth(), + (double)this.zoomSize.y() / mask.getHeight(), this.worldOffset.x, this.worldOffset.y ); diff --git a/src/com/hypixel/hytale/server/worldgen/loader/WorldGenPrefabSupplier.java b/src/com/hypixel/hytale/server/worldgen/loader/WorldGenPrefabSupplier.java index a3ee81f2..b129e573 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/WorldGenPrefabSupplier.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/WorldGenPrefabSupplier.java @@ -37,6 +37,11 @@ public class WorldGenPrefabSupplier implements PrefabSupplier { return this.path.toString(); } + @Nonnull + public String getPrefabKey() { + return this.prefabKey; + } + @Nonnull public String getPrefabName() { if (this.prefabName == null) { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/biome/BiomeJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/biome/BiomeJsonLoader.java index df865831..e23150fa 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/biome/BiomeJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/biome/BiomeJsonLoader.java @@ -54,7 +54,7 @@ public abstract class BiomeJsonLoader extends JsonLoader(this.seed, this.dataFolder, this.get("GridGenerator")) { + { + Objects.requireNonNull(BiomePatternGeneratorJsonLoader.this); + } + @Nonnull @Override protected PointDistanceFunction loadPointDistanceFunction() { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveGeneratorJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveGeneratorJsonLoader.java index 053cc895..7c79901d 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveGeneratorJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveGeneratorJsonLoader.java @@ -50,6 +50,7 @@ public class CaveGeneratorJsonLoader extends JsonLoader { protected final CaveNodeTypeStorage storage; @@ -98,9 +98,9 @@ public class CaveNodeChildEntryJsonLoader extends JsonLoader seed, Path dataFolder, JsonElement json, String name, CaveNodeTypeStorage storage, ZoneFileContext zoneContext @@ -47,6 +52,7 @@ public class CaveNodeTypeJsonLoader extends JsonLoader entries = CaveNodeType.CHILD_POOL.acquire()) { JsonElement childrenElement = this.get("Children"); - if (childrenElement.isJsonArray()) { - JsonArray childrenArray = childrenElement.getAsJsonArray(); - CaveNodeType.CaveNodeChildEntry[] children = new CaveNodeType.CaveNodeChildEntry[childrenArray.size()]; + if (childrenElement != null) { + if (childrenElement.isJsonArray()) { + JsonArray childrenArray = childrenElement.getAsJsonArray(); - for (int i = 0; i < childrenArray.size(); i++) { - children[i] = new CaveNodeChildEntryJsonLoader( - this.seed.append(String.format(".Child-%s", i)), this.dataFolder, this.getOrLoad(childrenArray.get(i)), this.storage - ) - .load(); + for (int i = 0; i < childrenArray.size(); i++) { + entries.add( + new CaveNodeChildEntryJsonLoader( + this.seed.append(String.format(".Child-%s", i)), this.dataFolder, this.getOrLoad(childrenArray.get(i)), this.storage + ) + .load() + ); + } + } else { + entries.add( + new CaveNodeChildEntryJsonLoader( + this.seed.append(String.format(".Child-%s", 0)), this.dataFolder, this.getOrLoad(childrenElement), this.storage + ) + .load() + ); } - - return children; } - if (childrenElement.isJsonObject()) { - return new CaveNodeType.CaveNodeChildEntry[]{ - new CaveNodeChildEntryJsonLoader(this.seed.append(String.format(".Child-%s", 0)), this.dataFolder, this.getOrLoad(childrenElement), this.storage) - .load() - }; - } + var7 = entries.toArray(); } - return CaveNodeType.CaveNodeChildEntry.EMPTY_ARRAY; + return var7; } @Nullable @@ -101,7 +111,8 @@ public class CaveNodeTypeJsonLoader extends JsonLoader entries = CaveNodeType.COVER_POOL.acquire()) { JsonElement coverElement = this.get("Cover"); - if (coverElement.isJsonArray()) { - JsonArray coverArray = coverElement.getAsJsonArray(); - entries = new CaveNodeType.CaveNodeCoverEntry[coverArray.size()]; + if (coverElement != null) { + if (coverElement.isJsonArray()) { + JsonArray coverArray = coverElement.getAsJsonArray(); - for (int i = 0; i < entries.length; i++) { - entries[i] = new CaveNodeCoverEntryJsonLoader(this.seed.append(String.format("-cover#%s", i)), this.dataFolder, coverArray.get(i)).load(); + for (int i = 0; i < coverArray.size(); i++) { + entries.add(new CaveNodeCoverEntryJsonLoader(this.seed.append(String.format("-cover#%s", i)), this.dataFolder, coverArray.get(i)).load()); + } + } else { + entries.add(new CaveNodeCoverEntryJsonLoader(this.seed, this.dataFolder, coverElement).load()); } - } else { - entries = new CaveNodeType.CaveNodeCoverEntry[]{new CaveNodeCoverEntryJsonLoader(this.seed, this.dataFolder, coverElement).load()}; } + + ModifyEvent.SeedGenerator seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.CaveCovers.class, + new ModifyEvents.CaveCovers( + this.seed, this.caveContext, entries, path -> new CaveNodeCoverEntryJsonLoader(seed.next(), this.dataFolder, this.loadFile(path)).load() + ) + ); + var8 = entries.toArray(); } - return entries; + return var8; } protected int loadPriority() { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/cave/CavePrefabContainerJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/cave/CavePrefabContainerJsonLoader.java index 40268ab3..f878e433 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/cave/CavePrefabContainerJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/cave/CavePrefabContainerJsonLoader.java @@ -2,20 +2,23 @@ package com.hypixel.hytale.server.worldgen.loader.cave; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; import com.hypixel.hytale.procedurallib.json.JsonLoader; import com.hypixel.hytale.procedurallib.json.SeedString; import com.hypixel.hytale.server.worldgen.SeedStringResource; import com.hypixel.hytale.server.worldgen.cave.prefab.CavePrefabContainer; -import com.hypixel.hytale.server.worldgen.loader.context.ZoneFileContext; +import com.hypixel.hytale.server.worldgen.loader.context.CaveFileContext; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.nio.file.Path; import javax.annotation.Nonnull; public class CavePrefabContainerJsonLoader extends JsonLoader { - private final ZoneFileContext zoneContext; + protected final CaveFileContext context; - public CavePrefabContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json, ZoneFileContext zoneContext) { + public CavePrefabContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json, CaveFileContext context) { super(seed.append(".CavePrefabContainer"), dataFolder, json); - this.zoneContext = zoneContext; + this.context = context; } @Nonnull @@ -25,20 +28,33 @@ public class CavePrefabContainerJsonLoader extends JsonLoader entries = CavePrefabContainer.ENTRY_POOL.acquire()) { + if (this.json != null) { + JsonArray prefabArray = this.mustGetArray("Entries", null); - for (int i = 0; i < entries.length; i++) { - entries[i] = new CavePrefabEntryJsonLoader(this.seed.append(String.format("-%s", i)), this.dataFolder, array.get(i), this.zoneContext).load(); + for (int i = 0; i < prefabArray.size(); i++) { + entries.add( + new CavePrefabEntryJsonLoader(this.seed.append(String.format("-%s", i)), this.dataFolder, prefabArray.get(i), this.context.getParentContext()) + .load() + ); + } } - return entries; + ModifyEvent.SeedGenerator seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.CavePrefabs.class, + new ModifyEvents.CavePrefabs( + this.seed, + this.context, + entries, + path -> new CavePrefabEntryJsonLoader(seed.next(), this.dataFolder, this.loadFile(path), this.context.getParentContext()).load() + ) + ); + var7 = entries.toArray(); } + + return var7; } public interface Constants { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveTypeJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveTypeJsonLoader.java index c6f05e6c..7f72b2b3 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveTypeJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveTypeJsonLoader.java @@ -6,6 +6,7 @@ import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition; import com.hypixel.hytale.procedurallib.condition.HeightThresholdCoordinateCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.condition.IHeightThresholdInterpreter; +import com.hypixel.hytale.procedurallib.file.FileIO; import com.hypixel.hytale.procedurallib.json.DoubleRangeJsonLoader; import com.hypixel.hytale.procedurallib.json.FloatRangeJsonLoader; import com.hypixel.hytale.procedurallib.json.HeightThresholdInterpreterJsonLoader; @@ -28,6 +29,7 @@ import com.hypixel.hytale.server.worldgen.loader.prefab.BlockPlacementMaskJsonLo import com.hypixel.hytale.server.worldgen.util.ConstantNoiseProperty; import com.hypixel.hytale.server.worldgen.util.condition.BlockMaskCondition; import com.hypixel.hytale.server.worldgen.util.condition.flag.Int2FlagsCondition; +import java.io.File; import java.nio.file.Path; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -96,17 +98,28 @@ public class CaveTypeJsonLoader extends JsonLoader JsonElement entry = this.get("Entry"); if (entry == null) { throw new IllegalArgumentException("\"Entry\" is not defined. Define an entry node type"); - } else { - CaveNodeTypeStorage caveNodeTypeStorage = new CaveNodeTypeStorage(this.seed, this.dataFolder, this.caveFolder, this.zoneContext); - if (entry.isJsonObject()) { - String entryNodeTypeString = this.seed.get().getUniqueName("CaveType#"); - return caveNodeTypeStorage.loadCaveNodeType(entryNodeTypeString, entry.getAsJsonObject()); - } else if (entry.isJsonPrimitive() && entry.getAsJsonPrimitive().isString()) { - String entryNodeTypeString = entry.getAsString(); - return caveNodeTypeStorage.loadCaveNodeType(entryNodeTypeString); - } else { - throw error("Invalid entry node type definition! Expected String or JsonObject: " + entry); + } else if (entry.isJsonObject()) { + String entryNodeTypeString = this.seed.get().getUniqueName("CaveType#"); + return new CaveNodeTypeStorage(this.seed, this.dataFolder, this.caveFolder, this.zoneContext) + .loadCaveNodeType(entryNodeTypeString, entry.getAsJsonObject()); + } else if (entry.isJsonPrimitive() && entry.getAsJsonPrimitive().isString()) { + Path caveFolder = this.caveFolder; + ZoneFileContext zoneContext = this.zoneContext; + String entryNodeTypeString = entry.getAsString(); + if (entryNodeTypeString.startsWith("Zones.")) { + Path filepath = this.dataFolder.resolve(entryNodeTypeString.replace(".", File.separator)); + Path relPath = FileIO.relativize(filepath, this.dataFolder); + if (relPath.getNameCount() > 1) { + String zoneName = relPath.getName(1).toString(); + zoneContext = zoneContext.getParentContext().getZones().get(zoneName); + caveFolder = zoneContext.getPath().resolve("Cave"); + entryNodeTypeString = FileIO.relativize(filepath, caveFolder).toString().replace(File.separator, "."); + } } + + return new CaveNodeTypeStorage(this.seed, this.dataFolder, caveFolder, zoneContext).loadCaveNodeType(entryNodeTypeString); + } else { + throw error("Invalid entry node type definition! Expected String or JsonObject: " + entry); } } diff --git a/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveTypesJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveTypesJsonLoader.java index 10c8b078..2604fd15 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveTypesJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/cave/CaveTypesJsonLoader.java @@ -3,54 +3,72 @@ package com.hypixel.hytale.server.worldgen.loader.cave; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; import com.hypixel.hytale.procedurallib.json.JsonLoader; import com.hypixel.hytale.procedurallib.json.SeedString; import com.hypixel.hytale.server.worldgen.SeedStringResource; import com.hypixel.hytale.server.worldgen.cave.CaveType; +import com.hypixel.hytale.server.worldgen.loader.context.CaveFileContext; import com.hypixel.hytale.server.worldgen.loader.context.ZoneFileContext; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.nio.file.Path; import javax.annotation.Nonnull; public class CaveTypesJsonLoader extends JsonLoader { protected final Path caveFolder; protected final ZoneFileContext zoneContext; + protected final CaveFileContext caveContext; public CaveTypesJsonLoader(SeedString seed, Path dataFolder, JsonElement json, Path caveFolder, ZoneFileContext zoneContext) { super(seed, dataFolder, json); this.caveFolder = caveFolder; this.zoneContext = zoneContext; + this.caveContext = new CaveFileContext("Caves", zoneContext); } @Nonnull public CaveType[] load() { if (this.json != null && this.json.isJsonArray()) { JsonArray typesArray = this.json.getAsJsonArray(); - CaveType[] caveTypes = new CaveType[typesArray.size()]; - for (int i = 0; i < typesArray.size(); i++) { - JsonElement entry = this.getOrLoad(typesArray.get(i)); - if (!entry.isJsonObject()) { - throw error("Expected CaveType entry to be a JsonObject at index: %d", i); + CaveType[] var7; + try (ListPool.Resource entries = CaveType.ENTRY_POOL.acquire(typesArray.size())) { + for (int i = 0; i < typesArray.size(); i++) { + JsonElement entry = this.getOrLoad(typesArray.get(i)); + if (!entry.isJsonObject()) { + throw error("Expected CaveType entry to be a JsonObject at index: %d", i); + } + + entries.add(this.loadCaveType(entry.getAsJsonObject())); } - JsonObject caveTypeJson = entry.getAsJsonObject(); - String name = this.loadName(caveTypeJson); - caveTypes[i] = this.loadCaveType(name, caveTypeJson); + ModifyEvent.dispatch( + ModifyEvents.CaveTypes.class, + new ModifyEvents.CaveTypes(this.seed, this.caveContext, entries, path -> this.loadCaveType(this.loadFile(path).getAsJsonObject())) + ); + var7 = entries.toArray(); } - return caveTypes; + return var7; } else { throw new IllegalArgumentException("CaveTypes must be a JSON array."); } } @Nonnull - protected CaveType loadCaveType(String name, JsonElement json) { - return new CaveTypeJsonLoader(this.seed.append(String.format("-%s", name)), this.dataFolder, json, this.caveFolder, name, this.zoneContext).load(); - } - - protected String loadName(@Nonnull JsonObject jsonObject) { - return jsonObject.get("Name").getAsString(); + protected CaveType loadCaveType(JsonObject json) { + JsonElement name = json.get("Name"); + if (name == null) { + throw error("CaveType is missing the 'Name' property"); + } else if (name.isJsonPrimitive() && name.getAsJsonPrimitive().isString()) { + return new CaveTypeJsonLoader( + this.seed.append(String.format("-%s", name)), this.dataFolder, json, this.caveFolder, name.getAsString(), this.zoneContext + ) + .load(); + } else { + throw error("CaveType 'Name' property is not a string"); + } } public interface Constants { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/climate/UniqueClimateJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/climate/UniqueClimateJsonLoader.java index f36aa3ce..0cb7eec4 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/climate/UniqueClimateJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/climate/UniqueClimateJsonLoader.java @@ -1,7 +1,6 @@ package com.hypixel.hytale.server.worldgen.loader.climate; import com.google.gson.JsonElement; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.procedurallib.json.JsonLoader; import com.hypixel.hytale.procedurallib.json.SeedResource; import com.hypixel.hytale.procedurallib.json.SeedString; @@ -11,6 +10,7 @@ import com.hypixel.hytale.server.worldgen.loader.util.ColorUtil; import java.nio.file.Path; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; public class UniqueClimateJsonLoader extends JsonLoader { public UniqueClimateJsonLoader(SeedString seed, Path dataFolder, @Nullable JsonElement json) { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/container/CoverContainerJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/container/CoverContainerJsonLoader.java index 56ec546c..fd908bee 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/container/CoverContainerJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/container/CoverContainerJsonLoader.java @@ -3,6 +3,8 @@ package com.hypixel.hytale.server.worldgen.loader.container; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.common.map.WeightedMap; import com.hypixel.hytale.procedurallib.condition.ConstantBlockFluidCondition; @@ -17,8 +19,10 @@ import com.hypixel.hytale.procedurallib.json.NoiseMaskConditionJsonLoader; import com.hypixel.hytale.procedurallib.json.SeedString; import com.hypixel.hytale.server.worldgen.SeedStringResource; import com.hypixel.hytale.server.worldgen.container.CoverContainer; +import com.hypixel.hytale.server.worldgen.loader.context.BiomeFileContext; import com.hypixel.hytale.server.worldgen.loader.util.ResolvedBlockArrayJsonLoader; import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry; +import com.hypixel.hytale.server.worldgen.util.ListPool; import com.hypixel.hytale.server.worldgen.util.ResolvedBlockArray; import com.hypixel.hytale.server.worldgen.util.condition.HashSetBlockFluidCondition; import it.unimi.dsi.fastutil.longs.LongSet; @@ -27,32 +31,44 @@ import java.util.Arrays; import javax.annotation.Nonnull; public class CoverContainerJsonLoader extends JsonLoader { - public CoverContainerJsonLoader(SeedString seed, Path dataFolder, JsonElement json) { + @Nonnull + protected final BiomeFileContext biomeContext; + + public CoverContainerJsonLoader(SeedString seed, Path dataFolder, JsonElement json, @Nonnull BiomeFileContext biomeContext) { super(seed, dataFolder, json); + this.biomeContext = biomeContext; } @Nonnull public CoverContainer load() { - CoverContainer.CoverContainerEntry[] coverContainerEntries; - if (this.json == null || this.json.isJsonNull()) { - coverContainerEntries = new CoverContainer.CoverContainerEntry[0]; - } else if (this.json.isJsonArray()) { - JsonArray coversArray = this.json.getAsJsonArray(); - coverContainerEntries = new CoverContainer.CoverContainerEntry[coversArray.size()]; + CoverContainer var9; + try (ListPool.Resource entries = CoverContainer.ENTY_POOL.acquire()) { + if (this.json != null && this.json.isJsonArray()) { + JsonArray coversArray = this.json.getAsJsonArray(); - for (int i = 0; i < coverContainerEntries.length; i++) { - JsonObject coversObject = coversArray.get(i).getAsJsonObject(); - coverContainerEntries[i] = new CoverContainerJsonLoader.CoverContainerEntryJsonLoader(this.seed.append("-" + i), this.dataFolder, coversObject) - .load(); + for (int i = 0; i < coversArray.size(); i++) { + JsonObject coversObject = coversArray.get(i).getAsJsonObject(); + entries.add(new CoverContainerJsonLoader.CoverContainerEntryJsonLoader(this.seed.append("-" + i), this.dataFolder, coversObject).load()); + } + } else if (this.json != null && this.json.isJsonObject()) { + JsonObject coversObject = this.json.getAsJsonObject(); + entries.add(new CoverContainerJsonLoader.CoverContainerEntryJsonLoader(this.seed.append("-0"), this.dataFolder, coversObject).load()); } - } else { - JsonObject coversObject = this.json.getAsJsonObject(); - coverContainerEntries = new CoverContainer.CoverContainerEntry[]{ - new CoverContainerJsonLoader.CoverContainerEntryJsonLoader(this.seed.append("-0"), this.dataFolder, coversObject).load() - }; + + ModifyEvent.SeedGenerator seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.BiomeCovers.class, + new ModifyEvents.BiomeCovers( + this.seed, + this.biomeContext, + entries, + path -> new CoverContainerJsonLoader.CoverContainerEntryJsonLoader(seed.next(), this.dataFolder, this.loadFile(path)).load() + ) + ); + var9 = new CoverContainer(entries.toArray()); } - return new CoverContainer(coverContainerEntries); + return var9; } public interface Constants { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/container/EnvironmentContainerJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/container/EnvironmentContainerJsonLoader.java index 825bf82a..7ea9f836 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/container/EnvironmentContainerJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/container/EnvironmentContainerJsonLoader.java @@ -2,6 +2,8 @@ package com.hypixel.hytale.server.worldgen.loader.container; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.common.map.WeightedMap; import com.hypixel.hytale.common.util.ArrayUtil; @@ -14,14 +16,20 @@ import com.hypixel.hytale.procedurallib.property.NoiseProperty; import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.worldgen.SeedStringResource; import com.hypixel.hytale.server.worldgen.container.EnvironmentContainer; +import com.hypixel.hytale.server.worldgen.loader.context.BiomeFileContext; import com.hypixel.hytale.server.worldgen.util.ConstantNoiseProperty; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.nio.file.Path; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class EnvironmentContainerJsonLoader extends JsonLoader { - public EnvironmentContainerJsonLoader(SeedString seed, Path dataFolder, JsonElement json) { + @Nonnull + protected final BiomeFileContext biomeContext; + + public EnvironmentContainerJsonLoader(SeedString seed, Path dataFolder, JsonElement json, @Nonnull BiomeFileContext biomeContext) { super(seed, dataFolder, json); + this.biomeContext = biomeContext; } @Nonnull @@ -47,29 +55,37 @@ public class EnvironmentContainerJsonLoader extends JsonLoader entries = EnvironmentContainer.ENTRY_POOL.acquire(envArray.size())) { + for (int i = 0; i < envArray.size(); i++) { + try { + entries.add( + new EnvironmentContainerJsonLoader.EnvironmentContainerEntryJsonLoader( + this.seed.append(String.format("-%s", i)), this.dataFolder, envArray.get(i) ) - .load(); - } catch (Throwable var5) { - throw new Error(String.format("Failed to load TintContainerEntry #%s", i), var5); - } + .load() + ); + } catch (Throwable var6) { + throw new Error(String.format("Failed to load TintContainerEntry #%s", i), var6); } - - return entries; } - } else { - return EnvironmentContainer.EnvironmentContainerEntry.EMPTY_ARRAY; + + ModifyEvent.SeedGenerator seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.BiomeEnvironments.class, + new ModifyEvents.BiomeEnvironments( + this.seed, + this.biomeContext, + entries, + path -> new EnvironmentContainerJsonLoader.EnvironmentContainerEntryJsonLoader(seed.next(), this.dataFolder, this.loadFile(path)).load() + ) + ); + e = entries.toArray(); } + + return e; } public interface Constants { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/container/LayerContainerJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/container/LayerContainerJsonLoader.java index 788254b2..959ea45c 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/container/LayerContainerJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/container/LayerContainerJsonLoader.java @@ -2,6 +2,8 @@ package com.hypixel.hytale.server.worldgen.loader.container; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.json.DoubleRangeJsonLoader; import com.hypixel.hytale.procedurallib.json.JsonLoader; @@ -17,16 +19,22 @@ import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.worldgen.SeedStringResource; import com.hypixel.hytale.server.worldgen.container.LayerContainer; +import com.hypixel.hytale.server.worldgen.loader.context.BiomeFileContext; import com.hypixel.hytale.server.worldgen.loader.util.NoiseBlockArrayJsonLoader; import com.hypixel.hytale.server.worldgen.util.ConstantNoiseProperty; +import com.hypixel.hytale.server.worldgen.util.ListPool; import com.hypixel.hytale.server.worldgen.util.NoiseBlockArray; import java.nio.file.Path; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class LayerContainerJsonLoader extends JsonLoader { - public LayerContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json) { + @Nonnull + protected final BiomeFileContext biomeContext; + + public LayerContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json, @Nonnull BiomeFileContext biomeContext) { super(seed.append(".LayerContainer"), dataFolder, json); + this.biomeContext = biomeContext; } @Nonnull @@ -63,42 +71,62 @@ public class LayerContainerJsonLoader extends JsonLoader entries = LayerContainer.STATIC_POOL.acquire(layerArray.size())) { + for (int i = 0; i < layerArray.size(); i++) { try { - layers[i] = new LayerContainerJsonLoader.StaticLayerJsonLoader(this.seed.append("-" + i), this.dataFolder, array.get(i)).load(); - } catch (Throwable var5) { - throw new Error(String.format("Error while loading StaticLayer #%s", i), var5); + entries.add(new LayerContainerJsonLoader.StaticLayerJsonLoader(this.seed.append("-" + i), this.dataFolder, layerArray.get(i)).load()); + } catch (Throwable var6) { + throw new Error(String.format("Error while loading StaticLayer #%s", i), var6); } } - return layers; + ModifyEvent.SeedGenerator seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.BiomeStaticLayers.class, + new ModifyEvents.BiomeStaticLayers( + this.seed, + this.biomeContext, + entries, + path -> new LayerContainerJsonLoader.StaticLayerJsonLoader(seed.next(), this.dataFolder, this.loadFile(path)).load() + ) + ); + e = entries.toArray(); } + + return e; } @Nonnull protected LayerContainer.DynamicLayer[] loadDynamicLayers() { - if (!this.has("Dynamic")) { - return new LayerContainer.DynamicLayer[0]; - } else { - JsonArray array = this.get("Dynamic").getAsJsonArray(); - LayerContainer.DynamicLayer[] layers = new LayerContainer.DynamicLayer[array.size()]; + JsonArray layerArray = this.mustGetArray("Dynamic", EMPTY_ARRAY); - for (int i = 0; i < layers.length; i++) { + LayerContainer.DynamicLayer[] e; + try (ListPool.Resource entries = LayerContainer.DYNAMIC_POOL.acquire(layerArray.size())) { + for (int i = 0; i < layerArray.size(); i++) { try { - layers[i] = new LayerContainerJsonLoader.DynamicLayerJsonLoader(this.seed.append("-" + i), this.dataFolder, array.get(i)).load(); - } catch (Throwable var5) { - throw new Error(String.format("Error while loading DynamicLayer #%s", i), var5); + entries.add(new LayerContainerJsonLoader.DynamicLayerJsonLoader(this.seed.append("-" + i), this.dataFolder, layerArray.get(i)).load()); + } catch (Throwable var6) { + throw new Error(String.format("Error while loading DynamicLayer #%s", i), var6); } } - return layers; + ModifyEvent.SeedGenerator seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.BiomeDynamicLayers.class, + new ModifyEvents.BiomeDynamicLayers( + this.seed, + this.biomeContext, + entries, + path -> new LayerContainerJsonLoader.DynamicLayerJsonLoader(seed.next(), this.dataFolder, this.loadFile(path)).load() + ) + ); + e = entries.toArray(); } + + return e; } public interface Constants { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/container/PrefabContainerJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/container/PrefabContainerJsonLoader.java index 00d9caf0..ebfff68e 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/container/PrefabContainerJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/container/PrefabContainerJsonLoader.java @@ -2,6 +2,8 @@ package com.hypixel.hytale.server.worldgen.loader.container; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.procedurallib.json.JsonLoader; import com.hypixel.hytale.procedurallib.json.SeedString; @@ -9,19 +11,25 @@ import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.worldgen.SeedStringResource; import com.hypixel.hytale.server.worldgen.container.PrefabContainer; import com.hypixel.hytale.server.worldgen.loader.WorldGenPrefabSupplier; +import com.hypixel.hytale.server.worldgen.loader.context.BiomeFileContext; import com.hypixel.hytale.server.worldgen.loader.context.FileLoadingContext; import com.hypixel.hytale.server.worldgen.loader.prefab.PrefabPatternGeneratorJsonLoader; import com.hypixel.hytale.server.worldgen.loader.prefab.WeightedPrefabMapJsonLoader; import com.hypixel.hytale.server.worldgen.prefab.PrefabPatternGenerator; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.nio.file.Path; import javax.annotation.Nonnull; public class PrefabContainerJsonLoader extends JsonLoader { - private final FileLoadingContext context; + @Nonnull + protected final BiomeFileContext biomeContext; + @Nonnull + protected final FileLoadingContext fileContext; - public PrefabContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json, FileLoadingContext context) { + public PrefabContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json, @Nonnull BiomeFileContext biomeContext) { super(seed.append(".PrefabContainer"), dataFolder, json); - this.context = context; + this.biomeContext = biomeContext; + this.fileContext = biomeContext.getParentContext().getParentContext(); } @Nonnull @@ -31,25 +39,35 @@ public class PrefabContainerJsonLoader extends JsonLoader entries = PrefabContainer.ENTRY_POOL.acquire()) { + for (int i = 0; i < prefabArray.size(); i++) { try { - entries[i] = new PrefabContainerJsonLoader.PrefabContainerEntryJsonLoader( - this.seed.append("-" + i), this.dataFolder, entryArray.get(i), this.context - ) - .load(); - } catch (Throwable var5) { - throw new Error(String.format("Failed to load prefab container entry #%s.", i), var5); + entries.add( + new PrefabContainerJsonLoader.PrefabContainerEntryJsonLoader(this.seed.append("-" + i), this.dataFolder, prefabArray.get(i), this.fileContext) + .load() + ); + } catch (Throwable var6) { + throw new Error(String.format("Failed to load prefab container entry #%s.", i), var6); } } - return entries; + ModifyEvent.SeedGenerator seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.BiomePrefabs.class, + new ModifyEvents.BiomePrefabs( + this.seed, + this.biomeContext, + entries, + path -> new PrefabContainerJsonLoader.PrefabContainerEntryJsonLoader(seed.next(), this.dataFolder, this.loadFile(path), this.fileContext).load() + ) + ); + e = entries.toArray(); } + + return e; } public interface Constants { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/container/TintContainerJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/container/TintContainerJsonLoader.java index 5a35e0f3..1dd25241 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/container/TintContainerJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/container/TintContainerJsonLoader.java @@ -2,6 +2,8 @@ package com.hypixel.hytale.server.worldgen.loader.container; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.common.map.WeightedMap; import com.hypixel.hytale.common.util.ArrayUtil; @@ -13,18 +15,23 @@ import com.hypixel.hytale.procedurallib.json.SeedString; import com.hypixel.hytale.procedurallib.property.NoiseProperty; import com.hypixel.hytale.server.worldgen.SeedStringResource; import com.hypixel.hytale.server.worldgen.container.TintContainer; +import com.hypixel.hytale.server.worldgen.loader.context.BiomeFileContext; import com.hypixel.hytale.server.worldgen.loader.util.ColorUtil; import com.hypixel.hytale.server.worldgen.util.ConstantNoiseProperty; +import com.hypixel.hytale.server.worldgen.util.ListPool; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class TintContainerJsonLoader extends JsonLoader { - public TintContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json) { + @Nonnull + private final BiomeFileContext biomeContext; + + public TintContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json, @Nonnull BiomeFileContext biomeContext) { super(seed.append(".TintContainer"), dataFolder, json); + this.biomeContext = biomeContext; } @Nonnull @@ -50,24 +57,34 @@ public class TintContainerJsonLoader extends JsonLoader loadEntries() { - if (!this.has("Entries")) { - return Collections.emptyList(); - } else { - JsonArray arr = this.get("Entries").getAsJsonArray(); - List entries = new ArrayList<>(arr.size()); + JsonArray tintArray = this.mustGetArray("Entries", EMPTY_ARRAY); - for (int i = 0; i < arr.size(); i++) { + ObjectArrayList e; + try (ListPool.Resource entries = TintContainer.ENTRY_POOL.acquire(tintArray.size())) { + for (int i = 0; i < tintArray.size(); i++) { try { entries.add( - new TintContainerJsonLoader.TintContainerEntryJsonLoader(this.seed.append(String.format("-%s", i)), this.dataFolder, arr.get(i)).load() + new TintContainerJsonLoader.TintContainerEntryJsonLoader(this.seed.append(String.format("-%s", i)), this.dataFolder, tintArray.get(i)).load() ); - } catch (Throwable var5) { - throw new Error(String.format("Failed to load TintContainerEntry #%s", i), var5); + } catch (Throwable var6) { + throw new Error(String.format("Failed to load TintContainerEntry #%s", i), var6); } } - return entries; + ModifyEvent.SeedGenerator seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.BiomeTints.class, + new ModifyEvents.BiomeTints( + this.seed, + this.biomeContext, + entries, + path -> new TintContainerJsonLoader.TintContainerEntryJsonLoader(seed.next(), this.dataFolder, this.loadFile(path)).load() + ) + ); + e = new ObjectArrayList<>(entries); } + + return e; } public interface Constants { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/container/WaterContainerJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/container/WaterContainerJsonLoader.java index 74230ba1..7de49253 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/container/WaterContainerJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/container/WaterContainerJsonLoader.java @@ -2,6 +2,8 @@ package com.hypixel.hytale.server.worldgen.loader.container; import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvent; +import com.hypixel.hytale.builtin.worldgen.modifier.event.ModifyEvents; import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.json.DoubleRangeJsonLoader; @@ -18,24 +20,33 @@ import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; import com.hypixel.hytale.server.worldgen.SeedStringResource; import com.hypixel.hytale.server.worldgen.container.WaterContainer; +import com.hypixel.hytale.server.worldgen.loader.context.BiomeFileContext; import com.hypixel.hytale.server.worldgen.util.ConstantNoiseProperty; +import com.hypixel.hytale.server.worldgen.util.ListPool; import java.nio.file.Path; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class WaterContainerJsonLoader extends JsonLoader { - public WaterContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json) { + @Nonnull + private final BiomeFileContext biomeContext; + + public WaterContainerJsonLoader(@Nonnull SeedString seed, Path dataFolder, JsonElement json, @Nonnull BiomeFileContext biomeContext) { super(seed.append(".WaterContainer"), dataFolder, json); + this.biomeContext = biomeContext; } @Nonnull public WaterContainer load() { - if (this.has("Block")) { - String blockString = this.get("Block").getAsString(); - int index = BlockType.getAssetMap().getIndex(blockString); - if (index == Integer.MIN_VALUE) { - throw new Error(String.format("Could not find Fluid for fluid: %s", blockString.toString())); - } else { + WaterContainer var15; + try (ListPool.Resource entries = WaterContainer.ENTRY_POOL.acquire()) { + if (this.has("Block")) { + String blockString = this.get("Block").getAsString(); + int index = BlockType.getAssetMap().getIndex(blockString); + if (index == Integer.MIN_VALUE) { + throw new Error(String.format("Could not find Fluid for fluid: %s", blockString.toString())); + } + IDoubleRange array = new DoubleRangeJsonLoader<>(this.seed, this.dataFolder, this.get("Height"), 0.0).load(); NoiseProperty heightmapNoise = ConstantNoiseProperty.DEFAULT_ZERO; if (this.has("Heightmap")) { @@ -43,24 +54,18 @@ public class WaterContainerJsonLoader extends JsonLoader(this.seed, this.dataFolder, this.get("Height"), 0.0).load(); NoiseProperty heightmapNoise = ConstantNoiseProperty.DEFAULT_ZERO; if (this.has("Heightmap")) { @@ -68,48 +73,44 @@ public class WaterContainerJsonLoader extends JsonLoader seed = new ModifyEvent.SeedGenerator<>(this.seed); + ModifyEvent.dispatch( + ModifyEvents.BiomeFluids.class, + new ModifyEvents.BiomeFluids( + this.seed, + this.biomeContext, + entries, + path -> new WaterContainerJsonLoader.WaterContainerEntryJsonLoader(seed.next(), this.dataFolder, this.loadFile(path)).load() + ) + ); + var15 = new WaterContainer(entries.toArray()); } + + return var15; } public interface Constants { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/context/BiomeFileContext.java b/src/com/hypixel/hytale/server/worldgen/loader/context/BiomeFileContext.java index 1d48f6ce..b1000d5f 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/context/BiomeFileContext.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/context/BiomeFileContext.java @@ -7,7 +7,7 @@ import javax.annotation.Nonnull; public class BiomeFileContext extends FileContext { private final BiomeFileContext.Type type; - public BiomeFileContext(int id, String name, Path filepath, BiomeFileContext.Type type, ZoneFileContext parent) { + public BiomeFileContext(int id, @Nonnull String name, @Nonnull Path filepath, @Nonnull BiomeFileContext.Type type, @Nonnull ZoneFileContext parent) { super(id, name, filepath, parent); this.type = type; } diff --git a/src/com/hypixel/hytale/server/worldgen/loader/context/CaveFileContext.java b/src/com/hypixel/hytale/server/worldgen/loader/context/CaveFileContext.java new file mode 100644 index 00000000..1ac425b7 --- /dev/null +++ b/src/com/hypixel/hytale/server/worldgen/loader/context/CaveFileContext.java @@ -0,0 +1,19 @@ +package com.hypixel.hytale.server.worldgen.loader.context; + +import java.nio.file.Path; +import javax.annotation.Nonnull; + +public class CaveFileContext extends FileContext { + public CaveFileContext(@Nonnull String name, @Nonnull ZoneFileContext parentContext) { + super(0, name, resolvePath(parentContext, name), parentContext); + } + + public CaveFileContext(@Nonnull String name, @Nonnull Path relativePath, @Nonnull ZoneFileContext parentContext) { + super(0, name, relativePath, parentContext); + } + + private static Path resolvePath(@Nonnull ZoneFileContext context, @Nonnull String name) { + String filepath = name.replace(".", context.getPath().getFileSystem().getSeparator()); + return context.getPath().resolve("Cave").resolve(filepath); + } +} diff --git a/src/com/hypixel/hytale/server/worldgen/loader/context/FileContext.java b/src/com/hypixel/hytale/server/worldgen/loader/context/FileContext.java index c2b84933..aca3761c 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/context/FileContext.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/context/FileContext.java @@ -3,17 +3,23 @@ package com.hypixel.hytale.server.worldgen.loader.context; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Iterator; import java.util.Map.Entry; import javax.annotation.Nonnull; -public class FileContext { +public class FileContext> { private final int id; + @Nonnull private final String name; + @Nonnull private final Path filepath; + @Nonnull private final T parentContext; + private transient String rootPath = null; + private transient String contentPath = null; - public FileContext(int id, String name, Path filepath, T parentContext) { + public FileContext(int id, @Nonnull String name, @Nonnull Path filepath, @Nonnull T parentContext) { this.id = id; this.name = name; this.filepath = filepath; @@ -24,18 +30,75 @@ public class FileContext { return this.id; } + @Nonnull public String getName() { return this.name; } + @Nonnull public Path getPath() { return this.filepath; } + @Nonnull + public String getRootPath() { + if (this.rootPath == null) { + this.rootPath = this.getRoot().filepath.getFileName().toString(); + } + + return this.rootPath; + } + + @Nonnull public T getParentContext() { return this.parentContext; } + @Nonnull + public String getContentPath() { + if (this.contentPath == null) { + this.contentPath = toContentPath(this.filepath, this.parentContext); + } + + return this.contentPath; + } + + @Nonnull + public FileContext getRoot() { + FileContext context = this; + + while (context.parentContext != FileContext.RootContext.INSTANCE) { + context = context.parentContext; + } + + return context; + } + + @Nonnull + private static String toContentPath(@Nonnull Path filepath, @Nonnull FileContext parent) { + StringBuilder sb = new StringBuilder(); + int start = parent == FileContext.RootContext.INSTANCE ? 0 : parent.getRoot().filepath.getNameCount(); + + for (int i = start; i < filepath.getNameCount(); i++) { + if (i > start) { + sb.append('.'); + } + + String name = filepath.getName(i).toString(); + int end = name.length(); + if (i == filepath.getNameCount() - 1) { + int ext = name.lastIndexOf(46); + if (ext != -1) { + end = ext; + } + } + + sb.append(name, 0, end); + } + + return sb.toString(); + } + public interface Constants { String ERROR_MISSING_ENTRY = "Missing %s entry for key %s"; String ERROR_DUPLICATE_ENTRY = "Duplicate %s entry registered for key %s"; @@ -87,4 +150,12 @@ public class FileContext { return this.backing.entrySet().iterator(); } } + + public static class RootContext extends FileContext { + public static final FileContext.RootContext INSTANCE = new FileContext.RootContext(); + + private RootContext() { + super(-1, ".", Paths.get("."), null); + } + } } diff --git a/src/com/hypixel/hytale/server/worldgen/loader/context/FileContextLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/context/FileContextLoader.java index 76dddba9..127cf33d 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/context/FileContextLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/context/FileContextLoader.java @@ -23,17 +23,19 @@ public class FileContextLoader { private static final UnaryOperator DISABLED_FILE = FileContextLoader::getDisabledFilePath; private static final Predicate ZONE_FILE_MATCHER = FileContextLoader::isValidZoneFile; private static final Predicate BIOME_FILE_MATCHER = FileContextLoader::isValidBiomeFile; + private final String name; private final Path dataFolder; private final Set zoneRequirement; - public FileContextLoader(Path dataFolder, Set zoneRequirement) { + public FileContextLoader(String name, Path dataFolder, Set zoneRequirement) { + this.name = name; this.dataFolder = dataFolder; this.zoneRequirement = zoneRequirement; } @Nonnull public FileLoadingContext load() { - FileLoadingContext context = new FileLoadingContext(this.dataFolder); + FileLoadingContext context = new FileLoadingContext(this.name, this.dataFolder); Path zonesFolder = this.dataFolder.resolve("Zones"); try { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/context/FileLoadingContext.java b/src/com/hypixel/hytale/server/worldgen/loader/context/FileLoadingContext.java index 0e6d0727..2e145eb0 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/context/FileLoadingContext.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/context/FileLoadingContext.java @@ -4,19 +4,14 @@ import com.hypixel.hytale.server.worldgen.prefab.PrefabCategory; import java.nio.file.Path; import javax.annotation.Nonnull; -public class FileLoadingContext extends FileContext { +public class FileLoadingContext extends FileContext { private final FileContext.Registry zones = new FileContext.Registry<>("Zone"); private final FileContext.Registry prefabCategories = new FileContext.Registry<>("Category"); private int zoneIdCounter = -1; private int biomeIdCounter = -1; - public FileLoadingContext(@Nonnull Path filepath) { - super(-1, filepath.getFileName().toString(), filepath, null); - } - - @Nonnull - public FileLoadingContext getParentContext() { - return this; + public FileLoadingContext(@Nonnull String name, @Nonnull Path filepath) { + super(-1, name, filepath, FileContext.RootContext.INSTANCE); } @Nonnull diff --git a/src/com/hypixel/hytale/server/worldgen/loader/context/ZoneFileContext.java b/src/com/hypixel/hytale/server/worldgen/loader/context/ZoneFileContext.java index bd8e3d6a..a182b2f8 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/context/ZoneFileContext.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/context/ZoneFileContext.java @@ -10,7 +10,7 @@ public class ZoneFileContext extends FileContext { private final FileContext.Registry tileBiomes = new FileContext.Registry<>("TileBiome"); private final FileContext.Registry customBiomes = new FileContext.Registry<>("CustomBiome"); - public ZoneFileContext(int id, String name, Path filepath, FileLoadingContext context) { + public ZoneFileContext(int id, @Nonnull String name, @Nonnull Path filepath, @Nonnull FileLoadingContext context) { super(id, name, filepath, context); } diff --git a/src/com/hypixel/hytale/server/worldgen/loader/prefab/unique/UniquePrefabConfigurationJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/prefab/unique/UniquePrefabConfigurationJsonLoader.java index df4f8164..bda0f08a 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/prefab/unique/UniquePrefabConfigurationJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/prefab/unique/UniquePrefabConfigurationJsonLoader.java @@ -2,8 +2,6 @@ package com.hypixel.hytale.server.worldgen.loader.prefab.unique; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.condition.ConstantBlockFluidCondition; import com.hypixel.hytale.procedurallib.condition.ConstantIntCondition; import com.hypixel.hytale.procedurallib.condition.HeightCondition; @@ -33,6 +31,8 @@ import it.unimi.dsi.fastutil.longs.LongSet; import java.nio.file.Path; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2d; +import org.joml.Vector3d; public class UniquePrefabConfigurationJsonLoader extends JsonLoader { protected final ZoneFileContext zoneContext; diff --git a/src/com/hypixel/hytale/server/worldgen/loader/util/Vector2dJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/util/Vector2dJsonLoader.java index 032920ef..f91248c8 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/util/Vector2dJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/util/Vector2dJsonLoader.java @@ -3,12 +3,12 @@ package com.hypixel.hytale.server.worldgen.loader.util; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.hypixel.hytale.math.vector.Vector2d; import com.hypixel.hytale.procedurallib.json.JsonLoader; import com.hypixel.hytale.procedurallib.json.SeedString; import com.hypixel.hytale.server.worldgen.SeedStringResource; import java.nio.file.Path; import javax.annotation.Nonnull; +import org.joml.Vector2d; public class Vector2dJsonLoader extends JsonLoader { public Vector2dJsonLoader(SeedString seed, Path dataFolder, JsonElement json) { diff --git a/src/com/hypixel/hytale/server/worldgen/loader/util/Vector3dJsonLoader.java b/src/com/hypixel/hytale/server/worldgen/loader/util/Vector3dJsonLoader.java index f7e9da00..2bcb6b29 100644 --- a/src/com/hypixel/hytale/server/worldgen/loader/util/Vector3dJsonLoader.java +++ b/src/com/hypixel/hytale/server/worldgen/loader/util/Vector3dJsonLoader.java @@ -3,12 +3,12 @@ package com.hypixel.hytale.server.worldgen.loader.util; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.json.JsonLoader; import com.hypixel.hytale.procedurallib.json.SeedString; import com.hypixel.hytale.server.worldgen.SeedStringResource; import java.nio.file.Path; import javax.annotation.Nonnull; +import org.joml.Vector3d; public class Vector3dJsonLoader extends JsonLoader { public Vector3dJsonLoader(SeedString seed, Path dataFolder, JsonElement json) { diff --git a/src/com/hypixel/hytale/server/worldgen/prefab/PrefabLoadingCache.java b/src/com/hypixel/hytale/server/worldgen/prefab/PrefabLoadingCache.java index d148b582..fc703e67 100644 --- a/src/com/hypixel/hytale/server/worldgen/prefab/PrefabLoadingCache.java +++ b/src/com/hypixel/hytale/server/worldgen/prefab/PrefabLoadingCache.java @@ -19,10 +19,7 @@ public class PrefabLoadingCache { } public void clear() { - this.cache.values().removeIf(buffer -> { - buffer.release(); - return true; - }); + this.cache.clear(); } @Nonnull diff --git a/src/com/hypixel/hytale/server/worldgen/prefab/PrefabPasteUtil.java b/src/com/hypixel/hytale/server/worldgen/prefab/PrefabPasteUtil.java index 58c2ebb9..cbc08318 100644 --- a/src/com/hypixel/hytale/server/worldgen/prefab/PrefabPasteUtil.java +++ b/src/com/hypixel/hytale/server/worldgen/prefab/PrefabPasteUtil.java @@ -4,7 +4,6 @@ import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.FastRandom; import com.hypixel.hytale.math.util.HashUtil; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateCondition; import com.hypixel.hytale.procedurallib.condition.DefaultCoordinateRndCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; @@ -19,6 +18,7 @@ import com.hypixel.hytale.server.worldgen.util.condition.BlockMaskCondition; import java.util.Random; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector3i; public class PrefabPasteUtil { public static final int MAX_RECURSION_DEPTH = 10; @@ -27,8 +27,8 @@ public class PrefabPasteUtil { @Nonnull PrefabPasteUtil.PrefabPasteBuffer buffer, PrefabRotation rotation, @Nonnull WorldGenPrefabSupplier supplier, int x, int y, int z, int cx, int cz ) { buffer.supplier = supplier; - buffer.posWorld.assign(x, y, z); - buffer.posChunk.assign(cx, y, cz); + buffer.posWorld.set(x, y, z); + buffer.posChunk.set(cx, y, cz); buffer.rotation = rotation; generate0(buffer, supplier); buffer.reset(); @@ -104,8 +104,8 @@ public class PrefabPasteUtil { PrefabRotation _rotation = buffer.rotation; boolean _fitHeightmap = buffer.fitHeightmap; generateChild(cx, cy, cz, path, fitHeightmap, inheritSeed, inheritHeightCondition, weights, rotation, buffer, buffer.childRandom); - buffer.posChunk.assign(_localX, _localY, _localZ); - buffer.posWorld.assign(_worldX, _worldY, _worldZ); + buffer.posChunk.set(_localX, _localY, _localZ); + buffer.posWorld.set(_worldX, _worldY, _worldZ); buffer.yOffset = _yOffset; buffer.originHeight = _originHeight; buffer.rotation = _rotation; diff --git a/src/com/hypixel/hytale/server/worldgen/prefab/unique/UniquePrefabConfiguration.java b/src/com/hypixel/hytale/server/worldgen/prefab/unique/UniquePrefabConfiguration.java index 0729fd0d..96927c86 100644 --- a/src/com/hypixel/hytale/server/worldgen/prefab/unique/UniquePrefabConfiguration.java +++ b/src/com/hypixel/hytale/server/worldgen/prefab/unique/UniquePrefabConfiguration.java @@ -1,7 +1,5 @@ package com.hypixel.hytale.server.worldgen.prefab.unique; -import com.hypixel.hytale.math.vector.Vector2d; -import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.procedurallib.condition.IBlockFluidCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateCondition; import com.hypixel.hytale.procedurallib.condition.ICoordinateRndCondition; @@ -11,6 +9,8 @@ import com.hypixel.hytale.server.worldgen.biome.Biome; import com.hypixel.hytale.server.worldgen.util.condition.BlockMaskCondition; import java.util.Random; import javax.annotation.Nonnull; +import org.joml.Vector2d; +import org.joml.Vector3d; public class UniquePrefabConfiguration { protected final ICoordinateRndCondition heightCondition; diff --git a/src/com/hypixel/hytale/server/worldgen/prefab/unique/UniquePrefabGenerator.java b/src/com/hypixel/hytale/server/worldgen/prefab/unique/UniquePrefabGenerator.java index 63d55323..0877dd51 100644 --- a/src/com/hypixel/hytale/server/worldgen/prefab/unique/UniquePrefabGenerator.java +++ b/src/com/hypixel/hytale/server/worldgen/prefab/unique/UniquePrefabGenerator.java @@ -3,8 +3,6 @@ package com.hypixel.hytale.server.worldgen.prefab.unique; import com.hypixel.hytale.common.map.IWeightedMap; import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.TrigMathUtil; -import com.hypixel.hytale.math.vector.Vector2i; -import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.procedurallib.condition.ICoordinateRndCondition; import com.hypixel.hytale.server.worldgen.biome.Biome; import com.hypixel.hytale.server.worldgen.chunk.ChunkGenerator; @@ -21,6 +19,8 @@ import java.util.Random; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; +import org.joml.Vector3i; public class UniquePrefabGenerator { private static final int UNIQUE_ZONE_PLACEMENT_HEURISTIC_ITERATIONS = 8; @@ -101,8 +101,8 @@ public class UniquePrefabGenerator { protected Vector3i tryPlacement( int seed, @Nonnull ChunkGenerator chunkGenerator, @Nonnull Random random, @Nonnull UniquePrefabContainer.UniquePrefabEntry[] entries ) { - double x = this.configuration.getAnchor().getX(); - double z = this.configuration.getAnchor().getY(); + double x = this.configuration.getAnchor().x(); + double z = this.configuration.getAnchor().y(); double distance = random.nextDouble() * this.configuration.getMaxDistance(); float angle = random.nextFloat() * (float) (Math.PI * 2); x += TrigMathUtil.cos(angle) * distance; @@ -144,8 +144,8 @@ public class UniquePrefabGenerator { @Nonnull protected Vector3i forceGeneration(int seed, @Nonnull ChunkGenerator chunkGenerator) { - double x = this.configuration.getAnchor().getX(); - double z = this.configuration.getAnchor().getY(); + double x = this.configuration.getAnchor().x(); + double z = this.configuration.getAnchor().y(); int lx = MathUtil.floor(x); int lz = MathUtil.floor(z); ZoneBiomeResult result = chunkGenerator.getZoneBiomeResultAt(seed, lx, lz); diff --git a/src/com/hypixel/hytale/server/worldgen/util/ListPool.java b/src/com/hypixel/hytale/server/worldgen/util/ListPool.java new file mode 100644 index 00000000..a775a85d --- /dev/null +++ b/src/com/hypixel/hytale/server/worldgen/util/ListPool.java @@ -0,0 +1,67 @@ +package com.hypixel.hytale.server.worldgen.util; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.util.concurrent.ConcurrentLinkedQueue; +import javax.annotation.Nonnull; + +public class ListPool { + private final int capacity; + private final T[] empty; + private final ConcurrentLinkedQueue> pool = new ConcurrentLinkedQueue<>(); + + public ListPool(int capacity, T[] empty) { + this.capacity = capacity; + this.empty = empty; + + for (int i = 0; i < capacity; i++) { + this.pool.add(new ListPool.Resource<>(this)); + } + } + + public T[] emptyArray() { + return this.empty; + } + + @Nonnull + public ListPool.Resource acquire() { + ListPool.Resource resource = this.pool.poll(); + return resource == null ? new ListPool.Resource<>(this) : resource; + } + + @Nonnull + public ListPool.Resource acquire(int capacity) { + ListPool.Resource resource = this.pool.poll(); + if (resource == null) { + resource = new ListPool.Resource<>(this); + } + + resource.ensureCapacity(capacity); + return resource; + } + + public void release(@Nonnull ListPool.Resource resource) { + if (this.pool.size() < this.capacity) { + resource.clear(); + this.pool.offer(resource); + } + } + + public static class Resource extends ObjectArrayList implements AutoCloseable { + private final ListPool pool; + + public Resource(ListPool pool) { + this.pool = pool; + } + + @Nonnull + @Override + public T[] toArray() { + return (T[])super.toArray(this.pool.empty); + } + + @Override + public void close() { + this.pool.release(this); + } + } +} diff --git a/src/com/hypixel/hytale/server/worldgen/util/bounds/IChunkBounds.java b/src/com/hypixel/hytale/server/worldgen/util/bounds/IChunkBounds.java index 64fe0e6c..b5049734 100644 --- a/src/com/hypixel/hytale/server/worldgen/util/bounds/IChunkBounds.java +++ b/src/com/hypixel/hytale/server/worldgen/util/bounds/IChunkBounds.java @@ -42,11 +42,11 @@ public interface IChunkBounds { } default int randomX(@Nonnull Random random) { - return random.nextInt(this.getHighBoundX() - this.getLowBoundX()) + this.getLowBoundX(); + return getRandomOffset(this.getLowBoundX(), this.getHighBoundX(), random); } default int randomZ(@Nonnull Random random) { - return random.nextInt(this.getHighBoundZ() - this.getLowBoundZ()) + this.getLowBoundZ(); + return getRandomOffset(this.getLowBoundZ(), this.getHighBoundZ(), random); } default double fractionX(double d) { @@ -72,4 +72,21 @@ public interface IChunkBounds { default int getHighChunkZ() { return ChunkUtil.chunkCoordinate(this.getHighBoundZ()); } + + default boolean isValid() { + return this.getHighBoundX() > this.getLowBoundX() && this.getHighBoundZ() > this.getLowBoundZ(); + } + + static int getRandomOffset(int min, int max, @Nonnull Random random) { + if (!.$assertionsDisabled && max <= min) { + throw new AssertionError("Invalid bounds: " + min + " to " + max); + } else { + return max <= min ? min : random.nextInt(max - min) + min; + } + } + + static { + if (.$assertionsDisabled) { + } + } } diff --git a/src/com/hypixel/hytale/server/worldgen/util/bounds/IWorldBounds.java b/src/com/hypixel/hytale/server/worldgen/util/bounds/IWorldBounds.java index 87618ba6..7ddc0c1e 100644 --- a/src/com/hypixel/hytale/server/worldgen/util/bounds/IWorldBounds.java +++ b/src/com/hypixel/hytale/server/worldgen/util/bounds/IWorldBounds.java @@ -15,10 +15,15 @@ public interface IWorldBounds extends IChunkBounds { } default int randomY(@Nonnull Random random) { - return random.nextInt(this.getHighBoundY() - this.getLowBoundY()) + this.getLowBoundY(); + return IChunkBounds.getRandomOffset(this.getLowBoundY(), this.getHighBoundY(), random); } default double fractionY(double d) { return (this.getHighBoundY() - this.getLowBoundY()) * d + this.getLowBoundY(); } + + @Override + default boolean isValid() { + return IChunkBounds.super.isValid() && this.getHighBoundY() > this.getLowBoundY(); + } } diff --git a/src/com/hypixel/hytale/server/worldgen/util/condition/BlockMaskCondition.java b/src/com/hypixel/hytale/server/worldgen/util/condition/BlockMaskCondition.java index 1a5208b3..87ef2690 100644 --- a/src/com/hypixel/hytale/server/worldgen/util/condition/BlockMaskCondition.java +++ b/src/com/hypixel/hytale/server/worldgen/util/condition/BlockMaskCondition.java @@ -110,7 +110,7 @@ public class BlockMaskCondition { this(false, false); } - private MaskEntry(boolean any, boolean replace) { + public MaskEntry(boolean any, boolean replace) { this.any = any; this.replace = replace; } diff --git a/src/com/hypixel/hytale/server/worldgen/zone/Zone.java b/src/com/hypixel/hytale/server/worldgen/zone/Zone.java index 447c4fa2..ab4a5574 100644 --- a/src/com/hypixel/hytale/server/worldgen/zone/Zone.java +++ b/src/com/hypixel/hytale/server/worldgen/zone/Zone.java @@ -1,12 +1,12 @@ package com.hypixel.hytale.server.worldgen.zone; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.worldgen.biome.BiomePatternGenerator; import com.hypixel.hytale.server.worldgen.cave.CaveGenerator; import com.hypixel.hytale.server.worldgen.container.UniquePrefabContainer; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; public record Zone( int id, diff --git a/src/com/hypixel/hytale/server/worldgen/zoom/ExactZoom.java b/src/com/hypixel/hytale/server/worldgen/zoom/ExactZoom.java index 142db607..e05bcd25 100644 --- a/src/com/hypixel/hytale/server/worldgen/zoom/ExactZoom.java +++ b/src/com/hypixel/hytale/server/worldgen/zoom/ExactZoom.java @@ -2,7 +2,6 @@ package com.hypixel.hytale.server.worldgen.zoom; import com.hypixel.hytale.math.util.FastRandom; import com.hypixel.hytale.math.util.MathUtil; -import com.hypixel.hytale.math.vector.Vector2i; import com.hypixel.hytale.server.worldgen.util.LogUtil; import com.hypixel.hytale.server.worldgen.zone.Zone; import java.awt.image.BufferedImage; @@ -12,6 +11,7 @@ import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.joml.Vector2i; public class ExactZoom { @Nonnull diff --git a/src/com/hypixel/hytale/storage/IndexedStorageFile.java b/src/com/hypixel/hytale/storage/IndexedStorageFile.java index 5db33410..f9cf78ee 100644 --- a/src/com/hypixel/hytale/storage/IndexedStorageFile.java +++ b/src/com/hypixel/hytale/storage/IndexedStorageFile.java @@ -3,6 +3,7 @@ package com.hypixel.hytale.storage; import com.github.luben.zstd.Zstd; import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.metrics.MetricsRegistry; +import com.hypixel.hytale.sneakythrow.SneakyThrow; import com.hypixel.hytale.unsafe.UnsafeUtil; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; @@ -14,16 +15,15 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileAttribute; import java.util.Arrays; import java.util.BitSet; -import java.util.HashSet; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.StampedLock; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -40,7 +40,7 @@ public class IndexedStorageFile implements Closeable { }, Codec.LONG) .register("CompressionLevel", file -> file.getCompressionLevel(), Codec.INTEGER) .register("BlobCount", file -> file.getBlobCount(), Codec.INTEGER) - .register("UsedBlobCount", file -> file.keys().size(), Codec.INTEGER) + .register("UsedBlobCount", SneakyThrow.sneakyFunction(file -> file.usedBlobCount.get()), Codec.INTEGER) .register("SegmentSize", file -> file.segmentSize(), Codec.INTEGER) .register("SegmentCount", file -> file.segmentCount(), Codec.INTEGER); public static final String MAGIC_STRING = "HytaleIndexedStorage"; @@ -74,7 +74,9 @@ public class IndexedStorageFile implements Closeable { private int version; private int blobCount; private int segmentSize; + private final AtomicInteger usedBlobCount = new AtomicInteger(0); private StampedLock[] indexLocks; + @Nullable private MappedByteBuffer mappedBlobIndexes; private final StampedLock segmentLocksLock = new StampedLock(); private StampedLock[] segmentLocks = EMPTY_STAMPED_LOCKS; @@ -128,43 +130,16 @@ public class IndexedStorageFile implements Closeable { storageFile.readHeader(); storageFile.memoryMapBlobIndexes(); if (storageFile.version == 0) { - storageFile = migrateV0(path, blobCount, segmentSize, options, attrs, storageFile); - } else { - storageFile.readUsedSegments(); + throw new IOException("IndexedStorageFile version 0 is no longer supported"); } + + storageFile.readUsedSegments(); } return storageFile; } } - private static IndexedStorageFile migrateV0( - Path path, int blobCount, int segmentSize, Set options, FileAttribute[] attrs, IndexedStorageFile storageFile - ) throws IOException { - storageFile.close(); - Path tempFile = path.resolveSibling(path.getFileName().toString() + ".old"); - Path tempPath = Files.move(path, tempFile, StandardCopyOption.REPLACE_EXISTING); - HashSet newOptions = new HashSet<>(options); - newOptions.add(StandardOpenOption.CREATE); - storageFile = new IndexedStorageFile(path, FileChannel.open(path, newOptions, attrs)); - storageFile.create(blobCount, segmentSize); - - try (IndexedStorageFile_v0 oldStorageFile = new IndexedStorageFile_v0(tempPath, FileChannel.open(tempPath, options, attrs))) { - oldStorageFile.open(); - - for (int blobIndex = 0; blobIndex < blobCount; blobIndex++) { - ByteBuffer blob = oldStorageFile.readBlob(blobIndex); - if (blob != null) { - storageFile.writeBlob(blobIndex, blob); - } - } - } finally { - Files.delete(tempFile); - } - - return storageFile; - } - private IndexedStorageFile(@Nonnull Path path, @Nonnull FileChannel fileChannel) { this.path = path; this.fileChannel = fileChannel; @@ -187,6 +162,10 @@ public class IndexedStorageFile implements Closeable { return this.compressionLevel; } + public int getUsedBlobCount() { + return this.usedBlobCount.get(); + } + public void setFlushOnWrite(boolean flushOnWrite) { this.flushOnWrite = flushOnWrite; } @@ -258,24 +237,64 @@ public class IndexedStorageFile implements Closeable { this.indexLocks[i] = new StampedLock(); } - this.mappedBlobIndexes = this.fileChannel.map(MapMode.READ_WRITE, HEADER_LENGTH, this.blobCount * 4L); + try { + this.mappedBlobIndexes = this.fileChannel.map(MapMode.READ_WRITE, HEADER_LENGTH, this.blobCount * 4L); + } catch (UnsupportedOperationException var2) { + this.mappedBlobIndexes = null; + } + } + + protected int getBlobIndex(int blobIndex) throws IOException { + int indexPos = blobIndex * 4; + if (this.mappedBlobIndexes == null) { + ByteBuffer buf = getTempBuffer(4); + if (this.fileChannel.read(buf, HEADER_LENGTH + indexPos) != 4) { + throw new IllegalStateException(); + } else { + return buf.getInt(0); + } + } else { + return this.mappedBlobIndexes.getInt(indexPos); + } + } + + protected void putBlobIndex(int blobIndex, int segmentIndex) throws IOException { + int indexPos = blobIndex * 4; + if (this.mappedBlobIndexes == null) { + ByteBuffer buf = getTempBuffer(4); + buf.putInt(0, segmentIndex); + if (this.fileChannel.write(buf, HEADER_LENGTH + indexPos) != 4) { + throw new IllegalStateException(); + } else { + if (this.flushOnWrite) { + this.fileChannel.force(false); + } + } + } else { + this.mappedBlobIndexes.putInt(indexPos, segmentIndex); + if (this.flushOnWrite) { + this.mappedBlobIndexes.force(indexPos, 4); + } + } } protected void readUsedSegments() throws IOException { long stamp = this.usedSegmentsLock.writeLock(); try { + int count = 0; + for (int blobIndex = 0; blobIndex < this.blobCount; blobIndex++) { - int indexPos = blobIndex * 4; long segmentStamp = this.indexLocks[blobIndex].readLock(); int firstSegmentIndex; int compressedLength; try { - firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); + firstSegmentIndex = this.getBlobIndex(blobIndex); if (firstSegmentIndex == 0) { compressedLength = 0; } else { + count++; ByteBuffer blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex); compressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET); } @@ -288,6 +307,8 @@ public class IndexedStorageFile implements Closeable { this.usedSegments.set(firstSegmentIndex, firstSegmentIndex + segmentsCount); } } + + this.usedBlobCount.set(count); } finally { this.usedSegmentsLock.unlockWrite(stamp); } @@ -325,14 +346,13 @@ public class IndexedStorageFile implements Closeable { } @Nonnull - public IntList keys() { + public IntList keys() throws IOException { IntArrayList list = new IntArrayList(this.blobCount); for (int blobIndex = 0; blobIndex < this.blobCount; blobIndex++) { - int indexPos = blobIndex * 4; StampedLock lock = this.indexLocks[blobIndex]; long stamp = lock.tryOptimisticRead(); - int segmentIndex = this.mappedBlobIndexes.getInt(indexPos); + int segmentIndex = this.getBlobIndex(blobIndex); if (lock.validate(stamp)) { if (segmentIndex != 0) { list.add(blobIndex); @@ -341,7 +361,7 @@ public class IndexedStorageFile implements Closeable { stamp = lock.readLock(); try { - if (this.mappedBlobIndexes.getInt(indexPos) != 0) { + if (this.getBlobIndex(blobIndex) != 0) { list.add(blobIndex); } } finally { @@ -355,12 +375,11 @@ public class IndexedStorageFile implements Closeable { public int readBlobLength(int blobIndex) throws IOException { if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; long stamp = this.indexLocks[blobIndex].readLock(); byte blobHeaderBuffer; try { - int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); + int firstSegmentIndex = this.getBlobIndex(blobIndex); if (firstSegmentIndex != 0) { ByteBuffer blobHeaderBufferx = this.readBlobHeader(firstSegmentIndex); return blobHeaderBufferx.getInt(SRC_LENGTH_OFFSET); @@ -379,12 +398,11 @@ public class IndexedStorageFile implements Closeable { public int readBlobCompressedLength(int blobIndex) throws IOException { if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; long stamp = this.indexLocks[blobIndex].readLock(); byte blobHeaderBuffer; try { - int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); + int firstSegmentIndex = this.getBlobIndex(blobIndex); if (firstSegmentIndex != 0) { ByteBuffer blobHeaderBufferx = this.readBlobHeader(firstSegmentIndex); return blobHeaderBufferx.getInt(COMPRESSED_LENGTH_OFFSET); @@ -404,7 +422,6 @@ public class IndexedStorageFile implements Closeable { @Nullable public ByteBuffer readBlob(int blobIndex) throws IOException { if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; long stamp = this.indexLocks[blobIndex].readLock(); ByteBuffer src; @@ -412,7 +429,7 @@ public class IndexedStorageFile implements Closeable { label43: { ByteBuffer blobHeaderBuffer; try { - int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); + int firstSegmentIndex = this.getBlobIndex(blobIndex); if (firstSegmentIndex != 0) { blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex); srcLength = blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET); @@ -438,13 +455,12 @@ public class IndexedStorageFile implements Closeable { public void readBlob(int blobIndex, @Nonnull ByteBuffer dest) throws IOException { if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; long stamp = this.indexLocks[blobIndex].readLock(); ByteBuffer src; int srcLength; try { - int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); + int firstSegmentIndex = this.getBlobIndex(blobIndex); if (firstSegmentIndex == 0) { return; } @@ -536,12 +552,11 @@ public class IndexedStorageFile implements Closeable { dest.putInt(COMPRESSED_LENGTH_OFFSET, compressedLength); dest.limit(dest.position()); dest.position(0); - int indexPos = blobIndex * 4; long stamp = this.indexLocks[blobIndex].writeLock(); try { int oldSegmentLength = 0; - int oldFirstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); + int oldFirstSegmentIndex = this.getBlobIndex(blobIndex); if (oldFirstSegmentIndex != 0) { ByteBuffer blobHeaderBuffer = this.readBlobHeader(oldFirstSegmentIndex); int oldCompressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET); @@ -553,9 +568,9 @@ public class IndexedStorageFile implements Closeable { this.fileChannel.force(false); } - this.mappedBlobIndexes.putInt(indexPos, firstSegmentIndex); - if (this.flushOnWrite) { - this.mappedBlobIndexes.force(indexPos, 4); + this.putBlobIndex(blobIndex, firstSegmentIndex); + if (oldFirstSegmentIndex == 0) { + this.usedBlobCount.incrementAndGet(); } if (oldSegmentLength > 0) { @@ -577,20 +592,16 @@ public class IndexedStorageFile implements Closeable { public void removeBlob(int blobIndex) throws IOException { if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; long stamp = this.indexLocks[blobIndex].writeLock(); try { - int oldFirstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); + int oldFirstSegmentIndex = this.getBlobIndex(blobIndex); if (oldFirstSegmentIndex != 0) { ByteBuffer blobHeaderBuffer = this.readBlobHeader(oldFirstSegmentIndex); int oldCompressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET); int oldSegmentLength = this.requiredSegments(BLOB_HEADER_LENGTH + oldCompressedLength); - this.mappedBlobIndexes.putInt(indexPos, 0); - if (this.flushOnWrite) { - this.mappedBlobIndexes.force(indexPos, 4); - } - + this.putBlobIndex(blobIndex, 0); + this.usedBlobCount.decrementAndGet(); long usedSegmentsStamp = this.usedSegmentsLock.writeLock(); try { @@ -754,13 +765,15 @@ public class IndexedStorageFile implements Closeable { public void force(boolean metaData) throws IOException { this.fileChannel.force(metaData); - this.mappedBlobIndexes.force(); + if (this.mappedBlobIndexes != null) { + this.mappedBlobIndexes.force(); + } } @Override public void close() throws IOException { this.fileChannel.close(); - if (UnsafeUtil.UNSAFE != null) { + if (UnsafeUtil.UNSAFE != null && this.mappedBlobIndexes != null) { UnsafeUtil.UNSAFE.invokeCleaner(this.mappedBlobIndexes); } @@ -809,6 +822,8 @@ public class IndexedStorageFile implements Closeable { private final long[] stamps; public SegmentRangeWriteLock(int segmentIndex, int count, long[] stamps) { + Objects.requireNonNull(IndexedStorageFile.this); + super(); if (segmentIndex == 0) { throw new IllegalArgumentException("Invalid segment index!"); } else if (count == 0) { diff --git a/src/com/hypixel/hytale/storage/IndexedStorageFile_v0.java b/src/com/hypixel/hytale/storage/IndexedStorageFile_v0.java deleted file mode 100644 index 7b1653ce..00000000 --- a/src/com/hypixel/hytale/storage/IndexedStorageFile_v0.java +++ /dev/null @@ -1,1001 +0,0 @@ -package com.hypixel.hytale.storage; - -import com.github.luben.zstd.Zstd; -import com.hypixel.hytale.codec.Codec; -import com.hypixel.hytale.logger.HytaleLogger; -import com.hypixel.hytale.metrics.MetricsRegistry; -import com.hypixel.hytale.unsafe.UnsafeUtil; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; -import java.io.Closeable; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.nio.channels.FileChannel.MapMode; -import java.nio.charset.StandardCharsets; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.nio.file.attribute.FileAttribute; -import java.util.Arrays; -import java.util.Set; -import java.util.concurrent.locks.StampedLock; -import java.util.logging.Level; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -@Deprecated -public class IndexedStorageFile_v0 implements Closeable { - private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass(); - public static final StampedLock[] EMPTY_STAMPED_LOCKS = new StampedLock[0]; - public static final MetricsRegistry METRICS_REGISTRY = new MetricsRegistry() - .register("Size", file -> { - try { - return file.size(); - } catch (IOException var2) { - return -1L; - } - }, Codec.LONG) - .register("CompressionLevel", file -> file.getCompressionLevel(), Codec.INTEGER) - .register("ContiguousBlobs", file -> file.isContiguousBlobs(), Codec.BOOLEAN) - .register("BlobCount", file -> file.getBlobCount(), Codec.INTEGER) - .register("UsedBlobCount", file -> file.keys().size(), Codec.INTEGER) - .register("SegmentSize", file -> file.segmentSize(), Codec.INTEGER) - .register("SegmentCount", file -> file.segmentCount(), Codec.INTEGER); - public static final String MAGIC_STRING = "HytaleIndexedStorage"; - public static final int VERSION = 0; - public static final int DEFAULT_BLOB_COUNT = 1024; - public static final int DEFAULT_SEGMENT_SIZE = 4096; - public static final int DEFAULT_COMPRESSION_LEVEL = 3; - public static final boolean DEFAULT_CONTIGUOUS_BLOBS = true; - static final IndexedStorageFile_v0.OffsetHelper HOH = new IndexedStorageFile_v0.OffsetHelper(); - public static final int MAGIC_LENGTH = 20; - public static final int MAGIC_OFFSET = HOH.next(20); - public static final int VERSION_OFFSET = HOH.next(4); - public static final int BLOB_COUNT_OFFSET = HOH.next(4); - public static final int SEGMENT_SIZE_OFFSET = HOH.next(4); - public static final int HEADER_LENGTH = HOH.length(); - static final IndexedStorageFile_v0.OffsetHelper SOH = new IndexedStorageFile_v0.OffsetHelper(); - public static final int NEXT_SEGMENT_OFFSET = SOH.next(4); - public static final int SEGMENT_HEADER_LENGTH = SOH.length(); - static final IndexedStorageFile_v0.OffsetHelper BOH = new IndexedStorageFile_v0.OffsetHelper(); - public static final int SRC_LENGTH_OFFSET = BOH.next(4); - public static final int COMPRESSED_LENGTH_OFFSET = BOH.next(4); - public static final int BLOB_HEADER_LENGTH = BOH.length(); - public static final int INDEX_SIZE = 4; - public static final int UNASSIGNED_INDEX = 0; - public static final int END_BLOB_INDEX = Integer.MIN_VALUE; - public static final int FIRST_SEGMENT_INDEX = 1; - public static final FileAttribute[] NO_ATTRIBUTES = new FileAttribute[0]; - static final byte[] MAGIC_BYTES = "HytaleIndexedStorage".getBytes(StandardCharsets.UTF_8); - private static final ByteBuffer MAGIC_BUFFER = ByteBuffer.wrap(MAGIC_BYTES); - private static final ThreadLocal CACHED_TEMP_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(HEADER_LENGTH)); - @Nonnull - private final Path path; - private final FileChannel fileChannel; - private int compressionLevel = 3; - private boolean contiguousBlobs = true; - private int blobCount; - private int segmentSize; - private StampedLock[] indexLocks; - @Nullable - private MappedByteBuffer mappedBlobIndexes; - private final StampedLock segmentLocksLock = new StampedLock(); - private StampedLock[] segmentLocks = EMPTY_STAMPED_LOCKS; - private final StampedLock nextSegmentIndexesLock = new StampedLock(); - @Nonnull - private int[] nextSegmentIndexes = new int[0]; - - @Nonnull - private static ByteBuffer getTempBuffer(int length) { - ByteBuffer buffer = CACHED_TEMP_BUFFER.get(); - buffer.position(0); - buffer.limit(length); - return buffer; - } - - @Nonnull - private static ByteBuffer allocateDirect(int length) { - return ByteBuffer.allocateDirect(length); - } - - @Nonnull - public static IndexedStorageFile_v0 open(@Nonnull Path path, OpenOption... options) throws IOException { - return open(path, 1024, 4096, Set.of(options), NO_ATTRIBUTES); - } - - @Nonnull - public static IndexedStorageFile_v0 open(@Nonnull Path path, @Nonnull Set options, FileAttribute... attrs) throws IOException { - return open(path, 1024, 4096, options, attrs); - } - - @Nonnull - public static IndexedStorageFile_v0 open(@Nonnull Path path, int blobCount, int segmentSize, OpenOption... options) throws IOException { - return open(path, blobCount, segmentSize, Set.of(options), NO_ATTRIBUTES); - } - - @Nonnull - public static IndexedStorageFile_v0 open( - @Nonnull Path path, int blobCount, int segmentSize, @Nonnull Set options, FileAttribute... attrs - ) throws IOException { - IndexedStorageFile_v0 storageFile = new IndexedStorageFile_v0(path, options, attrs); - if (options.contains(StandardOpenOption.CREATE_NEW)) { - storageFile.create(blobCount, segmentSize); - return storageFile; - } else { - if (options.contains(StandardOpenOption.CREATE) && storageFile.fileChannel.size() == 0L) { - storageFile.create(blobCount, segmentSize); - } else { - storageFile.open(); - } - - return storageFile; - } - } - - private IndexedStorageFile_v0(@Nonnull Path path, Set options, FileAttribute[] attrs) throws IOException { - this.path = path; - this.fileChannel = FileChannel.open(path, options, attrs); - } - - IndexedStorageFile_v0(@Nonnull Path path, FileChannel fileChannel) throws IOException { - this.path = path; - this.fileChannel = fileChannel; - } - - @Nonnull - public Path getPath() { - return this.path; - } - - public int getBlobCount() { - return this.blobCount; - } - - public int getSegmentSize() { - return this.segmentSize; - } - - public int getCompressionLevel() { - return this.compressionLevel; - } - - public void setCompressionLevel(int compressionLevel) { - this.compressionLevel = compressionLevel; - } - - public boolean isContiguousBlobs() { - return this.contiguousBlobs; - } - - public void setContiguousBlobs(boolean contiguousBlobs) { - this.contiguousBlobs = contiguousBlobs; - } - - @Nonnull - protected IndexedStorageFile_v0 create(int blobCount, int segmentSize) throws IOException { - if (blobCount <= 0) { - throw new IllegalArgumentException("blobCount must be > 0"); - } else if (segmentSize <= 0) { - throw new IllegalArgumentException("segmentSize must be > 0"); - } else { - this.blobCount = blobCount; - this.segmentSize = segmentSize; - if (this.fileChannel.size() != 0L) { - throw new IOException("file channel is not empty"); - } else { - this.writeHeader(blobCount, segmentSize); - this.memoryMapBlobIndexes(); - return this; - } - } - } - - protected void writeHeader(int blobCount, int segmentSize) throws IOException { - ByteBuffer header = getTempBuffer(HEADER_LENGTH); - header.put(MAGIC_BYTES); - header.putInt(VERSION_OFFSET, 0); - header.putInt(BLOB_COUNT_OFFSET, blobCount); - header.putInt(SEGMENT_SIZE_OFFSET, segmentSize); - header.position(0); - if (this.fileChannel.write(header, 0L) != HEADER_LENGTH) { - throw new IllegalStateException(); - } - } - - @Nonnull - protected IndexedStorageFile_v0 open() throws IOException { - if (this.fileChannel.size() == 0L) { - throw new IOException("file channel is empty"); - } else { - this.readHeader(); - this.memoryMapBlobIndexes(); - this.readNextIndexes(); - this.processTempIndexes(); - return this; - } - } - - protected void readHeader() throws IOException { - ByteBuffer header = getTempBuffer(HEADER_LENGTH); - if (this.fileChannel.read(header, 0L) != HEADER_LENGTH) { - throw new IllegalStateException(); - } else { - header.position(0); - header.limit(20); - if (!MAGIC_BUFFER.equals(header)) { - header.position(0); - byte[] dst = new byte[20]; - header.get(dst); - throw new IOException("Invalid MAGIC! " + header + ", " + Arrays.toString(dst) + " expected " + Arrays.toString(MAGIC_BYTES)); - } else { - header.limit(HEADER_LENGTH); - int version = header.getInt(VERSION_OFFSET); - if (version >= 0 && version <= 0) { - this.blobCount = header.getInt(BLOB_COUNT_OFFSET); - this.segmentSize = header.getInt(SEGMENT_SIZE_OFFSET); - } else { - throw new IOException("Invalid version! " + version); - } - } - } - } - - protected void memoryMapBlobIndexes() throws IOException { - this.indexLocks = new StampedLock[this.blobCount]; - - for (int i = 0; i < this.blobCount; i++) { - this.indexLocks[i] = new StampedLock(); - } - - int indexesSize = this.indexesLength() * 2; - this.mappedBlobIndexes = this.fileChannel.map(MapMode.READ_WRITE, HEADER_LENGTH, indexesSize); - } - - protected void readNextIndexes() throws IOException { - ByteBuffer tempIndexBuffer = getTempBuffer(4); - long stamp = this.nextSegmentIndexesLock.writeLock(); - - try { - this.nextSegmentIndexes = new int[this.requiredSegments(this.fileChannel.size() - this.segmentsBase()) + 1]; - - for (int segmentIndex = 1; segmentIndex < this.nextSegmentIndexes.length; segmentIndex++) { - tempIndexBuffer.position(0); - if (this.fileChannel.read(tempIndexBuffer, this.segmentPosition(segmentIndex)) != 4) { - tempIndexBuffer.position(0); - tempIndexBuffer.putInt(0, 0); - this.fileChannel.write(tempIndexBuffer, this.segmentPosition(segmentIndex)); - break; - } - - this.nextSegmentIndexes[segmentIndex] = tempIndexBuffer.getInt(NEXT_SEGMENT_OFFSET); - } - } finally { - this.nextSegmentIndexesLock.unlockWrite(stamp); - } - } - - protected void processTempIndexes() throws IOException { - int blobsCleared = 0; - int segmentsCleared = 0; - ByteBuffer tempIndexBuffer = getTempBuffer(4); - int indexesLength = this.indexesLength(); - - for (int blobIndex = 0; blobIndex < this.blobCount; blobIndex++) { - int tempIndexPos = indexesLength + blobIndex * 4; - int firstSegmentIndex = this.mappedBlobIndexes.getInt(tempIndexPos); - if (firstSegmentIndex != 0) { - blobsCleared++; - segmentsCleared += this.clearSegments(firstSegmentIndex, tempIndexBuffer); - this.mappedBlobIndexes.putInt(tempIndexPos, 0); - } - } - - if (blobsCleared != 0 || segmentsCleared != 0) { - LOGGER.at(Level.WARNING).log("Detected failed write for %s! Cleaned %s blobs with %s segments!", this.fileChannel, blobsCleared, segmentsCleared); - } - } - - protected int clearSegments(int firstSegmentIndex, @Nonnull ByteBuffer tempIndexBuffer) throws IOException { - int[] segments = new int[8]; - int count = 0; - int nextSegmentIndex = firstSegmentIndex; - - while (nextSegmentIndex != 0 && nextSegmentIndex != Integer.MIN_VALUE) { - if (count >= segments.length) { - segments = Arrays.copyOf(segments, count * 2); - } - - segments[count] = nextSegmentIndex; - count++; - long indexesStamp = this.nextSegmentIndexesLock.tryOptimisticRead(); - int segmentIndex = this.nextSegmentIndexes[nextSegmentIndex]; - if (!this.nextSegmentIndexesLock.validate(indexesStamp)) { - indexesStamp = this.nextSegmentIndexesLock.readLock(); - - try { - segmentIndex = this.nextSegmentIndexes[nextSegmentIndex]; - } finally { - this.nextSegmentIndexesLock.unlockRead(indexesStamp); - } - } - - nextSegmentIndex = segmentIndex; - } - - tempIndexBuffer.putInt(0, 0); - - for (int i = count - 1; i >= 0; i--) { - tempIndexBuffer.position(0); - int segmentIndex = segments[i]; - StampedLock segmentLock = this.getSegmentLock(segmentIndex); - long segmentStamp = segmentLock.writeLock(); - - try { - if (this.fileChannel.write(tempIndexBuffer, this.segmentPosition(segmentIndex)) != 4) { - throw new IllegalStateException(); - } - - long indexesStamp = this.nextSegmentIndexesLock.writeLock(); - - try { - this.nextSegmentIndexes[segmentIndex] = 0; - } finally { - this.nextSegmentIndexesLock.unlockWrite(indexesStamp); - } - } finally { - segmentLock.unlockWrite(segmentStamp); - } - } - - return count; - } - - public long size() throws IOException { - return this.fileChannel.size(); - } - - public int segmentSize() { - long stamp = this.nextSegmentIndexesLock.tryOptimisticRead(); - int value = this.nextSegmentIndexes.length; - if (this.nextSegmentIndexesLock.validate(stamp)) { - return value; - } else { - stamp = this.nextSegmentIndexesLock.readLock(); - - int var4; - try { - var4 = this.nextSegmentIndexes.length; - } finally { - this.nextSegmentIndexesLock.unlockRead(stamp); - } - - return var4; - } - } - - public int segmentCount() { - long stamp = this.nextSegmentIndexesLock.tryOptimisticRead(); - int count = 0; - int[] temp = this.nextSegmentIndexes; - - for (int i = 0; i < temp.length; i++) { - if (temp[i] != 0) { - count++; - } - } - - if (this.nextSegmentIndexesLock.validate(stamp)) { - return count; - } else { - stamp = this.nextSegmentIndexesLock.readLock(); - - int var13; - try { - count = 0; - temp = this.nextSegmentIndexes; - - for (int ix = 0; ix < temp.length; ix++) { - if (temp[ix] != 0) { - count++; - } - } - - var13 = count; - } finally { - this.nextSegmentIndexesLock.unlockRead(stamp); - } - - return var13; - } - } - - @Nonnull - public IntList keys() { - IntArrayList list = new IntArrayList(this.blobCount); - - for (int blobIndex = 0; blobIndex < this.blobCount; blobIndex++) { - int indexPos = blobIndex * 4; - StampedLock lock = this.indexLocks[blobIndex]; - long stamp = lock.tryOptimisticRead(); - int segmentIndex = this.mappedBlobIndexes.getInt(indexPos); - if (lock.validate(stamp)) { - if (segmentIndex != 0) { - list.add(blobIndex); - } - } else { - stamp = lock.readLock(); - - try { - if (this.mappedBlobIndexes.getInt(indexPos) != 0) { - list.add(blobIndex); - } - } finally { - lock.unlockRead(stamp); - } - } - } - - return list; - } - - public int readBlobLength(int blobIndex) throws IOException { - if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; - long stamp = this.indexLocks[blobIndex].readLock(); - - byte blobHeaderBuffer; - try { - int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); - if (firstSegmentIndex != 0) { - ByteBuffer blobHeaderBufferx = this.readBlobHeader(firstSegmentIndex); - return blobHeaderBufferx.getInt(SRC_LENGTH_OFFSET); - } - - blobHeaderBuffer = 0; - } finally { - this.indexLocks[blobIndex].unlockRead(stamp); - } - - return blobHeaderBuffer; - } else { - throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount); - } - } - - public int readBlobCompressedLength(int blobIndex) throws IOException { - if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; - long stamp = this.indexLocks[blobIndex].readLock(); - - byte blobHeaderBuffer; - try { - int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); - if (firstSegmentIndex != 0) { - ByteBuffer blobHeaderBufferx = this.readBlobHeader(firstSegmentIndex); - return blobHeaderBufferx.getInt(COMPRESSED_LENGTH_OFFSET); - } - - blobHeaderBuffer = 0; - } finally { - this.indexLocks[blobIndex].unlockRead(stamp); - } - - return blobHeaderBuffer; - } else { - throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount); - } - } - - @Nullable - public ByteBuffer readBlob(int blobIndex) throws IOException { - if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; - long stamp = this.indexLocks[blobIndex].readLock(); - - ByteBuffer src; - int srcLength; - label43: { - ByteBuffer blobHeaderBuffer; - try { - int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); - if (firstSegmentIndex != 0) { - blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex); - srcLength = blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET); - int compressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET); - src = this.readSegments(firstSegmentIndex, compressedLength, blobHeaderBuffer); - break label43; - } - - blobHeaderBuffer = null; - } finally { - this.indexLocks[blobIndex].unlockRead(stamp); - } - - return blobHeaderBuffer; - } - - src.position(0); - return Zstd.decompress(src, srcLength); - } else { - throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount); - } - } - - public void readBlob(int blobIndex, @Nonnull ByteBuffer dest) throws IOException { - if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; - long stamp = this.indexLocks[blobIndex].readLock(); - - ByteBuffer src; - int srcLength; - try { - int firstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); - if (firstSegmentIndex == 0) { - return; - } - - ByteBuffer blobHeaderBuffer = this.readBlobHeader(firstSegmentIndex); - srcLength = blobHeaderBuffer.getInt(SRC_LENGTH_OFFSET); - int compressedLength = blobHeaderBuffer.getInt(COMPRESSED_LENGTH_OFFSET); - if (srcLength > dest.remaining()) { - throw new IllegalArgumentException("dest buffer is not large enough! required dest.remaining() >= " + srcLength); - } - - src = this.readSegments(firstSegmentIndex, compressedLength, blobHeaderBuffer); - } finally { - this.indexLocks[blobIndex].unlockRead(stamp); - } - - src.position(0); - if (dest.isDirect()) { - Zstd.decompress(dest, src); - } else { - ByteBuffer tempDest = allocateDirect(srcLength); - Zstd.decompress(tempDest, src); - tempDest.position(0); - dest.put(tempDest); - } - } else { - throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount); - } - } - - @Nonnull - protected ByteBuffer readBlobHeader(int firstSegmentIndex) throws IOException { - if (firstSegmentIndex == 0) { - throw new IllegalArgumentException("Invalid segment index!"); - } else { - ByteBuffer blobHeaderBuffer = getTempBuffer(BLOB_HEADER_LENGTH); - if (this.fileChannel.read(blobHeaderBuffer, this.blobHeaderPosition(firstSegmentIndex)) != BLOB_HEADER_LENGTH) { - throw new IllegalStateException(); - } else { - return blobHeaderBuffer; - } - } - } - - @Nonnull - protected ByteBuffer readSegments(int firstSegmentIndex, int compressedLength, @Nonnull ByteBuffer tempHeaderBuffer) throws IOException { - tempHeaderBuffer.limit(SEGMENT_HEADER_LENGTH); - ByteBuffer buffer = allocateDirect(compressedLength); - int remainingBytes = compressedLength; - int nextSegmentIndex = firstSegmentIndex; - - while (nextSegmentIndex != 0 && nextSegmentIndex != Integer.MIN_VALUE) { - long segmentPosition = this.segmentPosition(nextSegmentIndex); - long indexesStamp = this.nextSegmentIndexesLock.tryOptimisticRead(); - int segmentIndex = this.nextSegmentIndexes[nextSegmentIndex]; - if (!this.nextSegmentIndexesLock.validate(indexesStamp)) { - indexesStamp = this.nextSegmentIndexesLock.readLock(); - - try { - segmentIndex = this.nextSegmentIndexes[nextSegmentIndex]; - } finally { - this.nextSegmentIndexesLock.unlockRead(indexesStamp); - } - } - - nextSegmentIndex = segmentIndex; - int dataOffset = SEGMENT_HEADER_LENGTH; - if (buffer.position() == 0) { - dataOffset += BLOB_HEADER_LENGTH; - } - - int dataToRead = Math.min(this.segmentSize - dataOffset, remainingBytes); - buffer.limit(buffer.position() + dataToRead); - if (this.fileChannel.read(buffer, segmentPosition + dataOffset) != dataToRead) { - throw new IllegalStateException(); - } - - remainingBytes -= dataToRead; - if (remainingBytes == 0) { - break; - } - } - - if (buffer.remaining() == 0 && nextSegmentIndex == Integer.MIN_VALUE) { - return buffer; - } else { - throw new IOException("Failed to read segments: " + firstSegmentIndex + ", " + compressedLength + ", " + buffer + ", " + nextSegmentIndex); - } - } - - public void writeBlob(int blobIndex, @Nonnull ByteBuffer src) throws IOException { - if (blobIndex >= 0 && blobIndex < this.blobCount) { - int srcLength = src.remaining(); - int maxCompressedLength = (int)Zstd.compressBound(srcLength); - ByteBuffer dest = allocateDirect(BLOB_HEADER_LENGTH + maxCompressedLength); - dest.putInt(SRC_LENGTH_OFFSET, srcLength); - dest.position(BLOB_HEADER_LENGTH); - int compressedLength; - if (src.isDirect()) { - compressedLength = Zstd.compress(dest, src, this.compressionLevel); - } else { - ByteBuffer tempSrc = allocateDirect(srcLength); - tempSrc.put(src); - tempSrc.position(0); - compressedLength = Zstd.compress(dest, tempSrc, this.compressionLevel); - } - - dest.putInt(COMPRESSED_LENGTH_OFFSET, compressedLength); - dest.limit(dest.position()); - dest.position(0); - int indexPos = blobIndex * 4; - int tempIndexPos = this.indexesLength() + indexPos; - long stamp = this.indexLocks[blobIndex].writeLock(); - - try { - int firstSegmentIndex = this.writeSegments(blobIndex, dest); - int oldFirstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); - this.mappedBlobIndexes.putInt(indexPos, firstSegmentIndex); - this.mappedBlobIndexes.putInt(tempIndexPos, oldFirstSegmentIndex); - if (oldFirstSegmentIndex != 0) { - ByteBuffer tempIndexBuffer = getTempBuffer(4); - this.clearSegments(oldFirstSegmentIndex, tempIndexBuffer); - this.mappedBlobIndexes.putInt(tempIndexPos, 0); - } - } finally { - this.indexLocks[blobIndex].unlockWrite(stamp); - } - } else { - throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount); - } - } - - public void removeBlob(int blobIndex) throws IOException { - if (blobIndex >= 0 && blobIndex < this.blobCount) { - int indexPos = blobIndex * 4; - int tempIndexPos = this.indexesLength() + indexPos; - long stamp = this.indexLocks[blobIndex].writeLock(); - - try { - int oldFirstSegmentIndex = this.mappedBlobIndexes.getInt(indexPos); - if (oldFirstSegmentIndex != 0) { - this.mappedBlobIndexes.putInt(indexPos, 0); - this.mappedBlobIndexes.putInt(tempIndexPos, oldFirstSegmentIndex); - ByteBuffer tempIndexBuffer = getTempBuffer(4); - this.clearSegments(oldFirstSegmentIndex, tempIndexBuffer); - this.mappedBlobIndexes.putInt(tempIndexPos, 0); - } - } finally { - this.indexLocks[blobIndex].unlockWrite(stamp); - } - } else { - throw new IndexOutOfBoundsException("Index out of range: " + blobIndex + " blobCount: " + this.blobCount); - } - } - - protected int writeSegments(int blobIndex, @Nonnull ByteBuffer data) throws IOException { - int dataRemaining = data.remaining(); - int indexPos = blobIndex * 4; - int tempIndexPos = this.indexesLength() + indexPos; - ByteBuffer tempIndexBuffer = getTempBuffer(4); - if (this.contiguousBlobs) { - int segmentsCount = this.requiredSegments(dataRemaining); - IndexedStorageFile_v0.SegmentRangeLock segmentLock = this.findFreeSegment(segmentsCount); - - int var34; - try { - int firstSegmentIndex = segmentLock.segmentIndex; - this.mappedBlobIndexes.putInt(tempIndexPos, firstSegmentIndex); - int endSegmentIndex = firstSegmentIndex + segmentsCount; - long indexesResizeStamp = this.nextSegmentIndexesLock.writeLock(); - - try { - if (endSegmentIndex >= this.nextSegmentIndexes.length) { - this.nextSegmentIndexes = Arrays.copyOf(this.nextSegmentIndexes, endSegmentIndex); - } - } finally { - this.nextSegmentIndexesLock.unlockWrite(indexesResizeStamp); - } - - for (int segmentIndex = firstSegmentIndex; segmentIndex < endSegmentIndex; segmentIndex++) { - long segmentPosition = this.segmentPosition(segmentIndex); - int next = segmentIndex + 1; - int nextSegmentIndex = next < endSegmentIndex ? next : Integer.MIN_VALUE; - tempIndexBuffer.position(0); - tempIndexBuffer.putInt(0, nextSegmentIndex); - if (this.fileChannel.write(tempIndexBuffer, segmentPosition) != SEGMENT_HEADER_LENGTH) { - throw new IllegalStateException(); - } - - long indexesStamp = this.nextSegmentIndexesLock.writeLock(); - - try { - this.nextSegmentIndexes[segmentIndex] = nextSegmentIndex; - } finally { - this.nextSegmentIndexesLock.unlockWrite(indexesStamp); - } - - int dataToWrite = Math.min(this.segmentSize - SEGMENT_HEADER_LENGTH, dataRemaining); - data.limit(data.position() + dataToWrite); - if (this.fileChannel.write(data, segmentPosition + SEGMENT_HEADER_LENGTH) != dataToWrite) { - throw new IllegalStateException(); - } - - dataRemaining -= dataToWrite; - } - - var34 = firstSegmentIndex; - } finally { - segmentLock.unlockWrite(); - } - - return var34; - } else { - throw new UnsupportedOperationException("Not implemented!"); - } - } - - @Nonnull - private IndexedStorageFile_v0.SegmentRangeLock findFreeSegment(int count) { - int start = 0; - int index = 1; - int found = 0; - - while (true) { - label75: - while (found >= count) { - IndexedStorageFile_v0.SegmentRangeLock segmentRangeLock = this.tryWriteLockSegmentRange(start, count); - if (segmentRangeLock == null) { - start++; - found--; - } else { - for (int i = count - 1; i >= 0; i--) { - int segmentIndex = start + i; - int next = this.getNextIndex(segmentIndex); - if (next != 0) { - segmentRangeLock.unlockWrite(); - index = segmentIndex + 1; - start = 0; - found = 0; - continue label75; - } - } - - return segmentRangeLock; - } - } - - StampedLock segmentLock = this.getSegmentLock(index); - long stamp = segmentLock.tryReadLock(); - if (stamp == 0L) { - start = 0; - found = 0; - index++; - } else { - int next; - try { - next = this.getNextIndex(index); - } finally { - segmentLock.unlockRead(stamp); - } - - if (next == 0) { - if (start == 0) { - start = index; - } - - found++; - } else { - start = 0; - found = 0; - } - - index++; - } - } - } - - protected int getNextIndex(int segmentIndex) { - long indexesStamp = this.nextSegmentIndexesLock.tryOptimisticRead(); - int nextSegmentIndex = segmentIndex < this.nextSegmentIndexes.length ? this.nextSegmentIndexes[segmentIndex] : 0; - if (this.nextSegmentIndexesLock.validate(indexesStamp)) { - return nextSegmentIndex; - } else { - indexesStamp = this.nextSegmentIndexesLock.readLock(); - - int var5; - try { - var5 = segmentIndex < this.nextSegmentIndexes.length ? this.nextSegmentIndexes[segmentIndex] : 0; - } finally { - this.nextSegmentIndexesLock.unlockRead(indexesStamp); - } - - return var5; - } - } - - protected StampedLock getSegmentLock(int segmentIndex) { - if (segmentIndex < this.segmentLocks.length) { - return this.segmentLocks[segmentIndex]; - } else { - long stamp = this.segmentLocksLock.writeLock(); - - StampedLock newLength; - try { - if (segmentIndex >= this.segmentLocks.length) { - int newLengthx = segmentIndex + 1; - StampedLock[] newArray = Arrays.copyOf(this.segmentLocks, newLengthx); - - for (int i = this.segmentLocks.length; i < newLengthx; i++) { - newArray[i] = new StampedLock(); - } - - this.segmentLocks = newArray; - return this.segmentLocks[segmentIndex]; - } - - newLength = this.segmentLocks[segmentIndex]; - } finally { - this.segmentLocksLock.unlockWrite(stamp); - } - - return newLength; - } - } - - @Nullable - protected IndexedStorageFile_v0.SegmentRangeLock tryWriteLockSegmentRange(int start, int count) { - long[] stamps = new long[count]; - - for (int i = 0; i < count; i++) { - StampedLock segmentLock = this.getSegmentLock(start + i); - if (segmentLock.isWriteLocked()) { - for (int i1 = 0; i1 < i; i1++) { - this.getSegmentLock(start + i1).unlockWrite(stamps[i1]); - } - - return null; - } - - stamps[i] = segmentLock.writeLock(); - } - - return new IndexedStorageFile_v0.SegmentRangeLock(start, count, stamps); - } - - protected int indexesLength() { - return this.blobCount * 4; - } - - protected long segmentsBase() { - return HEADER_LENGTH + this.indexesLength() * 2L; - } - - protected long segmentOffset(int segmentIndex) { - if (segmentIndex == 0) { - throw new IllegalArgumentException("Invalid segment index!"); - } else { - return (long)(segmentIndex - 1) * this.segmentSize; - } - } - - protected long segmentPosition(int segmentIndex) { - return this.segmentOffset(segmentIndex) + this.segmentsBase(); - } - - protected int positionToSegment(long position) { - long segmentOffset = position - this.segmentsBase(); - if (segmentOffset < 0L) { - throw new IllegalArgumentException("position is before the segments start"); - } else { - return (int)(segmentOffset / this.segmentSize) + 1; - } - } - - protected long blobHeaderPosition(int segmentIndex) { - return this.segmentPosition(segmentIndex) + SEGMENT_HEADER_LENGTH; - } - - protected int requiredSegments(long dataLength) { - int size = this.segmentSize - SEGMENT_HEADER_LENGTH; - return (int)((dataLength + size - 1L) / size); - } - - public FileLock lock() throws IOException { - return this.fileChannel.lock(); - } - - public void force(boolean metaData) throws IOException { - this.mappedBlobIndexes.force(); - this.fileChannel.force(metaData); - } - - @Override - public void close() throws IOException { - this.fileChannel.close(); - if (UnsafeUtil.UNSAFE != null) { - UnsafeUtil.UNSAFE.invokeCleaner(this.mappedBlobIndexes); - } - - this.mappedBlobIndexes = null; - } - - @Nonnull - @Override - public String toString() { - return "IndexedStorageFile{fileChannel=" - + this.fileChannel - + ", compressionLevel=" - + this.compressionLevel - + ", contiguousBlobs=" - + this.contiguousBlobs - + ", blobCount=" - + this.blobCount - + ", segmentSize=" - + this.segmentSize - + ", mappedBlobIndexes=" - + this.mappedBlobIndexes - + ", nextSegmentIndexes=" - + Arrays.toString(this.nextSegmentIndexes) - + "}"; - } - - static { - MAGIC_BUFFER.position(0); - } - - static class OffsetHelper { - private int index; - - public int next(int len) { - int cur = this.index; - this.index += len; - return cur; - } - - public int length() { - return this.index; - } - } - - protected class SegmentRangeLock { - private final int segmentIndex; - private final int count; - private final long[] stamps; - - public SegmentRangeLock(int segmentIndex, int count, long[] stamps) { - this.segmentIndex = segmentIndex; - this.count = count; - this.stamps = stamps; - } - - protected void unlockRead() { - for (int i = 0; i < this.count; i++) { - IndexedStorageFile_v0.this.getSegmentLock(this.segmentIndex + i).unlockRead(this.stamps[i]); - this.stamps[i] = 0L; - } - } - - protected void unlockWrite() { - for (int i = 0; i < this.count; i++) { - IndexedStorageFile_v0.this.getSegmentLock(this.segmentIndex + i).unlockWrite(this.stamps[i]); - this.stamps[i] = 0L; - } - } - } -} diff --git a/src/manifests.json b/src/manifests.json index 06286898..9bec0a79 100644 --- a/src/manifests.json +++ b/src/manifests.json @@ -20,7 +20,6 @@ "ServerVersion": "*", "Dependencies": { "Hytale:I18nModule": "*", - "Hytale:BlockStateModule": "*", "Hytale:BlockModule": "*", "Hytale:BlockPhysics": "*" } @@ -56,7 +55,8 @@ "Main": "com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin", "ServerVersion": "*", "Dependencies": { - "Hytale:EntityModule": "*" + "Hytale:EntityModule": "*", + "Hytale:InteractionModule": "*" } } ,{ @@ -67,7 +67,7 @@ "Main": "com.hypixel.hytale.builtin.crafting.CraftingPlugin", "ServerVersion": "*", "Dependencies": { - "Hytale:BlockStateModule": "*", + "Hytale:BlockModule": "*", "Hytale:EntityModule": "*", "Hytale:InteractionModule": "*" } @@ -89,8 +89,10 @@ "ServerVersion": "*", "Dependencies": { "Hytale:I18nModule": "*", - "Hytale:BlockStateModule": "*", - "Hytale:BlockPhysics": "*" + "Hytale:BlockModule": "*", + "Hytale:BlockPhysics": "*", + "Hytale:InteractionModule": "*", + "Hytale:Teleport": "*" } } ,{ @@ -166,7 +168,7 @@ "ServerVersion": "*", "Dependencies": { "Hytale:I18nModule": "*", - "Hytale:BlockStateModule": "*", + "Hytale:BlockModule": "*", "Hytale:InteractionModule": "*", "Hytale:EntityModule": "*" } @@ -180,7 +182,8 @@ "ServerVersion": "*", "Dependencies": { "Hytale:Objectives": "*", - "Hytale:Shop": "*" + "Hytale:Shop": "*", + "Hytale:InteractionModule": "*" } } ,{ @@ -276,7 +279,7 @@ "ServerVersion": "*", "Dependencies": { "Hytale:ItemModule": "*", - "Hytale:BlockStateModule": "*" + "Hytale:BlockModule": "*" } } ,{ @@ -342,7 +345,7 @@ "ServerVersion": "*", "Dependencies": { "Hytale:InteractionModule": "*", - "Hytale:BlockStateModule": "*", + "Hytale:BlockModule": "*", "Hytale:BlockPhysics": "*" } } @@ -386,7 +389,10 @@ "Version": "1.0.0", "Website": "https://hypixel.com/", "Main": "com.hypixel.hytale.builtin.model.ModelPlugin", - "ServerVersion": "*" + "ServerVersion": "*", + "Dependencies": { + "Hytale:Universe": "*" + } } ,{ "Group": "Hytale", @@ -446,7 +452,8 @@ "ServerVersion": "*", "Dependencies": { "Hytale:NPC": "*", - "Hytale:DamageModule": "*" + "Hytale:DamageModule": "*", + "Hytale:InteractionModule": "*" } } ,{ @@ -465,8 +472,9 @@ "Main": "com.hypixel.hytale.builtin.adventure.teleporter.TeleporterPlugin", "ServerVersion": "*", "Dependencies": { - "Hytale:BlockStateModule": "*", - "Hytale:Teleport": "*" + "Hytale:BlockModule": "*", + "Hytale:Teleport": "*", + "Hytale:InteractionModule": "*" } } ,{ @@ -477,7 +485,8 @@ "Main": "com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin", "ServerVersion": "*", "Dependencies": { - "Hytale:NPC": "*" + "Hytale:NPC": "*", + "Hytale:InteractionModule": "*" } } ,{ @@ -486,7 +495,10 @@ "Version": "1.0.0", "Website": "https://hypixel.com/", "Main": "com.hypixel.hytale.builtin.deployables.DeployablesPlugin", - "ServerVersion": "*" + "ServerVersion": "*", + "Dependencies": { + "Hytale:InteractionModule": "*" + } } ,{ "Group": "Hytale", @@ -499,7 +511,8 @@ "Dependencies": { "Hytale:NPC": "*", "Hytale:Ambience": "*", - "Hytale:Instances": "*" + "Hytale:Instances": "*", + "Hytale:InteractionModule": "*" } } ,{ @@ -511,7 +524,8 @@ "Main": "com.hypixel.hytale.builtin.beds.BedsPlugin", "ServerVersion": "*", "Dependencies": { - "Hytale:Mounts": "*" + "Hytale:Mounts": "*", + "Hytale:InteractionModule": "*" } } ,{ @@ -535,7 +549,8 @@ "Dependencies": { "Hytale:I18nModule": "*", "Hytale:Instances": "*", - "Hytale:AnchorActionModule": "*" + "Hytale:AnchorActionModule": "*", + "Hytale:InteractionModule": "*" } } ,{